improve line comment joining and splicing
[lisp-parkour.git] / input.lua
blob1915d39cf5cf2ce0af726e1b61137f79ece7b4ab
1 local M = {}
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)
15 return true
16 elseif escaped.is_char then
17 return true
18 end
19 write(range.start, opening)
20 seek(range.start + #opening)
21 parser.tree.rewind(escaped.start)
22 else
23 local _, parent = walker.sexp_at(range)
24 local on_electric_line = parent.finish and parent.finish > eol_at(range.finish)
25 and not parent.find_after(range, function(t) return t.indent end)
26 local newpos = edit.insert_pair(range, opening, shiftless, auto_square)
27 seek(edit.refmt_at(parent, {start = newpos, finish = newpos}, on_electric_line) or newpos)
28 end
29 return true
30 end
32 local function close(range, seek, kind)
33 local pos = range.start
34 local escaped = walker.escaped_at(range)
35 if escaped then
36 write(pos, kind)
37 seek(pos + 1)
38 parser.tree.rewind(escaped.start)
39 else
40 local _, parent = walker.sexp_at(range)
41 if parent and parent.is_list then
42 range = {start = parent.finish, finish = parent.finish}
43 local newpos = edit.refmt_at(parent, range)
44 seek(newpos + 1)
45 parser.tree.rewind(parent.start)
46 end
47 end
48 return true
49 end
51 local function spacekey(range)
52 local _, parent = walker.sexp_at(range)
53 -- if parent[#parent + 1] is nil, we are at EOF
54 if not parent.is_root or parent.is_parsed(range.start) or parent[#parent + 1] then
55 parser.tree.rewind(parent.start or range.start)
56 end
57 end
59 local function enterkey(range, seek)
60 local _, parent = walker.sexp_at(range)
61 local newpos = edit.newline(parent, range)
62 if newpos then
63 seek(newpos)
64 return true
65 end
66 end
68 local function semicolon(range, seek, char)
69 local escaped = walker.escaped_at(range)
70 if escaped and escaped.is_char then return true end
71 local datum, sexp, parent
72 if not (escaped and range.start > escaped.start) then
73 sexp, parent = walker.sexp_at(range, true)
74 datum = sexp and sexp.p and sexp.p:find("^#") and range.start == sexp.start + 1
75 local _, next_safe = walker.next_finish_wrapped(range)
76 local eol = eol_at(range.start) -- XXX: must be before the write; the vis eol_at wrapper is leaky
77 local bol = bol_at(range.start) -- XXX: must be before the write; the vis eol_at wrapper is leaky
78 if next_safe and not datum then
79 write(next_safe, parser.opposite[char])
80 end
81 if not datum then
82 local prev = parent.before(range.start, finishof)
83 local nxt = parent.after(range.start, startof)
84 local on_empty_line = (not prev or prev.finish < bol) and (not nxt or nxt.start > eol + 1)
85 if on_empty_line or next_safe == range.start and next_safe ~= parent.finish then
86 if parent.is_root then
87 char = ";;; "
88 else
89 char = "\n;; "
90 end
91 elseif not nxt or nxt.start > eol + 1 then
92 local tabwidth = 8
93 local margin_column = 40
94 local last_col = (not nxt and prev.finish or eol) - bol
95 local margin = math.ceil((margin_column - last_col) / tabwidth)
96 char = (margin > 0 and string.rep("\t", margin) or " ")..char
97 end
98 end
99 end
100 write(range.start, char)
101 -- if parent[#parent + 1] is nil, we are at EOF
102 if not parent or not parent.is_root or parent.is_parsed(range.start) or parent[#parent + 1] then
103 parser.tree.rewind(range.start - (datum and 1 or 0))
105 range.start = range.start + #char
106 range.finish = range.start
107 if not escaped and not parent.is_root then
108 range.start = edit.refmt_at(parent, range) or range.start
110 seek(range.start)
111 return true
114 return function(range, seek, char, shiftless, auto_square)
115 if shiftless then
116 char = swap[char] or char
118 local handler =
119 d1:match(char) and open or
120 char == '"' and open or
121 parser.opposite['|'] and char == '|' and open or
122 D2:match(char) and close or
123 char == " " and spacekey or
124 char == "\n" and enterkey or
125 char == ";" and semicolon
126 if handler then
127 return handler(range, seek, char, shiftless, auto_square)
128 else
129 if parser.tree.is_parsed(range.start) then
130 parser.tree.rewind(range.start)
132 if shiftless and char:find'[%[%]]' then
133 write(range.start, char)
134 seek(range.start + 1)
135 return true
141 function M.new(parser, d1, D2, walker, edit, write, eol_at, bol_at)
142 return {
143 insert = make_insert(parser, d1, D2, walker, edit, write, eol_at, bol_at),
147 return M