1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
-- helpers for the search bar (C-f)
function Text.draw_search_bar(State)
local h = State.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)
App.screen.print(State.search_term, 25,y-5)
Text.draw_cursor(State, 25+State.font:getWidth(State.search_term),y-5)
end
function Text.search_next(State)
assert(#State.search_term > 0)
-- search current line from cursor
local curr_pos = State.cursor1.pos
local curr_line = State.lines[State.cursor1.line].data
local curr_offset = Text.offset(curr_line, curr_pos)
local offset = find(curr_line, State.search_term, curr_offset, --[[literal]] true)
if offset then
State.cursor1.pos = utf8.len(curr_line, 1, offset)
end
if offset == nil then
-- search lines below cursor
for i=State.cursor1.line+1,#State.lines do
local curr_line = State.lines[i].data
offset = find(curr_line, State.search_term, --[[from start]] nil, --[[literal]] true)
if offset then
State.cursor1 = {line=i, pos=utf8.len(curr_line, 1, offset)}
break
end
end
end
if offset == nil then
-- wrap around
for i=1,State.cursor1.line-1 do
local curr_line = State.lines[i].data
offset = find(curr_line, State.search_term, --[[from start]] nil, --[[literal]] true)
if offset then
State.cursor1 = {line=i, pos=utf8.len(curr_line, 1, offset)}
break
end
end
end
if offset == nil then
-- search current line until cursor
local curr_line = State.lines[State.cursor1.line].data
offset = find(curr_line, State.search_term, --[[from start]] nil, --[[literal]] true)
local pos = utf8.len(curr_line, 1, offset)
if pos and pos < State.cursor1.pos then
State.cursor1.pos = pos
end
end
if offset == nil then
State.cursor1.line = State.search_backup.cursor.line
State.cursor1.pos = State.search_backup.cursor.pos
State.screen_top1.line = State.search_backup.screen_top.line
State.screen_top1.pos = State.search_backup.screen_top.pos
end
local screen_bottom1 = Text.screen_bottom1(State)
if Text.lt1(State.cursor1, State.screen_top1) or Text.lt1(screen_bottom1, State.cursor1) then
State.screen_top1.line = State.cursor1.line
local pos = Text.pos_at_start_of_screen_line(State, State.cursor1)
State.screen_top1.pos = pos
end
end
function Text.search_previous(State)
assert(#State.search_term > 0)
-- search current line before cursor
local curr_pos = State.cursor1.pos
local curr_line = State.lines[State.cursor1.line].data
local curr_offset = Text.offset(curr_line, curr_pos)
local offset = rfind(curr_line, State.search_term, curr_offset-1, --[[literal]] true)
if offset then
State.cursor1.pos = utf8.len(curr_line, 1, offset)
end
if offset == nil then
-- search lines above cursor
for i=State.cursor1.line-1,1,-1 do
local curr_line = State.lines[i].data
offset = rfind(curr_line, State.search_term, --[[from end]] nil, --[[literal]] true)
if offset then
State.cursor1 = {line=i, pos=utf8.len(curr_line, 1, offset)}
break
end
end
end
if offset == nil then
-- wrap around
for i=#State.lines,State.cursor1.line+1,-1 do
local curr_line = State.lines[i].data
offset = rfind(curr_line, State.search_term, --[[from end]] nil, --[[literal]] true)
if offset then
State.cursor1 = {line=i, pos=utf8.len(curr_line, 1, offset)}
break
end
end
end
if offset == nil then
-- search current line after cursor
local curr_line = State.lines[State.cursor1.line].data
offset = rfind(curr_line, State.search_term, --[[from end]] nil, --[[literal]] true)
local pos = utf8.len(curr_line, 1, offset)
if pos and pos > State.cursor1.pos then
State.cursor1.pos = pos
end
end
if offset == nil then
State.cursor1.line = State.search_backup.cursor.line
State.cursor1.pos = State.search_backup.cursor.pos
State.screen_top1.line = State.search_backup.screen_top.line
State.screen_top1.pos = State.search_backup.screen_top.pos
end
local screen_bottom1 = Text.screen_bottom1(State)
if Text.lt1(State.cursor1, State.screen_top1) or Text.lt1(screen_bottom1, State.cursor1) then
State.screen_top1.line = State.cursor1.line
local pos = Text.pos_at_start_of_screen_line(State, State.cursor1)
State.screen_top1.pos = pos
end
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
|