From 56eace627a0702da5713240b8e5e2544cb6e9aa7 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Fri, 10 Sep 2021 21:32:34 +0200 Subject: [PATCH 1/6] Add reverse option to `search.find` --- data/core/doc/search.lua | 71 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/data/core/doc/search.lua b/data/core/doc/search.lua index 04090673..7ff2dca7 100644 --- a/data/core/doc/search.lua +++ b/data/core/doc/search.lua @@ -23,6 +23,45 @@ local function init_args(doc, line, col, text, opt) end +local function plain_rfind(text, pattern, index) + local len = #text + text = text:reverse() + pattern = pattern:reverse() + if index >= 0 then + index = len - index + 1 + else + index = index * -1 + end + local s, e = text:find(pattern, index, true) + return e and len - e + 1, s and len - s + 1 +end + + +local function rfind(text, pattern, index, plain) + if plain then return plain_rfind(text, pattern, index) end + local s, e = text:find(pattern) + local last_s, last_e + if index < 0 then index = #text - index + 1 end + while e and e <= index do + last_s, last_e = s, e + s, e = text:find(pattern, s + 1) + end + return last_s, last_e +end + + +local function rcmatch(re, text, index) + local s, e = re:cmatch(text) + local last_s, last_e + if index < 0 then index = #text - index + 1 end + while e and e <= index + 1 do + last_s, last_e = s, e + s, e = re:cmatch(text, s + 1) + end + return last_s, last_e +end + + function search.find(doc, line, col, text, opt) doc, line, col, text, opt = init_args(doc, line, col, text, opt) @@ -30,29 +69,47 @@ function search.find(doc, line, col, text, opt) if opt.regex then re = regex.compile(text, opt.no_case and "i" or "") end - for line = line, #doc.lines do + local start, finish, step = line, #doc.lines, 1 + if opt.reverse then + start, finish, step = line, 1, -1 + end + for line = start, finish, step do local line_text = doc.lines[line] if opt.regex then - local s, e = re:cmatch(line_text, col) + local s, e + if opt.reverse then + s, e = rcmatch(re, line_text, col - 1) + else + s, e = re:cmatch(line_text, col) + end if s then return line, s, line, e end - col = 1 + col = opt.reverse and -1 or 1 else if opt.no_case then line_text = line_text:lower() end - local s, e = line_text:find(text, col, true) + local s, e + if opt.reverse then + s, e = rfind(line_text, text, col - 1, true) + else + s, e = line_text:find(text, col, true) + end if s then return line, s, line, e + 1 end - col = 1 + col = opt.reverse and -1 or 1 end end if opt.wrap then - opt = { no_case = opt.no_case, regex = opt.regex } - return search.find(doc, 1, 1, text, opt) + opt = { no_case = opt.no_case, regex = opt.regex, reverse = opt.reverse } + if opt.reverse then + return search.find(doc, #doc.lines, #doc.lines[#doc.lines], text, opt) + else + return search.find(doc, 1, 1, text, opt) + end end end From e7be9652c96a99a5e218bdfd84dc06290ca176bf Mon Sep 17 00:00:00 2001 From: Guldoman Date: Fri, 10 Sep 2021 21:54:50 +0200 Subject: [PATCH 2/6] Add `find-replace:select-previous` --- data/core/commands/findreplace.lua | 25 ++++++++++++++++--------- data/core/keymap.lua | 1 + 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index f8e8e45a..e1153fe5 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -142,7 +142,7 @@ local function is_in_any_selection(line, col) return false end -local function select_next(all) +local function select_add_next(all) local il1, ic1 = doc():get_selection(true) for idx, l1, c1, l2, c2 in doc():get_selections(true, true) do local text = doc():get_text(l1, c1, l2, c2) @@ -161,15 +161,22 @@ local function select_next(all) end end -command.add(has_unique_selection, { - ["find-replace:select-next"] = function() - local l1, c1, l2, c2 = doc():get_selection(true) - local text = doc():get_text(l1, c1, l2, c2) +local function select_next(reverse) + local l1, c1, l2, c2 = doc():get_selection(true) + local text = doc():get_text(l1, c1, l2, c2) + if reverse then + l1, c1, l2, c2 = search.find(doc(), l1, c1, text, { wrap = true, reverse = true }) + else l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true }) - if l2 then doc():set_selection(l2, c2, l1, c1) end - end, - ["find-replace:select-add-next"] = function() select_next(false) end, - ["find-replace:select-add-all"] = function() select_next(true) end + end + if l2 then doc():set_selection(l2, c2, l1, c1) end +end + +command.add(has_unique_selection, { + ["find-replace:select-next"] = select_next, + ["find-replace:select-previous"] = function() select_next(true) end, + ["find-replace:select-add-next"] = select_add_next, + ["find-replace:select-add-all"] = function() select_add_next(true) end }) command.add("core.docview", { diff --git a/data/core/keymap.lua b/data/core/keymap.lua index 7f7c0fe2..0c2dd863 100644 --- a/data/core/keymap.lua +++ b/data/core/keymap.lua @@ -170,6 +170,7 @@ keymap.add_direct { ["ctrl+a"] = "doc:select-all", ["ctrl+d"] = { "find-replace:select-add-next", "doc:select-word" }, ["ctrl+f3"] = "find-replace:select-next", + ["ctrl+shift+f3"] = "find-replace:select-previous", ["ctrl+l"] = "doc:select-lines", ["ctrl+shift+l"] = { "find-replace:select-add-all", "doc:select-word" }, ["ctrl+/"] = "doc:toggle-line-comments", From 1976facaf1a2b9934f3cfdb4524712c1418000ef Mon Sep 17 00:00:00 2001 From: Guldoman Date: Fri, 10 Sep 2021 21:58:11 +0200 Subject: [PATCH 3/6] Use reverse search for `find-replace:previous-find` --- data/core/commands/findreplace.lua | 39 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index e1153fe5..cff023d2 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -7,8 +7,7 @@ local DocView = require "core.docview" local CommandView = require "core.commandview" local StatusView = require "core.statusview" -local max_last_finds = 50 -local last_finds, last_view, last_fn, last_text, last_sel +local last_view, last_fn, last_text, last_sel local case_sensitive = config.find_case_sensitive or false local find_regex = config.find_regex or false @@ -53,8 +52,8 @@ end local function find(label, search_fn) - last_view, last_sel, last_finds = core.active_view, - { core.active_view.doc:get_selection() }, {} + last_view, last_sel = core.active_view, + { core.active_view.doc:get_selection() } local text = last_view.doc:get_text(unpack(last_sel)) found_expression = false @@ -181,8 +180,8 @@ command.add(has_unique_selection, { command.add("core.docview", { ["find-replace:find"] = function() - find("Find Text", function(doc, line, col, text, case_sensitive, find_regex) - local opt = { wrap = true, no_case = not case_sensitive, regex = find_regex } + find("Find Text", function(doc, line, col, text, case_sensitive, find_regex, find_reverse) + local opt = { wrap = true, no_case = not case_sensitive, regex = find_regex, reverse = find_reverse } return search.find(doc, line, col, text, opt) end) end, @@ -228,29 +227,29 @@ command.add(valid_for_finding, { core.error("No find to continue from") else local sl1, sc1, sl2, sc2 = doc():get_selection(true) - local line1, col1, line2, col2 = last_fn(doc(), sl1, sc2, last_text, case_sensitive, find_regex) + local line1, col1, line2, col2 = last_fn(doc(), sl1, sc2, last_text, case_sensitive, find_regex, false) if line1 then - if last_view.doc ~= doc() then - last_finds = {} - end - if #last_finds >= max_last_finds then - table.remove(last_finds, 1) - end - table.insert(last_finds, { sl1, sc1, sl2, sc2 }) doc():set_selection(line2, col2, line1, col1) last_view:scroll_to_line(line2, true) + else + core.error("Couldn't find %q", last_text) end end end, ["find-replace:previous-find"] = function() - local sel = table.remove(last_finds) - if not sel or doc() ~= last_view.doc then - core.error("No previous finds") - return + if not last_fn then + core.error("No find to continue from") + else + local sl1, sc1, sl2, sc2 = doc():get_selection(true) + local line1, col1, line2, col2 = last_fn(doc(), sl1, sc1, last_text, case_sensitive, find_regex, true) + if line1 then + doc():set_selection(line2, col2, line1, col1) + last_view:scroll_to_line(line2, true) + else + core.error("Couldn't find %q", last_text) + end end - doc():set_selection(table.unpack(sel)) - last_view:scroll_to_line(sel[3], true) end, }) From af925d603bc7632ff87918ad1ad080032fb3c0a5 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Sat, 11 Sep 2021 03:37:12 +0200 Subject: [PATCH 4/6] Fix `doc` selection in `findreplace` Use `last_view` if `active_view` is `CommandView`. --- data/core/commands/findreplace.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index cff023d2..67aa90d5 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -14,7 +14,8 @@ local find_regex = config.find_regex or false local found_expression local function doc() - return core.active_view:is(DocView) and core.active_view.doc or last_view.doc + local is_DocView = core.active_view:is(DocView) and not core.active_view:is(CommandView) + return is_DocView and core.active_view.doc or last_view.doc end local function get_find_tooltip() From cfe0c79a0415ab9089f0cb6f3c1d421ce29b87fd Mon Sep 17 00:00:00 2001 From: Guldoman Date: Sat, 9 Oct 2021 00:24:48 +0200 Subject: [PATCH 5/6] Simplify reverse search Remove `plain_rfind` optimization. --- data/core/doc/search.lua | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/data/core/doc/search.lua b/data/core/doc/search.lua index 7ff2dca7..babf9cc8 100644 --- a/data/core/doc/search.lua +++ b/data/core/doc/search.lua @@ -23,28 +23,13 @@ local function init_args(doc, line, col, text, opt) end -local function plain_rfind(text, pattern, index) - local len = #text - text = text:reverse() - pattern = pattern:reverse() - if index >= 0 then - index = len - index + 1 - else - index = index * -1 - end - local s, e = text:find(pattern, index, true) - return e and len - e + 1, s and len - s + 1 -end - - local function rfind(text, pattern, index, plain) - if plain then return plain_rfind(text, pattern, index) end - local s, e = text:find(pattern) + local s, e = text:find(pattern, 1, plain) local last_s, last_e if index < 0 then index = #text - index + 1 end while e and e <= index do last_s, last_e = s, e - s, e = text:find(pattern, s + 1) + s, e = text:find(pattern, s + 1, plain) end return last_s, last_e end From 3a1274fd08dfc3d6a9c3a0e659e3bb856ed791c0 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Sun, 10 Oct 2021 00:42:30 +0200 Subject: [PATCH 6/6] Merge reverse find functions for lua patterns and regexes --- data/core/doc/search.lua | 68 +++++++++++++++------------------------- 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/data/core/doc/search.lua b/data/core/doc/search.lua index babf9cc8..b4c553c9 100644 --- a/data/core/doc/search.lua +++ b/data/core/doc/search.lua @@ -22,26 +22,20 @@ local function init_args(doc, line, col, text, opt) return doc, line, col, text, opt end +-- This function is needed to uniform the behavior of +-- `regex:cmatch` and `string.find`. +local function regex_func(text, re, index, _) + local s, e = re:cmatch(text, index) + return s, e and e - 1 +end -local function rfind(text, pattern, index, plain) - local s, e = text:find(pattern, 1, plain) +local function rfind(func, text, pattern, index, plain) + local s, e = func(text, pattern, 1, plain) local last_s, last_e if index < 0 then index = #text - index + 1 end while e and e <= index do last_s, last_e = s, e - s, e = text:find(pattern, s + 1, plain) - end - return last_s, last_e -end - - -local function rcmatch(re, text, index) - local s, e = re:cmatch(text) - local last_s, last_e - if index < 0 then index = #text - index + 1 end - while e and e <= index + 1 do - last_s, last_e = s, e - s, e = re:cmatch(text, s + 1) + s, e = func(text, pattern, s + 1, plain) end return last_s, last_e end @@ -49,10 +43,11 @@ end function search.find(doc, line, col, text, opt) doc, line, col, text, opt = init_args(doc, line, col, text, opt) - - local re + local pattern = text + local search_func = string.find if opt.regex then - re = regex.compile(text, opt.no_case and "i" or "") + pattern = regex.compile(text, opt.no_case and "i" or "") + search_func = regex_func end local start, finish, step = line, #doc.lines, 1 if opt.reverse then @@ -60,32 +55,19 @@ function search.find(doc, line, col, text, opt) end for line = start, finish, step do local line_text = doc.lines[line] - if opt.regex then - local s, e - if opt.reverse then - s, e = rcmatch(re, line_text, col - 1) - else - s, e = re:cmatch(line_text, col) - end - if s then - return line, s, line, e - end - col = opt.reverse and -1 or 1 - else - if opt.no_case then - line_text = line_text:lower() - end - local s, e - if opt.reverse then - s, e = rfind(line_text, text, col - 1, true) - else - s, e = line_text:find(text, col, true) - end - if s then - return line, s, line, e + 1 - end - col = opt.reverse and -1 or 1 + if opt.no_case and not opt.regex then + line_text = line_text:lower() end + local s, e + if opt.reverse then + s, e = rfind(search_func, line_text, pattern, col - 1) + else + s, e = search_func(line_text, pattern, col) + end + if s then + return line, s, line, e + 1 + end + col = opt.reverse and -1 or 1 end if opt.wrap then