From 56eace627a0702da5713240b8e5e2544cb6e9aa7 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Fri, 10 Sep 2021 21:32:34 +0200 Subject: [PATCH] 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