3 local swap
= {["("] = "[", [")"] = "]", ["["] = "(", ["]"] = ")"}
5 local function startof(node
) return node
.start
end
6 local function finishof(node
) return node
.finish
end
8 local function make_insert(parser
, d1
, D2
, walker
, edit
, write, eol_at
, bol_at
)
10 local function open(range
, seek, opening
, shiftless
, auto_square
)
11 local escaped
= walker
.escaped_at(range
)
12 if escaped
and range
.start
> escaped
.start
then
13 if opening
== escaped
.d
then
14 seek(escaped
.finish
+ 1)
16 elseif escaped
.is_char
then
19 write(range
.start
, opening
)
20 seek(range
.start
+ #opening
)
21 parser
.tree
.rewind(escaped
.start
)
23 local _
, parent
= walker
.sexp_at(range
)
24 -- TODO: make it work even when before the first existing element on the electric line:
25 local on_electric_line
= parent
.finish
and parent
.finish
> eol_at(range
.finish
)
26 and not parent
.find_after(range
, function(t
) return t
.indent
end)
27 local newpos
= edit
.insert_pair(range
, opening
, shiftless
, auto_square
)
28 seek(edit
.refmt_at(parent
, {start
= newpos
, finish
= newpos
}, on_electric_line
))
33 local function close(range
, seek, kind
)
34 local pos
= range
.start
35 local escaped
= walker
.escaped_at(range
)
39 parser
.tree
.rewind(escaped
.start
)
41 local _
, parent
= walker
.sexp_at(range
)
42 if parent
and parent
.is_list
then
43 range
= {start
= parent
.finish
, finish
= parent
.finish
}
44 local newpos
= edit
.refmt_at(parent
, range
)
46 parser
.tree
.rewind(parent
.start
)
52 local function spacekey(range
, seek, char
)
53 local sexp
, parent
= walker
.sexp_at(range
)
54 -- if parent[#parent + 1] is nil, we are at EOF
55 if not parent
.is_root
or parent
.is_parsed(range
.start
) or parent
[#parent
+ 1] then
56 parser
.tree
.rewind(parent
.start
or range
.start
)
58 if not parent
.is_list
or parent
.is_empty
then return end
59 local after_last
= parent
[#parent
].finish
and range
.start
> parent
[#parent
].finish
60 local bol
= bol_at(range
.start
)
61 local eol
= eol_at(range
.start
)
62 local prev
= parent
.before(range
.start
, finishof
)
63 local nxt
= parent
.after(range
.start
, startof
)
64 local on_empty_line
= not sexp
and ((prev
and prev
.finish
or parent
.start
) < bol
)
65 and ((nxt
and nxt
.start
or parent
.finish
) > eol
)
66 local at_eol
= range
.start
== eol
68 if not on_empty_line
then
70 write(range
.start
, char
)
71 newpos
= range
.start
+ #char
72 _
, parent
= walker
.sexp_at(range
)
76 if not at_eol
or on_empty_line
then
77 newpos
= edit
.refmt_at(parent
, {start
= newpos
, finish
= newpos
}, after_last
and not on_empty_line
)
79 parser
.tree
.rewind(newpos
)
81 newpos
= newpos
+ #char
88 local function enterkey(range
, seek)
89 local _
, parent
= walker
.sexp_at(range
)
90 local newpos
= edit
.newline(parent
, range
)
97 local comment_start
= parser
.opposite
["\n"]
99 local function open_comment(range
, seek, char
)
100 local escaped
= walker
.escaped_at(range
)
101 if escaped
and escaped
.is_char
then return true end
102 local datum
, sexp
, parent
103 if not (escaped
and range
.start
> escaped
.start
) then
104 sexp
, parent
= walker
.sexp_at(range
, true)
105 datum
= sexp
and sexp
.p
and sexp
.p
:find("^#") and range
.start
== sexp
.start
+ 1
106 local _
, next_safe
= walker
.next_finish_wrapped(range
)
107 local eol
= eol_at(range
.start
) -- XXX: must be before the write. The vis eol_at wrapper is leaky
108 local bol
= bol_at(range
.start
) -- XXX: must be before the write. The vis eol_at wrapper is leaky
109 if next_safe
and not datum
then
110 write(next_safe
, parser
.opposite
[char
])
113 local prev
= parent
.before(range
.start
, finishof
)
114 local nxt
= parent
.after(range
.start
, startof
)
115 local on_empty_line
= not sexp
and
116 ((prev
and prev
.finish
or parent
.start
or range
.start
- 1) < bol
)
117 and ((nxt
and nxt
.start
or parent
.finish
or range
.start
+ 1) > eol
)
118 if on_empty_line
or next_safe
== range
.start
and next_safe
~= parent
.finish
then
119 if parent
.is_root
then
120 char
= char
:rep(3) .. " "
122 char
= "\n" .. char
:rep(2) .. " "
124 elseif nxt
and nxt
.start
> eol
or range
.start
== eol
then
126 local margin_column
= 40
127 local last_col
= (not nxt
and prev
.finish
or eol
) - bol
128 local margin
= math
.ceil((margin_column
- last_col
) / tabwidth
)
129 char
= (margin
> 0 and string.rep("\t", margin
) or " ")..char
133 write(range
.start
, char
)
134 -- if parent[#parent + 1] is nil, we are at EOF
135 if not parent
or not parent
.is_root
or parent
.is_parsed(range
.start
) or parent
[#parent
+ 1] then
136 parser
.tree
.rewind(range
.start
- (datum
and 1 or 0))
138 range
.start
= range
.start
+ #char
139 range
.finish
= range
.start
140 if not escaped
and not parent
.is_root
then
141 range
.start
= edit
.refmt_at(parent
, range
)
147 return function(range
, seek, char
, shiftless
, auto_square
)
149 char
= swap
[char
] or char
152 d1
:match(char
) and open
or
153 char
== '"' and open
or
154 parser
.opposite
["|"] and char
== "|" and open
or
155 D2
:match(char
) and close
or
156 char
== " " and spacekey
or
157 char
== "\n" and enterkey
or
158 char
== comment_start
and open_comment
160 return handler(range
, seek, char
, shiftless
, auto_square
)
162 if parser
.tree
.is_parsed(range
.start
) then
163 parser
.tree
.rewind(range
.start
)
165 if shiftless
and char
:find
"[%[%]]" then
166 write(range
.start
, char
)
167 seek(range
.start
+ 1)
174 function M
.new(parser
, d1
, D2
, walker
, edit
, write, eol_at
, bol_at
)
176 insert
= make_insert(parser
, d1
, D2
, walker
, edit
, write, eol_at
, bol_at
),