I'm working on a project in Racket, and the syntax highlighting in Emacs' racket-mode is becoming an issue for me. Racket is a highly modular language, and one of the modules that I am not using contains builtins that I want to use as identifiers. I want some way to disable the syntax highlighting for that module, but it's difficult: the racket-mode package apparently uses Racket code to dynamically dump all of the keywords and builtins from several modules of the language into a racket-keywords-and-builtins.el to then be passed through regexp-opt in racket-font-lock.el to produce the actual syntax highlighting rules.

The element of font-lock-keywords that I want change is (#21="long-ass-regular-expression" . font-lock-builtin-face). It turns out that the variables from which the mode has constructed the font-lock-keywords list are still in scope, so my tentative plan is as follows:

  • Use regexp-opt to reconstruct the regular expression from the list that was used to generate it (in this case, racket-builtins-2-of-2); bind this to a variable racket-builtins-regexp
  • Take the set difference of racket-builtins-2-of-2 and a list of the builtins I don't want; pass this through regexp-opt; bind it to a variable racket-builtins-regexp-prime
  • Replace the syntax highlighting rule in question with a modified one as follows:
(setq font-lock-keywords
      (append (remove (assoc racket-builtins-regexp font-lock-keywords)
              `((,racket-builtins-regexp-prime . font-lock-builtin-face))))

This feels like a kludge. Is there a simpler/more idiomatic way of doing what I'm trying to do?

  • "uses Racket code to dynamically dump all of the keywords and builtins from several modules of the language into a racket-keywords-and-builtins.el" doesn't seem dynamic to me -- I tried installing racket-mode and it came pre-packaged with that file. It does have comments saying "This list was generated using keywords.rkt" but that process is evidentially not dynamic as far as Emacs is concerned; so despite the "don't edit individual items here!" you could do exactly that (either literally edit the file, or else load it in your config and update the lists before anything else loads it).
    – phils
    Nov 18 '21 at 22:56
  • Ah, but you'd need to re-byte-compile racket-font-lock.el after that to make the changes take effect, so that's less tidy.
    – phils
    Nov 18 '21 at 23:01
  • I think this seems more like an upstream feature request for racket-mode, tbh. You could discuss that with the maintainers, and work on a patch if they think it's a good idea.
    – phils
    Nov 18 '21 at 23:17

I agree with the @phils that this should be address on the racket side. A good solution would be a variable containing a list of keywords that could be converted to a regexp when the mode is activated.

Until then, you can use font-lock-add-keywords and font-lock-remove-keywords if you want a cleaner solution. (This assumes that the position of the rule within the keyword list doesn't matter.)


You can partially override highlighting rules by putting your own rules first. Unless otherwise specified, each highlighting rule only applies to text that hasn't been highlighted by a previous rule. So you if you put an early rule that specifies that certain identifiers should be in the default face, those identifiers won't be highlighted as keywords.

Here's some very lightly tested code that implements this for Racket mode.

(defcustom racket-disabled-keyword-list
  '("let" "example")
  "List of words that will not be fontified in Racket mode.

This disables highlighting of words that would normally be considered
types, builtins, etc."
  :type '(list string)
  :safe '(lambda (x)
       (and (listp x)
        (null (cdr (last x)))
          (seq-every-p (lambda (s)
                 (string-match "\\`\\(\\w\\|\\s_\\)+\\'" s))

(defun racket-font-lock-disable-some-keywords ()
  (when racket-disabled-keyword-list
    (setq font-lock-defaults
      `(((,(regexp-opt racket-disabled-keyword-list 'symbols) . 'default)
         ,@(car font-lock-defaults))
        ,@(cdr font-lock-defaults)))
(add-hook 'racket-mode-hook 'racket-font-lock-disable-some-keywords)

(Why is the call to hack-local-variables needed here? In my testing, the local variables were not yet applied when the major mode hook ran, and I can't figure out why. This call may have negative side effects.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.