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