2

I found this piece of code to change org-mode list elements from dashes (-) to circles (•):

(font-lock-add-keywords 'org-mode
                          '(("^ *\\([-]\\) "
                             (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•"))))))

It works wonderfully, but I cannot figure out how to change the color of the circle to red. I tried reading the documentation of font-lock-add-keywords, but the syntax seems quite cryptic to me. Any help, pointers towards more information, or suggestions on how to achieve what I want would be most welcome.

EDIT:

Based on NickD's comment and on a blog post by Kitchin on colorizing strings, I ended up with the following:

(font-lock-add-keywords
   'org-mode
   '(("^ *\\([-]\\) "
      (0 (prog1 () (compose-region
                    (match-beginning 1)
                    (match-end 1)
                    (propertize "•" 'font-lock-face '(:foreground "red"))))))))

But that doesn't seem to do the trick. I guess the compose-region function must be ignoring any text properties, as inserting the propertized string directly in the buffer does work.

Any help is highly appreciated.

Daniel
  • 3,563
  • 16
  • 41
  • Add properties to the "•" string - see e.g. [this question](https://emacs.stackexchange.com/questions/59794/colored-string-in-mode-line). – NickD Jul 29 '20 at 23:44
  • Thanks @NickD. I did add properties to the string, but when inserted in the org-buffer, it seems to be fontified according to whatever "higher priority" face org determines the current location/context to have. If I insert it within an org-link, for example, it'll be coloured according to org-link, and not its inherent property. Would you know of a way around that? – Daniel Jul 30 '20 at 12:37
  • Sorry, I don't. – NickD Jul 30 '20 at 14:10

2 Answers2

2

The Elisp code below shows how it can be done.
First, some notes:

  1. You can use the display text property to replace characters visually with other text.
  2. If you want to display the bullet in red, propertize the replacement text. You can use text properties in strings for that purpose.
  3. Use the OVERRIDE flag prepend or append for the font lock keyword. This is the most sensible choice if you modify an existing mode through personal configuration. It ensures that the fontification of the original mode and your own fontification is combined and none of the two overrides the other completely. If you do not add this OVERRIDE flag and prepend your own fontification to that one of the original mode your fontification prevents tweaks by the original mode. If you append your fontification without OVERRIDE it has no effect if the original mode fontifies the same stretch of text.
  4. Add the keyword at the end of font-lock-keywords so that your fontification is not overridden by other rules (HOW argument of font-lock-add-keywords).
  5. Note, a regexp as matcher is not sufficient since it also gives false positives. For an example, matches in source blocks are also highlighted. That is the reason why I introduced the matcher function org+-match-item-marker.
(require 'org-element)
(defun org+-match-item-marker (bound)
  "Match the bullet of itemizations."
  (and
   (re-search-forward "^ *\\(-\\) " bound t)
   (save-match-data
     (save-excursion
       (goto-char (match-end 1))
       (eq (org-element-type (org-element-at-point)) 'item)))))

(font-lock-add-keywords
 'org-mode
 '((org+-match-item-marker ;; matcher
    (1 ;; subexpression
     '(face default display #("•" 0 1 (face (:foreground "red")))) ;; face
     append ;; override
     )))
 t) ;; how
Tobias
  • 32,569
  • 1
  • 34
  • 75
1

This is a complementary answer to the one posted by Tobias. I noticed that the following worked for me too:

(setq red-unicode-dot (propertize "•" 'font-lock-face '(:foreground "red")))
  (font-lock-add-keywords
   'org-mode
   '(("^ *\\([-]\\) "
      (0 (prog1 () (put-text-property
                    (match-beginning 1)
                    (match-end 1)
                    'display red-unicode-dot))))))
NickD
  • 27,023
  • 3
  • 23
  • 42
Daniel
  • 3,563
  • 16
  • 41