GenericMode

Using Generic Modes For Obscure Languages

The following in your ~/.emacs adds very basic syntax highlighting for all sorts of files:

    (require 'generic-x)

It will add syntax highlighting for batch files, ini files, command files, registry files, apache files, samba files, resource files, fvwm files, etc. For a complete list, see below.

When editing the init files of obscure programs, I often add the following to the first line of the file:

    # -*- mode: default-generic -*-

Simple Example

Suppose we have a little language called foo; a typical foo-file might look something like:

    !! this is a comment
    account=foo; !! another comment
    user=jimmy;
    password=$3cre7;

Using define-generic-mode, we can easily define a mode for this:

    (require 'generic-x) ;; we need this
    
    (define-generic-mode 
      'foo-mode                         ;; name of the mode to create
      '("!!")                           ;; comments start with '!!'
      '("account" "user" 
        "password")                     ;; some keywords
      '(("=" . 'font-lock-operator)     ;; '=' is an operator
        (";" . 'font-lock-builtin))     ;; ';' is a built-in 
      '("\\.foo$")                      ;; files for which to activate this mode 
       nil                              ;; other functions to call
      "A mode for foo files"            ;; doc string for this mode
    )

(example taken from http://emacs-fu.blogspot.com/2010/04/creating-custom-modes-easy-way-with.html)

Defining New Generic Modes

For very simple languages, you can use generic mode to “generate” a new MajorMode for you. The following example is for keymap files as used by the loadkeys(1) program.

First, an example of the language:

    # this only works for console!
    # allow control tab
    control keycode 15 = Macro
    # shift capslock is compose key
    shift keycode 58 = Compose
    # cedilla on 4
    altgr keycode 5 = ccedilla
    shift altgr keycode 5 = Ccedilla
    # control up down left right home end
    control keycode 103 = Meta_p
    control keycode 108 = Meta_n
    control keycode 105 = Meta_Control_b
    control keycode 106 = Meta_Control_f
    control keycode 102 = Meta_less
    control keycode 107 = Meta_greater

And now for the major mode.

Here’s our first try. It has the following features:

    (define-generic-mode 'keymap-mode
      '("#") 
      '("control" "meta" "shift" "alt" "altgr" "compose" "keycode")
      nil
      '(".keymap\\'" ".map\\'")
      nil)

Here’s the second try. Additional features:

    (define-generic-mode 'keymap-mode
      '("#")
      '("control" "meta" "shift" "alt" "altgr" "compose" "keycode")
      '(("[0-9]+" . 'font-lock-variable-name-face))
      '(".keymap\\'" ".map\\'")
      nil
      "Major mode for editing keymap files as used by loadkeys(1).")

The use of the FONT-LOCK-LIST parameter is very powerful. It allows you to highlight any RegularExpression using any Face.

Here’s how to define a very simple PL/SQL mode. Naturally you’d be better off customizing SqlMode, but this example is just here to illustrate how to make "--" a comment starter. In order to highlight the stuff correctly, we use a font-lock expression. In order to set ‘comment-start’, we use an anonymous function (a lambda expression, which needs no quoting).

    (define-generic-mode 'poor-man-plsql-mode
      () 
      '("if" "end" "else" "begin" "declare" "function" "procedure")
      '(("\\(--.*\\)" 1 'font-lock-comment-face))
      '("\\.pls\\'")
      (list (lambda () (setq comment-start "--")))
      "Major mode for very simple PL/SQL highlighting.")

For more info on the font locking, see FontLockKeywords.

