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