Kasama

nano-opencomputer

Aug 21st, 2020
580
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 19.99 KB | None | 0 0
  1. --[[
  2. nano clone based off GNU nano for OpenComputers
  3. by Gamax92
  4. --]]
  5. local component = require("component")
  6. local computer = require("computer")
  7. local kbd = require("keyboard")
  8. local keys = kbd.keys
  9. local fs = require("filesystem")
  10. local shell = require("shell")
  11. local event = require("event")
  12. local term = require("term")
  13. local unicode = require("unicode")
  14.  
  15. local nanoVERSION = "1.0.0"
  16.  
  17. local function printf(...) print(string.format(...)) end
  18. local function eprintf(...) io.stderr:write(string.format(...) .. "\n") end
  19. local event_pull = term.pull or event.pull
  20. -- Fix keys.none
  21. keys["none"] = 0
  22. keys[0] = "none"
  23.  
  24. -- TODO: Replace with more gnuopt style parser
  25. local args, opts = shell.parse(...)
  26.  
  27. -- TODO: Once above has been done, check for invalid arguments
  28. -- Default options
  29. local options = {
  30.     fgcolor = 0xFFFFFF,
  31.     bgcolor = 0x000000,
  32.     tabsize = 8,
  33. }
  34. local flags={"autoindent","backup","backwards","brighttext","casesensitive","const","cut","morespace","mouse","multibuffer","noconvert","nohelp","nonewlines","nowrap","quiet","quickblank","patterns","smarthome","smooth","softwrap","tabstospaces","tempfile","undo","view"}
  35. for i = 1,#flags do
  36.     options[flags[i]] = false
  37. end
  38. -- TODO: HACK, REMOVE WHEN HELP AREA IS IMPLEMENTED
  39. options.nohelp = true
  40. flags = nil
  41. -- Default bindings
  42. -- TODO: Bindings for things other than main
  43. local bind = {
  44.     main = {
  45.         {"help","^G","F1"},
  46.         {"exit","^X","F2"},
  47.         {"writeout","^O","F3"},
  48.         {"insert","^R","F5","insert"},
  49.         {"whereis","^W","F6"},
  50.         {"replace","^\\","M-R","F14"},
  51.         {"cut","^K","F9"},
  52.         {"uncut","^U","F10"},
  53.         {"justify","^J","F4"},
  54.         {"speller","^T","F12"},
  55.         {"curpos","^C","F11"},
  56.         {"gotoline","^_","M-G","F13"},
  57.         {"prevpage","^Y","F7","pageUp"},
  58.         {"nextpage","^V","F8","pageDown"},
  59.         {"firstline","M-\\","M-|"},
  60.         {"lastline","M-/","M-?"},
  61.         {"searchagain","M-W","F16"},
  62.         {"findbracket","M-]"},
  63.         {"mark","^^","M-A","F15"},
  64.         {"copytext","M-^","M-6"},
  65.         {"indent","M-}"},
  66.         {"unindent","M-{"},
  67.         {"undo","M-U"},
  68.         {"redo","M-E"},
  69.         {"left","^B","left"},
  70.         {"right","^F","right"},
  71.         {"prevword","M-Space"},
  72.         {"nextword","^Space"},
  73.         {"home","^A","home"},
  74.         {"end","^E","end"},
  75.         {"prevline","^P","up"},
  76.         {"nextline","^N","down"},
  77.         {"beginpara","M-(","M-9"},
  78.         {"endpara","M-)","M-0"},
  79.         {"scrollup","M--","M-_"},
  80.         {"scrolldown","M-+","M-="},
  81.         {"prevbuf","M-<","M-,"},
  82.         {"nextbuf","M->","M-."},
  83.         {"verbatim","M-V"},
  84.         {"tab","^I"},
  85.         {"enter","^M","enter"},
  86.         {"delete","^D","delete"},
  87.         {"backspace","^H","back"},
  88.         {"cutrestoffile","M-T"},
  89.         {"fulljustify","M-J"},
  90.         {"wordcount","M-D"},
  91.         {"refresh","^L"},
  92.         {"suspend","^Z"},
  93.         {"nohelp","M-X"},
  94.         {"constupdate","M-C"},
  95.         {"morespace","M-O"},
  96.         {"smoothscroll","M-S"},
  97.         {"softwrap","M-$"},
  98.         {"whitespacedisplay","M-P"},
  99.         {"nosyntax","M-Y"},
  100.         {"smarthome","M-H"},
  101.         {"autoindent","M-I"},
  102.         {"cuttoend","M-K"},
  103.         {"nowrap","M-L"},
  104.         {"tabstospaces","M-Q"},
  105.         {"backupfile","M-B"},
  106.         {"multibuffer","M-F"},
  107.         {"mouse","M-M"},
  108.         {"noconvert","M-N"},
  109.         {"suspendenable","M-Z"},
  110.     }
  111. }
  112. local vfn = {}
  113. for k,v in pairs(bind) do
  114.     for i = 1,#v do
  115.         local b = v[i]
  116.         for j = 2,#b do
  117.             v[unicode.upper(b[j])]=b[1]
  118.         end
  119.         vfn[b[1]]=true
  120.         v[i] = nil
  121.     end
  122. end
  123.  
  124. local function parseRC(filename)
  125.     local problem = false
  126.     local i = 1
  127.     local function bad(msg, ...)
  128.         problem = true
  129.         printf("Error in %s on line %d: " .. msg, filename, i, ...)
  130.     end
  131.     local file, err = io.open(filename,"rb")
  132.     if not file then
  133.         problem = true; printf("Error reading %s: %s", filename, err)
  134.     else
  135.         for line in file:lines() do
  136.             local cmd = {}
  137.             for part in line:gmatch("%S+") do
  138.                 cmd[#cmd+1] = part
  139.             end
  140.             if cmd[1] == "set" or cmd[1] == "unset" then
  141.                 if #cmd < 2 then
  142.                     bad("Missing flag")
  143.                 elseif options[cmd[2]] ~= nil then
  144.                     if type(options[cmd[2]]) == "boolean" then
  145.                         options[cmd[2]] = cmd[1] == "set"
  146.                     elseif cmd[1] == "unset" then
  147.                         bad("Cannot unset option \"%s\"", cmd[2])
  148.                     elseif type(options[cmd[2]]) == "number" then
  149.                         if tonumber(cmd[3]) == nil then
  150.                             bad("Parameter \"%s\" is invalid", cmd[3])
  151.                         else
  152.                             options[cmd[2]] = tonumber(cmd[3])
  153.                         end
  154.                     end
  155.                 else
  156.                     bad("Unknown flag \"%s\"", cmd[2])
  157.                 end
  158.             elseif cmd[1] == "bind" or cmd[1] == "unbind" then
  159.                 if cmd[2] == nil then
  160.                     bad("Missing key name")
  161.                 else
  162.                     local first = unicode.upper(cmd[2]:sub(1,1))
  163.                     local last = cmd[1] == "bind" and cmd[4] or cmd[3]
  164.                     if first ~= "^" and first ~= "M" and first ~= "F" then
  165.                         bad("Key name must begin with \"^\", \"M\", or \"F\"")
  166.                     elseif cmd[1] == "bind" and cmd[3] == nil then
  167.                         bad("Must specify a function to bind the key to")
  168.                     elseif last == nil then
  169.                         bad("Must specify a menu (or \"all\") in which to bind/unbind the key")
  170.                     elseif cmd[1] == "bind" and not vfn[cmd[3]] then
  171.                         bad("Cannot map name \"%s\" to a function", cmd[3])
  172.                     elseif bind[last] == nil then
  173.                         bad("Cannot map name \"%s\" to a menu", last)
  174.                     else
  175.                         if cmd[1] == "bind" then
  176.                             bind[last][cmd[2]] = cmd[3]
  177.                         elseif cmd[1] == "unbind" then
  178.                             bind[last][cmd[2]] = nil
  179.                         end
  180.                     end
  181.                 end
  182.             else
  183.                 bad("Command \"%s\" not understood", cmd[1])
  184.             end
  185.             i = i + 1
  186.         end
  187.     end
  188.     if problem then
  189.         print("Press Enter to continue starting nano.")
  190.         while true do
  191.             local name,_,_,code = event_pull()
  192.             if name == "key_down" and code == keys.enter then break end
  193.         end
  194.     end
  195. end
  196. if fs.exists("/etc/nanorc") then
  197.     parseRC("/etc/nanorc")
  198. end
  199. -- TODO: Parse arguments
  200.  
  201. local buffers, buffer = {}
  202. local gpu = component.gpu
  203. gpu.setForeground(options.fgcolor)
  204. gpu.setBackground(options.bgcolor)
  205. local gpuW, gpuH = gpu.getResolution()
  206. gpu.fill(1,1,gpuW,gpuH," ")
  207. term.setCursorBlink(true)
  208.  
  209. -- TODO: more modes
  210. local mode = "main"
  211. -- Calculate positions for screen elements
  212. local linesY = (options.morespace and 2 or 3)
  213. local linesH = gpuH - linesY
  214. local statusbarY = gpuH
  215. if not options.nohelp then
  216.     linesH = linesH - 2
  217.     statusbarY = statusbarY - 2
  218. end
  219. -- TODO: Help lines
  220.  
  221.  
  222. local utf8char = "[%z\1-\127\194-\244][\128-\191]*"
  223. local function formatLine(text,map)
  224.     local newstr, pos = {}, 0
  225.     local cmap
  226.     if map then
  227.         cmap = {}
  228.     end
  229.     for char in text:gmatch(utf8char) do
  230.         if map then
  231.             cmap[#cmap+1] = pos+1
  232.         end
  233.         if char == "\t" then
  234.             local amt = options.tabsize - pos%options.tabsize
  235.             for i = 1,amt do
  236.                 newstr[#newstr+1] = " "
  237.             end
  238.             pos = pos + amt
  239.         else
  240.             newstr[#newstr+1] = char
  241.             pos = pos + unicode.charWidth(char)
  242.         end
  243.     end
  244.     if map then
  245.         cmap[#cmap+1] = pos+1
  246.     end
  247.     return table.concat(newstr), cmap
  248. end
  249.  
  250. local function setTitleBar(buffer)
  251.     local title = " New Buffer "
  252.     if buffer.filename ~= nil then
  253.         title = " File: " .. buffer.filename:match(".*/(.+)") .. " "
  254.     end
  255.     gpu.setForeground(options.bgcolor)
  256.     gpu.setBackground(options.fgcolor)
  257.     gpu.fill(1,1,gpuW,1," ")
  258.     local startX = (gpuW - unicode.wlen(title))/2
  259.     gpu.set(3,1,"Lua nano " .. nanoVERSION)
  260.     if buffer.modified then
  261.         local str = "Modified"
  262.         gpu.set(gpuW-unicode.wlen(str)-2,1,str)
  263.     end
  264.     gpu.set(startX,1,title)
  265.     gpu.setForeground(options.fgcolor)
  266.     gpu.setBackground(options.bgcolor)
  267. end
  268.  
  269. local function setModified(buffer,mod)
  270.     if mod == nil then mod = true end
  271.     if buffer.modified ~= mod then
  272.         buffer.modified = mod
  273.         setTitleBar(buffer)
  274.     end
  275. end
  276.  
  277. local timeout = math.huge
  278. local function setStatusBar(text)
  279.     timeout = 0
  280.     text = "[ " .. text .. " ]"
  281.     gpu.fill(1,statusbarY,gpuW,1," ")
  282.     gpu.setForeground(options.bgcolor)
  283.     gpu.setBackground(options.fgcolor)
  284.     local startX = (gpuW - unicode.wlen(text))/2
  285.     gpu.set(startX,statusbarY,text)
  286.     gpu.setForeground(options.fgcolor)
  287.     gpu.setBackground(options.bgcolor)
  288. end
  289.  
  290. local function createNewBuffer()
  291.     return {
  292.         filename=nil,
  293.         modified=false,
  294.         lines = {
  295.         },
  296.         x = 1,
  297.         tx = 1,
  298.         y = 1,
  299.         startLine = 1,
  300.     }
  301. end
  302.  
  303. local function createBuffer(filename)
  304.     local buffer = createNewBuffer()
  305.     if fs.exists(filename) then
  306.         local file, err = io.open(filename,"rb")
  307.         if file then
  308.             buffer.filename = filename
  309.             for line in file:lines() do
  310.                 buffer.lines[#buffer.lines+1] = line
  311.             end
  312.             file:close()
  313.             setStatusBar("Read " .. #buffer.lines .. " line" .. (#buffer.lines ~= 1 and "s" or ""))
  314.         else
  315.             setStatusBar("Couldn't open " .. filename .. ": " .. err)
  316.         end
  317.     else
  318.         buffer.filename = filename
  319.     end
  320.     if #buffer.lines == 0 then
  321.         buffer.lines[1] = ""
  322.     end
  323.     buffers[#buffers+1] = buffer
  324. end
  325.  
  326. local function drawLine(line,y)
  327.     local fline = formatLine(line)
  328.     if unicode.wlen(fline) < gpuW then
  329.         fline = fline .. string.rep(" ",gpuW - unicode.wlen(fline))
  330.     elseif unicode.wlen(fline) > gpuW then
  331.         fline = unicode.wtrunc(fline,gpuW)
  332.         fline = fline .. string.rep(" ",gpuW - unicode.wlen(fline) - 1) .. "$"
  333.     end
  334.     gpu.set(1,y,fline)
  335. end
  336.  
  337. local function updateActiveLine(simple)
  338.     local line = buffer.lines[buffer.y]
  339.     term.setCursorBlink(false)
  340.     if not simple then
  341.         local fline,map = formatLine(line,true)
  342.         -- TODO: This is not nano style line clamping, plus it messes up when tabs have things behind them
  343.         local xwant = map[math.min(buffer.x,#map)]
  344.         local gwant = math.floor(gpuW*3/4)
  345.         if unicode.wlen(fline) >= gpuW and xwant >= gwant then
  346.             for i = 2,#map do
  347.                 if xwant-map[i]+1 < gwant then
  348.                     line = unicode.sub(line,i)
  349.                     xwant = xwant-map[i]+1
  350.                     break
  351.                 end
  352.             end
  353.         end
  354.         term.setCursor(xwant,buffer.y-buffer.startLine+linesY)
  355.     end
  356.     drawLine(line,buffer.y-buffer.startLine+linesY)
  357.     for i=1,2 do term.setCursorBlink(true)end
  358. end
  359.  
  360. local function updateTX()
  361.     local _,map = formatLine(buffer.lines[buffer.y],true)
  362.     buffer.tx = map[math.min(buffer.x,#map)]
  363. end
  364.  
  365. local function scrollBuffer()
  366.     local amt
  367.     local startLine = buffer.startLine
  368.     if buffer.y-startLine < 0 then
  369.         amt = buffer.y-startLine
  370.     elseif buffer.y-startLine > (linesH-1) then
  371.         amt = buffer.y-startLine-(linesH-1)
  372.     end
  373.     if not amt then return end
  374.     term.setCursorBlink(false)
  375.     startLine = startLine + amt
  376.     buffer.startLine = startLine
  377.     if amt > 0 then
  378.         gpu.copy(1,linesY+amt,gpuW,linesH-amt,0,-amt)
  379.         for i = startLine+linesH-1-amt, math.min(startLine+linesH-1,#buffer.lines) do
  380.             drawLine(buffer.lines[i],i-startLine+linesY)
  381.         end
  382.     else
  383.         gpu.copy(1,linesY,gpuW,linesH+amt,0,-amt)
  384.         for i = startLine, math.min(startLine-amt-1,#buffer.lines) do
  385.             drawLine(buffer.lines[i],i-startLine+linesY)
  386.         end
  387.     end
  388.     term.setCursorBlink(true)
  389. end
  390.  
  391. local function redraw()
  392.     setTitleBar(buffer)
  393.     -- TODO: Only clear the area lacking lines
  394.     gpu.fill(1,linesY,gpuW,linesH," ")
  395.     local startLine, amt = buffer.startLine
  396.     if buffer.y-startLine < 0 then
  397.         amt = buffer.y-startLine
  398.     elseif buffer.y-startLine > (linesH-1) then
  399.         amt = buffer.y-startLine-(linesH-1)
  400.     end
  401.     if amt then
  402.         startLine = startLine + amt
  403.         buffer.startLine = startLine
  404.     end
  405.     for i = startLine, math.min(startLine+linesH-1,#buffer.lines) do
  406.         drawLine(buffer.lines[i],i-startLine+linesY)
  407.     end
  408.     updateActiveLine()
  409. end
  410.  
  411. local function switchBuffers(index)
  412.     buffer = buffers[index]
  413.     if buffers.cur ~= nil then
  414.         local name = "New Buffer"
  415.         if buffer.filename ~= nil then
  416.             name = buffer.filename:match(".*/(.+)")
  417.         end
  418.         setStatusBar("Switched to " .. name)
  419.     end
  420.     buffers.cur = index
  421.     redraw()
  422. end
  423.  
  424. local function statusPrompt(text)
  425.     gpu.fill(1,statusbarY,gpuW,1," ")
  426.     gpu.set(1,statusbarY,text)
  427.     term.setCursor(unicode.wlen(text)+1,statusbarY)
  428. end
  429.  
  430. local function resetColor()
  431.     gpu.setForeground(options.fgcolor)
  432.     gpu.setBackground(options.bgcolor)
  433. end
  434.  
  435. local running = true
  436. local function clul() return unicode.len(buffer.lines[buffer.y]) end
  437. local binding = {}
  438. function binding.exit()
  439.     -- TODO: Get a MYESNO mode
  440.     if buffer.modified then
  441.         gpu.setForeground(options.bgcolor)
  442.         gpu.setBackground(options.fgcolor)
  443.         statusPrompt("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? ")
  444.         while true do
  445.             local e,_,c = event_pull()
  446.             if e == "key_down" then
  447.                 c = unicode.lower(unicode.char(c))
  448.                 local ctrl = kbd.isControlDown()
  449.                 if not ctrl and c == "y" then
  450.                     -- TODO: Get a MWRITEFILE mode
  451.                     -- TODO: Needs more options
  452.                     local oldname = ""
  453.                     if buffer.filename ~= nil then
  454.                         oldname = buffer.filename:match(".*/(.+)")
  455.                     end
  456.                     local filename = oldname
  457.                     ::tryagain::
  458.                     statusPrompt("File Name to Write: ")
  459.                     -- TODO: HACK HACK HACK
  460.                     computer.pushSignal("key_down",computer.address(),0,keys.up)
  461.                     filename = term.read({filename},false)
  462.                     if filename then
  463.                         filename = filename:gsub("[\r\n]","")
  464.                     end
  465.                     if filename == "" or filename == nil then
  466.                         resetColor()
  467.                         setStatusBar("Cancelled")
  468.                         return
  469.                     end
  470.                     if filename ~= oldname and fs.exists(shell.resolve(filename)) then
  471.                         -- TODO: Again, MYESNO mode
  472.                         statusPrompt("File exists, OVERWRITE ? ")
  473.                         while true do
  474.                             local e,_,c = event_pull()
  475.                             c = unicode.lower(unicode.char(c))
  476.                             if e == "key_down" then
  477.                                 local ctrl = kbd.isControlDown()
  478.                                 if not ctrl and c == "y" then
  479.                                     break
  480.                                 elseif (not ctrl and c == "n") or (ctrl and c == "\3") then
  481.                                     goto tryagain
  482.                                 end
  483.                             end
  484.                         end
  485.                     end
  486.                     local file, err = io.open(shell.resolve(filename),"wb")
  487.                     if not file then
  488.                         resetColor()
  489.                         setStatusBar("Error writing " .. filename .. ": " .. err)
  490.                         return
  491.                     end
  492.                     for i = 1,#buffer.lines do
  493.                         file:write(buffer.lines[i] .. "\n")
  494.                     end
  495.                     file:close()
  496.                     break
  497.                 elseif not ctrl and c == "n" then
  498.                     break
  499.                 elseif ctrl and c == "\3" then
  500.                     resetColor()
  501.                     setStatusBar("Cancelled")
  502.                     return
  503.                 end
  504.             end
  505.         end
  506.     end
  507.     gpu.setForeground(options.fgcolor)
  508.     gpu.setBackground(options.bgcolor)
  509.     table.remove(buffers,buffers.cur)
  510.     buffers.cur = math.min(buffers.cur,#buffers)
  511.     if buffers.cur < 1 then
  512.         running = false
  513.     else
  514.         switchBuffers(buffers.cur)
  515.     end
  516. end
  517. function binding.left()
  518.     if buffer.x > 1 or buffer.y > 1 then
  519.         buffer.x = buffer.x - 1
  520.         if buffer.x < 1 then
  521.             updateActiveLine(true)
  522.             buffer.y = buffer.y - 1
  523.             buffer.x = unicode.len(buffer.lines[buffer.y])+1
  524.             scrollBuffer()
  525.         end
  526.         updateTX()
  527.         updateActiveLine()
  528.     end
  529. end
  530. function binding.right()
  531.     if buffer.x < clul()+1 or buffer.y < #buffer.lines then
  532.         buffer.x = buffer.x + 1
  533.         if buffer.x > clul()+1 then
  534.             updateActiveLine(true)
  535.             buffer.y = buffer.y + 1
  536.             buffer.x = 1
  537.             scrollBuffer()
  538.         end
  539.         updateTX()
  540.         updateActiveLine()
  541.     end
  542. end
  543. function binding.prevline()
  544.     if buffer.y > 1 then
  545.         updateActiveLine(true)
  546.         buffer.y = buffer.y - 1
  547.         local _,map = formatLine(buffer.lines[buffer.y],true)
  548.         for i = 1,#map do
  549.             if map[i] > buffer.tx then break end
  550.             buffer.x = i
  551.         end
  552.         scrollBuffer()
  553.         updateActiveLine()
  554.     end
  555. end
  556. function binding.nextline()
  557.     if buffer.y < #buffer.lines then
  558.         updateActiveLine(true)
  559.         buffer.y = buffer.y + 1
  560.         local _,map = formatLine(buffer.lines[buffer.y],true)
  561.         for i = 1,#map do
  562.             if map[i] > buffer.tx then break end
  563.             buffer.x = i
  564.         end
  565.         scrollBuffer()
  566.         updateActiveLine()
  567.     end
  568. end
  569. function binding.prevpage()
  570.     if buffer.y > 1 then
  571.         local diff = math.max(buffer.y - linesH, 1) - buffer.y
  572.         buffer.y = buffer.y + diff
  573.         buffer.startLine = math.max(buffer.startLine + diff, 1)
  574.         local _,map = formatLine(buffer.lines[buffer.y],true)
  575.         for i = 1,#map do
  576.             if map[i] > buffer.tx then break end
  577.             buffer.x = i
  578.         end
  579.         redraw()
  580.     end
  581. end
  582. function binding.nextpage()
  583.     if buffer.y < #buffer.lines then
  584.         local diff = math.min(buffer.y + linesH, #buffer.lines) - buffer.y
  585.         buffer.y = buffer.y + diff
  586.         buffer.startLine = math.min(buffer.startLine + diff, #buffer.lines-linesH+1)
  587.         local _,map = formatLine(buffer.lines[buffer.y],true)
  588.         for i = 1,#map do
  589.             if map[i] > buffer.tx then break end
  590.             buffer.x = i
  591.         end
  592.         redraw()
  593.     end
  594. end
  595. function binding.home()
  596.     if buffer.x > 1 then
  597.         buffer.x = 1
  598.         updateTX()
  599.         updateActiveLine()
  600.     end
  601. end
  602. binding["end"] = function()
  603.     if buffer.x < clul()+1 then
  604.         buffer.x = clul()+1
  605.         updateTX()
  606.         updateActiveLine()
  607.     end
  608. end
  609. function binding.backspace()
  610.     if buffer.x == 1 then
  611.         if buffer.y > 1 then
  612.             setModified(buffer)
  613.             buffer.x = unicode.len(buffer.lines[buffer.y-1])+1
  614.             buffer.lines[buffer.y-1] = buffer.lines[buffer.y-1] .. buffer.lines[buffer.y]
  615.             table.remove(buffer.lines,buffer.y)
  616.             buffer.y = buffer.y - 1
  617.             -- TODO: Don't redraw everything
  618.             redraw()
  619.         end
  620.     else
  621.         setModified(buffer)
  622.         local line = buffer.lines[buffer.y]
  623.         buffer.lines[buffer.y] = unicode.sub(line,1,buffer.x-2) .. unicode.sub(line,buffer.x)
  624.         buffer.x = buffer.x - 1
  625.         updateActiveLine()
  626.     end
  627.     updateTX()
  628. end
  629. function binding.delete()
  630.     if buffer.x >= clul()+1 then
  631.         setModified(buffer)
  632.         if buffer.y < #buffer.lines then
  633.             buffer.lines[buffer.y] = buffer.lines[buffer.y] .. buffer.lines[buffer.y+1]
  634.             table.remove(buffer.lines,buffer.y+1)
  635.             -- TODO: Don't redraw everything
  636.             redraw()
  637.         end
  638.     else
  639.         setModified(buffer)
  640.         local line = buffer.lines[buffer.y]
  641.         buffer.lines[buffer.y] = unicode.sub(line,1,buffer.x-1) .. unicode.sub(line,buffer.x+1)
  642.         updateActiveLine()
  643.     end
  644. end
  645. function binding.enter()
  646.     setModified(buffer)
  647.     local line = buffer.lines[buffer.y]
  648.     table.insert(buffer.lines,buffer.y+1,unicode.sub(line,buffer.x))
  649.     buffer.lines[buffer.y] = unicode.sub(line,1,buffer.x-1)
  650.     buffer.x = 1
  651.     buffer.tx = 1
  652.     buffer.y = buffer.y + 1
  653.     -- TODO: Don't redraw everything
  654.     redraw()
  655. end
  656. function binding.prevbuf()
  657.     buffers.cur = ((buffers.cur-2)%#buffers)+1
  658.     switchBuffers(buffers.cur)
  659. end
  660. function binding.nextbuf()
  661.     buffers.cur = (buffers.cur%#buffers)+1
  662.     switchBuffers(buffers.cur)
  663. end
  664. function binding.curpos()
  665.     -- TODO: Character count
  666.     local la,lb = buffer.y,#buffer.lines
  667.     local oa,ob = buffer.x,#buffer.lines[buffer.y]+1
  668.     local cb = 0
  669.     for i = 1,buffer.y-1 do
  670.         cb = cb + #buffer.lines[i]+1
  671.     end
  672.     local ca = cb + buffer.x
  673.     for i = buffer.y,#buffer.lines do
  674.         cb = cb + #buffer.lines[i]+1
  675.     end
  676.     setStatusBar(string.format("line %d/%d (%d%%), col %d/%d (%d%%), char %d/%d (%d%%)",la,lb,la/lb*100+0.5, oa,ob,oa/ob*100+0.5, ca,cb,ca/cb*100+0.5))
  677. end
  678.  
  679. -- TODO: Line and column arguments
  680. -- Load files
  681. if #args > 0 then
  682.     for i = 1,#args do
  683.         createBuffer(shell.resolve(args[i]))
  684.     end
  685.     if #buffers > 1 then
  686.         setStatusBar("Read " .. #buffers[1].lines .. " line" .. (#buffers[1].lines ~= 1 and "s" or ""))
  687.     end
  688. else
  689.     local buffer = createNewBuffer()
  690.     buffer.lines[1] = ""
  691.     buffers[#buffers+1] = buffer
  692. end
  693.  
  694. -- Show the first file
  695. switchBuffers(1)
  696.  
  697. while running do
  698.     local e = { event_pull() }
  699.     if e[1] == "key_down" then
  700.         local char, code = e[3], e[4]
  701.         local ctrl = kbd.isControlDown()
  702.         local alt = kbd.isAltDown()
  703.         local scp,sc = keys[code]:sub(1,1), keys[code]:sub(2)
  704.         local schar
  705.         if not kbd.isControl(char) and char ~= 32 then
  706.             schar = unicode.upper(unicode.char(char))
  707.         else
  708.             schar = unicode.upper(keys[code])
  709.         end
  710.         if ctrl then
  711.             -- TODO: This doesn't cover everything
  712.             if unicode.len(keys[code]) == 1 then
  713.                 schar = unicode.upper(keys[code])
  714.             end
  715.         end
  716.         if ctrl then
  717.             schar = "^" .. schar
  718.         elseif alt then
  719.             schar = "M-" .. schar
  720.         end
  721.         if (scp == "l" or scp == "r") and (sc == "control" or sc == "menu" or sc == "shift") then
  722.         elseif bind[mode][schar] ~= nil then
  723.             local cmd = bind[mode][schar]
  724.             if binding[cmd] ~= nil then
  725.                 binding[cmd]()
  726.             else
  727.                 setStatusBar("Binding \"" .. cmd .. "\" Unimplemented")
  728.             end
  729.         elseif ctrl or alt then
  730.             setStatusBar("Unknown Command")
  731.         elseif not kbd.isControl(char) or char == 9 then
  732.             timeout = timeout + 1
  733.             if timeout >= (options.quickblank and 1 or 25) then
  734.                 gpu.fill(1,statusbarY,gpuW,1," ")
  735.             end
  736.             setModified(buffer)
  737.             local line = buffer.lines[buffer.y]
  738.             buffer.lines[buffer.y] = unicode.sub(line,1,buffer.x-1) .. unicode.char(char) .. unicode.sub(line,buffer.x)
  739.             buffer.x = buffer.x + 1
  740.             updateTX()
  741.             updateActiveLine()
  742.         end
  743.     end
  744. end
  745.  
  746. term.clear()
  747. term.setCursorBlink(false)
  748.  
  749. -- Unfix keys.none
  750. keys["none"] = nil
  751. keys[0] = nil
Add Comment
Please, Sign In to add comment