Deprecate the raw search wrappers and state the nav contracts

API-NAME-01: tp-search-forward / tp-search-backward are marked
obsolete since 0.3.0 with tp-forward / tp-backward as the recorded
replacements, and their docstrings now document all four arguments,
the primitive's nil-PREDICATE not-`equal' default, and the raw-use
advice (call the Emacs primitives directly - the wrappers add
nothing).  The wrappers keep their exact primitive-delegating bodies
so every existing call stays bit-identical; a defalias onto
tp-forward/tp-backward would have flipped both the argument order
(OBJECT/N vs PREDICATE/NOT-CURRENT) and the default matching
semantics, and would have recursed through tp-forward's own calls.
Internal callers (tp-forward, tp--forward-do) now call the
primitives, leaving the deprecated names with zero in-tree callers.

The tp-forward-do / tp-backward-do summaries now read "search TIMES
times; apply FUNCTION at the Nth match" and explicitly name
tp-search-map as the for-each, so the -do suffix stops reading as
one.  tp-forward's docstring states the string-path contract
precisely (list of the FIRST N matches from position 0, point never
involved) next to the buffer path's point motion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kinneyzhang 2026-07-27 02:26:36 +08:00
parent 575c99be88
commit 985b51d2c6
2 changed files with 112 additions and 10 deletions

View File

