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