luacheck: unused loop variable after 024df10
[lisp-parkour.git] / input.lua
blobda78a0c0a1bb9d0402983f01c8778c063b3e9fcf
1 local M = {}
3 local function startof(node) return node.start end
4 local function finishof(node) return node.finish end
6 local function make_insert(parser, d1, D2, walker, edit, write, eol_at, bol_at)
8 local function open(range, seek, opening, auto_square)
9 local escaped = walker.escaped_at(range)
10 if escaped and range.start > escaped.start then
11 if opening == escaped.d then
12 seek(escaped.finish + 1)
13 return true
14 elseif escaped.is_char then
15 return true
16 end
17 write(range.start, opening)
18 seek(range.start + #opening)
19 parser.tree.rewind(escaped.start)
20 else
21 local _, parent = walker.sexp_at(range)
22 -- TODO: make it work even when before the first existing element on the electric line:
23 local on_electric_line = parent.finish and parent.finish > eol_at(range.finish)
24 and not parent.find_after(range, function(t) return t.indent end)
25 local newpos = edit.insert_pair(range, opening, auto_square)
26 seek(edit.refmt_at(parent, {start = newpos, finish = newpos}, on_electric_line))
27 end
28 return true
29 end
31 local function close(range, seek, kind)
32 local pos = range.start
33 local escaped = walker.escaped_at(range)
34 if escaped then
35 write(pos, kind)
36 seek(pos + 1)
37 parser.tree.rewind(escaped.start)
38 else
39 local _, parent = walker.sexp_at(range)
40 if parent and parent.is_list then
41 range = {start = parent.finish, finish = parent.finish}
42 local newpos = edit.refmt_at(parent, range)
43 seek(newpos + 1)
44 parser.tree.rewind(parent.start)
45 end
46 end
47 return true
48 end
50 local function spacekey(range, seek, char)
51 local sexp, parent = walker.sexp_at(range)
52 -- if parent[#parent + 1] is nil, we are at EOF
53 if not parent.is_root or parent.is_parsed(range.start) or parent[#parent + 1] then
54 parser.tree.rewind(parent.start or range.start)
55 end
56 if not parent.is_list or parent.is_empty then return end
57 local after_last = parent[#parent].finish and range.start > parent[#parent].finish
58 local bol = bol_at(range.start)
59 local eol = eol_at(range.start)
60 local prev = parent.before(range.start, finishof)
61 local nxt = parent.after(range.start, startof)
62 local on_empty_line = not sexp and ((prev and prev.finish or parent.start) < bol)
63 and ((nxt and nxt.start or parent.finish) > eol)
64 local at_eol = range.start == eol
65 local newpos
66 if not on_empty_line then
67 local _
68 write(range.start, char)
69 newpos = range.start + #char
70 _, parent = walker.sexp_at(range)
71 else
72 newpos = bol - 1
73 end
74 if not at_eol or on_empty_line then
75 newpos = edit.refmt_at(parent, {start = newpos, finish = newpos}, after_last and not on_empty_line)
76 if on_empty_line then
77 parser.tree.rewind(newpos)
78 write(newpos, char)
79 newpos = newpos + #char
80 end
81 end
82 seek(newpos)
83 return true
84 end
86 local function enterkey(range, seek)
87 local _, parent = walker.sexp_at(range)
88 local newpos = edit.newline(parent, range)
89 if newpos then
90 seek(newpos)
91 return true
92 end
93 end
95 local comment_start = parser.opposite["\n"]
97 local function open_comment(range, seek, char)
98 local escaped = walker.escaped_at(range)
99 if escaped and escaped.is_char then return true end
100 local datum, sexp, parent
101 if not (escaped and range.start > escaped.start) then
102 sexp, parent = walker.sexp_at(range, true)
103 datum = sexp and sexp.p and sexp.p:find("^#") and range.start == sexp.start + 1
104 local _, next_safe = walker.next_finish_wrapped(range)
105 local eol = eol_at(range.start) -- XXX: must be before the write. The vis eol_at wrapper is leaky
106 local bol = bol_at(range.start) -- XXX: must be before the write. The vis eol_at wrapper is leaky
107 if next_safe and not datum then
108 write(next_safe, parser.opposite[char])
110 if not datum then
111 local prev = parent.before(range.start, finishof)
112 local nxt = parent.after(range.start, startof)
113 local on_empty_line = not sexp and
114 ((prev and prev.finish or parent.start or range.start - 1) < bol)
115 and ((nxt and nxt.start or parent.finish or range.start + 1) > eol)
116 if on_empty_line or next_safe == range.start and next_safe ~= parent.finish then
117 if parent.is_root then
118 char = char:rep(3) .. " "
119 else
120 char = "\n" .. char:rep(2) .. " "
122 elseif nxt and nxt.start > eol or range.start == eol then
123 local tabwidth = 8
124 local margin_column = 40
125 local last_col = (not nxt and prev and prev.finish or eol) - bol
126 local margin = math.ceil((margin_column - last_col) / tabwidth)
127 char = (margin > 0 and string.rep("\t", margin) or " ")..char
131 write(range.start, char)
132 -- if parent[#parent + 1] is nil, we are at EOF
133 if not parent or not parent.is_root or parent.is_parsed(range.start) or parent[#parent + 1] then
134 parser.tree.rewind(range.start - (datum and 1 or 0))
136 range.start = range.start + #char
137 range.finish = range.start
138 if not escaped and not parent.is_root then
139 range.start = edit.refmt_at(parent, range)
141 seek(range.start)
142 return true
145 return function(range, seek, char, auto_square)
146 local handler =
147 d1:match(char) and open or
148 char == '"' and open or
149 parser.opposite["|"] and char == "|" and open or
150 D2:match(char) and close or
151 char == " " and spacekey or
152 char == "\n" and enterkey or
153 char == comment_start and open_comment
154 if handler then
155 return handler(range, seek, char, auto_square)
156 else
157 if parser.tree.is_parsed(range.start) then
158 parser.tree.rewind(range.start)
164 function M.new(parser, d1, D2, walker, edit, write, eol_at, bol_at)
165 return {
166 insert = make_insert(parser, d1, D2, walker, edit, write, eol_at, bol_at),
170 return M