-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathlua.lua
357 lines (335 loc) · 10.9 KB
/
lua.lua
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
local code = require("dit.code")
local tab_complete = require("dit.tab_complete")
local mobdebug = require("dit.lua.mobdebug")
local check = require "luacheck.check"
local filter = require "luacheck.filter"
local ok, picotyped = pcall(require, "picotyped")
if not ok then
picotyped = nil
end
local lines
local commented_lines = {}
local controlled_change = false
local function run_luacheck(src)
if picotyped then
src = picotyped.translate(src)
end
local checker = type(check) == "function" and check or check.check
local ok, report = pcall(checker, src)
if not ok then report = {error = "syntax"} end
return filter.filter({ report })
end
function highlight_file()
lines = {}
local src = table.concat(buffer, "\n")
local report, err = run_luacheck(src)
if not report then
return
end
if report.error == "syntax" then
local _, err = load(src)
if err then
local nr = err:match("^[^:]*:([%d]+):.*")
local errmsg = err:match("^[^:]*:[%d]+: (.*)")
if nr then
lines[tonumber(nr)] = {{ column = 1, name = (" "):rep(255), code = 0, message = errmsg }}
end
end
return
end
for _, note in ipairs(report[1]) do
local t = lines[note.line] or {}
t[#t+1] = note
lines[note.line] = t
end
end
local function each_note(y, x)
return coroutine.wrap(function()
if not lines then return end
local curr = lines[y]
local line = buffer[y]
if not curr then return end
for _, note in ipairs(curr) do
local fchar = note.column
local lchar = fchar
if note.name then
lchar = fchar + #note.name - 1
else
while line[lchar+1]:match("[A-Za-z0-9_]") do
lchar = lchar + 1
end
end
if not x or (x >= fchar and x <= lchar) then
coroutine.yield(note, fchar, lchar)
end
end
end)
end
function highlight_line(line, y)
local ret = {}
for i = 1, #line do ret[i] = " " end
if mobdebug.is_debugging() then
local filename = buffer:filename()
if mobdebug.is_breakpoint(filename, y) then
ret[1] = "*"
end
if mobdebug.file == filename and mobdebug.line == y then
return ("*"):rep(#line)
end
end
for note, fchar, lchar in each_note(y) do
local key = "*"
if note.code == "111" then
key = "D"
-- For error numbers, see http://luacheck.readthedocs.org/en/0.11.0/warnings.html
elseif note.secondary or note.code == "421" or (note.code == "411" and note.name == "err") then
key = " "
elseif note.code:sub(1,1) == "6" then -- cosmetic notes
key = " "
end
for i = fchar, lchar do
ret[i] = key
end
end
if ret == nil then
return ""
end
for i = 1, #ret do
if not ret[i] then
ret[i] = " "
end
end
return table.concat(ret)
end
function on_change()
if not controlled_change then
lines = nil
end
end
function on_save(filename)
highlight_file()
end
local function prefix_if_indirect(fmt)
return function(w)
if w.indirect then
return "indirectly " .. fmt
else
return fmt
end
end
end
local message_formats = {
["011"] = "{msg}",
["021"] = "invalid inline option",
["022"] = "unpaired push directive",
["023"] = "unpaired pop directive",
["111"] = function(w)
if w.module then
return "setting non-module global variable {name!}"
else
return "setting non-standard global variable {name!}"
end
end,
["112"] = "mutating non-standard global variable {name!}",
["113"] = "accessing undefined variable {name!}",
["121"] = "setting read-only global variable {name!}",
["122"] = prefix_if_indirect("setting read-only field {field!} of global {name!}"),
["131"] = "unused global variable {name!}",
["142"] = prefix_if_indirect("setting undefined field {field!} of global {name!}"),
["143"] = prefix_if_indirect("accessing undefined field {field!} of global {name!}"),
["211"] = function(w)
if w.func then
if w.recursive then
return "unused recursive function {name!}"
elseif w.mutually_recursive then
return "unused mutually recursive function {name!}"
else
return "unused function {name!}"
end
else
return "unused variable {name!}"
end
end,
["212"] = function(w)
if w.name == "..." then
return "unused variable length argument"
else
return "unused argument {name!}"
end
end,
["213"] = "unused loop variable {name!}",
["221"] = "variable {name!} is never set",
["231"] = "variable {name!} is never accessed",
["232"] = "argument {name!} is never accessed",
["233"] = "loop variable {name!} is never accessed",
["241"] = "variable {name!} is mutated but never accessed",
["311"] = "value assigned to variable {name!} is unused",
["312"] = "value of argument {name!} is unused",
["313"] = "value of loop variable {name!} is unused",
["314"] = function(w)
return "value assigned to " .. (w.index and "index" or "field") .. " {field!} is unused"
end,
["321"] = "accessing uninitialized variable {name!}",
["331"] = "value assigned to variable {name!} is mutated but never accessed",
["341"] = "mutating uninitialized variable {name!}",
["411"] = "variable {name!} was previously defined on line {prev_line}",
["412"] = "variable {name!} was previously defined as an argument on line {prev_line}",
["413"] = "variable {name!} was previously defined as a loop variable on line {prev_line}",
["421"] = "shadowing definition of variable {name!} on line {prev_line}",
["422"] = "shadowing definition of argument {name!} on line {prev_line}",
["423"] = "shadowing definition of loop variable {name!} on line {prev_line}",
["431"] = "shadowing upvalue {name!} on line {prev_line}",
["432"] = "shadowing upvalue argument {name!} on line {prev_line}",
["433"] = "shadowing upvalue loop variable {name!} on line {prev_line}",
["511"] = "unreachable code",
["512"] = "loop is executed at most once",
["521"] = "unused label {label!}",
["531"] = "left-hand side of assignment is too short",
["532"] = "left-hand side of assignment is too long",
["541"] = "empty do..end block",
["542"] = "empty if branch",
["551"] = "empty statement",
["611"] = "line contains only whitespace",
["612"] = "line contains trailing whitespace",
["613"] = "trailing whitespace in a string",
["614"] = "trailing whitespace in a comment",
["621"] = "inconsistent indentation (SPACE followed by TAB)",
["631"] = "line is too long ({end_column} > {max_length})"
}
local function get_message_format(warning)
local message_format = message_formats[warning.code]
if type(message_format) == "function" then
return message_format(warning)
else
return message_format
end
end
-- Substitutes markers within string format with values from a table.
-- "{field_name}" marker is replaced with `values.field_name`.
-- "{field_name!}" marker adds quoting.
local function substitute(string_format, values)
return (string_format:gsub("{([_a-zA-Z0-9]+)(!?)}", function(field_name, highlight)
local value = tostring(assert(values[field_name], "No field " .. field_name))
if highlight == "!" then
return "'" .. value .. "'"
else
return value
end
end))
end
function on_ctrl(key)
if key == '_' then
controlled_change = true
code.comment_block("--", "%-%-", lines, commented_lines)
controlled_change = false
elseif key == "O" then
local str = buffer:selection()
if str == "" then
str = buffer:token()
end
if str and str ~= "" then
local out = mobdebug.command("eval " .. str)
if type(out) == "table" then
buffer:draw_popup(out)
end
else
buffer:draw_popup({ "Select a token to evaluate" })
end
elseif key == "D" then
local x, y = buffer:xy()
for note in each_note(y, x) do
local message = substitute(get_message_format(note), note)
buffer:draw_popup({message}) -- lines[y][x].description)
return true
end
end
return true
end
function on_fkey(key)
if key == "F7" then
code.expand_selection()
elseif key == "F2" then
local ok, err = mobdebug.listen()
if ok then
buffer:draw_popup({
"Now debbuging. Press:",
"F4 to step-over",
"Shift-F4 to step-into",
"F6 to toggle breakpoint",
"F11 to run until breakpoint",
})
else
buffer:draw_popup({err})
end
elseif key == "SHIFT_F4" then
local ok, err = mobdebug.command("step")
if err then
buffer:draw_popup({err})
end
elseif key == "F4" then
local ok, err = mobdebug.command("over")
if err then
buffer:draw_popup({err})
end
elseif key == "F11" then
local ok, err = mobdebug.command("run")
if err then
buffer:draw_popup({err})
end
elseif key == "F6" then
local filename = buffer:filename()
local x, y = buffer:xy()
if mobdebug.is_breakpoint(filename, y) then
mobdebug.command("delb " .. filename .. " " .. y)
mobdebug.set_breakpoint(filename, y, nil)
else
mobdebug.command("setb " .. filename .. " " .. y)
mobdebug.set_breakpoint(filename, y, true)
end
buffer:go_to(1, y+1)
elseif key == "F9" then
code.pick_merge_conflict_branch()
end
end
function on_key(code)
local handled = false
local selection, startx, starty, stopx, stopy = buffer:selection()
if selection == "" then
if code == 13 then
local x, y = buffer:xy()
local line = buffer[y]
if line:sub(1, x - 1):match("^%s*$") and line:sub(x):match("^[^%s]") then
buffer:begin_undo()
buffer:emit("\n")
buffer:go_to(x, y, false)
buffer:end_undo()
handled = true
end
elseif code == 330 then
local x, y = buffer:xy()
local line = buffer[y]
local nextline = buffer[y+1]
if x == #line + 1 and line:match("^%s*$") and nextline:match("^"..line) then
buffer:begin_undo()
buffer:select(x, y, x, y + 1)
buffer:emit("\8")
buffer:end_undo()
handled = true
end
end
end
local tab_handled = false
if not handled and starty == stopy then
tab_handled = tab_complete.on_key(code)
end
return tab_handled or handled
end
function on_alt(key)
if key == 'L' then
local filename = buffer:filename()
local x, y = buffer:xy()
local page = tabs:open(filename:gsub("%.lua$", ".tl"))
tabs:set_page(page)
tabs:get_buffer(page):go_to(x, y)
end
end