diff options
Diffstat (limited to 'data/core/doc/search.lua')
| -rw-r--r-- | data/core/doc/search.lua | 65 |
1 files changed, 45 insertions, 20 deletions
diff --git a/data/core/doc/search.lua b/data/core/doc/search.lua index 04090673..8395769a 100644 --- a/data/core/doc/search.lua +++ b/data/core/doc/search.lua @@ -22,37 +22,62 @@ 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 plain = not opt.pattern + 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 + 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, plain) 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 + s, e = search_func(line_text, pattern, col, plain) + 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 |
