0

For example, a buffer has some major mode and it does its own fontification.

If I turn on a minor mode A then it adds its own fontification which changes the existing fontification by changing the apperance of some characters which are already fontified by the major mode.

Then if I turn off the minor mode it removes its own fontification and for those characters which the major mode fontified originally the original fontification is restored.

Is it possible for a minor mode to change fontification in such a layer-like way, so that it overshadows the existing fontification, but this layer of fontification can be removed cleanly if the mode is turned off?

  • 1
    I have got the impression that this is an XY-problem. Why do you want to do what you ask for? Why does the way it is not work for you? – Tobias Mar 26 at 3:31
  • @Tobias: Here's an example: if you use orgmode then you know it uses dashes for table lines and it also colors the table with font lock. Suppose, I want to replace the dash character with a character for better visual appearance (the lines will be continuous) by a minor mode which shows tables with character when turned on, and with the default dashes when it's turned off. How can I implement this with font lock? How can one override orgmode's font lock rules and restore them if the mode is off? – Tom Mar 26 at 14:05
  • Yes, it's possible. I've published a number of such minor modes, e.g. github.com/Lindydancer/lisp-extra-font-lock – Lindydancer Mar 27 at 15:20
1

Yes it is possible to add the fontification of a minor mode to the existing fontification of the major mode.

You add font lock keywords with font-lock-add-keywords and remove them with font-lock-remove-keywords.

There are many possible structures for the keywords. See the doc for the variable font-lock-keywords for the full list.

One such structure is:

(MATCHER SUBEXP FACENAME [OVERRIDE])

  • MATCHER can be a regexp or a function matching the keyword; The function is called with point at the beginning of the region to be fontified and it receives the end of that region as argument. The function should set the match-data like re-search-forward
  • SUBEXP is the number of the sub-expression of MATCHER that should be fontified
  • FACENAME can be an expression returning the face name to use or a list (face FACE PROP1 VAL1 PROP2 VAL2 ...); The second form is handy to set text properties on the keyword.
  • OVERRIDE Overrides all previous fontification if set to t. Other allowed values are prepend and append with the obvious meaning.

Normally, you would use the OVERRIDE flag with value t, prepend, or append to overwrite or combine the fontification of the major and the minor mode.

If the rule for the fontification of the match is more complicated you can also set the face and the text properties directly in MATCHER. In this case add MATCHER as follows to font-lock-keywords:

(font-lock-add-keywords nil '((MATCHER)))

That technique is often used by org-mode. See, e.g., org-fontify-entities.

The following Elisp code gives an example for a minor mode that adds display text properties to org tables for a fancier presentation:

(defun org+-fancy-table-lines (limit)
  "Draw org tables with fancy lines.
This is a face-lock matcher with arg LIMIT."
  (let ((p (point))
    hline-p
    found)
    (forward-line 0)
    (while (re-search-forward "^[[:space:]]*|" limit t)
      (backward-char)
      (let* ((hline-p (looking-at org-table-hline-regexp))
         (re (if hline-p
             "[|+-]"
           "|"))
         (line-limit (min limit (line-end-position))))
    (when (< (point) p) (goto-char p))
    (while (re-search-forward re line-limit 'noError)
      (let ((b (1- (point)))
        (e (point)))
        (setq found t)
        (cl-case (char-before)
          (?-
           (skip-chars-forward "-")
           (setq e (point))
           (put-text-property b e 'display (make-string (- e b) ?─)))
          (?+
           (put-text-property b e 'display "┼"))
          (?|
           (if hline-p
           (if (eq (char-before b) ?-)
               (put-text-property b e 'display "┤")
             (put-text-property b e 'display "├"))
         (put-text-property b e 'display "│"))))))))
    found))

(defconst org+-fancy-table-keywords '((org+-fancy-table-lines))
  "Font lock keywords for `org+-fancy-table-mode'.")

(define-minor-mode org+-fancy-table-mode
  "Display tables with fancy lines."
  :lighter " ─"
  (if org+-fancy-table-mode
      (progn
    (font-lock-add-keywords nil org+-fancy-table-keywords 'at-end)
    (make-local-variable 'font-lock-extra-managed-props)
    (cl-pushnew 'display font-lock-extra-managed-props))
    (font-lock-remove-keywords nil org+-fancy-table-keywords))
  (font-lock-flush))

Note, that here we add display to the font-lock-extra-managed-props list. That gives font-lock the control over those text properties. It is a quite drastic measure and may interfere with other commands directly using the display property.

| improve this answer | |
  • Really nice, thanks! Only the top and the bottom of table (marked by an empty line or a # line) is not completely OK, because at the bottom instead of this i.imgur.com/LI3XQPw.png it should look like this: i.imgur.com/bqtgqDj.png And the same goes for the top. – Tom Mar 26 at 19:45
  • @Tom I usually do not put hlines at top or bottom. So I didn't notice. – Tobias Mar 31 at 14:58
  • you should put this minor mode on Github/Melpa. It really makes org tables look better. Many orgmode users would appreciate it. – Tom Apr 18 at 16:42

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.