From ad34bdfed9510cdbc12c27149cb2c0ca09ea4958 Mon Sep 17 00:00:00 2001 From: simon Date: Tue, 9 Aug 2011 18:34:25 +0000 Subject: [PATCH] First attempt at an Emacs mode that syntax-highlights Halibut markup. git-svn-id: svn://svn.tartarus.org/sgt/halibut@9259 cda61777-01e9-0310-a592-d414129be87e --- misc/halibut.el | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 misc/halibut.el diff --git a/misc/halibut.el b/misc/halibut.el new file mode 100644 index 0000000..b5d6190 --- /dev/null +++ b/misc/halibut.el @@ -0,0 +1,113 @@ +;; Halibut mode for emacs. +;; +;; Tested on GNU emacs 23.2.1. + +(defun halibut-mode-font-lock-extend-region () + (save-excursion + (let (new-beg new-end) + (goto-char font-lock-beg) + (if (re-search-backward "\n[ \t]*\n" nil 1) + (goto-char (match-end 0))) + (setq new-beg (point)) + (goto-char font-lock-end) + (re-search-forward "\n[ \t]*\n" nil 1) + (setq new-end (point)) + (if (and (= new-beg font-lock-beg) (= new-end font-lock-end)) + nil ;; did nothing + (setq font-lock-beg new-beg) + (setq font-lock-end new-end) + t)))) + +(defun halibut-mode-match-braced-comment (limit) + ;; Look for a braced Halibut comment, which starts with \#{ and ends + ;; with } but has to skip over matching unescaped braces in between. + (if (not (search-forward "\\#{" limit t)) + nil ;; didn't find the introducer string + (let ((start (match-beginning 0)) + (depth 1)) + (goto-char (match-end 0)) + ;; Repeatedly find the next unescaped brace and adjust depth. + (while (and (> depth 0) + (looking-at "\\([^\\\\{}]\\|\\\\.\\)*\\([{}]\\)") + (< (match-end 2) limit)) + (setq depth (if (string= (match-string 2) "{") (1+ depth) (1- depth))) + (goto-char (match-end 2))) + ;; If depth hit zero, we've stopped just after the closing + ;; brace. If it didn't, we should stop at limit. + (if (> depth 0) (goto-char limit)) + ;; Now the string between 'start' and point is our match. + (set-match-data (list start (point))) + t))) + +(defun halibut-mode-match-paragraph-comment (limit) + ;; Look for a whole-paragraph Halibut comment, which starts with \# + ;; and then something other than an open brace, and ends at the next + ;; paragraph break. + (catch 'found-one + (while (search-forward "\\#" limit t) + (let ((start (match-beginning 0))) + ;; For each \# we find, check to see if it's eligible. + (when (and + ;; It must not be followed by {. + (not (looking-at "\\\\#{")) + ;; It must be the first thing in its paragraph (either + ;; because the chunk of whitespace immediately preceding it + ;; contains more than one \n, or because that chunk of + ;; whitespace terminates at the beginning of the file). + (let ((this-line (line-number-at-pos))) + (save-excursion + (goto-char start) + (skip-chars-backward "\n\t ") + (or (= (point) (point-min)) + (< (line-number-at-pos) (1- this-line)))))) + ;; If those conditions are satisfied, we've found an + ;; eligible \#. Search forward for the next paragraph end. + (if (re-search-forward "\n[ \t]*\n" nil 1) + (goto-char (match-beginning 0))) + ;; Now the string between 'start' and point is our match. + (set-match-data (list start (point))) + ;; Terminate the while loop. + (throw 'found-one t)))) + ;; The loop terminated without finding anything. + nil)) + +(defun halibut-mode-match-code-or-emphasis-line (char limit) + ;; Look for a Halibut code line (starting with "\c " or containing + ;; only "\c"). Either right here... + (if (and (= (current-column) 0) (looking-at (concat "\\\\" char "[ \n]"))) + (let ((start (match-beginning 0))) + (end-of-line) + (set-match-data (list start (min limit (point)))) + t) + ;; ... or further down... + (if (re-search-forward (concat "\n\\\\" char "[ \n]") limit t) + (let ((start (1+ (match-beginning 0)))) + (goto-char start) + (end-of-line) + (set-match-data (list start (min limit (point)))) + t) + ;; and if neither of those, we didn't find one. + nil))) + +(defun halibut-mode-match-code-line (limit) + (halibut-mode-match-code-or-emphasis-line "c" limit)) +(defun halibut-mode-match-emphasis-line (limit) + (halibut-mode-match-code-or-emphasis-line "e" limit)) + +(defconst halibut-font-lock-keywords + '((halibut-mode-match-braced-comment . font-lock-comment-face) + (halibut-mode-match-paragraph-comment . font-lock-comment-face) + (halibut-mode-match-code-line . font-lock-string-face) + (halibut-mode-match-emphasis-line . font-lock-preprocessor-face) + ("\\\\\\([-{}_\\\\]\\|u[0-9a-fA-F]*\\|[A-Za-tv-z][0-9A-Za-z]*\\)" . + font-lock-keyword-face)) + "Syntax highlighting for Halibut mode.") + +;;;###autoload +(define-derived-mode halibut-mode fundamental-mode "Halibut" + "Major mode for editing Halibut documentation markup." + (setq font-lock-defaults '(halibut-font-lock-keywords t)) + (add-hook 'font-lock-extend-region-functions 'halibut-mode-font-lock-extend-region)) + +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.but\\'" . halibut-mode)) -- 2.11.0