178 lines
6.2 KiB
Lua
178 lines
6.2 KiB
Lua
-- helpers for the search bar (C-f)
|
|
|
|
function Text.draw_search_bar(editor)
|
|
local h = editor.line_height+2
|
|
local y = App.screen.height-h
|
|
love.graphics.setColor(0.9,0.9,0.9)
|
|
love.graphics.rectangle('fill', 0, y-10, App.screen.width-1, h+8)
|
|
love.graphics.setColor(0.6,0.6,0.6)
|
|
love.graphics.line(0, y-10, App.screen.width-1, y-10)
|
|
love.graphics.setColor(1,1,1)
|
|
love.graphics.rectangle('fill', 20, y-6, App.screen.width-40, h+2, 2,2)
|
|
love.graphics.setColor(0.6,0.6,0.6)
|
|
love.graphics.rectangle('line', 20, y-6, App.screen.width-40, h+2, 2,2)
|
|
App.color(Text_color)
|
|
love.graphics.print(editor.search_term, 25,y-5)
|
|
Text.draw_cursor(editor, 25+editor.font:getWidth(editor.search_term),y-5)
|
|
end
|
|
|
|
function Text.search_next(editor)
|
|
local offset
|
|
if editor.cursor.mode == 'text' then
|
|
-- search current line from cursor
|
|
local curr_pos = editor.cursor.pos
|
|
local curr_line = editor.lines[editor.cursor.line].data
|
|
local curr_offset = Text.offset(curr_line, curr_pos)
|
|
offset = find(curr_line, editor.search_term, curr_offset, --[[literal]] true)
|
|
if offset then
|
|
editor.cursor.pos = utf8.len(curr_line, 1, offset)
|
|
end
|
|
end
|
|
if offset == nil then
|
|
-- search lines below cursor
|
|
for i=editor.cursor.line+1,#editor.lines do
|
|
if editor.lines[i].mode == 'text' then
|
|
local curr_line = editor.lines[i].data
|
|
offset = find(curr_line, editor.search_term, --[[from start]] nil, --[[literal]] true)
|
|
if offset then
|
|
editor.cursor = {mode='text', line=i, pos=utf8.len(curr_line, 1, offset)}
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if offset == nil then
|
|
-- wrap around
|
|
for i=1,editor.cursor.line-1 do
|
|
if editor.lines[i].mode == 'text' then
|
|
local curr_line = editor.lines[i].data
|
|
offset = find(curr_line, editor.search_term, --[[from start]] nil, --[[literal]] true)
|
|
if offset then
|
|
editor.cursor = {mode='text', line=i, pos=utf8.len(curr_line, 1, offset)}
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if offset == nil then
|
|
-- search current line until cursor
|
|
if editor.lines[editor.cursor.line].mode == 'text' then
|
|
local curr_line = editor.lines[editor.cursor.line].data
|
|
offset = find(curr_line, editor.search_term, --[[from start]] nil, --[[literal]] true)
|
|
local pos = utf8.len(curr_line, 1, offset)
|
|
if pos and pos < editor.cursor.pos then
|
|
editor.cursor.pos = pos
|
|
end
|
|
end
|
|
end
|
|
if offset then
|
|
maybe_snap_cursor_to_bottom_of_screen(editor)
|
|
else
|
|
-- roll back
|
|
editor.cursor = deepcopy(editor.search_backup.cursor)
|
|
editor.screen_top = deepcopy(editor.search_backup.screen_top)
|
|
end
|
|
end
|
|
|
|
function Text.search_previous(editor)
|
|
local offset
|
|
if editor.cursor.mode == 'text' then
|
|
-- search current line before cursor
|
|
local curr_pos = editor.cursor.pos
|
|
local curr_line = editor.lines[editor.cursor.line].data
|
|
local curr_offset = Text.offset(curr_line, curr_pos)
|
|
offset = rfind(curr_line, editor.search_term, curr_offset-1, --[[literal]] true)
|
|
if offset then
|
|
editor.cursor.pos = utf8.len(curr_line, 1, offset)
|
|
end
|
|
end
|
|
if offset == nil then
|
|
-- search lines above cursor
|
|
for i=editor.cursor.line-1,1,-1 do
|
|
if editor.lines[i].mode == 'text' then
|
|
local curr_line = editor.lines[i].data
|
|
offset = rfind(curr_line, editor.search_term, --[[from end]] nil, --[[literal]] true)
|
|
if offset then
|
|
editor.cursor = {mode='text', line=i, pos=utf8.len(curr_line, 1, offset)}
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if offset == nil then
|
|
-- wrap around
|
|
for i=#editor.lines,editor.cursor.line+1,-1 do
|
|
if editor.lines[i].mode == 'text' then
|
|
local curr_line = editor.lines[i].data
|
|
offset = rfind(curr_line, editor.search_term, --[[from end]] nil, --[[literal]] true)
|
|
if offset then
|
|
editor.cursor = {mode='text', line=i, pos=utf8.len(curr_line, 1, offset)}
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if offset == nil then
|
|
-- search current line after cursor
|
|
if editor.lines[editor.cursor.line].mode == 'text' then
|
|
local curr_line = editor.lines[editor.cursor.line].data
|
|
offset = rfind(curr_line, editor.search_term, --[[from end]] nil, --[[literal]] true)
|
|
local pos = utf8.len(curr_line, 1, offset)
|
|
if pos and pos > editor.cursor.pos then
|
|
editor.cursor.pos = pos
|
|
end
|
|
end
|
|
end
|
|
if offset then
|
|
maybe_snap_cursor_to_top_of_screen(editor)
|
|
else
|
|
-- roll back
|
|
editor.cursor = deepcopy(editor.search_backup.cursor)
|
|
editor.screen_top = deepcopy(editor.search_backup.screen_top)
|
|
end
|
|
end
|
|
|
|
-- return true if cursor marks start of search term and line_index,pos is in that region
|
|
function in_search(editor, line_index, pos)
|
|
if editor.search_term == nil then return false end
|
|
if #editor.search_term == 0 then return false end
|
|
if editor.cursor.mode == 'drawing' then return false end
|
|
if line_index ~= editor.cursor.line then return false end
|
|
return find_at(editor.lines[line_index].data, editor.search_term, editor.cursor.pos)
|
|
and editor.cursor.pos <= pos and pos <= editor.cursor.pos+utf8.len(editor.search_term)-1
|
|
end
|
|
|
|
function find_at(data, pat, pos)
|
|
local offset = utf8.offset(data, pos)
|
|
return data:sub(offset, offset+#pat-1) == pat
|
|
end
|
|
|
|
function find(s, pat, i, plain)
|
|
if s == nil then return end
|
|
return s:find(pat, i, plain)
|
|
end
|
|
|
|
-- TODO: avoid the expensive reverse() operations
|
|
-- Particularly if we only care about literal matches, we don't need all of string.find
|
|
function rfind(s, pat, i, plain)
|
|
if s == nil then return end
|
|
if #pat == 0 then return #s end
|
|
local rs = s:reverse()
|
|
local rpat = pat:reverse()
|
|
if i == nil then i = #s end
|
|
local ri = #s - i + 1
|
|
local rendpos = rs:find(rpat, ri, plain)
|
|
if rendpos == nil then return nil end
|
|
local endpos = #s - rendpos + 1
|
|
assert (endpos >= #pat, ('rfind: endpos %d should be >= #pat %d at this point'):format(endpos, #pat))
|
|
return endpos-#pat+1
|
|
end
|
|
|
|
function test_rfind()
|
|
check_eq(rfind('abc', ''), 3, 'empty pattern')
|
|
check_eq(rfind('abc', 'c'), 3, 'final char')
|
|
check_eq(rfind('acbc', 'c', 3), 2, 'previous char')
|
|
check_nil(rfind('abc', 'd'), 'missing char')
|
|
check_nil(rfind('abc', 'c', 2), 'no more char')
|
|
end
|