Refactor tp-forward/backward and tp-search-do API

- tp-forward and tp-backward now support string objects
- Renamed tp-forward-do to tp--forward-do (private) with string support
- Renamed tp-backward-do to tp--backward-do (private) with string support
- New public tp-forward-do and tp-backward-do: function receives matched text only
- Renamed tp-search-do to tp--search-do (private)
- New public tp-search-map: function receives matched text only
- Updated English and Chinese documentation
- Updated and added tests for all new functionality

Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-12-15 09:00:13 +00:00
parent 080239e3ce
commit 0b0bfedc6b
4 changed files with 382 additions and 115 deletions

View File

@ -88,12 +88,12 @@ A complete overview of all tp.el functions organized by category:
|----------|-------------|
| [`tp-search-forward`](#tp-search-forward--tp-search-backward) | Raw wrapper for text-property-search-forward |
| [`tp-search-backward`](#tp-search-forward--tp-search-backward) | Raw wrapper for text-property-search-backward |
| [`tp-forward`](#tp-forward--tp-backward) | Search forward N times for text with property |
| [`tp-backward`](#tp-forward--tp-backward) | Search backward N times for text with property |
| [`tp-forward-do`](#tp-forward-do--tp-backward-do) | Apply function to N forward matches |
| [`tp-backward-do`](#tp-forward-do--tp-backward-do) | Apply function to N backward matches |
| [`tp-forward`](#tp-forward--tp-backward) | Search forward N times for text with property (buffers and strings) |
| [`tp-backward`](#tp-forward--tp-backward) | Search backward N times for text with property (buffers and strings) |
| [`tp-forward-do`](#tp-forward-do--tp-backward-do) | Apply function to matched text for N forward matches |
| [`tp-backward-do`](#tp-forward-do--tp-backward-do) | Apply function to matched text for N backward matches |
| [`tp-search`](#tp-search---search-all-matches) | Search all matching properties in range or string |
| [`tp-search-do`](#tp-search-do---apply-function-to-all-matches) | Apply function to all matching properties |
| [`tp-search-map`](#tp-search-map---apply-function-to-matched-text) | Apply function to matched text for all matches |
#### Query Functions
| Function | Description |
@ -573,8 +573,9 @@ Search forward/backward N times for text with PROPERTY.
- **N** is the number of searches, defaulting to 1.
- **VALUE** is the optional value to match.
- **OBJECT** can be a buffer; nil defaults to current buffer.
- Returns the prop-match object from the last successful search.
- **OBJECT** can be a buffer or string; nil defaults to current buffer.
- For buffers, returns the prop-match object from the last successful search.
- For strings, returns a list of (START END VALUE) for all matches found.
**Examples:**
@ -587,6 +588,10 @@ Search forward/backward N times for text with PROPERTY.
;; Search forward 3 times
(tp-forward 'marker nil nil 3)
;; Search in a string
(tp-forward 'marker nil my-string 2)
;; => ((0 5 t) (12 17 t))
```
---
@ -598,19 +603,27 @@ Search forward/backward N times for text with PROPERTY.
(tp-backward-do FUNCTION PROPERTY &optional VALUE OBJECT N)
```
Search forward/backward N times for text with PROPERTY and apply FUNCTION to each match.
Search forward/backward N times for text with PROPERTY and apply FUNCTION to matched text.
- **FUNCTION** receives two arguments: the prop-match object and OBJECT.
- **FUNCTION** receives the matched text as its only argument. The return value
of FUNCTION replaces the matched text in the string or buffer.
- **N** is the number of searches, defaulting to 1.
- **OBJECT** can be a buffer or string; nil defaults to current buffer.
- Returns the number of successful matches.
**Examples:**
```elisp
;; Apply function to next 3 markers
;; Upcase matched text in buffer
(tp-forward-do #'upcase 'marker nil nil 3)
;; Upcase matched text in string
(tp-forward-do #'upcase 'marker nil my-string 2)
;; Custom transformation
(tp-forward-do
(lambda (match obj)
(message "Found at %d" (prop-match-beginning match)))
(lambda (text)
(concat "[" text "]"))
'marker nil nil 3)
```
@ -647,36 +660,36 @@ Returns a list of (START END VALUE) for all matching regions.
---
#### `tp-search-do` - Apply Function to All Matches
#### `tp-search-map` - Apply Function to Matched Text
```elisp
;; Buffer/string region
(tp-search-do FUNCTION START END PROPERTY &optional VALUE OBJECT)
(tp-search-map FUNCTION START END PROPERTY &optional VALUE OBJECT)
;; Entire string
(tp-search-do FUNCTION STRING PROPERTY &optional VALUE)
(tp-search-map FUNCTION STRING PROPERTY &optional VALUE)
```
Execute FUNCTION on all matches of PROPERTY in a buffer/string range or entire string.
Apply FUNCTION to matched text for all matches of PROPERTY.
- **FUNCTION** receives two arguments: the match (list of START END VALUE) and OBJECT.
- **FUNCTION** receives the matched text as its only argument. The return value
of FUNCTION replaces the matched text in the string or buffer.
- Returns the number of matches processed.
**Examples:**
```elisp
;; Process all markers in buffer range
(tp-search-do
(lambda (match obj)
(message "Found at %d-%d with value %s"
(car match) (cadr match) (caddr match)))
1 100 'marker)
;; Upcase all markers in string
(tp-search-map #'upcase my-string 'marker)
;; Process all headings in string
(tp-search-do
(lambda (match obj)
(upcase (substring obj (car match) (cadr match))))
my-string 'type 'heading)
;; Upcase all markers in buffer range
(tp-search-map #'upcase 1 100 'marker)
;; Custom transformation
(tp-search-map
(lambda (text)
(concat "[" text "]"))
my-string 'marker)
```
---

View File

@ -87,12 +87,12 @@ tp.el 所有函数按类别组织的完整概览:
|------|------|
| [`tp-search-forward`](#tp-search-forward--tp-search-backward) | text-property-search-forward 的原始包装 |
| [`tp-search-backward`](#tp-search-forward--tp-search-backward) | text-property-search-backward 的原始包装 |
| [`tp-forward`](#tp-forward--tp-backward) | 向前搜索 N 次具有属性的文本 |
| [`tp-backward`](#tp-forward--tp-backward) | 向后搜索 N 次具有属性的文本 |
| [`tp-forward-do`](#tp-forward-do--tp-backward-do) | 对 N 个向前匹配应用函数 |
| [`tp-backward-do`](#tp-forward-do--tp-backward-do) | 对 N 个向后匹配应用函数 |
| [`tp-forward`](#tp-forward--tp-backward) | 向前搜索 N 次具有属性的文本(支持缓冲区和字符串) |
| [`tp-backward`](#tp-forward--tp-backward) | 向后搜索 N 次具有属性的文本(支持缓冲区和字符串) |
| [`tp-forward-do`](#tp-forward-do--tp-backward-do) | 对 N 个向前匹配的文本应用函数 |
| [`tp-backward-do`](#tp-forward-do--tp-backward-do) | 对 N 个向后匹配的文本应用函数 |
| [`tp-search`](#tp-search---搜索所有匹配) | 在范围或字符串中搜索所有匹配的属性 |
| [`tp-search-do`](#tp-search-do---对所有匹配应用函数) | 对所有匹配的属性应用函数 |
| [`tp-search-map`](#tp-search-map---对匹配文本应用函数) | 对所有匹配的文本应用函数 |
#### 查询函数
| 函数 | 描述 |
@ -572,8 +572,9 @@ Emacs 的 `text-property-search-forward` 和 `text-property-search-backward` 的
- **N** 是搜索次数,默认为 1。
- **VALUE** 是可选的匹配值。
- **OBJECT** 可以是缓冲区nil 默认为当前缓冲区。
- 返回最后一次成功搜索的 prop-match 对象。
- **OBJECT** 可以是缓冲区或字符串nil 默认为当前缓冲区。
- 对于缓冲区,返回最后一次成功搜索的 prop-match 对象。
- 对于字符串,返回所有匹配的 (START END VALUE) 列表。
**示例:**
@ -586,6 +587,10 @@ Emacs 的 `text-property-search-forward` 和 `text-property-search-backward` 的
;; 向前搜索 3 次
(tp-forward 'marker nil nil 3)
;; 在字符串中搜索
(tp-forward 'marker nil my-string 2)
;; => ((0 5 t) (12 17 t))
```
---
@ -597,19 +602,26 @@ Emacs 的 `text-property-search-forward` 和 `text-property-search-backward` 的
(tp-backward-do FUNCTION PROPERTY &optional VALUE OBJECT N)
```
向前/向后搜索 N 次具有 PROPERTY 的文本,并对每个匹配应用 FUNCTION。
向前/向后搜索 N 次具有 PROPERTY 的文本,并对匹配的文本应用 FUNCTION。
- **FUNCTION** 接收两个参数prop-match 对象和 OBJECT
- **FUNCTION** 接收匹配到的文本作为唯一参数。FUNCTION 的返回值将替换字符串或缓冲区中的匹配文本
- **N** 是搜索次数,默认为 1。
- **OBJECT** 可以是缓冲区或字符串nil 默认为当前缓冲区。
- 返回成功匹配的数量。
**示例:**
```elisp
;; 对下 3 个 marker 应用函数
;; 将缓冲区中匹配的文本转为大写
(tp-forward-do #'upcase 'marker nil nil 3)
;; 将字符串中匹配的文本转为大写
(tp-forward-do #'upcase 'marker nil my-string 2)
;; 自定义转换
(tp-forward-do
(lambda (match obj)
(message "在 %d 处找到" (prop-match-beginning match)))
(lambda (text)
(concat "[" text "]"))
'marker nil nil 3)
```
@ -646,36 +658,35 @@ Emacs 的 `text-property-search-forward` 和 `text-property-search-backward` 的
---
#### `tp-search-do` - 对所有匹配应用函数
#### `tp-search-map` - 对匹配文本应用函数
```elisp
;; 缓冲区/字符串区域
(tp-search-do FUNCTION START END PROPERTY &optional VALUE OBJECT)
(tp-search-map FUNCTION START END PROPERTY &optional VALUE OBJECT)
;; 整个字符串
(tp-search-do FUNCTION STRING PROPERTY &optional VALUE)
(tp-search-map FUNCTION STRING PROPERTY &optional VALUE)
```
在缓冲区/字符串范围或整个字符串中对所有 PROPERTY 匹配执行 FUNCTION。
对所有 PROPERTY 匹配的文本应用 FUNCTION。
- **FUNCTION** 接收两个参数匹配START END VALUE 列表)和 OBJECT
- **FUNCTION** 接收匹配到的文本作为唯一参数。FUNCTION 的返回值将替换字符串或缓冲区中的匹配文本
- 返回处理的匹配数量。
**示例:**
```elisp
;; 处理缓冲区范围内的所有 marker
(tp-search-do
(lambda (match obj)
(message "在 %d-%d 处找到,值为 %s"
(car match) (cadr match) (caddr match)))
1 100 'marker)
;; 将字符串中所有 marker 文本转为大写
(tp-search-map #'upcase my-string 'marker)
;; 处理字符串中的所有标题
(tp-search-do
(lambda (match obj)
(upcase (substring obj (car match) (cadr match))))
my-string 'type 'heading)
;; 将缓冲区范围内所有 marker 文本转为大写
(tp-search-map #'upcase 1 100 'marker)
;; 自定义转换
(tp-search-map
(lambda (text)
(concat "[" text "]"))
my-string 'marker)
```
---

View File

@ -557,6 +557,16 @@
(should match)
(should (= (prop-match-beginning match) 7)))))
(ert-deftest tp-test-forward-on-string ()
"Test tp-forward works on string objects."
(let ((str (copy-sequence "Hello World Hello")))
(tp-set 0 5 '(marker t) str)
(tp-set 12 17 '(marker t) str)
(let ((matches (tp-forward 'marker nil str 2)))
(should (= (length matches) 2))
(should (equal (car matches) '(0 5 t)))
(should (equal (cadr matches) '(12 17 t))))))
(ert-deftest tp-test-forward-with-n ()
"Test tp-forward with N parameter."
(tp-test-with-temp-buffer
@ -583,8 +593,34 @@
(should match)
(should (= (prop-match-beginning match) 1)))))
(ert-deftest tp-test-backward-on-string ()
"Test tp-backward works on string objects."
(let ((str (copy-sequence "Hello World Hello")))
(tp-set 0 5 '(marker t) str)
(tp-set 12 17 '(marker t) str)
(let ((matches (tp-backward 'marker nil str 2)))
(should (= (length matches) 2))
;; Backward returns matches in reverse order
(should (equal (car matches) '(12 17 t)))
(should (equal (cadr matches) '(0 5 t))))))
(ert-deftest tp-test-forward-do ()
"Test tp-forward-do applies function to matches."
"Test tp-forward-do applies function to matched text."
(tp-test-with-temp-buffer
(insert "hello World test")
(tp-set 1 6 '(marker t))
(tp-set 13 17 '(marker t))
(goto-char 1)
(skip-unless (fboundp 'text-property-search-forward))
;; Test that function receives text and can transform it
(let ((count (tp-forward-do #'upcase 'marker nil nil 2)))
(should (= count 2))
;; Check that text was upcased
(should (equal (buffer-substring 1 6) "HELLO"))
(should (equal (buffer-substring 13 17) "TEST")))))
(ert-deftest tp-test--forward-do ()
"Test tp--forward-do applies function to matches (internal API)."
(tp-test-with-temp-buffer
(insert "Hello World Test")
(tp-set 1 6 '(marker t))
@ -592,14 +628,29 @@
(goto-char 1)
(skip-unless (fboundp 'text-property-search-forward))
(let ((result nil))
(tp-forward-do
(tp--forward-do
(lambda (match obj)
(push (prop-match-beginning match) result))
'marker nil nil 2)
(should (= (length result) 2)))))
(ert-deftest tp-test-backward-do ()
"Test tp-backward-do applies function to matches."
"Test tp-backward-do applies function to matched text."
(tp-test-with-temp-buffer
(insert "hello World test")
(tp-set 1 6 '(marker t))
(tp-set 13 17 '(marker t))
(goto-char 18)
(skip-unless (fboundp 'text-property-search-backward))
;; Test that function receives text and can transform it
(let ((count (tp-backward-do #'upcase 'marker nil nil 2)))
(should (= count 2))
;; Check that text was upcased
(should (equal (buffer-substring 1 6) "HELLO"))
(should (equal (buffer-substring 13 17) "TEST")))))
(ert-deftest tp-test--backward-do ()
"Test tp--backward-do applies function to matches (internal API)."
(tp-test-with-temp-buffer
(insert "Hello World Test")
(tp-set 1 6 '(marker t))
@ -607,12 +658,34 @@
(goto-char 18)
(skip-unless (fboundp 'text-property-search-backward))
(let ((result nil))
(tp-backward-do
(tp--backward-do
(lambda (match obj)
(push (prop-match-beginning match) result))
'marker nil nil 2)
(should (= (length result) 2)))))
(ert-deftest tp-test-forward-do-on-string ()
"Test tp-forward-do works on string objects."
(let ((str (copy-sequence "hello World hello")))
(tp-set 0 5 '(marker t) str)
(tp-set 12 17 '(marker t) str)
(let ((count (tp-forward-do #'upcase 'marker nil str 2)))
(should (= count 2))
;; Check that text was upcased
(should (equal (substring str 0 5) "HELLO"))
(should (equal (substring str 12 17) "HELLO")))))
(ert-deftest tp-test-backward-do-on-string ()
"Test tp-backward-do works on string objects."
(let ((str (copy-sequence "hello World hello")))
(tp-set 0 5 '(marker t) str)
(tp-set 12 17 '(marker t) str)
(let ((count (tp-backward-do #'upcase 'marker nil str 2)))
(should (= count 2))
;; Check that text was upcased
(should (equal (substring str 0 5) "HELLO"))
(should (equal (substring str 12 17) "HELLO")))))
(ert-deftest tp-test-search-on-string ()
"Test tp-search finds all matching properties in a string."
(let ((str (copy-sequence "Hello World Hello")))
@ -645,13 +718,13 @@
(should (equal (car matches) '(1 6 t)))
(should (equal (cadr matches) '(13 18 t))))))
(ert-deftest tp-test-search-do-on-string ()
"Test tp-search-do applies function to all matches in a string."
(ert-deftest tp-test--search-do-on-string ()
"Test tp--search-do applies function to all matches in a string (internal API)."
(let ((str (copy-sequence "Hello World Hello")))
(tp-set 0 5 '(marker t) str)
(tp-set 12 17 '(marker t) str)
(let ((result nil))
(tp-search-do
(tp--search-do
(lambda (match obj)
(push (car match) result))
str 'marker)
@ -659,14 +732,14 @@
(should (member 0 result))
(should (member 12 result)))))
(ert-deftest tp-test-search-do-in-range ()
"Test tp-search-do applies function to all matches in a buffer range."
(ert-deftest tp-test--search-do-in-range ()
"Test tp--search-do applies function to all matches in a buffer range (internal API)."
(tp-test-with-temp-buffer
(insert "Hello World Hello")
(tp-set 1 6 '(marker t))
(tp-set 13 18 '(marker t))
(let ((result nil))
(tp-search-do
(tp--search-do
(lambda (match obj)
(push (car match) result))
1 18 'marker)
@ -674,6 +747,29 @@
(should (member 1 result))
(should (member 13 result)))))
(ert-deftest tp-test-search-map-on-string ()
"Test tp-search-map applies function to matched text in a string."
(let ((str (copy-sequence "hello World hello")))
(tp-set 0 5 '(marker t) str)
(tp-set 12 17 '(marker t) str)
(let ((count (tp-search-map #'upcase str 'marker)))
(should (= count 2))
;; Check that text was upcased
(should (equal (substring str 0 5) "HELLO"))
(should (equal (substring str 12 17) "HELLO")))))
(ert-deftest tp-test-search-map-in-range ()
"Test tp-search-map applies function to matched text in a buffer range."
(tp-test-with-temp-buffer
(insert "hello World hello")
(tp-set 1 6 '(marker t))
(tp-set 13 18 '(marker t))
(let ((count (tp-search-map #'upcase 1 18 'marker)))
(should (= count 2))
;; Check that text was upcased
(should (equal (buffer-substring 1 6) "HELLO"))
(should (equal (buffer-substring 13 18) "HELLO")))))
;;; ============================================================
;;; Utility Function Tests
;;; ============================================================

211
tp.el
View File

@ -1012,76 +1012,181 @@ VALUE, PREDICATE, and NOT-CURRENT work as in `text-property-search-backward'."
N is the number of searches, defaulting to 1.
VALUE is the optional value to match.
OBJECT can be a buffer; nil defaults to current buffer.
OBJECT can be a buffer or string; nil defaults to current buffer.
Returns the prop-match object from the last successful search,
or nil if not found.
For buffers, returns the prop-match object from the last successful search.
For strings, returns a list of (START END VALUE) for all matches found.
Uses `tp-search-forward' internally."
(let ((count (or n 1))
(result nil)
Uses `tp-search-forward' for buffers and `tp-search' for strings."
(let ((count (or n 1)))
(cond
;; String object - use tp-search
((stringp object)
(let ((matches (tp-search object property value)))
(seq-take matches count)))
;; Buffer or nil
(t
(let ((result nil)
(buf (or object (current-buffer))))
(with-current-buffer buf
(dotimes (_ count)
(setq result (tp-search-forward property value))))
result))
result)))))
(defun tp-backward (property &optional value object n)
"Search backward N times for text with PROPERTY.
N is the number of searches, defaulting to 1.
VALUE is the optional value to match.
OBJECT can be a buffer; nil defaults to current buffer.
OBJECT can be a buffer or string; nil defaults to current buffer.
Returns the prop-match object from the last successful search,
or nil if not found.
For buffers, returns the prop-match object from the last successful search.
For strings, returns a list of (START END VALUE) for the last N matches
in reverse order (from end to start).
Uses `tp-search-backward' internally."
(let ((count (or n 1))
(result nil)
Uses `tp-search-backward' for buffers and `tp-search' for strings."
(let ((count (or n 1)))
(cond
;; String object - use tp-search and reverse
((stringp object)
(let ((matches (nreverse (tp-search object property value))))
(seq-take matches count)))
;; Buffer or nil
(t
(let ((result nil)
(buf (or object (current-buffer))))
(with-current-buffer buf
(dotimes (_ count)
(setq result (tp-search-backward property value))))
result))
result)))))
(defun tp-forward-do (function property &optional value object n)
"Search forward N times for text with PROPERTY and apply FUNCTION to each match.
(defun tp--forward-do (function property &optional value object n)
"Internal: Search forward N times for PROPERTY and apply FUNCTION to each match.
FUNCTION receives two arguments: the prop-match object and OBJECT.
FUNCTION receives two arguments: the prop-match object (or list for strings)
and OBJECT.
N is the number of searches, defaulting to 1.
VALUE is the optional value to match.
OBJECT can be a buffer; nil defaults to current buffer.
OBJECT can be a buffer or string; nil defaults to current buffer.
Returns the number of successful matches."
(let ((count (or n 1))
(matches 0)
(let ((count (or n 1)))
(cond
;; String object
((stringp object)
(let ((matches (seq-take (tp-search object property value) count)))
(dolist (match matches)
(funcall function match object))
(length matches)))
;; Buffer or nil
(t
(let ((matches 0)
(buf (or object (current-buffer))))
(with-current-buffer buf
(dotimes (_ count)
(when-let ((match (tp-search-forward property value)))
(funcall function match buf)
(cl-incf matches))))
matches))
matches)))))
(defun tp-backward-do (function property &optional value object n)
"Search backward N times for text with PROPERTY and apply FUNCTION to each match.
(defun tp-forward-do (function property &optional value object n)
"Search forward N times for text with PROPERTY and apply FUNCTION to each match.
FUNCTION receives the matched text as its only argument. The return value
of FUNCTION replaces the matched text in the string or buffer.
FUNCTION receives two arguments: the prop-match object and OBJECT.
N is the number of searches, defaulting to 1.
VALUE is the optional value to match.
OBJECT can be a buffer; nil defaults to current buffer.
OBJECT can be a buffer or string; nil defaults to current buffer.
Returns the number of successful matches.
Example:
;; Upcase all matched text
(tp-forward-do #\\='upcase \\='marker nil my-string 3)"
(tp--forward-do
(lambda (match obj)
(let* ((start (if (listp match) (car match) (prop-match-beginning match)))
(end (if (listp match) (cadr match) (prop-match-end match)))
(text (if (stringp obj)
(substring obj start end)
(buffer-substring start end)))
(new-text (funcall function text)))
(when (and new-text (not (equal new-text text)))
(if (stringp obj)
;; For strings, we need to replace in-place
(progn
(store-substring obj start new-text))
;; For buffers, delete and insert
(save-excursion
(delete-region start end)
(goto-char start)
(insert new-text))))))
property value object n))
(defun tp--backward-do (function property &optional value object n)
"Internal: Search backward N times for PROPERTY and apply FUNCTION to each match.
FUNCTION receives two arguments: the prop-match object (or list for strings)
and OBJECT.
N is the number of searches, defaulting to 1.
VALUE is the optional value to match.
OBJECT can be a buffer or string; nil defaults to current buffer.
Returns the number of successful matches."
(let ((count (or n 1))
(matches 0)
(let ((count (or n 1)))
(cond
;; String object - reverse the matches
((stringp object)
(let ((matches (seq-take (nreverse (tp-search object property value)) count)))
(dolist (match matches)
(funcall function match object))
(length matches)))
;; Buffer or nil
(t
(let ((matches 0)
(buf (or object (current-buffer))))
(with-current-buffer buf
(dotimes (_ count)
(when-let ((match (tp-search-backward property value)))
(funcall function match buf)
(cl-incf matches))))
matches))
matches)))))
(defun tp-backward-do (function property &optional value object n)
"Search backward N times for text with PROPERTY and apply FUNCTION to each match.
FUNCTION receives the matched text as its only argument. The return value
of FUNCTION replaces the matched text in the string or buffer.
N is the number of searches, defaulting to 1.
VALUE is the optional value to match.
OBJECT can be a buffer or string; nil defaults to current buffer.
Returns the number of successful matches.
Example:
;; Upcase all matched text
(tp-backward-do #\\='upcase \\='marker nil my-string 3)"
(tp--backward-do
(lambda (match obj)
(let* ((start (if (listp match) (car match) (prop-match-beginning match)))
(end (if (listp match) (cadr match) (prop-match-end match)))
(text (if (stringp obj)
(substring obj start end)
(buffer-substring start end)))
(new-text (funcall function text)))
(when (and new-text (not (equal new-text text)))
(if (stringp obj)
;; For strings, we need to replace in-place
(progn
(store-substring obj start new-text))
;; For buffers, delete and insert
(save-excursion
(delete-region start end)
(goto-char start)
(insert new-text))))))
property value object n))
(defun tp-search (start-or-string &optional end-or-property property-or-value value object)
"Search for all text with PROPERTY in a buffer/string range or entire string.
@ -1157,16 +1262,16 @@ Each element contains the start position, end position, and property value."
(nreverse results)))
(t (error "Invalid first argument: %S" start-or-string))))
(defun tp-search-do (function start-or-string &optional end-or-property property-or-value value object)
"Execute FUNCTION on all matches of PROPERTY in a buffer/string range or entire string.
(defun tp--search-do (function start-or-string &optional end-or-property property-or-value value object)
"Internal: Execute FUNCTION on all matches of PROPERTY.
This function supports two calling conventions:
1. Buffer/string region:
(tp-search-do FUNCTION START END PROPERTY &optional VALUE OBJECT)
(tp--search-do FUNCTION START END PROPERTY &optional VALUE OBJECT)
2. Entire string:
(tp-search-do FUNCTION STRING PROPERTY &optional VALUE)
(tp--search-do FUNCTION STRING PROPERTY &optional VALUE)
FUNCTION receives two arguments: the prop-match (list of START END VALUE) and OBJECT.
Returns the number of matches processed."
@ -1187,6 +1292,48 @@ Returns the number of matches processed."
(funcall function match obj))
(length matches)))
(defun tp-search-map (function start-or-string &optional end-or-property property-or-value value object)
"Apply FUNCTION to matched text for all matches of PROPERTY.
This function supports two calling conventions:
1. Buffer/string region:
(tp-search-map FUNCTION START END PROPERTY &optional VALUE OBJECT)
2. Entire string:
(tp-search-map FUNCTION STRING PROPERTY &optional VALUE)
FUNCTION receives the matched text as its only argument. The return value
of FUNCTION replaces the matched text in the string or buffer.
Returns the number of matches processed.
Example:
;; Upcase all matched text
(tp-search-map #\\='upcase my-string \\='marker)"
(let ((obj (cond
((stringp start-or-string) start-or-string)
((numberp start-or-string) (or object (current-buffer)))
(t nil))))
(tp--search-do
(lambda (match obj)
(let* ((start (car match))
(end (cadr match))
(text (if (stringp obj)
(substring obj start end)
(buffer-substring start end)))
(new-text (funcall function text)))
(when (and new-text (not (equal new-text text)))
(if (stringp obj)
;; For strings, replace in-place
(store-substring obj start new-text)
;; For buffers, delete and insert
(save-excursion
(delete-region start end)
(goto-char start)
(insert new-text))))))
start-or-string end-or-property property-or-value value object)))
;;; Query Functions