· 7 years ago · Feb 01, 2019, 12:12 AM
1{
2 programs = {
3 [ "edit.lua" ] = "\
4-- \
5-- Lua IDE\
6-- Made by GravityScore\
7-- \
8\
9\
10-- -------- Variables\
11\
12-- Version\
13local version = \"1.0\"\
14local args = {...}\
15\
16-- Editing\
17local w, h = term.getSize()\
18local tabWidth = 2\
19\
20local autosaveInterval = 20\
21local allowEditorEvent = true\
22local keyboardShortcutTimeout = 0.4\
23\
24-- Clipboard\
25local clipboard = nil\
26\
27-- Theme\
28local theme = {}\
29\
30-- Language\
31local languages = {}\
32local curLanguage = {}\
33\
34-- Events\
35local event_distract = \"luaide_distractionEvent\"\
36\
37-- Locations\
38local updateURL = \"https://raw.github.com/GravityScore/LuaIDE/master/luaide.lua\"\
39local ideLocation = \"/\" .. shell.getRunningProgram()\
40local themeLocation = \"/.LuaIDE-Theme\"\
41\
42local function isAdvanced() return term.isColor and term.isColor() end\
43\
44\
45-- -------- Utilities\
46\
47local function modRead(properties)\
48 local w, h = term.getSize()\
49 local defaults = {replaceChar = nil, history = nil, visibleLength = nil, textLength = nil, \
50 liveUpdates = nil, exitOnKey = nil}\
51 if not properties then properties = {} end\
52 for k, v in pairs(defaults) do if not properties[k] then properties[k] = v end end\
53 if properties.replaceChar then properties.replaceChar = properties.replaceChar:sub(1, 1) end\
54 if not properties.visibleLength then properties.visibleLength = w end\
55\
56 local sx, sy = term.getCursorPos()\
57 local line = \"\"\
58 local pos = 0\
59 local historyPos = nil\
60\
61 local function redraw(repl)\
62 local scroll = 0\
63 if properties.visibleLength and sx + pos > properties.visibleLength + 1 then \
64 scroll = (sx + pos) - (properties.visibleLength + 1)\
65 end\
66\
67 term.setCursorPos(sx, sy)\
68 local a = repl or properties.replaceChar\
69 if a then term.write(string.rep(a, line:len() - scroll))\
70 else term.write(line:sub(scroll + 1, -1)) end\
71 term.setCursorPos(sx + pos - scroll, sy)\
72 end\
73\
74 local function sendLiveUpdates(event, ...)\
75 if type(properties.liveUpdates) == \"function\" then\
76 local ox, oy = term.getCursorPos()\
77 local a, data = properties.liveUpdates(line, event, ...)\
78 if a == true and data == nil then\
79 term.setCursorBlink(false)\
80 return line\
81 elseif a == true and data ~= nil then\
82 term.setCursorBlink(false)\
83 return data\
84 end\
85 term.setCursorPos(ox, oy)\
86 end\
87 end\
88\
89 term.setCursorBlink(true)\
90 while true do\
91 local e, but, x, y, p4, p5 = os.pullEvent()\
92\
93 if e == \"char\" then\
94 local s = false\
95 if properties.textLength and line:len() < properties.textLength then s = true\
96 elseif not properties.textLength then s = true end\
97\
98 local canType = true\
99 if not properties.grantPrint and properties.refusePrint then\
100 local canTypeKeys = {}\
101 if type(properties.refusePrint) == \"table\" then\
102 for _, v in pairs(properties.refusePrint) do\
103 table.insert(canTypeKeys, tostring(v):sub(1, 1))\
104 end\
105 elseif type(properties.refusePrint) == \"string\" then\
106 for char in properties.refusePrint:gmatch(\".\") do\
107 table.insert(canTypeKeys, char)\
108 end\
109 end\
110 for _, v in pairs(canTypeKeys) do if but == v then canType = false end end\
111 elseif properties.grantPrint then\
112 canType = false\
113 local canTypeKeys = {}\
114 if type(properties.grantPrint) == \"table\" then\
115 for _, v in pairs(properties.grantPrint) do\
116 table.insert(canTypeKeys, tostring(v):sub(1, 1))\
117 end\
118 elseif type(properties.grantPrint) == \"string\" then\
119 for char in properties.grantPrint:gmatch(\".\") do\
120 table.insert(canTypeKeys, char)\
121 end\
122 end\
123 for _, v in pairs(canTypeKeys) do if but == v then canType = true end end\
124 end\
125\
126 if s and canType then\
127 line = line:sub(1, pos) .. but .. line:sub(pos + 1, -1)\
128 pos = pos + 1\
129 redraw()\
130 end\
131 elseif e == \"key\" then\
132 if but == keys.enter then break\
133 elseif but == keys.left then if pos > 0 then pos = pos - 1 redraw() end\
134 elseif but == keys.right then if pos < line:len() then pos = pos + 1 redraw() end\
135 elseif (but == keys.up or but == keys.down) and properties.history then\
136 redraw(\" \")\
137 if but == keys.up then\
138 if historyPos == nil and #properties.history > 0 then \
139 historyPos = #properties.history\
140 elseif historyPos > 1 then \
141 historyPos = historyPos - 1\
142 end\
143 elseif but == keys.down then\
144 if historyPos == #properties.history then historyPos = nil\
145 elseif historyPos ~= nil then historyPos = historyPos + 1 end\
146 end\
147\
148 if properties.history and historyPos then\
149 line = properties.history[historyPos]\
150 pos = line:len()\
151 else\
152 line = \"\"\
153 pos = 0\
154 end\
155\
156 redraw()\
157 local a = sendLiveUpdates(\"history\")\
158 if a then return a end\
159 elseif but == keys.backspace and pos > 0 then\
160 redraw(\" \")\
161 line = line:sub(1, pos - 1) .. line:sub(pos + 1, -1)\
162 pos = pos - 1\
163 redraw()\
164 local a = sendLiveUpdates(\"delete\")\
165 if a then return a end\
166 elseif but == keys.home then\
167 pos = 0\
168 redraw()\
169 elseif but == keys.delete and pos < line:len() then\
170 redraw(\" \")\
171 line = line:sub(1, pos) .. line:sub(pos + 2, -1)\
172 redraw()\
173 local a = sendLiveUpdates(\"delete\")\
174 if a then return a end\
175 elseif but == keys[\"end\"] then\
176 pos = line:len()\
177 redraw()\
178 elseif properties.exitOnKey then \
179 if but == properties.exitOnKey or (properties.exitOnKey == \"control\" and \
180 (but == 29 or but == 157)) then \
181 term.setCursorBlink(false)\
182 return nil\
183 end\
184 end\
185 end\
186 local a = sendLiveUpdates(e, but, x, y, p4, p5)\
187 if a then return a end\
188 end\
189\
190 term.setCursorBlink(false)\
191 if line ~= nil then line = line:gsub(\"^%s*(.-)%s*$\", \"%1\") end\
192 return line\
193end\
194\
195\
196-- -------- Themes\
197\
198local defaultTheme = {\
199 background = \"gray\",\
200 backgroundHighlight = \"lightGray\",\
201 prompt = \"cyan\",\
202 promptHighlight = \"lightBlue\",\
203 err = \"red\",\
204 errHighlight = \"pink\",\
205\
206 editorBackground = \"gray\",\
207 editorLineHightlight = \"lightBlue\",\
208 editorLineNumbers = \"gray\",\
209 editorLineNumbersHighlight = \"lightGray\",\
210 editorError = \"pink\",\
211 editorErrorHighlight = \"red\",\
212\
213 textColor = \"white\",\
214 conditional = \"yellow\",\
215 constant = \"orange\",\
216 [\"function\"] = \"magenta\",\
217 string = \"red\",\
218 comment = \"lime\"\
219}\
220\
221local normalTheme = {\
222 background = \"black\",\
223 backgroundHighlight = \"black\",\
224 prompt = \"black\",\
225 promptHighlight = \"black\",\
226 err = \"black\",\
227 errHighlight = \"black\",\
228\
229 editorBackground = \"black\",\
230 editorLineHightlight = \"black\",\
231 editorLineNumbers = \"black\",\
232 editorLineNumbersHighlight = \"white\",\
233 editorError = \"black\",\
234 editorErrorHighlight = \"black\",\
235\
236 textColor = \"white\",\
237 conditional = \"white\",\
238 constant = \"white\",\
239 [\"function\"] = \"white\",\
240 string = \"white\",\
241 comment = \"white\"\
242}\
243\
244local availableThemes = {\
245 {\"Water (Default)\", \"https://raw.github.com/GravityScore/LuaIDE/master/themes/default.txt\"},\
246 {\"Fire\", \"https://raw.github.com/GravityScore/LuaIDE/master/themes/fire.txt\"},\
247 {\"Sublime Text 2\", \"https://raw.github.com/GravityScore/LuaIDE/master/themes/st2.txt\"},\
248 {\"Midnight\", \"https://raw.github.com/GravityScore/LuaIDE/master/themes/midnight.txt\"},\
249 {\"TheOriginalBIT\", \"https://raw.github.com/GravityScore/LuaIDE/master/themes/bit.txt\"},\
250 {\"Superaxander\", \"https://raw.github.com/GravityScore/LuaIDE/master/themes/superaxander.txt\"},\
251 {\"Forest\", \"https://raw.github.com/GravityScore/LuaIDE/master/themes/forest.txt\"},\
252 {\"Night\", \"https://raw.github.com/GravityScore/LuaIDE/master/themes/night.txt\"},\
253 {\"Original\", \"https://raw.github.com/GravityScore/LuaIDE/master/themes/original.txt\"},\
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 then\
368 sel = sel - 2\
369 draw(sel)\
370 elseif isGrid and e == \"key\" and but == 205 and sel < 3 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\
497local 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 local function draw(status)\
1971 title(\"LuaIDE - Update\")\
1972 term.setBackgroundColor(colors[theme.prompt])\
1973 term.setTextColor(colors[theme.textColor])\
1974 for i = 8, 10 do\
1975 term.setCursorPos(w/2 - (status:len() + 4), i)\
1976 write(string.rep(\" \", status:len() + 4))\
1977 end\
1978 term.setCursorPos(w/2 - (status:len() + 4), 9)\
1979 term.write(\" - \" .. status .. \" \")\
1980\
1981 term.setBackgroundColor(colors[theme.errHighlight])\
1982 for i = 8, 10 do\
1983 term.setCursorPos(w/2 + 2, i)\
1984 term.write(string.rep(\" \", 10))\
1985 end\
1986 term.setCursorPos(w/2 + 2, 9)\
1987 term.write(\" > Cancel \")\
1988 end\
1989\
1990 if not http then\
1991 draw(\"HTTP API Disabled!\")\
1992 sleep(1.6)\
1993 return \"settings\"\
1994 end\
1995\
1996 draw(\"Updating...\")\
1997 local tID = os.startTimer(10)\
1998 http.request(updateURL)\
1999 while true do\
2000 local e, but, x, y = os.pullEvent()\
2001 if (e == \"key\" and but == 28) or\
2002 (e == \"mouse_click\" and x >= w/2 + 2 and x <= w/2 + 12 and y == 9) then\
2003 draw(\"Cancelled\")\
2004 sleep(1.6)\
2005 break\
2006 elseif e == \"http_success\" and but == updateURL then\
2007 local new = x.readAll()\
2008 local curf = io.open(ideLocation, \"r\")\
2009 local cur = curf:read(\"*a\")\
2010 curf:close()\
2011\
2012 if cur ~= new then\
2013 draw(\"Update Found\")\
2014 sleep(1.6)\
2015 local f = io.open(ideLocation, \"w\")\
2016 f:write(new)\
2017 f:close()\
2018\
2019 draw(\"Click to Exit\")\
2020 while true do\
2021 local e = os.pullEvent()\
2022 if e == \"mouse_click\" or (not isAdvanced() and e == \"key\") then break end\
2023 end\
2024 return \"exit\"\
2025 else\
2026 draw(\"No Updates Found!\")\
2027 sleep(1.6)\
2028 break\
2029 end\
2030 elseif e == \"http_failure\" or (e == \"timer\" and but == tID) then\
2031 draw(\"Update Failed!\")\
2032 sleep(1.6)\
2033 break\
2034 end\
2035 end\
2036\
2037 return \"settings\"\
2038end\
2039\
2040local function changeTheme()\
2041 title(\"LuaIDE - Theme\")\
2042\
2043 if isAdvanced() then\
2044 local disThemes = {\"Back\"}\
2045 for _, v in pairs(availableThemes) do table.insert(disThemes, v[1]) end\
2046 local t = scrollingPrompt(disThemes)\
2047 local url = nil\
2048 for _, v in pairs(availableThemes) do if v[1] == t then url = v[2] end end\
2049\
2050 if not url then return \"settings\" end\
2051 if t == \"Dawn (Default)\" then\
2052 term.setBackgroundColor(colors[theme.backgroundHighlight])\
2053 term.setCursorPos(3, 3)\
2054 term.clearLine()\
2055 term.write(\"LuaIDE - Loaded Theme!\")\
2056 sleep(1.6)\
2057\
2058 fs.delete(themeLocation)\
2059 theme = defaultTheme\
2060 return \"menu\"\
2061 end\
2062\
2063 term.setBackgroundColor(colors[theme.backgroundHighlight])\
2064 term.setCursorPos(3, 3)\
2065 term.clearLine()\
2066 term.write(\"LuaIDE - Downloading...\")\
2067\
2068 fs.delete(\"/.LuaIDE_temp_theme_file\")\
2069 download(url, \"/.LuaIDE_temp_theme_file\")\
2070 local a = loadTheme(\"/.LuaIDE_temp_theme_file\")\
2071\
2072 term.setCursorPos(3, 3)\
2073 term.clearLine()\
2074 if a then\
2075 term.write(\"LuaIDE - Loaded Theme!\")\
2076 fs.delete(themeLocation)\
2077 fs.move(\"/.LuaIDE_temp_theme_file\", themeLocation)\
2078 theme = a\
2079 sleep(1.6)\
2080 return \"menu\"\
2081 end\
2082 \
2083 term.write(\"LuaIDE - Could Not Load Theme!\")\
2084 fs.delete(\"/.LuaIDE_temp_theme_file\")\
2085 sleep(1.6)\
2086 return \"settings\"\
2087 else\
2088 term.setCursorPos(1, 8)\
2089 centerPrint(\"Themes are not available on\")\
2090 centerPrint(\"normal computers!\")\
2091 end\
2092end\
2093\
2094local function settings()\
2095 title(\"LuaIDE - Settings\")\
2096\
2097 local opt = prompt({{\"Change Theme\", w/2 - 17, 8}, {\"Return to Menu\", w/2 - 22, 13},\
2098 {\"Check for Updates\", w/2 + 2, 8}, {\"Exit IDE\", w/2 + 2, 13, bg = colors[theme.err], \
2099 highlight = colors[theme.errHighlight]}}, \"vertical\", true)\
2100 if opt == \"Change Theme\" then return changeTheme()\
2101 elseif opt == \"Check for Updates\" then return update()\
2102 elseif opt == \"Return to Menu\" then return \"menu\"\
2103 elseif opt == \"Exit IDE\" then return \"exit\" end\
2104end\
2105\
2106\
2107-- -------- Menu\
2108\
2109local function menu()\
2110 title(\"Welcome to LuaIDE \" .. version)\
2111\
2112 local opt = prompt({{\"New File\", w/2 - 13, 8}, {\"Open File\", w/2 - 14, 13},\
2113 {\"Settings\", w/2 + 2, 8}, {\"Exit IDE\", w/2 + 2, 13, bg = colors[theme.err],\
2114 highlight = colors[theme.errHighlight]}}, \"vertical\", true)\
2115 if opt == \"New File\" then return \"new\"\
2116 elseif opt == \"Open File\" then return \"open\"\
2117 elseif opt == \"Settings\" then return \"settings\"\
2118 elseif opt == \"Exit IDE\" then return \"exit\" end\
2119end\
2120\
2121\
2122-- -------- Main\
2123\
2124local function main(arguments)\
2125 local opt, data = \"menu\", nil\
2126\
2127 -- Check arguments\
2128 if type(arguments) == \"table\" and #arguments > 0 then\
2129 local f = \"/\" .. shell.resolve(arguments[1])\
2130 if fs.isDir(f) then print(\"Cannot edit a directory.\") end\
2131 opt, data = \"edit\", f\
2132 end\
2133\
2134 -- Main run loop\
2135 while true do\
2136 -- Menu\
2137 if opt == \"menu\" then opt = menu() end\
2138\
2139 -- Other\
2140 if opt == \"new\" then opt, data = newFile()\
2141 elseif opt == \"open\" then opt, data = openFile()\
2142 elseif opt == \"settings\" then opt = settings()\
2143 end if opt == \"exit\" then break end\
2144\
2145 -- Edit\
2146 if opt == \"edit\" and data then opt = edit(data) end\
2147 end\
2148end\
2149\
2150-- Load Theme\
2151if fs.exists(themeLocation) then theme = loadTheme(themeLocation) end\
2152if not theme and isAdvanced() then theme = defaultTheme\
2153elseif not theme then theme = normalTheme end\
2154\
2155-- Run\
2156local _, err = pcall(function()\
2157 parallel.waitForAny(function() main(args) end, monitorKeyboardShortcuts)\
2158end)\
2159\
2160-- Catch errors\
2161if err and not err:find(\"Terminated\") then\
2162 term.setCursorBlink(false)\
2163 title(\"LuaIDE - Crash! D:\")\
2164\
2165 term.setBackgroundColor(colors[theme.err])\
2166 for i = 6, 8 do\
2167 term.setCursorPos(5, i)\
2168 term.write(string.rep(\" \", 36))\
2169 end\
2170 term.setCursorPos(6, 7)\
2171 term.write(\"LuaIDE Has Crashed! D:\")\
2172\
2173 term.setBackgroundColor(colors[theme.background])\
2174 term.setCursorPos(2, 10)\
2175 print(err)\
2176\
2177 term.setBackgroundColor(colors[theme.prompt])\
2178 local _, cy = term.getCursorPos()\
2179 for i = cy + 1, cy + 4 do\
2180 term.setCursorPos(5, i)\
2181 term.write(string.rep(\" \", 36))\
2182 end\
2183 term.setCursorPos(6, cy + 2)\
2184 term.write(\"Please report this error to\")\
2185 term.setCursorPos(6, cy + 3)\
2186 term.write(\"GravityScore! \")\
2187 \
2188 term.setBackgroundColor(colors[theme.background])\
2189 if isAdvanced() then centerPrint(\"Click to Exit...\", h - 1)\
2190 else centerPrint(\"Press Any Key to Exit...\", h - 1) end\
2191 while true do\
2192 local e = os.pullEvent()\
2193 if e == \"mouse_click\" or (not isAdvanced() and e == \"key\") then break end\
2194 end\
2195\
2196 -- Prevent key from being shown\
2197 os.queueEvent(event_distract)\
2198 os.pullEvent()\
2199end\
2200\
2201-- Exit\
2202term.setBackgroundColor(colors.black)\
2203term.setTextColor(colors.white)\
2204term.clear()\
2205term.setCursorPos(1, 1)",
2206 [ "rm.lua" ] = "fs.delete(fs.combine(fs.combine(_G.hlsh.sandbox, _G.hlsh.currDir), argv[1]))",
2207 [ "mkdir.lua" ] = "fs.makeDir(fs.combine(fs.combine(_G.hlsh.sandbox, _G.hlsh.currDir), argv[1]))",
2208 [ "cd.lua" ] = "_G.hlsh.currDir = fs.combine(_G.hlsh.currDir, argv[1]);",
2209 [ "lua.lua" ] = "local running = true\
2210\
2211function _G.exit()\
2212 running = false\
2213end\
2214\
2215while running do\
2216 term.write(\"LuaSH> \")\
2217 local command = assert(loadstring(read()))\
2218 \
2219 function _G.exit()\
2220 running = false\
2221 end\
2222\
2223 local status, err = pcall(command)\
2224 if status then\
2225 print(\"\")\
2226 else\
2227 term.setTextColor(colors.red)\
2228 print(\"Error: \" .. err)\
2229 term.setTextColor(colors.white)\
2230 end\
2231\
2232 function _G.exit()\
2233 write(\"\")\
2234 end\
2235end",
2236 [ "ls.lua" ] = "local stuff = fs.list(fs.combine(_G.hlsh.sandbox,_G.hlsh.currDir))\
2237\
2238for _, file in ipairs(stuff) do\
2239 if fs.isDir(fs.combine(fs.combine(_G.hlsh.sandbox,_G.hlsh.currDir), file)) then\
2240 term.setTextColor(colors.blue)\
2241 print(file)\
2242 term.setTextColor(colors.white)\
2243 else\
2244 term.setTextColor(colors.green)\
2245 print(file)\
2246 term.setTextColor(colors.white)\
2247 end\
2248end",
2249 [ "wget.lua" ] = "\
2250local function printUsage()\
2251 print( \"Usage:\" )\
2252 print( \"wget <url> <filename>\" )\
2253end\
2254 \
2255if #argv < 2 then\
2256 printUsage()\
2257 return\
2258end\
2259 \
2260if not http then\
2261 printError( \"wget requires http API\" )\
2262 printError( \"Set http_enable to true in ComputerCraft.cfg\" )\
2263 return\
2264end\
2265 \
2266local function get( sUrl )\
2267 write( \"Connecting to \" .. sUrl .. \"... \" )\
2268\
2269 local ok, err = http.checkURL( sUrl )\
2270 if not ok then\
2271 print( \"Failed.\" )\
2272 if err then\
2273 printError( err )\
2274 end\
2275 return nil\
2276 end\
2277\
2278 local response = http.get( sUrl , nil , true )\
2279 if not response then\
2280 print( \"Failed.\" )\
2281 return nil\
2282 end\
2283\
2284 print( \"Success.\" )\
2285\
2286 local sResponse = response.readAll()\
2287 response.close()\
2288 return sResponse\
2289end\
2290 \
2291-- Determine file to download\
2292local sUrl = argv[1]\
2293local sFile = argv[2]\
2294local sPath = shell.resolve( sFile )\
2295if fs.exists( sPath ) then\
2296 print( \"File already exists\" )\
2297 return\
2298end\
2299\
2300-- Do the get\
2301local res = get( sUrl )\
2302if res then\
2303 local file = fs.open( sPath, \"wb\" )\
2304 file.write( res )\
2305 file.close()\
2306\
2307 print( \"Downloaded as \"..sFile )\
2308end",
2309 [ "pastebin.lua" ] = "\
2310local function printUsage()\
2311 print( \"Usages:\" )\
2312 print( \"pastebin put <filename>\" )\
2313 print( \"pastebin get <code> <filename>\" )\
2314 print( \"pastebin run <code> <arguments>\" )\
2315end\
2316 \
2317if #argv < 2 then\
2318 printUsage()\
2319 return\
2320end\
2321 \
2322if not http then\
2323 printError( \"Pastebin requires http API\" )\
2324 printError( \"Set http_enable to true in ComputerCraft.cfg\" )\
2325 return\
2326end\
2327 \
2328local function get(paste)\
2329 write( \"Connecting to pastebin.com... \" )\
2330 local response = http.get(\
2331 \"https://pastebin.com/raw/\"..textutils.urlEncode( paste )\
2332 )\
2333 \
2334 if response then\
2335 print( \"Success.\" )\
2336 \
2337 local sResponse = response.readAll()\
2338 response.close()\
2339 return sResponse\
2340 else\
2341 print( \"Failed.\" )\
2342 end\
2343end\
2344 \
2345local sCommand = argv[1]\
2346if sCommand == \"put\" then\
2347 -- Upload a file to pastebin.com\
2348 -- Determine file to upload\
2349 local sFile = argv[2]\
2350 local sPath = shell.resolve( sFile )\
2351 if not fs.exists( sPath ) or fs.isDir( sPath ) then\
2352 print( \"No such file\" )\
2353 return\
2354 end\
2355 \
2356 -- Read in the file\
2357 local sName = fs.getName( sPath )\
2358 local file = fs.open( sPath, \"r\" )\
2359 local sText = file.readAll()\
2360 file.close()\
2361 \
2362 -- POST the contents to pastebin\
2363 write( \"Connecting to pastebin.com... \" )\
2364 local key = \"0ec2eb25b6166c0c27a394ae118ad829\"\
2365 local response = http.post(\
2366 \"https://pastebin.com/api/api_post.php\", \
2367 \"api_option=paste&\"..\
2368 \"api_dev_key=\"..key..\"&\"..\
2369 \"api_paste_format=lua&\"..\
2370 \"api_paste_name=\"..textutils.urlEncode(sName)..\"&\"..\
2371 \"api_paste_code=\"..textutils.urlEncode(sText)\
2372 )\
2373 \
2374 if response then\
2375 print( \"Success.\" )\
2376 \
2377 local sResponse = response.readAll()\
2378 response.close()\
2379 \
2380 local sCode = string.match( sResponse, \"[^/]+$\" )\
2381 print( \"Uploaded as \"..sResponse )\
2382 print( \"Run \\\"pastebin get \"..sCode..\"\\\" to download anywhere\" )\
2383 \
2384 else\
2385 print( \"Failed.\" )\
2386 end\
2387 \
2388elseif sCommand == \"get\" then\
2389 -- Download a file from pastebin.com\
2390 if #argv < 3 then\
2391 printUsage()\
2392 return\
2393 end\
2394 \
2395 -- Determine file to download\
2396 local sCode = argv[2]\
2397 local sFile = argv[3]\
2398 local sPath = shell.resolve( sFile )\
2399 if fs.exists( sPath ) then\
2400 print( \"File already exists\" )\
2401 return\
2402 end\
2403 \
2404 -- GET the contents from pastebin\
2405 local res = get(sCode)\
2406 if res then \
2407 local file = fs.open( sPath, \"w\" )\
2408 file.write( res )\
2409 file.close()\
2410 \
2411 print( \"Downloaded as \"..sFile )\
2412 end \
2413elseif sCommand == \"run\" then\
2414 local sCode = argv[2]\
2415 \
2416 local res = get(sCode)\
2417 if res then\
2418 local func, err = load(res, sCode, \"t\", _ENV)\
2419 if not func then\
2420 printError( err )\
2421 return\
2422 end\
2423 local success, msg = pcall(func, table.unpack(argv, 3))\
2424 if not success then\
2425 printError( msg )\
2426 end\
2427 end\
2428else\
2429 printUsage()\
2430 return\
2431end",
2432 [ "pwd.lua" ] = "print(fs.combine(_G.hlsh.sandbox,_G.hlsh.currDir))",
2433 },
2434 utils = {
2435 [ "multiproc.lua" ] = "local hidTerm = window.create(term,1,1,1,1,false)\
2436\
2437term.",
2438 },
2439 [ "startup.lua" ] = "settings.load(\"/sys/conf/main.lua\")\
2440\
2441function interp(s, tab)\
2442 return (s:gsub('($%b{})', function(w) return tab[w:sub(3, -2)] or w end))\
2443end\
2444\
2445function split(str, sep)\
2446 local array = {}\
2447 local reg = string.format(\"([^%s]+)\", sep)\
2448 for mem in string.gmatch(str, reg) do\
2449 table.insert(array, mem)\
2450 end\
2451 return array\
2452end\
2453\
2454function printArr(tab)\
2455 for _, file in ipairs(tab) do\
2456 print(file)\
2457 end\
2458end\
2459\
2460_G.hlsh = {\
2461 currDir = \"\",\
2462 sandbox = \"/sys/disks/hlsh/\",\
2463 pathEnv = {}\
2464}\
2465\
2466_G.hlsh.pathEnv = split(settings.get(\"hlsh.path\"), \";\")\
2467\
2468function runFromPath(filePath, argv)\
2469 for i, cPathEnv in ipairs(_G.hlsh.pathEnv) do\
2470 local fullPath = fs.combine(cPathEnv, filePath)\
2471 if fs.exists(fullPath) or fs.exists(fullPath .. \".lua\") then\
2472 if fs.exists(fullPath) then\
2473 os.run({argv = argv}, fullPath)\
2474 return fullPath\
2475 end\
2476 if fs.exists(fullPath .. \".lua\") then\
2477 os.run({argv = argv}, fullPath .. \".lua\")\
2478 return fullPath .. \".lua\"\
2479 end\
2480 end\
2481 end\
2482 term.setTextColor(colors.red)\
2483 print(\"Program not found.\")\
2484 term.setTextColor(colors.white)\
2485end\
2486\
2487function runFromLocal(filePath, argv)\
2488 local fullPath = fs.combine(_G.hlsh.currDir, filePath)\
2489 fullPath = fs.combine(_G.hlsh.sandbox, fullPath)\
2490 if fs.exists(fullPath) or fs.exists(fullPath .. \".lua\") then\
2491 if fs.exists(fullPath) then\
2492 os.run({argv = argv}, fullPath)\
2493 end\
2494 if fs.exists(fullPath .. \".lua\") then\
2495 os.run({argv = argv}, fullPath .. \".lua\")\
2496 end\
2497 return fullPath\
2498 else\
2499 term.setTextColor(colors.red)\
2500 print(\"Program not found.\")\
2501 term.setTextColor(colors.white)\
2502 end\
2503end\
2504\
2505while true do\
2506 local vars = {dir = _G.hlsh.currDir}\
2507 term.write(interp(settings.get(\"hlsh.prompt\"), vars))\
2508 local command = read()\
2509 -- if command ~= \"\" then\
2510 if string.sub(command, 1, 2) == \"./\" then\
2511 command = string.sub(command, 3)\
2512 local argv = split(command, \" \")\
2513 local program = table.remove(argv, 1)\
2514 runFromLocal(program, argv)\
2515 elseif command == \"\" then\
2516 print()\
2517 else\
2518 local argv = split(command, \" \")\
2519 local program = table.remove(argv, 1)\
2520 runFromPath(program, argv)\
2521 end\
2522-- end\
2523end\
2524\
2525_G.hlsh = {}",
2526}