HomeMathComputingArtsWordsLiteratureMusictwitter facebook webfeed

How to Add Comment Handling in Your Major Mode

Advertise Here For Profit

Xah Lee, 2008-11, 2010-09-14

This page gives a practical example of writing a emacs major mode that handles comment and uncomment. If you are not familiar with writing a major mode, see: How To Write A Emacs Major Mode For Syntax Coloring.

The Problem

You want to write a command to comment or uncomment code in your own major mode.

Detail

Emacs has a standard command to insert or delete comment, named “comment-dwim” 【Alt+;】. When there is a text selection, “comment-dwim” will comment or uncomment the region in a smart way. If there is no text selection, “comment-dwim” will insert a comment syntax at the end of line.

The “comment-dwim” is the standard command to comment/uncomment code. Your major mode should support it. When a user types the keyboard shortcut for comment-dwim while in your language mode, he should expect it to work for that language.

Solution

Sample Source Code

Let's say our new lang is called “xx”, and the following is sample source code for your lang. First, save it to a file, name it “test.xx”.

-*- mode: xx -*-

Sin[x]^2 + Cos[y]^2 == 1
Pi^2/6 == Sum[1/x^2,{x,1,Infinity}]

# Perl, Python, Bash, PHP

// C, PHP, C++, Java, Javascript

/* PHP, Java, C++, Javascript */

(* Applescript, Mathematica, Pascal, OCaml *)

The various comment texts above is there for testing purposes. In later part of this tutorial, we'll modify our code to handle each comment syntax.

The first line -*- mode: xx -*- is a quick way to tell emacs to load “xx-mode” when the file is opened. This way, we don't have to manually call “xx-mode” each time we open the test file.

The Major Mode Template

One type of comment syntax starts with a comment char and end with a newline char. For example, Perl, Python, Bash, all uses “#” for starting comment. Here's a major mode template to handle this comment syntax:

;; the command to comment/uncomment text
(defun xx-comment-dwim (arg)
"Comment or uncomment current line or region in a smart way.
For detail, see `comment-dwim'."
   (interactive "*P")
   (require 'newcomment)
   (let ((deactivate-mark nil) (comment-start "#") (comment-end ""))
     (comment-dwim arg)))

;; keywords for syntax coloring
(setq myKeywords
 `(
   ( ,(regexp-opt '("Sin" "Cos" "Sum") 'word) . font-lock-function-name-face)
   ( ,(regexp-opt '("Pi" "Infinity") 'word) . font-lock-constant-face)
  )
)

;; define the major mode.
(define-derived-mode xx-mode fundamental-mode
"xx-mode is a major mode for editing language xx."
  (setq font-lock-defaults '(myKeywords))

  ;; modify the keymap
  (define-key xx-mode-map [remap comment-dwim] 'xx-comment-dwim)

  ;; perl style comment: “# …” 
  (modify-syntax-entry ?# "< b" xx-mode-syntax-table)
  (modify-syntax-entry ?\n "> b" xx-mode-syntax-table)
)

Save the above in a file and name it 〔xx-mode.el〕.

The “xx-comment-dwim” is our command to comment and uncomment code. The implementation is based on the “newcomment.el”'s infrastructure. The 〔newcomment.el〕 is bundled with emacs and is probably used by most lang modes. It is a good idea to based on it instead of writing your own.

The line “define-key” defines a keyboard shortcut for invoking “xx-comment-dwim”. The variable for keymap named “xx-mode-map” is automatically created for you when you called “define-derived-mode” with first argument being “xx-mode”. That's why we don't need to create and define it, we simply start to call “define-key” to modify its content.

The [remap comment-dwim] is a special syntax of define-key to tell emacs to use the same binding that is currently bound to “comment-dwim”. This way, we make sure the shortcut stays the same as comment-dwim even if user may have changed it to some other key.

The “modify-syntax-entry” lines are to make sure that “#” and “\n” chars's syntax are that of starting and ending of comment. A syntax table is automatically created when you use define-derived-mode, and the name is “‹mode prefix›-syntax-table”. The “mode prefix” is the name of the first argument you passed to define-derived-mode. Once comment chars have the correct syntax table entry, comments are automatically syntax colored. (that is, you don't need to write other code to syntax color comments.)

(info "(elisp) Syntax Tables")

Testing

Now, open the sample source code file “test.xx”, type 【Alt+x xx-mode】, and the code will be highlighted like this:

Sin[x]^2 + Cos[y]^2 == 1
Pi^2/6 == Sum[1/x^2,{x,1,Infinity}]

# perl, python, bash

Now, call “xx-comment-dwim”, you'll see that it works. Also try it on a text selection.

C Style Comments

To do the C style comments // …, you will need to change the syntax entry lines as follows:

  ;; c style comment “// …” 
  (modify-syntax-entry ?\/ ". 12b" xx-mode-syntax-table)
  (modify-syntax-entry ?\n "> b" xx-mode-syntax-table)

and also change the code in “xx-comment-dwim” to:

(comment-start "//") (comment-end "")

Java Style Comments

To do the Java style comments /* … */, you will need to change the syntax entry lines as follows:

  ;; define comment for this style: “/* … */” 
  (modify-syntax-entry ?\/ ". 14" xx-mode-syntax-table)
  (modify-syntax-entry ?* ". 23" xx-mode-syntax-table)

and also change the code in “xx-comment-dwim” to:

(comment-start "/*") (comment-end "*/")

Mathematica Style Comments

To do the Mathematica or Pascal style comments (* … *), you will need to change the syntax entry lines as follows:

  ;; Mathematica style comment: “(* … *)” 
  (modify-syntax-entry ?\( ". 1" xx-mode-syntax-table)
  (modify-syntax-entry ?\) ". 4" xx-mode-syntax-table)
  (modify-syntax-entry ?* ". 23" xx-mode-syntax-table)

and also change the code in “xx-comment-dwim” to:

(comment-start "(*") (comment-end "*)")

Summary

There are 2 issues with comments. One is the syntax coloring of comments. The other is a command that comment/uncomment code.

For syntax coloring:

For comment command:

If you don't like the behavior of comment-dwim, or you don't want your function based on it, you can write your own. See: Comment Command from Scratch.

Complex Comment Syntax

Emacs's syntax table only support comments syntaxes that are used in mainstream languages. Here are the comment syntax types supported by emacs's syntax table:

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#, 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#, 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. You need to use emacs syntax coloring mechanisms to color comment like any other syntax. (see: How To Write A Emacs Major Mode For Syntax Coloring.) You also need to write your own command to comment/uncomment code. (See: Comment Command from Scratch.)

blog comments powered by Disqus