Add optional idx parameter support to tp-search-map function

The FUNCTION parameter in tp-search-map can now optionally accept a
second argument representing the 0-based index of the current match.
This is backwards compatible - functions that only accept one argument
continue to work as before.

Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-12-15 15:27:33 +00:00
parent 8adb9231dd
commit e88337ff1f
4 changed files with 87 additions and 8 deletions

View File

@ -994,7 +994,8 @@ Returns a list of (START END VALUE) for all matching regions.
Apply FUNCTION to matched text for all matches of PROPERTY. Apply FUNCTION to matched text for all matches of PROPERTY.
- **FUNCTION** receives the matched text as its only argument. The return value - **FUNCTION** receives the matched text as its first argument, and optionally
the 0-based index of the current match as its second argument. The return value
of FUNCTION replaces the matched text in the string or buffer. of FUNCTION replaces the matched text in the string or buffer.
- Returns the number of matches processed. - Returns the number of matches processed.
@ -1018,7 +1019,19 @@ Apply FUNCTION to matched text for all matches of PROPERTY.
(buffer-string)) (buffer-string))
;; => "HELLO world TEST" ;; => "HELLO world TEST"
;; Custom transformation ;; Custom transformation with index
(let ((my-string (copy-sequence "aaa bbb ccc")))
(tp-set 0 3 '(marker t) my-string)
(tp-set 4 7 '(marker t) my-string)
(tp-set 8 11 '(marker t) my-string)
(tp-search-map
(lambda (text idx)
(format "%d:%s" idx text))
my-string 'marker)
my-string)
;; => "0:aaa1:bbb2:ccc"
;; Custom transformation without index
(let ((my-string (copy-sequence "hello world"))) (let ((my-string (copy-sequence "hello world")))
(tp-set 0 5 '(marker t) my-string) (tp-set 0 5 '(marker t) my-string)
(tp-search-map (tp-search-map

View File

@ -991,7 +991,7 @@ Emacs 的 `text-property-search-forward` 和 `text-property-search-backward` 的
对所有 PROPERTY 匹配的文本应用 FUNCTION。 对所有 PROPERTY 匹配的文本应用 FUNCTION。
- **FUNCTION** 接收匹配到的文本作为唯一参数。FUNCTION 的返回值将替换字符串或缓冲区中的匹配文本。 - **FUNCTION** 接收匹配到的文本作为第一个参数,可选地接收当前匹配的 0 基索引作为第二个参数。FUNCTION 的返回值将替换字符串或缓冲区中的匹配文本。
- 返回处理的匹配数量。 - 返回处理的匹配数量。
**示例:** **示例:**
@ -1014,7 +1014,19 @@ Emacs 的 `text-property-search-forward` 和 `text-property-search-backward` 的
(buffer-string)) (buffer-string))
;; => "HELLO world TEST" ;; => "HELLO world TEST"
;; 自定义转换 ;; 使用索引的自定义转换
(let ((my-string (copy-sequence "aaa bbb ccc")))
(tp-set 0 3 '(marker t) my-string)
(tp-set 4 7 '(marker t) my-string)
(tp-set 8 11 '(marker t) my-string)
(tp-search-map
(lambda (text idx)
(format "%d:%s" idx text))
my-string 'marker)
my-string)
;; => "0:aaa1:bbb2:ccc"
;; 不使用索引的自定义转换
(let ((my-string (copy-sequence "hello world"))) (let ((my-string (copy-sequence "hello world")))
(tp-set 0 5 '(marker t) my-string) (tp-set 0 5 '(marker t) my-string)
(tp-search-map (tp-search-map

View File

@ -854,6 +854,52 @@
(should (equal (plist-get (plist-get props-0 'face) :background) "orange")) (should (equal (plist-get (plist-get props-0 'face) :background) "orange"))
(should (equal (plist-get (plist-get props-12 'face) :background) "orange"))))) (should (equal (plist-get (plist-get props-12 'face) :background) "orange")))))
(ert-deftest tp-test-search-map-with-idx ()
"Test tp-search-map passes index to function when it accepts two arguments."
(let ((str (copy-sequence "aaa bbb ccc"))
(indices nil))
(tp-set 0 3 '(marker t) str)
(tp-set 4 7 '(marker t) str)
(tp-set 8 11 '(marker t) str)
;; Use a function that accepts idx and records the indices
(tp-search-map (lambda (txt idx)
(push idx indices)
(format "%d:%s" idx txt))
str 'marker)
;; Check indices were passed in order (reversed due to push)
(should (equal (reverse indices) '(0 1 2)))
;; Check text was transformed with index
(should (equal (substring str 0 5) "0:aaa"))
(should (equal (substring str 4 9) "1:bbb"))
(should (equal (substring str 8 13) "2:ccc"))))
(ert-deftest tp-test-search-map-with-idx-in-buffer ()
"Test tp-search-map passes index to function in buffer range."
(tp-test-with-temp-buffer
(insert "aaa bbb ccc")
(tp-set 1 4 '(marker t))
(tp-set 5 8 '(marker t))
(tp-set 9 12 '(marker t))
(let ((indices nil))
(tp-search-map (lambda (txt idx)
(push idx indices)
(format "[%d]" idx))
1 12 'marker)
;; Check indices were passed in order
(should (equal (reverse indices) '(0 1 2)))
;; Check text was replaced with index markers
(should (string-match-p "\\[0\\]" (buffer-string)))
(should (string-match-p "\\[1\\]" (buffer-string)))
(should (string-match-p "\\[2\\]" (buffer-string))))))
(ert-deftest tp-test-search-map-backward-compat ()
"Test tp-search-map still works with single-argument functions."
(let ((str (copy-sequence "hello world")))
(tp-set 0 5 '(marker t) str)
;; Use #'upcase which only takes one argument
(tp-search-map #'upcase str 'marker)
(should (equal (substring str 0 5) "HELLO"))))
;;; ============================================================ ;;; ============================================================
;;; Utility Function Tests ;;; Utility Function Tests
;;; ============================================================ ;;; ============================================================

16
tp.el
View File

@ -1284,7 +1284,8 @@ This function supports two calling conventions:
2. Entire string: 2. Entire string:
(tp-search-map FUNCTION STRING PROPERTY &optional VALUE) (tp-search-map FUNCTION STRING PROPERTY &optional VALUE)
FUNCTION receives a copy of the matched text as its only argument. FUNCTION receives the matched text as its first argument, and optionally
the 0-based index of the current match as its second argument.
FUNCTION can either: FUNCTION can either:
- Return a new/modified string to replace the matched text - Return a new/modified string to replace the matched text
- Modify the text properties of the argument and return it - Modify the text properties of the argument and return it
@ -1304,11 +1305,15 @@ Example:
;; Upcase all matched text ;; Upcase all matched text
(tp-search-map #\\='upcase my-string \\='marker) (tp-search-map #\\='upcase my-string \\='marker)
;; Add properties to matched text ;; Add properties to matched text
(tp-search-map (lambda (txt) (tp-add txt \\='face \\='bold)) str \\='marker)" (tp-search-map (lambda (txt) (tp-add txt \\='face \\='bold)) str \\='marker)
;; Use index to add numbering
(tp-search-map (lambda (txt idx) (format \"%d: %s\" idx txt)) str \\='marker)"
(let ((obj (cond (let ((obj (cond
((stringp start-or-string) start-or-string) ((stringp start-or-string) start-or-string)
((numberp start-or-string) (or object (current-buffer))) ((numberp start-or-string) (or object (current-buffer)))
(t nil)))) (t nil)))
(idx 0)
(arity (func-arity function)))
(tp--search-do (tp--search-do
(lambda (match obj) (lambda (match obj)
(let* ((start (car match)) (let* ((start (car match))
@ -1316,7 +1321,10 @@ Example:
(text (if (stringp obj) (text (if (stringp obj)
(substring obj start end) (substring obj start end)
(buffer-substring start end))) (buffer-substring start end)))
(new-text (funcall function text))) (new-text (if (>= (cdr arity) 2)
(funcall function text idx)
(funcall function text))))
(setq idx (1+ idx))
(when (stringp new-text) (when (stringp new-text)
(if (stringp obj) (if (stringp obj)
;; For strings: copy text content and properties separately ;; For strings: copy text content and properties separately