Search code examples
emacshighlightfont-lock

Emacs highlighting: how to deal with highlighting messed up by unusual usage of special characters?


Problem:

In Emacs configuration modes (e.g. conf-xdefaults-mode or conf-space-mode), some special characters are used in unusual ways, for instance when they define keybindings. This messes up the highlighting for the rest of the buffer.

Example:

The ranger rc.conf file uses conf-space-mode which greatly helps its readability. But lines such as:

map #  console shell -p%space
map "<any>  tag_toggle tag=%any

mess up the highlighting since # usually defines a comment and is followed by font-lock-comment-face until the end of the line and " defines the beginning of a string and is followed by font-lock-string-face until it encounters a closing quote.

Escaping those characters is not an option because it would prevent them from defining the keybindings.

Possible solution:

The best solution I can think of is to fiddle with font lock settings for those configuration modes to remove the highlighting after those special characters. But I will then loose the proper highlighting after those characters when it is suitable.

A compromise could be to keep highlighting after # since this only messes up one line and there are a lot of comments in those configuration files, while removing the highlighting after single and double quotes since those mess up the entire rest of the buffer and strings are not so common in configuration files.

Question:

What is the proper way to deal with these situations?

Is there a way to reset the highlighting at a point in the buffer? or to insert a character which would affect the highlighting (to fix it) without affecting the code? or is there a way to "escape" some characters for the highlighting only without affecting the code?


Solution

  • The easy way

    It's probably easiest to just live with it but keep things constrained. Here, I took ranger's default rc.conf and re-arranged some of the font-lock errors.

    example font-lock errors in ranger's rc.conf file

    Let's ignore the blue "map" for now. We have two visible font-lock errors. The map #... is font-locking as a comment, and the map "... font-locking as a string to the end of the buffer. The first error is self-constrained. Comments end at the end of the line. The second error we constrain by adding a comment. (I don't know if ranger would accept comments in the middle of the line, so I'm only using beginning-of-line comments here.)

    constraining the first string-delimiter error

    The second error is now constrained to one line, but a couple more errors have popped up. Quickly adjusting these we get.

    our example errors, fixed

    This is something that I could live with, as I'm not in conf files all day long (as opposed to say, source code.) It would be even neater if our new "comments" could be included on the same line.

    The hard way

    You'll want to use Emacs font-lock-add-keywords. Let's get back to that blue map in the first image. It's rendering blue because conf-space-mode thinks that a string, followed by any amount of whitespace, followed by an opening brace should be rendered in font-lock-type-face (the actually regexp that triggers this is ^[_space__tab_]*\\(.+?\\)[_space__tab_\n]*{[^{}]*?$ where _space_ and _tab_ are actual space and tab characters.)

    We can override this in a simple fashion by evaluating

    (font-lock-remove-keywords
     'conf-space-mode
     '(("^\\<\\(map\\)\\>" 1 font-lock-variable-name-face)))
    

    and reloading the buffer with C-x C-v RET. Now, every time that the word "map" appears at the beginning of a line it's rendered as font-lock-variable-name-face (yellow in our example.)

    You can make this change permanent by adding a hook to your init file.

    (add-hook 'conf-space-mode-hook (lambda ()
      (font-lock-remove-keywords
       nil
       '(("^\\<\\(map\\)\\>" 1 font-lock-variable-name-face)))))
    

    This method doesn't appear to work for your comment (#) and string (' ") delimiters as they're defined in the syntax table. Modifying the syntax table to provide special cases is probably more effort than it's worth.