From d3679eccadcbf9640f85b3012d617a060f9bed0e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 18:39:08 +0000 Subject: [PATCH] Support mixed face list format like (bold :foreground "green") Added tp--parse-face-list helper function to parse mixed face lists that contain both symbols and inline plist keys/values: - (bold :foreground "green") -> symbols: (bold), plist: (:foreground "green") - (bold (:foreground "green")) -> symbols: (bold), plist: (:foreground "green") - (:foreground "green") -> symbols: nil, plist: (:foreground "green") Updated tp--merge-face-values to use this helper when merging face lists. Added test case for mixed format to tp-test-tp-add-face-override-subprops. Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com> --- tp-tests.el | 15 +++++++++++++++ tp.el | 49 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/tp-tests.el b/tp-tests.el index 932f374..68e2e3e 100644 --- a/tp-tests.el +++ b/tp-tests.el @@ -3180,6 +3180,21 @@ the inserted text should be that string, not the source text." 'tp-text (propertize "vim" 'face '(:foreground "red"))))) (should (equal result "vim")) ;; Face should be (bold (:foreground "green")) - new overrides old + (let ((face-val (tp-at 0 'face result))) + (should (member 'bold (if (listp face-val) face-val (list face-val)))) + ;; Should have green, not red + (should (cl-some (lambda (f) + (and (listp f) + (keywordp (car-safe f)) + (equal (plist-get f :foreground) "green"))) + (if (and (listp face-val) (not (keywordp (car-safe face-val)))) + face-val + (list face-val)))))) + ;; Mixed format case: new (bold :foreground "green") with embedded (:foreground "red") + (let ((result (tp-add "emacs" 'face '(bold :foreground "green") + 'tp-text (propertize "vim" 'face '(:foreground "red"))))) + (should (equal result "vim")) + ;; Face should be (bold (:foreground "green")) - parsed correctly and new overrides old (let ((face-val (tp-at 0 'face result))) (should (member 'bold (if (listp face-val) face-val (list face-val)))) ;; Should have green, not red diff --git a/tp.el b/tp.el index 02687bd..91d99af 100644 --- a/tp.el +++ b/tp.el @@ -258,6 +258,42 @@ Scans the entire string, not just position 0." (and (stringp str) (not (null (object-intervals str))))) +(defun tp--parse-face-list (face-list) + "Parse a mixed face list into symbols and a plist. +FACE-LIST can be a mix of: +- Face symbols (like bold, italic) +- Face plists (like (:foreground \"red\")) +- Inline plist keys and values (like bold :foreground \"green\") + +Returns (SYMBOLS . PLIST) where SYMBOLS is a list of face symbols +and PLIST is the merged plist of all face attributes." + (let ((symbols nil) + (plist nil) + (i 0) + (len (length face-list))) + (while (< i len) + (let ((elem (nth i face-list))) + (cond + ;; Nested plist like (:foreground "red") + ((and (listp elem) (keywordp (car-safe elem))) + (setq plist (if plist (tp--deep-merge-plist plist elem) elem)) + (setq i (1+ i))) + ;; Inline keyword - consume key and value + ((keywordp elem) + (let ((key elem) + (val (nth (1+ i) face-list))) + (setq plist (if plist + (plist-put plist key val) + (list key val))) + (setq i (+ i 2)))) + ;; Face symbol + ((symbolp elem) + (push elem symbols) + (setq i (1+ i))) + ;; Something else - skip + (t (setq i (1+ i)))))) + (cons (nreverse symbols) plist))) + (defun tp--merge-string-props-into-plist (str props) "Merge text properties from string STR into PROPS plist. Properties from PROPS take precedence over those in STR. @@ -334,17 +370,14 @@ Returns the merged face value." (append face2 (list face1)))) ;; face1 is a plist - need to merge any plist in face2 with face1 ((and (listp face1) (keywordp (car-safe face1))) - ;; Extract plist and symbols from face2 - (let ((symbols nil) - (plist nil)) - (dolist (f face2) - (if (and (listp f) (keywordp (car-safe f))) - (setq plist (if plist (tp--deep-merge-plist plist f) f)) - (push f symbols))) + ;; Use tp--parse-face-list to handle mixed formats like (bold :foreground "green") + (let* ((parsed (tp--parse-face-list face2)) + (symbols (car parsed)) + (plist (cdr parsed))) ;; Merge face2's plist with face1, then prepend symbols (let ((merged-plist (if plist (tp--deep-merge-plist face1 plist) face1))) (if symbols - (append (nreverse symbols) (list merged-plist)) + (append symbols (list merged-plist)) merged-plist)))) ((listp face1) (append face2