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