Search code examples
emacselispsyntax-highlightingfont-lockmajor-mode

Avoid font-locking interfering inside of comments


In my font-lock-defaults I have:

("\\(^\\| \\|\t\\)\\(![^\n]+\\)\n" 2 'factor-font-lock-comment)

The comment character is ! and this makes it so comments get the right face. This works mostly, except when there is a competing font-locked entity inside the comment, like a string (delimited by double quotes):

! this line is font-locked fine
! this one is "not" because "strings"

How do you get font-lock to understand that the comment is already font-locked fine and it doesn't need to try to font-lock any strings inside of it? The obvious way is to add ! to the comment starter class in the syntax table:

(modify-syntax-entry ?! "< 2b" table)

This solution is not possible because function names and other symbols containing ! are legal such as map! filter! and foo!bar. And adding ! would cause code containing such names to be highlighted incorrectly.


Solution

  • Generally, it's a bad idea to highlight comments using a font-lock keyword. It's better to use the syntactic phase for this.

    Even though the syntax table isn't powerful enough to describe the syntax of your language, it's still possible to highlight comments using in the syntactic font-lock phase. The solution is to provide a custom function to assign syntactic properties to the ! characters that should start a comment. This is done using the variable syntax-propertize-function.

    See the elisp manual for details. Also, this tutorial covers this in great detail.

    Update: The following is a simple example that define ! to be comment start character, but not within identifiers. A real world example might need a more refined way to check if something is an identifier.

    (defun exmark-syntax-propertize (start end)
      (funcall (syntax-propertize-rules
                ("[[:alnum:]_]\\(!\\)"
                 (1 "_")))
               start
               end))
    
    (defvar exmark-mode-syntax-table
      (let ((table (make-syntax-table)))
        (modify-syntax-entry ?\n ">   " table)
        (modify-syntax-entry ?!  "<   " table)
        table))
    
    (define-derived-mode exmark-mode prog-mode "!-Mark"
      "Major mode for !-mark."
      (set (make-local-variable 'syntax-propertize-function)
           'exmark-syntax-propertize))