Why does font-lock-mode work with various programming languages but it does not in text-mode?
font-lock-mode works fine in
text-mode, it's just that
text-mode does not initialise
font-lock-mode by setting
(info "(elisp) Font Lock Basics"):
There are several variables that control how Font Lock mode
highlights text. But major modes should not set any of these variables
directly. Instead, they should set ‘font-lock-defaults’ as a
buffer-local variable. The value assigned to this variable is used, if
and when Font Lock mode is enabled, to set all the other variables.
-- Variable: font-lock-defaults
This variable is set by modes to specify how to fontify text in
that mode. It automatically becomes buffer-local when set. If its
value is ‘nil’, Font Lock mode does no highlighting, and you can
use the ‘Faces’ menu (under ‘Edit’ and then ‘Text Properties’ in
the menu bar) to assign faces explicitly to text in the buffer.
(info "(elisp) Search-based Fontification"):
The variable which directly controls search-based fontification is
‘font-lock-keywords’, which is typically specified via the KEYWORDS
element in ‘font-lock-defaults’.
-- Variable: font-lock-keywords
The value of this variable is a list of the keywords to highlight.
Lisp programs should not set this variable directly. Normally, the
value is automatically set by Font Lock mode, using the KEYWORDS
element in ‘font-lock-defaults’. The value can also be altered
using the functions ‘font-lock-add-keywords’ and
‘font-lock-remove-keywords’ (see Customizing Keywords).
So the problem here is that
python-mode all initialise
font-lock-defaults to some non-
nil value with their custom fontification rules, but
text-mode doesn't (because it doesn't by itself have any fontifification rules).
Indeed, with the following your code should work (this is just for illustration, not recommended):
(add-hook 'text-mode-hook (lambda () (setq font-lock-defaults '(nil))))
So far so good. The real subtlety, though, lies in what
font-lock-add-keywords does. With a non-
nil argument, it actually modifies
font-lock-keywords-alist, not the variables
font-lock-mode uses to determine whether and how to do any fontification. This is because
font-lock-add-keywords implicitly expects the mode in question to have already initialised
By contrast, with a
font-lock-add-keywords forcefully initialises and reenables
font-lock-mode if it has to:
(when (and font-lock-mode
(not (or font-lock-keywords font-lock-defaults)))
;; The major mode has not set any keywords, so when we enabled
;; font-lock-mode it only enabled the font-core.el part, not the
;; font-lock-mode-internal. Try again.
(setq-local font-lock-defaults '(nil t))
This is kind of hinted at in the docstring of
A user-level keywords list is what a major mode or the user would
set up. Normally the list would come from `font-lock-defaults'.
through selection of a fontification level and evaluation of any
contained expressions. You can also alter it by calling
`font-lock-add-keywords' or `font-lock-remove-keywords' with MODE = nil.
So one simple workaround is to call
font-lock-add-keywords with a
nil argument, as shown in
(info "(emacs) Font Lock"):
"List of mode hooks in which to fontify \"FIXME\" keywords.")
'((t :foreground "Red" :weight bold :underline t))
"Default face for \"FIXME\" keywords.")
'((t :foreground "Dark Green" :inherit my-fixme-face))
"Face for \"NOTE\" keywords.")
(defun my-fixme-fontify ()
"Fontify \"FIXME\" keywords in the current buffer."
'(("\\<\\(TODO\\)" 1 'my-fixme-face t)
("\\<\\(NOTE\\)" 1 'my-note-face t))))
(dolist (hook my-fixme-hooks)
(add-hook hook #'my-fixme-fontify))
The only problem with this is that it applies not only to the mode hooks in
my-fixme-hooks, but also to all their derived modes. If this is undesirable, I think the simplest workaround is to set
font-lock-defaults as I showed at the start (but only if it's
Either way, I think the situation and documentation is confusing/subtle enough that I recommend reporting it upstream via
M-x report-emacs-bug RET. Even if
font-lock-add-keywords is behaving 100% as desired, it would be nice if the caveat in question were documented more clearly. TIA.