Add support for deeply nested sub-properties and multiple key extraction in tp-get

Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-12-14 15:25:53 +00:00
parent b31077e486
commit 3f7f8cd9b9
4 changed files with 75 additions and 1 deletions

View File

@ -312,6 +312,12 @@ For range and entire string queries, returns a list of `(START END VALUE)` inter
(tp-get START END '(PROPERTY) OBJECT) (tp-get START END '(PROPERTY) OBJECT)
(tp-get START END '(PROPERTY SUB-KEY ...) OBJECT) (tp-get START END '(PROPERTY SUB-KEY ...) OBJECT)
;; Range with deeply nested property path
(tp-get START END '(PROPERTY SUB-KEY SUB-SUB-KEY ...) OBJECT)
;; Range extracting multiple keys from nested property
(tp-get START END '(PROPERTY SUB-KEY (KEY1 KEY2 ...)) OBJECT)
;; Range - all properties (returns list of intervals) ;; Range - all properties (returns list of intervals)
(tp-get START END) (tp-get START END)
(tp-get START END OBJECT) (tp-get START END OBJECT)
@ -320,6 +326,7 @@ For range and entire string queries, returns a list of `(START END VALUE)` inter
(tp-get STRING) (tp-get STRING)
(tp-get STRING PROPERTY) (tp-get STRING PROPERTY)
(tp-get STRING PROPERTY SUB-KEY ...) (tp-get STRING PROPERTY SUB-KEY ...)
(tp-get STRING PROPERTY SUB-KEY '(KEY1 KEY2 ...))
(tp-get STRING '(PROPERTY SUB-KEY ...)) (tp-get STRING '(PROPERTY SUB-KEY ...))
``` ```
@ -348,6 +355,13 @@ For range and entire string queries, returns a list of `(START END VALUE)` inter
;; Get with property path as list ;; Get with property path as list
(tp-get 5 20 '(face :underline :style) my-string) ; => ((5 20 wave)) (tp-get 5 20 '(face :underline :style) my-string) ; => ((5 20 wave))
;; Get deeply nested property from entire string
(tp-get str 'face :underline :color) ; => ((0 5 "green") (6 11 "yellow"))
;; Get multiple keys from nested property
(tp-get str 'face :underline '(:color :style))
;; => ((0 5 (:color "green" :style wave)) (6 11 (:color "yellow" :style line)))
;; Get all properties from range ;; Get all properties from range
(tp-get 1 10) ; => ((1 6 (face bold help-echo "test"))) (tp-get 1 10) ; => ((1 6 (face bold help-echo "test")))

View File

@ -311,6 +311,12 @@ tp.el 提供三个主要的属性设置函数,每个有不同的语义:
(tp-get START END '(PROPERTY) OBJECT) (tp-get START END '(PROPERTY) OBJECT)
(tp-get START END '(PROPERTY SUB-KEY ...) OBJECT) (tp-get START END '(PROPERTY SUB-KEY ...) OBJECT)
;; 范围 - 深层嵌套属性路径
(tp-get START END '(PROPERTY SUB-KEY SUB-SUB-KEY ...) OBJECT)
;; 范围 - 从嵌套属性中提取多个键
(tp-get START END '(PROPERTY SUB-KEY (KEY1 KEY2 ...)) OBJECT)
;; 范围 - 所有属性(返回区间列表) ;; 范围 - 所有属性(返回区间列表)
(tp-get START END) (tp-get START END)
(tp-get START END OBJECT) (tp-get START END OBJECT)
@ -319,6 +325,7 @@ tp.el 提供三个主要的属性设置函数,每个有不同的语义:
(tp-get STRING) (tp-get STRING)
(tp-get STRING PROPERTY) (tp-get STRING PROPERTY)
(tp-get STRING PROPERTY SUB-KEY ...) (tp-get STRING PROPERTY SUB-KEY ...)
(tp-get STRING PROPERTY SUB-KEY '(KEY1 KEY2 ...))
(tp-get STRING '(PROPERTY SUB-KEY ...)) (tp-get STRING '(PROPERTY SUB-KEY ...))
``` ```
@ -347,6 +354,13 @@ tp.el 提供三个主要的属性设置函数,每个有不同的语义:
;; 使用列表形式的属性路径 ;; 使用列表形式的属性路径
(tp-get 5 20 '(face :underline :style) my-string) ; => ((5 20 wave)) (tp-get 5 20 '(face :underline :style) my-string) ; => ((5 20 wave))
;; 从整个字符串获取深层嵌套属性
(tp-get str 'face :underline :color) ; => ((0 5 "green") (6 11 "yellow"))
;; 从嵌套属性中获取多个键
(tp-get str 'face :underline '(:color :style))
;; => ((0 5 (:color "green" :style wave)) (6 11 (:color "yellow" :style line)))
;; 获取范围内的所有属性 ;; 获取范围内的所有属性
(tp-get 1 10) ; => ((1 6 (face bold help-echo "test"))) (tp-get 1 10) ; => ((1 6 (face bold help-echo "test")))

View File

@ -1239,5 +1239,38 @@ Returns list of (START END VALUE) intervals."
(should (equal (car intervals) '(0 5 bold))) (should (equal (car intervals) '(0 5 bold)))
(should (equal (cadr intervals) '(12 17 italic)))))) (should (equal (cadr intervals) '(12 17 italic))))))
(ert-deftest tp-test-get-deeply-nested-property ()
"Test tp-get with deeply nested property path."
(let ((str (copy-sequence "Hello World")))
(put-text-property 0 5 'face '(:foreground "red" :underline (:color "green" :style wave)) str)
(put-text-property 6 11 'face '(:foreground "blue" :underline (:color "yellow" :style line)) str)
;; Test deeply nested single key from entire string
(let ((intervals (tp-get str 'face :underline :color)))
(should (= (length intervals) 2))
(should (equal (caddr (car intervals)) "green"))
(should (equal (caddr (cadr intervals)) "yellow")))
;; Test range with deeply nested key
(let ((intervals (tp-get 0 7 '(face :underline :color) str)))
(should (= (length intervals) 2))
(should (equal (caddr (car intervals)) "green")))))
(ert-deftest tp-test-get-multiple-nested-keys ()
"Test tp-get with multiple keys from nested property."
(let ((str (copy-sequence "Hello World")))
(put-text-property 0 5 'face '(:foreground "red" :underline (:color "green" :style wave)) str)
(put-text-property 6 11 'face '(:foreground "blue" :underline (:color "yellow" :style line)) str)
;; Test extracting multiple keys from entire string
(let ((intervals (tp-get str 'face :underline '(:color :style))))
(should (= (length intervals) 2))
(let ((val1 (caddr (car intervals)))
(val2 (caddr (cadr intervals))))
(should (equal (plist-get val1 :color) "green"))
(should (eq (plist-get val1 :style) 'wave))
(should (equal (plist-get val2 :color) "yellow"))
(should (eq (plist-get val2 :style) 'line))))
;; Test range with multiple keys
(let ((intervals (tp-get 0 7 '(face :underline (:color :style)) str)))
(should (= (length intervals) 2)))))
(provide 'tp-ert-tests) (provide 'tp-ert-tests)
;;; tp-ert-tests.el ends here ;;; tp-ert-tests.el ends here

15
tp.el
View File

@ -398,13 +398,26 @@ Return the modified object (string) or region (START . END) for buffer."
(defun tp--get-nested (value path) (defun tp--get-nested (value path)
"Get nested value from VALUE following PATH. "Get nested value from VALUE following PATH.
PATH is a list of keys/symbols to traverse nested structures. PATH is a list of keys/symbols to traverse nested structures.
Supports plists, alists, and special display property formats." Supports plists, alists, and special display property formats.
If an element in PATH is a list of keys, extract those keys from the
current value and return a plist with those keys.
Example: (tp--get-nested \\='(:a 1 :b 2 :c 3) \\='((:a :b))) => (:a 1 :b 2)"
(if (null path) (if (null path)
value value
(let* ((key (car path)) (let* ((key (car path))
(rest (cdr path)) (rest (cdr path))
(next-value (next-value
(cond (cond
;; Key is a list of keys - extract multiple keys from value
((and (listp key) (not (null key)))
(when (and (listp value) (keywordp (car value)))
(let ((result nil))
(dolist (k (reverse key))
(let ((v (plist-get value k)))
(when v
(setq result (plist-put result k v)))))
result)))
;; Value is a plist with keyword keys ;; Value is a plist with keyword keys
((and (listp value) (keywordp (car value))) ((and (listp value) (keywordp (car value)))
(plist-get value key)) (plist-get value key))