Update tp-forward-do/tp-backward-do docs and tests for "last match only" behavior
Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
parent
ed8baf3970
commit
47b6505d51
28
README.md
28
README.md
@ -885,11 +885,10 @@ Search forward/backward N times for text with PROPERTY.
|
||||
(tp-backward-do FUNCTION PROPERTY &optional VALUE OBJECT POINT N)
|
||||
```
|
||||
|
||||
Search forward/backward N times for text with PROPERTY and apply FUNCTION to matched text.
|
||||
Search forward/backward N times for text with PROPERTY and apply FUNCTION **only to the last 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.
|
||||
- **FUNCTION** receives the matched text as its first argument. Optionally, FUNCTION can accept two additional arguments: START and END, representing the start and end positions of the match. The return value of FUNCTION replaces the matched text in the string or buffer.
|
||||
- **N** is the number of searches, defaulting to 1. The function searches N times but only applies FUNCTION to the last (Nth) match found.
|
||||
- **OBJECT** can be a buffer or string; nil defaults to current buffer.
|
||||
- **POINT** is the starting position for search; for buffers nil means current point,
|
||||
for strings nil means 0 (forward) or end of string (backward).
|
||||
@ -898,7 +897,7 @@ Search forward/backward N times for text with PROPERTY and apply FUNCTION to mat
|
||||
**Examples:**
|
||||
|
||||
```elisp
|
||||
;; Upcase matched text in buffer (starting from current point)
|
||||
;; Upcase only the last (2nd) match in buffer
|
||||
(with-temp-buffer
|
||||
(insert "hello world test")
|
||||
(tp-set 1 6 '(marker t))
|
||||
@ -906,17 +905,17 @@ Search forward/backward N times for text with PROPERTY and apply FUNCTION to mat
|
||||
(goto-char 1)
|
||||
(tp-forward-do #'upcase 'marker nil nil nil 2)
|
||||
(buffer-string))
|
||||
;; => "HELLO world TEST"
|
||||
;; => "hello world TEST" ; Only the 2nd match is upcased
|
||||
|
||||
;; Upcase matched text in string (starting from position 0)
|
||||
;; Upcase only the last (2nd) match in string
|
||||
(let ((my-string (copy-sequence "hello world hello")))
|
||||
(tp-set 0 5 '(marker t) my-string)
|
||||
(tp-set 12 17 '(marker t) my-string)
|
||||
(tp-forward-do #'upcase 'marker nil my-string nil 2)
|
||||
my-string)
|
||||
;; => "HELLO world HELLO"
|
||||
;; => "hello world HELLO" ; Only the 2nd match is upcased
|
||||
|
||||
;; Start search from specific position
|
||||
;; Start search from specific position (only 1 match found and transformed)
|
||||
(let ((my-string (copy-sequence "hello world hello")))
|
||||
(tp-set 0 5 '(marker t) my-string)
|
||||
(tp-set 12 17 '(marker t) my-string)
|
||||
@ -924,17 +923,18 @@ Search forward/backward N times for text with PROPERTY and apply FUNCTION to mat
|
||||
my-string)
|
||||
;; => "hello world HELLO" ; Only matches from position 6 onward
|
||||
|
||||
;; Custom transformation
|
||||
;; Using function with start and end parameters
|
||||
(with-temp-buffer
|
||||
(insert "hello world test")
|
||||
(tp-set 1 6 '(marker t))
|
||||
(tp-set 13 17 '(marker t))
|
||||
(goto-char 1)
|
||||
(tp-forward-do
|
||||
(lambda (text)
|
||||
(concat "[" text "]"))
|
||||
'marker nil nil nil 1)
|
||||
(lambda (text start end)
|
||||
(format "[%d-%d]%s" start end text))
|
||||
'marker nil nil nil 2)
|
||||
(buffer-string))
|
||||
;; => "[hello] world test"
|
||||
;; => "hello world [13-17]test" ; Only the last match is transformed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
27
README_CN.md
27
README_CN.md
@ -884,10 +884,10 @@ Emacs 的 `text-property-search-forward` 和 `text-property-search-backward` 的
|
||||
(tp-backward-do FUNCTION PROPERTY &optional VALUE OBJECT POINT N)
|
||||
```
|
||||
|
||||
向前/向后搜索 N 次具有 PROPERTY 的文本,并对匹配的文本应用 FUNCTION。
|
||||
向前/向后搜索 N 次具有 PROPERTY 的文本,**仅对最后一次匹配应用 FUNCTION**。
|
||||
|
||||
- **FUNCTION** 接收匹配到的文本作为唯一参数。FUNCTION 的返回值将替换字符串或缓冲区中的匹配文本。
|
||||
- **N** 是搜索次数,默认为 1。
|
||||
- **FUNCTION** 接收匹配到的文本作为第一个参数。可选地,FUNCTION 可以接受两个额外的参数:START 和 END,表示匹配的起始和结束位置。FUNCTION 的返回值将替换字符串或缓冲区中的匹配文本。
|
||||
- **N** 是搜索次数,默认为 1。该函数会搜索 N 次,但仅对找到的最后(第 N 次)匹配应用 FUNCTION。
|
||||
- **OBJECT** 可以是缓冲区或字符串;nil 默认为当前缓冲区。
|
||||
- **POINT** 是搜索的起始位置;对于缓冲区 nil 表示当前位置,对于字符串 nil 表示 0(向前)或字符串末尾(向后)。
|
||||
- 返回成功匹配的数量。
|
||||
@ -895,7 +895,7 @@ Emacs 的 `text-property-search-forward` 和 `text-property-search-backward` 的
|
||||
**示例:**
|
||||
|
||||
```elisp
|
||||
;; 将缓冲区中匹配的文本转为大写(从当前位置开始)
|
||||
;; 仅将最后一次(第 2 次)匹配的文本转为大写
|
||||
(with-temp-buffer
|
||||
(insert "hello world test")
|
||||
(tp-set 1 6 '(marker t))
|
||||
@ -903,17 +903,17 @@ Emacs 的 `text-property-search-forward` 和 `text-property-search-backward` 的
|
||||
(goto-char 1)
|
||||
(tp-forward-do #'upcase 'marker nil nil nil 2)
|
||||
(buffer-string))
|
||||
;; => "HELLO world TEST"
|
||||
;; => "hello world TEST" ; 仅第 2 次匹配被转为大写
|
||||
|
||||
;; 将字符串中匹配的文本转为大写(从位置 0 开始)
|
||||
;; 仅将最后一次(第 2 次)匹配的文本转为大写
|
||||
(let ((my-string (copy-sequence "hello world hello")))
|
||||
(tp-set 0 5 '(marker t) my-string)
|
||||
(tp-set 12 17 '(marker t) my-string)
|
||||
(tp-forward-do #'upcase 'marker nil my-string nil 2)
|
||||
my-string)
|
||||
;; => "HELLO world HELLO"
|
||||
;; => "hello world HELLO" ; 仅第 2 次匹配被转为大写
|
||||
|
||||
;; 从特定位置开始搜索
|
||||
;; 从特定位置开始搜索(仅找到 1 次匹配并转换)
|
||||
(let ((my-string (copy-sequence "hello world hello")))
|
||||
(tp-set 0 5 '(marker t) my-string)
|
||||
(tp-set 12 17 '(marker t) my-string)
|
||||
@ -921,17 +921,18 @@ Emacs 的 `text-property-search-forward` 和 `text-property-search-backward` 的
|
||||
my-string)
|
||||
;; => "hello world HELLO" ; 只处理位置 6 之后的匹配
|
||||
|
||||
;; 自定义转换
|
||||
;; 使用带有 start 和 end 参数的函数
|
||||
(with-temp-buffer
|
||||
(insert "hello world test")
|
||||
(tp-set 1 6 '(marker t))
|
||||
(tp-set 13 17 '(marker t))
|
||||
(goto-char 1)
|
||||
(tp-forward-do
|
||||
(lambda (text)
|
||||
(concat "[" text "]"))
|
||||
'marker nil nil nil 1)
|
||||
(lambda (text start end)
|
||||
(format "[%d-%d]%s" start end text))
|
||||
'marker nil nil nil 2)
|
||||
(buffer-string))
|
||||
;; => "[hello] world test"
|
||||
;; => "hello world [13-17]test" ; 仅最后一次匹配被转换
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
98
tp-tests.el
98
tp-tests.el
@ -643,22 +643,23 @@
|
||||
(should (equal (cadr matches) '(0 5 t))))))
|
||||
|
||||
(ert-deftest tp-test-forward-do ()
|
||||
"Test tp-forward-do applies function to matched text."
|
||||
"Test tp-forward-do applies function only to the last match."
|
||||
(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
|
||||
;; Test that function is applied only to the last (2nd) match
|
||||
(let ((count (tp-forward-do #'upcase 'marker nil nil nil 2)))
|
||||
(should (= count 2))
|
||||
;; Check that text was upcased
|
||||
(should (equal (buffer-substring 1 6) "HELLO"))
|
||||
;; First match should NOT be upcased
|
||||
(should (equal (buffer-substring 1 6) "hello"))
|
||||
;; Only the last (2nd) match should be upcased
|
||||
(should (equal (buffer-substring 13 17) "TEST")))))
|
||||
|
||||
(ert-deftest tp-test--forward-do ()
|
||||
"Test tp--forward-do applies function to matches (internal API)."
|
||||
"Test tp--forward-do applies function only to the last match (internal API)."
|
||||
(tp-test-with-temp-buffer
|
||||
(insert "Hello World Test")
|
||||
(tp-set 1 6 '(marker t))
|
||||
@ -670,25 +671,28 @@
|
||||
(lambda (match obj)
|
||||
(push (prop-match-beginning match) result))
|
||||
'marker nil nil nil 2)
|
||||
(should (= (length result) 2)))))
|
||||
;; Only the last match should be processed
|
||||
(should (= (length result) 1))
|
||||
(should (= (car result) 13)))))
|
||||
|
||||
(ert-deftest tp-test-backward-do ()
|
||||
"Test tp-backward-do applies function to matched text."
|
||||
"Test tp-backward-do applies function only to the last match."
|
||||
(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
|
||||
;; Test that function is applied only to the last (2nd) match
|
||||
(let ((count (tp-backward-do #'upcase 'marker nil nil nil 2)))
|
||||
(should (= count 2))
|
||||
;; Check that text was upcased
|
||||
;; Only the last (2nd) match should be upcased
|
||||
(should (equal (buffer-substring 1 6) "HELLO"))
|
||||
(should (equal (buffer-substring 13 17) "TEST")))))
|
||||
;; First match (searched backward) should NOT be upcased
|
||||
(should (equal (buffer-substring 13 17) "test")))))
|
||||
|
||||
(ert-deftest tp-test--backward-do ()
|
||||
"Test tp--backward-do applies function to matches (internal API)."
|
||||
"Test tp--backward-do applies function only to the last match (internal API)."
|
||||
(tp-test-with-temp-buffer
|
||||
(insert "Hello World Test")
|
||||
(tp-set 1 6 '(marker t))
|
||||
@ -700,29 +704,33 @@
|
||||
(lambda (match obj)
|
||||
(push (prop-match-beginning match) result))
|
||||
'marker nil nil nil 2)
|
||||
(should (= (length result) 2)))))
|
||||
;; Only the last match should be processed
|
||||
(should (= (length result) 1))
|
||||
(should (= (car result) 1)))))
|
||||
|
||||
(ert-deftest tp-test-forward-do-on-string ()
|
||||
"Test tp-forward-do works on string objects."
|
||||
"Test tp-forward-do applies only to the last match 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 nil 2)))
|
||||
(should (= count 2))
|
||||
;; Check that text was upcased
|
||||
(should (equal (substring str 0 5) "HELLO"))
|
||||
;; First match should NOT be upcased
|
||||
(should (equal (substring str 0 5) "hello"))
|
||||
;; Only the last (2nd) match should be upcased
|
||||
(should (equal (substring str 12 17) "HELLO")))))
|
||||
|
||||
(ert-deftest tp-test-backward-do-on-string ()
|
||||
"Test tp-backward-do works on string objects."
|
||||
"Test tp-backward-do applies only to the last match 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 nil 2)))
|
||||
(should (= count 2))
|
||||
;; Check that text was upcased
|
||||
;; Only the last (2nd) match should be upcased
|
||||
(should (equal (substring str 0 5) "HELLO"))
|
||||
(should (equal (substring str 12 17) "HELLO")))))
|
||||
;; First match (searched backward) should NOT be upcased
|
||||
(should (equal (substring str 12 17) "hello")))))
|
||||
|
||||
(ert-deftest tp-test-forward-do-with-point ()
|
||||
"Test tp-forward-do with point parameter."
|
||||
@ -751,7 +759,7 @@
|
||||
(should (equal (substring str 12 17) "hello")))))
|
||||
|
||||
(ert-deftest tp-test-forward-do-with-start-end ()
|
||||
"Test tp-forward-do passes optional start and end to function."
|
||||
"Test tp-forward-do passes optional start and end to function for last match only."
|
||||
(let ((str (copy-sequence "hello World hello"))
|
||||
(starts nil)
|
||||
(ends nil))
|
||||
@ -764,15 +772,15 @@
|
||||
(upcase txt))
|
||||
'marker nil str nil 2)))
|
||||
(should (= count 2))
|
||||
;; Check positions were passed correctly
|
||||
(should (equal (sort starts #'<) '(0 12)))
|
||||
(should (equal (sort ends #'<) '(5 17)))
|
||||
;; Check text was upcased
|
||||
(should (equal (substring str 0 5) "HELLO"))
|
||||
;; Check only the last match positions were passed
|
||||
(should (equal starts '(12)))
|
||||
(should (equal ends '(17)))
|
||||
;; Only the last match should be upcased
|
||||
(should (equal (substring str 0 5) "hello"))
|
||||
(should (equal (substring str 12 17) "HELLO")))))
|
||||
|
||||
(ert-deftest tp-test-forward-do-with-start-only ()
|
||||
"Test tp-forward-do passes start when function accepts 2 args."
|
||||
"Test tp-forward-do passes start to function for last match only when function accepts 2 args."
|
||||
(let ((str (copy-sequence "hello World hello"))
|
||||
(starts nil))
|
||||
(tp-set 0 5 '(marker t) str)
|
||||
@ -783,24 +791,25 @@
|
||||
(upcase txt))
|
||||
'marker nil str nil 2)))
|
||||
(should (= count 2))
|
||||
;; Check start positions were passed correctly
|
||||
(should (equal (sort starts #'<) '(0 12)))
|
||||
;; Check text was upcased
|
||||
(should (equal (substring str 0 5) "HELLO"))
|
||||
;; Check only the last match start position was passed
|
||||
(should (equal starts '(12)))
|
||||
;; Only the last match should be upcased
|
||||
(should (equal (substring str 0 5) "hello"))
|
||||
(should (equal (substring str 12 17) "HELLO")))))
|
||||
|
||||
(ert-deftest tp-test-forward-do-backward-compat ()
|
||||
"Test tp-forward-do works with single-argument functions (backward compat)."
|
||||
"Test tp-forward-do applies only to last match with single-argument functions."
|
||||
(let ((str (copy-sequence "hello World hello")))
|
||||
(tp-set 0 5 '(marker t) str)
|
||||
(tp-set 12 17 '(marker t) str)
|
||||
;; Use #'upcase which only takes one argument
|
||||
(tp-forward-do #'upcase 'marker nil str nil 2)
|
||||
(should (equal (substring str 0 5) "HELLO"))
|
||||
;; Only the last match should be upcased
|
||||
(should (equal (substring str 0 5) "hello"))
|
||||
(should (equal (substring str 12 17) "HELLO"))))
|
||||
|
||||
(ert-deftest tp-test-backward-do-with-start-end ()
|
||||
"Test tp-backward-do passes optional start and end to function."
|
||||
"Test tp-backward-do passes optional start and end to function for last match only."
|
||||
(let ((str (copy-sequence "hello World hello"))
|
||||
(starts nil)
|
||||
(ends nil))
|
||||
@ -813,15 +822,15 @@
|
||||
(upcase txt))
|
||||
'marker nil str nil 2)))
|
||||
(should (= count 2))
|
||||
;; Check positions were passed correctly
|
||||
(should (equal (sort starts #'<) '(0 12)))
|
||||
(should (equal (sort ends #'<) '(5 17)))
|
||||
;; Check text was upcased
|
||||
;; Check only the last match positions were passed
|
||||
(should (equal starts '(0)))
|
||||
(should (equal ends '(5)))
|
||||
;; Only the last match should be upcased
|
||||
(should (equal (substring str 0 5) "HELLO"))
|
||||
(should (equal (substring str 12 17) "HELLO")))))
|
||||
(should (equal (substring str 12 17) "hello")))))
|
||||
|
||||
(ert-deftest tp-test-backward-do-with-start-only ()
|
||||
"Test tp-backward-do passes start when function accepts 2 args."
|
||||
"Test tp-backward-do passes start to function for last match only when function accepts 2 args."
|
||||
(let ((str (copy-sequence "hello World hello"))
|
||||
(starts nil))
|
||||
(tp-set 0 5 '(marker t) str)
|
||||
@ -832,21 +841,22 @@
|
||||
(upcase txt))
|
||||
'marker nil str nil 2)))
|
||||
(should (= count 2))
|
||||
;; Check start positions were passed correctly
|
||||
(should (equal (sort starts #'<) '(0 12)))
|
||||
;; Check text was upcased
|
||||
;; Check only the last match start position was passed
|
||||
(should (equal starts '(0)))
|
||||
;; Only the last match should be upcased
|
||||
(should (equal (substring str 0 5) "HELLO"))
|
||||
(should (equal (substring str 12 17) "HELLO")))))
|
||||
(should (equal (substring str 12 17) "hello")))))
|
||||
|
||||
(ert-deftest tp-test-backward-do-backward-compat ()
|
||||
"Test tp-backward-do works with single-argument functions (backward compat)."
|
||||
"Test tp-backward-do applies only to last match with single-argument functions."
|
||||
(let ((str (copy-sequence "hello World hello")))
|
||||
(tp-set 0 5 '(marker t) str)
|
||||
(tp-set 12 17 '(marker t) str)
|
||||
;; Use #'upcase which only takes one argument
|
||||
(tp-backward-do #'upcase 'marker nil str nil 2)
|
||||
;; Only the last match should be upcased
|
||||
(should (equal (substring str 0 5) "HELLO"))
|
||||
(should (equal (substring str 12 17) "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."
|
||||
|
||||
24
tp.el
24
tp.el
@ -1023,7 +1023,7 @@ Returns the number of successful matches."
|
||||
matches)))))
|
||||
|
||||
(defun tp-forward-do (function property &optional value object point n)
|
||||
"Search forward N times for text with PROPERTY and apply FUNCTION to each match.
|
||||
"Search forward N times for text with PROPERTY and apply FUNCTION only to the last match.
|
||||
|
||||
FUNCTION receives the matched text as its first argument. Optionally,
|
||||
FUNCTION can accept two additional arguments: START and END, representing
|
||||
@ -1032,7 +1032,8 @@ it receives (TEXT START). If it accepts 3 or more arguments, it receives
|
||||
(TEXT START END). The return value of FUNCTION replaces the matched text
|
||||
in the string or buffer.
|
||||
|
||||
N is the number of searches, defaulting to 1.
|
||||
N is the number of searches, defaulting to 1. The function searches N times
|
||||
but only applies FUNCTION to the last (Nth) match found.
|
||||
VALUE is the optional value to match.
|
||||
OBJECT can be a buffer or string; nil defaults to current buffer.
|
||||
POINT is the starting position for search; for buffers nil means current point,
|
||||
@ -1046,14 +1047,15 @@ If the replacement is shorter, only that portion will be replaced.
|
||||
If the replacement is longer, it will be truncated.
|
||||
|
||||
Example:
|
||||
;; Upcase all matched text
|
||||
;; Upcase only the last (2nd) match
|
||||
(setq my-string (copy-sequence \"hello world hello\"))
|
||||
(tp-set 0 5 \\='(marker t) my-string)
|
||||
(tp-set 12 17 \\='(marker t) my-string)
|
||||
(tp-forward-do #\\='upcase \\='marker nil my-string nil 3)
|
||||
(tp-forward-do #\\='upcase \\='marker nil my-string nil 2)
|
||||
;; => \"hello world HELLO\" - only the 2nd match is upcased
|
||||
;; Use start and end positions
|
||||
(tp-forward-do (lambda (txt start end) (format \"[%d-%d]%s\" start end txt))
|
||||
\\='marker nil my-string nil 3)"
|
||||
\\='marker nil my-string nil 2)"
|
||||
(let ((arity (func-arity function)))
|
||||
(tp--forward-do
|
||||
(lambda (match obj)
|
||||
@ -1137,7 +1139,7 @@ Returns the number of successful matches."
|
||||
matches)))))
|
||||
|
||||
(defun tp-backward-do (function property &optional value object point n)
|
||||
"Search backward N times for text with PROPERTY and apply FUNCTION to each match.
|
||||
"Search backward N times for text with PROPERTY and apply FUNCTION only to the last match.
|
||||
|
||||
FUNCTION receives the matched text as its first argument. Optionally,
|
||||
FUNCTION can accept two additional arguments: START and END, representing
|
||||
@ -1146,7 +1148,8 @@ it receives (TEXT START). If it accepts 3 or more arguments, it receives
|
||||
(TEXT START END). The return value of FUNCTION replaces the matched text
|
||||
in the string or buffer.
|
||||
|
||||
N is the number of searches, defaulting to 1.
|
||||
N is the number of searches, defaulting to 1. The function searches N times
|
||||
but only applies FUNCTION to the last (Nth) match found.
|
||||
VALUE is the optional value to match.
|
||||
OBJECT can be a buffer or string; nil defaults to current buffer.
|
||||
POINT is the starting position for search; for buffers nil means current point,
|
||||
@ -1160,14 +1163,15 @@ If the replacement is shorter, only that portion will be replaced.
|
||||
If the replacement is longer, it will be truncated.
|
||||
|
||||
Example:
|
||||
;; Upcase all matched text
|
||||
;; Upcase only the last (2nd) match
|
||||
(setq my-string (copy-sequence \"hello world hello\"))
|
||||
(tp-set 0 5 \\='(marker t) my-string)
|
||||
(tp-set 12 17 \\='(marker t) my-string)
|
||||
(tp-backward-do #\\='upcase \\='marker nil my-string nil 3)
|
||||
(tp-backward-do #\\='upcase \\='marker nil my-string nil 2)
|
||||
;; => \"hello world HELLO\" - only the 2nd (last) match is upcased
|
||||
;; Use start and end positions
|
||||
(tp-backward-do (lambda (txt start end) (format \"[%d-%d]%s\" start end txt))
|
||||
\\='marker nil my-string nil 3)"
|
||||
\\='marker nil my-string nil 2)"
|
||||
(let ((arity (func-arity function)))
|
||||
(tp--backward-do
|
||||
(lambda (match obj)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user