2

So I've been trying to add custom syntax highlighting for digits using font-lock-add-keywords and regex.

I've been looking at all the Emacs resources to do this, and while I have been able to successfully define a face, font-lock-add-keywords has not been working for any apparent reason.

I have even copied the examples from different sources and directly tried to get them to work with no apparent success. I can't seem to figure out what's wrong with my code. I'm not receiving any errors, but when I try to eval the font-lock-add-keywords (with C-x C-e), it prints nil in the *Messages* buffer.

Update

I took Gilles advice and created a minor mode, set font-lock-add-keywords to that custom minor mode, and used a quote for the face. While I can see the minor mode in the mode line and know that it's working, font-lock-add-keywords still does not seem to work. Here is my updated code:

(defgroup gio-group nil
  "Group for customization"
  :prefix "gio-")

(defface gio-highlight-numbers-face
  '((t :inherit (default)
       :foreground "#ffff00"))
  "Face for numbers"
  :group 'gio-group )

(define-minor-mode gio-minor-mode
  "Minor mode for customizaion"
  :init-value t
  :lighter " GioMode"
  :global t
  :group 'gio-group)

(font-lock-add-keywords 'gio-minor-mode '(("[0-9]+" . 'gio-highlight-numbers-face)))

I'm running GNU Emacs 26.3 (build 1, x86_64-w64-mingw32) on Windows 10. Any help is much appreciated! Thank you!

5
  • Does it work if you evaluate (font-lock-add-keywords nil '(("[0-9]+" . custom-faces-highlight-numbers-face))) in the buffer that you are trying to fontify? That's the same expression as in your code except that nil is passed in for the MODE parameter. Do C-h v font-lock-add-keywords for more details. I believe that passing font-lock-mode for the MODE parameter is wrong, but I'm no expert and YMMV.
    – NickD
    Aug 18, 2020 at 16:36
  • @NickD It does work when I run that, either in scratch or directly in my init.el file. It also works in my init.el file when I set the mode to 'emacs-lisp-mode, but not the custom minor mode I made.
    – Gyo
    Aug 19, 2020 at 1:21
  • By the way, depending on what your real use case is, you might benefit from existing modes rather than doing it yourself. Suggested reading: emacswiki.org/emacs/ShowWhiteSpace#HighlightChars, emacswiki.org/…, and perhaps other searches including “Emacs” and “highlight”. Aug 19, 2020 at 7:01
  • 1
    @Gyo - You can't add keywords to a minor mode. Instead, the body of the minor mode should add it to "nil" (i.e. the current major mode). To make it a proper minor mode you should also add code to remove the keyword when the mode is disabled. An example of this is github.com/Lindydancer/ruby-extra-highlight Aug 19, 2020 at 8:23
  • @Lindydancer I ended up discovering that through further research (although I can promise you I had done much googling before posting my question), and I'm going to put my fix in an answer. Thank you for the example though, I'll definitely take a look!
    – Gyo
    Aug 19, 2020 at 9:55

2 Answers 2

2

With further research and some help from comments from Gilles and Lindydancer, I discovered that you cannot add Font Lock keywords to minor modes. Instead, you have to add and remove the keywords to the major mode when the minor mode is loaded and unloaded.

Here is the working code that highlights the keywords using a minor mode:

(defgroup gio-group nil
  "Group for customization"
  :prefix "gio-")

(defface gio-highlight-numbers-face
  '((t :inherit (default)
       :foreground "#ffff00"))
  "Face for numbers"
  :group 'gio-group )

(defvar gio-keywords '(("\\(\\b\\|[-]\\)\\([-]?\\([0-9]+\\)\\(\\.?[0-9]\\)*\\)\\b" . 'gio-highlight-numbers-face)) ;; Integers & Decimals
  "Keywords for gio-minor-mode highlighting")

(define-minor-mode gio-minor-mode
  "Minor mode for customization"
  :init-value 1
  :lighter " GioMode"
  :group 'gio-group
  (when (bound-and-true-p gio-minor-mode)
    (font-lock-add-keywords nil gio-keywords)
    (font-lock-fontify-buffer))
  (when (not (bound-and-true-p gio-minor-mode))
    (font-lock-remove-keywords nil gio-keywords)
    (font-lock-fontify-buffer)))

(define-globalized-minor-mode gio-global-minor-mode gio-minor-mode gio-minor-mode :group 'gio-group)
(gio-global-minor-mode 1)
0

font-lock-add-keywords adds keywords for a specific major mode, such as Lisp mode or C mode or HTML mode. Since font-lock-mode is not a major mode (font-lock-add-keywords 'font-lock-mode …) never has any effect.

If you really want font lock keywords that apply in every major mode, you need to define a minor mode. That's how Whitespace mode works. A lot of Whitespace mode is more complicated than what you need. You could perhaps add a “digit highlight” setting to whitespace mode, but it might be easier to define your own mode. The convenience function define-minor-mode helps, but you still need to add and remove the keywords manually when the mode is turned on or off, by calling (font-lock-add-keywords nil …) and (font-lock-remove-keywords nil …). See Can I add highlighting in a minor mode? for instructions.

In addition, there's a gotcha in how faces work: a face name is a symbol which is not a variable. The (MATCHER . FACESPEC) form of font-lock-keywords requires FACESPEC to be an expression whose value specifies a face. custom-faces-highlight-numbers-face doesn't work because it isn't a valid expression since the symbol custom-faces-highlight-numbers-face is not bound. There are two solutions to this. The traditional solution is to define a variable with the same name as the face; that's how it works for the faces that Font Lock mode defines, but

Note that in new code, in the vast majority of cases there is no need to create variables that specify face names. Simply using faces directly is enough. Font-lock is not a template to be followed in this area.

The recommended but cumbersome solution is to quote the symbol.

(font-lock-add-keywords 'gyo-highlight-numbers-mode '(("[0-9]+" . 'custom-faces-highlight-numbers-face)))
3
  • Thanks for your response! I took all of your advice and tried everything out, but it still doesn't seem to be working... I edited my post and put in the updated code. Thanks again!
    – Gyo
    Aug 19, 2020 at 0:48
  • @Gyo My bad, it's actually less automatic than I thought, you have to write the code to add and remove the keywords in the minor mode definition. Font lock is designed to do syntax highlighting. What you want to do is possible but doesn't come naturally. Aug 19, 2020 at 7:00
  • No worries! After doing further research I discovered that as well, and I'll put it in an answer. Thanks for taking the time to answer!
    – Gyo
    Aug 19, 2020 at 9:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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