Unify -do shortfall to all-or-nothing on strings and buffers
tp-forward-do/tp-backward-do target the TIMES-th match specifically; when fewer matches exist the string paths applied FUNCTION to the last available match -- the wrong target -- while buffer paths applied nothing. Both now apply nothing on shortfall and return the available count. Updates the two legacy with-range tests that codified the string behavior, adds four shortfall/exact-count regression tests, and documents the contract in both READMEs and the docstrings. Suite 443/443 green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2b33495898
commit
ecdd952b55
16
CHANGELOG.md
16
CHANGELOG.md
@ -140,6 +140,13 @@ Search and navigation (tp-search):
|
||||
were previously skipped).
|
||||
- The triplicated ~38-line replacement lambda was extracted into one
|
||||
shared helper.
|
||||
- `tp-forward-do` / `tp-backward-do` shortfall is now all-or-nothing on
|
||||
both paths: TIMES targets the TIMES-th match specifically, so when
|
||||
fewer matches exist nothing is applied and the available count is
|
||||
returned. String paths previously acted on the last available match —
|
||||
the wrong target; the two legacy tests codifying that
|
||||
(`tp-test-forward-do-on-string-with-range` and its backward twin)
|
||||
were updated.
|
||||
|
||||
Reactive rendering (tp-reactive / tp-render):
|
||||
|
||||
@ -172,15 +179,6 @@ Test infrastructure:
|
||||
tests); the suite passes in randomized order.
|
||||
- `tp-tests.el` header and `provide` renamed to match its file name.
|
||||
|
||||
### Known divergences
|
||||
|
||||
- On shortfall (fewer than TIMES matches in the range),
|
||||
`tp-forward-do` / `tp-backward-do` string paths still apply the
|
||||
function to the last available match while buffer paths apply
|
||||
nothing. Two legacy tests codify the string behavior
|
||||
(`tp-test-forward-do-on-string-with-range` and its backward twin), so
|
||||
it was left unchanged; unifying it is a pending semantics decision.
|
||||
|
||||
### Added
|
||||
|
||||
- `tp-member`: like `tp-at`, but distinguishes "property present with
|
||||
|
||||
@ -1376,7 +1376,7 @@ Search forward/backward for text with PROPERTY and apply FUNCTION **only to the
|
||||
- **PROPERTY** is the text property to search for.
|
||||
- **VALUE** is the optional value to match; nil means search for PROPERTY without matching value.
|
||||
- **OBJECT** can be a buffer or string; nil defaults to current buffer.
|
||||
- **TIMES** is the number of searches, defaulting to 1. The function searches TIMES times but only applies FUNCTION to the last (Nth) match found.
|
||||
- **TIMES** is the number of searches, defaulting to 1. The function searches TIMES times but only applies FUNCTION to the TIMES-th match. All-or-nothing: if fewer than TIMES matches exist, FUNCTION is not applied at all and the number of available matches is returned.
|
||||
- **START** and **END** define the search range; defaults are object start and end.
|
||||
- Returns the number of successful matches.
|
||||
|
||||
|
||||
@ -1363,7 +1363,7 @@ Emacs 的 `text-property-search-forward` 和 `text-property-search-backward` 的
|
||||
- **PROPERTY** 是要搜索的文本属性。
|
||||
- **VALUE** 为 nil 时,表示搜索 PROPERTY 属性,不用匹配值。
|
||||
- **OBJECT** 默认是当前 buffer 或指定的字符串或指定的 buffer。
|
||||
- **TIMES** 表示向前/向后搜索几次,默认搜索一次。该函数会搜索 TIMES 次,但仅对找到的最后(第 N 次)匹配应用 FUNCTION。
|
||||
- **TIMES** 表示向前/向后搜索几次,默认搜索一次。该函数会搜索 TIMES 次,但仅对第 TIMES 个匹配应用 FUNCTION。要么全有要么全无:当匹配数量不足 TIMES 时,完全不应用 FUNCTION,仅返回实际找到的匹配数量。
|
||||
- **START** 和 **END** 默认为 OBJECT 的起始和结束位置。
|
||||
- 返回成功匹配的数量。
|
||||
|
||||
|
||||
@ -170,6 +170,40 @@ Old code silently wrote 10 chars, yielding \"hellohellod\"."
|
||||
(should (= (tp-search-map #'upcase 'marker nil str) 2))
|
||||
(should (equal (substring-no-properties str) "HELLO world HELLO"))))
|
||||
|
||||
;;; B43: -do shortfall is all-or-nothing on strings and buffers alike
|
||||
|
||||
(ert-deftest tp-search-test-forward-do-shortfall-string ()
|
||||
"Requesting the Nth match when fewer exist applies nothing (string).
|
||||
The count of available matches is still returned."
|
||||
(let ((str (copy-sequence "hello world")))
|
||||
(tp-set 0 5 '(marker t) str)
|
||||
(should (= (tp-forward-do #'upcase 'marker nil str 3) 1))
|
||||
(should (equal (substring-no-properties str) "hello world"))))
|
||||
|
||||
(ert-deftest tp-search-test-forward-do-shortfall-buffer ()
|
||||
"Requesting the Nth match when fewer exist applies nothing (buffer)."
|
||||
(with-temp-buffer
|
||||
(insert "hello world")
|
||||
(put-text-property 1 6 'marker t)
|
||||
(should (= (tp-forward-do #'upcase 'marker t nil 3) 1))
|
||||
(should (equal (buffer-substring-no-properties (point-min) (point-max))
|
||||
"hello world"))))
|
||||
|
||||
(ert-deftest tp-search-test-backward-do-shortfall-string ()
|
||||
"tp-backward-do shortfall applies nothing on strings."
|
||||
(let ((str (copy-sequence "hello world")))
|
||||
(tp-set 6 11 '(marker t) str)
|
||||
(should (= (tp-backward-do #'upcase 'marker nil str 2) 1))
|
||||
(should (equal (substring-no-properties str) "hello world"))))
|
||||
|
||||
(ert-deftest tp-search-test-forward-do-exact-count-applies ()
|
||||
"With exactly TIMES matches, FUNCTION is applied to the TIMES-th."
|
||||
(let ((str (copy-sequence "aaa bbb aaa")))
|
||||
(tp-set 0 3 '(marker t) str)
|
||||
(tp-set 8 11 '(marker t) str)
|
||||
(should (= (tp-forward-do #'upcase 'marker nil str 2) 2))
|
||||
(should (equal (substring-no-properties str) "aaa bbb AAA"))))
|
||||
|
||||
(ert-deftest tp-search-test-forward-do-buffer-longer-replacement-grows ()
|
||||
"Buffers may grow on longer replacements (delete-region + insert).
|
||||
Uses an explicit VALUE: the buffer paths of the -do functions match
|
||||
|
||||
16
tp-search.el
16
tp-search.el
@ -377,7 +377,9 @@ VALUE is the optional value to match.
|
||||
OBJECT can be a buffer or string; nil defaults to current buffer.
|
||||
START and END define the search range; defaults are object start and end.
|
||||
|
||||
Returns the number of successful matches."
|
||||
FUNCTION is called only when the TIMES-th match exists; if fewer
|
||||
matches are available, nothing is applied.
|
||||
Returns the number of matches found (at most TIMES)."
|
||||
(let ((count (or times 1)))
|
||||
(cond
|
||||
;; String object
|
||||
@ -390,7 +392,10 @@ Returns the number of successful matches."
|
||||
(<= (cadr m) end-pos)))
|
||||
all-matches))
|
||||
(matches (seq-take filtered-matches count)))
|
||||
(when matches
|
||||
;; All-or-nothing, mirroring the buffer path: FUNCTION targets
|
||||
;; the TIMES-th match specifically, so when fewer matches exist
|
||||
;; acting on a different one would hit the wrong target.
|
||||
(when (= (length matches) count)
|
||||
(funcall function (car (last matches)) object))
|
||||
(length matches)))
|
||||
;; Buffer or nil
|
||||
@ -530,7 +535,9 @@ VALUE is the optional value to match.
|
||||
OBJECT can be a buffer or string; nil defaults to current buffer.
|
||||
START and END define the search range; defaults are object start and end.
|
||||
|
||||
Returns the number of successful matches."
|
||||
FUNCTION is called only when the TIMES-th match exists; if fewer
|
||||
matches are available, nothing is applied.
|
||||
Returns the number of matches found (at most TIMES)."
|
||||
(let ((count (or times 1)))
|
||||
(cond
|
||||
;; String object - reverse the matches
|
||||
@ -544,7 +551,8 @@ Returns the number of successful matches."
|
||||
(<= (cadr m) end-pos)))
|
||||
all-matches))
|
||||
(matches (seq-take (nreverse filtered-matches) count)))
|
||||
(when matches
|
||||
;; All-or-nothing; see tp--forward-do.
|
||||
(when (= (length matches) count)
|
||||
(funcall function (car (last matches)) object))
|
||||
(length matches)))
|
||||
;; Buffer or nil
|
||||
|
||||
18
tp-tests.el
18
tp-tests.el
@ -718,7 +718,10 @@ leak between tests regardless of how BODY exits."
|
||||
(should (equal (substring str 12 17) "HELLO")))))
|
||||
|
||||
(ert-deftest tp-test-forward-do-on-string-with-range ()
|
||||
"Test tp-forward-do on string with start/end range."
|
||||
"Test tp-forward-do on string with start/end range.
|
||||
TIMES targets the TIMES-th match specifically; with only one match in
|
||||
range, asking for the 2nd applies nothing (all-or-nothing, matching
|
||||
the buffer path) and returns the available count."
|
||||
(let ((str (copy-sequence "hello World hello")))
|
||||
(tp-set 0 5 '(marker t) str)
|
||||
(tp-set 12 17 '(marker t) str)
|
||||
@ -727,8 +730,8 @@ leak between tests regardless of how BODY exits."
|
||||
(should (= count 1)) ; Only one match in range 6-17
|
||||
;; First match should NOT be upcased
|
||||
(should (equal (substring str 0 5) "hello"))
|
||||
;; Second match should be upcased
|
||||
(should (equal (substring str 12 17) "HELLO")))))
|
||||
;; The requested 2nd match does not exist: nothing is applied
|
||||
(should (equal (substring str 12 17) "hello")))))
|
||||
|
||||
(ert-deftest tp-test-forward-do-function-receives-start-end ()
|
||||
"Test tp-forward-do passes start and end to function."
|
||||
@ -776,16 +779,17 @@ leak between tests regardless of how BODY exits."
|
||||
(should (equal (substring str 12 17) "hello")))))
|
||||
|
||||
(ert-deftest tp-test-backward-do-on-string-with-range ()
|
||||
"Test tp-backward-do on string with start/end range."
|
||||
"Test tp-backward-do on string with start/end range.
|
||||
All-or-nothing: with one match in range, requesting the 2nd applies
|
||||
nothing and returns the available count."
|
||||
(let ((str (copy-sequence "hello World hello")))
|
||||
(tp-set 0 5 '(marker t) str)
|
||||
(tp-set 12 17 '(marker t) str)
|
||||
;; Search only in range 0-10 (before second match)
|
||||
(let ((count (tp-backward-do #'upcase 'marker nil str 2 0 10)))
|
||||
(should (= count 1)) ; Only one match in range 0-10
|
||||
;; First match should be upcased
|
||||
(should (equal (substring str 0 5) "HELLO"))
|
||||
;; Second match should NOT be upcased
|
||||
;; The requested 2nd match does not exist: nothing is applied
|
||||
(should (equal (substring str 0 5) "hello"))
|
||||
(should (equal (substring str 12 17) "hello")))))
|
||||
|
||||
(ert-deftest tp-test-backward-do-function-receives-start-end ()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user