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