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