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 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,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in New Issue