Here’s an even longer example:

    (define-generic-mode 'htaccess-mode
      '(?#)
      '(;; core
        "AcceptPathInfo" "AccessFileName" "AddDefaultCharset" "AddOutputFilterByType"
        "AllowEncodedSlashes" "AllowOverride" "AuthName" "AuthType"
        "CGIMapExtension" "ContentDigest" "DefaultType" "DocumentRoot"
        "EnableMMAP" "EnableSendfile" "ErrorDocument" "ErrorLog"
        "FileETag" "ForceType" "HostnameLookups" "IdentityCheck"
        "Include" "KeepAlive" "KeepAliveTimeout" "LimitInternalRecursion"
        "LimitRequestBody" "LimitRequestFields" "LimitRequestFieldSize" "LimitRequestLine"
        "LimitXMLRequestBody" "LogLevel" "MaxKeepAliveRequests" "NameVirtualHost"
        "Options" "Require" "RLimitCPU" "RLimitMEM"
        "RLimitNPROC" "Satisfy" "ScriptInterpreterSource" "ServerAdmin"
        "ServerAlias" "ServerName" "ServerPath" "ServerRoot"
        "ServerSignature" "ServerTokens" "SetHandler" "SetInputFilter"
        "SetOutputFilter" "TimeOut" "UseCanonicalName"
        ;; .htaccess tutorial
        "AddHandler" "AuthUserFile" "AuthGroupFile"
        ;; mod_rewrite
        "RewriteBase" "RewriteCond" "RewriteEngine" "RewriteLock" "RewriteLog"
        "RewriteLogLevel" "RewriteMap" "RewriteOptions" "RewriteRule"
        ;; mod_alias
        "Alias" "AliasMatch" "Redirect" "RedirectMatch" "RedirectPermanent"
        "RedirectTemp" "ScriptAlias" "ScriptAliasMatch")
      '(("%{\\([A-Z_]+\\)}" 1 font-lock-variable-name-face)
        ("\\b[0-9][0-9][0-9]\\b" . font-lock-constant-face)
        ("\\[.*\\]" . font-lock-type-face))
      '(".htaccess\\'")
      nil
      "Generic mode for Apache .htaccess files.")

Activation

First, test whether it works by calling M-x foo-mode if you defined a new generic mode called foo-mode. If that works, you might want to enable foo-mode for all files whose names match a certain pattern (regexp).

Traditionally, you would add an entry to ‘auto-mode-alist’ to do that. Here is an example which enables a fictive scala-mode for all files whose names end in ".scala":

    (add-to-list 'auto-mode-alist '(""\\.scala\\'" . scala-mode))

Using ‘define-generic-mode’ takes care of that for you, however. Note how the fourth argument, AUTO-MODE-LIST, allows you to specify the patterns filenames should match. There is no need for adding patterns to ‘auto-mode-alist’ as shown above, if you define new modes using ‘define-generic-mode’.

Predefined generic modes

Put this in your ~/.emacs file:

    (require 'generic-x)

Now you should have a lot more modes to choose from.

Note that the list depends on the operating system used. You can set the variables ‘generic-define-mswindows-modes’ and ‘generic-define-unix-modes’ before loading generic-x. That will make sure you get the respective defaults. Here’s a list so that a search of the wiki will find this page:

alias-generic-mode apache-conf-generic-mode apache-log-generic-mode astap-generic-mode bat-generic-mode default-generic-mode etc-fstab-generic-mode etc-passwd-generic-mode etc-services-generic-mode fvwm-generic-mode hosts-generic-mode ibis-generic-mode inetd-conf-generic-mode inf-generic-mode ini-generic-mode java-manifest-generic-mode java-properties-generic-mode javascript-generic-mode mailrc-generic-mode named-boot-generic-mode named-database-generic-mode rc-generic-mode reg-generic-mode resolve-conf-generic-mode rul-generic-mode samba-generic-mode show-tabs-generic-mode spice-generic-mode vrml-generic-mode x-resource-generic-mode

Problems

How does it work to use a face other than any of the font-lock-xxx faces?

This does not work:

    (define-generic-mode 'test-mode
     (list "# ")                      ;; orange   comment-list
     (list "test1")                   ;; blue     keyword-list
     (list (list "\\(HIGHLIGHT-THIS\\)" '(1 'info-menu-star))) ;; font-lock-list
     ()                        ;;  auto-mode-list
     ()                        ;; function-list
     "test-mode")

Does it interfere with packages that do more complex highlighting?

Yes it does. Generic mode is just like any other major mode. Simpler perhaps, but it certainly prevents any font-lock stuff by other (major) modes.

XEmacs compatibility

This doesn’t appear to work with XEmacs 21.1. Does anyone know how to do this with XEmacs?

More Power?

The next step of complexity is DerivedMode.


CategoryDotEmacs CategoryCode