ecoMeco

buggy synedit

Nov 15th, 2016
672
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. local event = require("event")
  2. local fs = require("filesystem")
  3. local keyboard = require("keyboard")
  4. local shell = require("shell")
  5. local term = require("term")
  6. local text = require("text")
  7. local unicode = require("unicode")
  8.  
  9. -- SYNEDIT MODIFICATIONS start
  10.  
  11. --Monokai color scheme
  12. local bgColor = 0x272822
  13. local textColor = 0xF8F8F2
  14. local highlightColor = 0xF92672
  15. local keywordColor = 0xF92672
  16. local commentColor = 0x75715E
  17. local stringColor = 0xE6DB74
  18. local numberColor = 0xAE81FF
  19.  
  20. local tKeywords = {
  21.     ["and"] = true,
  22.     ["break"] = true,
  23.     ["do"] = true,
  24.     ["else"] = true,
  25.     ["elseif"] = true,
  26.     ["end"] = true,
  27.     ["false"] = true,
  28.     ["for"] = true,
  29.     ["function"] = true,
  30.     ["if"] = true,
  31.     ["in"] = true,
  32.     ["local"] = true,
  33.     ["nil"] = true,
  34.     ["not"] = true,
  35.     ["or"] = true,
  36.     ["repeat"] = true,
  37.     ["return"] = true,
  38.     ["then"] = true,
  39.     ["true"] = true,
  40.     ["until"] = true,
  41.     ["while"] = true,
  42.  
  43.     --Doesn't work for some reason...
  44.     ["+"] = true,
  45.     ["-"] = true,
  46.     ["*"] = true,
  47.     ["/"] = true,
  48.     ["="] = true,
  49. }
  50. --SYNEDIT MODIFICATIONS end
  51.  
  52. if not term.isAvailable() then
  53.   return
  54. end
  55. local gpu = term.gpu()
  56. local args, options = shell.parse(...)
  57. if #args == 0 then
  58.   io.write("Usage: edit <filename>")
  59.   return
  60. end
  61.  
  62. local filename = shell.resolve(args[1])
  63.  
  64. local readonly = options.r or fs.get(filename) == nil or fs.get(filename).isReadOnly()
  65.  
  66. if not fs.exists(filename) then
  67.   if fs.isDirectory(filename) then
  68.     io.stderr:write("file is a directory\n")
  69.     return 1
  70.   elseif readonly then
  71.     io.stderr:write("file system is read only\n")
  72.     return 1
  73.   end
  74. end
  75.  
  76. local function loadConfig()
  77.   -- Try to load user settings.
  78.   local env = {}
  79.   local config = loadfile("/etc/edit.cfg", nil, env)
  80.   if config then
  81.     pcall(config)
  82.   end
  83.   -- Fill in defaults.
  84.   env.keybinds = env.keybinds or {
  85.     left = {{"left"}},
  86.     right = {{"right"}},
  87.     up = {{"up"}},
  88.     down = {{"down"}},
  89.     home = {{"home"}},
  90.     eol = {{"end"}},
  91.     pageUp = {{"pageUp"}},
  92.     pageDown = {{"pageDown"}},
  93.  
  94.     backspace = {{"back"}},
  95.     delete = {{"delete"}},
  96.     deleteLine = {{"control", "delete"}, {"shift", "delete"}},
  97.     newline = {{"enter"}},
  98.  
  99.     save = {{"control", "s"}},
  100.     close = {{"control", "w"}},
  101.     find = {{"control", "f"}},
  102.     findnext = {{"control", "g"}, {"control", "n"}, {"f3"}}
  103.   }
  104.   -- Generate config file if it didn't exist.
  105.   if not config then
  106.     local root = fs.get("/")
  107.     if root and not root.isReadOnly() then
  108.       fs.makeDirectory("/etc")
  109.       local f = io.open("/etc/edit.cfg", "w")
  110.       if f then
  111.         local serialization = require("serialization")
  112.         for k, v in pairs(env) do
  113.           f:write(k.."="..tostring(serialization.serialize(v, math.huge)).."\n")
  114.         end
  115.         f:close()
  116.       end
  117.     end
  118.   end
  119.   return env
  120. end
  121.  
  122. term.clear()
  123. term.setCursorBlink(true)
  124.  
  125. local running = true
  126. local buffer = {}
  127. local scrollX, scrollY = 0, 0
  128. local config = loadConfig()
  129.  
  130. local getKeyBindHandler -- forward declaration for refind()
  131.  
  132. local function helpStatusText()
  133.   local function prettifyKeybind(label, command)
  134.     local keybind = type(config.keybinds) == "table" and config.keybinds[command]
  135.     if type(keybind) ~= "table" or type(keybind[1]) ~= "table" then return "" end
  136.     local alt, control, shift, key
  137.     for _, value in ipairs(keybind[1]) do
  138.       if value == "alt" then alt = true
  139.       elseif value == "control" then control = true
  140.       elseif value == "shift" then shift = true
  141.       else key = value end
  142.     end
  143.     if not key then return "" end
  144.     return label .. ": [" ..
  145.            (control and "Ctrl+" or "") ..
  146.            (alt and "Alt+" or "") ..
  147.            (shift and "Shift+" or "") ..
  148.            unicode.upper(key) ..
  149.            "] "
  150.   end
  151.   return prettifyKeybind("Save", "save") ..
  152.          prettifyKeybind("Close", "close") ..
  153.          prettifyKeybind("Find", "find")
  154. end
  155.  
  156. -------------------------------------------------------------------------------
  157.  
  158. local function setStatus(value)
  159.   local x, y, w, h = term.getGlobalArea()
  160.   value = unicode.wlen(value) > w - 10 and unicode.wtrunc(value, w - 9) or value
  161.   value = text.padRight(value, w - 10)
  162.   gpu.set(x, y + h - 1, value)
  163. end
  164.  
  165. local function getArea()
  166.   local x, y, w, h = term.getGlobalArea()
  167.   return x, y, w, h - 1
  168. end
  169.  
  170. local function removePrefix(line, length)
  171.   if length >= unicode.wlen(line) then
  172.     return ""
  173.   else
  174.     local prefix = unicode.wtrunc(line, length + 1)
  175.     local suffix = unicode.sub(line, unicode.len(prefix) + 1)
  176.     length = length - unicode.wlen(prefix)
  177.     if length > 0 then
  178.       suffix = (" "):rep(unicode.charWidth(suffix) - length) .. unicode.sub(suffix, 2)
  179.     end
  180.     return suffix
  181.   end
  182. end
  183.  
  184. local function lengthToChars(line, length)
  185.   if length > unicode.wlen(line) then
  186.     return unicode.len(line) + 1
  187.   else
  188.     local prefix = unicode.wtrunc(line, length)
  189.     return unicode.len(prefix) + 1
  190.   end
  191. end
  192.  
  193.  
  194. local function isWideAtPosition(line, x)
  195.   local index = lengthToChars(line, x)
  196.   if index > unicode.len(line) then
  197.     return false, false
  198.   end
  199.   local prefix = unicode.sub(line, 1, index)
  200.   local char = unicode.sub(line, index, index)
  201.   --isWide, isRight
  202.   return unicode.isWide(char), unicode.wlen(prefix) == x
  203. end
  204.  
  205.  
  206. --SYNEDIT MODIFICATIONS start
  207. local function tryWrite(sLine, regex, color, x, y, lineNr)
  208.     local match = string.match(sLine, regex)
  209.     if match then
  210.         if type(color) == "number" then
  211.             gpu.setForeground(color)
  212.         else
  213.             gpu.setForeground(color(match))
  214.         end
  215.         --gpu.set(x, y - 1 + lineNr - scrollY, sLine)
  216.         term.write(match)
  217.         gpu.setForeground(textColor)
  218.         return string.sub(sLine, string.len(match) + 1)
  219.     end
  220.     return nil
  221. end
  222.  
  223. local function writeHighlighted(sLine, x, y, lineNr)
  224.     while string.len(sLine) > 0 do
  225.         sLine =
  226.             tryWrite(sLine, "^%-%-%[%[.-%]%]", commentColor, x, y, lineNr) or
  227.             tryWrite(sLine, "^%-%-.*", commentColor, x, y, lineNr)         or
  228.             tryWrite(sLine, "^\"\"", stringColor, x, y, lineNr)            or
  229.             tryWrite(sLine, "^\".-[^\\]\"", stringColor, x, y, lineNr)     or
  230.             tryWrite(sLine, "^\'\'", stringColor, x, y, lineNr)            or
  231.             tryWrite(sLine, "^\'.-[^\\]\'", stringColor, x, y, lineNr)     or
  232.             tryWrite(sLine, "^%[%[.-%]%]", stringColor, x, y, lineNr)      or
  233.             tryWrite(sLine, "^[%w_]+", function(match)
  234.                 if tonumber(match) ~= nil then
  235.                     return numberColor
  236.                 elseif tKeywords[match] then
  237.                     return keywordColor
  238.                 end
  239.                 return textColor
  240.             end, x, y, lineNr)                                             or
  241.             tryWrite(sLine, "^[^%w_]", textColor, x, y, lineNr)
  242.     end
  243. end
  244. -- SYNEDIT MODIFICATIONS end
  245.  
  246. local function drawLine(x, y, w, h, lineNr)
  247.   local yLocal = lineNr - scrollY
  248.   if yLocal > 0 and yLocal <= h then
  249.     local str = removePrefix(buffer[lineNr] or "", scrollX)
  250.     str = unicode.wlen(str) > w and unicode.wtrunc(str, w + 1) or str
  251.     str = text.padRight(str, w)
  252.     writeHighlighted(str, x, y, lineNr)
  253.     --gpu.set(x, y - 1 + lineNr - scrollY, str)
  254.   end
  255. end
  256.  
  257. local function getCursor()
  258.   local cx, cy = term.getCursor()
  259.   return cx + scrollX, cy + scrollY
  260. end
  261.  
  262. local function line()
  263.   local cbx, cby = getCursor()
  264.   return buffer[cby]
  265. end
  266.  
  267. local function getNormalizedCursor()
  268.   local cbx, cby = getCursor()
  269.   local wide, right = isWideAtPosition(buffer[cby], cbx)
  270.   if wide and right then
  271.     cbx = cbx - 1
  272.   end
  273.   return cbx, cby
  274. end
  275.  
  276. local function setCursor(nbx, nby)
  277.   local x, y, w, h = getArea()
  278.   nby = math.max(1, math.min(#buffer, nby))
  279.  
  280.   local ncy = nby - scrollY
  281.   if ncy > h then
  282.     term.setCursorBlink(false)
  283.     local sy = nby - h
  284.     local dy = math.abs(scrollY - sy)
  285.     scrollY = sy
  286.     if h > dy then
  287.       gpu.copy(x, y + dy, w, h - dy, 0, -dy)
  288.     end
  289.     for lineNr = nby - (math.min(dy, h) - 1), nby do
  290.       drawLine(x, y, w, h, lineNr)
  291.     end
  292.   elseif ncy < 1 then
  293.     term.setCursorBlink(false)
  294.     local sy = nby - 1
  295.     local dy = math.abs(scrollY - sy)
  296.     scrollY = sy
  297.     if h > dy then
  298.       gpu.copy(x, y, w, h - dy, 0, dy)
  299.     end
  300.     for lineNr = nby, nby + (math.min(dy, h) - 1) do
  301.       drawLine(x, y, w, h, lineNr)
  302.     end
  303.   end
  304.   term.setCursor(term.getCursor(), nby - scrollY)
  305.  
  306.   nbx = math.max(1, math.min(unicode.wlen(line()) + 1, nbx))
  307.   local wide, right = isWideAtPosition(line(), nbx)
  308.   local ncx = nbx - scrollX
  309.   if ncx > w or (ncx + 1 > w and wide and not right) then
  310.     term.setCursorBlink(false)
  311.     scrollX = nbx - w + ((wide and not right) and 1 or 0)
  312.     for lineNr = 1 + scrollY, math.min(h + scrollY, #buffer) do
  313.       drawLine(x, y, w, h, lineNr)
  314.     end
  315.   elseif ncx < 1 or (ncx - 1 < 1 and wide and right) then
  316.     term.setCursorBlink(false)
  317.     scrollX = nbx - 1 - ((wide and right) and 1 or 0)
  318.     for lineNr = 1 + scrollY, math.min(h + scrollY, #buffer) do
  319.       drawLine(x, y, w, h, lineNr)
  320.     end
  321.   end
  322.   term.setCursor(nbx - scrollX, nby - scrollY)
  323.   --update with term lib
  324.   nbx, nby = getCursor()
  325.   gpu.set(x + w - 10, y + h, text.padLeft(string.format("%d,%d", nby, nbx), 10))
  326. end
  327.  
  328. local function highlight(bx, by, length, enabled)
  329.   local x, y, w, h = getArea()
  330.   local cx, cy = bx - scrollX, by - scrollY
  331.   cx = math.max(1, math.min(w, cx))
  332.   cy = math.max(1, math.min(h, cy))
  333.   length = math.max(1, math.min(w - cx, length))
  334.  
  335.   local fg, fgp = gpu.getForeground()
  336.   local bg, bgp = gpu.getBackground()
  337.   if enabled then
  338.     gpu.setForeground(bg, bgp)
  339.     gpu.setBackground(fg, fgp)
  340.   end
  341.   local indexFrom = lengthToChars(buffer[by], bx)
  342.   local value = unicode.sub(buffer[by], indexFrom)
  343.   if unicode.wlen(value) > length then
  344.     value = unicode.wtrunc(value, length + 1)
  345.   end
  346.   gpu.set(x - 1 + cx, y - 1 + cy, value)
  347.   if enabled then
  348.     gpu.setForeground(fg, fgp)
  349.     gpu.setBackground(bg, bgp)
  350.   end
  351. end
  352.  
  353. local function home()
  354.   local cbx, cby = getCursor()
  355.   setCursor(1, cby)
  356. end
  357.  
  358. local function ende()
  359.   local cbx, cby = getCursor()
  360.   setCursor(unicode.wlen(line()) + 1, cby)
  361. end
  362.  
  363. local function left()
  364.   local cbx, cby = getNormalizedCursor()
  365.   if cbx > 1 then
  366.     local wideTarget, rightTarget = isWideAtPosition(line(), cbx - 1)
  367.     if wideTarget and rightTarget then
  368.       setCursor(cbx - 2, cby)
  369.     else
  370.       setCursor(cbx - 1, cby)
  371.     end
  372.     return true -- for backspace
  373.   elseif cby > 1 then
  374.     setCursor(cbx, cby - 1)
  375.     ende()
  376.     return true -- again, for backspace
  377.   end
  378. end
  379.  
  380. local function right(n)
  381.   n = n or 1
  382.   local cbx, cby = getNormalizedCursor()
  383.   local be = unicode.wlen(line()) + 1
  384.   local wide, right = isWideAtPosition(line(), cbx + n)
  385.   if wide and right then
  386.     n = n + 1
  387.   end
  388.   if cbx + n <= be then
  389.     setCursor(cbx + n, cby)
  390.   elseif cby < #buffer then
  391.     setCursor(1, cby + 1)
  392.   end
  393. end
  394.  
  395. local function up(n)
  396.   n = n or 1
  397.   local cbx, cby = getCursor()
  398.   if cby > 1 then
  399.     setCursor(cbx, cby - n)
  400.   end
  401. end
  402.  
  403. local function down(n)
  404.   n = n or 1
  405.   local cbx, cby = getCursor()
  406.   if cby < #buffer then
  407.     setCursor(cbx, cby + n)
  408.   end
  409. end
  410.  
  411. local function delete(fullRow)
  412.   local cx, cy = term.getCursor()
  413.   local cbx, cby = getCursor()
  414.   local x, y, w, h = getArea()
  415.   local function deleteRow(row)
  416.     local content = table.remove(buffer, row)
  417.     local rcy = cy + (row - cby)
  418.     if rcy <= h then
  419.       gpu.copy(x, y + rcy, w, h - rcy, 0, -1)
  420.       drawLine(x, y, w, h, row + (h - rcy))
  421.     end
  422.     return content
  423.   end
  424.   if fullRow then
  425.     term.setCursorBlink(false)
  426.     if #buffer > 1 then
  427.       deleteRow(cby)
  428.     else
  429.       buffer[cby] = ""
  430.       gpu.fill(x, y - 1 + cy, w, 1, " ")
  431.     end
  432.     setCursor(1, cby)
  433.   elseif cbx <= unicode.wlen(line()) then
  434.     term.setCursorBlink(false)
  435.     local index = lengthToChars(line(), cbx)
  436.     buffer[cby] = unicode.sub(line(), 1, index - 1) ..
  437.                   unicode.sub(line(), index + 1)
  438.     drawLine(x, y, w, h, cby)
  439.   elseif cby < #buffer then
  440.     term.setCursorBlink(false)
  441.     local append = deleteRow(cby + 1)
  442.     buffer[cby] = buffer[cby] .. append
  443.     drawLine(x, y, w, h, cby)
  444.   else
  445.     return
  446.   end
  447.   setStatus(helpStatusText())
  448. end
  449.  
  450. local function insert(value)
  451.   if not value or unicode.len(value) < 1 then
  452.     return
  453.   end
  454.   term.setCursorBlink(false)
  455.   local cx, cy = term.getCursor()
  456.   local cbx, cby = getCursor()
  457.   local x, y, w, h = getArea()
  458.   local index = lengthToChars(line(), cbx)
  459.   buffer[cby] = unicode.sub(line(), 1, index - 1) ..
  460.                 value ..
  461.                 unicode.sub(line(), index)
  462.   drawLine(x, y, w, h, cby)
  463.   right(unicode.wlen(value))
  464.   setStatus(helpStatusText())
  465. end
  466.  
  467. local function enter()
  468.   term.setCursorBlink(false)
  469.   local cx, cy = term.getCursor()
  470.   local cbx, cby = getCursor()
  471.   local x, y, w, h = getArea()
  472.   local index = lengthToChars(line(), cbx)
  473.   table.insert(buffer, cby + 1, unicode.sub(buffer[cby], index))
  474.   buffer[cby] = unicode.sub(buffer[cby], 1, index - 1)
  475.   drawLine(x, y, w, h, cby)
  476.   if cy < h then
  477.     if cy < h - 1 then
  478.       gpu.copy(x, y + cy, w, h - (cy + 1), 0, 1)
  479.     end
  480.     drawLine(x, y, w, h, cby + 1)
  481.   end
  482.   setCursor(1, cby + 1)
  483.   setStatus(helpStatusText())
  484. end
  485.  
  486. local findText = ""
  487.  
  488. local function find()
  489.   local x, y, w, h = getArea()
  490.   local cx, cy = term.getCursor()
  491.   local cbx, cby = getCursor()
  492.   local ibx, iby = cbx, cby
  493.   while running do
  494.     if unicode.len(findText) > 0 then
  495.       local sx, sy
  496.       for syo = 1, #buffer do -- iterate lines with wraparound
  497.         sy = (iby + syo - 1 + #buffer - 1) % #buffer + 1
  498.         sx = string.find(buffer[sy], findText, syo == 1 and ibx or 1, true)
  499.         if sx and (sx >= ibx or syo > 1) then
  500.           break
  501.         end
  502.       end
  503.       if not sx then -- special case for single matches
  504.         sy = iby
  505.         sx = string.find(buffer[sy], findText, nil, true)
  506.       end
  507.       if sx then
  508.         sx = unicode.wlen(string.sub(buffer[sy], 1, sx - 1)) + 1
  509.         cbx, cby = sx, sy
  510.         setCursor(cbx, cby)
  511.         highlight(cbx, cby, unicode.wlen(findText), true)
  512.       end
  513.     end
  514.     term.setCursor(7 + unicode.wlen(findText), h + 1)
  515.     setStatus("Find: " .. findText)
  516.  
  517.     local _, address, char, code = term.pull("key_down")
  518.     if address == term.keyboard() then
  519.       local handler, name = getKeyBindHandler(code)
  520.       highlight(cbx, cby, unicode.wlen(findText), false)
  521.       if name == "newline" then
  522.         break
  523.       elseif name == "close" then
  524.         handler()
  525.       elseif name == "backspace" then
  526.         findText = unicode.sub(findText, 1, -2)
  527.       elseif name == "find" or name == "findnext" then
  528.         ibx = cbx + 1
  529.         iby = cby
  530.       elseif not keyboard.isControl(char) then
  531.         findText = findText .. unicode.char(char)
  532.       end
  533.     end
  534.   end
  535.   setCursor(cbx, cby)
  536.   setStatus(helpStatusText())
  537. end
  538.  
  539. -------------------------------------------------------------------------------
  540.  
  541. local keyBindHandlers = {
  542.   left = left,
  543.   right = right,
  544.   up = up,
  545.   down = down,
  546.   home = home,
  547.   eol = ende,
  548.   pageUp = function()
  549.     local x, y, w, h = getArea()
  550.     up(h - 1)
  551.   end,
  552.   pageDown = function()
  553.     local x, y, w, h = getArea()
  554.     down(h - 1)
  555.   end,
  556.  
  557.   backspace = function()
  558.     if not readonly and left() then
  559.       delete()
  560.     end
  561.   end,
  562.   delete = function()
  563.     if not readonly then
  564.       delete()
  565.     end
  566.   end,
  567.   deleteLine = function()
  568.     if not readonly then
  569.       delete(true)
  570.     end
  571.   end,
  572.   newline = function()
  573.     if not readonly then
  574.       enter()
  575.     end
  576.   end,
  577.  
  578.   save = function()
  579.     if readonly then return end
  580.     local new = not fs.exists(filename)
  581.     local backup
  582.     if not new then
  583.       backup = filename .. "~"
  584.       for i = 1, math.huge do
  585.         if not fs.exists(backup) then
  586.           break
  587.         end
  588.         backup = filename .. "~" .. i
  589.       end
  590.       fs.copy(filename, backup)
  591.     end
  592.     local f, reason = io.open(filename, "w")
  593.     if f then
  594.       local chars, firstLine = 0, true
  595.       for _, line in ipairs(buffer) do
  596.         if not firstLine then
  597.           line = "\n" .. line
  598.         end
  599.         firstLine = false
  600.         f:write(line)
  601.         chars = chars + unicode.len(line)
  602.       end
  603.       f:close()
  604.       local format
  605.       if new then
  606.         format = [["%s" [New] %dL,%dC written]]
  607.       else
  608.         format = [["%s" %dL,%dC written]]
  609.       end
  610.       setStatus(string.format(format, fs.name(filename), #buffer, chars))
  611.     else
  612.       setStatus(reason)
  613.     end
  614.     if not new then
  615.       fs.remove(backup)
  616.     end
  617.   end,
  618.   close = function()
  619.     -- TODO ask to save if changed
  620.     running = false
  621.   end,
  622.   find = function()
  623.     findText = ""
  624.     find()
  625.   end,
  626.   findnext = find
  627. }
  628.  
  629. getKeyBindHandler = function(code)
  630.   if type(config.keybinds) ~= "table" then return end
  631.   -- Look for matches, prefer more 'precise' keybinds, e.g. prefer
  632.   -- ctrl+del over del.
  633.   local result, resultName, resultWeight = nil, nil, 0
  634.   for command, keybinds in pairs(config.keybinds) do
  635.     if type(keybinds) == "table" and keyBindHandlers[command] then
  636.       for _, keybind in ipairs(keybinds) do
  637.         if type(keybind) == "table" then
  638.           local alt, control, shift, key
  639.           for _, value in ipairs(keybind) do
  640.             if value == "alt" then alt = true
  641.             elseif value == "control" then control = true
  642.             elseif value == "shift" then shift = true
  643.             else key = value end
  644.           end
  645.           local keyboardAddress = term.keyboard()
  646.           if (not alt or keyboard.isAltDown(keyboardAddress)) and
  647.              (not control or keyboard.isControlDown(keyboardAddress)) and
  648.              (not shift or keyboard.isShiftDown(keyboardAddress)) and
  649.              code == keyboard.keys[key] and
  650.              #keybind > resultWeight
  651.           then
  652.             resultWeight = #keybind
  653.             resultName = command
  654.             result = keyBindHandlers[command]
  655.           end
  656.         end
  657.       end
  658.     end
  659.   end
  660.   return result, resultName
  661. end
  662.  
  663. -------------------------------------------------------------------------------
  664.  
  665. local function onKeyDown(char, code)
  666.   local handler = getKeyBindHandler(code)
  667.   if handler then
  668.     handler()
  669.   elseif readonly and code == keyboard.keys.q then
  670.     running = false
  671.   elseif not readonly then
  672.     if not keyboard.isControl(char) then
  673.       insert(unicode.char(char))
  674.     elseif unicode.char(char) == "\t" then
  675.       insert("  ")
  676.     end
  677.   end
  678. end
  679.  
  680. local function onClipboard(value)
  681.   value = value:gsub("\r\n", "\n")
  682.   local cbx, cby = getCursor()
  683.   local start = 1
  684.   local l = value:find("\n", 1, true)
  685.   if l then
  686.     repeat
  687.       local line = string.sub(value, start, l - 1)
  688.       line = text.detab(line, 2)
  689.       insert(line)
  690.       enter()
  691.       start = l + 1
  692.       l = value:find("\n", start, true)
  693.     until not l
  694.   end
  695.   insert(string.sub(value, start))
  696. end
  697.  
  698. local function onClick(x, y)
  699.   setCursor(x + scrollX, y + scrollY)
  700. end
  701.  
  702. local function onScroll(direction)
  703.   local cbx, cby = getCursor()
  704.   setCursor(cbx, cby - direction * 12)
  705. end
  706.  
  707. -------------------------------------------------------------------------------
  708.  
  709. do
  710.   local f = io.open(filename)
  711.   if f then
  712.     local x, y, w, h = getArea()
  713.     local chars = 0
  714.     for line in f:lines() do
  715.       if line:sub(-1) == "\r" then
  716.         line = line:sub(1, -2)
  717.       end
  718.       table.insert(buffer, line)
  719.       chars = chars + unicode.len(line)
  720.       if #buffer <= h then
  721.         drawLine(x, y, w, h, #buffer)
  722.       end
  723.     end
  724.     f:close()
  725.     if #buffer == 0 then
  726.       table.insert(buffer, "")
  727.     end
  728.     local format
  729.     if readonly then
  730.       format = [["%s" [readonly] %dL,%dC]]
  731.     else
  732.       format = [["%s" %dL,%dC]]
  733.     end
  734.     setStatus(string.format(format, fs.name(filename), #buffer, chars))
  735.   else
  736.     table.insert(buffer, "")
  737.     setStatus(string.format([["%s" [New File] ]], fs.name(filename)))
  738.   end
  739.   setCursor(1, 1)
  740. end
  741.  
  742. while running do
  743.   local event, address, arg1, arg2, arg3 = term.pull()
  744.   if address == term.keyboard() or address == term.screen() then
  745.     local blink = true
  746.     if event == "key_down" then
  747.       onKeyDown(arg1, arg2)
  748.     elseif event == "clipboard" and not readonly then
  749.       onClipboard(arg1)
  750.     elseif event == "touch" or event == "drag" then
  751.       local x, y, w, h = getArea()
  752.       arg1 = arg1 - x + 1
  753.       arg2 = arg2 - y + 1
  754.       if arg1 >= 1 and arg2 >= 1 and arg1 <= w and arg2 <= h then
  755.         onClick(arg1, arg2)
  756.       end
  757.     elseif event == "scroll" then
  758.       onScroll(arg3)
  759.     else
  760.       blink = false
  761.     end
  762.     if blink then
  763.       term.setCursorBlink(true)
  764.     end
  765.   end
  766. end
  767.  
  768. term.clear()
  769. term.setCursorBlink(false)
RAW Paste Data