Advertisement
Xandaros

vim ComputerCraft

Sep 1st, 2012
898
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.15 KB | None | 0 0
  1. local state = 0
  2.  
  3. local tArgs = { ... }
  4.  
  5. local x,y = 1,1
  6. local w,h = term.getSize()
  7. local tLines = {}
  8. local scrollX, scrollY = 0,0
  9.  
  10. local clipboard = {}
  11.  
  12. local sStatus,sSearch = "",""
  13.  
  14. local bSearchBack = false
  15.  
  16. if #tArgs == 0 then
  17. error("Usage: vim <path>")
  18. end
  19.  
  20. local sPath = shell.resolve(tArgs[1])
  21. local sFileName = string.match(sPath, "[^/]+$")
  22. local bReadOnly = fs.isReadOnly(sPath)
  23. local bReadOnlyW = false
  24.  
  25. if fs.exists(sPath) and fs.isDir(sPath) then
  26.     error("Cannot edit a directory")
  27. end
  28.  
  29. local bNewFile = false
  30.  
  31. local bChanged = false
  32.  
  33. local redrawS
  34.  
  35.  
  36. -- Helper functions
  37.  
  38. local function getFilesize()
  39.     local filesize = 0
  40.     for k,v in pairs(tLines) do
  41.         filesize = filesize + string.len(v)+1
  42.     end
  43.     return filesize
  44. end
  45.  
  46. local function redrawCursor(bsec)
  47.     local size = string.len(tostring(x)) + string.len(tostring(y)) + 4
  48.     local _x,_y = term.getCursorPos()
  49.  
  50.     term.setCursorPos(w - size, h)
  51.  
  52.     if not bsec then
  53.         term.clearLine()
  54.         redrawS(true)
  55.     end
  56.  
  57.     local percent
  58.     if y == 1 then
  59.         percent = "Top"
  60.     elseif y == #tLines then
  61.         percent = "Bot"
  62.     else
  63.         percent = math.floor(y / #tLines * 100) .. "%"
  64.     end
  65.  
  66.     term.write(y .. "," .. x .. " " .. percent)
  67.  
  68.     term.setCursorPos(_x,_y)
  69. end
  70.  
  71. local function redrawStatus(bsec)
  72.     local _x,_y = term.getCursorPos()
  73.     term.setCursorPos(1, h)
  74.     if not bsec then
  75.         term.clearLine()
  76.         redrawCursor(true)
  77.     end
  78.     term.write(sStatus)
  79.     term.setCursorPos(_x,_y)
  80. end
  81.  
  82. local function setStatus(_s)
  83.     sStatus = _s
  84.     redrawStatus()
  85. end
  86.  
  87. local function load()
  88.     local lines = 0
  89.  
  90.     if fs.exists(sPath) then
  91.         local file = io.open(sPath, "r")
  92.         local sLine = file:read()
  93.         while sLine do
  94.             table.insert(tLines, sLine)
  95.             sLine = file:read()
  96.             lines = lines + 1
  97.         end
  98.         file:close()
  99.     else
  100.         table.insert(tLines,"")
  101.         bNewFile = true
  102.     end
  103.  
  104.     local _sStatus = "\"" .. sFileName .. "\""
  105.     if bReadOnly then
  106.         _sStatus = _sStatus .. " [Read Only]"
  107.     elseif bNewFile then
  108.         _sStatus = _sStatus .. " [New File]"
  109.     end
  110.  
  111.     if not bNewFile then
  112.         _sStatus = _sStatus .. " " .. lines .. "L, " .. getFilesize() .. "C"
  113.     end
  114.  
  115.     setStatus(_sStatus)
  116. end
  117.  
  118. local function save()
  119.     local file = io.open(sPath, "w")
  120.     if file then
  121.         for k,v in pairs(tLines) do
  122.             file:write(v .. "\n")
  123.         end
  124.         file:close()
  125.         return true
  126.     end
  127.     return false
  128. end
  129.  
  130. local function redrawText()
  131.     for _y=1, h-1 do
  132.         term.setCursorPos(1 - scrollX, _y)
  133.         term.clearLine()
  134.  
  135.         local sLine = tLines[_y + scrollY]
  136.         if sLine ~= nil then
  137.             term.write(sLine)
  138.         end
  139.     end
  140.  
  141.     term.setCursorPos(x - scrollX, y - scrollY)
  142. end
  143.  
  144. local function setCursor(x, y)
  145.     local screenX = x - scrollX
  146.     local screenY = y - scrollY
  147.  
  148.     local bRedraw = false
  149.  
  150.     if screenX < 1 then
  151.         scrollX = x - 1
  152.         screenX = 1
  153.         bRedraw = true
  154.     elseif screenX > w then
  155.         scrollX = x - w
  156.         screenX = w
  157.         bRedraw = true
  158.     end
  159.  
  160.     if screenY < 1 then
  161.         scrollY = y - 1
  162.         screenY = 1
  163.         bRedraw = true
  164.     elseif screenY > h-1 then
  165.         scrollY = y - (h - 1)
  166.         screenY = h - 1
  167.         bRedraw = true
  168.     end
  169.  
  170.     term.setCursorPos(screenX, screenY)
  171.  
  172.     if bRedraw then redrawText() end
  173.     redrawCursor()
  174. end
  175.  
  176. local function redrawLine()
  177.     local _x,_y = term.getCursorPos()
  178.     term.setCursorPos(1 - scrollX, _y)
  179.     term.clearLine()
  180.     term.write(tLines[y])
  181.     term.setCursorPos(_x, _y)
  182. end
  183.  
  184. local function cursor(param)
  185.     if param == 203 then
  186.         -- Left
  187.         if x > 1 then
  188.             x = x - 1
  189.             setCursor(x,y)
  190.         end
  191.         return true
  192.     elseif param == 205 then
  193.         -- Right
  194.         if x < string.len(tLines[y])+1 then
  195.             x = x + 1
  196.             setCursor(x,y)
  197.         end
  198.         return true
  199.     elseif param == 200 then
  200.         -- Up
  201.         if y > 1 then
  202.             y = y - 1
  203.             local len = string.len(tLines[y])+1
  204.             if x > len then
  205.                 x = len
  206.             end
  207.             setCursor(x,y)
  208.         end
  209.         return true
  210.     elseif param == 208 then
  211.         -- Down
  212.         if y < #tLines then
  213.             y = y + 1
  214.             local len = string.len(tLines[y])+1
  215.             if x > len then
  216.                 x = len
  217.             end
  218.             setCursor(x,y)
  219.         end
  220.         return true
  221.     elseif param == 199 then
  222.         -- Home
  223.         x = 1
  224.         setCursor(x,y)
  225.         return true
  226.     elseif param == 207 then
  227.         -- End
  228.         x = string.len(tLines[y]) + 1
  229.         setCursor(x,y)
  230.         return true
  231.     elseif param == 201 then
  232.         -- PageUp
  233.         y = y - h + 1
  234.         if y < 1 then y = 1 end
  235.        
  236.         local len = tLines[y]:len()
  237.         if x > len+1 then
  238.             x = len+1
  239.         end
  240.  
  241.         setCursor(x,y)
  242.     elseif param == 209 then
  243.         -- PageDown
  244.         y = y + h - 2
  245.         if y > #tLines then
  246.             y = #tLines
  247.         end
  248.        
  249.         local len = tLines[y]:len()
  250.         if x > len+1 then
  251.             x = len+1
  252.         end
  253.  
  254.         setCursor(x,y)
  255.     end
  256.  
  257.     return false
  258. end
  259.  
  260. local function searchF(s)
  261.     sSearch = s
  262.     for i = x+1, tLines[y]:len() do
  263.         if tLines[y]:sub(i,i+s:len()-1) == s then
  264.             return i,y
  265.         end
  266.     end
  267.     for i = y+1, #tLines do
  268.         if i > #tLines then break end
  269.         for j = 1, tLines[i]:len() do
  270.             if tLines[i]:sub(j,j+s:len()-1):lower() == s:lower() then
  271.                 return j,i
  272.             end
  273.         end
  274.     end
  275.     setStatus("search hit BOTTON, continuing at TOP")
  276.     for i = 1, y do
  277.         for j = 1, tLines[i]:len() do
  278.             if tLines[i]:sub(j,j+s:len()-1):lower() == s:lower() then
  279.                 return j,i
  280.             end
  281.         end
  282.     end
  283.  
  284.     setStatus("E486: Pattern not found: " .. s)
  285.     return x,y
  286. end
  287.  
  288. local function searchB(s)
  289.     sSearch = s
  290.     bSearchBack = true
  291.     for i = x-1, 1, -1 do
  292.         if x < 1 then break end
  293.         if tLines[y]:sub(i,i+s:len()-1) == s then
  294.             return i,y
  295.         end
  296.     end
  297.     for i = y-1, 1, -1 do
  298.         if i < 1 then break end
  299.         for j = tLines[i]:len(), 1, -1 do
  300.             if tLines[i]:sub(j,j+s:len()-1):lower() == s:lower() then
  301.                 return j,i
  302.             end
  303.         end
  304.     end
  305.     setStatus("search hit TOP, continuing at BOTTOM")
  306.     for i = #tLines, y, -1 do
  307.         for j = tLines[i]:len(), 1, -1 do
  308.             if tLines[i]:sub(j,j+s:len()-1):lower() == s:lower() then
  309.                 return j,i
  310.             end
  311.         end
  312.     end
  313.     setStatus("E486: Pattern not found: " .. s)
  314.     return x,y
  315. end
  316.  
  317. local function executecmd(rangemin, rangemax, command, amount, force)
  318.     if force == nil then force = false end
  319.     if amount == nil or amount == "" then amount = 1 end
  320.  
  321.     if rangemin > rangemax then
  322.         rangemax = rangemin
  323.     end
  324.  
  325.     if rangemin == 0 and rangemax == 0 then
  326.         rangemin,rangemax = 0,0
  327.     end
  328.  
  329.     local function _quit()
  330.         if bChanged and not force then
  331.             setStatus("E37: No write since last change (add ! to override)")
  332.             state = 0
  333.         else
  334.             setStatus("")
  335.             state = -1
  336.         end
  337.     end
  338.  
  339.     local function _save()
  340.         if bReadOnly and not force then
  341.             setStatus("E45: 'readonly' option is set (add ! to override)")
  342.             state = 0
  343.         else
  344.             if not save() then
  345.                 setStatus("E212: Can't open file for writing")
  346.                 state = 0
  347.             else
  348.                 local _sStatus = "\"" .. sFileName .. "\""
  349.                 if bNewFile then
  350.                     _sStatus = _sStatus .. " [New] "
  351.                     bNewFile = false
  352.                 end
  353.  
  354.                 _sStatus = _sStatus .. " " .. #tLines .. "L, " .. getFilesize() .. "C written"
  355.  
  356.                 setStatus(_sStatus)
  357.                 bChanged = false
  358.                 state = 0
  359.             end
  360.         end
  361.     end
  362.  
  363.     if command == "q" then
  364.         _quit()
  365.     elseif command == "w" then
  366.         _save()
  367.     elseif command == "wq" then
  368.         _save()
  369.         _quit()
  370.     elseif command == "d" then
  371.         local _y = y
  372.         y = rangemin
  373.         if rangemin ~= rangemax then
  374.             amount = rangemax - rangemin + 1
  375.         end
  376.         clipboard = {}
  377.  
  378.         for i = 1, amount do
  379.             if tLines[y] ~= nil then
  380.                 table.insert(clipboard,tLines[y])
  381.                 table.remove(tLines, y)
  382.                
  383.                 if #tLines == 0 then table.insert(tLines, "") end
  384.  
  385.                 redrawText()
  386.                 bChanged = true
  387.  
  388.                 if tLines[y] == nil then
  389.                     y = y - 1
  390.                     break
  391.                 end
  392.  
  393.                 local len = string.len(tLines[y]) + 1
  394.                 if x > len then
  395.                     x = len
  396.                     setCursor(x,y)
  397.                 end
  398.             end
  399.         end
  400.         y = _y
  401.  
  402.         state = 0
  403.     elseif command:sub(1,1) == "/" then
  404.         x,y = searchF(command:sub(2))
  405.         setCursor(x,y)
  406.  
  407.         state = 0
  408.  
  409.     elseif command:sub(1,1) == "?" then
  410.         x,y = searchB(command:sub(2))
  411.         setCursor(x,y)
  412.  
  413.         state = 0
  414.  
  415.     elseif command:sub(1,1) == "s" then
  416.        
  417.  
  418.     elseif command == "=" then
  419.         setStatus(#tLines)
  420.         state = 0
  421.     else
  422.         setStatus("E492: Not an editor command: " .. command)
  423.         state = 0
  424.     end
  425. end
  426.  
  427. local function defaultmode()
  428.     local keybuffer = ""
  429.     while state == 0 do
  430.         local sEvent,param = os.pullEvent()
  431.         if sEvent == "key" then
  432.             if param == 28 then param = 208 end
  433.             if param == 14 then param = 203 end
  434.             if cursor(param) then
  435.                 bGPressed = false
  436.             end
  437.         elseif sEvent == "char" then
  438.             if param == 'i' then
  439.                 state = 2
  440.             elseif param == ":" then
  441.                 state = 1
  442.             elseif param == "G" then
  443.                 x = 1
  444.                 y = #tLines
  445.                 setCursor(x,y)
  446.  
  447.             elseif param == "n" then
  448.                 if bSearchBack then
  449.                     setStatus("?" .. sSearch)
  450.                     x,y = searchB(sSearch)
  451.                 else
  452.                     setStatus("/" .. sSearch)
  453.                     x,y = searchF(sSearch)
  454.                 end
  455.                 setCursor(x,y)
  456.  
  457.             elseif param == "N" then
  458.                 if bSearchBack then
  459.                     setStatus("/" .. sSearch)
  460.                     x,y = searchF(sSearch)
  461.                 else
  462.                     setStatus("?" .. sSearch)
  463.                     x,y = searchB(sSearch)
  464.                 end
  465.  
  466.             elseif param == "p" then
  467.                 for i = #clipboard, 1, -1 do
  468.                     table.insert(tLines,y+1,clipboard[i])
  469.                 end
  470.                 redrawText()
  471.                 bChanged = true
  472.  
  473.             elseif param == "P" then
  474.                 for i = #clipboard, 1, -1 do
  475.                     table.insert(tLines,y,clipboard[i])
  476.                 end
  477.                 redrawText()
  478.                 bChanged = true
  479.             end
  480.  
  481.             if param == "g" then
  482.                 if keybuffer == "g" then
  483.                     x = 1
  484.                     y = 1
  485.                     setCursor(x,y)
  486.  
  487.                     keybuffer = ""
  488.                 else
  489.                     keybuffer = "g"
  490.                 end
  491.  
  492.             elseif param == "y" then
  493.                 if keybuffer == "y" then
  494.                     clipboard = {tLines[y]}
  495.                     keybuffer = ""
  496.                 else
  497.                     keybuffer = "y"
  498.                 end
  499.  
  500.             elseif param == "d" then
  501.                 if keybuffer == "d" then
  502.                     clipboard = {tLines[y]}
  503.                     table.remove(tLines,y)
  504.                     if #tLines == 0 then table.insert(tLines,"") end
  505.  
  506.                     redrawText()
  507.  
  508.                     if y > #tLines then
  509.                         y = y - 1
  510.                         setCursor(x,y)
  511.                     end
  512.                     if x > tLines[y]:len()+1 then
  513.                         x = tLines[y]:len()
  514.                         setCursor(x,y)
  515.                     end
  516.  
  517.                     bChanged = true
  518.  
  519.                     keybuffer = ""
  520.                 else
  521.                     keybuffer = "d"
  522.                 end
  523.             else
  524.                 keybuffer = ""
  525.             end
  526.         end
  527.     end
  528. end
  529.  
  530. local function commandmode()
  531.     local command = ":"
  532.  
  533.     term.setCursorPos(2,h)
  534.  
  535.     setStatus(":")
  536.  
  537.     while state == 1 do
  538.         local _x,_y = term.getCursorPos()
  539.         local sEvent,param = os.pullEvent()
  540.  
  541.         if sEvent == "key" then
  542.             if param == 28 then
  543.                 -- Return
  544.                 command = command:sub(2)
  545.  
  546.                 if command:sub(1,1) == "s" or command:sub(1,1) == "/" or command:sub(1,1) == "?" then
  547.                     executecmd(0,0,command,0,false)
  548.                 else
  549.                     local _,_,minmax,cmd,amnt,force = command:find("(%d*,?%d*)([a-zA-Z=]*)(%d*)(!?)")
  550.                     local minim,maxim = 0,0
  551.  
  552.                     local i = 1
  553.                     local minmax2,flag = minmax:sub(i,i), false
  554.                     while minmax2 ~= "" do
  555.                         if minmax2 == "," then
  556.                             flag = true
  557.                         else
  558.                             if flag then
  559.                                 maxim = maxim * 10 + tonumber(minmax2)
  560.                             else
  561.                                 minim = minim * 10 + tonumber(minmax2)
  562.                             end
  563.                         end
  564.                         i = i + 1
  565.                         minmax2 = minmax:sub(i,i)
  566.                     end
  567.  
  568.                     executecmd(minim,maxim,cmd,amnt,force == "!")
  569.                 end
  570.             elseif param == 59 then
  571.                 -- F1
  572.                 state = 0
  573.             elseif param == 14 then
  574.                 -- Backspace
  575.                 if _x > 1 then
  576.                     command = string.sub(command,1,_x-2) .. string.sub(command,_x)
  577.                     _x = _x - 1
  578.                     term.setCursorPos(_x,_y)
  579.                 end
  580.                 if command == "" then
  581.                     state = 0
  582.                 end
  583.                 setStatus(command)
  584.             elseif param == 203 then
  585.                 -- Left
  586.                 if _x > 1 then
  587.                     _x = _x + 1
  588.                     term.setCursorPos(_x,_y)
  589.                 end
  590.             end
  591.         elseif sEvent == "char" then
  592.             command = string.sub(command,1,_x-1) .. param .. string.sub(command,_x)
  593.             setStatus(command)
  594.             _x = _x + 1
  595.             term.setCursorPos(_x,_y)
  596.         end
  597.     end
  598.  
  599.     setCursor(x,y)
  600. end
  601.  
  602. local function insertmode()
  603.     local _sStatus = "-- INSERT -- "
  604.     if bReadOnly and not bReadOnlyW then
  605.         _sStatus = _sStatus .. " W10: Changing a readonly file"
  606.         bReadOnlyW = true
  607.     end
  608.  
  609.     setStatus(_sStatus)
  610.  
  611.     while state == 2 do
  612.         local sEvent,param = os.pullEvent()
  613.         if sEvent == "key" and not cursor(param) then
  614.              if param == 59 then
  615.                  -- F1
  616.                  state = 0
  617.              elseif param == 28 then
  618.                  -- Return
  619.                  local sLine = tLines[y]
  620.                  tLines[y] = string.sub(sLine,1,x-1)
  621.                  table.insert(tLines,y+1,string.sub(sLine,x))
  622.  
  623.                  redrawText()
  624.  
  625.                  x = 1
  626.                  y = y + 1
  627.                  setCursor(x,y)
  628.                  bChanged = true
  629.              elseif param == 14 then
  630.                 -- Backspace
  631.                 if x > 1 then
  632.                     local sLine = tLines[y]
  633.                     tLines[y] = string.sub(sLine,1,x-2) .. string.sub(sLine,x)
  634.                     redrawLine()
  635.  
  636.                     x = x - 1
  637.                 elseif y > 1 then
  638.                     local sPrevLine = tLines[y-1]
  639.                     tLines[y-1] = sPrevLine .. tLines[y]
  640.                     table.remove(tLines, y)
  641.  
  642.                     redrawText()
  643.  
  644.                     x = string.len(sPrevLine) + 1
  645.                     y = y - 1
  646.                 end
  647.  
  648.                 setCursor(x,y)
  649.                 bChanged = true
  650.  
  651.             elseif param == 211 then
  652.                 -- Del
  653.                 local sLine = tLines[y]
  654.                 local len = string.len(sLine)+1
  655.                 if x < len then
  656.                     tLines[y] = string.sub(sLine,1,x-1) .. string.sub(sLine,x+1)
  657.                     redrawLine()
  658.                 elseif y < #tLines then
  659.                     tLines[y] = sLine .. tLines[y+1]
  660.                     table.remove(tLines, y+1)
  661.                     redrawText()
  662.                 end
  663.  
  664.                 bChanged = true
  665.             elseif param == 15 then
  666.                 -- Tab
  667.                 local sLine = tLines[y]
  668.                 tLines[y] = string.sub(sLine,1,x-1) .. "    " .. string.sub(sLine,x)
  669.                 redrawLine()
  670.  
  671.                 x = x + 4
  672.                 setCursor(x,y)
  673.  
  674.                 bChanged = true
  675.             end
  676.  
  677.         elseif sEvent == "char" then
  678.             local sLine = tLines[y]
  679.             tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)
  680.             redrawLine()
  681.  
  682.             x = x + string.len(param)
  683.             setCursor(x,y)
  684.  
  685.             bChanged = true
  686.         end
  687.     end
  688.     setStatus("")
  689. end
  690.  
  691. local function tsmode()
  692.  
  693. end
  694.  
  695. -- Main routine
  696.  
  697. local tModes = {}
  698. tModes[0] = defaultmode
  699. tModes[1] = commandmode
  700. tModes[2] = insertmode
  701. tModes[3] = tsmode
  702.  
  703. term.clear()
  704. term.setCursorPos(1,1)
  705. term.setCursorBlink(true)
  706.  
  707. load()
  708. redrawText()
  709.  
  710. redrawS = redrawStatus
  711.  
  712. redrawCursor()
  713.  
  714. while state ~= -1 do
  715.     tModes[state]()
  716. end
  717. term.clear()
  718. term.setCursorPos(1,1)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement