Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions TeXmacs/progs/generic/search-widgets.scm
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,19 @@ tree 或 #f
(perform-search*)
(set! isreplace? #t))))

(tm-define (replace-all-occurrences . args)
(let ((u (if (null? args) (master-buffer) (car args)))
(raux (if (null? args) (replace-buffer) (cadr args))))
(and-with by (or (by-tree raux) current-replace)
Comment on lines +726 to +729
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argument parsing in replace-all-occurrences is unsafe: when args is non-empty but has only one element, (cadr args) will throw. Since the procedure signature is variadic (. args), either enforce exactly 0 or 2 args (and signal a clear error) or make raux default when (length args) < 2.

Copilot uses AI. Check for mistakes.
(with-buffer u
(search-extreme-match #f)
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace-all already accepts an explicit target buffer u, but this new call to search-extreme-match does not pass it, so search-extreme-match will default to (master-buffer) instead. If replace-all is invoked programmatically with a buffer that differs from master-buffer (or if master-buffer is #f), the cursor navigation can happen in the wrong buffer and the replace loop will then run in u with inconsistent search state. Prefer calling search-extreme-match with u (or call extreme-search-result directly inside with-buffer u) to guarantee it operates on the same buffer being edited.

Suggested change
(search-extreme-match #f)
(search-extreme-match u #f)

Copilot uses AI. Check for mistakes.
(start-editing)
(while (replace-next by)
(perform-search*))
(end-editing))
(perform-search*)
(set! isreplace? #t))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Customized keyboard shortcuts in search and replace modes
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand Down Expand Up @@ -957,6 +970,8 @@ tree 或 #f
"Replace all further occurrences (Command+Enter)"
"Replace all further occurrences (Ctrl+Enter)")))
(replace-all u raux))
((balloon (icon "tm_replace_all.xpm") "Replace all occurrences")
(replace-all-occurrences u raux))
>>>
(=> (balloon (icon "tm_preferences.xpm")
"Search and replace preferences")
Expand Down Expand Up @@ -996,6 +1011,8 @@ tree 或 #f
(replace-one))
((balloon (icon "tm_replace_all.xpm") "Replace all further occurrences")
(replace-all))
((balloon (icon "tm_replace_all.xpm") "Replace all occurrences")
(replace-all-occurrences))
>>>
(=> (balloon (icon "tm_preferences.xpm")
"Search and replace preferences")
Expand Down Expand Up @@ -1197,6 +1214,8 @@ tree 或 #f
(replace-one))
((balloon (icon "tm_replace_all.xpm") "Replace all further occurrences")
(replace-all))
((balloon (icon "tm_replace_all.xpm") "Replace all occurrences")
(replace-all-occurrences))
>>>
((check (balloon (icon "tm_filter.xpm") "Only show paragraphs with hits")
"v" (search-filter-enabled?))
Expand Down
38 changes: 38 additions & 0 deletions devel/202_110.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# [202_110] Search and replace: add "Replace all occurrences" button

## How to test
1. Open a document with multiple occurrences of a word (e.g. "hello" appearing 5 times)
2. Open search and replace (Edit → Search and replace)
3. Place cursor in the middle of the document (e.g. after the 3rd occurrence)
4. Enter the search term "hello" and the replacement "world"
5. Click "Replace all further occurrences" (or press Ctrl+Enter / Command+Enter)
6. Confirm that only the occurrences **after the cursor** are replaced (original behavior preserved)
7. Undo, then click "Replace all occurrences"
8. Confirm that **all 5 occurrences** are replaced, including those before the cursor

## 2026/02/25
### What
Add a separate "Replace all occurrences" button that replaces every match in the entire document, while keeping the original "Replace all further occurrences" button and behavior.

### Why
Users expect a way to replace every occurrence in the document regardless of cursor position (issue #2857). The maintainer requested this as a separate button so the original "Replace all further occurrences" behavior is preserved.

### How
Added a new `replace-all-occurrences` function that calls `(search-extreme-match #f)` before the replace loop to navigate to the first match, so replacement starts from the beginning of the document. The original `replace-all` function is kept unchanged.

```scheme
(tm-define (replace-all-occurrences . args)
(let ((u (if (null? args) (master-buffer) (car args)))
(raux (if (null? args) (replace-buffer) (cadr args))))
(and-with by (or (by-tree raux) current-replace)
(with-buffer u
(search-extreme-match #f) ;; Go to the first match
(start-editing)
(while (replace-next by)
(perform-search*))
(end-editing))
(perform-search*)
(set! isreplace? #t))))
```

Added a new "Replace all occurrences" button in all 3 replace UI locations (dialog, side panel, toolbar) alongside the existing "Replace all further occurrences" button.