Emacs Lisp Mode Syntax Coloring Problem

perm url Emacs Lisp Mode Syntax Coloring Problem.

Emacs Lisp Mode Syntax Coloring Problem

Xah Lee, 2009-03-16

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.

It appears that this is because only a particular type of elisp keywords are highlighted. Namely, keywords that are technically known as lisp's “special forms” are are colored, while all the others are not (macros, functions, commands, predefined variables ...). Here's a example of a elisp code in 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 most language modes, all the language's keywords are syntax colored. They are colored differently according to the keyword or symbol's role. 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.

One might think that the colored keywords are 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 at the goal of ease-of-reading. Namely, keywords that are likely to be significant and starting a code block are colored. (e.g. 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, inconsistant, and difficult to understand.

If emacs-lisp-mode colors all keywords by their lexical semantics, 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 is emacs 22.2.

gnu.emacs.help newsgroup discussion: http://groups.google.com/group/gnu.emacs.help/browse_frm/thread/c4da689e79124c46. I filed a emacs bug report, #2630. http://groups.google.com/group/gnu.emacs.bug/browse_frm/thread/76ae27dffbd356a6.

Popular posts from this blog

11 Years of Writing About Emacs

does md5 creates more randomness?

Google Code shutting down, future of ErgoEmacs