diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index af60f33f..47f5f001 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -2,67 +2,70 @@ local core = require "core" local command = require "core.command" local config = require "core.config" local search = require "core.doc.search" +local keymap = require "core.keymap" local DocView = require "core.docview" +local CommandView = require "core.commandview" +local StatusView = require "core.statusview" -local max_previous_finds = 50 +local max_last_finds = 50 +local last_finds, last_view, last_fn, last_text, last_sel +local case_insensitive = config.find_case_insensitive or false +local plain = config.find_plain or false local function doc() - return core.active_view.doc + return last_view and last_view.doc or core.active_view.doc end - -local previous_finds -local last_doc -local last_fn, last_text - - -local function push_previous_find(doc, sel) - if last_doc ~= doc then - last_doc = doc - previous_finds = {} - end - if #previous_finds >= max_previous_finds then - table.remove(previous_finds, 1) - end - table.insert(previous_finds, sel or { doc:get_selection() }) +local function get_find_tooltip() + local rf = keymap.get_binding("find-replace:repeat-find") + local ti = keymap.get_binding("find-replace:toggle-insensitivity") + local tr = keymap.get_binding("find-replace:toggle-plain") + return (plain and "[Plain] " or "") .. + (case_insensitive and "[Insensitive] " or "") .. + (rf and ("Press " .. rf .. " to select the next match.") or "") .. + (ti and (" " .. ti .. " toggles case sensitivity.") or "") .. + (tr and (" " .. tr .. " toggles plain find.") or "") end +local function update_preview(sel, search_fn, text) + local ok, line1, col1, line2, col2 = + pcall(search_fn, last_view.doc, sel[1], sel[2], text, case_insensitive, plain) + if ok and line1 and text ~= "" then + last_view.doc:set_selection(line2, col2, line1, col1) + last_view:scroll_to_line(line2, true) + return true + else + last_view.doc:set_selection(unpack(sel)) + return false + end +end local function find(label, search_fn) - local dv = core.active_view - local sel = { dv.doc:get_selection() } - local text = dv.doc:get_text(table.unpack(sel)) - local found = false + last_view, last_sel, last_finds = core.active_view, + { core.active_view.doc:get_selection() }, {} + local text, found = last_view.doc:get_text(unpack(last_sel)), false core.command_view:set_text(text, true) + core.status_view:show_tooltip(get_find_tooltip()) core.command_view:enter(label, function(text) + core.status_view:remove_tooltip() if found then last_fn, last_text = search_fn, text - previous_finds = {} - push_previous_find(dv.doc, sel) else core.error("Couldn't find %q", text) - dv.doc:set_selection(table.unpack(sel)) - dv:scroll_to_make_visible(sel[1], sel[2]) + last_view.doc:set_selection(unpack(last_sel)) + last_view:scroll_to_make_visible(unpack(last_sel)) end - end, function(text) - local ok, line1, col1, line2, col2 = pcall(search_fn, dv.doc, sel[1], sel[2], text) - if ok and line1 and text ~= "" then - dv.doc:set_selection(line2, col2, line1, col1) - dv:scroll_to_line(line2, true) - found = true - else - dv.doc:set_selection(table.unpack(sel)) - found = false - end - + found = update_preview(last_sel, search_fn, text) + last_fn, last_text = search_fn, text end, function(explicit) + core.status_view:remove_tooltip() if explicit then - dv.doc:set_selection(table.unpack(sel)) - dv:scroll_to_make_visible(sel[1], sel[2]) + last_view.doc:set_selection(unpack(last_sel)) + last_view:scroll_to_make_visible(unpack(last_sel)) end end) end @@ -71,6 +74,7 @@ end local function replace(kind, default, fn) core.command_view:set_text(default, true) + core.status_view:show_tooltip(get_find_tooltip()) core.command_view:enter("Find To Replace " .. kind, function(old) core.command_view:set_text(old, true) @@ -80,75 +84,39 @@ local function replace(kind, default, fn) return fn(text, old, new) end) core.log("Replaced %d instance(s) of %s %q with %q", n, kind, old, new) + end, function() end, function() + core.status_view:remove_tooltip() end) end) end - local function has_selection() - return core.active_view:is(DocView) - and core.active_view.doc:has_selection() + return core.active_view:is(DocView) and core.active_view.doc:has_selection() end - command.add(has_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 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 }) command.add("core.docview", { ["find-replace:find"] = function() - find("Find Text", function(doc, line, col, text) - local opt = { wrap = true, no_case = true } + find("Find Text", function(doc, line, col, text, case_insensitive, plain) + local opt = { wrap = true, no_case = case_insensitive, regex = not plain } return search.find(doc, line, col, text, opt) end) end, - ["find-replace:find-regex"] = function() - find("Find Text Regex", function(doc, line, col, text) - local opt = { wrap = true, no_case = true, regex = true } - return search.find(doc, line, col, text, opt) - end) - end, - - ["find-replace:repeat-find"] = function() - if not last_fn then - core.error("No find to continue from") - else - local line, col = doc():get_selection() - local line1, col1, line2, col2 = last_fn(doc(), line, col, last_text) - if line1 then - push_previous_find(doc()) - doc():set_selection(line2, col2, line1, col1) - core.active_view:scroll_to_line(line2, true) - end - end - end, - - ["find-replace:previous-find"] = function() - local sel = table.remove(previous_finds) - if not sel or doc() ~= last_doc then - core.error("No previous finds") - return - end - doc():set_selection(table.unpack(sel)) - core.active_view:scroll_to_line(sel[3], true) - end, - ["find-replace:replace"] = function() - replace("Text", "", function(text, old, new) - return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil) - end) - end, - - ["find-replace:replace-regex"] = function() - replace("Regex", "", function(text, old, new) - local re = regex.compile(old) - return regex.gsub(re, text, new) + replace("Text", function(text, old, new) + if plain then + return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil) + end + return regex.gsub(regex.compile(old), text, new) end) end, @@ -170,3 +138,53 @@ command.add("core.docview", { end) end, }) + +local function valid_for_finding() + return core.active_view:is(DocView) or core.active_view:is(CommandView) +end + +command.add(valid_for_finding, { + ["find-replace:repeat-find"] = function() + if not last_fn then + core.error("No find to continue from") + else + local line, col = doc():get_selection() + local line1, col1, line2, col2 = last_fn(doc(), line, col, last_text, case_insensitive, plain) + 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, { line, col }) + doc():set_selection(line2, col2, line1, col1) + last_view:scroll_to_line(line2, true) + 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 + end + doc():set_selection(table.unpack(sel)) + last_view:scroll_to_line(sel[3], true) + end, +}) + +command.add("core.commandview", { + ["find-replace:toggle-insensitivity"] = function() + case_insensitive = not case_insensitive + core.status_view:show_tooltip(get_find_tooltip()) + update_preview(last_sel, last_fn, last_text) + end, + + ["find-replace:toggle-plain"] = function() + plain = not plain + core.status_view:show_tooltip(get_find_tooltip()) + update_preview(last_sel, last_fn, last_text) + end +}) diff --git a/data/core/keymap.lua b/data/core/keymap.lua index 0bd153b8..c0d556b8 100644 --- a/data/core/keymap.lua +++ b/data/core/keymap.lua @@ -137,6 +137,8 @@ keymap.add_direct { ["ctrl+r"] = "find-replace:replace", ["f3"] = "find-replace:repeat-find", ["shift+f3"] = "find-replace:previous-find", + ["ctrl+i"] = "find-replace:toggle-insensitivity", + ["ctrl+shift+i"] = "find-replace:toggle-plain", ["ctrl+g"] = "doc:go-to-line", ["ctrl+s"] = "doc:save", ["ctrl+shift+s"] = "doc:save-as",