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