--- /dev/null
+;; 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))