aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/manual.md5
-rw-r--r--modules/textadept/find.lua53
-rw-r--r--test/test.lua13
3 files changed, 43 insertions, 28 deletions
diff --git a/docs/manual.md b/docs/manual.md
index d186ad2c..7a78964b 100644
--- a/docs/manual.md
+++ b/docs/manual.md
@@ -508,9 +508,8 @@ When the pane is closed, you can quickly perform "Find Next", "Find Prev", "Repl
on Windows, Linux, and BSD; `⌘G`, `⌘⇧G`, `^R`, and `^⇧R`, respectively, on macOS; and
`M-G`, `M-S-G`, `M-R`, and `M-S-R` in the terminal version.
-**Tip:** by default, "Replace All" replaces all text in the buffer. Selecting a contiguous block
-of text and then performing "Replace All" replaces all text in that selection only. Textadept does
-not currently support "Replace All" within non-contiguous selections (e.g. multiple selections).
+**Tip:** by default, "Replace All" replaces all text in the buffer. Selecting text and then
+performing "Replace All" replaces all text in that selection only.
[`ui.find.highlight_all_matches`]: api.html#ui.find.highlight_all_matches
diff --git a/modules/textadept/find.lua b/modules/textadept/find.lua
index c8a77d86..262c1574 100644
--- a/modules/textadept/find.lua
+++ b/modules/textadept/find.lua
@@ -398,35 +398,38 @@ local INDIC_REPLACE = _SCINTILLA.next_indic_number()
-- is replaced.
events.connect(events.REPLACE_ALL, function(ftext, rtext)
if ftext == '' then return end
- local count = 0
- local s, e = buffer.selection_start, buffer.selection_end
- local replace_in_sel = s ~= e and (ftext ~= find_text or buffer:get_sel_text() ~= found_text)
- if replace_in_sel then
- buffer.indicator_current = INDIC_REPLACE
- buffer:indicator_fill_range(e, 1)
- end
- local EOF = replace_in_sel and e == buffer.length + 1 -- no indicator at EOF
repl_text = rtext -- save for ui.find.focus()
+ local count = 0
+ local replace_in_sel = not buffer.selection_empty and
+ (ftext ~= find_text or buffer:get_sel_text() ~= found_text)
+ if replace_in_sel then buffer.indicator_current = INDIC_REPLACE end
+
+ for i = 1, buffer.selections do
+ local s, e = buffer.selection_n_start[i], buffer.selection_n_end[i]
+ if replace_in_sel then buffer:indicator_fill_range(e, 1) end
+ local EOF = replace_in_sel and e == buffer.length + 1 -- no indicator at EOF
+
+ -- Perform the search and replace.
+ buffer:begin_undo_action()
+ buffer.search_flags = get_flags()
+ buffer:set_target_range(not replace_in_sel and 1 or s, buffer.length + 1)
+ while buffer:search_in_target(ftext) ~= -1 and
+ (not replace_in_sel or buffer.target_end <= buffer:indicator_end(INDIC_REPLACE, s) or EOF) do
+ local offset = buffer.target_start ~= buffer.target_end and 0 or 1 -- for preventing loops
+ buffer:replace_target(not M.regex and rtext or unescape(rtext))
+ count = count + 1
+ buffer:set_target_range(buffer.target_end + offset, buffer.length + 1)
+ end
+ buffer:end_undo_action()
- -- Perform the search and replace.
- buffer:begin_undo_action()
- buffer.search_flags = get_flags()
- buffer:set_target_range(not replace_in_sel and 1 or s, buffer.length + 1)
- while buffer:search_in_target(ftext) ~= -1 and
- (not replace_in_sel or buffer.target_end <= buffer:indicator_end(INDIC_REPLACE, s) or EOF) do
- local offset = buffer.target_start ~= buffer.target_end and 0 or 1 -- for preventing loops
- buffer:replace_target(not M.regex and rtext or unescape(rtext))
- count = count + 1
- buffer:set_target_range(buffer.target_end + offset, buffer.length + 1)
+ -- Restore any original selection.
+ if replace_in_sel then
+ e = buffer:indicator_end(INDIC_REPLACE, s)
+ buffer.selection_n_start[i], buffer.selection_n_end[i] = s, e > 1 and e or buffer.length + 1
+ if e > 1 then buffer:indicator_clear_range(e, 1) end
+ end
end
- buffer:end_undo_action()
- -- Restore any original selection and report the number of replacements made.
- if replace_in_sel then
- e = buffer:indicator_end(INDIC_REPLACE, s)
- buffer:set_sel(s, e > 1 and e or buffer.length + 1)
- if e > 1 then buffer:indicator_clear_range(e, 1) end
- end
ui.statusbar_text = string.format('%d %s', count, _L['replacement(s) made'])
end)
diff --git a/test/test.lua b/test/test.lua
index 3850da0d..05084c20 100644
--- a/test/test.lua
+++ b/test/test.lua
@@ -2715,6 +2715,8 @@ function test_ui_find_replace_all()
ui.find.find_entry_text, ui.find.replace_entry_text = 'f(.)\\1', 'b\\1\\1'
ui.find.replace_all() -- replace in selection
assert_equal(buffer:get_text(), 'foo\nboobar\nboobaz\nboofoo')
+ assert_equal(buffer.selection_start, buffer:position_from_line(2))
+ assert_equal(buffer.selection_end, buffer:position_from_line(4) + 3)
ui.find.regex = false
buffer:undo()
ui.find.find_entry_text, ui.find.replace_entry_text = 'foo', ''
@@ -2723,6 +2725,17 @@ function test_ui_find_replace_all()
ui.find.find_entry_text, ui.find.replace_entry_text = 'quux', ''
ui.find.replace_all()
assert_equal(buffer:get_text(), '\nbar\nbaz\n')
+ buffer:undo()
+ buffer:set_selection(1, 4)
+ buffer:add_selection(buffer:position_from_line(3), buffer:position_from_line(3) + 3)
+ ui.find.find_entry_text, ui.find.replace_entry_text = 'foo', 'quux'
+ ui.find.replace_all() -- replace in multiple selection
+ assert_equal(buffer:get_text(), 'quux\nfoobar\nquuxbaz\nfoofoo')
+ assert_equal(buffer.selections, 2)
+ assert_equal(buffer.selection_n_start[1], 1)
+ assert_equal(buffer.selection_n_end[1], 5)
+ assert_equal(buffer.selection_n_start[2], buffer:position_from_line(3))
+ assert_equal(buffer.selection_n_end[2], buffer:position_from_line(3) + 4)
ui.find.find_entry_text, ui.find.replace_entry_text = '', ''
buffer:close(true)
end