local file = ... local fs = require("filesystem") local event = require("event") local component = require("component") local unicode = require("unicode") local workingDirectory = require("shell").getWorkingDirectory() local gpu = component.gpu local width, height = gpu.getResolution() local scrollPosX, scrollPosY = 1, 1 local cursorPosX, cursorPosY = 1, 1 local cursorWhite = true local changesMade = false local renderBuffer = gpu.allocateBuffer() local scrollSpeed = 5 local tab = " " --local ocelot = component.ocelot local function rawset(x, y, text) terminal.setCursorPos(x,y) terminal.write(text, false) end local filestring, filepath, handle, data, tmpdata if file then if file:sub(1, 1) == "/" then filepath = file else filepath = workingDirectory .. file end handle, data, tmpdata = fs.open(filepath, "r"), "", nil if fs.exists(filepath) then filestring = filepath repeat tmpdata = handle:read(math.huge) data = data .. (tmpdata or "") until not tmpdata tmpdata = {} if data:gmatch("(.-)\n")() then for line in data:gmatch("(.-)\n") do local newLine = line:gsub("\r", "") -- this took me SO LONG TO FIGURE OUT AAAAAAAA I HATE CRLF I HATE CRLF I HATE CRLF table.insert(tmpdata, newLine) end else tmpdata = {data} end else filepath = workingDirectory .. file filestring = "[NEW FILE]" tmpdata = {""} end else filepath = "" filestring = "[NEW FILE]" tmpdata = {""} end local function render() gpu.setActiveBuffer(renderBuffer) terminal.clear() --ocelot.log(tostring(scrollPosY)) local realCursorX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2) if realCursorX < 1 then scrollPosX = scrollPosX + realCursorX - 1 cursorPosX = 1 realCursorX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2) end for i = scrollPosY, height + scrollPosY - 3 do gpu.set(1, i - scrollPosY + 1, (tmpdata[i] or ""):sub(scrollPosX)) end rawset(1, height - 1, "\27[107m\27[30m" .. filestring .. string.rep(" ", width)) rawset(1, height, "\27[107m\27[30m^X\27[0m Exit \27[107m\27[30m^S\27[0m Save" .. string.rep(" ", width)) local char = gpu.get(realCursorX, cursorPosY) if cursorWhite then gpu.setForeground(0) gpu.setBackground(0xFFFFFF) end gpu.set(realCursorX, cursorPosY, char) gpu.bitblt() gpu.setActiveBuffer(0) end local renderFlag, cursorRenderFlag = false, false local function scrollUp() cursorPosY = cursorPosY - 1 cursorRenderFlag = true cursorWhite = true if cursorPosY < 1 then renderFlag = true scrollPosY = scrollPosY - 1 cursorPosY = 1 end if scrollPosY < 1 then renderFlag = false scrollPosY = 1 end if math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2) < 1 then renderFlag = true end end local function scrollDown() cursorPosY = cursorPosY + 1 cursorRenderFlag = true cursorWhite = true if cursorPosY + scrollPosY - 1 > #tmpdata then renderFlag = false cursorPosY = #tmpdata - scrollPosY + 1 end if cursorPosY > height - 2 then renderFlag = true scrollPosY = scrollPosY + 1 cursorPosY = height - 2 end if math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2) < 1 then renderFlag = true end end local function scrollLeft() cursorRenderFlag = true cursorWhite = true if cursorPosX > 1 then if cursorPosX <= unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2 then cursorPosX = cursorPosX - 1 elseif unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 1 > 1 then cursorPosX = unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 1 end elseif scrollPosX > 1 then scrollPosX = scrollPosX - 1 renderFlag = true end if math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2) < 1 then renderFlag = true end end local function scrollRight() cursorRenderFlag = true cursorWhite = true if cursorPosX <= unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 1 then cursorPosX = cursorPosX + 1 end if cursorPosX > width then cursorPosX = width scrollPosX = scrollPosX + 1 renderFlag = true end end local function processEvent(args) renderFlag, cursorRenderFlag = false, false if args[1] == "key_down" then local keycode = args[4] local key = keyboard.keys[keycode] if keyboard.getCtrlDown() then return false, false, key end if key == "down" and cursorPosY < #tmpdata then scrollDown() end if key == "up" then scrollUp() end if key == "left" then scrollLeft() end if key == "right" then scrollRight() end if key == "enter" then changesMade = true renderFlag = true cursorWhite = true table.insert(tmpdata, cursorPosY + 1, tmpdata[cursorPosY + scrollPosY - 1]:sub(cursorPosX)) tmpdata[cursorPosY + scrollPosY - 1] = tmpdata[cursorPosY + scrollPosY - 1]:sub(1, cursorPosX - 1) cursorPosX = 1 cursorPosY = cursorPosY + 1 scrollPosX = 1 if cursorPosY > height - 2 then scrollPosY = scrollPosY + 1 cursorPosY = height - 2 end end if key == "back" then changesMade = true cursorRenderFlag = true cursorWhite = true if cursorPosX == 1 and cursorPosY + scrollPosY - 1 > 1 then cursorPosY = cursorPosY - 1 if cursorPosY < 1 then scrollPosY = scrollPosY - 1 cursorPosY = 1 end if scrollPosY < 1 then scrollPosY = 1 end cursorPosX = unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2 if cursorPosX > width then scrollPosX = cursorPosX - width + 1 cursorPosX = width end tmpdata[cursorPosY + scrollPosY - 1] = tmpdata[cursorPosY + scrollPosY - 1] .. tmpdata[cursorPosY + 1] table.remove(tmpdata, cursorPosY + 1) renderFlag = true else tmpdata[cursorPosY + scrollPosY - 1] = tmpdata[cursorPosY + scrollPosY - 1]:sub(1, cursorPosX + scrollPosX - 3) .. tmpdata[cursorPosY + scrollPosY - 1]:sub(cursorPosX + scrollPosX - 1) cursorPosX = math.min(cursorPosX - 1, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) + 1) if cursorPosX < 1 then cursorPosX = 1 scrollPosX = scrollPosX - 1 renderFlag = true else gpu.set(1, cursorPosY, tmpdata[cursorPosY + scrollPosY - 1]:sub(scrollPosX) .. " ") end end end if key == "tab" then changesMade = true cursorRenderFlag = true cursorWhite = true tmpdata[cursorPosY + scrollPosY - 1] = tmpdata[cursorPosY + scrollPosY - 1]:sub(1, cursorPosX + scrollPosX - 2) .. tab .. tmpdata[cursorPosY + scrollPosY - 1]:sub(cursorPosX + scrollPosX - 1) cursorPosX = cursorPosX + unicode.wlen(tab) if cursorPosX > width then scrollPosX = scrollPosX + cursorPosX - width cursorPosX = width renderFlag = true else gpu.set(1, cursorPosY, tmpdata[cursorPosY + scrollPosY - 1]:sub(scrollPosX)) end end if args[3] >= 32 and args[3] <= 126 then changesMade = true cursorRenderFlag = true cursorWhite = true tmpdata[cursorPosY + scrollPosY - 1] = tmpdata[cursorPosY + scrollPosY - 1]:sub(1, cursorPosX + scrollPosX - 2) .. unicode.char(args[3]) .. tmpdata[cursorPosY + scrollPosY - 1]:sub(cursorPosX + scrollPosX - 1) cursorPosX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1])) + 1 --ocelot.log(tostring(cursorPosX)) if cursorPosX > width then cursorPosX = width scrollPosX = scrollPosX + 1 renderFlag = true else gpu.set(1, cursorPosY, tmpdata[cursorPosY + scrollPosY - 1]:sub(scrollPosX)) end end elseif args[1] == "scroll" then if args[5] == 1 then for i = 1, scrollSpeed do scrollUp() end elseif args[5] == -1 and cursorPosY < #tmpdata then for i = 1, scrollSpeed do scrollDown() end end end return renderFlag, cursorRenderFlag end local function save() gpu.setBackground(0xFFFFFF) gpu.setForeground(0) gpu.set(1, height - 1, string.rep(" ", width)) terminal.setCursorPos(1, height - 1) local savepath = terminal.read(nil, "\27[107m\27[30mSave location: ", filepath) gpu.setBackground(0xFFFFFF) gpu.setForeground(0) if fs.exists(savepath) then gpu.set(1, height - 1, string.rep(" ", width)) local answer = terminal.read(nil, "\27[107m\27[30mFile already exists. Overwrite it? [Y/n] ") if answer:lower() == "n" then gpu.setBackground(0xFFFFFF) gpu.setForeground(0) gpu.set(1, height - 1, filestring .. string.rep(" ", width)) return end end local handle, errorMessage = fs.open(savepath, "w") if handle then if table.concat(tmpdata, "\n"):sub(-1, -1) == "\n" then handle:write(table.concat(tmpdata, "\n")) else handle:write(table.concat(tmpdata, "\n") .. "\n") -- add a newline at the end to follow POSIX standards end handle:close() gpu.set(1, height - 1, filestring .. string.rep(" ", width)) else gpu.set(1, height - 1, "ERROR: " .. errorMessage:gsub("\n", "") .. string.rep(" ", width)) end changesMade = false end render() while true do local args = {event.pull(0.5)} local renderFlag, cursorRenderFlag, specialKey = false, false, nil local previousCursorX, previousCursorY = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2), cursorPosY if args and args[1] then cursorWhite = true renderFlag, cursorRenderFlag, specialKey = processEvent(args) if specialKey == "x" then if changesMade then terminal.setCursorPos(1, height - 1) local response = terminal.read(nil, "\27[107m\27[30mWould you like to save changes? [Y/n] ") if response:lower() ~= "n" then save() end end gpu.freeAllBuffers() terminal.clear() return end if specialKey == "s" then save() end repeat args = {event.pull("key_down", 0)} if args and args[1] then processEvent(args) end until not args or not args[1] else cursorWhite = not cursorWhite cursorRenderFlag = true end if cursorRenderFlag then local char = gpu.get(previousCursorX, previousCursorY) gpu.set(previousCursorX, previousCursorY, char) local realCursorX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2) if realCursorX < 1 then scrollPosX = scrollPosX + realCursorX - 1 cursorPosX = 1 realCursorX = math.min(cursorPosX, unicode.wlen(tmpdata[cursorPosY + scrollPosY - 1]) - scrollPosX + 2) end local char = gpu.get(realCursorX, cursorPosY) if cursorWhite then gpu.setBackground(0xFFFFFF) gpu.setForeground(0) end gpu.set(realCursorX, cursorPosY, char) if cursorWhite then gpu.setForeground(0xFFFFFF) gpu.setBackground(0) end end if renderFlag then render() end end