slipers

Minecraft. OpenComputers. Editor (tedit.lua).

Sep 1st, 2018
106
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 10.74 KB | None | 0 0
  1. local component = require("component")
  2. local event = require("event")
  3. local fs = require("filesystem")
  4. local keyboard = require("keyboard")
  5. local shell = require("shell")
  6. local term = require("term")
  7. local text = require("text")
  8. local unicode = require("unicode")
  9.  
  10. if not term.isAvailable() then
  11.   return
  12. end
  13.  
  14. local args, options = shell.parse(...)
  15. if #args == 0 then
  16.   io.write("Usage: edit <filename>")
  17.   return
  18. end
  19.  
  20. local filename = shell.resolve(args[1])
  21.  
  22. local readonly = options.r or fs.get(filename) == nil or fs.get(filename).isReadOnly()
  23.  
  24. if not fs.exists(filename) then
  25.   if fs.isDirectory(filename) then
  26.     io.stderr:write("file is a directory")
  27.     return
  28.   elseif readonly then
  29.     io.stderr:write("file system is read only")
  30.     return
  31.   end
  32. end
  33.  
  34. term.clear()
  35. term.setCursorBlink(true)
  36.  
  37. local running = true
  38. local buffer = {}
  39. local scrollX, scrollY = 0, 0
  40.  
  41. -------------------------------------------------------------------------------
  42.  
  43. local function setStatus(value)
  44.   local w, h = component.gpu.getResolution()
  45.   component.gpu.set(1, h, text.padRight(unicode.sub(value, 1, w - 10), w - 10))
  46. end
  47.  
  48. local function getSize()
  49.   local w, h = component.gpu.getResolution()
  50.   return w, h - 1
  51. end
  52.  
  53. local function getCursor()
  54.   local cx, cy = term.getCursor()
  55.   return cx + scrollX, cy + scrollY
  56. end
  57.  
  58. local function line()
  59.   local cbx, cby = getCursor()
  60.   return buffer[cby]
  61. end
  62.  
  63. local function setCursor(nbx, nby)
  64.   local w, h = getSize()
  65.   nby = math.max(1, math.min(#buffer, nby))
  66.  
  67.   local ncy = nby - scrollY
  68.   if ncy > h then
  69.     term.setCursorBlink(false)
  70.     local sy = nby - h
  71.     local dy = math.abs(scrollY - sy)
  72.     scrollY = sy
  73.     component.gpu.copy(1, 1 + dy, w, h - dy, 0, -dy)
  74.     for by = nby - (dy - 1), nby do
  75.       local str = text.padRight(unicode.sub(buffer[by], 1 + scrollX), w)
  76.       component.gpu.set(1, by - scrollY, str)
  77.     end
  78.   elseif ncy < 1 then
  79.     term.setCursorBlink(false)
  80.     local sy = nby - 1
  81.     local dy = math.abs(scrollY - sy)
  82.     scrollY = sy
  83.     component.gpu.copy(1, 1, w, h - dy, 0, dy)
  84.     for by = nby, nby + (dy - 1) do
  85.       local str = text.padRight(unicode.sub(buffer[by], 1 + scrollX), w)
  86.       component.gpu.set(1, by - scrollY, str)
  87.     end
  88.   end
  89.   term.setCursor(term.getCursor(), nby - scrollY)
  90.  
  91.   nbx = math.max(1, math.min(unicode.len(line()) + 1, nbx))
  92.   local ncx = nbx - scrollX
  93.   if ncx > w then
  94.     term.setCursorBlink(false)
  95.     local sx = nbx - w
  96.     local dx = math.abs(scrollX - sx)
  97.     scrollX = sx
  98.     component.gpu.copy(1 + dx, 1, w - dx, h, -dx, 0)
  99.     for by = 1 + scrollY, math.min(h + scrollY, #buffer) do
  100.       local str = unicode.sub(buffer[by], nbx - (dx - 1), nbx)
  101.       str = text.padRight(str, dx)
  102.       component.gpu.set(1 + (w - dx), by - scrollY, str)
  103.     end
  104.   elseif ncx < 1 then
  105.     term.setCursorBlink(false)
  106.     local sx = nbx - 1
  107.     local dx = math.abs(scrollX - sx)
  108.     scrollX = sx
  109.     component.gpu.copy(1, 1, w - dx, h, dx, 0)
  110.     for by = 1 + scrollY, math.min(h + scrollY, #buffer) do
  111.       local str = unicode.sub(buffer[by], nbx, nbx + dx)
  112.       --str = text.padRight(str, dx)
  113.       component.gpu.set(1, by - scrollY, str)
  114.     end
  115.   end
  116.   term.setCursor(nbx - scrollX, nby - scrollY)
  117.  
  118.   component.gpu.set(w - 9, h + 1, text.padLeft(string.format("%d,%d", nby, nbx), 10))
  119. end
  120.  
  121. local function home()
  122.   local cbx, cby = getCursor()
  123.   setCursor(1, cby)
  124. end
  125.  
  126. local function ende()
  127.   local cbx, cby = getCursor()
  128.   setCursor(unicode.len(line()) + 1, cby)
  129. end
  130.  
  131. local function left()
  132.   local cbx, cby = getCursor()
  133.   if cbx > 1 then
  134.     setCursor(cbx - 1, cby)
  135.     return true -- for backspace
  136.   elseif cby > 1 then
  137.     setCursor(cbx, cby - 1)
  138.     ende()
  139.     return true -- again, for backspace
  140.   end
  141. end
  142.  
  143. local function right(n)
  144.   n = n or 1
  145.   local cbx, cby = getCursor()
  146.   local be = unicode.len(line()) + 1
  147.   if cbx < be then
  148.     setCursor(cbx + n, cby)
  149.   elseif cby < #buffer then
  150.     setCursor(1, cby + 1)
  151.   end
  152. end
  153.  
  154. local function up(n)
  155.   n = n or 1
  156.   local cbx, cby = getCursor()
  157.   if cby > 1 then
  158.     setCursor(cbx, cby - n)
  159.     if getCursor() > unicode.len(line()) then
  160.       ende()
  161.     end
  162.   end
  163. end
  164.  
  165. local function down(n)
  166.   n = n or 1
  167.   local cbx, cby = getCursor()
  168.   if cby < #buffer then
  169.     setCursor(cbx, cby + n)
  170.     if getCursor() > unicode.len(line()) then
  171.       ende()
  172.     end
  173.   end
  174. end
  175.  
  176. local function delete()
  177.   local cx, cy = term.getCursor()
  178.   local cbx, cby = getCursor()
  179.   local w, h = getSize()
  180.   if cbx <= unicode.len(line()) then
  181.     term.setCursorBlink(false)
  182.     buffer[cby] = unicode.sub(line(), 1, cbx - 1) ..
  183.                   unicode.sub(line(), cbx + 1)
  184.     component.gpu.copy(cx + 1, cy, w - cx, 1, -1, 0)
  185.     local br = cbx + (w - cx)
  186.     local char = unicode.sub(line(), br, br)
  187.     if not char or unicode.len(char) == 0 then
  188.       char = " "
  189.     end
  190.     component.gpu.set(w, cy, char)
  191.   elseif cby < #buffer then
  192.     term.setCursorBlink(false)
  193.     local append = table.remove(buffer, cby + 1)
  194.     buffer[cby] = buffer[cby] .. append
  195.     component.gpu.set(cx, cy, append)
  196.     if cy < h then
  197.       component.gpu.copy(1, cy + 2, w, h - (cy + 1), 0, -1)
  198.       component.gpu.set(1, h, text.padRight(buffer[cby + (h - cy)], w))
  199.     end
  200.     setStatus("Save: [Ctrl+S] Close: [Ctrl+W]")
  201.   end
  202. end
  203.  
  204. local function insert(value)
  205.   if not value or unicode.len(value) < 1 then
  206.     return
  207.   end
  208.   term.setCursorBlink(false)
  209.   local cx, cy = term.getCursor()
  210.   local cbx, cby = getCursor()
  211.   local w, h = getSize()
  212.   buffer[cby] = unicode.sub(line(), 1, cbx - 1) ..
  213.                 value ..
  214.                 unicode.sub(line(), cbx)
  215.   local len = unicode.len(value)
  216.   local n = w - (cx - 1) - len
  217.   if n > 0 then
  218.     component.gpu.copy(cx, cy, n, 1, len, 0)
  219.   end
  220.   component.gpu.set(cx, cy, value)
  221.   right(len)
  222.   setStatus("Save: [Ctrl+S] Close: [Ctrl+W]")
  223. end
  224.  
  225. local function enter()
  226.   term.setCursorBlink(false)
  227.   local cx, cy = term.getCursor()
  228.   local cbx, cby = getCursor()
  229.   local w, h = getSize()
  230.   local s = 0
  231.   for i = 1, #buffer[cby] do
  232.     if buffer[cby]:sub(i, i) == " " then
  233.       s = s + 1
  234.     else
  235.       break
  236.     end
  237.   end
  238.   table.insert(buffer, cby + 1, string.rep(" ", s) .. unicode.sub(buffer[cby], cbx))
  239.   buffer[cby] = unicode.sub(buffer[cby], 1, cbx - 1)
  240.   component.gpu.fill(cx, cy, w - (cx - 1), 1, " ")
  241.   if cy < h then
  242.     if cy < h - 1 then
  243.       component.gpu.copy(1, cy + 1, w, h - (cy + 1), 0, 1)
  244.     end
  245.     component.gpu.set(1, cy + 1, text.padRight(buffer[cby + 1], w))
  246.   end
  247.   setCursor(1 + s, cby + 1)
  248.   setStatus("Save: [Ctrl+S] Close: [Ctrl+W]")
  249. end
  250.  
  251. local controlKeyCombos = {[keyboard.keys.s]=true,[keyboard.keys.w]=true,
  252.                           [keyboard.keys.c]=true,[keyboard.keys.x]=true}
  253. local function onKeyDown(char, code)
  254.   if code == keyboard.keys.back and not readonly then
  255.     if left() then
  256.       delete()
  257.     end
  258.   elseif code == keyboard.keys.delete and not readonly then
  259.     delete()
  260.   elseif code == keyboard.keys.left then
  261.     left()
  262.   elseif code == keyboard.keys.right then
  263.     right()
  264.   elseif code == keyboard.keys.home then
  265.     home()
  266.   elseif code == keyboard.keys["end"] then
  267.     ende()
  268.   elseif code == keyboard.keys.up then
  269.     up()
  270.   elseif code == keyboard.keys.down then
  271.     down()
  272.   elseif code == keyboard.keys.pageUp then
  273.     local w, h = getSize()
  274.     up(h - 1)
  275.   elseif code == keyboard.keys.pageDown then
  276.     local w, h = getSize()
  277.     down(h - 1)
  278.   elseif code == keyboard.keys.enter and not readonly then
  279.     enter()
  280.   elseif keyboard.isControlDown() and controlKeyCombos[code] then
  281.     local cbx, cby = getCursor()
  282.     if code == keyboard.keys.s and not readonly then
  283.       local new = not fs.exists(filename)
  284.       local f, reason = io.open(filename, "w")
  285.       if f then
  286.         local chars, firstLine = 0, true
  287.         for _, line in ipairs(buffer) do
  288.           if not firstLine then
  289.             line = "\n" .. line
  290.           end
  291.           firstLine = false
  292.           f:write(line)
  293.           chars = chars + unicode.len(line)
  294.         end
  295.         f:close()
  296.         local format
  297.         if new then
  298.           format = [["%s" [New] %dL,%dC written]]
  299.         else
  300.           format = [["%s" %dL,%dC written]]
  301.         end
  302.         setStatus(string.format(format, fs.name(filename), #buffer, chars))
  303.       else
  304.         setStatus(reason)
  305.       end
  306.     elseif code == keyboard.keys.w or
  307.            code == keyboard.keys.c or
  308.            code == keyboard.keys.x
  309.     then
  310.       -- TODO ask to save if changed
  311.       running = false
  312.     end
  313.   elseif readonly and code == keyboard.keys.q then
  314.     running = false
  315.   elseif not readonly then
  316.     if not keyboard.isControl(char) then
  317.       insert(unicode.char(char))
  318.     elseif unicode.char(char) == "\t" then
  319.       insert("  ")
  320.     end
  321.   end
  322. end
  323.  
  324. local function onClipboard(value)
  325.   local cbx, cby = getCursor()
  326.   local start = 1
  327.   local l = value:find("\n", 1, true)
  328.   if l then
  329.     repeat
  330.       insert(string.sub(value, start, l - 1))
  331.       enter()
  332.       start = l + 1
  333.       l = value:find("\n", start, true)
  334.     until not l
  335.   end
  336.   insert(string.sub(value, start))
  337. end
  338.  
  339. local function onClick(x, y)
  340.   setCursor(x + scrollX, y + scrollY)
  341. end
  342.  
  343. local function onScroll(direction)
  344.   local cbx, cby = getCursor()
  345.   setCursor(cbx, cby - direction * 12)
  346. end
  347.  
  348. -------------------------------------------------------------------------------
  349.  
  350. do
  351.   local f = io.open(filename)
  352.   if f then
  353.     local w, h = getSize()
  354.     local chars = 0
  355.     for line in f:lines() do
  356.       table.insert(buffer, line)
  357.       chars = chars + unicode.len(line)
  358.       if #buffer <= h then
  359.         component.gpu.set(1, #buffer, line)
  360.       end
  361.     end
  362.     f:close()
  363.     if #buffer == 0 then
  364.       table.insert(buffer, "")
  365.     end
  366.     local format
  367.     if readonly then
  368.       format = [["%s" [readonly] %dL,%dC]]
  369.     else
  370.       format = [["%s" %dL,%dC]]
  371.     end
  372.     setStatus(string.format(format, fs.name(filename), #buffer, chars))
  373.   else
  374.     table.insert(buffer, "")
  375.     setStatus(string.format([["%s" [New File] ]], fs.name(filename)))
  376.   end
  377.   setCursor(1, 1)
  378. end
  379.  
  380. while running do
  381.   local event, address, arg1, arg2, arg3 = event.pull()
  382.   if type(address) == "string" and component.isPrimary(address) then
  383.     local blink = true
  384.     if event == "key_down" then
  385.       onKeyDown(arg1, arg2)
  386.     elseif event == "clipboard" then
  387.       onClipboard(arg1)
  388.     elseif event == "touch" or event == "drag" then
  389.       onClick(arg1, arg2)
  390.     elseif event == "scroll" then
  391.       onScroll(arg3)
  392.     else
  393.       blink = false
  394.     end
  395.     if blink then
  396.       term.setCursorBlink(true)
  397.       term.setCursorBlink(true) -- force toggle to caret
  398.     end
  399.   end
  400. end
  401.  
  402. term.clear()
  403. term.setCursorBlink(false)
Add Comment
Please, Sign In to add comment