From 62b5e475487110117362c0a40ef4a2f7c01369d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Dec 2025 02:53:58 +0000 Subject: [PATCH] Remove internal APIs from public interface, delete tp-remove-list, fix tp-remove nested sub-property bug, add links in API Quick Reference Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com> --- README.md | 146 +++++++++++++++++---------------------------------- README_CN.md | 146 +++++++++++++++++---------------------------------- tp-tests.el | 62 +++++++++++++--------- tp.el | 35 ++++++------ 4 files changed, 151 insertions(+), 238 deletions(-) diff --git a/README.md b/README.md index cd5940b..582af37 100644 --- a/README.md +++ b/README.md @@ -63,94 +63,90 @@ A complete overview of all tp.el functions organized by category: #### Core Property Functions | Function | Description | |----------|-------------| -| `tp-set` | Set text properties (replaces specified properties only) | -| `tp-reset` | Replace ALL text properties | -| `tp-add` | Add/merge properties with deep merge support | -| `tp-set-face` | Set only the face property | -| `tp-set-display` | Set only the display property | -| `tp-get` | Get property value(s) from position or range | -| `tp-get-sub` | Get nested sub-property value | -| `tp-put-sub` | Set nested sub-property value | -| `tp-remove-sub` | Remove nested sub-property | -| `tp-at` | Get all properties at a position | -| `tp-remove` | Remove a property or sub-property | -| `tp-remove-list` | Remove multiple properties at once | -| `tp-clear` | Clear all text properties from a region | +| [`tp-set`](#tp-set---set-text-properties) | Set text properties (replaces specified properties only) | +| [`tp-reset`](#tp-reset---replace-all-properties) | Replace ALL text properties | +| [`tp-add`](#tp-add---addmerge-properties) | Add/merge properties with deep merge support | +| [`tp-set-face`](#tp-set-face---set-face-property) | Set only the face property | +| [`tp-set-display`](#tp-set-display---set-display-property) | Set only the display property | +| [`tp-get`](#tp-get---get-property-value) | Get property value(s) from position or range | +| [`tp-at`](#tp-at---get-all-properties) | Get all properties at a position | +| [`tp-remove`](#tp-remove---remove-property) | Remove a property or sub-property | +| [`tp-clear`](#tp-clear---clear-all-properties) | Clear all text properties from a region | #### Pattern Matching Functions | Function | Description | |----------|-------------| -| `tp-match` | Set properties on string pattern matches | -| `tp-match-reset` | Reset all properties on string matches | -| `tp-match-add` | Add/merge properties on string matches | -| `tp-regexp` | Set properties on regexp matches | -| `tp-regexp-reset` | Reset all properties on regexp matches | -| `tp-regexp-add` | Add/merge properties on regexp matches | +| [`tp-match`](#tp-match---match-string) | Set properties on string pattern matches | +| [`tp-match-reset`](#tp-match-reset---match-and-reset) | Reset all properties on string matches | +| [`tp-match-add`](#tp-match-add---match-and-add) | Add/merge properties on string matches | +| [`tp-regexp`](#tp-regexp---match-regexp) | Set properties on regexp matches | +| [`tp-regexp-reset`](#tp-regexp-reset---regexp-and-reset) | Reset all properties on regexp matches | +| [`tp-regexp-add`](#tp-regexp-add---regexp-and-add) | Add/merge properties on regexp matches | #### Search & Navigation Functions | Function | Description | |----------|-------------| -| `tp-forward` | Search forward for text with property | -| `tp-backward` | Search backward for text with property | -| `tp-next` | Get next position with text properties | -| `tp-prev` | Get previous position with text properties | -| `tp-goto-next` | Move point to next text with property | -| `tp-goto-prev` | Move point to previous text with property | -| `tp-regions-map` | Apply function to all regions with property | -| `tp-strings-map` | Apply function to all strings with property | +| [`tp-forward`](#tp-forward--tp-backward) | Search forward for text with property | +| [`tp-backward`](#tp-forward--tp-backward) | Search backward for text with property | +| [`tp-next`](#tp-next--tp-prev) | Get next position with text properties | +| [`tp-prev`](#tp-next--tp-prev) | Get previous position with text properties | +| [`tp-goto-next`](#tp-goto-next--tp-goto-prev) | Move point to next text with property | +| [`tp-goto-prev`](#tp-goto-next--tp-goto-prev) | Move point to previous text with property | +| [`tp-regions-map`](#tp-regions-map--tp-strings-map) | Apply function to all regions with property | +| [`tp-strings-map`](#tp-regions-map--tp-strings-map) | Apply function to all strings with property | #### Query Functions | Function | Description | |----------|-------------| -| `tp-in` | Find all regions with a specific property | -| `tp-all` | Get all propertized regions | -| `tp-intervals` | Get property intervals in a region | -| `tp-empty-p` | Check if object has no properties | -| `tp-plist` | Get merged plist of all properties | +| [`tp-in`](#tp-in---find-regions-with-property) | Find all regions with a specific property | +| [`tp-all`](#tp-all---get-all-propertized-regions) | Get all propertized regions | +| [`tp-intervals`](#tp-intervals---get-property-intervals) | Get property intervals in a region | +| [`tp-empty-p`](#tp-empty-p---check-for-properties) | Check if object has no properties | +| [`tp-plist`](#tp-plist---get-merged-properties) | Get merged plist of all properties | #### Layer Definition Functions | Function | Description | |----------|-------------| -| `tp-define-layer` | Define a layer or layer group | -| `tp-layer-props` | Get properties for a layer | -| `tp-group-props` | Get properties for all layers in a group | -| `tp-layer-undefine` | Remove layer definition | -| `tp-group-undefine` | Remove group definition | -| `tp-layer-reset` | Clear all layer/group definitions | +| [`tp-define-layer`](#tp-define-layer---define-layers) | Define a layer or layer group | +| [`tp-layer-props`](#tp-layer-props--tp-group-props) | Get properties for a layer | +| [`tp-group-props`](#tp-layer-props--tp-group-props) | Get properties for all layers in a group | +| [`tp-layer-undefine`](#tp-layer-undefine--tp-group-undefine) | Remove layer definition | +| [`tp-group-undefine`](#tp-layer-undefine--tp-group-undefine) | Remove group definition | +| [`tp-layer-reset`](#tp-layer-reset) | Clear all layer/group definitions | #### Layer Placement Functions | Function | Description | |----------|-------------| -| `tp-put-layer` | Set layer at specific index position | -| `tp-push-layer` | Push layer to top of stack | +| [`tp-put-layer`](#tp-put-layer---set-layer-at-index) | Set layer at specific index position | +| [`tp-push-layer`](#tp-push-layer---push-layer-to-top) | Push layer to top of stack | #### Layer Deletion Functions | Function | Description | |----------|-------------| -| `tp-delete-layer` | Delete layer by name or index | -| `tp-pop-layer` | Remove top layer | +| [`tp-delete-layer`](#tp-delete-layer---delete-layer-by-nameindex) | Delete layer by name or index | +| [`tp-pop-layer`](#tp-pop-layer---pop-top-layer) | Remove top layer | #### Layer Movement Functions | Function | Description | |----------|-------------| -| `tp-raise-layer` | Move layer up/down by N positions | -| `tp-rotate-layer` | Cycle layers (top goes to bottom) | -| `tp-pin-layer` | Pin a layer to top (make visible) | -| `tp-switch-layer` | Swap positions of two layers | +| [`tp-raise-layer`](#tp-raise-layer---move-layer-updown) | Move layer up/down by N positions | +| [`tp-rotate-layer`](#tp-rotate-layer---cycle-layers) | Cycle layers (top goes to bottom) | +| [`tp-pin-layer`](#tp-pin-layer---pin-layer-to-top) | Pin a layer to top (make visible) | +| [`tp-switch-layer`](#tp-switch-layer---switch-two-layers) | Swap positions of two layers | #### Layer Merging Functions | Function | Description | |----------|-------------| -| `tp-merge-layers` | Merge specified layers into a new layer | -| `tp-flatten-layers` | Flatten all layers into a single layer | +| [`tp-merge-layers`](#tp-merge-layers---merge-multiple-layers) | Merge specified layers into a new layer | +| [`tp-flatten-layers`](#tp-flatten-layers---flatten-all-layers) | Flatten all layers into a single layer | #### Layer Query Functions | Function | Description | |----------|-------------| -| `tp-layer-list` | List all layer names in region | -| `tp-layer-count` | Count layers in region | -| `tp-layer-exists-p` | Check if layer exists in region | -| `tp-layer-top` | Get name of top (visible) layer | +| [`tp-layer-list`](#tp-layer-list---list-all-layers) | List all layer names in region | +| [`tp-layer-count`](#tp-layer-count) | Count layers in region | +| [`tp-layer-exists-p`](#tp-layer-exists-p) | Check if layer exists in region | +| [`tp-layer-top`](#tp-layer-top) | Get name of top (visible) layer | --- @@ -364,36 +360,6 @@ For range and entire string queries, returns a list of `(START END VALUE)` inter --- -#### Fine-grained Property Functions - -For manipulating sub-properties within complex properties like `face` or `display`: - -```elisp -;; Get sub-property -(tp-get-sub POSITION PROPERTY SUB-PROPERTY &optional OBJECT) - -;; Set sub-property -(tp-put-sub START END PROPERTY SUB-PROPERTY VALUE &optional OBJECT) - -;; Remove sub-property -(tp-remove-sub START END PROPERTY SUB-PROPERTY &optional OBJECT) -``` - -**Examples:** - -```elisp -;; Get :foreground from face -(tp-get-sub 1 'face :foreground) ; => "red" - -;; Set :weight on face -(tp-put-sub 1 6 'face :weight 'bold) - -;; Remove :background from face -(tp-remove-sub 1 6 'face :background) -``` - ---- - #### `tp-at` - Get All Properties ```elisp @@ -453,22 +419,6 @@ Remove a property or nested sub-property from a region or entire string. --- -#### `tp-remove-list` - Remove Multiple Properties - -```elisp -(tp-remove-list START END PROPERTIES &optional OBJECT) -``` - -Remove multiple properties at once. - -**Examples:** - -```elisp -(tp-remove-list 1 10 '(face help-echo mouse-face)) -``` - ---- - #### `tp-clear` - Clear All Properties ```elisp diff --git a/README_CN.md b/README_CN.md index 415460b..309d9a9 100644 --- a/README_CN.md +++ b/README_CN.md @@ -62,94 +62,90 @@ tp.el 所有函数按类别组织的完整概览: #### 核心属性函数 | 函数 | 描述 | |------|------| -| `tp-set` | 设置文本属性(仅替换指定属性) | -| `tp-reset` | 替换所有文本属性 | -| `tp-add` | 添加/合并属性,支持深度合并 | -| `tp-set-face` | 仅设置 face 属性 | -| `tp-set-display` | 仅设置 display 属性 | -| `tp-get` | 从位置或范围获取属性值 | -| `tp-get-sub` | 获取嵌套子属性值 | -| `tp-put-sub` | 设置嵌套子属性值 | -| `tp-remove-sub` | 移除嵌套子属性 | -| `tp-at` | 获取某位置的所有属性 | -| `tp-remove` | 移除属性或子属性 | -| `tp-remove-list` | 一次移除多个属性 | -| `tp-clear` | 清除区域中的所有文本属性 | +| [`tp-set`](#tp-set---设置文本属性) | 设置文本属性(仅替换指定属性) | +| [`tp-reset`](#tp-reset---替换所有属性) | 替换所有文本属性 | +| [`tp-add`](#tp-add---添加合并属性) | 添加/合并属性,支持深度合并 | +| [`tp-set-face`](#tp-set-face---设置-face-属性) | 仅设置 face 属性 | +| [`tp-set-display`](#tp-set-display---设置-display-属性) | 仅设置 display 属性 | +| [`tp-get`](#tp-get---获取属性值) | 从位置或范围获取属性值 | +| [`tp-at`](#tp-at---获取所有属性) | 获取某位置的所有属性 | +| [`tp-remove`](#tp-remove---移除属性) | 移除属性或子属性 | +| [`tp-clear`](#tp-clear---清除所有属性) | 清除区域中的所有文本属性 | #### 模式匹配函数 | 函数 | 描述 | |------|------| -| `tp-match` | 在字符串匹配处设置属性 | -| `tp-match-reset` | 在字符串匹配处重置所有属性 | -| `tp-match-add` | 在字符串匹配处添加/合并属性 | -| `tp-regexp` | 在正则匹配处设置属性 | -| `tp-regexp-reset` | 在正则匹配处重置所有属性 | -| `tp-regexp-add` | 在正则匹配处添加/合并属性 | +| [`tp-match`](#tp-match---匹配字符串) | 在字符串匹配处设置属性 | +| [`tp-match-reset`](#tp-match-reset---匹配并重置) | 在字符串匹配处重置所有属性 | +| [`tp-match-add`](#tp-match-add---匹配并添加) | 在字符串匹配处添加/合并属性 | +| [`tp-regexp`](#tp-regexp---匹配正则表达式) | 在正则匹配处设置属性 | +| [`tp-regexp-reset`](#tp-regexp-reset---正则匹配并重置) | 在正则匹配处重置所有属性 | +| [`tp-regexp-add`](#tp-regexp-add---正则匹配并添加) | 在正则匹配处添加/合并属性 | #### 搜索和导航函数 | 函数 | 描述 | |------|------| -| `tp-forward` | 向前搜索具有属性的文本 | -| `tp-backward` | 向后搜索具有属性的文本 | -| `tp-next` | 获取下一个具有文本属性的位置 | -| `tp-prev` | 获取上一个具有文本属性的位置 | -| `tp-goto-next` | 移动光标到下一个具有属性的文本 | -| `tp-goto-prev` | 移动光标到上一个具有属性的文本 | -| `tp-regions-map` | 对所有具有属性的区域应用函数 | -| `tp-strings-map` | 对所有具有属性的字符串应用函数 | +| [`tp-forward`](#tp-forward--tp-backward) | 向前搜索具有属性的文本 | +| [`tp-backward`](#tp-forward--tp-backward) | 向后搜索具有属性的文本 | +| [`tp-next`](#tp-next--tp-prev) | 获取下一个具有文本属性的位置 | +| [`tp-prev`](#tp-next--tp-prev) | 获取上一个具有文本属性的位置 | +| [`tp-goto-next`](#tp-goto-next--tp-goto-prev) | 移动光标到下一个具有属性的文本 | +| [`tp-goto-prev`](#tp-goto-next--tp-goto-prev) | 移动光标到上一个具有属性的文本 | +| [`tp-regions-map`](#tp-regions-map--tp-strings-map) | 对所有具有属性的区域应用函数 | +| [`tp-strings-map`](#tp-regions-map--tp-strings-map) | 对所有具有属性的字符串应用函数 | #### 查询函数 | 函数 | 描述 | |------|------| -| `tp-in` | 查找所有具有特定属性的区域 | -| `tp-all` | 获取所有带属性的区域 | -| `tp-intervals` | 获取区域中的属性区间 | -| `tp-empty-p` | 检查对象是否没有属性 | -| `tp-plist` | 获取所有属性的合并列表 | +| [`tp-in`](#tp-in---查找具有属性的区域) | 查找所有具有特定属性的区域 | +| [`tp-all`](#tp-all---获取所有带属性的区域) | 获取所有带属性的区域 | +| [`tp-intervals`](#tp-intervals---获取属性区间) | 获取区域中的属性区间 | +| [`tp-empty-p`](#tp-empty-p---检查属性) | 检查对象是否没有属性 | +| [`tp-plist`](#tp-plist---获取合并的属性) | 获取所有属性的合并列表 | #### 图层定义函数 | 函数 | 描述 | |------|------| -| `tp-define-layer` | 定义图层或图层组 | -| `tp-layer-props` | 获取图层的属性 | -| `tp-group-props` | 获取图层组中所有图层的属性 | -| `tp-layer-undefine` | 移除图层定义 | -| `tp-group-undefine` | 移除图层组定义 | -| `tp-layer-reset` | 清除所有图层/图层组定义 | +| [`tp-define-layer`](#tp-define-layer---定义图层) | 定义图层或图层组 | +| [`tp-layer-props`](#tp-layer-props--tp-group-props) | 获取图层的属性 | +| [`tp-group-props`](#tp-layer-props--tp-group-props) | 获取图层组中所有图层的属性 | +| [`tp-layer-undefine`](#tp-layer-undefine--tp-group-undefine) | 移除图层定义 | +| [`tp-group-undefine`](#tp-layer-undefine--tp-group-undefine) | 移除图层组定义 | +| [`tp-layer-reset`](#tp-layer-reset) | 清除所有图层/图层组定义 | #### 图层放置函数 | 函数 | 描述 | |------|------| -| `tp-put-layer` | 在指定索引位置设置图层 | -| `tp-push-layer` | 将图层推到堆栈顶部 | +| [`tp-put-layer`](#tp-put-layer---在指定位置设置图层) | 在指定索引位置设置图层 | +| [`tp-push-layer`](#tp-push-layer---推送图层到顶部) | 将图层推到堆栈顶部 | #### 图层删除函数 | 函数 | 描述 | |------|------| -| `tp-delete-layer` | 按名称或索引删除图层 | -| `tp-pop-layer` | 移除顶层图层 | +| [`tp-delete-layer`](#tp-delete-layer---按名称索引删除图层) | 按名称或索引删除图层 | +| [`tp-pop-layer`](#tp-pop-layer---弹出顶层) | 移除顶层图层 | #### 图层移动函数 | 函数 | 描述 | |------|------| -| `tp-raise-layer` | 将图层上移/下移 N 个位置 | -| `tp-rotate-layer` | 轮换图层(顶层移到底部) | -| `tp-pin-layer` | 将图层置顶(使其可见) | -| `tp-switch-layer` | 交换两个图层的位置 | +| [`tp-raise-layer`](#tp-raise-layer---上移下移图层) | 将图层上移/下移 N 个位置 | +| [`tp-rotate-layer`](#tp-rotate-layer---轮换图层) | 轮换图层(顶层移到底部) | +| [`tp-pin-layer`](#tp-pin-layer---将图层置顶) | 将图层置顶(使其可见) | +| [`tp-switch-layer`](#tp-switch-layer---交换两个图层) | 交换两个图层的位置 | #### 图层合并函数 | 函数 | 描述 | |------|------| -| `tp-merge-layers` | 将指定图层合并为新图层 | -| `tp-flatten-layers` | 将所有图层扁平化为单一图层 | +| [`tp-merge-layers`](#tp-merge-layers---合并多个图层) | 将指定图层合并为新图层 | +| [`tp-flatten-layers`](#tp-flatten-layers---扁平化所有图层) | 将所有图层扁平化为单一图层 | #### 图层查询函数 | 函数 | 描述 | |------|------| -| `tp-layer-list` | 列出区域中的所有图层名称 | -| `tp-layer-count` | 计算区域中的图层数量 | -| `tp-layer-exists-p` | 检查区域中是否存在某图层 | -| `tp-layer-top` | 获取顶层(可见)图层的名称 | +| [`tp-layer-list`](#tp-layer-list---列出所有图层) | 列出区域中的所有图层名称 | +| [`tp-layer-count`](#tp-layer-count) | 计算区域中的图层数量 | +| [`tp-layer-exists-p`](#tp-layer-exists-p) | 检查区域中是否存在某图层 | +| [`tp-layer-top`](#tp-layer-top) | 获取顶层(可见)图层的名称 | --- @@ -363,36 +359,6 @@ tp.el 所有函数按类别组织的完整概览: --- -#### 细粒度属性函数 - -用于操作复杂属性(如 `face` 或 `display`)内的子属性: - -```elisp -;; 获取子属性 -(tp-get-sub POSITION PROPERTY SUB-PROPERTY &optional OBJECT) - -;; 设置子属性 -(tp-put-sub START END PROPERTY SUB-PROPERTY VALUE &optional OBJECT) - -;; 移除子属性 -(tp-remove-sub START END PROPERTY SUB-PROPERTY &optional OBJECT) -``` - -**示例:** - -```elisp -;; 获取 face 的 :foreground -(tp-get-sub 1 'face :foreground) ; => "red" - -;; 设置 face 的 :weight -(tp-put-sub 1 6 'face :weight 'bold) - -;; 移除 face 的 :background -(tp-remove-sub 1 6 'face :background) -``` - ---- - #### `tp-at` - 获取所有属性 ```elisp @@ -452,22 +418,6 @@ tp.el 所有函数按类别组织的完整概览: --- -#### `tp-remove-list` - 移除多个属性 - -```elisp -(tp-remove-list START END PROPERTIES &optional OBJECT) -``` - -一次移除多个属性。 - -**示例:** - -```elisp -(tp-remove-list 1 10 '(face help-echo mouse-face)) -``` - ---- - #### `tp-clear` - 清除所有属性 ```elisp diff --git a/tp-tests.el b/tp-tests.el index 5875b57..829fdcf 100644 --- a/tp-tests.el +++ b/tp-tests.el @@ -72,16 +72,6 @@ (should (null (tp-get 1 'face))) (should (equal (tp-get 1 'help-echo) "test")))) -(ert-deftest tp-test-remove-list () - "Test tp-remove-list removes multiple properties." - (tp-test-with-temp-buffer - (insert "Hello") - (tp-set 1 6 '(face bold help-echo "test" mouse-face highlight)) - (tp-remove-list 1 6 '(face help-echo)) - (should (null (tp-get 1 'face))) - (should (null (tp-get 1 'help-echo))) - (should (eq (tp-get 1 'mouse-face) 'highlight)))) - (ert-deftest tp-test-clear () "Test tp-clear removes all properties." (tp-test-with-temp-buffer @@ -778,39 +768,39 @@ Returns list of (START END VALUE) intervals." ;;; ============================================================ (ert-deftest tp-test-get-sub-property () - "Test tp-get-sub retrieves sub-property from face." + "Test tp--get-sub retrieves sub-property from face." (tp-test-with-temp-buffer (insert "Hello") (put-text-property 1 6 'face '(:foreground "red" :weight bold)) - (should (equal (tp-get-sub 1 'face :foreground) "red")) - (should (eq (tp-get-sub 1 'face :weight) 'bold)) - (should (null (tp-get-sub 1 'face :background))))) + (should (equal (tp--get-sub 1 'face :foreground) "red")) + (should (eq (tp--get-sub 1 'face :weight) 'bold)) + (should (null (tp--get-sub 1 'face :background))))) (ert-deftest tp-test-put-sub-property () - "Test tp-put-sub sets sub-property on face." + "Test tp--put-sub sets sub-property on face." (tp-test-with-temp-buffer (insert "Hello") - (tp-put-sub 1 6 'face :foreground "blue") - (should (equal (tp-get-sub 1 'face :foreground) "blue")) + (tp--put-sub 1 6 'face :foreground "blue") + (should (equal (tp--get-sub 1 'face :foreground) "blue")) ;; Add another sub-property - (tp-put-sub 1 6 'face :weight 'bold) - (should (eq (tp-get-sub 1 'face :weight) 'bold)) - (should (equal (tp-get-sub 1 'face :foreground) "blue")))) + (tp--put-sub 1 6 'face :weight 'bold) + (should (eq (tp--get-sub 1 'face :weight) 'bold)) + (should (equal (tp--get-sub 1 'face :foreground) "blue")))) (ert-deftest tp-test-remove-sub-property () - "Test tp-remove-sub removes sub-property from face." + "Test tp--remove-sub removes sub-property from face." (tp-test-with-temp-buffer (insert "Hello") (put-text-property 1 6 'face '(:foreground "red" :weight bold)) - (tp-remove-sub 1 6 'face :foreground) - (should (null (tp-get-sub 1 'face :foreground))) - (should (eq (tp-get-sub 1 'face :weight) 'bold)))) + (tp--remove-sub 1 6 'face :foreground) + (should (null (tp--get-sub 1 'face :foreground))) + (should (eq (tp--get-sub 1 'face :weight) 'bold)))) (ert-deftest tp-test-sub-property-on-string () "Test fine-grained property manipulation on strings." (let ((str (copy-sequence "Hello"))) - (tp-put-sub 0 5 'face :foreground "green" str) - (should (equal (tp-get-sub 0 'face :foreground str) "green")))) + (tp--put-sub 0 5 'face :foreground "green" str) + (should (equal (tp--get-sub 0 'face :foreground str) "green")))) ;;; ============================================================ ;;; New API Tests (tp-reset, tp-set, tp-set-face, tp-set-display, tp-add) @@ -1138,6 +1128,26 @@ Returns list of (START END VALUE) intervals." (should (equal (plist-get underline :color) "blue")) (should (null (plist-get underline :style)))))) +(ert-deftest tp-test-remove-entire-string-single-nested-key () + "Test tp-remove removes a single nested key from a sub-property. +This tests the fix for the bug where (tp-remove str 'face :underline :position) +was removing the entire :underline instead of just :position." + (let ((str (copy-sequence "happy hacking emacs"))) + (tp-set str 'face '(:foreground "red" :underline (:position t :color "green")) + 'line-prefix ">> " 'other "other") + (tp-remove str 'face :underline :position) + (let* ((face (get-text-property 0 'face str)) + (underline (plist-get face :underline))) + ;; :foreground should be preserved + (should (equal (plist-get face :foreground) "red")) + ;; :underline should still exist but without :position + (should underline) + (should (equal (plist-get underline :color) "green")) + (should (null (plist-get underline :position))) + ;; Other properties should be preserved + (should (equal (get-text-property 0 'line-prefix str) ">> ")) + (should (equal (get-text-property 0 'other str) "other"))))) + ;;; ============================================================ ;;; New API Tests - Issue 3 & 4: tp-get for strings and new API ;;; ============================================================ diff --git a/tp.el b/tp.el index 71fc05f..8243de3 100644 --- a/tp.el +++ b/tp.el @@ -673,12 +673,13 @@ OBJECT defaults to current buffer." (nreverse intervals))))) (t (error "Invalid arguments to tp-get")))) -;;; Fine-grained property manipulation for nested properties +;;; Fine-grained property manipulation for nested properties (internal) -(defun tp-get-sub (position property sub-property &optional object) +(defun tp--get-sub (position property sub-property &optional object) "Get SUB-PROPERTY from PROPERTY at POSITION in OBJECT. For example, get :foreground from a face property. -OBJECT defaults to current buffer." +OBJECT defaults to current buffer. +Internal function - use `tp-get' with nested path for public API." (let ((prop-value (get-text-property position property object))) (cond ;; Property is a plist (e.g., (:foreground \"red\" :weight bold)) @@ -694,11 +695,12 @@ OBJECT defaults to current buffer." thereis (plist-get spec sub-property))) (t nil)))) -(defun tp-put-sub (start end property sub-property value &optional object) +(defun tp--put-sub (start end property sub-property value &optional object) "Set SUB-PROPERTY of PROPERTY to VALUE from START to END in OBJECT. Merges the sub-property into the existing property value. For example, set :foreground of a face property. -OBJECT defaults to current buffer." +OBJECT defaults to current buffer. +Internal function - use `tp-add' with nested plist for public API." (let* ((pos start)) (while (< pos end) (let* ((current-value (get-text-property pos property object)) @@ -724,10 +726,11 @@ OBJECT defaults to current buffer." object (cons start end))) -(defun tp-remove-sub (start end property sub-property &optional object) +(defun tp--remove-sub (start end property sub-property &optional object) "Remove SUB-PROPERTY from PROPERTY between START and END in OBJECT. For example, remove :foreground from a face property. -OBJECT defaults to current buffer." +OBJECT defaults to current buffer. +Internal function - use `tp-remove' with nested path for public API." (let* ((pos start)) (while (< pos end) (let* ((current-value (get-text-property pos property object)) @@ -769,7 +772,7 @@ PROPERTY can be a symbol or a list for nested removal." (nested-keys (caddr property))) (if (null nested-keys) ;; Remove sub-key from property - (tp-remove-sub start end prop-name sub-key object) + (tp--remove-sub start end prop-name sub-key object) ;; Remove nested keys from sub-key (let ((pos start)) (while (< pos end) @@ -829,15 +832,21 @@ Returns the modified string for string input, or nil for buffer operations." (start 0) (end (length start-or-string))) (cond - ;; (tp-remove str 'face :underline '(:style :position)) - nested sub-property removal + ;; (tp-remove str 'face :underline '(:style :position)) - nested sub-property removal with list ((and (symbolp end-or-prop) (keywordp prop-or-sub) rest (listp (car rest))) (tp--remove-property start end (list end-or-prop prop-or-sub (car rest)) str)) + ;; (tp-remove str 'face :underline :position :style ...) - nested sub-property removal with keywords + ((and (symbolp end-or-prop) + (keywordp prop-or-sub) + rest + (keywordp (car rest))) + (tp--remove-property start end (list end-or-prop prop-or-sub rest) str)) ;; (tp-remove str 'face :underline) - sub-property removal ((and (symbolp end-or-prop) (keywordp prop-or-sub)) - (tp-remove-sub start end end-or-prop prop-or-sub str)) + (tp--remove-sub start end end-or-prop prop-or-sub str)) ;; (tp-remove str 'face 'help-echo ...) - multiple properties ((symbolp end-or-prop) (let ((props (cons end-or-prop (cons prop-or-sub rest)))) @@ -858,12 +867,6 @@ Returns the modified string for string input, or nil for buffer operations." nil)) (t (error "Invalid arguments to tp-remove")))) -(defun tp-remove-list (start end properties &optional object) - "Remove list of PROPERTIES from text between START and END in OBJECT. -PROPERTIES should be a list of property names." - (let ((plist (mapcan (lambda (p) (list p nil)) properties))) - (remove-text-properties start end plist object))) - ;;;###autoload (defun tp-clear (&optional start end object) "Clear all text properties from START to END in OBJECT.