3 local function finishof(node
) return node
.finish
end
4 local function startof(node
) return node
.start
end
5 local function is_comment(node
) return node
.is_comment
end
7 function M
.new(parser
, eol_at
, bol_at
)
9 local function in_quasilist(t
, range
)
10 return t
and t
.d
and not t
.is_list
and
11 range
.start
>= t
.start
+ #t
.d
and range
.finish
<= t
.finish
+ 1 - #parser
.opposite
[t
.d
]
14 local function sexp_at(range
, true_sexp
)
15 local node
, parent
, nth
= parser
.tree
.sexp_at(range
)
16 if not in_quasilist(node
, range
) or true_sexp
then
17 return node
, parent
, nth
19 return node
.word_at(range
), node
23 local function find_before_innermost(t
, range
, key
, pred
)
24 if t
.is_root
or (t
.d
and t
.start
< range
.start
) then
25 if t
.d
and not t
.is_list
then
27 local start
= keypos
== startof(t
)
28 local finish
= keypos
== finishof(t
)
31 newpos
= t
.finish_before(range
.finish
)
33 newpos
= t
.start_before(range
.start
)
36 local word
= t
.word_at({start
= newpos
, finish
= newpos
})
37 if word
and pred(word
) then
43 local _
, nearest_n
= t
.before(range
.start
, startof
)
45 for n
= nearest_n
, 1, -1 do
47 if child
.d
and not child
.is_empty
then
48 local ret
= find_before_innermost(child
, range
, key
, pred
)
52 elseif key(child
) + 1 < key(range
) and pred(child
, n
, #t
) then
57 if not t
.is_root
and pred(t
) then
63 local function find_after_innermost(t
, range
, key
, pred
)
64 if t
.is_root
or (t
.d
and t
.finish
>= range
.finish
) then
65 if t
.d
and not t
.is_list
then
67 local start
= keypos
== startof(t
)
68 local finish
= keypos
== finishof(t
)
71 newpos
= t
.finish_after(math
.max(t
.start
+ #t
.d
, range
.finish
))
73 newpos
= range
.finish
< t
.start
and t
.start
+ #t
.d
or t
.start_after(range
.start
)
76 local word
= t
.word_at({start
= newpos
, finish
= newpos
})
77 if word
and pred(word
) then
83 local _
, nearest_n
= t
.after(range
.finish
, finishof
)
85 for n
= nearest_n
, #t
do
87 if child
.d
and not child
.is_empty
then
88 local ret
= find_after_innermost(child
, range
, key
, pred
)
92 elseif key(child
) > key(range
) and pred(child
, n
, #t
) then
97 if not t
.is_root
and pred(t
) then
105 start_before
= function(range
, skip
)
106 local _
, parent
= parser
.tree
.sexp_at(range
)
107 local node
= parent
.before(range
.start
, startof
, skip
)
108 return node
and startof(node
)
111 start_after
= function(range
, skip
)
112 local _
, parent
= parser
.tree
.sexp_at(range
)
113 local node
= parent
.after(range
.finish
, startof
, skip
)
114 return node
and startof(node
)
117 finish_before
= function(range
, skip
)
118 local _
, parent
= parser
.tree
.sexp_at(range
)
119 local node
= parent
.before(range
.start
, finishof
, skip
)
120 return node
and finishof(node
) + 1
123 finish_after
= function(range
, skip
)
124 local _
, parent
= parser
.tree
.sexp_at(range
)
125 local node
= parent
.after(range
.finish
, finishof
, skip
)
126 return node
and finishof(node
) + 1
129 start_down_after
= function(range
, skip
)
130 local node
, parent
= parser
.tree
.sexp_at(range
)
131 if in_quasilist(node
, range
) then return end
132 if node
and node
.p
then
133 -- XXX: if we are past a prefix start, this will prevent skipping over its list
134 range
.start
= node
.start
136 local next_list
= parent
.find_after(range
, function(t
) return t
.is_list
end)
138 local first
= next_list
.after(range
.finish
, startof
, skip
)
142 return next_list
.start
+ (next_list
.p
and #next_list
.p
or 0) + 1
147 start_up_before
= function(range
)
148 local _
, parent
= sexp_at(range
)
149 return parent
.d
and parent
.start
152 finish_down_before
= function(range
, skip
)
153 local node
, parent
= parser
.tree
.sexp_at(range
)
154 if in_quasilist(node
, range
) then return end
155 local prev_list
= parent
.find_before(range
, function(t
) return t
.is_list
end)
157 local last
= prev_list
.before(range
.start
, finishof
, skip
)
159 return (last
.finish
or last
.p
and last
.start
+ #last
.p
) + 1, prev_list
161 return prev_list
.finish
, prev_list
166 finish_up_after
= function(range
)
167 local _
, parent
= sexp_at(range
)
168 return parent
.d
and parent
.finish
+ 1
171 indented_before
= function(range
)
172 return find_before_innermost(parser
.tree
, range
, startof
, function(t
) return t
.indent
end)
175 start_float_before
= function(range
, up_empty_list
)
176 if up_empty_list
then
177 local _
, parent
= sexp_at(range
)
178 if parent
.is_empty
then
179 return parent
.start
, parent
182 local node
= find_before_innermost(parser
.tree
, range
, startof
, function(t
, n
, _
)
183 return not t
.d
or t
.is_empty
and n
== 1 end)
184 return node
and node
.start
, node
187 start_float_after
= function(range
, up_empty_list
)
188 if up_empty_list
then
189 local _
, parent
= sexp_at(range
)
190 if parent
.is_empty
then
191 return parent
.finish
+ 1, parent
194 local node
= find_after_innermost(parser
.tree
, range
, startof
, function(t
, n
, max_n
)
195 return not t
.d
or t
.is_empty
and n
== max_n
end)
196 return node
and node
.start
, node
199 finish_float_before
= function(range
, up_empty_list
)
200 if up_empty_list
then
201 local _
, parent
= sexp_at(range
)
202 if parent
.is_empty
then
203 return parent
.start
, parent
206 local node
= find_before_innermost(parser
.tree
, range
, finishof
, function(t
, n
, _
)
207 return not t
.d
or t
.is_empty
and n
== 1 end)
208 return node
and node
.finish
+ 1, node
211 finish_float_after
= function(range
, up_empty_list
)
212 if up_empty_list
then
213 local _
, parent
= sexp_at(range
)
214 if parent
.is_empty
then
215 return parent
.finish
+ 1, parent
218 local node
= find_after_innermost(parser
.tree
, range
, finishof
, function(t
, n
, max_n
)
219 return not t
.d
or t
.is_empty
and n
== max_n
end)
220 return node
and node
.finish
+ 1, node
223 escaped_at
= function(range
)
224 local node
= parser
.escaped
.around(range
)
225 -- TODO: distinguish between doublequoted strings, chars, line comments, and block comments
226 -- and use approprite offset comparisons for each
227 return node
and range
.start
> node
.start
and range
.finish
<= node
.finish
and node
230 prev_start_wrapped
= function(range
)
231 local node
, parent
= parser
.tree
.sexp_at(range
)
232 if in_quasilist(node
, range
) then parent
= node
end
233 local pstart
= parent
.start
and (parent
.start
+ (parent
.p
and #parent
.p
or 0) + #parent
.d
)
234 local bol
= bol_at(range
.start
)
235 local prev_wrapped
= (parent
.is_list
or parent
.is_root
) and parent
.find_before(range
,
236 function(t
) return t
.start
< bol
and t
.finish
> bol
end,
237 function(t
) return t
.finish
< bol
239 local prev_start
= prev_wrapped
and prev_wrapped
.start
or bol
240 pstart
= pstart
or prev_start
241 return math
.max(pstart
, prev_start
)
244 next_finish_wrapped
= function(range
)
245 local node
, parent
= parser
.tree
.sexp_at(range
)
246 if in_quasilist(node
, range
) then parent
= node
end
247 local pfinish
= parent
.finish
and parent
.finish
+ 1 - #parser
.opposite
[parent
.d
]
248 local eol
= eol_at(range
.start
)
249 local next_wrapped
= (parent
.is_list
or parent
.is_root
) and parent
.find_after(range
,
250 function(t
) return t
.start
< eol
and t
.finish
> eol
end,
251 function(t
) return t
.start
> eol
253 local next_finish
= next_wrapped
and next_wrapped
.finish
or eol
254 -- XXX: second return value, to be used for safe line-commenting:
255 local next_start_on_line
= math
.min(pfinish
or range
.start
, next_wrapped
and next_wrapped
.start
or eol
+ 1)
256 pfinish
= pfinish
and pfinish
- 1 or next_finish
257 return math
.min(pfinish
, next_finish
) + 1, next_start_on_line
< eol
+ 1 and next_start_on_line
or nil
260 beginning_of_defun
= function(range
, prev_line
)
261 local prev
, n
= parser
.tree
.before(range
.start
, startof
, is_comment
)
262 if not prev_line
then
263 return prev
and prev
.start
265 local pprev
= n
and parser
.tree
[n
- 1]
266 local has_eol
= pprev
and pprev
.is_comment
and parser
.opposite
[pprev
.d
]:find("\n") -- grep -n XYZZ\Y *.lua
267 local adjacent
= pprev
and prev
and prev
.start
- (pprev
.finish
+ (has_eol
and 0 or 1)) <= 1
268 return prev
and prev
.start
- ((adjacent
or prev
.start
== 0) and 0 or 1)
271 end_of_defun
= function(range
, next_line
)
272 local defun
= parser
.tree
.around(range
)
273 local nxt
, n
= parser
.tree
.find_after(defun
or range
, function(t
)
274 return not t
.is_comment
and t
.finish
> range
.finish
276 return nxt
and nxt
.finish
+ 1 + (next_line
and parser
.tree
[n
+ 1] and parser
.tree
[n
+ 1].indent
and 1 or 0)
279 repl_line_begin
= function(range
)
280 local _
, parent
= parser
.tree
.sexp_at(range
)
281 if not parent
.is_root
then return end
282 local prev_indent
= parent
.find_before(range
, function(t
) return t
.indent
end)
283 return prev_indent
and prev_indent
.start
287 paragraph_at
= parser
.tree
.around
,
288 goto_path
= parser
.tree
.goto_path
,
289 sexp_path
= parser
.tree
.sexp_path
,
290 find_after
= parser
.tree
.find_after
,
291 find_before
= parser
.tree
.find_before
,