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