Advertisement
DevSDK

Untitled

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