From 985b51d2c68ee7e520b83b7c4e3eb3e1fe2dcd2a Mon Sep 17 00:00:00 2001 From: Kinneyzhang Date: Mon, 27 Jul 2026 02:26:36 +0800 Subject: [PATCH] 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 --- tp-search-tests.el | 60 ++++++++++++++++++++++++++++++++++++++++++++ tp-search.el | 62 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 112 insertions(+), 10 deletions(-) diff --git a/tp-search-tests.el b/tp-search-tests.el index 342edb2..e0e8d89 100644 --- a/tp-search-tests.el +++ b/tp-search-tests.el @@ -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 diff --git a/tp-search.el b/tp-search.el index 4d84e91..1614de9 100644 --- a/tp-search.el +++ b/tp-search.el @@ -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