Implement tp-text reactive text feature and optimize non-reactive property handling
- Modified tp--resolve-props to not add tp-name for non-reactive plists - Added tp-text property support for reactive text content - Created tp--handle-tp-text-property helper for code reuse - Added tp--update-reactive-text and tp--replace-reactive-text-in-buffer - Updated tp-set, tp-reset, tp-add to handle tp-text property - Added comprehensive tests for tp-text functionality - Updated documentation with reactive text examples Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
parent
31f08e83f3
commit
d75070824a
@ -341,6 +341,120 @@ tp.el 的响应式系统借鉴了 Vue 的 API,提供了三个强大的关键
|
||||
|
||||
匿名响应式层适用于简单的场景,当你不需要在多个地方复用同一个层定义时。
|
||||
|
||||
## 响应式文本 (tp-text)
|
||||
|
||||
除了响应式文本**属性**,tp.el 还支持响应式**文本内容**本身。通过特殊的 `tp-text` 属性,你可以让文本内容也变成响应式的——当绑定的变量改变时,文本内容会自动更新。
|
||||
|
||||
### 基本用法
|
||||
|
||||
`tp-text` 属性有两种使用方式:
|
||||
|
||||
#### 1. 初始化当前文本
|
||||
|
||||
当 `tp-text` 的值为 `nil` 时,它会被自动设置为当前区域的文本内容:
|
||||
|
||||
```lisp
|
||||
(tp-pop-to-buffer "*tp-test*"
|
||||
(insert "Hello World")
|
||||
;; tp-text 为 nil 时,自动初始化为当前文本 "Hello"
|
||||
(tp-set 1 6 '(face bold tp-text nil))
|
||||
;; 现在 tp-text 的值是 "Hello"
|
||||
(message "tp-text = %s" (tp-at 1 'tp-text)))
|
||||
;; => "Hello"
|
||||
```
|
||||
|
||||
#### 2. 替换文本内容
|
||||
|
||||
当 `tp-text` 的值为字符串时,它会替换区域内的文本,同时保留其他文本属性:
|
||||
|
||||
```lisp
|
||||
(tp-pop-to-buffer "*tp-test*"
|
||||
(insert "Hello World")
|
||||
;; tp-text 为字符串时,替换文本内容
|
||||
(tp-set 1 6 '(face bold tp-text "Hi"))
|
||||
;; 文本变为 "Hi World",且 "Hi" 仍然有 bold 样式
|
||||
(message "buffer = %s" (buffer-string)))
|
||||
;; => "Hi World"
|
||||
```
|
||||
|
||||
### 响应式文本层
|
||||
|
||||
`tp-text` 的真正威力在于与响应式变量结合使用:
|
||||
|
||||
```lisp
|
||||
;; 定义响应式变量
|
||||
(defvar my-dynamic-text "Loading...")
|
||||
|
||||
;; 定义包含 tp-text 的响应式层
|
||||
(tp-define-layer 'dynamic-content
|
||||
:props '(face (:foreground "blue") tp-text $my-dynamic-text))
|
||||
|
||||
;; 应用到文本
|
||||
(tp-pop-to-buffer "*tp-test*"
|
||||
(insert "placeholder")
|
||||
(tp-set 1 12 'dynamic-content)
|
||||
;; 文本现在显示 "Loading..."
|
||||
(message "初始文本: %s" (buffer-string))
|
||||
;; => "Loading... "
|
||||
|
||||
;; 改变变量
|
||||
(setq my-dynamic-text "数据加载完成!")
|
||||
;; 文本自动更新!
|
||||
(message "更新后: %s" (buffer-string)))
|
||||
;; => "数据加载完成! "
|
||||
```
|
||||
|
||||
### 使用 :compute 生成动态文本
|
||||
|
||||
`tp-text` 可以与 `:compute` 结合,创建由其他变量派生的动态文本:
|
||||
|
||||
```lisp
|
||||
(tp-define-layer 'greeting-layer
|
||||
:props '(face (:foreground "green") tp-text $full-greeting)
|
||||
:data '((user-name . "访客")
|
||||
(greeting-prefix . "欢迎"))
|
||||
:compute '((full-greeting
|
||||
(lambda ()
|
||||
(format "%s, %s!" greeting-prefix user-name)))))
|
||||
|
||||
;; 应用到文本
|
||||
(tp-pop-to-buffer "*tp-test*"
|
||||
(insert "placeholder")
|
||||
(tp-set 1 12 'greeting-layer)
|
||||
;; 显示 "欢迎, 访客!"
|
||||
(message "初始: %s" (buffer-string))
|
||||
|
||||
;; 改变用户名
|
||||
(setq user-name "张三")
|
||||
;; 文本自动更新为 "欢迎, 张三!"
|
||||
(message "更新后: %s" (buffer-string)))
|
||||
```
|
||||
|
||||
### 匿名响应式文本
|
||||
|
||||
你也可以直接在属性列表中使用响应式 `tp-text`,无需定义层:
|
||||
|
||||
```lisp
|
||||
(defvar inline-text "原始内容")
|
||||
|
||||
(tp-pop-to-buffer "*tp-test*"
|
||||
(insert "placeholder")
|
||||
;; 直接使用响应式 tp-text
|
||||
(tp-set 1 12 '(face bold tp-text $inline-text))
|
||||
;; 显示 "原始内容"
|
||||
|
||||
;; 改变变量
|
||||
(setq inline-text "新内容")
|
||||
;; 文本自动更新为 "新内容"
|
||||
)
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. **tp-text 只影响缓冲区文本**:对于字符串对象,由于 Emacs 字符串长度固定,`tp-text` 不会替换字符串内容。
|
||||
2. **保留现有属性**:使用 `tp-set` 或 `tp-add` 设置 `tp-text` 时,现有的文本属性会被保留。
|
||||
3. **非响应式属性不添加 tp-name**:如果文本属性中没有响应式变量(`$` 前缀),则不会添加 `tp-name` 等响应式专用属性,保持原生文本属性行为。
|
||||
|
||||
## 总结
|
||||
|
||||
tp.el 的响应式文本属性功能为 Emacs 开发带来了现代化的响应式编程体验。通过使用 `$` 前缀的响应式变量、`:data` 定义状态、`:compute` 计算派生值、`:watch` 监听变化,你可以构建出更加动态、易于维护的文本属性系统。
|
||||
@ -352,3 +466,4 @@ tp.el 的响应式文本属性功能为 Emacs 开发带来了现代化的响应
|
||||
4. **:compute**:定义由其他变量派生的计算属性
|
||||
5. **:watch**:监听变量变化并执行副作用
|
||||
6. **自动更新**:改变变量值,所有相关文本自动更新
|
||||
7. **响应式文本 (tp-text)**:让文本内容本身也能响应式更新
|
||||
|
||||
129
tp-tests.el
129
tp-tests.el
@ -2248,15 +2248,17 @@ incorrectly generate an anonymous tp-name instead of using the layer name."
|
||||
;;; Anonymous Layer and Reactive Text Property Tests
|
||||
;;; ============================================================
|
||||
|
||||
(ert-deftest tp-test-set-anonymous-layer-gets-tp-name ()
|
||||
"Test that tp-set with anonymous plist gets a tp-name."
|
||||
(ert-deftest tp-test-set-anonymous-layer-no-tp-name-for-non-reactive ()
|
||||
"Test that tp-set with non-reactive plist does NOT get tp-name.
|
||||
Per requirement 1: non-reactive properties should not have tp-name added,
|
||||
preserving the native text property behavior."
|
||||
(tp-test-with-temp-buffer
|
||||
(insert "Hello World")
|
||||
(tp-set 1 6 '(face bold))
|
||||
;; Anonymous layer should have a generated tp-name
|
||||
(should (tp-at 1 'tp-name))
|
||||
;; The tp-name should be a symbol starting with tp-anon-
|
||||
(should (string-prefix-p "tp-anon-" (symbol-name (tp-at 1 'tp-name))))))
|
||||
;; Non-reactive anonymous layer should NOT have tp-name
|
||||
(should-not (tp-at 1 'tp-name))
|
||||
;; But the face property should still be set
|
||||
(should (eq (tp-at 1 'face) 'bold))))
|
||||
|
||||
(ert-deftest tp-test-set-anonymous-reactive-layer ()
|
||||
"Test that tp-set with anonymous reactive plist works."
|
||||
@ -2943,5 +2945,120 @@ incorrectly generate an anonymous tp-name instead of using the layer name."
|
||||
;; Cleanup
|
||||
(ignore-errors (makunbound 'tp-test-applied-color)))))
|
||||
|
||||
;;; ============================================================
|
||||
;;; Reactive Text (tp-text) Tests
|
||||
;;; ============================================================
|
||||
|
||||
(ert-deftest tp-test-tp-text-nil-initializes-to-current-text ()
|
||||
"Test that tp-text with nil value is initialized to current text."
|
||||
(tp-test-with-temp-buffer
|
||||
(insert "Hello World")
|
||||
(tp-set 1 6 '(face bold tp-text nil))
|
||||
;; tp-text should be set to the current text
|
||||
(should (equal (tp-at 1 'tp-text) "Hello"))
|
||||
;; face should still be bold
|
||||
(should (eq (tp-at 1 'face) 'bold))))
|
||||
|
||||
(ert-deftest tp-test-tp-text-string-replaces-text ()
|
||||
"Test that tp-text with string value replaces the text in the region."
|
||||
(tp-test-with-temp-buffer
|
||||
(insert "Hello World")
|
||||
(tp-set 1 6 '(face bold tp-text "Hi"))
|
||||
;; Text should be replaced
|
||||
(should (equal (buffer-substring-no-properties 1 3) "Hi"))
|
||||
;; face should still be applied
|
||||
(should (eq (tp-at 1 'face) 'bold))
|
||||
;; tp-text property should be set
|
||||
(should (equal (tp-at 1 'tp-text) "Hi"))))
|
||||
|
||||
(ert-deftest tp-test-tp-text-preserves-other-properties ()
|
||||
"Test that tp-text replacement preserves existing properties."
|
||||
(tp-test-with-temp-buffer
|
||||
(insert "Hello World")
|
||||
;; First set some properties
|
||||
(tp-set 1 6 '(help-echo "greeting"))
|
||||
;; Then set tp-text with face
|
||||
(tp-set 1 6 '(face bold tp-text "Hi"))
|
||||
;; Text should be replaced
|
||||
(should (equal (buffer-substring-no-properties 1 3) "Hi"))
|
||||
;; Both face and help-echo should be preserved
|
||||
(should (eq (tp-at 1 'face) 'bold))
|
||||
(should (equal (tp-at 1 'help-echo) "greeting"))))
|
||||
|
||||
(ert-deftest tp-test-tp-reset-with-tp-text ()
|
||||
"Test that tp-reset with tp-text works correctly."
|
||||
(tp-test-with-temp-buffer
|
||||
(insert "Hello World")
|
||||
(tp-reset 1 6 '(face italic tp-text "Bye"))
|
||||
;; Text should be replaced
|
||||
(should (equal (buffer-substring-no-properties 1 4) "Bye"))
|
||||
;; Properties should be set
|
||||
(should (eq (tp-at 1 'face) 'italic))
|
||||
(should (equal (tp-at 1 'tp-text) "Bye"))))
|
||||
|
||||
(ert-deftest tp-test-tp-add-with-tp-text ()
|
||||
"Test that tp-add with tp-text works correctly."
|
||||
(tp-test-with-temp-buffer
|
||||
(insert "Hello World")
|
||||
(tp-set 1 6 '(help-echo "existing"))
|
||||
(tp-add 1 6 '(face bold tp-text "Hi"))
|
||||
;; Text should be replaced
|
||||
(should (equal (buffer-substring-no-properties 1 3) "Hi"))
|
||||
;; Both properties should be present
|
||||
(should (eq (tp-at 1 'face) 'bold))
|
||||
(should (equal (tp-at 1 'help-echo) "existing"))))
|
||||
|
||||
(ert-deftest tp-test-tp-text-reactive-layer ()
|
||||
"Test tp-text with reactive variable."
|
||||
(tp-test-with-temp-buffer
|
||||
(defvar tp-test-reactive-text nil "Test variable for reactive text.")
|
||||
(setq tp-test-reactive-text "Initial")
|
||||
(unwind-protect
|
||||
(progn
|
||||
(tp-define-layer 'test-reactive-text-layer
|
||||
:props '(face bold tp-text $tp-test-reactive-text))
|
||||
;; Apply layer to text
|
||||
(insert "Hello World")
|
||||
(tp-set 1 6 'test-reactive-text-layer)
|
||||
;; Initial text should be replaced
|
||||
(should (equal (buffer-substring-no-properties 1 8) "Initial"))
|
||||
;; tp-text should be set
|
||||
(should (equal (tp-at 1 'tp-text) "Initial"))
|
||||
;; Change the reactive variable
|
||||
(setq tp-test-reactive-text "Changed")
|
||||
;; Text should be updated
|
||||
(should (equal (buffer-substring-no-properties 1 8) "Changed"))
|
||||
;; face should still be applied
|
||||
(should (eq (tp-at 1 'face) 'bold)))
|
||||
;; Cleanup
|
||||
(makunbound 'tp-test-reactive-text))))
|
||||
|
||||
(ert-deftest tp-test-tp-text-reactive-computed ()
|
||||
"Test tp-text with computed reactive variable."
|
||||
(tp-test-with-temp-buffer
|
||||
(unwind-protect
|
||||
(progn
|
||||
(setq tp-test-name-part1 "Hello")
|
||||
(setq tp-test-name-part2 "World")
|
||||
(tp-define-layer 'test-computed-text-layer
|
||||
:props '(face bold tp-text $tp-test-full-text)
|
||||
:data '(tp-test-name-part1 tp-test-name-part2)
|
||||
:compute '((tp-test-full-text
|
||||
(lambda ()
|
||||
(concat tp-test-name-part1 " " tp-test-name-part2)))))
|
||||
;; Apply layer to text
|
||||
(insert "placeholder")
|
||||
(tp-set 1 12 'test-computed-text-layer)
|
||||
;; Text should be replaced with computed value
|
||||
(should (equal (buffer-substring-no-properties 1 12) "Hello World"))
|
||||
;; Change a data variable
|
||||
(setq tp-test-name-part1 "Goodbye")
|
||||
;; Text should be updated with new computed value
|
||||
(should (equal (buffer-substring-no-properties 1 14) "Goodbye World")))
|
||||
;; Cleanup
|
||||
(ignore-errors (makunbound 'tp-test-name-part1))
|
||||
(ignore-errors (makunbound 'tp-test-name-part2))
|
||||
(ignore-errors (makunbound 'tp-test-full-text)))))
|
||||
|
||||
(provide 'tp-ert-tests)
|
||||
;;; tp-ert-tests.el ends here
|
||||
|
||||
196
tp.el
196
tp.el
@ -231,7 +231,9 @@ Only 'set' operations trigger updates because:
|
||||
(dolist (dep deps)
|
||||
(let* ((layer-name (car dep))
|
||||
;; Get the reactive props stored directly in the dependency
|
||||
(reactive-props (cdr dep)))
|
||||
(reactive-props (cdr dep))
|
||||
;; Check if tp-text is affected by this variable
|
||||
(tp-text-affected (plist-member reactive-props 'tp-text)))
|
||||
;; Call user-defined watch callbacks for this layer
|
||||
(tp--invoke-layer-watchers layer-name symbol newval oldval)
|
||||
;; Update computed properties for this layer
|
||||
@ -250,9 +252,11 @@ Only 'set' operations trigger updates because:
|
||||
(plist-put current-props key val)))
|
||||
(tp--set-layer-props layer-name current-props))))))
|
||||
;; Update text regions with this layer
|
||||
;; If WHERE is a buffer (setq-local), only update that buffer
|
||||
;; If WHERE is nil (setq), update all buffers that have the text property
|
||||
(tp--update-layer-regions layer-name where))))))
|
||||
;; If tp-text is affected, use tp--update-reactive-text for text replacement
|
||||
;; Otherwise use tp--update-layer-regions for property-only updates
|
||||
(if tp-text-affected
|
||||
(tp--update-reactive-text layer-name where)
|
||||
(tp--update-layer-regions layer-name where)))))))
|
||||
|
||||
(defun tp--invoke-layer-watchers (layer-name symbol newval oldval)
|
||||
"Invoke all registered watcher callbacks for LAYER-NAME watching SYMBOL.
|
||||
@ -463,6 +467,118 @@ WHERE specifies which buffers to update:
|
||||
(setq tp-layer-data nil))
|
||||
|
||||
|
||||
;;; Reactive Text (tp-text) Functions
|
||||
|
||||
(defun tp--update-reactive-text (layer-name &optional where)
|
||||
"Update text regions that have tp-text property with LAYER-NAME applied.
|
||||
This is called when a reactive variable bound to tp-text changes.
|
||||
|
||||
WHERE specifies which buffers to update:
|
||||
- If WHERE is a buffer, only update that buffer (setq-local case).
|
||||
- If WHERE is nil, update all buffers that have the text property (setq case)."
|
||||
(let ((props (tp-layer-props layer-name)))
|
||||
(when props
|
||||
(let ((new-text (plist-get props 'tp-text)))
|
||||
(when (and new-text (stringp new-text))
|
||||
(if (and where (bufferp where) (buffer-live-p where))
|
||||
;; setq-local case: only update the specific buffer
|
||||
(tp-with-current-buffer where
|
||||
(save-excursion
|
||||
(tp--replace-reactive-text-in-buffer layer-name new-text props)))
|
||||
;; setq case: update all buffers that have the text property
|
||||
(dolist (buf (buffer-list))
|
||||
(when (buffer-live-p buf)
|
||||
(tp-with-current-buffer buf
|
||||
(save-excursion
|
||||
(tp--replace-reactive-text-in-buffer layer-name new-text props)))))))))))
|
||||
|
||||
(defun tp--replace-reactive-text-in-buffer (layer-name new-text props)
|
||||
"Replace text in current buffer for reactive text with LAYER-NAME.
|
||||
NEW-TEXT is the new text to replace with.
|
||||
PROPS are the properties to apply to the new text."
|
||||
(goto-char (point-min))
|
||||
(let ((match t))
|
||||
(while match
|
||||
(setq match (text-property-search-forward 'tp-name layer-name t))
|
||||
(when match
|
||||
(let* ((m-start (prop-match-beginning match))
|
||||
(m-end (prop-match-end match))
|
||||
(old-text (buffer-substring-no-properties m-start m-end)))
|
||||
;; Only replace if text is different
|
||||
(unless (equal old-text new-text)
|
||||
;; Delete old text and insert new
|
||||
(delete-region m-start m-end)
|
||||
(goto-char m-start)
|
||||
(insert new-text)
|
||||
;; Apply the layer properties (including tp-text and tp-name) to new text
|
||||
(let ((new-end (+ m-start (length new-text))))
|
||||
(set-text-properties m-start new-end props))))))))
|
||||
|
||||
(defun tp--handle-tp-text-property (start end props object)
|
||||
"Handle tp-text property in PROPS for region from START to END in OBJECT.
|
||||
If tp-text is nil, initialize it to the current text in the region.
|
||||
If tp-text is a string different from current text, replace the text.
|
||||
Returns (PROPS NEW-END) where PROPS is the updated props and NEW-END is
|
||||
the new end position after any text replacement."
|
||||
(if (not (plist-member props 'tp-text))
|
||||
;; tp-text not in props - return unchanged
|
||||
(list props end)
|
||||
(let ((tp-text-val (plist-get props 'tp-text)))
|
||||
(cond
|
||||
;; tp-text is nil - initialize it to the current text
|
||||
((null tp-text-val)
|
||||
(let ((current-text (if (stringp object)
|
||||
(substring object start end)
|
||||
(if object
|
||||
(with-current-buffer object
|
||||
(buffer-substring-no-properties start end))
|
||||
(buffer-substring-no-properties start end)))))
|
||||
(list (plist-put props 'tp-text current-text) end)))
|
||||
;; tp-text has a string value - replace the text in the region
|
||||
((stringp tp-text-val)
|
||||
(if (stringp object)
|
||||
;; For strings, we can't change length, so just return as-is
|
||||
(list props end)
|
||||
;; For buffers: replace text and adjust end position
|
||||
(let ((old-text (if object
|
||||
(with-current-buffer object
|
||||
(buffer-substring-no-properties start end))
|
||||
(buffer-substring-no-properties start end))))
|
||||
(if (equal old-text tp-text-val)
|
||||
;; Same text, no replacement needed
|
||||
(list props end)
|
||||
;; Need to replace text
|
||||
(let ((existing-props (if object
|
||||
(with-current-buffer object
|
||||
(text-properties-at start))
|
||||
(text-properties-at start))))
|
||||
(save-excursion
|
||||
(if object
|
||||
(with-current-buffer object
|
||||
(let ((inhibit-read-only t))
|
||||
(delete-region start end)
|
||||
(goto-char start)
|
||||
(insert tp-text-val)))
|
||||
(let ((inhibit-read-only t))
|
||||
(delete-region start end)
|
||||
(goto-char start)
|
||||
(insert tp-text-val))))
|
||||
(let ((new-end (+ start (length tp-text-val))))
|
||||
;; Re-apply existing properties to new text region
|
||||
(when existing-props
|
||||
(let ((i 0)
|
||||
(len (length existing-props)))
|
||||
(while (< i len)
|
||||
(put-text-property start new-end
|
||||
(nth i existing-props)
|
||||
(nth (1+ i) existing-props)
|
||||
object)
|
||||
(setq i (+ i 2)))))
|
||||
(list props new-end)))))))
|
||||
;; Other types - return unchanged
|
||||
(t (list props end))))))
|
||||
|
||||
|
||||
;;; Core Property Functions
|
||||
|
||||
(defun tp--parse-args (start-or-string end-or-prop props-or-val rest)
|
||||
@ -538,10 +654,21 @@ This function supports four calling conventions:
|
||||
PROPS can also be a symbol representing a layer or group name defined
|
||||
by `define-tp' or `define-tp-group', which will be resolved to its properties.
|
||||
|
||||
Special property `tp-text':
|
||||
If PROPS contains `tp-text' with a nil value, it will be initialized
|
||||
to the current text in the region, making the text reactive.
|
||||
If `tp-text' has a string value, the text in the region will be replaced
|
||||
with this value while preserving the text properties.
|
||||
|
||||
This replaces only the properties specified, preserving other properties.
|
||||
Return the modified object (string) or region (START . END) for buffer."
|
||||
(pcase-let ((`(,object ,start ,finish ,props)
|
||||
(tp--parse-args start-or-string end-or-prop props-or-val rest)))
|
||||
;; Handle tp-text property specially using helper function
|
||||
(pcase-let ((`(,new-props ,new-finish)
|
||||
(tp--handle-tp-text-property start finish props object)))
|
||||
(setq props new-props)
|
||||
(setq finish new-finish))
|
||||
;; Apply properties individually (preserves other properties)
|
||||
(let ((len (length props))
|
||||
(i 0))
|
||||
@ -579,9 +706,51 @@ PROPS can also be a symbol representing a layer or group name defined
|
||||
by `define-tp' or `define-tp-group', which will be resolved to its properties.
|
||||
|
||||
Unlike `tp-set', this completely replaces all existing properties.
|
||||
|
||||
Special property `tp-text':
|
||||
If PROPS contains `tp-text' with a nil value, it will be initialized
|
||||
to the current text in the region, making the text reactive.
|
||||
If `tp-text' has a string value, the text in the region will be replaced
|
||||
with this value while preserving the text properties.
|
||||
|
||||
Return the modified object (string) or region (START . END) for buffer."
|
||||
(pcase-let ((`(,object ,start ,finish ,props)
|
||||
(tp--parse-args start-or-string end-or-prop props-or-val rest)))
|
||||
;; Handle tp-text property specially using helper function
|
||||
;; Note: for tp-reset we don't preserve existing props on text replacement
|
||||
;; since tp-reset is meant to completely replace all properties
|
||||
(when (plist-member props 'tp-text)
|
||||
(let ((tp-text-val (plist-get props 'tp-text)))
|
||||
(cond
|
||||
;; tp-text is nil - initialize it to the current text
|
||||
((null tp-text-val)
|
||||
(let ((current-text (if (stringp object)
|
||||
(substring object start finish)
|
||||
(if object
|
||||
(with-current-buffer object
|
||||
(buffer-substring-no-properties start finish))
|
||||
(buffer-substring-no-properties start finish)))))
|
||||
(setq props (plist-put props 'tp-text current-text))))
|
||||
;; tp-text has a string value - replace the text in the region
|
||||
((stringp tp-text-val)
|
||||
(unless (stringp object)
|
||||
(let ((old-text (if object
|
||||
(with-current-buffer object
|
||||
(buffer-substring-no-properties start finish))
|
||||
(buffer-substring-no-properties start finish))))
|
||||
(unless (equal old-text tp-text-val)
|
||||
(save-excursion
|
||||
(if object
|
||||
(with-current-buffer object
|
||||
(let ((inhibit-read-only t))
|
||||
(delete-region start finish)
|
||||
(goto-char start)
|
||||
(insert tp-text-val)))
|
||||
(let ((inhibit-read-only t))
|
||||
(delete-region start finish)
|
||||
(goto-char start)
|
||||
(insert tp-text-val))))
|
||||
(setq finish (+ start (length tp-text-val))))))))))
|
||||
;; Completely replace all properties
|
||||
(set-text-properties start finish props object)
|
||||
(if (stringp object)
|
||||
@ -692,9 +861,20 @@ the existing face list rather than replacing. For example:
|
||||
(tp-add str \\='face \\='shadow) with existing face \\='bold
|
||||
results in face value \\='(shadow bold).
|
||||
|
||||
Special property `tp-text':
|
||||
If PROPS contains `tp-text' with a nil value, it will be initialized
|
||||
to the current text in the region, making the text reactive.
|
||||
If `tp-text' has a string value, the text in the region will be replaced
|
||||
with this value while preserving the text properties.
|
||||
|
||||
Return the modified object (string) or region (START . END) for buffer."
|
||||
(pcase-let ((`(,object ,start ,finish ,props)
|
||||
(tp--parse-args start-or-string end-or-prop props-or-val rest)))
|
||||
;; Handle tp-text property specially using helper function
|
||||
(pcase-let ((`(,new-props ,new-finish)
|
||||
(tp--handle-tp-text-property start finish props object)))
|
||||
(setq props new-props)
|
||||
(setq finish new-finish))
|
||||
;; Process each property with deep merging
|
||||
(let ((pos start))
|
||||
(while (< pos finish)
|
||||
@ -2422,11 +2602,9 @@ For group names, includes `tp-layers' property with the full layer stack."
|
||||
(tp--register-reactive-deps layer-name reactive-syms props)
|
||||
;; Return resolved props with tp-name
|
||||
(append resolved-props (list 'tp-name layer-name)))
|
||||
;; No reactive symbols - just add tp-name if not present
|
||||
(if existing-tp-name
|
||||
props
|
||||
(let ((layer-name (tp--generate-anonymous-layer-name)))
|
||||
(append props (list 'tp-name layer-name)))))))))
|
||||
;; No reactive symbols - return props as-is (no tp-name needed)
|
||||
;; This preserves the native text property behavior for non-reactive plists
|
||||
props)))))
|
||||
;; Symbol - check if it's a layer or group name
|
||||
((symbolp props)
|
||||
(cond
|
||||
|
||||
Loading…
Reference in New Issue
Block a user