@ -725,5 +725,65 @@ never registered the buffer (REG-1)."
nil nil nil 1)))
(should-not (get-text-property 1 'face))))
;;; API-NAME-01: raw wrappers deprecated, behavior bit-identical
(ert-deftest tp-search-test-raw-wrappers-marked-obsolete ()
"The raw wrappers carry obsolescence info pointing at tp-forward/backward."
(should (eq (car (get 'tp-search-forward 'byte-obsolete-info))
'tp-forward))
(should (eq (car (get 'tp-search-backward 'byte-obsolete-info))
'tp-backward)))
(ert-deftest tp-search-test-raw-wrapper-forward-primitive-semantics ()
"tp-search-forward still behaves exactly like the Emacs primitive.
The third argument stays PREDICATE (not tp-forward's OBJECT slot) and
the nil-PREDICATE default keeps the primitive's not-`equal' matching."
(with-temp-buffer
(insert "aaabbb")
(put-text-property 4 7 'k 'v)
(dolist (args '((k) (k v t) (k v t t) (k other) (k missing t)))
(goto-char (point-min))
(let ((prim (apply #'text-property-search-forward args))
(prim-pt (point)))
(goto-char (point-min))
(let ((wrap (with-suppressed-warnings ((obsolete tp-search-forward))
(apply #'tp-search-forward args))))
(should (equal wrap prim))
(should (= (point) prim-pt)))))
;; One concrete anchor: nil PREDICATE with nil VALUE finds the
;; non-nil run and moves point to its end.
(goto-char (point-min))
(let ((m (with-suppressed-warnings ((obsolete tp-search-forward))
(tp-search-forward 'k))))
(should m)
(should (= (prop-match-beginning m) 4))
(should (= (prop-match-end m) 7))
(should (= (point) 7)))))
(ert-deftest tp-search-test-raw-wrapper-backward-primitive-semantics ()
"tp-search-backward still behaves exactly like the Emacs primitive."
(with-temp-buffer
(insert "aaabbb")
(put-text-property 1 4 'k 'v)
(dolist (args '((k) (k v t) (k v t t) (k other) (k missing t)))
(goto-char (point-max))
(let ((prim (apply #'text-property-search-backward args))
(prim-pt (point)))
(goto-char (point-max))
(let ((wrap (with-suppressed-warnings ((obsolete tp-search-backward))
(apply #'tp-search-backward args))))
(should (equal wrap prim))
(should (= (point) prim-pt)))))))
(ert-deftest tp-search-test-forward-string-path-first-n-contract ()
"tp-forward's string path returns the FIRST N matches from position 0."
(let ((s (copy-sequence "aabbaabb")))
(put-text-property 0 2 'k 'v s)
(put-text-property 4 6 'k 'v s)
(should (equal (tp-forward 'k 'v s) '((0 2 v))))
(should (equal (tp-forward 'k 'v s 2) '((0 2 v) (4 6 v))))
;; Fewer matches than N: return what exists, not nil.
(should (equal (tp-forward 'k 'v s 5) '((0 2 v) (4 6 v))))))
(provide 'tp-search-tests)
;;; tp-search-tests.el ends here

View File

@ -414,14 +414,36 @@ regions."
object start end subexp))
(defun tp-search-forward (property &optional value predicate not-current)
"Search forward for text with PROPERTY.
Wraps `text-property-search-forward'."
"Search forward from point for text whose PROPERTY matches VALUE.
This is a raw wrapper: PROPERTY, VALUE, PREDICATE and NOT-CURRENT are
passed unchanged to `text-property-search-forward', whose semantics
apply in full - including the primitive's nil-PREDICATE default of
matching values that are non-nil and NOT `equal' to VALUE. On
success point moves to the end of the matched region and a prop-match
object is returned; otherwise nil.
Obsolete since tp 0.3.0: call `tp-forward' for tp's `equal'-matching
search (which also supports string OBJECTs and repeat counts), or
call the Emacs primitive `text-property-search-forward' directly for
raw use - this wrapper adds nothing to it."
(text-property-search-forward property value predicate not-current))
(make-obsolete 'tp-search-forward 'tp-forward "0.3.0")
(defun tp-search-backward (property &optional value predicate not-current)
"Search backward for text with PROPERTY.
Wraps `text-property-search-backward'."
"Search backward from point for text whose PROPERTY matches VALUE.
This is a raw wrapper: PROPERTY, VALUE, PREDICATE and NOT-CURRENT are
passed unchanged to `text-property-search-backward', whose semantics
apply in full - including the primitive's nil-PREDICATE default of
matching values that are non-nil and NOT `equal' to VALUE. On
success point moves to the beginning of the matched region and a
prop-match object is returned; otherwise nil.
Obsolete since tp 0.3.0: call `tp-backward' for tp's `equal'-matching
search (which also supports string OBJECTs and repeat counts), or
call the Emacs primitive `text-property-search-backward' directly for
raw use - this wrapper adds nothing to it."
(text-property-search-backward property value predicate not-current))
(make-obsolete 'tp-search-backward 'tp-backward "0.3.0")
(defun tp--property-match-p (value prop-value predicate)
"Return non-nil when PROP-VALUE matches VALUE under PREDICATE.
@ -498,7 +520,6 @@ matching region). Otherwise return nil and leave point alone."
(defun tp-forward (property &optional value object n predicate not-current)
"Search forward N times for text with PROPERTY.
Returns prop-match for buffers or list of (START END VALUE) for strings.
VALUE is the optional value to match; N is the number of searches,
defaulting to 1.
@ -511,7 +532,18 @@ is passed to `text-property-search-forward'.
NOT-CURRENT is passed to `text-property-search-forward' and, when
non-nil, makes the search skip a matching region containing point.
It only applies to the buffer path; strings have no point, so it is
ignored there."
ignored there.
For buffers, each search starts from point and each successful one
moves point to the end of its matched region; the return value is
the prop-match object of the N-th search, or nil when that search
found nothing.
For strings, point is not involved at all: the return value is the
list of the FIRST N matching regions counted from position 0 of the
string, each a (START END VALUE) list with 0-based positions - not
the N-th match alone. Fewer than N matches return however many
exist."
(let ((count (or n 1)))
(cond
;; String object - use tp-search (or the predicate-aware matcher)
@ -527,7 +559,7 @@ ignored there."
(buf (or object (current-buffer))))
(tp-with-current-buffer buf
(dotimes (_ count)
(setq result (tp-search-forward
(setq result (text-property-search-forward
property value
(if (functionp predicate) predicate t)
not-current))))
@ -625,7 +657,7 @@ Returns the number of matches found (at most TIMES)."
(save-excursion
(goto-char search-start)
(dotimes (i count)
(when-let ((match (tp-search-forward
(when-let ((match (text-property-search-forward
property value
(if (functionp predicate) predicate t)
not-current)))
@ -705,7 +737,12 @@ length-changing replacements" new-text (length new-text) len))
(defun tp-forward-do (function property &optional value object times
start end predicate not-current)
"Search forward for text with PROPERTY and apply FUNCTION to the last match.
"Search forward TIMES times for PROPERTY; apply FUNCTION at the Nth match.
Despite the -do suffix this is NOT a for-each: the search advances
through TIMES matches and FUNCTION is applied only to the final
\(TIMES-th) one. Use `tp-search-map' to apply a function to EVERY
match.
FUNCTION receives (TEXT &optional START END) where TEXT is the matched text,
START and END are the positions of the match. The return value of FUNCTION
@ -811,7 +848,12 @@ Returns the number of matches found (at most TIMES)."
(defun tp-backward-do (function property &optional value object times
start end predicate not-current)
"Search backward for text with PROPERTY and apply FUNCTION to the last match.
"Search backward TIMES times for PROPERTY; apply FUNCTION at the Nth match.
Despite the -do suffix this is NOT a for-each: the search walks back
through TIMES matches and FUNCTION is applied only to the final
\(TIMES-th) one. Use `tp-search-map' to apply a function to EVERY
match.
FUNCTION receives (TEXT &optional START END) where TEXT is the matched text,
START and END are the positions of the match. The return value of FUNCTION