· 6 years ago · Jun 15, 2019, 12:20 PM
1
2--
3-- Lua IDE
4-- Made by GravityScore
5-- Fixed by Justin8303
6--
7
8
9-- -------- Variables
10
11-- Version
12local version = "1.0"
13local args = {...}
14
15-- Editing
16local w, h = term.getSize()
17local tabWidth = 2
18
19local autosaveInterval = 20
20local allowEditorEvent = true
21local keyboardShortcutTimeout = 0.4
22
23-- Clipboard
24local clipboard = nil
25
26-- Theme
27local theme = {}
28
29-- Language
30local languages = {}
31local curLanguage = {}
32
33-- Events
34local event_distract = "luaide_distractionEvent"
35
36-- Locations
37local updateURL = "https://raw.github.com/GravityScore/LuaIDE/master/luaide.lua"
38local ideLocation = "/" .. shell.getRunningProgram()
39local themeLocation = "/.LuaIDE-Theme"
40local tempthemefile = "LuaIDE_temp_theme_file"
41
42local function isAdvanced() return term.isColor and term.isColor() end
43
44
45-- -------- Utilities
46
47local function modRead(properties)
48 local w, h = term.getSize()
49 local defaults = {replaceChar = nil, history = nil, visibleLength = nil, textLength = nil,
50 liveUpdates = nil, exitOnKey = nil}
51 if not properties then properties = {} end
52 for k, v in pairs(defaults) do if not properties[k] then properties[k] = v end end
53 if properties.replaceChar then properties.replaceChar = properties.replaceChar:sub(1, 1) end
54 if not properties.visibleLength then properties.visibleLength = w end
55
56 local sx, sy = term.getCursorPos()
57 local line = ""
58 local pos = 0
59 local historyPos = nil
60
61 local function redraw(repl)
62 local scroll = 0
63 if properties.visibleLength and sx + pos > properties.visibleLength + 1 then
64 scroll = (sx + pos) - (properties.visibleLength + 1)
65 end
66
67 term.setCursorPos(sx, sy)
68 local a = repl or properties.replaceChar
69 if a then term.write(string.rep(a, line:len() - scroll))
70 else term.write(line:sub(scroll + 1, -1)) end
71 term.setCursorPos(sx + pos - scroll, sy)
72 end
73
74 local function sendLiveUpdates(event, ...)
75 if type(properties.liveUpdates) == "function" then
76 local ox, oy = term.getCursorPos()
77 local a, data = properties.liveUpdates(line, event, ...)
78 if a == true and data == nil then
79 term.setCursorBlink(false)
80 return line
81 elseif a == true and data ~= nil then
82 term.setCursorBlink(false)
83 return data
84 end
85 term.setCursorPos(ox, oy)
86 end
87 end
88
89 term.setCursorBlink(true)
90 while true do
91 local e, but, x, y, p4, p5 = os.pullEvent()
92
93 if e == "char" then
94 local s = false
95 if properties.textLength and line:len() < properties.textLength then s = true
96 elseif not properties.textLength then s = true end
97
98 local canType = true
99 if not properties.grantPrint and properties.refusePrint then
100 local canTypeKeys = {}
101 if type(properties.refusePrint) == "table" then
102 for _, v in pairs(properties.refusePrint) do
103 table.insert(canTypeKeys, tostring(v):sub(1, 1))
104 end
105 elseif type(properties.refusePrint) == "string" then
106 for char in properties.refusePrint:gmatch(".") do
107 table.insert(canTypeKeys, char)
108 end
109 end
110 for _, v in pairs(canTypeKeys) do if but == v then canType = false end end
111 elseif properties.grantPrint then
112 canType = false
113 local canTypeKeys = {}
114 if type(properties.grantPrint) == "table" then
115 for _, v in pairs(properties.grantPrint) do
116 table.insert(canTypeKeys, tostring(v):sub(1, 1))
117 end
118 elseif type(properties.grantPrint) == "string" then
119 for char in properties.grantPrint:gmatch(".") do
120 table.insert(canTypeKeys, char)
121 end
122 end
123 for _, v in pairs(canTypeKeys) do if but == v then canType = true end end
124 end
125
126 if s and canType then
127 line = line:sub(1, pos) .. but .. line:sub(pos + 1, -1)
128 pos = pos + 1
129 redraw()
130 end
131 elseif e == "key" then
132 if but == keys.enter then break
133 elseif but == keys.left then if pos > 0 then pos = pos - 1 redraw() end
134 elseif but == keys.right then if pos < line:len() then pos = pos + 1 redraw() end
135 elseif (but == keys.up or but == keys.down) and properties.history then
136 redraw(" ")
137 if but == keys.up then
138 if historyPos == nil and #properties.history > 0 then
139 historyPos = #properties.history
140 elseif historyPos > 1 then
141 historyPos = historyPos - 1
142 end
143 elseif but == keys.down then
144 if historyPos == #properties.history then historyPos = nil
145 elseif historyPos ~= nil then historyPos = historyPos + 1 end
146 end
147
148 if properties.history and historyPos then
149 line = properties.history[historyPos]
150 pos = line:len()
151 else
152 line = ""
153 pos = 0
154 end
155
156 redraw()
157 local a = sendLiveUpdates("history")
158 if a then return a end
159 elseif but == keys.backspace and pos > 0 then
160 redraw(" ")
161 line = line:sub(1, pos - 1) .. line:sub(pos + 1, -1)
162 pos = pos - 1
163 redraw()
164 local a = sendLiveUpdates("delete")
165 if a then return a end
166 elseif but == keys.home then
167 pos = 0
168 redraw()
169 elseif but == keys.delete and pos < line:len() then
170 redraw(" ")
171 line = line:sub(1, pos) .. line:sub(pos + 2, -1)
172 redraw()
173 local a = sendLiveUpdates("delete")
174 if a then return a end
175 elseif but == keys["end"] then
176 pos = line:len()
177 redraw()
178 elseif properties.exitOnKey then
179 if but == properties.exitOnKey or (properties.exitOnKey == "control" and
180 (but == 29 or but == 157)) then
181 term.setCursorBlink(false)
182 return nil
183 end
184 end
185 end
186 local a = sendLiveUpdates(e, but, x, y, p4, p5)
187 if a then return a end
188 end
189
190 term.setCursorBlink(false)
191 if line ~= nil then line = line:gsub("^%s*(.-)%s*$", "%1") end
192 return line
193end
194
195
196-- -------- Themes
197
198local defaultTheme = {
199 background = "gray",
200 backgroundHighlight = "lightGray",
201 prompt = "cyan",
202 promptHighlight = "lightBlue",
203 err = "red",
204 errHighlight = "pink",
205
206 editorBackground = "gray",
207 editorLineHightlight = "lightBlue",
208 editorLineNumbers = "gray",
209 editorLineNumbersHighlight = "lightGray",
210 editorError = "pink",
211 editorErrorHighlight = "red",
212
213 textColor = "white",
214 conditional = "yellow",
215 constant = "orange",
216 ["function"] = "magenta",
217 string = "red",
218 comment = "lime"
219}
220
221local normalTheme = {
222 background = "black",
223 backgroundHighlight = "black",
224 prompt = "black",
225 promptHighlight = "black",
226 err = "black",
227 errHighlight = "black",
228
229 editorBackground = "black",
230 editorLineHightlight = "black",
231 editorLineNumbers = "black",
232 editorLineNumbersHighlight = "white",
233 editorError = "black",
234 editorErrorHighlight = "black",
235
236 textColor = "white",
237 conditional = "white",
238 constant = "white",
239 ["function"] = "white",
240 string = "white",
241 comment = "white"
242}
243
244local availableThemes = {
245 {"Water (Default)", "https://raw.githubusercontent.com/Justin8303/BetterCC/master/themes/default.txt"}
246}
247
248local function loadTheme(path)
249 local f = fs.open(path,"r")
250 local l = f.readLine()
251 local config = {}
252 while l ~= nil do
253 local k = l:match("%a+%s*="):gsub("%s*=","")
254 local v = l:match("=%s*%a+"):gsub("=%s*","")
255 if k and v then config[k] = v end
256 l = f.readLine()
257 end
258 f.close()
259 return config
260end
261
262-- Load Theme
263if isAdvanced() then theme = defaultTheme
264else theme = normalTheme end
265
266
267-- -------- Drawing
268
269local function centerPrint(text, ny)
270 if type(text) == "table" then for _, v in pairs(text) do centerPrint(v) end
271 else
272 local x, y = term.getCursorPos()
273 local w, h = term.getSize()
274 term.setCursorPos(w/2 - text:len()/2 + (#text % 2 == 0 and 1 or 0), ny or y)
275 print(text)
276 end
277end
278
279local function title(t)
280 term.setTextColor(colors[theme.textColor])
281 term.setBackgroundColor(colors[theme.background])
282 term.clear()
283
284 term.setBackgroundColor(colors[theme.backgroundHighlight])
285 for i = 2, 4 do term.setCursorPos(1, i) term.clearLine() end
286 term.setCursorPos(3, 3)
287 term.write(t)
288end
289
290local function centerRead(wid, begt)
291 local function liveUpdate(line, e, but, x, y, p4, p5)
292 if isAdvanced() and e == "mouse_click" and x >= w/2 - wid/2 and x <= w/2 - wid/2 + 10
293 and y >= 13 and y <= 15 then
294 return true, ""
295 end
296 end
297
298 if not begt then begt = "" end
299 term.setTextColor(colors[theme.textColor])
300 term.setBackgroundColor(colors[theme.promptHighlight])
301 for i = 8, 10 do
302 term.setCursorPos(w/2 - wid/2, i)
303 term.write(string.rep(" ", wid))
304 end
305
306 if isAdvanced() then
307 term.setBackgroundColor(colors[theme.errHighlight])
308 for i = 13, 15 do
309 term.setCursorPos(w/2 - wid/2 + 1, i)
310 term.write(string.rep(" ", 10))
311 end
312 term.setCursorPos(w/2 - wid/2 + 2, 14)
313 term.write("> Cancel")
314 end
315
316 term.setBackgroundColor(colors[theme.promptHighlight])
317 term.setCursorPos(w/2 - wid/2 + 1, 9)
318 term.write("> " .. begt)
319 return modRead({visibleLength = w/2 + wid/2, liveUpdates = liveUpdate})
320end
321
322
323-- -------- Prompt
324
325local function prompt(list, dir, isGrid)
326 local function draw(sel)
327 for i, v in ipairs(list) do
328 if i == sel then term.setBackgroundColor(v.highlight or colors[theme.promptHighlight])
329 else term.setBackgroundColor(v.bg or colors[theme.prompt]) end
330 term.setTextColor(v.tc or colors[theme.textColor])
331 for i = -1, 1 do
332 term.setCursorPos(v[2], v[3] + i)
333 term.write(string.rep(" ", v[1]:len() + 4))
334 end
335
336 term.setCursorPos(v[2], v[3])
337 if i == sel then
338 term.setBackgroundColor(v.highlight or colors[theme.promptHighlight])
339 term.write(" > ")
340 else term.write(" - ") end
341 term.write(v[1] .. " ")
342 end
343 end
344
345 local key1 = dir == "horizontal" and 203 or 200
346 local key2 = dir == "horizontal" and 205 or 208
347 local sel = 1
348 draw(sel)
349
350 while true do
351 local e, but, x, y = os.pullEvent()
352 if e == "key" and but == 28 then
353 return list[sel][1]
354 elseif e == "key" and but == key1 and sel > 1 then
355 sel = sel - 1
356 draw(sel)
357 elseif e == "key" and but == key2 and ((err == true and sel < #list - 1) or (sel < #list)) then
358 sel = sel + 1
359 draw(sel)
360 elseif isGrid and e == "key" and but == 203 and sel > 2 then
361 sel = sel - 2
362 draw(sel)
363 elseif isGrid and e == "key" and but == 205 and sel < 3 then
364 sel = sel + 2
365 draw(sel)
366 elseif e == "mouse_click" then
367 for i, v in ipairs(list) do
368 if x >= v[2] - 1 and x <= v[2] + v[1]:len() + 3 and y >= v[3] - 1 and y <= v[3] + 1 then
369 return list[i][1]
370 end
371 end
372 end
373 end
374end
375
376local function scrollingPrompt(list)
377 local function draw(items, sel, loc)
378 for i, v in ipairs(items) do
379 local bg = colors[theme.prompt]
380 local bghigh = colors[theme.promptHighlight]
381 if v:find("Back") or v:find("Return") then
382 bg = colors[theme.err]
383 bghigh = colors[theme.errHighlight]
384 end
385
386 if i == sel then term.setBackgroundColor(bghigh)
387 else term.setBackgroundColor(bg) end
388 term.setTextColor(colors[theme.textColor])
389 for x = -1, 1 do
390 term.setCursorPos(3, (i * 4) + x + 4)
391 term.write(string.rep(" ", w - 13))
392 end
393
394 term.setCursorPos(3, i * 4 + 4)
395 if i == sel then
396 term.setBackgroundColor(bghigh)
397 term.write(" > ")
398 else term.write(" - ") end
399 term.write(v .. " ")
400 end
401 end
402
403 local function updateDisplayList(items, loc, len)
404 local ret = {}
405 for i = 1, len do
406 local item = items[i + loc - 1]
407 if item then table.insert(ret, item) end
408 end
409 return ret
410 end
411
412 -- Variables
413 local sel = 1
414 local loc = 1
415 local len = 3
416 local disList = updateDisplayList(list, loc, len)
417 draw(disList, sel, loc)
418
419 -- Loop
420 while true do
421 local e, key, x, y = os.pullEvent()
422
423 if e == "mouse_click" then
424 for i, v in ipairs(disList) do
425 if x >= 3 and x <= w - 11 and y >= i * 4 + 3 and y <= i * 4 + 5 then return v end
426 end
427 elseif e == "key" and key == 200 then
428 if sel > 1 then
429 sel = sel - 1
430 draw(disList, sel, loc)
431 elseif loc > 1 then
432 loc = loc - 1
433 disList = updateDisplayList(list, loc, len)
434 draw(disList, sel, loc)
435 end
436 elseif e == "key" and key == 208 then
437 if sel < len then
438 sel = sel + 1
439 draw(disList, sel, loc)
440 elseif loc + len - 1 < #list then
441 loc = loc + 1
442 disList = updateDisplayList(list, loc, len)
443 draw(disList, sel, loc)
444 end
445 elseif e == "mouse_scroll" then
446 os.queueEvent("key", key == -1 and 200 or 208)
447 elseif e == "key" and key == 28 then
448 return disList[sel]
449 end
450 end
451end
452
453function monitorKeyboardShortcuts()
454 local ta, tb = nil, nil
455 local allowChar = false
456 local shiftPressed = false
457 while true do
458 local event, char = os.pullEvent()
459 if event == "key" and (char == 42 or char == 52) then
460 shiftPressed = true
461 tb = os.startTimer(keyboardShortcutTimeout)
462 elseif event == "key" and (char == 29 or char == 157 or char == 219 or char == 220) then
463 allowEditorEvent = false
464 allowChar = true
465 ta = os.startTimer(keyboardShortcutTimeout)
466 elseif event == "key" and allowChar then
467 local name = nil
468 for k, v in pairs(keys) do
469 if v == char then
470 if shiftPressed then os.queueEvent("shortcut", "ctrl shift", k:lower())
471 else os.queueEvent("shortcut", "ctrl", k:lower()) end
472 sleep(0.005)
473 allowEditorEvent = true
474 end
475 end
476 if shiftPressed then os.queueEvent("shortcut", "ctrl shift", char)
477 else os.queueEvent("shortcut", "ctrl", char) end
478 elseif event == "timer" and char == ta then
479 allowEditorEvent = true
480 allowChar = false
481 elseif event == "timer" and char == tb then
482 shiftPressed = false
483 end
484 end
485end
486
487
488-- -------- Saving and Loading
489
490local function download(url, path)
491 for i = 1, 3 do
492 local response = http.get(url)
493 if response then
494 local data = response.readAll()
495 response.close()
496 if path then
497 local f = io.open(path, "w")
498 f:write(data)
499 f:close()
500 end
501 return true
502 end
503 end
504
505 return false
506end
507
508local function saveFile(path, lines)
509 local dir = path:sub(1, path:len() - fs.getName(path):len())
510 if not fs.exists(dir) then fs.makeDir(dir) end
511 if not fs.isDir(path) and not fs.isReadOnly(path) then
512 local a = ""
513 for _, v in pairs(lines) do a = a .. v .. "\n" end
514
515 local f = io.open(path, "w")
516 f:write(a)
517 f:close()
518 return true
519 else return false end
520end
521
522local function loadFile(path)
523 if not fs.exists(path) then
524 local dir = path:sub(1, path:len() - fs.getName(path):len())
525 if not fs.exists(dir) then fs.makeDir(dir) end
526 local f = io.open(path, "w")
527 f:write("")
528 f:close()
529 end
530
531 local l = {}
532 if fs.exists(path) and not fs.isDir(path) then
533 local f = io.open(path, "r")
534 if f then
535 local a = f:read("*l")
536 while a do
537 table.insert(l, a)
538 a = f:read("*l")
539 end
540 f:close()
541 end
542 else return nil end
543
544 if #l < 1 then table.insert(l, "") end
545 return l
546end
547
548
549-- -------- Languages
550
551languages.lua = {}
552languages.brainfuck = {}
553languages.none = {}
554
555-- Lua
556
557languages.lua.helpTips = {
558 "A function you tried to call doesn't exist.",
559 "You made a typo.",
560 "The index of an array is nil.",
561 "The wrong variable type was passed.",
562 "A function/variable doesn't exist.",
563 "You missed an 'end'.",
564 "You missed a 'then'.",
565 "You declared a variable incorrectly.",
566 "One of your variables is mysteriously nil."
567}
568
569languages.lua.defaultHelpTips = {
570 2, 5
571}
572
573languages.lua.errors = {
574 ["Attempt to call nil."] = {1, 2},
575 ["Attempt to index nil."] = {3, 2},
576 [".+ expected, got .+"] = {4, 2, 9},
577 ["'end' expected"] = {6, 2},
578 ["'then' expected"] = {7, 2},
579 ["'=' expected"] = {8, 2}
580}
581
582languages.lua.keywords = {
583 ["and"] = "conditional",
584 ["break"] = "conditional",
585 ["do"] = "conditional",
586 ["else"] = "conditional",
587 ["elseif"] = "conditional",
588 ["end"] = "conditional",
589 ["for"] = "conditional",
590 ["function"] = "conditional",
591 ["if"] = "conditional",
592 ["in"] = "conditional",
593 ["local"] = "conditional",
594 ["not"] = "conditional",
595 ["or"] = "conditional",
596 ["repeat"] = "conditional",
597 ["return"] = "conditional",
598 ["then"] = "conditional",
599 ["until"] = "conditional",
600 ["while"] = "conditional",
601
602 ["true"] = "constant",
603 ["false"] = "constant",
604 ["nil"] = "constant",
605
606 ["print"] = "function",
607 ["write"] = "function",
608 ["sleep"] = "function",
609 ["pairs"] = "function",
610 ["ipairs"] = "function",
611 ["loadstring"] = "function",
612 ["loadfile"] = "function",
613 ["dofile"] = "function",
614 ["rawset"] = "function",
615 ["rawget"] = "function",
616 ["setfenv"] = "function",
617 ["getfenv"] = "function",
618}
619
620languages.lua.parseError = function(e)
621 local ret = {filename = "unknown", line = -1, display = "Unknown!", err = ""}
622 if e and e ~= "" then
623 ret.err = e
624 if e:find(":") then
625 ret.filename = e:sub(1, e:find(":") - 1):gsub("^%s*(.-)%s*$", "%1")
626 -- The "" is needed to circumvent a CC bug
627 e = (e:sub(e:find(":") + 1) .. ""):gsub("^%s*(.-)%s*$", "%1")
628 if e:find(":") then
629 ret.line = e:sub(1, e:find(":") - 1)
630 e = e:sub(e:find(":") + 2):gsub("^%s*(.-)%s*$", "%1") .. ""
631 end
632 end
633 ret.display = e:sub(1, 1):upper() .. e:sub(2, -1) .. "."
634 end
635
636 return ret
637end
638
639languages.lua.getCompilerErrors = function(code)
640 code = "local function ee65da6af1cb6f63fee9a081246f2fd92b36ef2(...)\n\n" .. code .. "\n\nend"
641 local fn, err = loadstring(code)
642 if not err then
643 local _, e = pcall(fn)
644 if e then err = e end
645 end
646
647 if err then
648 local a = err:find("]", 1, true)
649 if a then err = "string" .. err:sub(a + 1, -1) end
650 local ret = languages.lua.parseError(err)
651 if tonumber(ret.line) then ret.line = tonumber(ret.line) end
652 return ret
653 else return languages.lua.parseError(nil) end
654end
655
656languages.lua.run = function(path, ar)
657 local fn, err = loadfile(path)
658 setfenv(fn, getfenv())
659 if not err then
660 _, err = pcall(function() fn(unpack(ar)) end)
661 end
662 return err
663end
664
665
666-- Brainfuck
667
668languages.brainfuck.helpTips = {
669 "Well idk...",
670 "Isn't this the whole point of the language?",
671 "Ya know... Not being able to debug it?",
672 "You made a typo."
673}
674
675languages.brainfuck.defaultHelpTips = {
676 1, 2, 3
677}
678
679languages.brainfuck.errors = {
680 ["No matching '['"] = {1, 2, 3, 4}
681}
682
683languages.brainfuck.keywords = {}
684
685languages.brainfuck.parseError = function(e)
686 local ret = {filename = "unknown", line = -1, display = "Unknown!", err = ""}
687 if e and e ~= "" then
688 ret.err = e
689 ret.line = e:sub(1, e:find(":") - 1)
690 e = e:sub(e:find(":") + 2):gsub("^%s*(.-)%s*$", "%1") .. ""
691 ret.display = e:sub(1, 1):upper() .. e:sub(2, -1) .. "."
692 end
693
694 return ret
695end
696
697languages.brainfuck.mapLoops = function(code)
698 -- Map loops
699 local loopLocations = {}
700 local loc = 1
701 local line = 1
702 for let in string.gmatch(code, ".") do
703 if let == "[" then
704 loopLocations[loc] = true
705 elseif let == "]" then
706 local found = false
707 for i = loc, 1, -1 do
708 if loopLocations[i] == true then
709 loopLocations[i] = loc
710 found = true
711 end
712 end
713
714 if not found then
715 return line .. ": No matching '['"
716 end
717 end
718
719 if let == "\n" then line = line + 1 end
720 loc = loc + 1
721 end
722 return loopLocations
723end
724
725languages.brainfuck.getCompilerErrors = function(code)
726 local a = languages.brainfuck.mapLoops(code)
727 if type(a) == "string" then return languages.brainfuck.parseError(a)
728 else return languages.brainfuck.parseError(nil) end
729end
730
731languages.brainfuck.run = function(path)
732 -- Read from file
733 local f = io.open(path, "r")
734 local content = f:read("*a")
735 f:close()
736
737 -- Define environment
738 local dataCells = {}
739 local dataPointer = 1
740 local instructionPointer = 1
741
742 -- Map loops
743 local loopLocations = languages.brainfuck.mapLoops(content)
744 if type(loopLocations) == "string" then return loopLocations end
745
746 -- Execute code
747 while true do
748 local let = content:sub(instructionPointer, instructionPointer)
749
750 if let == ">" then
751 dataPointer = dataPointer + 1
752 if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
753 elseif let == "<" then
754 if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
755 dataPointer = dataPointer - 1
756 if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
757 elseif let == "+" then
758 if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
759 dataCells[tostring(dataPointer)] = dataCells[tostring(dataPointer)] + 1
760 elseif let == "-" then
761 if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
762 dataCells[tostring(dataPointer)] = dataCells[tostring(dataPointer)] - 1
763 elseif let == "." then
764 if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
765 if term.getCursorPos() >= w then print("") end
766 write(string.char(math.max(1, dataCells[tostring(dataPointer)])))
767 elseif let == "," then
768 if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
769 term.setCursorBlink(true)
770 local e, but = os.pullEvent("char")
771 term.setCursorBlink(false)
772 dataCells[tostring(dataPointer)] = string.byte(but)
773 if term.getCursorPos() >= w then print("") end
774 write(but)
775 elseif let == "/" then
776 if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
777 if term.getCursorPos() >= w then print("") end
778 write(dataCells[tostring(dataPointer)])
779 elseif let == "[" then
780 if dataCells[tostring(dataPointer)] == 0 then
781 for k, v in pairs(loopLocations) do
782 if k == instructionPointer then instructionPointer = v end
783 end
784 end
785 elseif let == "]" then
786 for k, v in pairs(loopLocations) do
787 if v == instructionPointer then instructionPointer = k - 1 end
788 end
789 end
790
791 instructionPointer = instructionPointer + 1
792 if instructionPointer > content:len() then print("") break end
793 end
794end
795
796-- None
797
798languages.none.helpTips = {}
799languages.none.defaultHelpTips = {}
800languages.none.errors = {}
801languages.none.keywords = {}
802
803languages.none.parseError = function(err)
804 return {filename = "", line = -1, display = "", err = ""}
805end
806
807languages.none.getCompilerErrors = function(code)
808 return languages.none.parseError(nil)
809end
810
811languages.none.run = function(path) end
812
813
814-- Load language
815curLanguage = languages.lua
816
817
818-- -------- Run GUI
819
820local function viewErrorHelp(e)
821 title("LuaIDE - Error Help")
822
823 local tips = nil
824 for k, v in pairs(curLanguage.errors) do
825 if e.display:find(k) then tips = v break end
826 end
827
828 term.setBackgroundColor(colors[theme.err])
829 for i = 6, 8 do
830 term.setCursorPos(5, i)
831 term.write(string.rep(" ", 35))
832 end
833
834 term.setBackgroundColor(colors[theme.prompt])
835 for i = 10, 18 do
836 term.setCursorPos(5, i)
837 term.write(string.rep(" ", 46))
838 end
839
840 if tips then
841 term.setBackgroundColor(colors[theme.err])
842 term.setCursorPos(6, 7)
843 term.write("Error Help")
844
845 term.setBackgroundColor(colors[theme.prompt])
846 for i, v in ipairs(tips) do
847 term.setCursorPos(7, i + 10)
848 term.write("- " .. curLanguage.helpTips[v])
849 end
850 else
851 term.setBackgroundColor(colors[theme.err])
852 term.setCursorPos(6, 7)
853 term.write("No Error Tips Available!")
854
855 term.setBackgroundColor(colors[theme.prompt])
856 term.setCursorPos(6, 11)
857 term.write("There are no error tips available, but")
858 term.setCursorPos(6, 12)
859 term.write("you could see if it was any of these:")
860
861 for i, v in ipairs(curLanguage.defaultHelpTips) do
862 term.setCursorPos(7, i + 12)
863 term.write("- " .. curLanguage.helpTips[v])
864 end
865 end
866
867 prompt({{"Back", w - 8, 7}}, "horizontal")
868end
869
870local function run(path, lines, useArgs)
871 local ar = {}
872 if useArgs then
873 title("LuaIDE - Run " .. fs.getName(path))
874 local s = centerRead(w - 13, fs.getName(path) .. " ")
875 for m in string.gmatch(s, "[^ \t]+") do ar[#ar + 1] = m:gsub("^%s*(.-)%s*$", "%1") end
876 end
877
878 saveFile(path, lines)
879 term.setCursorBlink(false)
880 term.setBackgroundColor(colors.black)
881 term.setTextColor(colors.white)
882 term.clear()
883 term.setCursorPos(1, 1)
884 local err = curLanguage.run(path, ar)
885
886 term.setBackgroundColor(colors.black)
887 print("\n")
888 if err then
889 if isAdvanced() then term.setTextColor(colors.red) end
890 centerPrint("The program has crashed!")
891 end
892 term.setTextColor(colors.white)
893 centerPrint("Press any key to return to LuaIDE...")
894 while true do
895 local e = os.pullEvent()
896 if e == "key" then break end
897 end
898
899 -- To prevent key from showing up in editor
900 os.queueEvent(event_distract)
901 os.pullEvent()
902
903 if err then
904 if curLanguage == languages.lua and err:find("]") then
905 err = fs.getName(path) .. err:sub(err:find("]", 1, true) + 1, -1)
906 end
907
908 while true do
909 title("LuaIDE - Error!")
910
911 term.setBackgroundColor(colors[theme.err])
912 for i = 6, 8 do
913 term.setCursorPos(3, i)
914 term.write(string.rep(" ", w - 5))
915 end
916 term.setCursorPos(4, 7)
917 term.write("The program has crashed!")
918
919 term.setBackgroundColor(colors[theme.prompt])
920 for i = 10, 14 do
921 term.setCursorPos(3, i)
922 term.write(string.rep(" ", w - 5))
923 end
924
925 local formattedErr = curLanguage.parseError(err)
926 term.setCursorPos(4, 11)
927 term.write("Line: " .. formattedErr.line)
928 term.setCursorPos(4, 12)
929 term.write("Error:")
930 term.setCursorPos(5, 13)
931
932 local a = formattedErr.display
933 local b = nil
934 if a:len() > w - 8 then
935 for i = a:len(), 1, -1 do
936 if a:sub(i, i) == " " then
937 b = a:sub(i + 1, -1)
938 a = a:sub(1, i)
939 break
940 end
941 end
942 end
943
944 term.write(a)
945 if b then
946 term.setCursorPos(5, 14)
947 term.write(b)
948 end
949
950 local opt = prompt({{"Error Help", w/2 - 15, 17}, {"Go To Line", w/2 + 2, 17}},
951 "horizontal")
952 if opt == "Error Help" then
953 viewErrorHelp(formattedErr)
954 elseif opt == "Go To Line" then
955 -- To prevent key from showing up in editor
956 os.queueEvent(event_distract)
957 os.pullEvent()
958
959 return "go to", tonumber(formattedErr.line)
960 end
961 end
962 end
963end
964
965
966-- -------- Functions
967
968local function goto()
969 term.setBackgroundColor(colors[theme.backgroundHighlight])
970 term.setCursorPos(2, 1)
971 term.clearLine()
972 term.write("Line: ")
973 local line = modRead({visibleLength = w - 2})
974
975 local num = tonumber(line)
976 if num and num > 0 then return num
977 else
978 term.setCursorPos(2, 1)
979 term.clearLine()
980 term.write("Not a line number!")
981 sleep(1.6)
982 return nil
983 end
984end
985
986local function setsyntax()
987 local opts = {
988 "[Lua] Brainfuck None ",
989 " Lua [Brainfuck] None ",
990 " Lua Brainfuck [None]"
991 }
992 local sel = 1
993
994 term.setCursorBlink(false)
995 term.setBackgroundColor(colors[theme.backgroundHighlight])
996 term.setCursorPos(2, 1)
997 term.clearLine()
998 term.write(opts[sel])
999 while true do
1000 local e, but, x, y = os.pullEvent("key")
1001 if but == 203 then
1002 sel = math.max(1, sel - 1)
1003 term.setCursorPos(2, 1)
1004 term.clearLine()
1005 term.write(opts[sel])
1006 elseif but == 205 then
1007 sel = math.min(#opts, sel + 1)
1008 term.setCursorPos(2, 1)
1009 term.clearLine()
1010 term.write(opts[sel])
1011 elseif but == 28 then
1012 if sel == 1 then curLanguage = languages.lua
1013 elseif sel == 2 then curLanguage = languages.brainfuck
1014 elseif sel == 3 then curLanguage = languages.none end
1015 term.setCursorBlink(true)
1016 return
1017 end
1018 end
1019end
1020
1021
1022-- -------- Re-Indenting
1023
1024local tabWidth = 2
1025
1026local comments = {}
1027local strings = {}
1028
1029local increment = {
1030 "if%s+.+%s+then%s*$",
1031 "for%s+.+%s+do%s*$",
1032 "while%s+.+%s+do%s*$",
1033 "repeat%s*$",
1034 "function%s+[a-zA-Z_0-9]\(.*\)%s*$"
1035}
1036
1037local decrement = {
1038 "end",
1039 "until%s+.+"
1040}
1041
1042local special = {
1043 "else%s*$",
1044 "elseif%s+.+%s+then%s*$"
1045}
1046
1047local function check(func)
1048 for _, v in pairs(func) do
1049 local cLineStart = v["lineStart"]
1050 local cLineEnd = v["lineEnd"]
1051 local cCharStart = v["charStart"]
1052 local cCharEnd = v["charEnd"]
1053
1054 if line >= cLineStart and line <= cLineEnd then
1055 if line == cLineStart then return cCharStart < charNumb
1056 elseif line == cLineEnd then return cCharEnd > charNumb
1057 else return true end
1058 end
1059 end
1060end
1061
1062local function isIn(line, loc)
1063 if check(comments) then return true end
1064 if check(strings) then return true end
1065 return false
1066end
1067
1068local function setComment(ls, le, cs, ce)
1069 comments[#comments + 1] = {}
1070 comments[#comments].lineStart = ls
1071 comments[#comments].lineEnd = le
1072 comments[#comments].charStart = cs
1073 comments[#comments].charEnd = ce
1074end
1075
1076local function setString(ls, le, cs, ce)
1077 strings[#strings + 1] = {}
1078 strings[#strings].lineStart = ls
1079 strings[#strings].lineEnd = le
1080 strings[#strings].charStart = cs
1081 strings[#strings].charEnd = ce
1082end
1083
1084local function map(contents)
1085 local inCom = false
1086 local inStr = false
1087
1088 for i = 1, #contents do
1089 if content[i]:find("%-%-%[%[") and not inStr and not inCom then
1090 local cStart = content[i]:find("%-%-%[%[")
1091 setComment(i, nil, cStart, nil)
1092 inCom = true
1093 elseif content[i]:find("%-%-%[=%[") and not inStr and not inCom then
1094 local cStart = content[i]:find("%-%-%[=%[")
1095 setComment(i, nil, cStart, nil)
1096 inCom = true
1097 elseif content[i]:find("%[%[") and not inStr and not inCom then
1098 local cStart = content[i]:find("%[%[")
1099 setString(i, nil, cStart, nil)
1100 inStr = true
1101 elseif content[i]:find("%[=%[") and not inStr and not inCom then
1102 local cStart = content[i]:find("%[=%[")
1103 setString(i, nil, cStart, nil)
1104 inStr = true
1105 end
1106
1107 if content[i]:find("%]%]") and inStr and not inCom then
1108 local cStart, cEnd = content[i]:find("%]%]")
1109 strings[#strings].lineEnd = i
1110 strings[#strings].charEnd = cEnd
1111 inStr = false
1112 elseif content[i]:find("%]=%]") and inStr and not inCom then
1113 local cStart, cEnd = content[i]:find("%]=%]")
1114 strings[#strings].lineEnd = i
1115 strings[#strings].charEnd = cEnd
1116 inStr = false
1117 end
1118
1119 if content[i]:find("%]%]") and not inStr and inCom then
1120 local cStart, cEnd = content[i]:find("%]%]")
1121 comments[#comments].lineEnd = i
1122 comments[#comments].charEnd = cEnd
1123 inCom = false
1124 elseif content[i]:find("%]=%]") and not inStr and inCom then
1125 local cStart, cEnd = content[i]:find("%]=%]")
1126 comments[#comments].lineEnd = i
1127 comments[#comments].charEnd = cEnd
1128 inCom = false
1129 end
1130
1131 if content[i]:find("%-%-") and not inStr and not inCom then
1132 local cStart = content[i]:find("%-%-")
1133 setComment(i, i, cStart, -1)
1134 elseif content[i]:find("'") and not inStr and not inCom then
1135 local cStart, cEnd = content[i]:find("'")
1136 local nextChar = content[i]:sub(cEnd + 1, string.len(content[i]))
1137 local _, cEnd = nextChar:find("'")
1138 setString(i, i, cStart, cEnd)
1139 elseif content[i]:find('"') and not inStr and not inCom then
1140 local cStart, cEnd = content[i]:find('"')
1141 local nextChar = content[i]:sub(cEnd + 1, string.len(content[i]))
1142 local _, cEnd = nextChar:find('"')
1143 setString(i, i, cStart, cEnd)
1144 end
1145 end
1146end
1147
1148local function reindent(contents)
1149 local err = nil
1150 if curLanguage ~= languages.lua then
1151 err = "Cannot indent languages other than Lua!"
1152 elseif curLanguage.getCompilerErrors(table.concat(contents, "\n")).line ~= -1 then
1153 err = "Cannot indent a program with errors!"
1154 end
1155
1156 if err then
1157 term.setCursorBlink(false)
1158 term.setCursorPos(2, 1)
1159 term.setBackgroundColor(colors[theme.backgroundHighlight])
1160 term.clearLine()
1161 term.write(err)
1162 sleep(1.6)
1163 return contents
1164 end
1165
1166 local new = {}
1167 local level = 0
1168 for k, v in pairs(contents) do
1169 local incrLevel = false
1170 local foundIncr = false
1171 for _, incr in pairs(increment) do
1172 if v:find(incr) and not isIn(k, v:find(incr)) then
1173 incrLevel = true
1174 end
1175 if v:find(incr:sub(1, -2)) and not isIn(k, v:find(incr)) then
1176 foundIncr = true
1177 end
1178 end
1179
1180 local decrLevel = false
1181 if not incrLevel then
1182 for _, decr in pairs(decrement) do
1183 if v:find(decr) and not isIn(k, v:find(decr)) and not foundIncr then
1184 level = math.max(0, level - 1)
1185 decrLevel = true
1186 end
1187 end
1188 end
1189
1190 if not decrLevel then
1191 for _, sp in pairs(special) do
1192 if v:find(sp) and not isIn(k, v:find(sp)) then
1193 incrLevel = true
1194 level = math.max(0, level - 1)
1195 end
1196 end
1197 end
1198
1199 new[k] = string.rep(" ", level * tabWidth) .. v
1200 if incrLevel then level = level + 1 end
1201 end
1202
1203 return new
1204end
1205
1206
1207-- -------- Menu
1208
1209local menu = {
1210 [1] = {"File",
1211-- "About",
1212-- "Settings",
1213-- "",
1214 "New File ^+N",
1215 "Open File ^+O",
1216 "Save File ^+S",
1217 "Close ^+W",
1218 "Print ^+P",
1219 "Quit ^+Q"
1220 }, [2] = {"Edit",
1221 "Cut Line ^+X",
1222 "Copy Line ^+C",
1223 "Paste Line ^+V",
1224 "Delete Line",
1225 "Clear Line"
1226 }, [3] = {"Functions",
1227 "Go To Line ^+G",
1228 "Re-Indent ^+I",
1229 "Set Syntax ^+E",
1230 "Start of Line ^+<",
1231 "End of Line ^+>"
1232 }, [4] = {"Run",
1233 "Run Program ^+R",
1234 "Run w/ Args ^+Shift+R"
1235 }
1236}
1237
1238local shortcuts = {
1239 -- File
1240 ["ctrl n"] = "New File ^+N",
1241 ["ctrl o"] = "Open File ^+O",
1242 ["ctrl s"] = "Save File ^+S",
1243 ["ctrl w"] = "Close ^+W",
1244 ["ctrl p"] = "Print ^+P",
1245 ["ctrl q"] = "Quit ^+Q",
1246
1247 -- Edit
1248 ["ctrl x"] = "Cut Line ^+X",
1249 ["ctrl c"] = "Copy Line ^+C",
1250 ["ctrl v"] = "Paste Line ^+V",
1251
1252 -- Functions
1253 ["ctrl g"] = "Go To Line ^+G",
1254 ["ctrl i"] = "Re-Indent ^+I",
1255 ["ctrl e"] = "Set Syntax ^+E",
1256 ["ctrl 203"] = "Start of Line ^+<",
1257 ["ctrl 205"] = "End of Line ^+>",
1258
1259 -- Run
1260 ["ctrl r"] = "Run Program ^+R",
1261 ["ctrl shift r"] = "Run w/ Args ^+Shift+R"
1262}
1263
1264local menuFunctions = {
1265 -- File
1266-- ["About"] = function() end,
1267-- ["Settings"] = function() end,
1268 ["New File ^+N"] = function(path, lines) saveFile(path, lines) return "new" end,
1269 ["Open File ^+O"] = function(path, lines) saveFile(path, lines) return "open" end,
1270 ["Save File ^+S"] = function(path, lines) saveFile(path, lines) end,
1271 ["Close ^+W"] = function(path, lines) saveFile(path, lines) return "menu" end,
1272 ["Print ^+P"] = function(path, lines) saveFile(path, lines) return nil end,
1273 ["Quit ^+Q"] = function(path, lines) saveFile(path, lines) return "exit" end,
1274
1275 -- Edit
1276 ["Cut Line ^+X"] = function(path, lines, y)
1277 clipboard = lines[y] table.remove(lines, y) return nil, lines end,
1278 ["Copy Line ^+C"] = function(path, lines, y) clipboard = lines[y] end,
1279 ["Paste Line ^+V"] = function(path, lines, y)
1280 if clipboard then table.insert(lines, y, clipboard) end return nil, lines end,
1281 ["Delete Line"] = function(path, lines, y) table.remove(lines, y) return nil, lines end,
1282 ["Clear Line"] = function(path, lines, y) lines[y] = "" return nil, lines, "cursor" end,
1283
1284 -- Functions
1285 ["Go To Line ^+G"] = function() return nil, "go to", goto() end,
1286 ["Re-Indent ^+I"] = function(path, lines)
1287 local a = reindent(lines) saveFile(path, lines) return nil, a
1288 end,
1289 ["Set Syntax ^+E"] = function(path, lines)
1290 setsyntax()
1291 if curLanguage == languages.brainfuck and lines[1] ~= "-- Syntax: Brainfuck" then
1292 table.insert(lines, 1, "-- Syntax: Brainfuck")
1293 return nil, lines
1294 end
1295 end,
1296 ["Start of Line ^+<"] = function() os.queueEvent("key", 199) end,
1297 ["End of Line ^+>"] = function() os.queueEvent("key", 207) end,
1298
1299 -- Run
1300 ["Run Program ^+R"] = function(path, lines)
1301 saveFile(path, lines)
1302 return nil, run(path, lines, false)
1303 end,
1304 ["Run w/ Args ^+Shift+R"] = function(path, lines)
1305 saveFile(path, lines)
1306 return nil, run(path, lines, true)
1307 end,
1308}
1309
1310local function drawMenu(open)
1311 term.setCursorPos(1, 1)
1312 term.setTextColor(colors[theme.textColor])
1313 term.setBackgroundColor(colors[theme.backgroundHighlight])
1314 term.clearLine()
1315 local curX = 0
1316 for _, v in pairs(menu) do
1317 term.setCursorPos(3 + curX, 1)
1318 term.write(v[1])
1319 curX = curX + v[1]:len() + 3
1320 end
1321
1322 if open then
1323 local it = {}
1324 local x = 1
1325 for _, v in pairs(menu) do
1326 if open == v[1] then
1327 it = v
1328 break
1329 end
1330 x = x + v[1]:len() + 3
1331 end
1332 x = x + 1
1333
1334 local items = {}
1335 for i = 2, #it do
1336 table.insert(items, it[i])
1337 end
1338
1339 local len = 1
1340 for _, v in pairs(items) do if v:len() + 2 > len then len = v:len() + 2 end end
1341
1342 for i, v in ipairs(items) do
1343 term.setCursorPos(x, i + 1)
1344 term.write(string.rep(" ", len))
1345 term.setCursorPos(x + 1, i + 1)
1346 term.write(v)
1347 end
1348 term.setCursorPos(x, #items + 2)
1349 term.write(string.rep(" ", len))
1350 return items, len
1351 end
1352end
1353
1354local function triggerMenu(cx, cy)
1355 -- Determine clicked menu
1356 local curX = 0
1357 local open = nil
1358 for _, v in pairs(menu) do
1359 if cx >= curX + 3 and cx <= curX + v[1]:len() + 2 then
1360 open = v[1]
1361 break
1362 end
1363 curX = curX + v[1]:len() + 3
1364 end
1365 local menux = curX + 2
1366 if not open then return false end
1367
1368 -- Flash menu item
1369 term.setCursorBlink(false)
1370 term.setCursorPos(menux, 1)
1371 term.setBackgroundColor(colors[theme.background])
1372 term.write(string.rep(" ", open:len() + 2))
1373 term.setCursorPos(menux + 1, 1)
1374 term.write(open)
1375 sleep(0.1)
1376 local items, len = drawMenu(open)
1377
1378 local ret = true
1379
1380 -- Pull events on menu
1381 local ox, oy = term.getCursorPos()
1382 while type(ret) ~= "string" do
1383 local e, but, x, y = os.pullEvent()
1384 if e == "mouse_click" then
1385 -- If clicked outside menu
1386 if x < menux - 1 or x > menux + len - 1 then break
1387 elseif y > #items + 2 then break
1388 elseif y == 1 then break end
1389
1390 for i, v in ipairs(items) do
1391 if y == i + 1 and x >= menux and x <= menux + len - 2 then
1392 -- Flash when clicked
1393 term.setCursorPos(menux, y)
1394 term.setBackgroundColor(colors[theme.background])
1395 term.write(string.rep(" ", len))
1396 term.setCursorPos(menux + 1, y)
1397 term.write(v)
1398 sleep(0.1)
1399 drawMenu(open)
1400
1401 -- Return item
1402 ret = v
1403 break
1404 end
1405 end
1406 end
1407 end
1408
1409 term.setCursorPos(ox, oy)
1410 term.setCursorBlink(true)
1411 return ret
1412end
1413
1414
1415-- -------- Editing
1416
1417local standardsCompletions = {
1418 "if%s+.+%s+then%s*$",
1419 "for%s+.+%s+do%s*$",
1420 "while%s+.+%s+do%s*$",
1421 "repeat%s*$",
1422 "function%s+[a-zA-Z_0-9]?\(.*\)%s*$",
1423 "=%s*function%s*\(.*\)%s*$",
1424 "else%s*$",
1425 "elseif%s+.+%s+then%s*$"
1426}
1427
1428local liveCompletions = {
1429 ["("] = ")",
1430 ["{"] = "}",
1431 ["["] = "]",
1432 ["\""] = "\"",
1433 ["'"] = "'",
1434}
1435
1436local x, y = 0, 0
1437local edw, edh = 0, h - 1
1438local offx, offy = 0, 1
1439local scrollx, scrolly = 0, 0
1440local lines = {}
1441local liveErr = curLanguage.parseError(nil)
1442local displayCode = true
1443local lastEventClock = os.clock()
1444
1445local function attemptToHighlight(line, regex, col)
1446 local match = string.match(line, regex)
1447 if match then
1448 if type(col) == "number" then term.setTextColor(col)
1449 elseif type(col) == "function" then term.setTextColor(col(match)) end
1450 term.write(match)
1451 term.setTextColor(colors[theme.textColor])
1452 return line:sub(match:len() + 1, -1)
1453 end
1454 return nil
1455end
1456
1457local function writeHighlighted(line)
1458 if curLanguage == languages.lua then
1459 while line:len() > 0 do
1460 line = attemptToHighlight(line, "^%-%-%[%[.-%]%]", colors[theme.comment]) or
1461 attemptToHighlight(line, "^%-%-.*", colors[theme.comment]) or
1462 attemptToHighlight(line, "^\".*[^\\]\"", colors[theme.string]) or
1463 attemptToHighlight(line, "^\'.*[^\\]\'", colors[theme.string]) or
1464 attemptToHighlight(line, "^%[%[.-%]%]", colors[theme.string]) or
1465 attemptToHighlight(line, "^[%w_]+", function(match)
1466 if curLanguage.keywords[match] then
1467 return colors[theme[curLanguage.keywords[match]]]
1468 end
1469 return colors[theme.textColor]
1470 end) or
1471 attemptToHighlight(line, "^[^%w_]", colors[theme.textColor])
1472 end
1473 else term.write(line) end
1474end
1475
1476local function draw()
1477 -- Menu
1478 term.setTextColor(colors[theme.textColor])
1479 term.setBackgroundColor(colors[theme.editorBackground])
1480 term.clear()
1481 drawMenu()
1482
1483 -- Line numbers
1484 offx, offy = tostring(#lines):len() + 1, 1
1485 edw, edh = w - offx, h - 1
1486
1487 -- Draw text
1488 for i = 1, edh do
1489 local a = lines[scrolly + i]
1490 if a then
1491 local ln = string.rep(" ", offx - 1 - tostring(scrolly + i):len()) .. tostring(scrolly + i)
1492 local l = a:sub(scrollx + 1, edw + scrollx + 1)
1493 ln = ln .. ":"
1494
1495 if liveErr.line == scrolly + i then ln = string.rep(" ", offx - 2) .. "!:" end
1496
1497 term.setCursorPos(1, i + offy)
1498 term.setBackgroundColor(colors[theme.editorBackground])
1499 if scrolly + i == y then
1500 if scrolly + i == liveErr.line and os.clock() - lastEventClock > 3 then
1501 term.setBackgroundColor(colors[theme.editorErrorHighlight])
1502 else term.setBackgroundColor(colors[theme.editorLineHightlight]) end
1503 term.clearLine()
1504 elseif scrolly + i == liveErr.line then
1505 term.setBackgroundColor(colors[theme.editorError])
1506 term.clearLine()
1507 end
1508
1509 term.setCursorPos(1 - scrollx + offx, i + offy)
1510 if scrolly + i == y then
1511 if scrolly + i == liveErr.line and os.clock() - lastEventClock > 3 then
1512 term.setBackgroundColor(colors[theme.editorErrorHighlight])
1513 else term.setBackgroundColor(colors[theme.editorLineHightlight]) end
1514 elseif scrolly + i == liveErr.line then term.setBackgroundColor(colors[theme.editorError])
1515 else term.setBackgroundColor(colors[theme.editorBackground]) end
1516 if scrolly + i == liveErr.line then
1517 if displayCode then term.write(a)
1518 else term.write(liveErr.display) end
1519 else writeHighlighted(a) end
1520
1521 term.setCursorPos(1, i + offy)
1522 if scrolly + i == y then
1523 if scrolly + i == liveErr.line and os.clock() - lastEventClock > 3 then
1524 term.setBackgroundColor(colors[theme.editorError])
1525 else term.setBackgroundColor(colors[theme.editorLineNumbersHighlight]) end
1526 elseif scrolly + i == liveErr.line then
1527 term.setBackgroundColor(colors[theme.editorErrorHighlight])
1528 else term.setBackgroundColor(colors[theme.editorLineNumbers]) end
1529 term.write(ln)
1530 end
1531 end
1532 term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
1533end
1534
1535local function drawLine(...)
1536 local ls = {...}
1537 offx = tostring(#lines):len() + 1
1538 for _, ly in pairs(ls) do
1539 local a = lines[ly]
1540 if a then
1541 local ln = string.rep(" ", offx - 1 - tostring(ly):len()) .. tostring(ly)
1542 local l = a:sub(scrollx + 1, edw + scrollx + 1)
1543 ln = ln .. ":"
1544
1545 if liveErr.line == ly then ln = string.rep(" ", offx - 2) .. "!:" end
1546
1547 term.setCursorPos(1, (ly - scrolly) + offy)
1548 term.setBackgroundColor(colors[theme.editorBackground])
1549 if ly == y then
1550 if ly == liveErr.line and os.clock() - lastEventClock > 3 then
1551 term.setBackgroundColor(colors[theme.editorErrorHighlight])
1552 else term.setBackgroundColor(colors[theme.editorLineHightlight]) end
1553 elseif ly == liveErr.line then
1554 term.setBackgroundColor(colors[theme.editorError])
1555 end
1556 term.clearLine()
1557
1558 term.setCursorPos(1 - scrollx + offx, (ly - scrolly) + offy)
1559 if ly == y then
1560 if ly == liveErr.line and os.clock() - lastEventClock > 3 then
1561 term.setBackgroundColor(colors[theme.editorErrorHighlight])
1562 else term.setBackgroundColor(colors[theme.editorLineHightlight]) end
1563 elseif ly == liveErr.line then term.setBackgroundColor(colors[theme.editorError])
1564 else term.setBackgroundColor(colors[theme.editorBackground]) end
1565 if ly == liveErr.line then
1566 if displayCode then term.write(a)
1567 else term.write(liveErr.display) end
1568 else writeHighlighted(a) end
1569
1570 term.setCursorPos(1, (ly - scrolly) + offy)
1571 if ly == y then
1572 if ly == liveErr.line and os.clock() - lastEventClock > 3 then
1573 term.setBackgroundColor(colors[theme.editorError])
1574 else term.setBackgroundColor(colors[theme.editorLineNumbersHighlight]) end
1575 elseif ly == liveErr.line then
1576 term.setBackgroundColor(colors[theme.editorErrorHighlight])
1577 else term.setBackgroundColor(colors[theme.editorLineNumbers]) end
1578 term.write(ln)
1579 end
1580 end
1581 term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
1582end
1583
1584local function cursorLoc(x, y, force)
1585 local sx, sy = x - scrollx, y - scrolly
1586 local redraw = false
1587 if sx < 1 then
1588 scrollx = x - 1
1589 sx = 1
1590 redraw = true
1591 elseif sx > edw then
1592 scrollx = x - edw
1593 sx = edw
1594 redraw = true
1595 end if sy < 1 then
1596 scrolly = y - 1
1597 sy = 1
1598 redraw = true
1599 elseif sy > edh then
1600 scrolly = y - edh
1601 sy = edh
1602 redraw = true
1603 end if redraw or force then draw() end
1604 term.setCursorPos(sx + offx, sy + offy)
1605end
1606
1607local function executeMenuItem(a, path)
1608 if type(a) == "string" and menuFunctions[a] then
1609 local opt, nl, gtln = menuFunctions[a](path, lines, y)
1610 if type(opt) == "string" then term.setCursorBlink(false) return opt end
1611 if type(nl) == "table" then
1612 if #lines < 1 then table.insert(lines, "") end
1613 y = math.min(y, #lines)
1614 x = math.min(x, lines[y]:len() + 1)
1615 lines = nl
1616 elseif type(nl) == "string" then
1617 if nl == "go to" and gtln then
1618 x, y = 1, math.min(#lines, gtln)
1619 cursorLoc(x, y)
1620 end
1621 end
1622 end
1623 term.setCursorBlink(true)
1624 draw()
1625 term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
1626end
1627
1628local function edit(path)
1629 -- Variables
1630 x, y = 1, 1
1631 offx, offy = 0, 1
1632 scrollx, scrolly = 0, 0
1633 lines = loadFile(path)
1634 if not lines then return "menu" end
1635
1636 -- Enable brainfuck
1637 if lines[1] == "-- Syntax: Brainfuck" then
1638 curLanguage = languages.brainfuck
1639 end
1640
1641 -- Clocks
1642 local autosaveClock = os.clock()
1643 local scrollClock = os.clock() -- To prevent redraw flicker
1644 local liveErrorClock = os.clock()
1645 local hasScrolled = false
1646
1647 -- Draw
1648 draw()
1649 term.setCursorPos(x + offx, y + offy)
1650 term.setCursorBlink(true)
1651
1652 -- Main loop
1653 local tid = os.startTimer(3)
1654 while true do
1655 local e, key, cx, cy = os.pullEvent()
1656 if e == "key" and allowEditorEvent then
1657 if key == 200 and y > 1 then
1658 -- Up
1659 x, y = math.min(x, lines[y - 1]:len() + 1), y - 1
1660 drawLine(y, y + 1)
1661 cursorLoc(x, y)
1662 elseif key == 208 and y < #lines then
1663 -- Down
1664 x, y = math.min(x, lines[y + 1]:len() + 1), y + 1
1665 drawLine(y, y - 1)
1666 cursorLoc(x, y)
1667 elseif key == 203 and x > 1 then
1668 -- Left
1669 x = x - 1
1670 local force = false
1671 if y - scrolly + offy < offy + 1 then force = true end
1672 cursorLoc(x, y, force)
1673 elseif key == 205 and x < lines[y]:len() + 1 then
1674 -- Right
1675 x = x + 1
1676 local force = false
1677 if y - scrolly + offy < offy + 1 then force = true end
1678 cursorLoc(x, y, force)
1679 elseif (key == 28 or key == 156) and (displayCode and true or y + scrolly - 1 ==
1680 liveErr.line) then
1681 -- Enter
1682 local f = nil
1683 for _, v in pairs(standardsCompletions) do
1684 if lines[y]:find(v) then f = v end
1685 end
1686
1687 local _, spaces = lines[y]:find("^[ ]+")
1688 if not spaces then spaces = 0 end
1689 if f then
1690 table.insert(lines, y + 1, string.rep(" ", spaces + 2))
1691 if not f:find("else", 1, true) and not f:find("elseif", 1, true) then
1692 table.insert(lines, y + 2, string.rep(" ", spaces) ..
1693 (f:find("repeat", 1, true) and "until " or f:find("{", 1, true) and "}" or
1694 "end"))
1695 end
1696 x, y = spaces + 3, y + 1
1697 cursorLoc(x, y, true)
1698 else
1699 local oldLine = lines[y]
1700
1701 lines[y] = lines[y]:sub(1, x - 1)
1702 table.insert(lines, y + 1, string.rep(" ", spaces) .. oldLine:sub(x, -1))
1703
1704 x, y = spaces + 1, y + 1
1705 cursorLoc(x, y, true)
1706 end
1707 elseif key == 14 and (displayCode and true or y + scrolly - 1 == liveErr.line) then
1708 -- Backspace
1709 if x > 1 then
1710 local f = false
1711 for k, v in pairs(liveCompletions) do
1712 if lines[y]:sub(x - 1, x - 1) == k then f = true end
1713 end
1714
1715 lines[y] = lines[y]:sub(1, x - 2) .. lines[y]:sub(x + (f and 1 or 0), -1)
1716 drawLine(y)
1717 x = x - 1
1718 cursorLoc(x, y)
1719 elseif y > 1 then
1720 local prevLen = lines[y - 1]:len() + 1
1721 lines[y - 1] = lines[y - 1] .. lines[y]
1722 table.remove(lines, y)
1723 x, y = prevLen, y - 1
1724 cursorLoc(x, y, true)
1725 end
1726 elseif key == 199 then
1727 -- Home
1728 x = 1
1729 local force = false
1730 if y - scrolly + offy < offy + 1 then force = true end
1731 cursorLoc(x, y, force)
1732 elseif key == 207 then
1733 -- End
1734 x = lines[y]:len() + 1
1735 local force = false
1736 if y - scrolly + offy < offy + 1 then force = true end
1737 cursorLoc(x, y, force)
1738 elseif key == 211 and (displayCode and true or y + scrolly - 1 == liveErr.line) then
1739 -- Forward Delete
1740 if x < lines[y]:len() + 1 then
1741 lines[y] = lines[y]:sub(1, x - 1) .. lines[y]:sub(x + 1)
1742 local force = false
1743 if y - scrolly + offy < offy + 1 then force = true end
1744 drawLine(y)
1745 cursorLoc(x, y, force)
1746 elseif y < #lines then
1747 lines[y] = lines[y] .. lines[y + 1]
1748 table.remove(lines, y + 1)
1749 draw()
1750 cursorLoc(x, y)
1751 end
1752 elseif key == 15 and (displayCode and true or y + scrolly - 1 == liveErr.line) then
1753 -- Tab
1754 lines[y] = string.rep(" ", tabWidth) .. lines[y]
1755 x = x + 2
1756 local force = false
1757 if y - scrolly + offy < offy + 1 then force = true end
1758 drawLine(y)
1759 cursorLoc(x, y, force)
1760 elseif key == 201 then
1761 -- Page up
1762 y = math.min(math.max(y - edh, 1), #lines)
1763 x = math.min(lines[y]:len() + 1, x)
1764 cursorLoc(x, y, true)
1765 elseif key == 209 then
1766 -- Page down
1767 y = math.min(math.max(y + edh, 1), #lines)
1768 x = math.min(lines[y]:len() + 1, x)
1769 cursorLoc(x, y, true)
1770 end
1771 elseif e == "char" and allowEditorEvent and (displayCode and true or
1772 y + scrolly - 1 == liveErr.line) then
1773 local shouldIgnore = false
1774 for k, v in pairs(liveCompletions) do
1775 if key == v and lines[y]:find(k, 1, true) and lines[y]:sub(x, x) == v then
1776 shouldIgnore = true
1777 end
1778 end
1779
1780 local addOne = false
1781 if not shouldIgnore then
1782 for k, v in pairs(liveCompletions) do
1783 if key == k and lines[y]:sub(x, x) ~= k then key = key .. v addOne = true end
1784 end
1785 lines[y] = lines[y]:sub(1, x - 1) .. key .. lines[y]:sub(x, -1)
1786 end
1787
1788 x = x + (addOne and 1 or key:len())
1789 local force = false
1790 if y - scrolly + offy < offy + 1 then force = true end
1791 drawLine(y)
1792 cursorLoc(x, y, force)
1793 elseif e == "mouse_click" and key == 1 then
1794 if cy > 1 then
1795 if cx <= offx and cy - offy == liveErr.line - scrolly then
1796 displayCode = not displayCode
1797 drawLine(liveErr.line)
1798 else
1799 local oldy = y
1800 y = math.min(math.max(scrolly + cy - offy, 1), #lines)
1801 x = math.min(math.max(scrollx + cx - offx, 1), lines[y]:len() + 1)
1802 if oldy ~= y then drawLine(oldy, y) end
1803 cursorLoc(x, y)
1804 end
1805 else
1806 local a = triggerMenu(cx, cy)
1807 if a then
1808 local opt = executeMenuItem(a, path)
1809 if opt then return opt end
1810 end
1811 end
1812 elseif e == "shortcut" then
1813 local a = shortcuts[key .. " " .. cx]
1814 if a then
1815 local parent = nil
1816 local curx = 0
1817 for i, mv in ipairs(menu) do
1818 for _, iv in pairs(mv) do
1819 if iv == a then
1820 parent = menu[i][1]
1821 break
1822 end
1823 end
1824 if parent then break end
1825 curx = curx + mv[1]:len() + 3
1826 end
1827 local menux = curx + 2
1828
1829 -- Flash menu item
1830 term.setCursorBlink(false)
1831 term.setCursorPos(menux, 1)
1832 term.setBackgroundColor(colors[theme.background])
1833 term.write(string.rep(" ", parent:len() + 2))
1834 term.setCursorPos(menux + 1, 1)
1835 term.write(parent)
1836 sleep(0.1)
1837 drawMenu()
1838
1839 -- Execute item
1840 local opt = executeMenuItem(a, path)
1841 if opt then return opt end
1842 end
1843 elseif e == "mouse_scroll" then
1844 if key == -1 and scrolly > 0 then
1845 scrolly = scrolly - 1
1846 if os.clock() - scrollClock > 0.0005 then
1847 draw()
1848 term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
1849 end
1850 scrollClock = os.clock()
1851 hasScrolled = true
1852 elseif key == 1 and scrolly < #lines - edh then
1853 scrolly = scrolly + 1
1854 if os.clock() - scrollClock > 0.0005 then
1855 draw()
1856 term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
1857 end
1858 scrollClock = os.clock()
1859 hasScrolled = true
1860 end
1861 elseif e == "timer" and key == tid then
1862 drawLine(y)
1863 tid = os.startTimer(3)
1864 end
1865
1866 -- Draw
1867 if hasScrolled and os.clock() - scrollClock > 0.1 then
1868 draw()
1869 term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
1870 hasScrolled = false
1871 end
1872
1873 -- Autosave
1874 if os.clock() - autosaveClock > autosaveInterval then
1875 saveFile(path, lines)
1876 autosaveClock = os.clock()
1877 end
1878
1879 -- Errors
1880 if os.clock() - liveErrorClock > 1 then
1881 local prevLiveErr = liveErr
1882 liveErr = curLanguage.parseError(nil)
1883 local code = ""
1884 for _, v in pairs(lines) do code = code .. v .. "\n" end
1885
1886 liveErr = curLanguage.getCompilerErrors(code)
1887 liveErr.line = math.min(liveErr.line - 2, #lines)
1888 if liveErr ~= prevLiveErr then draw() end
1889 liveErrorClock = os.clock()
1890 end
1891 end
1892
1893 return "menu"
1894end
1895
1896
1897-- -------- Open File
1898
1899local function newFile()
1900 local wid = w - 13
1901
1902 -- Get name
1903 title("Lua IDE - New File")
1904 local name = centerRead(wid, "/")
1905 if not name or name == "" then return "menu" end
1906 name = "/" .. name
1907
1908 -- Clear
1909 title("Lua IDE - New File")
1910 term.setTextColor(colors[theme.textColor])
1911 term.setBackgroundColor(colors[theme.promptHighlight])
1912 for i = 8, 10 do
1913 term.setCursorPos(w/2 - wid/2, i)
1914 term.write(string.rep(" ", wid))
1915 end
1916 term.setCursorPos(1, 9)
1917 if fs.isDir(name) then
1918 centerPrint("Cannot Edit a Directory!")
1919 sleep(1.6)
1920 return "menu"
1921 elseif fs.exists(name) then
1922 centerPrint("File Already Exists!")
1923 local opt = prompt({{"Open", w/2 - 9, 14}, {"Cancel", w/2 + 2, 14}}, "horizontal")
1924 if opt == "Open" then return "edit", name
1925 elseif opt == "Cancel" then return "menu" end
1926 else return "edit", name end
1927end
1928
1929local function openFile()
1930 local wid = w - 13
1931
1932 -- Get name
1933 title("Lua IDE - Open File")
1934 local name = centerRead(wid, "/")
1935 if not name or name == "" then return "menu" end
1936 name = "/" .. name
1937
1938 -- Clear
1939 title("Lua IDE - New File")
1940 term.setTextColor(colors[theme.textColor])
1941 term.setBackgroundColor(colors[theme.promptHighlight])
1942 for i = 8, 10 do
1943 term.setCursorPos(w/2 - wid/2, i)
1944 term.write(string.rep(" ", wid))
1945 end
1946 term.setCursorPos(1, 9)
1947 if fs.isDir(name) then
1948 centerPrint("Cannot Open a Directory!")
1949 sleep(1.6)
1950 return "menu"
1951 elseif not fs.exists(name) then
1952 centerPrint("File Doesn't Exist!")
1953 local opt = prompt({{"Create", w/2 - 11, 14}, {"Cancel", w/2 + 2, 14}}, "horizontal")
1954 if opt == "Create" then return "edit", name
1955 elseif opt == "Cancel" then return "menu" end
1956 else return "edit", name end
1957end
1958
1959
1960-- -------- Settings
1961
1962local function update()
1963 local function draw(status)
1964 title("LuaIDE - Update")
1965 term.setBackgroundColor(colors[theme.prompt])
1966 term.setTextColor(colors[theme.textColor])
1967 for i = 8, 10 do
1968 term.setCursorPos(w/2 - (status:len() + 4), i)
1969 write(string.rep(" ", status:len() + 4))
1970 end
1971 term.setCursorPos(w/2 - (status:len() + 4), 9)
1972 term.write(" - " .. status .. " ")
1973
1974 term.setBackgroundColor(colors[theme.errHighlight])
1975 for i = 8, 10 do
1976 term.setCursorPos(w/2 + 2, i)
1977 term.write(string.rep(" ", 10))
1978 end
1979 term.setCursorPos(w/2 + 2, 9)
1980 term.write(" > Cancel ")
1981 end
1982
1983 if not http then
1984 draw("HTTP API Disabled!")
1985 sleep(1.6)
1986 return "settings"
1987 end
1988
1989 draw("Updating...")
1990 local tID = os.startTimer(10)
1991 http.request(updateURL)
1992 while true do
1993 local e, but, x, y = os.pullEvent()
1994 if (e == "key" and but == 28) or
1995 (e == "mouse_click" and x >= w/2 + 2 and x <= w/2 + 12 and y == 9) then
1996 draw("Cancelled")
1997 sleep(1.6)
1998 break
1999 elseif e == "http_success" and but == updateURL then
2000 local new = x.readAll()
2001 local curf = io.open(ideLocation, "r")
2002 local cur = curf:read("*a")
2003 curf:close()
2004
2005 if cur ~= new then
2006 draw("Update Found")
2007 sleep(1.6)
2008 local f = io.open(ideLocation, "w")
2009 f:write(new)
2010 f:close()
2011
2012 draw("Click to Exit")
2013 while true do
2014 local e = os.pullEvent()
2015 if e == "mouse_click" or (not isAdvanced() and e == "key") then break end
2016 end
2017 return "exit"
2018 else
2019 draw("No Updates Found!")
2020 sleep(1.6)
2021 break
2022 end
2023 elseif e == "http_failure" or (e == "timer" and but == tID) then
2024 draw("Update Failed!")
2025 sleep(1.6)
2026 break
2027 end
2028 end
2029
2030 return "settings"
2031end
2032
2033local function changeTheme()
2034 title("LuaIDE - Theme")
2035
2036 if isAdvanced() then
2037 local disThemes = {"Back"}
2038 for _, v in pairs(availableThemes) do table.insert(disThemes, v[1]) end
2039 local t = scrollingPrompt(disThemes)
2040 local url = nil
2041 for _, v in pairs(availableThemes) do if v[1] == t then url = v[2] end end
2042
2043 if not url then return "settings" end
2044 if t == "Dawn (Default)" then
2045 term.setBackgroundColor(colors[theme.backgroundHighlight])
2046 term.setCursorPos(3, 3)
2047 term.clearLine()
2048 term.write("LuaIDE - Loaded Theme!")
2049 sleep(1.6)
2050
2051 fs.delete(themeLocation)
2052 theme = defaultTheme
2053 return "menu"
2054 end
2055
2056 term.setBackgroundColor(colors[theme.backgroundHighlight])
2057 term.setCursorPos(3, 3)
2058 term.clearLine()
2059 term.write("LuaIDE - Downloading...")
2060
2061 fs.delete(tempthemefile)
2062 download(url, tempthemefile)
2063 local a = loadTheme(tempthemefile)
2064
2065 term.setCursorPos(3, 3)
2066 term.clearLine()
2067 if a then
2068 term.write("LuaIDE - Loaded Theme!")
2069 fs.delete(themeLocation)
2070 fs.move(tempthemefile, themeLocation)
2071 theme = a
2072 sleep(1.6)
2073 return "menu"
2074 end
2075
2076 term.write("LuaIDE - Could Not Load Theme!")
2077 fs.delete(tempthemefile)
2078 sleep(1.6)
2079 return "settings"
2080 else
2081 term.setCursorPos(1, 8)
2082 centerPrint("Themes are not available on")
2083 centerPrint("normal computers!")
2084 end
2085end
2086
2087local function settings()
2088 title("LuaIDE - Settings")
2089
2090 local opt = prompt({{"Change Theme", w/2 - 17, 8}, {"Return to Menu", w/2 - 22, 13},
2091 {"Check for Updates", w/2 + 2, 8}, {"Exit IDE", w/2 + 2, 13, bg = colors[theme.err],
2092 highlight = colors[theme.errHighlight]}}, "vertical", true)
2093 if opt == "Change Theme" then return changeTheme()
2094 elseif opt == "Check for Updates" then return update()
2095 elseif opt == "Return to Menu" then return "menu"
2096 elseif opt == "Exit IDE" then return "exit" end
2097end
2098
2099
2100-- -------- Menu
2101
2102local function menu()
2103 title("Welcome to LuaIDE " .. version)
2104
2105 local opt = prompt({{"New File", w/2 - 13, 8}, {"Open File", w/2 - 14, 13},
2106 {"Settings", w/2 + 2, 8}, {"Exit IDE", w/2 + 2, 13, bg = colors[theme.err],
2107 highlight = colors[theme.errHighlight]}}, "vertical", true)
2108 if opt == "New File" then return "new"
2109 elseif opt == "Open File" then return "open"
2110 elseif opt == "Settings" then return "settings"
2111 elseif opt == "Exit IDE" then return "exit" end
2112end
2113
2114
2115-- -------- Main
2116
2117local function main(arguments)
2118 local opt, data = "menu", nil
2119
2120 -- Check arguments
2121 if type(arguments) == "table" and #arguments > 0 then
2122 local f = "/" .. shell.resolve(arguments[1])
2123 if fs.isDir(f) then print("Cannot edit a directory.") end
2124 opt, data = "edit", f
2125 end
2126
2127 -- Main run loop
2128 while true do
2129 -- Menu
2130 if opt == "menu" then opt = menu() end
2131
2132 -- Other
2133 if opt == "new" then opt, data = newFile()
2134 elseif opt == "open" then opt, data = openFile()
2135 elseif opt == "settings" then opt = settings()
2136 end if opt == "exit" then break end
2137
2138 -- Edit
2139 if opt == "edit" and data then opt = edit(data) end
2140 end
2141end
2142
2143-- Load Theme
2144if fs.exists(themeLocation) then theme = loadTheme(themeLocation) end
2145if not theme and isAdvanced() then theme = defaultTheme
2146elseif not theme then theme = normalTheme end
2147
2148-- Run
2149local _, err = pcall(function()
2150 parallel.waitForAny(function() main(args) end, monitorKeyboardShortcuts)
2151end)
2152
2153-- Catch errors
2154if err and not err:find("Terminated") then
2155 term.setCursorBlink(false)
2156 title("LuaIDE - Crash! D:")
2157
2158 term.setBackgroundColor(colors[theme.err])
2159 for i = 6, 8 do
2160 term.setCursorPos(5, i)
2161 term.write(string.rep(" ", 36))
2162 end
2163 term.setCursorPos(6, 7)
2164 term.write("LuaIDE Has Crashed! D:")
2165
2166 term.setBackgroundColor(colors[theme.background])
2167 term.setCursorPos(2, 10)
2168 print(err)
2169
2170 term.setBackgroundColor(colors[theme.prompt])
2171 local _, cy = term.getCursorPos()
2172 for i = cy + 1, cy + 4 do
2173 term.setCursorPos(5, i)
2174 term.write(string.rep(" ", 36))
2175 end
2176 term.setCursorPos(6, cy + 2)
2177 term.write("Please report this error to")
2178 term.setCursorPos(6, cy + 3)
2179 term.write("GravityScore! ")
2180
2181 term.setBackgroundColor(colors[theme.background])
2182 if isAdvanced() then centerPrint("Click to Exit...", h - 1)
2183 else centerPrint("Press Any Key to Exit...", h - 1) end
2184 while true do
2185 local e = os.pullEvent()
2186 if e == "mouse_click" or (not isAdvanced() and e == "key") then break end
2187 end
2188
2189 -- Prevent key from being shown
2190 os.queueEvent(event_distract)
2191 os.pullEvent()
2192end