Emacs Lisp: How to Color Comment in Major Mode

By Xah Lee. Date: . Last updated: .

This page shows how to implement comment coloring for writing a major mode.

emacs doge comment coloring
coloring of comment and string. 〔doge.txt

Problem

You are writing a major mode for a language. You want comment be syntax colored.

(For how to write a command to comment/uncomment, see: Emacs Lisp: How to Write Comment Command in Major Mode)

Solution

The typical way to syntax color comment is via emacs syntax table. Two things you need to do:

  1. Set syntax table for the comment characters.
  2. Set font-lock-defaults to non-nil.

Comment AND string will then automatically be syntax colored, when font-lock-mode is on. (it is on by default)

Let's do a example.

Say our comment syntax is # to the end of line, used by Python, Perl, PHP, Bash.

Here's complete code of a major mode:

;; sample mode that does coloring of python style commment syntax

(defvar xpy-mode-syntax-table nil "Syntax table for `xpy-mode'.")

(setq xpy-mode-syntax-table
      (let ( (synTable (make-syntax-table)))
        ;; python style comment: “# …”
        (modify-syntax-entry ?# "<" synTable)
        (modify-syntax-entry ?\n ">" synTable)
        synTable))

(define-derived-mode xpy-mode prog-mode "xpy"
  "xpy-mode is a major mode for editing language xpy."

  (setq font-lock-defaults (list nil nil))

  (set-syntax-table xpy-mode-syntax-table)
  ;; actually no need, because our syntax table name is “xpy-mode” + “-syntax-table”, so define-derived-mode will find it and set it
  )

Now, copy paste the above into a buffer then call eval-buffer to evaluate the code.

Now, open a new buffer, type the following:

some "thing" # wow

Then, M-x xpy-mode, you see that the string and comment are colored.

How Does Syntax Coloring Works

Here's how it works.

font-lock-mode

  1. font-lock-mode is a buffer-local minor mode. (that is, it can be on or off for each buffer, independent of each other.)
  2. font-lock-mode is on by default for all buffers since 2007 or so.
  3. font-lock-mode is a high-level “API” to syntax color buffer.
  4. font-lock-mode color text in two ways: ① by syntactic parsing based on the syntax table. This basically means, lookup the delimiter characters for string in syntax table, and color text betwen it in buffer. Same for comment. ② search by regular expressions. This is how keywords, function names, variable names, html title text, Markdown text, Org Mode text, etc, are colored.
  5. Syntactic fontification happens first. It finds comments and string and color them. Search-based fontification happens second. Once a text is colored, it is not changed. For example, if a text is colored as string or comment, subsequent searh by regex for coloring will skip those parts.
  6. font-lock-mode need 2 things to do the coloring job. ① Syntax table. ② the value of font-lock-defaults. It uses those info to go thru buffer and do syntax coloring.
  7. Vast majority of programing language major modes do syntax color by: ① set up the proper syntax table. (in particular, the characters for string and character for comment.) ② Set up proper value for font-lock-defaults. (then, turn on font-lock-mode, which is on by default)

(info "(elisp) Font Lock Mode")

font-lock-defaults

(info "(elisp) Font Lock Basics")

Syntax Table

Emacs Lisp: Syntax Table Tutorial

Here's the appropriate syntax table setup for popular languages.

C++ Style Comments

(defvar xcpp-mode-syntax-table nil "Syntax table for `xcpp-mode'.")

(setq xcpp-mode-syntax-table
      (let ( (synTable (make-syntax-table)))
        ;; C++ style comment “// …”
        (modify-syntax-entry ?\/ ". 12b" synTable)
        (modify-syntax-entry ?\n "> b" synTable)
        synTable))

(define-derived-mode xcpp-mode prog-mode "xcpp"
  "xcpp-mode is a major mode for editing language xcpp."
  (setq font-lock-defaults (list nil)))

Java Style Comments

(defvar xjv-mode-syntax-table nil "Syntax table for `xjv-mode'.")

(setq xjv-mode-syntax-table
      (let ( (synTable (make-syntax-table)))
        ;; comment style “/* … */”
        (modify-syntax-entry ?\/ ". 14" synTable)
        (modify-syntax-entry ?* ". 23" synTable)
        synTable))

(define-derived-mode xjv-mode prog-mode "xjv"
  "xjv-mode is a major mode for editing language xjv."
  (setq font-lock-defaults (list nil)))

Wolfram Language Style Comments

(defvar xwl-mode-syntax-table nil "Syntax table for `xwl-mode'.")

(setq xwl-mode-syntax-table
      (let ((synTable (make-syntax-table)))
        ;; Wolfram Language style comment “(* … *)”
        (modify-syntax-entry ?\( ". 1" synTable)
        (modify-syntax-entry ?\) ". 4" synTable)
        (modify-syntax-entry ?* ". 23" synTable)
        synTable))

(define-derived-mode xwl-mode prog-mode "xwl"
  "xwl-mode is a major mode for editing language xwl."
  (setq font-lock-defaults (list nil)))

Syntax Table Supports Limited Comment Syntax

Emacs's syntax table only support comment syntaxes that are used in mainstream languages.

Emacs Syntax Table Support of Comment Syntax Types
ExampleSyntax Type
# …\n (Python, Perl, PHP, Bash, shells)
; …\n (lisp)
' …\n (Visual Basic)
Start with a char to newline char.
// … \n (C, C++, C#, Java, JavaScript, PHP)Start with 2 identical chars to newline char.
(* … *) (Mathematica, Pascal, OCaml, Applescript)
{- … -} (Haskell)
A matching pair chars with another char.
/* … */ (C, C++, C#, Java, JavaScript)Two chars used in a ad hoc way as matching pair.

If your language's comment syntax is not one of the above, then emacs syntax table is not able to capture it.

To handle non-mainstream comment syntax, you have 2 ways:

Now, continue to Emacs Lisp: How to Write Comment Command in Major Mode

Like it? Buy Xah Emacs Tutorial. Thanks.

or, buy something from Best Keyboard for Emacs