Merge pull request #520 from Guldoman/select_previous

Add reverse search and some related commands
This commit is contained in:
Francesco 2021-10-10 10:01:55 +02:00 committed by GitHub
commit c179f909e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 51 deletions

View File

@ -7,15 +7,15 @@ local DocView = require "core.docview"
local CommandView = require "core.commandview" local CommandView = require "core.commandview"
local StatusView = require "core.statusview" local StatusView = require "core.statusview"
local max_last_finds = 50 local last_view, last_fn, last_text, last_sel
local last_finds, last_view, last_fn, last_text, last_sel
local case_sensitive = config.find_case_sensitive or false local case_sensitive = config.find_case_sensitive or false
local find_regex = config.find_regex or false local find_regex = config.find_regex or false
local found_expression local found_expression
local function doc() 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 end
local function get_find_tooltip() local function get_find_tooltip()
@ -53,8 +53,8 @@ end
local function find(label, search_fn) local function find(label, search_fn)
last_view, last_sel, last_finds = core.active_view, last_view, last_sel = core.active_view,
{ core.active_view.doc:get_selection() }, {} { core.active_view.doc:get_selection() }
local text = last_view.doc:get_text(unpack(last_sel)) local text = last_view.doc:get_text(unpack(last_sel))
found_expression = false found_expression = false
@ -142,7 +142,7 @@ local function is_in_any_selection(line, col)
return false return false
end end
local function select_next(all) local function select_add_next(all)
local il1, ic1 = doc():get_selection(true) local il1, ic1 = doc():get_selection(true)
for idx, l1, c1, l2, c2 in doc():get_selections(true, true) do for idx, l1, c1, l2, c2 in doc():get_selections(true, true) do
local text = doc():get_text(l1, c1, l2, c2) local text = doc():get_text(l1, c1, l2, c2)
@ -161,21 +161,28 @@ local function select_next(all)
end end
end end
command.add(has_unique_selection, { local function select_next(reverse)
["find-replace:select-next"] = function() local l1, c1, l2, c2 = doc():get_selection(true)
local l1, c1, l2, c2 = doc():get_selection(true) local text = doc():get_text(l1, c1, l2, c2)
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 }) l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true })
if l2 then doc():set_selection(l2, c2, l1, c1) end end
end, if l2 then doc():set_selection(l2, c2, l1, c1) end
["find-replace:select-add-next"] = function() select_next(false) end, end
["find-replace:select-add-all"] = function() select_next(true) 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", { command.add("core.docview", {
["find-replace:find"] = function() ["find-replace:find"] = function()
find("Find Text", function(doc, line, col, text, case_sensitive, 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 } local opt = { wrap = true, no_case = not case_sensitive, regex = find_regex, reverse = find_reverse }
return search.find(doc, line, col, text, opt) return search.find(doc, line, col, text, opt)
end) end)
end, end,
@ -221,29 +228,29 @@ command.add(valid_for_finding, {
core.error("No find to continue from") core.error("No find to continue from")
else else
local sl1, sc1, sl2, sc2 = doc():get_selection(true) 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 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) doc():set_selection(line2, col2, line1, col1)
last_view:scroll_to_line(line2, true) last_view:scroll_to_line(line2, true)
else
core.error("Couldn't find %q", last_text)
end end
end end
end, end,
["find-replace:previous-find"] = function() ["find-replace:previous-find"] = function()
local sel = table.remove(last_finds) if not last_fn then
if not sel or doc() ~= last_view.doc then core.error("No find to continue from")
core.error("No previous finds") else
return 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 end
doc():set_selection(table.unpack(sel))
last_view:scroll_to_line(sel[3], true)
end, end,
}) })

View File

@ -22,37 +22,61 @@ local function init_args(doc, line, col, text, opt)
return doc, line, col, text, opt return doc, line, col, text, opt
end 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(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 = func(text, pattern, s + 1, plain)
end
return last_s, last_e
end
function search.find(doc, line, col, text, opt) function search.find(doc, line, col, text, opt)
doc, line, col, text, opt = init_args(doc, line, col, text, opt) doc, line, col, text, opt = init_args(doc, line, col, text, opt)
local pattern = text
local re local search_func = string.find
if opt.regex then 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 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] local line_text = doc.lines[line]
if opt.regex then if opt.no_case and not opt.regex then
local s, e = re:cmatch(line_text, col) line_text = line_text:lower()
if s then
return line, s, line, e
end
col = 1
else
if opt.no_case then
line_text = line_text:lower()
end
local s, e = line_text:find(text, col, true)
if s then
return line, s, line, e + 1
end
col = 1
end 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 end
if opt.wrap then if opt.wrap then
opt = { no_case = opt.no_case, regex = opt.regex } opt = { no_case = opt.no_case, regex = opt.regex, reverse = opt.reverse }
return search.find(doc, 1, 1, text, opt) 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
end end

View File

@ -170,6 +170,7 @@ keymap.add_direct {
["ctrl+a"] = "doc:select-all", ["ctrl+a"] = "doc:select-all",
["ctrl+d"] = { "find-replace:select-add-next", "doc:select-word" }, ["ctrl+d"] = { "find-replace:select-add-next", "doc:select-word" },
["ctrl+f3"] = "find-replace:select-next", ["ctrl+f3"] = "find-replace:select-next",
["ctrl+shift+f3"] = "find-replace:select-previous",
["ctrl+l"] = "doc:select-lines", ["ctrl+l"] = "doc:select-lines",
["ctrl+shift+l"] = { "find-replace:select-add-all", "doc:select-word" }, ["ctrl+shift+l"] = { "find-replace:select-add-all", "doc:select-word" },
["ctrl+/"] = "doc:toggle-line-comments", ["ctrl+/"] = "doc:toggle-line-comments",