aboutsummaryrefslogtreecommitdiff
path: root/Programs/LuaIDE.bup/Contents/bits-UI/luaide.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Programs/LuaIDE.bup/Contents/bits-UI/luaide.lua')
-rw-r--r--Programs/LuaIDE.bup/Contents/bits-UI/luaide.lua2224
1 files changed, 2224 insertions, 0 deletions
diff --git a/Programs/LuaIDE.bup/Contents/bits-UI/luaide.lua b/Programs/LuaIDE.bup/Contents/bits-UI/luaide.lua
new file mode 100644
index 0000000..5e06e6b
--- /dev/null
+++ b/Programs/LuaIDE.bup/Contents/bits-UI/luaide.lua
@@ -0,0 +1,2224 @@
+
+--
+-- Lua IDE
+-- Made by GravityScore
+--
+
+
+
+
+-- Variables
+
+local version = "1.1"
+local arguments = {...}
+
+
+local w, h = term.getSize()
+local tabWidth = 2
+
+
+local autosaveInterval = 20
+local allowEditorEvent = true
+local keyboardShortcutTimeout = 0.4
+
+
+local clipboard = nil
+
+
+local theme = {
+ background = colors.gray,
+ titleBar = colors.lightGray,
+
+ top = colors.lightBlue,
+ bottom = colors.cyan,
+
+ button = colors.cyan,
+ buttonHighlighted = colors.lightBlue,
+
+ dangerButton = colors.red,
+ dangerButtonHighlighted = colors.pink,
+
+ text = colors.white,
+ folder = colors.lime,
+ readOnly = colors.red,
+}
+
+
+local languages = {}
+local currentLanguage = {}
+
+
+local updateURL = "https://raw.github.com/GravityScore/LuaIDE/master/computercraft/ide.lua"
+local ideLocation = "/" .. shell.getRunningProgram()
+local themeLocation = "/.luaide_theme"
+
+local function isAdvanced()
+ return term.isColor and term.isColor()
+end
+
+
+
+
+-- -------- Utilities
+
+local function modRead(properties)
+ local w, h = term.getSize()
+ local defaults = {replaceChar = nil, history = nil, visibleLength = nil, textLength = nil,
+ liveUpdates = nil, exitOnKey = nil}
+ if not properties then properties = {} end
+ for k, v in pairs(defaults) do if not properties[k] then properties[k] = v end end
+ if properties.replaceChar then properties.replaceChar = properties.replaceChar:sub(1, 1) end
+ if not properties.visibleLength then properties.visibleLength = w end
+
+ local sx, sy = term.getCursorPos()
+ local line = ""
+ local pos = 0
+ local historyPos = nil
+
+ local function redraw(repl)
+ local scroll = 0
+ if properties.visibleLength and sx + pos > properties.visibleLength + 1 then
+ scroll = (sx + pos) - (properties.visibleLength + 1)
+ end
+
+ term.setCursorPos(sx, sy)
+ local a = repl or properties.replaceChar
+ if a then term.write(string.rep(a, line:len() - scroll))
+ else term.write(line:sub(scroll + 1, -1)) end
+ term.setCursorPos(sx + pos - scroll, sy)
+ end
+
+ local function sendLiveUpdates(event, ...)
+ if type(properties.liveUpdates) == "function" then
+ local ox, oy = term.getCursorPos()
+ local a, data = properties.liveUpdates(line, event, ...)
+ if a == true and data == nil then
+ term.setCursorBlink(false)
+ return line
+ elseif a == true and data ~= nil then
+ term.setCursorBlink(false)
+ return data
+ end
+ term.setCursorPos(ox, oy)
+ end
+ end
+
+ term.setCursorBlink(true)
+ while true do
+ local e, but, x, y, p4, p5 = os.pullEvent()
+
+ if e == "char" then
+ local s = false
+ if properties.textLength and line:len() < properties.textLength then s = true
+ elseif not properties.textLength then s = true end
+
+ local canType = true
+ if not properties.grantPrint and properties.refusePrint then
+ local canTypeKeys = {}
+ if type(properties.refusePrint) == "table" then
+ for _, v in pairs(properties.refusePrint) do
+ table.insert(canTypeKeys, tostring(v):sub(1, 1))
+ end
+ elseif type(properties.refusePrint) == "string" then
+ for char in properties.refusePrint:gmatch(".") do
+ table.insert(canTypeKeys, char)
+ end
+ end
+ for _, v in pairs(canTypeKeys) do if but == v then canType = false end end
+ elseif properties.grantPrint then
+ canType = false
+ local canTypeKeys = {}
+ if type(properties.grantPrint) == "table" then
+ for _, v in pairs(properties.grantPrint) do
+ table.insert(canTypeKeys, tostring(v):sub(1, 1))
+ end
+ elseif type(properties.grantPrint) == "string" then
+ for char in properties.grantPrint:gmatch(".") do
+ table.insert(canTypeKeys, char)
+ end
+ end
+ for _, v in pairs(canTypeKeys) do if but == v then canType = true end end
+ end
+
+ if s and canType then
+ line = line:sub(1, pos) .. but .. line:sub(pos + 1, -1)
+ pos = pos + 1
+ redraw()
+ end
+ elseif e == "key" then
+ if but == keys.enter then break
+ elseif but == keys.left then if pos > 0 then pos = pos - 1 redraw() end
+ elseif but == keys.right then if pos < line:len() then pos = pos + 1 redraw() end
+ elseif (but == keys.up or but == keys.down) and properties.history then
+ redraw(" ")
+ if but == keys.up then
+ if historyPos == nil and #properties.history > 0 then
+ historyPos = #properties.history
+ elseif historyPos > 1 then
+ historyPos = historyPos - 1
+ end
+ elseif but == keys.down then
+ if historyPos == #properties.history then historyPos = nil
+ elseif historyPos ~= nil then historyPos = historyPos + 1 end
+ end
+
+ if properties.history and historyPos then
+ line = properties.history[historyPos]
+ pos = line:len()
+ else
+ line = ""
+ pos = 0
+ end
+
+ redraw()
+ local a = sendLiveUpdates("history")
+ if a then return a end
+ elseif but == keys.backspace and pos > 0 then
+ redraw(" ")
+ line = line:sub(1, pos - 1) .. line:sub(pos + 1, -1)
+ pos = pos - 1
+ redraw()
+ local a = sendLiveUpdates("delete")
+ if a then return a end
+ elseif but == keys.home then
+ pos = 0
+ redraw()
+ elseif but == keys.delete and pos < line:len() then
+ redraw(" ")
+ line = line:sub(1, pos) .. line:sub(pos + 2, -1)
+ redraw()
+ local a = sendLiveUpdates("delete")
+ if a then return a end
+ elseif but == keys["end"] then
+ pos = line:len()
+ redraw()
+ elseif properties.exitOnKey then
+ if but == properties.exitOnKey or (properties.exitOnKey == "control" and
+ (but == 29 or but == 157)) then
+ term.setCursorBlink(false)
+ return nil
+ end
+ end
+ end
+ local a = sendLiveUpdates(e, but, x, y, p4, p5)
+ if a then return a end
+ end
+
+ term.setCursorBlink(false)
+ if line ~= nil then line = line:gsub("^%s*(.-)%s*$", "%1") end
+ return line
+end
+
+
+-- -------- Themes
+
+local defaultTheme = {
+ background = "gray",
+ backgroundHighlight = "lightGray",
+ prompt = "cyan",
+ promptHighlight = "lightBlue",
+ err = "red",
+ errHighlight = "pink",
+
+ editorBackground = "gray",
+ editorLineHightlight = "lightBlue",
+ editorLineNumbers = "gray",
+ editorLineNumbersHighlight = "lightGray",
+ editorError = "pink",
+ editorErrorHighlight = "red",
+
+ textColor = "white",
+ conditional = "yellow",
+ constant = "orange",
+ ["function"] = "magenta",
+ string = "red",
+ comment = "lime"
+}
+
+local normalTheme = {
+ background = "black",
+ backgroundHighlight = "black",
+ prompt = "black",
+ promptHighlight = "black",
+ err = "black",
+ errHighlight = "black",
+
+ editorBackground = "black",
+ editorLineHightlight = "black",
+ editorLineNumbers = "black",
+ editorLineNumbersHighlight = "white",
+ editorError = "black",
+ editorErrorHighlight = "black",
+
+ textColor = "white",
+ conditional = "white",
+ constant = "white",
+ ["function"] = "white",
+ string = "white",
+ comment = "white"
+}
+
+local availableThemes = {
+ {"Water (Default)", "https://raw.github.com/GravityScore/LuaIDE/master/themes/default.txt"},
+ {"Fire", "https://raw.github.com/GravityScore/LuaIDE/master/themes/fire.txt"},
+ {"Sublime Text 2", "https://raw.github.com/GravityScore/LuaIDE/master/themes/st2.txt"},
+ {"Midnight", "https://raw.github.com/GravityScore/LuaIDE/master/themes/midnight.txt"},
+ {"TheOriginalBIT", "https://raw.github.com/GravityScore/LuaIDE/master/themes/bit.txt"},
+ {"Superaxander", "https://raw.github.com/GravityScore/LuaIDE/master/themes/superaxander.txt"},
+ {"Forest", "https://raw.github.com/GravityScore/LuaIDE/master/themes/forest.txt"},
+ {"Night", "https://raw.github.com/GravityScore/LuaIDE/master/themes/night.txt"},
+ {"Original", "https://raw.github.com/GravityScore/LuaIDE/master/themes/original.txt"},
+}
+
+local function loadTheme(path)
+ local f = io.open(path)
+ local l = f:read("*l")
+ local config = {}
+ while l ~= nil do
+ local k, v = string.match(l, "^(%a+)=(%a+)")
+ if k and v then config[k] = v end
+ l = f:read("*l")
+ end
+ f:close()
+ return config
+end
+
+-- Load Theme
+if isAdvanced() then theme = defaultTheme
+else theme = normalTheme end
+
+
+-- -------- Drawing
+
+local function centerPrint(text, ny)
+ if type(text) == "table" then for _, v in pairs(text) do centerPrint(v) end
+ else
+ local x, y = term.getCursorPos()
+ local w, h = term.getSize()
+ term.setCursorPos(w/2 - text:len()/2 + (#text % 2 == 0 and 1 or 0), ny or y)
+ print(text)
+ end
+end
+
+local function title(t)
+ term.setTextColor(colors[theme.textColor])
+ term.setBackgroundColor(colors[theme.background])
+ term.clear()
+
+ term.setBackgroundColor(colors[theme.backgroundHighlight])
+ for i = 2, 4 do term.setCursorPos(1, i) term.clearLine() end
+ term.setCursorPos(3, 3)
+ term.write(t)
+end
+
+local function centerRead(wid, begt)
+ local function liveUpdate(line, e, but, x, y, p4, p5)
+ if isAdvanced() and e == "mouse_click" and x >= w/2 - wid/2 and x <= w/2 - wid/2 + 10
+ and y >= 13 and y <= 15 then
+ return true, ""
+ end
+ end
+
+ if not begt then begt = "" end
+ term.setTextColor(colors[theme.textColor])
+ term.setBackgroundColor(colors[theme.promptHighlight])
+ for i = 8, 10 do
+ term.setCursorPos(w/2 - wid/2, i)
+ term.write(string.rep(" ", wid))
+ end
+
+ if isAdvanced() then
+ term.setBackgroundColor(colors[theme.errHighlight])
+ for i = 13, 15 do
+ term.setCursorPos(w/2 - wid/2 + 1, i)
+ term.write(string.rep(" ", 10))
+ end
+ term.setCursorPos(w/2 - wid/2 + 2, 14)
+ term.write("> Cancel")
+ end
+
+ term.setBackgroundColor(colors[theme.promptHighlight])
+ term.setCursorPos(w/2 - wid/2 + 1, 9)
+ term.write("> " .. begt)
+ return modRead({visibleLength = w/2 + wid/2, liveUpdates = liveUpdate})
+end
+
+
+-- -------- Prompt
+
+local function prompt(list, dir, isGrid)
+ local function draw(sel)
+ for i, v in ipairs(list) do
+ if i == sel then term.setBackgroundColor(v.highlight or colors[theme.promptHighlight])
+ else term.setBackgroundColor(v.bg or colors[theme.prompt]) end
+ term.setTextColor(v.tc or colors[theme.textColor])
+ for i = -1, 1 do
+ term.setCursorPos(v[2], v[3] + i)
+ term.write(string.rep(" ", v[1]:len() + 4))
+ end
+
+ term.setCursorPos(v[2], v[3])
+ if i == sel then
+ term.setBackgroundColor(v.highlight or colors[theme.promptHighlight])
+ term.write(" > ")
+ else term.write(" - ") end
+ term.write(v[1] .. " ")
+ end
+ end
+
+ local key1 = dir == "horizontal" and 203 or 200
+ local key2 = dir == "horizontal" and 205 or 208
+ local sel = 1
+ draw(sel)
+
+ while true do
+ local e, but, x, y = os.pullEvent()
+ if e == "key" and but == 28 then
+ return list[sel][1]
+ elseif e == "key" and but == key1 and sel > 1 then
+ sel = sel - 1
+ draw(sel)
+ elseif e == "key" and but == key2 and ((err == true and sel < #list - 1) or (sel < #list)) then
+ sel = sel + 1
+ draw(sel)
+ elseif isGrid and e == "key" and but == 203 and sel > 2 then
+ sel = sel - 2
+ draw(sel)
+ elseif isGrid and e == "key" and but == 205 and sel < 3 then
+ sel = sel + 2
+ draw(sel)
+ elseif e == "mouse_click" then
+ for i, v in ipairs(list) do
+ if x >= v[2] - 1 and x <= v[2] + v[1]:len() + 3 and y >= v[3] - 1 and y <= v[3] + 1 then
+ return list[i][1]
+ end
+ end
+ end
+ end
+end
+
+local function scrollingPrompt(list)
+ local function draw(items, sel, loc)
+ for i, v in ipairs(items) do
+ local bg = colors[theme.prompt]
+ local bghigh = colors[theme.promptHighlight]
+ if v:find("Back") or v:find("Return") then
+ bg = colors[theme.err]
+ bghigh = colors[theme.errHighlight]
+ end
+
+ if i == sel then term.setBackgroundColor(bghigh)
+ else term.setBackgroundColor(bg) end
+ term.setTextColor(colors[theme.textColor])
+ for x = -1, 1 do
+ term.setCursorPos(3, (i * 4) + x + 4)
+ term.write(string.rep(" ", w - 13))
+ end
+
+ term.setCursorPos(3, i * 4 + 4)
+ if i == sel then
+ term.setBackgroundColor(bghigh)
+ term.write(" > ")
+ else term.write(" - ") end
+ term.write(v .. " ")
+ end
+ end
+
+ local function updateDisplayList(items, loc, len)
+ local ret = {}
+ for i = 1, len do
+ local item = items[i + loc - 1]
+ if item then table.insert(ret, item) end
+ end
+ return ret
+ end
+
+ -- Variables
+ local sel = 1
+ local loc = 1
+ local len = 3
+ local disList = updateDisplayList(list, loc, len)
+ draw(disList, sel, loc)
+
+ -- Loop
+ while true do
+ local e, key, x, y = os.pullEvent()
+
+ if e == "mouse_click" then
+ for i, v in ipairs(disList) do
+ if x >= 3 and x <= w - 11 and y >= i * 4 + 3 and y <= i * 4 + 5 then return v end
+ end
+ elseif e == "key" and key == 200 then
+ if sel > 1 then
+ sel = sel - 1
+ draw(disList, sel, loc)
+ elseif loc > 1 then
+ loc = loc - 1
+ disList = updateDisplayList(list, loc, len)
+ draw(disList, sel, loc)
+ end
+ elseif e == "key" and key == 208 then
+ if sel < len then
+ sel = sel + 1
+ draw(disList, sel, loc)
+ elseif loc + len - 1 < #list then
+ loc = loc + 1
+ disList = updateDisplayList(list, loc, len)
+ draw(disList, sel, loc)
+ end
+ elseif e == "mouse_scroll" then
+ os.queueEvent("key", key == -1 and 200 or 208)
+ elseif e == "key" and key == 28 then
+ return disList[sel]
+ end
+ end
+end
+
+function monitorKeyboardShortcuts()
+ local ta, tb = nil, nil
+ local allowChar = false
+ local shiftPressed = false
+ while true do
+ local event, char = os.pullEvent()
+ if event == "key" and (char == 42 or char == 52) then
+ shiftPressed = true
+ tb = os.startTimer(keyboardShortcutTimeout)
+ elseif event == "key" and (char == 29 or char == 157 or char == 219 or char == 220) then
+ allowEditorEvent = false
+ allowChar = true
+ ta = os.startTimer(keyboardShortcutTimeout)
+ elseif event == "key" and allowChar then
+ local name = nil
+ for k, v in pairs(keys) do
+ if v == char then
+ if shiftPressed then os.queueEvent("shortcut", "ctrl shift", k:lower())
+ else os.queueEvent("shortcut", "ctrl", k:lower()) end
+ sleep(0.005)
+ allowEditorEvent = true
+ end
+ end
+ if shiftPressed then os.queueEvent("shortcut", "ctrl shift", char)
+ else os.queueEvent("shortcut", "ctrl", char) end
+ elseif event == "timer" and char == ta then
+ allowEditorEvent = true
+ allowChar = false
+ elseif event == "timer" and char == tb then
+ shiftPressed = false
+ end
+ end
+end
+
+
+-- -------- Saving and Loading
+
+local function download(url, path)
+ for i = 1, 3 do
+ local response = http.get(url)
+ if response then
+ local data = response.readAll()
+ response.close()
+ if path then
+ local f = io.open(path, "w")
+ f:write(data)
+ f:close()
+ end
+ return true
+ end
+ end
+
+ return false
+end
+
+local function saveFile(path, lines)
+ local dir = path:sub(1, path:len() - fs.getName(path):len())
+ if not fs.exists(dir) then fs.makeDir(dir) end
+ if not fs.isDir(path) and not fs.isReadOnly(path) then
+ local a = ""
+ for _, v in pairs(lines) do a = a .. v .. "\n" end
+
+ local f = io.open(path, "w")
+ f:write(a)
+ f:close()
+ return true
+ else return false end
+end
+
+local function loadFile(path)
+ if not fs.exists(path) then
+ local dir = path:sub(1, path:len() - fs.getName(path):len())
+ if not fs.exists(dir) then fs.makeDir(dir) end
+ local f = io.open(path, "w")
+ f:write("")
+ f:close()
+ end
+
+ local l = {}
+ if fs.exists(path) and not fs.isDir(path) then
+ local f = io.open(path, "r")
+ if f then
+ local a = f:read("*l")
+ while a do
+ table.insert(l, a)
+ a = f:read("*l")
+ end
+ f:close()
+ end
+ else return nil end
+
+ if #l < 1 then table.insert(l, "") end
+ return l
+end
+
+
+-- -------- Languages
+
+languages.lua = {}
+languages.brainfuck = {}
+languages.none = {}
+
+-- Lua
+
+languages.lua.helpTips = {
+ "A function you tried to call doesn't exist.",
+ "You made a typo.",
+ "The index of an array is nil.",
+ "The wrong variable type was passed.",
+ "A function/variable doesn't exist.",
+ "You missed an 'end'.",
+ "You missed a 'then'.",
+ "You declared a variable incorrectly.",
+ "One of your variables is mysteriously nil."
+}
+
+languages.lua.defaultHelpTips = {
+ 2, 5
+}
+
+languages.lua.errors = {
+ ["Attempt to call nil."] = {1, 2},
+ ["Attempt to index nil."] = {3, 2},
+ [".+ expected, got .+"] = {4, 2, 9},
+ ["'end' expected"] = {6, 2},
+ ["'then' expected"] = {7, 2},
+ ["'=' expected"] = {8, 2}
+}
+
+languages.lua.keywords = {
+ ["and"] = "conditional",
+ ["break"] = "conditional",
+ ["do"] = "conditional",
+ ["else"] = "conditional",
+ ["elseif"] = "conditional",
+ ["end"] = "conditional",
+ ["for"] = "conditional",
+ ["function"] = "conditional",
+ ["if"] = "conditional",
+ ["in"] = "conditional",
+ ["local"] = "conditional",
+ ["not"] = "conditional",
+ ["or"] = "conditional",
+ ["repeat"] = "conditional",
+ ["return"] = "conditional",
+ ["then"] = "conditional",
+ ["until"] = "conditional",
+ ["while"] = "conditional",
+
+ ["true"] = "constant",
+ ["false"] = "constant",
+ ["nil"] = "constant",
+
+ ["print"] = "function",
+ ["write"] = "function",
+ ["sleep"] = "function",
+ ["pairs"] = "function",
+ ["ipairs"] = "function",
+ ["loadstring"] = "function",
+ ["loadfile"] = "function",
+ ["dofile"] = "function",
+ ["rawset"] = "function",
+ ["rawget"] = "function",
+ ["setfenv"] = "function",
+ ["getfenv"] = "function",
+}
+
+languages.lua.parseError = function(e)
+ local ret = {filename = "unknown", line = -1, display = "Unknown!", err = ""}
+ if e and e ~= "" then
+ ret.err = e
+ if e:find(":") then
+ ret.filename = e:sub(1, e:find(":") - 1):gsub("^%s*(.-)%s*$", "%1")
+ -- The "" is needed to circumvent a CC bug
+ e = (e:sub(e:find(":") + 1) .. ""):gsub("^%s*(.-)%s*$", "%1")
+ if e:find(":") then
+ ret.line = e:sub(1, e:find(":") - 1)
+ e = e:sub(e:find(":") + 2):gsub("^%s*(.-)%s*$", "%1") .. ""
+ end
+ end
+ ret.display = e:sub(1, 1):upper() .. e:sub(2, -1) .. "."
+ end
+
+ return ret
+end
+
+languages.lua.getCompilerErrors = function(code)
+ code = "local function ee65da6af1cb6f63fee9a081246f2fd92b36ef2(...)\n\n" .. code .. "\n\nend"
+ local fn, err = loadstring(code)
+ if not err then
+ local _, e = pcall(fn)
+ if e then err = e end
+ end
+
+ if err then
+ local a = err:find("]", 1, true)
+ if a then err = "string" .. err:sub(a + 1, -1) end
+ local ret = languages.lua.parseError(err)
+ if tonumber(ret.line) then ret.line = tonumber(ret.line) end
+ return ret
+ else return languages.lua.parseError(nil) end
+end
+
+languages.lua.run = function(path, ar)
+ local fn, err = loadfile(path)
+ setfenv(fn, getfenv())
+ if not err then
+ _, err = pcall(function() fn(unpack(ar)) end)
+ end
+ return err
+end
+
+
+-- Brainfuck
+
+languages.brainfuck.helpTips = {
+ "Well idk...",
+ "Isn't this the whole point of the language?",
+ "Ya know... Not being able to debug it?",
+ "You made a typo."
+}
+
+languages.brainfuck.defaultHelpTips = {
+ 1, 2, 3
+}
+
+languages.brainfuck.errors = {
+ ["No matching '['"] = {1, 2, 3, 4}
+}
+
+languages.brainfuck.keywords = {}
+
+languages.brainfuck.parseError = function(e)
+ local ret = {filename = "unknown", line = -1, display = "Unknown!", err = ""}
+ if e and e ~= "" then
+ ret.err = e
+ ret.line = e:sub(1, e:find(":") - 1)
+ e = e:sub(e:find(":") + 2):gsub("^%s*(.-)%s*$", "%1") .. ""
+ ret.display = e:sub(1, 1):upper() .. e:sub(2, -1) .. "."
+ end
+
+ return ret
+end
+
+languages.brainfuck.mapLoops = function(code)
+ -- Map loops
+ local loopLocations = {}
+ local loc = 1
+ local line = 1
+ for let in string.gmatch(code, ".") do
+ if let == "[" then
+ loopLocations[loc] = true
+ elseif let == "]" then
+ local found = false
+ for i = loc, 1, -1 do
+ if loopLocations[i] == true then
+ loopLocations[i] = loc
+ found = true
+ end
+ end
+
+ if not found then
+ return line .. ": No matching '['"
+ end
+ end
+
+ if let == "\n" then line = line + 1 end
+ loc = loc + 1
+ end
+ return loopLocations
+end
+
+languages.brainfuck.getCompilerErrors = function(code)
+ local a = languages.brainfuck.mapLoops(code)
+ if type(a) == "string" then return languages.brainfuck.parseError(a)
+ else return languages.brainfuck.parseError(nil) end
+end
+
+languages.brainfuck.run = function(path)
+ -- Read from file
+ local f = io.open(path, "r")
+ local content = f:read("*a")
+ f:close()
+
+ -- Define environment
+ local dataCells = {}
+ local dataPointer = 1
+ local instructionPointer = 1
+
+ -- Map loops
+ local loopLocations = languages.brainfuck.mapLoops(content)
+ if type(loopLocations) == "string" then return loopLocations end
+
+ -- Execute code
+ while true do
+ local let = content:sub(instructionPointer, instructionPointer)
+
+ if let == ">" then
+ dataPointer = dataPointer + 1
+ if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
+ elseif let == "<" then
+ if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
+ dataPointer = dataPointer - 1
+ if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
+ elseif let == "+" then
+ if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
+ dataCells[tostring(dataPointer)] = dataCells[tostring(dataPointer)] + 1
+ elseif let == "-" then
+ if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
+ dataCells[tostring(dataPointer)] = dataCells[tostring(dataPointer)] - 1
+ elseif let == "." then
+ if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
+ if term.getCursorPos() >= w then print("") end
+ write(string.char(math.max(1, dataCells[tostring(dataPointer)])))
+ elseif let == "," then
+ if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
+ term.setCursorBlink(true)
+ local e, but = os.pullEvent("char")
+ term.setCursorBlink(false)
+ dataCells[tostring(dataPointer)] = string.byte(but)
+ if term.getCursorPos() >= w then print("") end
+ write(but)
+ elseif let == "/" then
+ if not dataCells[tostring(dataPointer)] then dataCells[tostring(dataPointer)] = 0 end
+ if term.getCursorPos() >= w then print("") end
+ write(dataCells[tostring(dataPointer)])
+ elseif let == "[" then
+ if dataCells[tostring(dataPointer)] == 0 then
+ for k, v in pairs(loopLocations) do
+ if k == instructionPointer then instructionPointer = v end
+ end
+ end
+ elseif let == "]" then
+ for k, v in pairs(loopLocations) do
+ if v == instructionPointer then instructionPointer = k - 1 end
+ end
+ end
+
+ instructionPointer = instructionPointer + 1
+ if instructionPointer > content:len() then print("") break end
+ end
+end
+
+-- None
+
+languages.none.helpTips = {}
+languages.none.defaultHelpTips = {}
+languages.none.errors = {}
+languages.none.keywords = {}
+
+languages.none.parseError = function(err)
+ return {filename = "", line = -1, display = "", err = ""}
+end
+
+languages.none.getCompilerErrors = function(code)
+ return languages.none.parseError(nil)
+end
+
+languages.none.run = function(path) end
+
+
+-- Load language
+currentLanguage = languages.lua
+
+
+-- -------- Run GUI
+
+local function viewErrorHelp(e)
+ title("LuaIDE - Error Help")
+
+ local tips = nil
+ for k, v in pairs(currentLanguage.errors) do
+ if e.display:find(k) then tips = v break end
+ end
+
+ term.setBackgroundColor(colors[theme.err])
+ for i = 6, 8 do
+ term.setCursorPos(5, i)
+ term.write(string.rep(" ", 35))
+ end
+
+ term.setBackgroundColor(colors[theme.prompt])
+ for i = 10, 18 do
+ term.setCursorPos(5, i)
+ term.write(string.rep(" ", 46))
+ end
+
+ if tips then
+ term.setBackgroundColor(colors[theme.err])
+ term.setCursorPos(6, 7)
+ term.write("Error Help")
+
+ term.setBackgroundColor(colors[theme.prompt])
+ for i, v in ipairs(tips) do
+ term.setCursorPos(7, i + 10)
+ term.write("- " .. currentLanguage.helpTips[v])
+ end
+ else
+ term.setBackgroundColor(colors[theme.err])
+ term.setCursorPos(6, 7)
+ term.write("No Error Tips Available!")
+
+ term.setBackgroundColor(colors[theme.prompt])
+ term.setCursorPos(6, 11)
+ term.write("There are no error tips available, but")
+ term.setCursorPos(6, 12)
+ term.write("you could see if it was any of these:")
+
+ for i, v in ipairs(currentLanguage.defaultHelpTips) do
+ term.setCursorPos(7, i + 12)
+ term.write("- " .. currentLanguage.helpTips[v])
+ end
+ end
+
+ prompt({{"Back", w - 8, 7}}, "horizontal")
+end
+
+local function run(path, lines, useArgs)
+ local ar = {}
+ if useArgs then
+ title("LuaIDE - Run " .. fs.getName(path))
+ local s = centerRead(w - 13, fs.getName(path) .. " ")
+ for m in string.gmatch(s, "[^ \t]+") do ar[#ar + 1] = m:gsub("^%s*(.-)%s*$", "%1") end
+ end
+
+ saveFile(path, lines)
+ term.setCursorBlink(false)
+ term.setBackgroundColor(colors.black)
+ term.setTextColor(colors.white)
+ term.clear()
+ term.setCursorPos(1, 1)
+ local err = currentLanguage.run(path, ar)
+
+ term.setBackgroundColor(colors.black)
+ print("\n")
+ if err then
+ if isAdvanced() then term.setTextColor(colors.red) end
+ centerPrint("The program has crashed!")
+ end
+ term.setTextColor(colors.white)
+ centerPrint("Press any key to return to LuaIDE...")
+ while true do
+ local e = os.pullEvent()
+ if e == "key" then break end
+ end
+
+ -- To prevent key from showing up in editor
+ os.queueEvent("")
+ os.pullEvent()
+
+ if err then
+ if currentLanguage == languages.lua and err:find("]") then
+ err = fs.getName(path) .. err:sub(err:find("]", 1, true) + 1, -1)
+ end
+
+ while true do
+ title("LuaIDE - Error!")
+
+ term.setBackgroundColor(colors[theme.err])
+ for i = 6, 8 do
+ term.setCursorPos(3, i)
+ term.write(string.rep(" ", w - 5))
+ end
+ term.setCursorPos(4, 7)
+ term.write("The program has crashed!")
+
+ term.setBackgroundColor(colors[theme.prompt])
+ for i = 10, 14 do
+ term.setCursorPos(3, i)
+ term.write(string.rep(" ", w - 5))
+ end
+
+ local formattedErr = currentLanguage.parseError(err)
+ term.setCursorPos(4, 11)
+ term.write("Line: " .. formattedErr.line)
+ term.setCursorPos(4, 12)
+ term.write("Error:")
+ term.setCursorPos(5, 13)
+
+ local a = formattedErr.display
+ local b = nil
+ if a:len() > w - 8 then
+ for i = a:len(), 1, -1 do
+ if a:sub(i, i) == " " then
+ b = a:sub(i + 1, -1)
+ a = a:sub(1, i)
+ break
+ end
+ end
+ end
+
+ term.write(a)
+ if b then
+ term.setCursorPos(5, 14)
+ term.write(b)
+ end
+
+ local opt = prompt({{"Error Help", w/2 - 15, 17}, {"Go To Line", w/2 + 2, 17}},
+ "horizontal")
+ if opt == "Error Help" then
+ viewErrorHelp(formattedErr)
+ elseif opt == "Go To Line" then
+ -- To prevent key from showing up in editor
+ os.queueEvent("")
+ os.pullEvent()
+
+ return "go to", tonumber(formattedErr.line)
+ end
+ end
+ end
+end
+
+
+-- -------- Functions
+
+local function goto()
+ term.setBackgroundColor(colors[theme.backgroundHighlight])
+ term.setCursorPos(2, 1)
+ term.clearLine()
+ term.write("Line: ")
+ local line = modRead({visibleLength = w - 2})
+
+ local num = tonumber(line)
+ if num and num > 0 then return num
+ else
+ term.setCursorPos(2, 1)
+ term.clearLine()
+ term.write("Not a line number!")
+ sleep(1.6)
+ return nil
+ end
+end
+
+local function setsyntax()
+ local opts = {
+ "[Lua] Brainfuck None ",
+ " Lua [Brainfuck] None ",
+ " Lua Brainfuck [None]"
+ }
+ local sel = 1
+
+ term.setCursorBlink(false)
+ term.setBackgroundColor(colors[theme.backgroundHighlight])
+ term.setCursorPos(2, 1)
+ term.clearLine()
+ term.write(opts[sel])
+ while true do
+ local e, but, x, y = os.pullEvent("key")
+ if but == 203 then
+ sel = math.max(1, sel - 1)
+ term.setCursorPos(2, 1)
+ term.clearLine()
+ term.write(opts[sel])
+ elseif but == 205 then
+ sel = math.min(#opts, sel + 1)
+ term.setCursorPos(2, 1)
+ term.clearLine()
+ term.write(opts[sel])
+ elseif but == 28 then
+ if sel == 1 then currentLanguage = languages.lua
+ elseif sel == 2 then currentLanguage = languages.brainfuck
+ elseif sel == 3 then currentLanguage = languages.none end
+ term.setCursorBlink(true)
+ return
+ end
+ end
+end
+
+
+-- -------- Re-Indenting
+
+local tabWidth = 2
+
+local comments = {}
+local strings = {}
+
+local increment = {
+ "if%s+.+%s+then%s*$",
+ "for%s+.+%s+do%s*$",
+ "while%s+.+%s+do%s*$",
+ "repeat%s*$",
+ "function%s+[a-zA-Z_0-9]\(.*\)%s*$"
+}
+
+local decrement = {
+ "end",
+ "until%s+.+"
+}
+
+local special = {
+ "else%s*$",
+ "elseif%s+.+%s+then%s*$"
+}
+
+local function check(func)
+ for _, v in pairs(func) do
+ local cLineStart = v["lineStart"]
+ local cLineEnd = v["lineEnd"]
+ local cCharStart = v["charStart"]
+ local cCharEnd = v["charEnd"]
+
+ if line >= cLineStart and line <= cLineEnd then
+ if line == cLineStart then return cCharStart < charNumb
+ elseif line == cLineEnd then return cCharEnd > charNumb
+ else return true end
+ end
+ end
+end
+
+local function isIn(line, loc)
+ if check(comments) then return true end
+ if check(strings) then return true end
+ return false
+end
+
+local function setComment(ls, le, cs, ce)
+ comments[#comments + 1] = {}
+ comments[#comments].lineStart = ls
+ comments[#comments].lineEnd = le
+ comments[#comments].charStart = cs
+ comments[#comments].charEnd = ce
+end
+
+local function setString(ls, le, cs, ce)
+ strings[#strings + 1] = {}
+ strings[#strings].lineStart = ls
+ strings[#strings].lineEnd = le
+ strings[#strings].charStart = cs
+ strings[#strings].charEnd = ce
+end
+
+local function map(contents)
+ local inCom = false
+ local inStr = false
+
+ for i = 1, #contents do
+ if content[i]:find("%-%-%[%[") and not inStr and not inCom then
+ local cStart = content[i]:find("%-%-%[%[")
+ setComment(i, nil, cStart, nil)
+ inCom = true
+ elseif content[i]:find("%-%-%[=%[") and not inStr and not inCom then
+ local cStart = content[i]:find("%-%-%[=%[")
+ setComment(i, nil, cStart, nil)
+ inCom = true
+ elseif content[i]:find("%[%[") and not inStr and not inCom then
+ local cStart = content[i]:find("%[%[")
+ setString(i, nil, cStart, nil)
+ inStr = true
+ elseif content[i]:find("%[=%[") and not inStr and not inCom then
+ local cStart = content[i]:find("%[=%[")
+ setString(i, nil, cStart, nil)
+ inStr = true
+ end
+
+ if content[i]:find("%]%]") and inStr and not inCom then
+ local cStart, cEnd = content[i]:find("%]%]")
+ strings[#strings].lineEnd = i
+ strings[#strings].charEnd = cEnd
+ inStr = false
+ elseif content[i]:find("%]=%]") and inStr and not inCom then
+ local cStart, cEnd = content[i]:find("%]=%]")
+ strings[#strings].lineEnd = i
+ strings[#strings].charEnd = cEnd
+ inStr = false
+ end
+
+ if content[i]:find("%]%]") and not inStr and inCom then
+ local cStart, cEnd = content[i]:find("%]%]")
+ comments[#comments].lineEnd = i
+ comments[#comments].charEnd = cEnd
+ inCom = false
+ elseif content[i]:find("%]=%]") and not inStr and inCom then
+ local cStart, cEnd = content[i]:find("%]=%]")
+ comments[#comments].lineEnd = i
+ comments[#comments].charEnd = cEnd
+ inCom = false
+ end
+
+ if content[i]:find("%-%-") and not inStr and not inCom then
+ local cStart = content[i]:find("%-%-")
+ setComment(i, i, cStart, -1)
+ elseif content[i]:find("'") and not inStr and not inCom then
+ local cStart, cEnd = content[i]:find("'")
+ local nextChar = content[i]:sub(cEnd + 1, string.len(content[i]))
+ local _, cEnd = nextChar:find("'")
+ setString(i, i, cStart, cEnd)
+ elseif content[i]:find('"') and not inStr and not inCom then
+ local cStart, cEnd = content[i]:find('"')
+ local nextChar = content[i]:sub(cEnd + 1, string.len(content[i]))
+ local _, cEnd = nextChar:find('"')
+ setString(i, i, cStart, cEnd)
+ end
+ end
+end
+
+local function reindent(contents)
+ local err = nil
+ if currentLanguage ~= languages.lua then
+ err = "Cannot indent languages other than Lua!"
+ elseif currentLanguage.getCompilerErrors(table.concat(contents, "\n")).line ~= -1 then
+ err = "Cannot indent a program with errors!"
+ end
+
+ if err then
+ term.setCursorBlink(false)
+ term.setCursorPos(2, 1)
+ term.setBackgroundColor(colors[theme.backgroundHighlight])
+ term.clearLine()
+ term.write(err)
+ sleep(1.6)
+ return contents
+ end
+
+ local new = {}
+ local level = 0
+ for k, v in pairs(contents) do
+ local incrLevel = false
+ local foundIncr = false
+ for _, incr in pairs(increment) do
+ if v:find(incr) and not isIn(k, v:find(incr)) then
+ incrLevel = true
+ end
+ if v:find(incr:sub(1, -2)) and not isIn(k, v:find(incr)) then
+ foundIncr = true
+ end
+ end
+
+ local decrLevel = false
+ if not incrLevel then
+ for _, decr in pairs(decrement) do
+ if v:find(decr) and not isIn(k, v:find(decr)) and not foundIncr then
+ level = math.max(0, level - 1)
+ decrLevel = true
+ end
+ end
+ end
+
+ if not decrLevel then
+ for _, sp in pairs(special) do
+ if v:find(sp) and not isIn(k, v:find(sp)) then
+ incrLevel = true
+ level = math.max(0, level - 1)
+ end
+ end
+ end
+
+ new[k] = string.rep(" ", level * tabWidth) .. v
+ if incrLevel then level = level + 1 end
+ end
+
+ return new
+end
+
+
+-- -------- Menu
+
+local menu = {
+ [1] = {"File",
+-- "About",
+-- "Settings",
+-- "",
+ "New File ^+N",
+ "Open File ^+O",
+ "Save File ^+S",
+ "Close ^+W",
+ "Print ^+P",
+ "Quit ^+Q"
+ }, [2] = {"Edit",
+ "Cut Line ^+X",
+ "Copy Line ^+C",
+ "Paste Line ^+V",
+ "Delete Line",
+ "Clear Line"
+ }, [3] = {"Functions",
+ "Go To Line ^+G",
+ "Re-Indent ^+I",
+ "Set Syntax ^+E",
+ "Start of Line ^+<",
+ "End of Line ^+>"
+ }, [4] = {"Run",
+ "Run Program ^+R",
+ "Run w/ Args ^+Shift+R"
+ }
+}
+
+local shortcuts = {
+ -- File
+ ["ctrl n"] = "New File ^+N",
+ ["ctrl o"] = "Open File ^+O",
+ ["ctrl s"] = "Save File ^+S",
+ ["ctrl w"] = "Close ^+W",
+ ["ctrl p"] = "Print ^+P",
+ ["ctrl q"] = "Quit ^+Q",
+
+ -- Edit
+ ["ctrl x"] = "Cut Line ^+X",
+ ["ctrl c"] = "Copy Line ^+C",
+ ["ctrl v"] = "Paste Line ^+V",
+
+ -- Functions
+ ["ctrl g"] = "Go To Line ^+G",
+ ["ctrl i"] = "Re-Indent ^+I",
+ ["ctrl e"] = "Set Syntax ^+E",
+ ["ctrl 203"] = "Start of Line ^+<",
+ ["ctrl 205"] = "End of Line ^+>",
+
+ -- Run
+ ["ctrl r"] = "Run Program ^+R",
+ ["ctrl shift r"] = "Run w/ Args ^+Shift+R"
+}
+
+local menuFunctions = {
+ -- File
+-- ["About"] = function() end,
+-- ["Settings"] = function() end,
+ ["New File ^+N"] = function(path, lines) saveFile(path, lines) return "new" end,
+ ["Open File ^+O"] = function(path, lines) saveFile(path, lines) return "open" end,
+ ["Save File ^+S"] = function(path, lines) saveFile(path, lines) end,
+ ["Close ^+W"] = function(path, lines) saveFile(path, lines) return "menu" end,
+ ["Print ^+P"] = function(path, lines) saveFile(path, lines) return nil end,
+ ["Quit ^+Q"] = function(path, lines) saveFile(path, lines) return "exit" end,
+
+ -- Edit
+ ["Cut Line ^+X"] = function(path, lines, y)
+ clipboard = lines[y] table.remove(lines, y) return nil, lines end,
+ ["Copy Line ^+C"] = function(path, lines, y) clipboard = lines[y] end,
+ ["Paste Line ^+V"] = function(path, lines, y)
+ if clipboard then table.insert(lines, y, clipboard) end return nil, lines end,
+ ["Delete Line"] = function(path, lines, y) table.remove(lines, y) return nil, lines end,
+ ["Clear Line"] = function(path, lines, y) lines[y] = "" return nil, lines, "cursor" end,
+
+ -- Functions
+ ["Go To Line ^+G"] = function() return nil, "go to", goto() end,
+ ["Re-Indent ^+I"] = function(path, lines)
+ local a = reindent(lines) saveFile(path, lines) return nil, a
+ end,
+ ["Set Syntax ^+E"] = function(path, lines)
+ setsyntax()
+ if currentLanguage == languages.brainfuck and lines[1] ~= "-- Syntax: Brainfuck" then
+ table.insert(lines, 1, "-- Syntax: Brainfuck")
+ return nil, lines
+ end
+ end,
+ ["Start of Line ^+<"] = function() os.queueEvent("key", 199) end,
+ ["End of Line ^+>"] = function() os.queueEvent("key", 207) end,
+
+ -- Run
+ ["Run Program ^+R"] = function(path, lines)
+ saveFile(path, lines)
+ return nil, run(path, lines, false)
+ end,
+ ["Run w/ Args ^+Shift+R"] = function(path, lines)
+ saveFile(path, lines)
+ return nil, run(path, lines, true)
+ end,
+}
+
+local function drawMenu(open)
+ term.setCursorPos(1, 1)
+ term.setTextColor(colors[theme.textColor])
+ term.setBackgroundColor(colors[theme.backgroundHighlight])
+ term.clearLine()
+ local curX = 0
+ for _, v in pairs(menu) do
+ term.setCursorPos(3 + curX, 1)
+ term.write(v[1])
+ curX = curX + v[1]:len() + 3
+ end
+
+ if open then
+ local it = {}
+ local x = 1
+ for _, v in pairs(menu) do
+ if open == v[1] then
+ it = v
+ break
+ end
+ x = x + v[1]:len() + 3
+ end
+ x = x + 1
+
+ local items = {}
+ for i = 2, #it do
+ table.insert(items, it[i])
+ end
+
+ local len = 1
+ for _, v in pairs(items) do if v:len() + 2 > len then len = v:len() + 2 end end
+
+ for i, v in ipairs(items) do
+ term.setCursorPos(x, i + 1)
+ term.write(string.rep(" ", len))
+ term.setCursorPos(x + 1, i + 1)
+ term.write(v)
+ end
+ term.setCursorPos(x, #items + 2)
+ term.write(string.rep(" ", len))
+ return items, len
+ end
+end
+
+local function triggerMenu(cx, cy)
+ -- Determine clicked menu
+ local curX = 0
+ local open = nil
+ for _, v in pairs(menu) do
+ if cx >= curX + 3 and cx <= curX + v[1]:len() + 2 then
+ open = v[1]
+ break
+ end
+ curX = curX + v[1]:len() + 3
+ end
+ local menux = curX + 2
+ if not open then return false end
+
+ -- Flash menu item
+ term.setCursorBlink(false)
+ term.setCursorPos(menux, 1)
+ term.setBackgroundColor(colors[theme.background])
+ term.write(string.rep(" ", open:len() + 2))
+ term.setCursorPos(menux + 1, 1)
+ term.write(open)
+ sleep(0.1)
+ local items, len = drawMenu(open)
+
+ local ret = true
+
+ -- Pull events on menu
+ local ox, oy = term.getCursorPos()
+ while type(ret) ~= "string" do
+ local e, but, x, y = os.pullEvent()
+ if e == "mouse_click" then
+ -- If clicked outside menu
+ if x < menux - 1 or x > menux + len - 1 then break
+ elseif y > #items + 2 then break
+ elseif y == 1 then break end
+
+ for i, v in ipairs(items) do
+ if y == i + 1 and x >= menux and x <= menux + len - 2 then
+ -- Flash when clicked
+ term.setCursorPos(menux, y)
+ term.setBackgroundColor(colors[theme.background])
+ term.write(string.rep(" ", len))
+ term.setCursorPos(menux + 1, y)
+ term.write(v)
+ sleep(0.1)
+ drawMenu(open)
+
+ -- Return item
+ ret = v
+ break
+ end
+ end
+ end
+ end
+
+ term.setCursorPos(ox, oy)
+ term.setCursorBlink(true)
+ return ret
+end
+
+
+-- -------- Editing
+
+local standardsCompletions = {
+ "if%s+.+%s+then%s*$",
+ "for%s+.+%s+do%s*$",
+ "while%s+.+%s+do%s*$",
+ "repeat%s*$",
+ "function%s+[a-zA-Z_0-9]?\(.*\)%s*$",
+ "=%s*function%s*\(.*\)%s*$",
+ "else%s*$",
+ "elseif%s+.+%s+then%s*$"
+}
+
+local liveCompletions = {
+ ["("] = ")",
+ ["{"] = "}",
+ ["["] = "]",
+ ["\""] = "\"",
+ ["'"] = "'",
+}
+
+local x, y = 0, 0
+local edw, edh = 0, h - 1
+local offx, offy = 0, 1
+local scrollx, scrolly = 0, 0
+local lines = {}
+local liveErr = currentLanguage.parseError(nil)
+local displayCode = true
+local lastEventClock = os.clock()
+
+local function attemptToHighlight(line, regex, col)
+ local match = string.match(line, regex)
+ if match then
+ if type(col) == "number" then term.setTextColor(col)
+ elseif type(col) == "function" then term.setTextColor(col(match)) end
+ term.write(match)
+ term.setTextColor(colors[theme.textColor])
+ return line:sub(match:len() + 1, -1)
+ end
+ return nil
+end
+
+local function writeHighlighted(line)
+ if currentLanguage == languages.lua then
+ while line:len() > 0 do
+ line = attemptToHighlight(line, "^%-%-%[%[.-%]%]", colors[theme.comment]) or
+ attemptToHighlight(line, "^%-%-.*", colors[theme.comment]) or
+ attemptToHighlight(line, "^\".*[^\\]\"", colors[theme.string]) or
+ attemptToHighlight(line, "^\'.*[^\\]\'", colors[theme.string]) or
+ attemptToHighlight(line, "^%[%[.-%]%]", colors[theme.string]) or
+ attemptToHighlight(line, "^[%w_]+", function(match)
+ if currentLanguage.keywords[match] then
+ return colors[theme[currentLanguage.keywords[match]]]
+ end
+ return colors[theme.textColor]
+ end) or
+ attemptToHighlight(line, "^[^%w_]", colors[theme.textColor])
+ end
+ else term.write(line) end
+end
+
+local function draw()
+ -- Menu
+ term.setTextColor(colors[theme.textColor])
+ term.setBackgroundColor(colors[theme.editorBackground])
+ term.clear()
+ drawMenu()
+
+ -- Line numbers
+ offx, offy = tostring(#lines):len() + 1, 1
+ edw, edh = w - offx, h - 1
+
+ -- Draw text
+ for i = 1, edh do
+ local a = lines[scrolly + i]
+ if a then
+ local ln = string.rep(" ", offx - 1 - tostring(scrolly + i):len()) .. tostring(scrolly + i)
+ local l = a:sub(scrollx + 1, edw + scrollx + 1)
+ ln = ln .. ":"
+
+ if liveErr.line == scrolly + i then ln = string.rep(" ", offx - 2) .. "!:" end
+
+ term.setCursorPos(1, i + offy)
+ term.setBackgroundColor(colors[theme.editorBackground])
+ if scrolly + i == y then
+ if scrolly + i == liveErr.line and os.clock() - lastEventClock > 3 then
+ term.setBackgroundColor(colors[theme.editorErrorHighlight])
+ else term.setBackgroundColor(colors[theme.editorLineHightlight]) end
+ term.clearLine()
+ elseif scrolly + i == liveErr.line then
+ term.setBackgroundColor(colors[theme.editorError])
+ term.clearLine()
+ end
+
+ term.setCursorPos(1 - scrollx + offx, i + offy)
+ if scrolly + i == y then
+ if scrolly + i == liveErr.line and os.clock() - lastEventClock > 3 then
+ term.setBackgroundColor(colors[theme.editorErrorHighlight])
+ else term.setBackgroundColor(colors[theme.editorLineHightlight]) end
+ elseif scrolly + i == liveErr.line then term.setBackgroundColor(colors[theme.editorError])
+ else term.setBackgroundColor(colors[theme.editorBackground]) end
+ if scrolly + i == liveErr.line then
+ if displayCode then term.write(a)
+ else term.write(liveErr.display) end
+ else writeHighlighted(a) end
+
+ term.setCursorPos(1, i + offy)
+ if scrolly + i == y then
+ if scrolly + i == liveErr.line and os.clock() - lastEventClock > 3 then
+ term.setBackgroundColor(colors[theme.editorError])
+ else term.setBackgroundColor(colors[theme.editorLineNumbersHighlight]) end
+ elseif scrolly + i == liveErr.line then
+ term.setBackgroundColor(colors[theme.editorErrorHighlight])
+ else term.setBackgroundColor(colors[theme.editorLineNumbers]) end
+ term.write(ln)
+ end
+ end
+ term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
+end
+
+local function drawLine(...)
+ local ls = {...}
+ offx = tostring(#lines):len() + 1
+ for _, ly in pairs(ls) do
+ local a = lines[ly]
+ if a then
+ local ln = string.rep(" ", offx - 1 - tostring(ly):len()) .. tostring(ly)
+ local l = a:sub(scrollx + 1, edw + scrollx + 1)
+ ln = ln .. ":"
+
+ if liveErr.line == ly then ln = string.rep(" ", offx - 2) .. "!:" end
+
+ term.setCursorPos(1, (ly - scrolly) + offy)
+ term.setBackgroundColor(colors[theme.editorBackground])
+ if ly == y then
+ if ly == liveErr.line and os.clock() - lastEventClock > 3 then
+ term.setBackgroundColor(colors[theme.editorErrorHighlight])
+ else term.setBackgroundColor(colors[theme.editorLineHightlight]) end
+ elseif ly == liveErr.line then
+ term.setBackgroundColor(colors[theme.editorError])
+ end
+ term.clearLine()
+
+ term.setCursorPos(1 - scrollx + offx, (ly - scrolly) + offy)
+ if ly == y then
+ if ly == liveErr.line and os.clock() - lastEventClock > 3 then
+ term.setBackgroundColor(colors[theme.editorErrorHighlight])
+ else term.setBackgroundColor(colors[theme.editorLineHightlight]) end
+ elseif ly == liveErr.line then term.setBackgroundColor(colors[theme.editorError])
+ else term.setBackgroundColor(colors[theme.editorBackground]) end
+ if ly == liveErr.line then
+ if displayCode then term.write(a)
+ else term.write(liveErr.display) end
+ else writeHighlighted(a) end
+
+ term.setCursorPos(1, (ly - scrolly) + offy)
+ if ly == y then
+ if ly == liveErr.line and os.clock() - lastEventClock > 3 then
+ term.setBackgroundColor(colors[theme.editorError])
+ else term.setBackgroundColor(colors[theme.editorLineNumbersHighlight]) end
+ elseif ly == liveErr.line then
+ term.setBackgroundColor(colors[theme.editorErrorHighlight])
+ else term.setBackgroundColor(colors[theme.editorLineNumbers]) end
+ term.write(ln)
+ end
+ end
+ term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
+end
+
+local function cursorLoc(x, y, force)
+ local sx, sy = x - scrollx, y - scrolly
+ local redraw = false
+ if sx < 1 then
+ scrollx = x - 1
+ sx = 1
+ redraw = true
+ elseif sx > edw then
+ scrollx = x - edw
+ sx = edw
+ redraw = true
+ end if sy < 1 then
+ scrolly = y - 1
+ sy = 1
+ redraw = true
+ elseif sy > edh then
+ scrolly = y - edh
+ sy = edh
+ redraw = true
+ end if redraw or force then draw() end
+ term.setCursorPos(sx + offx, sy + offy)
+end
+
+local function executeMenuItem(a, path)
+ if type(a) == "string" and menuFunctions[a] then
+ local opt, nl, gtln = menuFunctions[a](path, lines, y)
+ if type(opt) == "string" then term.setCursorBlink(false) return opt end
+ if type(nl) == "table" then
+ if #lines < 1 then table.insert(lines, "") end
+ y = math.min(y, #lines)
+ x = math.min(x, lines[y]:len() + 1)
+ lines = nl
+ elseif type(nl) == "string" then
+ if nl == "go to" and gtln then
+ x, y = 1, math.min(#lines, gtln)
+ cursorLoc(x, y)
+ end
+ end
+ end
+ term.setCursorBlink(true)
+ draw()
+ term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
+end
+
+local function edit(path)
+ -- Variables
+ x, y = 1, 1
+ offx, offy = 0, 1
+ scrollx, scrolly = 0, 0
+ lines = loadFile(path)
+ if not lines then return "menu" end
+
+ -- Enable brainfuck
+ if lines[1] == "-- Syntax: Brainfuck" then
+ currentLanguage = languages.brainfuck
+ end
+
+ -- Clocks
+ local autosaveClock = os.clock()
+ local scrollClock = os.clock() -- To prevent redraw flicker
+ local liveErrorClock = os.clock()
+ local hasScrolled = false
+
+ -- Draw
+ draw()
+ term.setCursorPos(x + offx, y + offy)
+ term.setCursorBlink(true)
+
+ -- Main loop
+ local tid = os.startTimer(3)
+ while true do
+ local e, key, cx, cy = os.pullEvent()
+ if e == "key" and allowEditorEvent then
+ if key == 200 and y > 1 then
+ -- Up
+ x, y = math.min(x, lines[y - 1]:len() + 1), y - 1
+ drawLine(y, y + 1)
+ cursorLoc(x, y)
+ elseif key == 208 and y < #lines then
+ -- Down
+ x, y = math.min(x, lines[y + 1]:len() + 1), y + 1
+ drawLine(y, y - 1)
+ cursorLoc(x, y)
+ elseif key == 203 and x > 1 then
+ -- Left
+ x = x - 1
+ local force = false
+ if y - scrolly + offy < offy + 1 then force = true end
+ cursorLoc(x, y, force)
+ elseif key == 205 and x < lines[y]:len() + 1 then
+ -- Right
+ x = x + 1
+ local force = false
+ if y - scrolly + offy < offy + 1 then force = true end
+ cursorLoc(x, y, force)
+ elseif (key == 28 or key == 156) and (displayCode and true or y + scrolly - 1 ==
+ liveErr.line) then
+ -- Enter
+ local f = nil
+ for _, v in pairs(standardsCompletions) do
+ if lines[y]:find(v) and x == #lines[y] + 1 then f = v end
+ end
+
+ local _, spaces = lines[y]:find("^[ ]+")
+ if not spaces then spaces = 0 end
+ if f then
+ table.insert(lines, y + 1, string.rep(" ", spaces + 2))
+ if not f:find("else", 1, true) and not f:find("elseif", 1, true) then
+ table.insert(lines, y + 2, string.rep(" ", spaces) ..
+ (f:find("repeat", 1, true) and "until " or f:find("{", 1, true) and "}" or
+ "end"))
+ end
+ x, y = spaces + 3, y + 1
+ cursorLoc(x, y, true)
+ else
+ local oldLine = lines[y]
+
+ lines[y] = lines[y]:sub(1, x - 1)
+ table.insert(lines, y + 1, string.rep(" ", spaces) .. oldLine:sub(x, -1))
+
+ x, y = spaces + 1, y + 1
+ cursorLoc(x, y, true)
+ end
+ elseif key == 14 and (displayCode and true or y + scrolly - 1 == liveErr.line) then
+ -- Backspace
+ if x > 1 then
+ local f = false
+ for k, v in pairs(liveCompletions) do
+ if lines[y]:sub(x - 1, x - 1) == k then f = true end
+ end
+
+ lines[y] = lines[y]:sub(1, x - 2) .. lines[y]:sub(x + (f and 1 or 0), -1)
+ drawLine(y)
+ x = x - 1
+ cursorLoc(x, y)
+ elseif y > 1 then
+ local prevLen = lines[y - 1]:len() + 1
+ lines[y - 1] = lines[y - 1] .. lines[y]
+ table.remove(lines, y)
+ x, y = prevLen, y - 1
+ cursorLoc(x, y, true)
+ end
+ elseif key == 199 then
+ -- Home
+ x = 1
+ local force = false
+ if y - scrolly + offy < offy + 1 then force = true end
+ cursorLoc(x, y, force)
+ elseif key == 207 then
+ -- End
+ x = lines[y]:len() + 1
+ local force = false
+ if y - scrolly + offy < offy + 1 then force = true end
+ cursorLoc(x, y, force)
+ elseif key == 211 and (displayCode and true or y + scrolly - 1 == liveErr.line) then
+ -- Forward Delete
+ if x < lines[y]:len() + 1 then
+ lines[y] = lines[y]:sub(1, x - 1) .. lines[y]:sub(x + 1)
+ local force = false
+ if y - scrolly + offy < offy + 1 then force = true end
+ drawLine(y)
+ cursorLoc(x, y, force)
+ elseif y < #lines then
+ lines[y] = lines[y] .. lines[y + 1]
+ table.remove(lines, y + 1)
+ draw()
+ cursorLoc(x, y)
+ end
+ elseif key == 15 and (displayCode and true or y + scrolly - 1 == liveErr.line) then
+ -- Tab
+ lines[y] = string.rep(" ", tabWidth) .. lines[y]
+ x = x + 2
+ local force = false
+ if y - scrolly + offy < offy + 1 then force = true end
+ drawLine(y)
+ cursorLoc(x, y, force)
+ elseif key == 201 then
+ -- Page up
+ y = math.min(math.max(y - edh, 1), #lines)
+ x = math.min(lines[y]:len() + 1, x)
+ cursorLoc(x, y, true)
+ elseif key == 209 then
+ -- Page down
+ y = math.min(math.max(y + edh, 1), #lines)
+ x = math.min(lines[y]:len() + 1, x)
+ cursorLoc(x, y, true)
+ end
+ elseif e == "char" and allowEditorEvent and (displayCode and true or
+ y + scrolly - 1 == liveErr.line) then
+ local shouldIgnore = false
+ for k, v in pairs(liveCompletions) do
+ if key == v and lines[y]:find(k, 1, true) and lines[y]:sub(x, x) == v then
+ shouldIgnore = true
+ end
+ end
+
+ local addOne = false
+ if not shouldIgnore then
+ for k, v in pairs(liveCompletions) do
+ if key == k and lines[y]:sub(x, x) ~= k then key = key .. v addOne = true end
+ end
+ lines[y] = lines[y]:sub(1, x - 1) .. key .. lines[y]:sub(x, -1)
+ end
+
+ x = x + (addOne and 1 or key:len())
+ local force = false
+ if y - scrolly + offy < offy + 1 then force = true end
+ drawLine(y)
+ cursorLoc(x, y, force)
+ elseif e == "mouse_click" and key == 1 then
+ if cy > 1 then
+ if cx <= offx and cy - offy == liveErr.line - scrolly then
+ displayCode = not displayCode
+ drawLine(liveErr.line)
+ else
+ local oldy = y
+ y = math.min(math.max(scrolly + cy - offy, 1), #lines)
+ x = math.min(math.max(scrollx + cx - offx, 1), lines[y]:len() + 1)
+ if oldy ~= y then drawLine(oldy, y) end
+ cursorLoc(x, y)
+ end
+ else
+ local a = triggerMenu(cx, cy)
+ if a then
+ local opt = executeMenuItem(a, path)
+ if opt then return opt end
+ end
+ end
+ elseif e == "shortcut" then
+ local a = shortcuts[key .. " " .. cx]
+ if a then
+ local parent = nil
+ local curx = 0
+ for i, mv in ipairs(menu) do
+ for _, iv in pairs(mv) do
+ if iv == a then
+ parent = menu[i][1]
+ break
+ end
+ end
+ if parent then break end
+ curx = curx + mv[1]:len() + 3
+ end
+ local menux = curx + 2
+
+ -- Flash menu item
+ term.setCursorBlink(false)
+ term.setCursorPos(menux, 1)
+ term.setBackgroundColor(colors[theme.background])
+ term.write(string.rep(" ", parent:len() + 2))
+ term.setCursorPos(menux + 1, 1)
+ term.write(parent)
+ sleep(0.1)
+ drawMenu()
+
+ -- Execute item
+ local opt = executeMenuItem(a, path)
+ if opt then return opt end
+ end
+ elseif e == "mouse_scroll" then
+ if key == -1 and scrolly > 0 then
+ scrolly = scrolly - 1
+ if os.clock() - scrollClock > 0.0005 then
+ draw()
+ term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
+ end
+ scrollClock = os.clock()
+ hasScrolled = true
+ elseif key == 1 and scrolly < #lines - edh then
+ scrolly = scrolly + 1
+ if os.clock() - scrollClock > 0.0005 then
+ draw()
+ term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
+ end
+ scrollClock = os.clock()
+ hasScrolled = true
+ end
+ elseif e == "timer" and key == tid then
+ drawLine(y)
+ tid = os.startTimer(3)
+ end
+
+ -- Draw
+ if hasScrolled and os.clock() - scrollClock > 0.1 then
+ draw()
+ term.setCursorPos(x - scrollx + offx, y - scrolly + offy)
+ hasScrolled = false
+ end
+
+ -- Autosave
+ if os.clock() - autosaveClock > autosaveInterval then
+ saveFile(path, lines)
+ autosaveClock = os.clock()
+ end
+
+ -- Errors
+ if os.clock() - liveErrorClock > 1 then
+ local prevLiveErr = liveErr
+ liveErr = currentLanguage.parseError(nil)
+ local code = ""
+ for _, v in pairs(lines) do code = code .. v .. "\n" end
+
+ liveErr = currentLanguage.getCompilerErrors(code)
+ liveErr.line = math.min(liveErr.line - 2, #lines)
+ if liveErr ~= prevLiveErr then draw() end
+ liveErrorClock = os.clock()
+ end
+ end
+
+ return "menu"
+end
+
+
+-- -------- Open File
+
+local function newFile()
+ local wid = w - 13
+
+ -- Get name
+ title("Lua IDE - New File")
+ local name = centerRead(wid, "/")
+ if not name or name == "" then return "menu" end
+ name = "/" .. name
+
+ -- Clear
+ title("Lua IDE - New File")
+ term.setTextColor(colors[theme.textColor])
+ term.setBackgroundColor(colors[theme.promptHighlight])
+ for i = 8, 10 do
+ term.setCursorPos(w/2 - wid/2, i)
+ term.write(string.rep(" ", wid))
+ end
+ term.setCursorPos(1, 9)
+ if fs.isDir(name) then
+ centerPrint("Cannot Edit a Directory!")
+ sleep(1.6)
+ return "menu"
+ elseif fs.exists(name) then
+ centerPrint("File Already Exists!")
+ local opt = prompt({{"Open", w/2 - 9, 14}, {"Cancel", w/2 + 2, 14}}, "horizontal")
+ if opt == "Open" then return "edit", name
+ elseif opt == "Cancel" then return "menu" end
+ else return "edit", name end
+end
+
+local function openFile()
+ local wid = w - 13
+
+ -- Get name
+ title("Lua IDE - Open File")
+ local name = centerRead(wid, "/")
+ if not name or name == "" then return "menu" end
+ name = "/" .. name
+
+ -- Clear
+ title("Lua IDE - New File")
+ term.setTextColor(colors[theme.textColor])
+ term.setBackgroundColor(colors[theme.promptHighlight])
+ for i = 8, 10 do
+ term.setCursorPos(w/2 - wid/2, i)
+ term.write(string.rep(" ", wid))
+ end
+ term.setCursorPos(1, 9)
+ if fs.isDir(name) then
+ centerPrint("Cannot Open a Directory!")
+ sleep(1.6)
+ return "menu"
+ elseif not fs.exists(name) then
+ centerPrint("File Doesn't Exist!")
+ local opt = prompt({{"Create", w/2 - 11, 14}, {"Cancel", w/2 + 2, 14}}, "horizontal")
+ if opt == "Create" then return "edit", name
+ elseif opt == "Cancel" then return "menu" end
+ else return "edit", name end
+end
+
+
+-- -------- Settings
+
+local function update()
+ local function draw(status)
+ title("LuaIDE - Update")
+ term.setBackgroundColor(colors[theme.prompt])
+ term.setTextColor(colors[theme.textColor])
+ for i = 8, 10 do
+ term.setCursorPos(w/2 - (status:len() + 4), i)
+ write(string.rep(" ", status:len() + 4))
+ end
+ term.setCursorPos(w/2 - (status:len() + 4), 9)
+ term.write(" - " .. status .. " ")
+
+ term.setBackgroundColor(colors[theme.errHighlight])
+ for i = 8, 10 do
+ term.setCursorPos(w/2 + 2, i)
+ term.write(string.rep(" ", 10))
+ end
+ term.setCursorPos(w/2 + 2, 9)
+ term.write(" > Cancel ")
+ end
+
+ if not http then
+ draw("HTTP API Disabled!")
+ sleep(1.6)
+ return "settings"
+ end
+
+ draw("Updating...")
+ local tID = os.startTimer(10)
+ http.request(updateURL)
+ while true do
+ local e, but, x, y = os.pullEvent()
+ if (e == "key" and but == 28) or
+ (e == "mouse_click" and x >= w/2 + 2 and x <= w/2 + 12 and y == 9) then
+ draw("Cancelled")
+ sleep(1.6)
+ break
+ elseif e == "http_success" and but == updateURL then
+ local new = x.readAll()
+ local curf = io.open(ideLocation, "r")
+ local cur = curf:read("*a")
+ curf:close()
+
+ if cur ~= new then
+ draw("Update Found")
+ sleep(1.6)
+ local f = io.open(ideLocation, "w")
+ f:write(new)
+ f:close()
+
+ draw("Click to Exit")
+ while true do
+ local e = os.pullEvent()
+ if e == "mouse_click" or (not isAdvanced() and e == "key") then break end
+ end
+ return "exit"
+ else
+ draw("No Updates Found!")
+ sleep(1.6)
+ break
+ end
+ elseif e == "http_failure" or (e == "timer" and but == tID) then
+ draw("Update Failed!")
+ sleep(1.6)
+ break
+ end
+ end
+
+ return "settings"
+end
+
+local function changeTheme()
+ title("LuaIDE - Theme")
+
+ if isAdvanced() then
+ local disThemes = {"Back"}
+ for _, v in pairs(availableThemes) do table.insert(disThemes, v[1]) end
+ local t = scrollingPrompt(disThemes)
+ local url = nil
+ for _, v in pairs(availableThemes) do if v[1] == t then url = v[2] end end
+
+ if not url then return "settings" end
+ if t == "Dawn (Default)" then
+ term.setBackgroundColor(colors[theme.backgroundHighlight])
+ term.setCursorPos(3, 3)
+ term.clearLine()
+ term.write("LuaIDE - Loaded Theme!")
+ sleep(1.6)
+
+ fs.delete(themeLocation)
+ theme = defaultTheme
+ return "menu"
+ end
+
+ term.setBackgroundColor(colors[theme.backgroundHighlight])
+ term.setCursorPos(3, 3)
+ term.clearLine()
+ term.write("LuaIDE - Downloading...")
+
+ fs.delete("/.LuaIDE_temp_theme_file")
+ download(url, "/.LuaIDE_temp_theme_file")
+ local a = loadTheme("/.LuaIDE_temp_theme_file")
+
+ term.setCursorPos(3, 3)
+ term.clearLine()
+ if a then
+ term.write("LuaIDE - Loaded Theme!")
+ fs.delete(themeLocation)
+ fs.move("/.LuaIDE_temp_theme_file", themeLocation)
+ theme = a
+ sleep(1.6)
+ return "menu"
+ end
+
+ term.write("LuaIDE - Could Not Load Theme!")
+ fs.delete("/.LuaIDE_temp_theme_file")
+ sleep(1.6)
+ return "settings"
+ else
+ term.setCursorPos(1, 8)
+ centerPrint("Themes are not available on")
+ centerPrint("normal computers!")
+ end
+end
+
+local function settings()
+ title("LuaIDE - Settings")
+
+ local opt = prompt({{"Change Theme", w/2 - 17, 8}, {"Return to Menu", w/2 - 22, 13},
+ {"Check for Updates", w/2 + 2, 8}, {"Exit IDE", w/2 + 2, 13, bg = colors[theme.err],
+ highlight = colors[theme.errHighlight]}}, "vertical", true)
+ if opt == "Change Theme" then return changeTheme()
+ elseif opt == "Check for Updates" then return update()
+ elseif opt == "Return to Menu" then return "menu"
+ elseif opt == "Exit IDE" then return "exit" end
+end
+
+
+-- -------- Menu
+
+local function menu()
+ title("Welcome to LuaIDE " .. version)
+
+ local opt = prompt({{"New File", w/2 - 13, 8}, {"Open File", w/2 - 14, 13},
+ {"Settings", w/2 + 2, 8}, {"Exit IDE", w/2 + 2, 13, bg = colors[theme.err],
+ highlight = colors[theme.errHighlight]}}, "vertical", true)
+ if opt == "New File" then return "new"
+ elseif opt == "Open File" then return "open"
+ elseif opt == "Settings" then return "settings"
+ elseif opt == "Exit IDE" then return "exit" end
+end
+
+
+-- -------- Main
+
+local function main(arguments)
+ local opt, data = "menu", nil
+
+ -- Check arguments
+ if type(arguments) == "table" and #arguments > 0 then
+ local f = "/" .. shell.resolve(arguments[1])
+ if fs.isDir(f) then print("Cannot edit a directory.") end
+ opt, data = "edit", f
+ end
+
+ -- Main run loop
+ while true do
+ -- Menu
+ if opt == "menu" then opt = menu() end
+
+ -- Other
+ if opt == "new" then opt, data = newFile()
+ elseif opt == "open" then opt, data = openFile()
+ elseif opt == "settings" then opt = settings()
+ end if opt == "exit" then break end
+
+ -- Edit
+ if opt == "edit" and data then opt = edit(data) end
+ end
+end
+
+-- Load Theme
+if fs.exists(themeLocation) then theme = loadTheme(themeLocation) end
+if not theme and isAdvanced() then theme = defaultTheme
+elseif not theme then theme = normalTheme end
+
+-- Run
+local _, err = pcall(function()
+ parallel.waitForAny(function() main(arguments) end, monitorKeyboardShortcuts)
+end)
+
+-- Catch errors
+if err and not err:find("Terminated") then
+ term.setCursorBlink(false)
+ title("LuaIDE - Crash! D:")
+
+ term.setBackgroundColor(colors[theme.err])
+ for i = 6, 8 do
+ term.setCursorPos(5, i)
+ term.write(string.rep(" ", 36))
+ end
+ term.setCursorPos(6, 7)
+ term.write("LuaIDE Has Crashed! D:")
+
+ term.setBackgroundColor(colors[theme.background])
+ term.setCursorPos(2, 10)
+ print(err)
+
+ term.setBackgroundColor(colors[theme.prompt])
+ local _, cy = term.getCursorPos()
+ for i = cy + 1, cy + 4 do
+ term.setCursorPos(5, i)
+ term.write(string.rep(" ", 36))
+ end
+ term.setCursorPos(6, cy + 2)
+ term.write("Please report this error to")
+ term.setCursorPos(6, cy + 3)
+ term.write("GravityScore! ")
+
+ term.setBackgroundColor(colors[theme.background])
+ if isAdvanced() then centerPrint("Click to Exit...", h - 1)
+ else centerPrint("Press Any Key to Exit...", h - 1) end
+ while true do
+ local e = os.pullEvent()
+ if e == "mouse_click" or (not isAdvanced() and e == "key") then break end
+ end
+
+ -- Prevent key from being shown
+ os.queueEvent("")
+ os.pullEvent()
+end
+
+-- Exit
+term.setBackgroundColor(colors.black)
+term.setTextColor(colors.white)
+term.clear()
+term.setCursorPos(1, 1)
+centerPrint("Thank You for Using Lua IDE " .. version)
+centerPrint("Made by GravityScore") \ No newline at end of file