From 3f7f8cd9b94cc4cb25a32a494793175607ae2dcd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 15:25:53 +0000 Subject: [PATCH] Add support for deeply nested sub-properties and multiple key extraction in tp-get Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com> --- README.md | 14 ++++++++++++++ README_CN.md | 14 ++++++++++++++ tp-tests.el | 33 +++++++++++++++++++++++++++++++++ tp.el | 15 ++++++++++++++- 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 437160c..6a041cf 100644 --- a/README.md +++ b/README.md @@ -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 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) (tp-get START END) (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 PROPERTY) (tp-get STRING PROPERTY SUB-KEY ...) +(tp-get STRING PROPERTY SUB-KEY '(KEY1 KEY2 ...)) (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 (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 (tp-get 1 10) ; => ((1 6 (face bold help-echo "test"))) diff --git a/README_CN.md b/README_CN.md index 30f8e67..e736d27 100644 --- a/README_CN.md +++ b/README_CN.md @@ -311,6 +311,12 @@ tp.el 提供三个主要的属性设置函数,每个有不同的语义: (tp-get START END '(PROPERTY) 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 OBJECT) @@ -319,6 +325,7 @@ tp.el 提供三个主要的属性设置函数,每个有不同的语义: (tp-get STRING) (tp-get STRING PROPERTY) (tp-get STRING PROPERTY SUB-KEY ...) +(tp-get STRING PROPERTY SUB-KEY '(KEY1 KEY2 ...)) (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 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"))) diff --git a/tp-tests.el b/tp-tests.el index fd9aa28..ef0145a 100644 --- a/tp-tests.el +++ b/tp-tests.el @@ -1239,5 +1239,38 @@ Returns list of (START END VALUE) intervals." (should (equal (car intervals) '(0 5 bold))) (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) ;;; tp-ert-tests.el ends here diff --git a/tp.el b/tp.el index 91463d9..2aa7871 100644 --- a/tp.el +++ b/tp.el @@ -398,13 +398,26 @@ Return the modified object (string) or region (START . END) for buffer." (defun tp--get-nested (value path) "Get nested value from VALUE following PATH. 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) value (let* ((key (car path)) (rest (cdr path)) (next-value (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 ((and (listp value) (keywordp (car value))) (plist-get value key))