Emacs Lisp Mode Syntax Coloring Problem

Advertise Here

, 2009-03-16, 2010-05-18

This page gives a description of emacs's emacs-lisp-mode's flaw.

Emacs has a mode for editing emacs lisp code. The mode is called emacs-lisp-mode. However, the syntax highlighting in emacs-lisp-mode only support the very minimum of highlighting.

For example, these keywords are colored purple by default: defun, lambda, while, if, progn, save-restriction … etc. while the following built-in keywords are uncolored (black): narrow-to-region, mapc, goto-char, point-min, search-forward, car, nil, replace-match, …, etc.

Here's a example of code colored by emacs-lisp-mode:

(defun lisp-complete-symbol (&optional predicate)
  "Perform completion on Lisp symbol preceding point.
…"
  (interactive)
  (let ((window (get-buffer-window "*Completions*" 0)))
    (if (and (eq last-command this-command)
       window (window-live-p window) (window-buffer window)
       (buffer-name (window-buffer window)))
  ;; If this command was repeated, and
  ;; there's a fresh completion window with a live buffer,
  ;; and this command is repeated, scroll that window.
  (with-current-buffer (window-buffer window)
    (if (pos-visible-in-window-p (point-max) window)
        (set-window-start window (point-min))
      (save-selected-window
        (select-window window)
        (scroll-up))))

      ;; Do completion.
      (let* ((end (point))
       (beg (with-syntax-table emacs-lisp-mode-syntax-table
        (save-excursion
          (backward-sexp 1)
          (while (= (char-syntax (following-char)) ?\')
      (forward-char 1))
          (point))))
       (pattern (buffer-substring-no-properties beg end))
       (predicate
        (or predicate
      (save-excursion
        (goto-char beg)
        (if (not (eq (char-before) ?\())
      (lambda (sym) ;why not just nil ?   -sm
        (or (boundp sym) (fboundp sym)
            (symbol-plist sym)))
          ;; Looks like a funcall position.  Let's double check.
          (if (condition-case nil
            (progn (up-list -2) (forward-char 1)
             (eq (char-after) ?\())
          (error nil))
        ;; If the first element of the parent list is an open
        ;; parenthesis we are probably not in a funcall position.
        ;; Maybe a `let' varlist or something.
        nil
      ;; Else, we assume that a function name is expected.
      'fboundp)))))
       (completion (try-completion pattern obarray predicate)))
  (cond ((eq completion t))
        ((null completion)
         (message "Can't find completion for \"%s\"" pattern)
         (ding))
        ((not (string= pattern completion))
         (delete-region beg end)
         (insert completion)
         ;; Don't leave around a completions buffer that's out of date.
         (let ((win (get-buffer-window "*Completions*" 0)))
     (if win (with-selected-window win (bury-buffer)))))
        (t
         (let ((minibuf-is-in-use
          (eq (minibuffer-window) (selected-window))))
     (unless minibuf-is-in-use
       (message "Making completion list..."))
     (let ((list (all-completions pattern obarray predicate)))
       (setq list (sort list 'string<))
       (or (eq predicate 'fboundp)
           (let (new)
       (while list
         (setq new (cons (if (fboundp (intern (car list)))
                 (list (car list) " <f>")
               (car list))
             new))
         (setq list (cdr list)))
       (setq list (nreverse new))))
       (if (> (length list) 1)
           (with-output-to-temp-buffer "*Completions*"
       (display-completion-list list pattern))
         ;; Don't leave around a completions buffer that's
         ;; out of date.
         (let ((win (get-buffer-window "*Completions*" 0)))
           (if win (with-selected-window win (bury-buffer))))))
     (unless minibuf-is-in-use
       (message "Making completion list...%s" "done")))))))))

In many language modes, all the language's keywords are syntax colored. They are colored differently according to the keyword's role (e.g. function, class, object, type, constant, etc.). If you don't like so many colors, you can set font-lock-maximum-decoration to 1, 2, or 3, for progressively more colors. However, in emacs-lisp-mode, only a subset of keywords are colored, and emacs-lisp-mode doesn't support customize-group, nor levels for font-lock-maximum-decoration. So, you are stuck with the most basic coloring of a subset of keywords.

It is suggested that the mode only color a special class of elisp symbols called “special forms” (see: (info "(elisp) What Is a Function")). However, that is not true. “or” is a special form, but is not colored.

I think the symbols chosen to color is heuristic based for the goal of ease-of-reading. Namely, keywords that are likely to be significant and starting a code block are colored. (example: let, lambda, if, unless, while, progn, cond, condition-case, save-excursion, with-current-buffer, with-selected-window, with-output-to-temp-buffer, with-selected-window, and few more.) However, the coloring scheme is unpredictable, inconsistent, and difficult to understand.

If emacs-lisp-mode colors all built-in symbols by their semantics class (e.g. function, command, variable, macro, special form …), with support for levels of font-lock, the advantage of highlighting only significant keyword can be maintained, while making it more systematic, understandable, and flexible.

This report applies to emacs 22.2, emacs 23.2.

This essay is originated posted to gnu.emacs.help newsgroup. I filed this as emacs bug report, debbugs.gnu.org #2630.

blog comments powered by Disqus