Skip to content

Commit

Permalink
Reference checking
Browse files Browse the repository at this point in the history
Using public domain code by Dmitry Dzhus <mail@sphinx.net.ru>
from markdown-goodies (with permission).
  • Loading branch information
Jason Blevins committed Jun 4, 2008
1 parent 18ae13a commit 0bd97a2
Showing 1 changed file with 142 additions and 0 deletions.
142 changes: 142 additions & 0 deletions markdown-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@
;; output in another buffer while `C-c C-c p` runs Markdown on the
;; current buffer and previews the output in a browser.
;;
;; `C-c C-c c` will check for undefined references. If there are any,
;; a small buffer will open with a list. Selecting a reference from
;; this list and pressing `RET` will insert a reference template at
;; the end of the buffer.
;;
;; * Images: `C-c C-i`
;;
;; `C-c C-i i` inserts an image, using the active region (if any) as
Expand Down Expand Up @@ -151,6 +156,7 @@
;; * Greg Bognar <greg_bognar@hms.harvard.edu> for menus and a patch.
;; * Daniel Burrows <dburrows@debian.org> for filing Debian bug #456592.
;; * Peter S. Galbraith <psg@debian.org> for maintaining emacs-goodies-el.
;; * Dmitry Dzhus <mail@sphinx.net.ru> for reference checking functions.

;;; Bugs:

Expand Down Expand Up @@ -642,6 +648,8 @@ as preformatted text."
;; Markdown functions
(define-key markdown-mode-map "\C-c\C-cm" 'markdown)
(define-key markdown-mode-map "\C-c\C-cp" 'markdown-preview)
;; References
(define-key markdown-mode-map "\C-c\C-cc" 'markdown-check-refs)
markdown-mode-map)
"Keymap for Markdown major mode")

Expand Down Expand Up @@ -673,11 +681,145 @@ as preformatted text."
["Insert image" markdown-insert-image]
["Insert horizontal rule" markdown-insert-hr]
"---"
["Check references" markdown-check-refs]
"---"
["Version" markdown-show-version]
))



;;; References ================================================================

;;; Undefined reference checking code by Dmitry Dzhus <mail@sphinx.net.ru>.

(defconst markdown-refcheck-buffer
"*Undefined references for %BUFFER%*"
"Name of buffer which will contain a list of undefined
references in `markdown-mode' buffer named %BUFFER%.")

(defun markdown-has-reference-definition (reference)
"Find out whether Markdown REFERENCE is defined.
REFERENCE should include the square brackets, like [this]."
(let ((reference (downcase reference)))
(save-excursion
(goto-char (point-min))
(catch 'found
(while (re-search-forward markdown-regex-reference-definition nil t)
(when (string= reference (downcase (match-string-no-properties 1)))
(throw 'found t)))))))

(defun markdown-get-undefined-refs ()
"Return a list of undefined Markdown references.
Result is an alist of pairs (reference . occurencies), where
occurencies is itself another alist of pairs (label .
line-number).
For example, an alist corresponding to [Nice editor][Emacs] at line 12,
\[GNU Emacs][Emacs] at line 45 and [manual][elisp] at line 127 is
\((\"[emacs]\" (\"[Nice editor]\" . 12) (\"[GNU Emacs]\" . 45)) (\"[elisp]\" (\"[manual]\" . 127)))."
(let ((missing))
(save-excursion
(goto-char (point-min))
(while
(re-search-forward markdown-regex-link-reference nil t)
(let* ((label (match-string-no-properties 1))
(reference (match-string-no-properties 2))
(target (downcase (if (string= reference "[]") label reference))))
(unless (markdown-has-reference-definition target)
(let ((entry (assoc target missing)))
(if (not entry)
(add-to-list 'missing (cons target
(list (cons label (line-number-at-pos)))) t)
(setcdr entry
(append (cdr entry) (list (cons label (line-number-at-pos))))))))))
missing)))

(defun markdown-add-missing-ref-definition (ref buffer &optional recheck)
"Add blank REF definition to the end of BUFFER.
REF is a Markdown reference in square brackets, like \"[lisp-history]\".
When RECHECK is non-nil, BUFFER gets rechecked for undefined
references so that REF disappears from the list of those links."
(with-current-buffer buffer
(when (not (eq major-mode 'markdown-mode))
(error "Not available in current mdoe"))
(goto-char (point-max))
(indent-new-comment-line)
(insert (concat ref ": ")))
(switch-to-buffer-other-window buffer)
(goto-char (point-max))
(when recheck
(markdown-check-refs t)))

;; Button which adds an empty Markdown reference definition to the end
;; of buffer specified as its 'target-buffer property. Reference name
;; is button's label
(define-button-type 'markdown-ref-button
'help-echo "Push to create an empty reference definition"
'face 'bold
'action (lambda (b)
(markdown-add-missing-ref-definition
(button-label b) (button-get b 'target-buffer) t)))

;; Button jumping to line in buffer specified as its 'target-buffer
;; property. Line number is button's 'line property.
(define-button-type 'goto-line-button
'help-echo "Push to go to this line"
'face 'italic
'action (lambda (b)
(message (button-get b 'buffer))
(switch-to-buffer-other-window (button-get b 'target-buffer))
(goto-line (button-get b 'target-line))))

(defun markdown-check-refs (&optional silent)
"Show all undefined Markdown references in current `markdown-mode' buffer.
If SILENT is non-nil, do not message anything when no undefined
references found.
Links which have empty reference definitions are considered to be
defined."
(interactive "P")
(when (not (eq major-mode 'markdown-mode))
(error "Not available in current mode"))
(let ((oldbuf (current-buffer))
(refs (markdown-get-undefined-refs))
(refbuf (get-buffer-create (replace-regexp-in-string
"%BUFFER%" (buffer-name)
markdown-refcheck-buffer t))))
(if (null refs)
(progn
(when (not silent)
(message "No undefined references found"))
(kill-buffer refbuf))
(with-current-buffer refbuf
(when view-mode
(View-exit-and-edit))
(erase-buffer)
(insert "Following references lack definitions:")
(newline 2)
(dolist (ref refs)
(let ((button-label (format "%s" (car ref))))
(insert-text-button button-label
:type 'markdown-ref-button
'target-buffer oldbuf)
(insert " (")
(dolist (occurency (cdr ref))
(let ((line (cdr occurency)))
(insert-button (number-to-string line)
:type 'goto-line-button
'target-buffer oldbuf
'target-line line)
(insert " "))) (delete-backward-char 1)
(insert ")")
(newline))))
(view-buffer-other-window refbuf)
(goto-line 4))))


;;; Commands ==================================================================

(defun markdown ()
Expand Down

0 comments on commit 0bd97a2

Please sign in to comment.