如何用Emacs Lisp编写函数实现Haskell语法高亮?
Got it, let's get your Haskell function definition highlighting working in Emacs. The function-based approach is the right call here since Elisp regex doesn't support lookarounds, but there are a few issues in your code that are keeping it from working properly.
First, let's go over the main problems:
- Invalid newline character syntax: In
find-equal-character, you wrote?which is not valid Elisp—newlines need to be written as?\n. That's probably causing errors right off the bat. - Inefficient character lookup: Your
get-cur-characterusesbuffer-substringwhich creates unnecessary strings; we can usechar-afterdirectly for better performance and cleaner code. - Match data preservation: While your logic checks for the equals sign, we need to make sure the match data from the initial regex search stays intact so font-lock knows exactly what text to highlight.
Here's the fixed and improved version of your code:
(defun get-cur-character (pos) ;; Grab the character at the given position using char-after (more efficient) (char-after (1- pos))) (defun find-equal-character (pos) (let ((curchar nil) (result nil) (max-pos (point-max))) (while (and (not (equal curchar ?\n)) (< pos max-pos)) (setq curchar (get-cur-character pos)) (setq pos (1+ pos)) (when (equal curchar ?=) (setq result t) (setq curchar ?\n))) ; Exit loop as soon as we find = result)) (defun highlight-haskell-function (limit) (save-excursion (when (re-search-forward "^\\([a-z]+\\w*\\)" limit t) ;; Only return true if we found an equals sign after the match (when (find-equal-character (match-end 0)) t)))) ; Match data from re-search-forward is still valid here ;; Make sure we add the keywords after haskell-mode is loaded (eval-after-load 'haskell-mode '(font-lock-add-keywords 'haskell-mode '((highlight-haskell-function . 'font-lock-keyword-face)))) (provide 'haskell-mode)
Let's walk through the changes:
- Fixed newline syntax: Replaced the broken
?with?\nso Elisp recognizes the newline character correctly. - Optimized character fetching:
get-cur-characternow useschar-after, which is faster and avoids creating unnecessary substrings. - Early loop exit: Once we find the
=sign, we setcurcharto?\nto break out of the loop immediately instead of scanning the rest of the line. - Safe loading: Used
eval-after-loadto ensure we don't try to modifyhaskell-modebefore it's been loaded into Emacs—this prevents errors on startup. - Preserved match data: By returning
tonly when both the regex match and equals sign check pass, we keep the match data from the initialre-search-forwardintact. Font-lock uses this match data to know which part of the buffer to apply the face to.
To use this:
- Save this code in a file (e.g.,
haskell-function-highlight.el) - Add
(require 'haskell-function-highlight)to your Emacs config file (init.elor.emacs) - Open a Haskell file—any function starting with a lowercase letter (followed by letters, digits, or underscores) with an
=on the same line should now be highlighted with the keyword face.
If you want to use a different face (like the standard function name face), just change font-lock-keyword-face to font-lock-function-name-face in the font-lock-add-keywords line.
One quick note: Your current regex ^\\([a-z]+\\w*\\) doesn't match function names with hyphens (like my-cool-function), which are common in Haskell. If you want to support those, adjust the regex to ^\\([a-z][\\w-]*\\)—this allows hyphens in the function name.
内容的提问来源于stack exchange,提问作者user9775773

