diff options
-rw-r--r-- | docs/manual.md | 5 | ||||
-rw-r--r-- | modules/textadept/find.lua | 53 | ||||
-rw-r--r-- | test/test.lua | 13 |
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 |