· 5 years ago · Jun 17, 2020, 03:28 AM
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 == keys.leftCtrl or but == rightCtrl)) 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 == keys.leftShift or char == keys.rightShift) then
484 shiftPressed = true
485 tb = os.startTimer(keyboardShortcutTimeout)
486 elseif event == "key" and (char == keys.leftCtrl or char == keys.rightCtrl) 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 == keys.left then
1026 sel = math.max(1, sel - 1)
1027 term.setCursorPos(2, 1)
1028 term.clearLine()
1029 term.write(opts[sel])
1030 elseif but == keys.right then
1031 sel = math.min(#opts, sel + 1)
1032 term.setCursorPos(2, 1)
1033 term.clearLine()
1034 term.write(opts[sel])
1035 elseif but == keys.enter or but == keys.numPadEnter 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 " .. keys.left] = "Start of Line ^+<",
1281 ["ctrl " .. keys.right] = "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", keys.home) end,
1321 ["End of Line ^+>"] = function() os.queueEvent("key", keys['end']) 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 == keys.up 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 == keys.down 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 == keys.left 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 == keys.right 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 == keys.enter or key == keys.numPadEnter) and (displayCode and true or y + scrolly - 1 == liverErr.line) then
1704 -- Enter
1705 local f = nil
1706 for _, v in pairs(standardsCompletions) do
1707 if lines[y]:find(v) and x == #lines[y] + 1 then f = v end
1708 end
1709
1710 local _, spaces = lines[y]:find("^[ ]+")
1711 if not spaces then spaces = 0 end
1712 if f then
1713 table.insert(lines, y + 1, string.rep(" ", spaces + 2))
1714 if not f:find("else", 1, true) and not f:find("elseif", 1, true) then
1715 table.insert(lines, y + 2, string.rep(" ", spaces) ..
1716 (f:find("repeat", 1, true) and "until " or f:find("{", 1, true) and "}" or
1717 "end"))
1718 end
1719 x, y = spaces + 3, y + 1
1720 cursorLoc(x, y, true)
1721 else
1722 local oldLine = lines[y]
1723
1724 lines[y] = lines[y]:sub(1, x - 1)
1725 table.insert(lines, y + 1, string.rep(" ", spaces) .. oldLine:sub(x, -1))
1726
1727 x, y = spaces + 1, y + 1
1728 cursorLoc(x, y, true)
1729 end
1730 elseif key == keys.backspace and (displayCode and true or y + scrolly - 1 == liveErr.line) then
1731 -- Backspace
1732 if x > 1 then
1733 local f = false
1734 for k, v in pairs(liveCompletions) do
1735 if lines[y]:sub(x - 1, x - 1) == k then f = true end
1736 end
1737
1738 lines[y] = lines[y]:sub(1, x - 2) .. lines[y]:sub(x + (f and 1 or 0), -1)
1739 drawLine(y)
1740 x = x - 1
1741 cursorLoc(x, y)
1742 elseif y > 1 then
1743 local prevLen = lines[y - 1]:len() + 1
1744 lines[y - 1] = lines[y - 1] .. lines[y]
1745 table.remove(lines, y)
1746 x, y = prevLen, y - 1
1747 cursorLoc(x, y, true)
1748 end
1749 elseif key == keys.home then
1750 -- Home
1751 x = 1
1752 local force = false
1753 if y - scrolly + offy < offy + 1 then force = true end
1754 cursorLoc(x, y, force)
1755 elseif key == keys['end'] then
1756 -- End
1757 x = lines[y]:len() + 1
1758 local force = false
1759 if y - scrolly + offy < offy + 1 then force = true end
1760 cursorLoc(x, y, force)
1761 elseif key == keys.delete and (displayCode and true or y + scrolly - 1 == liveErr.line) then
1762 -- Forward Delete
1763 if x < lines[y]:len() + 1 then
1764 lines[y] = lines[y]:sub(1, x - 1) .. lines[y]:sub(x + 1)
1765 local force = false
1766 if y - scrolly + offy < offy + 1 then force = true end
1767 drawLine(y)
1768 cursorLoc(x, y, force)
1769 elseif y < #lines then
1770 lines[y] = lines[y] .. lines[y + 1]
1771 table.remove(lines, y + 1)
1772 draw()
1773 cursorLoc(x, y)
1774 end
1775 elseif key == keys.tab and (displayCode and true or y + scrolly - 1 == liveErr.line) then
1776 -- Tab
1777 lines[y] = string.rep(" ", tabWidth) .. lines[y]
1778 x = x + 2
1779 local force = false
1780 if y - scrolly + offy < offy + 1 then force = true end
1781 drawLine(y)
1782 cursorLoc(x, y, force)
1783 elseif key == keys.pageUp then
1784 -- Page up
1785 y = math.min(math.max(y - edh, 1), #lines)
1786 x = math.min(lines[y]:len() + 1, x)
1787 cursorLoc(x, y, true)
1788 elseif key == keys.pageDown then
1789 -- Page down
1790 y = math.min(math.max(y + edh, 1), #lines)
1791 x = math.min(lines[y]:len() + 1, x)
1792 cursorLoc(x, y, true)
1793 end
1794 elseif e == "char" and allowEditorEvent and (displayCode and true or
1795 y + scrolly - 1 == liveErr.line) then
1796 local shouldIgnore = false
1797 for k, v in pairs(liveCompletions) do
1798 if key == v and lines[y]:find(k, 1, true) and lines[y]:sub(x, x) == v then
1799 shouldIgnore = true
1800 end
1801 end
1802
1803 local addOne = false
1804 if not shouldIgnore then
1805 for k, v in pairs(liveCompletions) do
1806 if key == k and lines[y]:sub(x, x) ~= k then key = key .. v addOne = true end
1807 end
1808 lines[y] = lines[y]:sub(1, x - 1) .. key .. lines[y]:sub(x, -1)
1809 end
1810
1811 x = x + (addOne and 1 or key:len())
1812 local force = false
1813 if y - scrolly + offy < offy + 1 then force = true end
1814 drawLine(y)
1815 cursorLoc(x, y, force)
1816 elseif e == "mouse_click" and key == 1 then
1817 if cy > 1 then
1818 if cx <= offx and cy - offy == liveErr.line - scrolly then
1819 displayCode = not displayCode
1820 drawLine(liveErr.line)
1821 else
1822 local oldy = y
1823 y = math.min(math.max(scrolly + cy - offy, 1), #lines)
1824 x = math.min(math.max(scrollx + cx - offx, 1), lines[y]:len() + 1)
1825 if oldy ~= y then drawLine(oldy, y) end
1826 cursorLoc(x, y)
1827 end
1828 else
1829 local a = triggerMenu(cx, cy)
1830 if a then
1831 local opt = executeMenuItem(a, path)
1832 if opt then return opt end
1833 end
1834 end
1835 elseif e == "shortcut" then
1836 local a = shortcuts[key .. " " .. cx]
1837 if a then
1838 local parent = nil
1839 local curx = 0
1840 for i, mv in ipairs(menu) do
1841 for _, iv in pairs(mv) do
1842 if iv == a then
1843 parent = menu[i][1]
1844 break
1845 end
1846 end
1847 if parent then break end
1848 curx = curx + mv[1]:len() + 3
1849 end
1850 local menux = curx + 2
1851
1852 -- Flash menu item
1853 term.setCursorBlink(false)
1854 term.setCursorPos(menux, 1)
1855 term.setBackgroundColor(colors[theme.background])
1856 term.write(string.rep(" ", parent:len() + 2))
1857 term.setCursorPos(menux + 1, 1)
1858 term.write(parent)
1859 sleep(0.1)
1860 drawMenu()
1861
1862 -- Execute item
1863 local opt = executeMenuItem(a, path)
1864 if opt then return opt end
1865 end
1866 elseif e == "mouse_scroll" then
1867 if key == -1 and scrolly > 0 then
1868 scrolly = scrolly - 1
1869 if os.clock() - scrollClock > 0.0005 then
1870 draw()
1871 term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
1872 end
1873 scrollClock = os.clock()
1874 hasScrolled = true
1875 elseif key == 1 and scrolly < #lines - edh then
1876 scrolly = scrolly + 1
1877 if os.clock() - scrollClock > 0.0005 then
1878 draw()
1879 term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
1880 end
1881 scrollClock = os.clock()
1882 hasScrolled = true
1883 end
1884 elseif e == "timer" and key == tid then
1885 drawLine(y)
1886 tid = os.startTimer(3)
1887 end
1888
1889 -- Draw
1890 if hasScrolled and os.clock() - scrollClock > 0.1 then
1891 draw()
1892 term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
1893 hasScrolled = false
1894 end
1895
1896 -- Autosave
1897 if os.clock() - autosaveClock > autosaveInterval then
1898 saveFile(path, lines)
1899 autosaveClock = os.clock()
1900 end
1901
1902 -- Errors
1903 if os.clock() - liveErrorClock > 1 then
1904 local prevLiveErr = liveErr
1905 liveErr = currentLanguage.parseError(nil)
1906 local code = ""
1907 for _, v in pairs(lines) do code = code .. v .. "\n" end
1908
1909 liveErr = currentLanguage.getCompilerErrors(code)
1910 liveErr.line = math.min(liveErr.line - 2, #lines)
1911 if liveErr ~= prevLiveErr then draw() end
1912 liveErrorClock = os.clock()
1913 end
1914 end
1915
1916 return "menu"
1917end
1918
1919
1920-- -------- Open File
1921
1922local function newFile()
1923 local wid = w - 13
1924
1925 -- Get name
1926 title("Lua IDE - New File")
1927 local name = centerRead(wid, "/")
1928 if not name or name == "" then return "menu" end
1929 name = "/" .. name
1930
1931 -- Clear
1932 title("Lua IDE - New File")
1933 term.setTextColor(colors[theme.textColor])
1934 term.setBackgroundColor(colors[theme.promptHighlight])
1935 for i = 8, 10 do
1936 term.setCursorPos(w/2 - wid/2, i)
1937 term.write(string.rep(" ", wid))
1938 end
1939 term.setCursorPos(1, 9)
1940 if fs.isDir(name) then
1941 centerPrint("Cannot Edit a Directory!")
1942 sleep(1.6)
1943 return "menu"
1944 elseif fs.exists(name) then
1945 centerPrint("File Already Exists!")
1946 local opt = prompt({{"Open", w/2 - 9, 14}, {"Cancel", w/2 + 2, 14}}, "horizontal")
1947 if opt == "Open" then return "edit", name
1948 elseif opt == "Cancel" then return "menu" end
1949 else return "edit", name end
1950end
1951
1952local function openFile()
1953 local wid = w - 13
1954
1955 -- Get name
1956 title("Lua IDE - Open File")
1957 local name = centerRead(wid, "/")
1958 if not name or name == "" then return "menu" end
1959 name = "/" .. name
1960
1961 -- Clear
1962 title("Lua IDE - New File")
1963 term.setTextColor(colors[theme.textColor])
1964 term.setBackgroundColor(colors[theme.promptHighlight])
1965 for i = 8, 10 do
1966 term.setCursorPos(w/2 - wid/2, i)
1967 term.write(string.rep(" ", wid))
1968 end
1969 term.setCursorPos(1, 9)
1970 if fs.isDir(name) then
1971 centerPrint("Cannot Open a Directory!")
1972 sleep(1.6)
1973 return "menu"
1974 elseif not fs.exists(name) then
1975 centerPrint("File Doesn't Exist!")
1976 local opt = prompt({{"Create", w/2 - 11, 14}, {"Cancel", w/2 + 2, 14}}, "horizontal")
1977 if opt == "Create" then return "edit", name
1978 elseif opt == "Cancel" then return "menu" end
1979 else return "edit", name end
1980end
1981
1982
1983-- -------- Settings
1984
1985local function update()
1986 -- Disable updating.
1987 return "settings"
1988 local function draw(status)
1989 title("LuaIDE - Update")
1990 term.setBackgroundColor(colors[theme.prompt])
1991 term.setTextColor(colors[theme.textColor])
1992 for i = 8, 10 do
1993 term.setCursorPos(w/2 - (status:len() + 4), i)
1994 write(string.rep(" ", status:len() + 4))
1995 end
1996 term.setCursorPos(w/2 - (status:len() + 4), 9)
1997 term.write(" - " .. status .. " ")
1998
1999 term.setBackgroundColor(colors[theme.errHighlight])
2000 for i = 8, 10 do
2001 term.setCursorPos(w/2 + 2, i)
2002 term.write(string.rep(" ", 10))
2003 end
2004 term.setCursorPos(w/2 + 2, 9)
2005 term.write(" > Cancel ")
2006 end
2007
2008 if not http then
2009 draw("HTTP API Disabled!")
2010 sleep(1.6)
2011 return "settings"
2012 end
2013
2014 draw("Updating...")
2015 local tID = os.startTimer(10)
2016 http.request(updateURL)
2017 while true do
2018 local e, but, x, y = os.pullEvent()
2019 if (e == "key" and but == 28) or
2020 (e == "mouse_click" and x >= w/2 + 2 and x <= w/2 + 12 and y == 9) then
2021 draw("Cancelled")
2022 sleep(1.6)
2023 break
2024 elseif e == "http_success" and but == updateURL then
2025 local new = x.readAll()
2026 local curf = io.open(ideLocation, "r")
2027 local cur = curf:read("*a")
2028 curf:close()
2029
2030 if cur ~= new then
2031 draw("Update Found")
2032 sleep(1.6)
2033 local f = io.open(ideLocation, "w")
2034 f:write(new)
2035 f:close()
2036
2037 draw("Click to Exit")
2038 while true do
2039 local e = os.pullEvent()
2040 if e == "mouse_click" or (not isAdvanced() and e == "key") then break end
2041 end
2042 return "exit"
2043 else
2044 draw("No Updates Found!")
2045 sleep(1.6)
2046 break
2047 end
2048 elseif e == "http_failure" or (e == "timer" and but == tID) then
2049 draw("Update Failed!")
2050 sleep(1.6)
2051 break
2052 end
2053 end
2054
2055 return "settings"
2056end
2057
2058local function changeTheme()
2059 title("LuaIDE - Theme")
2060
2061 if isAdvanced() then
2062 local disThemes = {"Back"}
2063 for _, v in pairs(availableThemes) do table.insert(disThemes, v[1]) end
2064 local t = scrollingPrompt(disThemes)
2065 local url = nil
2066 for _, v in pairs(availableThemes) do if v[1] == t then url = v[2] end end
2067
2068 if not url then return "settings" end
2069 if t == "Dawn (Default)" then
2070 term.setBackgroundColor(colors[theme.backgroundHighlight])
2071 term.setCursorPos(3, 3)
2072 term.clearLine()
2073 term.write("LuaIDE - Loaded Theme!")
2074 sleep(1.6)
2075
2076 fs.delete(themeLocation)
2077 theme = defaultTheme
2078 return "menu"
2079 end
2080
2081 term.setBackgroundColor(colors[theme.backgroundHighlight])
2082 term.setCursorPos(3, 3)
2083 term.clearLine()
2084 term.write("LuaIDE - Downloading...")
2085
2086 fs.delete("/.LuaIDE_temp_theme_file")
2087 download(url, "/.LuaIDE_temp_theme_file")
2088 local a = loadTheme("/.LuaIDE_temp_theme_file")
2089
2090 term.setCursorPos(3, 3)
2091 term.clearLine()
2092 if a then
2093 term.write("LuaIDE - Loaded Theme!")
2094 fs.delete(themeLocation)
2095 fs.move("/.LuaIDE_temp_theme_file", themeLocation)
2096 theme = a
2097 sleep(1.6)
2098 return "menu"
2099 end
2100
2101 term.write("LuaIDE - Could Not Load Theme!")
2102 fs.delete("/.LuaIDE_temp_theme_file")
2103 sleep(1.6)
2104 return "settings"
2105 else
2106 term.setCursorPos(1, 8)
2107 centerPrint("Themes are not available on")
2108 centerPrint("normal computers!")
2109 end
2110end
2111
2112local function settings()
2113 title("LuaIDE - Settings")
2114
2115 local opt = prompt({{"Change Theme", w/2 - 17, 8}, {"Return to Menu", w/2 - 22, 13},
2116 {"Check for Updates", w/2 + 2, 8}, {"Exit IDE", w/2 + 2, 13, bg = colors[theme.err],
2117 highlight = colors[theme.errHighlight]}}, "vertical", true)
2118 if opt == "Change Theme" then return changeTheme()
2119 elseif opt == "Check for Updates" then return update()
2120 elseif opt == "Return to Menu" then return "menu"
2121 elseif opt == "Exit IDE" then return "exit" end
2122end
2123
2124
2125-- -------- Menu
2126
2127local function menu()
2128 title("Welcome to LuaIDE " .. version)
2129
2130 local opt = prompt({{"New File", w/2 - 13, 8}, {"Open File", w/2 - 14, 13},
2131 {"Settings", w/2 + 2, 8}, {"Exit IDE", w/2 + 2, 13, bg = colors[theme.err],
2132 highlight = colors[theme.errHighlight]}}, "vertical", true)
2133 if opt == "New File" then return "new"
2134 elseif opt == "Open File" then return "open"
2135 elseif opt == "Settings" then return "settings"
2136 elseif opt == "Exit IDE" then return "exit" end
2137end
2138
2139
2140-- -------- Main
2141
2142local function main(arguments)
2143 local opt, data = "menu", nil
2144
2145 -- Check arguments
2146 if type(arguments) == "table" and #arguments > 0 then
2147 local f = "/" .. shell.resolve(arguments[1])
2148 if fs.isDir(f) then print("Cannot edit a directory.") end
2149 opt, data = "edit", f
2150 end
2151
2152 -- Main run loop
2153 while true do
2154 -- Menu
2155 if opt == "menu" then opt = menu() end
2156
2157 -- Other
2158 if opt == "new" then opt, data = newFile()
2159 elseif opt == "open" then opt, data = openFile()
2160 elseif opt == "settings" then opt = settings()
2161 end if opt == "exit" then break end
2162
2163 -- Edit
2164 if opt == "edit" and data then opt = edit(data) end
2165 end
2166end
2167
2168-- Load Theme
2169if fs.exists(themeLocation) then theme = loadTheme(themeLocation) end
2170if not theme and isAdvanced() then theme = defaultTheme
2171elseif not theme then theme = normalTheme end
2172
2173-- Run
2174local _, err = pcall(function()
2175 parallel.waitForAny(function() main(arguments) end, monitorKeyboardShortcuts)
2176end)
2177
2178-- Catch errors
2179if err and not err:find("Terminated") then
2180 term.setCursorBlink(false)
2181 title("LuaIDE - Crash! D:")
2182
2183 term.setBackgroundColor(colors[theme.err])
2184 for i = 6, 8 do
2185 term.setCursorPos(5, i)
2186 term.write(string.rep(" ", 36))
2187 end
2188 term.setCursorPos(6, 7)
2189 term.write("LuaIDE Has Crashed! D:")
2190
2191 term.setBackgroundColor(colors[theme.background])
2192 term.setCursorPos(2, 10)
2193 print(err)
2194
2195 term.setBackgroundColor(colors[theme.prompt])
2196 local _, cy = term.getCursorPos()
2197 for i = cy + 1, cy + 4 do
2198 term.setCursorPos(5, i)
2199 term.write(string.rep(" ", 36))
2200 end
2201 term.setCursorPos(6, cy + 2)
2202 term.write("Please report this error to")
2203 term.setCursorPos(6, cy + 3)
2204 term.write("GravityScore! ")
2205
2206 term.setBackgroundColor(colors[theme.background])
2207 if isAdvanced() then centerPrint("Click to Exit...", h - 1)
2208 else centerPrint("Press Any Key to Exit...", h - 1) end
2209 while true do
2210 local e = os.pullEvent()
2211 if e == "mouse_click" or (not isAdvanced() and e == "key") then break end
2212 end
2213
2214 -- Prevent key from being shown
2215 os.queueEvent("")
2216 os.pullEvent()
2217end
2218
2219-- Exit
2220term.setBackgroundColor(colors.black)
2221term.setTextColor(colors.white)
2222term.clear()
2223term.setCursorPos(1, 1)
2224centerPrint("Thank You for Using Lua IDE " .. version)
2225centerPrint("Made by GravityScore")