0

Text with properties can have faces assigned, how can one face be replaced with another?

An example use case for this is highlighting some text or it's background. Seeing as Emacs doesn't have the ability to alpha-overlay colors over of existing text, something similar could be achieved by blending colors of existing faces.

Given some propertized text extracted from a buffer (with buffer-substringfor example), it should be possible inspect the face properties and swap faces for alternative versions (which could be generated as needed).

4
  • What do you mean by the "replacement" of one face by another? Please clarify your question. Preferably provide a recipe or show what behavior/effect you're seeking.
    – Drew
    Nov 25, 2021 at 16:15
  • One can't guess from your vague question what you're requesting. Since you provide your own answer, the question becomes clear only from that answer. Please provide such details in the question itself.
    – Drew
    Nov 25, 2021 at 16:17
  • Added an example which roughly matches the use-case I was looking into.
    – ideasman42
    Nov 25, 2021 at 23:07
  • Updated question to make it clear what
    – ideasman42
    Nov 25, 2021 at 23:19

1 Answer 1

1

This is a utility function to replace faces using an assosiation-list,

This example function only works when faces are referenced as atoms and should be expanded for other cases for full support.

(defun text-property-face-replace (text face-replace-alist)
  "Replace faces in TEXT using FACE-REPLACE-ALIST."
  (let ((pos-beg 0)
        (pos-end (length text)))
    (while (< pos-beg pos-end)
      (let ((text-props (text-properties-at pos-beg text)))
        (let ((prop-iter (memq 'face text-props)))
          (when prop-iter
            (let ((value-src (car (cdr prop-iter))))
              (let ((value-dst
                     (cond
                      ((listp value-src)
                       nil) ;; TODO: support `plists' and other complex cases.
                      (t
                       (assq value-src face-replace-alist)))))
                (when value-dst
                  (setcdr prop-iter (cons (cdr value-dst) (cdr (cdr prop-iter))))))))))
      (setq pos-beg (next-property-change pos-beg text pos-end)))))

Example use:

(defvar text-replace-table
  '((font-lock-doc-face . font-lock-warning-face)
    (font-lock-comment-face . font-lock-warning-face)
    (font-lock-comment-delimiter-face . font-lock-warning-face)
    (font-lock-constant-face . font-lock-warning-face)))

(defun text-replace-example ()
  "Show replaced face for the current line."
  (interactive)
  (let ((line (buffer-substring (line-beginning-position) (line-end-position))))
    (message "Replaced text: %s" (text-property-face-replace line text-replace-table))))
2
  • 1
    The value of face is either an atom (which you handle) or a property list (a.k.a. plist), i.e. a list where every second element is a key and every second is a value, e.g. (face font-lock-warning-face :weight bold). Your code walks through the plist element by element. For faces like font-lock-warning-face changes are low that this will cause a problem, but if you replace the face bold you could end up with something like (face font-lock-warning-face :weight my-new-face) which isn't what you want. Nov 25, 2021 at 7:49
  • Thanks for pointing this out, removed this logic and marked is as a TODO since I was only testing faces as atoms when trying this out.
    – ideasman42
    Nov 25, 2021 at 23:14

Your Answer

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

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