Merge pull request #520 from Guldoman/select_previous
Add reverse search and some related commands
This commit is contained in:
commit
c179f909e2
|
@ -7,15 +7,15 @@ 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
|
||||
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()
|
||||
|
@ -53,8 +53,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
|
||||
|
||||
|
@ -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,21 +161,28 @@ 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", {
|
||||
["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,
|
||||
|
@ -221,29 +228,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,
|
||||
})
|
||||
|
||||
|
|
|
@ -22,37 +22,61 @@ 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(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)
|
||||
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
|
||||
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)
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue