Advertisement
Guest User

textedit

a guest
Oct 24th, 2016
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 17.52 KB | None | 0 0
  1. -- Get file to edit
  2. local tArgs = { ... }
  3. if #tArgs == 0 then
  4.     print( "Usage: edit <path>" )
  5.     return
  6. end
  7.  
  8. -- Error checking
  9. local sPath = shell.resolve( tArgs[1] )
  10. local bReadOnly = fs.isReadOnly( sPath )
  11. if fs.exists( sPath ) and fs.isDir( sPath ) then
  12.     print( "Cannot edit a directory." )
  13.     return
  14. end
  15.  
  16. local x,y = 1,1
  17. local w,h = term.getSize()
  18. local scrollX, scrollY = 0,0
  19.  
  20. local tLines = {}
  21. local bRunning = true
  22.  
  23. -- Colours
  24. local highlightColour, keywordColour, commentColour, textColour, bgColour
  25. if term.isColour() then
  26.     bgColour = colours.white
  27.     textColour = colours.black
  28.     highlightColour = colours.black
  29.     keywordColour = colours.black
  30.     commentColour = colours.black
  31.     stringColour = colours.black
  32. else
  33.     bgColour = colours.black
  34.     textColour = colours.white
  35.     highlightColour = colours.white
  36.     keywordColour = colours.white
  37.     commentColour = colours.white
  38.     stringColour = colours.white
  39. end
  40.  
  41. -- Menus
  42. local bMenu = false
  43. local nMenuItem = 1
  44. local tMenuItems = {}
  45. if not bReadOnly then
  46.     table.insert( tMenuItems, "Save" )
  47. end
  48. if shell.openTab then
  49.     table.insert( tMenuItems, "Run" )
  50. end
  51. if peripheral.find( "printer" ) then
  52.     table.insert( tMenuItems, "Print" )
  53. end
  54. table.insert( tMenuItems, "Exit" )
  55.  
  56. local sStatus = "Press Ctrl to access menu"
  57. if string.len( sStatus ) > w - 5 then
  58.     sStatus = "Press Ctrl for menu"
  59. end
  60.  
  61. local function load( _sPath )
  62.     tLines = {}
  63.     if fs.exists( _sPath ) then
  64.         local file = io.open( _sPath, "r" )
  65.         local sLine = file:read()
  66.         while sLine do
  67.             table.insert( tLines, sLine )
  68.             sLine = file:read()
  69.         end
  70.         file:close()
  71.     end
  72.    
  73.     if #tLines == 0 then
  74.         table.insert( tLines, "" )
  75.     end
  76. end
  77.  
  78. local function save( _sPath )
  79.     -- Create intervening folder
  80.     local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len() )
  81.     if not fs.exists( sDir ) then
  82.         fs.makeDir( sDir )
  83.     end
  84.  
  85.     -- Save
  86.     local file = nil
  87.     local function innerSave()
  88.         file = fs.open( _sPath, "w" )
  89.         if file then
  90.             for n, sLine in ipairs( tLines ) do
  91.                 file.write( sLine .. "\n" )
  92.             end
  93.         else
  94.             error( "Failed to open ".._sPath )
  95.         end
  96.     end
  97.    
  98.     local ok, err = pcall( innerSave )
  99.     if file then
  100.         file.close()
  101.     end
  102.     return ok, err
  103. end
  104.  
  105. local tKeywords = {
  106. }
  107.  
  108. local function tryWrite( sLine, regex, colour )
  109.     local match = string.match( sLine, regex )
  110.     color = colours.black
  111.     if match then
  112.         if type(colour) == "number" then
  113.             term.setTextColour( colour )
  114.         else
  115.             term.setTextColour( colour(match) )
  116.         end
  117.         term.write( match )
  118.         term.setTextColour( textColour )
  119.         return string.sub( sLine, string.len(match) + 1 )
  120.     end
  121.     return nil
  122. end
  123.  
  124. local function writeHighlighted( sLine )
  125.     while string.len(sLine) > 0 do 
  126.         sLine =
  127.             tryWrite( sLine, "^%-%-%[%[.-%]%]", commentColour ) or
  128.             tryWrite( sLine, "^%-%-.*", commentColour ) or
  129.             tryWrite( sLine, "^\".-[^\\]\"", stringColour ) or
  130.             tryWrite( sLine, "^\'.-[^\\]\'", stringColour ) or
  131.             tryWrite( sLine, "^%[%[.-%]%]", stringColour ) or
  132.             tryWrite( sLine, "^[%w_]+", function( match )
  133.                 if tKeywords[ match ] then
  134.                     return keywordColour
  135.                 end
  136.                 return textColour
  137.             end ) or
  138.             tryWrite( sLine, "^[^%w_]", textColour )
  139.     end
  140. end
  141.  
  142. local tCompletions
  143. local nCompletion
  144.  
  145. local tCompleteEnv = _ENV
  146. local function complete( sLine )
  147.     local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.]+$" )
  148.     if nStartPos then
  149.         sLine = string.sub( sLine, nStartPos )
  150.     end
  151.     if #sLine > 0 then
  152.         return textutils.complete( sLine, tCompleteEnv )
  153.     end
  154.     return nil
  155. end
  156.  
  157. local function recomplete()
  158.     local sLine = tLines[y]
  159.     if not bMenu and not bReadOnly and x == string.len(sLine) + 1 then
  160.         tCompletions = complete( sLine )
  161.         if tCompletions and #tCompletions > 0 then
  162.             nCompletion = 1
  163.         else
  164.             nCompletion = nil
  165.         end
  166.     else
  167.         tCompletions = nil
  168.         nCompletion = nil
  169.     end
  170. end
  171.  
  172. local function writeCompletion( sLine )
  173. end
  174.  
  175. local function redrawText()
  176.     local cursorX, cursorY = x, y
  177.     for y=1,h-1 do
  178.         term.setCursorPos( 1 - scrollX, y )
  179.         term.clearLine()
  180.  
  181.         local sLine = tLines[ y + scrollY ]
  182.         if sLine ~= nil then
  183.             writeHighlighted( sLine )
  184.             if cursorY == y and cursorX == #sLine + 1 then
  185.                 writeCompletion()
  186.             end
  187.         end
  188.     end
  189.     term.setCursorPos( x - scrollX, y - scrollY )
  190. end
  191.  
  192. local function redrawLine(_nY)
  193.     local sLine = tLines[_nY]
  194.     if sLine then
  195.         term.setCursorPos( 1 - scrollX, _nY - scrollY )
  196.         term.clearLine()
  197.         writeHighlighted( sLine )
  198.         if _nY == y and x == #sLine + 1 then
  199.             writeCompletion()
  200.         end
  201.         term.setCursorPos( x - scrollX, _nY - scrollY )
  202.     end
  203. end
  204.  
  205. local function redrawMenu()
  206.     -- Clear line
  207.     term.setCursorPos( 1, h )
  208.     term.clearLine()
  209.  
  210.     -- Draw line numbers
  211.     term.setCursorPos( w - string.len( "Ln "..y ) + 1, h )
  212.     term.setTextColour( highlightColour )
  213.     term.write( "Ln " )
  214.     term.setTextColour( textColour )
  215.     term.write( y )
  216.  
  217.     term.setCursorPos( 1, h )
  218.     if bMenu then
  219.         -- Draw menu
  220.         term.setTextColour( textColour )
  221.         for nItem,sItem in pairs( tMenuItems ) do
  222.             if nItem == nMenuItem then
  223.                 term.setTextColour( highlightColour )
  224.                 term.write( "[" )
  225.                 term.setTextColour( textColour )
  226.                 term.write( sItem )
  227.                 term.setTextColour( highlightColour )
  228.                 term.write( "]" )
  229.                 term.setTextColour( textColour )
  230.             else
  231.                 term.write( " "..sItem.." " )
  232.             end
  233.         end
  234.     else
  235.         -- Draw status
  236.         term.setTextColour( highlightColour )
  237.         term.write( sStatus )
  238.         term.setTextColour( textColour )
  239.     end
  240.  
  241.     -- Reset cursor
  242.     term.setCursorPos( x - scrollX, y - scrollY )
  243. end
  244.  
  245. local tMenuFuncs = {
  246.     Save = function()
  247.         if bReadOnly then
  248.             sStatus = "Access denied"
  249.         else
  250.             local ok, err = save( sPath )
  251.             if ok then
  252.                 sStatus="Saved to "..sPath
  253.             else
  254.                 sStatus="Error saving to "..sPath
  255.             end
  256.         end
  257.         redrawMenu()
  258.     end,
  259.     Print = function()
  260.         local printer = peripheral.find( "printer" )
  261.         if not printer then
  262.             sStatus = "No printer attached"
  263.             return
  264.         end
  265.  
  266.         local nPage = 0
  267.         local sName = fs.getName( sPath )
  268.         if printer.getInkLevel() < 1 then
  269.             sStatus = "Printer out of ink"
  270.             return
  271.         elseif printer.getPaperLevel() < 1 then
  272.             sStatus = "Printer out of paper"
  273.             return
  274.         end
  275.  
  276.         local screenTerminal = term.current()
  277.         local printerTerminal = {
  278.             getCursorPos = printer.getCursorPos,
  279.             setCursorPos = printer.setCursorPos,
  280.             getSize = printer.getPageSize,
  281.             write = printer.write,
  282.         }
  283.         printerTerminal.scroll = function()
  284.             if nPage == 1 then
  285.                 printer.setPageTitle( sName.." (page "..nPage..")" )           
  286.             end
  287.            
  288.             while not printer.newPage() do
  289.                 if printer.getInkLevel() < 1 then
  290.                     sStatus = "Printer out of ink, please refill"
  291.                 elseif printer.getPaperLevel() < 1 then
  292.                     sStatus = "Printer out of paper, please refill"
  293.                 else
  294.                     sStatus = "Printer output tray full, please empty"
  295.                 end
  296.    
  297.                 term.redirect( screenTerminal )
  298.                 redrawMenu()
  299.                 term.redirect( printerTerminal )
  300.                
  301.                 local timer = os.startTimer(0.5)
  302.                 sleep(0.5)
  303.             end
  304.  
  305.             nPage = nPage + 1
  306.             if nPage == 1 then
  307.                 printer.setPageTitle( sName )
  308.             else
  309.                 printer.setPageTitle( sName.." (page "..nPage..")" )
  310.             end
  311.         end
  312.        
  313.         bMenu = false
  314.         term.redirect( printerTerminal )
  315.         local ok, error = pcall( function()
  316.             term.scroll()
  317.             for n, sLine in ipairs( tLines ) do
  318.                 print( sLine )
  319.             end
  320.         end )
  321.         term.redirect( screenTerminal )
  322.         if not ok then
  323.             print( error )
  324.         end
  325.        
  326.         while not printer.endPage() do
  327.             sStatus = "Printer output tray full, please empty"
  328.             redrawMenu()
  329.             sleep( 0.5 )
  330.         end
  331.         bMenu = true
  332.            
  333.         if nPage > 1 then
  334.             sStatus = "Printed "..nPage.." Pages"
  335.         else
  336.             sStatus = "Printed 1 Page"
  337.         end
  338.         redrawMenu()
  339.     end,
  340.     Exit = function()
  341.         bRunning = false
  342.     end,
  343.     Run = function()
  344.         local sTempPath = "/.temp"
  345.         local ok, err = save( sTempPath )
  346.         if ok then
  347.             local nTask = shell.openTab( sTempPath )
  348.             if nTask then
  349.                 shell.switchTab( nTask )
  350.             else
  351.                 sStatus="Error starting Task"
  352.             end
  353.             fs.delete( sTempPath )
  354.         else
  355.             sStatus="Error saving to "..sTempPath
  356.         end
  357.         redrawMenu()
  358.     end
  359. }
  360.  
  361. local function doMenuItem( _n )
  362.     tMenuFuncs[tMenuItems[_n]]()
  363.     if bMenu then
  364.         bMenu = false
  365.         term.setCursorBlink( true )
  366.     end
  367.     redrawMenu()
  368. end
  369.  
  370. local function setCursor( newX, newY )
  371.     local oldX, oldY = x, y
  372.     x, y = newX, newY
  373.     local screenX = x - scrollX
  374.     local screenY = y - scrollY
  375.    
  376.     local bRedraw = false
  377.     if screenX < 1 then
  378.         scrollX = x - 1
  379.         screenX = 1
  380.         bRedraw = true
  381.     elseif screenX > w then
  382.         scrollX = x - w
  383.         screenX = w
  384.         bRedraw = true
  385.     end
  386.    
  387.     if screenY < 1 then
  388.         scrollY = y - 1
  389.         screenY = 1
  390.         bRedraw = true
  391.     elseif screenY > h-1 then
  392.         scrollY = y - (h-1)
  393.         screenY = h-1
  394.         bRedraw = true
  395.     end
  396.  
  397.     recomplete()
  398.     if bRedraw then
  399.         redrawText()
  400.     elseif y ~= oldY then
  401.         redrawLine( oldY )
  402.         redrawLine( y )
  403.     else
  404.         redrawLine( y )
  405.     end
  406.     term.setCursorPos( screenX, screenY )
  407.  
  408.     redrawMenu()
  409. end
  410.  
  411. -- Actual program functionality begins
  412. load(sPath)
  413.  
  414. term.setBackgroundColour( bgColour )
  415. term.clear()
  416. term.setCursorPos(x,y)
  417. term.setCursorBlink( true )
  418.  
  419. recomplete()
  420. redrawText()
  421. redrawMenu()
  422.  
  423. local function acceptCompletion()
  424.     if nCompletion then
  425.         -- Find the common prefix of all the other suggestions which start with the same letter as the current one
  426.         local sCompletion = tCompletions[ nCompletion ]
  427.         local sFirstLetter = string.sub( sCompletion, 1, 1 )
  428.         local sCommonPrefix = sCompletion
  429.         for n=1,#tCompletions do
  430.             local sResult = tCompletions[n]
  431.             if n ~= nCompletion and string.find( sResult, sFirstLetter, 1, true ) == 1 then
  432.                 while #sCommonPrefix > 1 do
  433.                     if string.find( sResult, sCommonPrefix, 1, true ) == 1 then
  434.                         break
  435.                     else
  436.                         sCommonPrefix = string.sub( sCommonPrefix, 1, #sCommonPrefix - 1 )
  437.                     end
  438.                 end
  439.             end
  440.         end
  441.  
  442.         -- Append this string
  443.         tLines[y] = tLines[y] .. sCommonPrefix
  444.         setCursor( x + string.len( sCommonPrefix ), y )
  445.     end
  446. end
  447.  
  448. -- Handle input
  449. while bRunning do
  450.     local sEvent, param, param2, param3 = os.pullEvent()
  451.     if sEvent == "key" then
  452.         local oldX, oldY = x, y
  453.         if param == keys.up then
  454.             -- Up
  455.             if not bMenu then
  456.                 if nCompletion then
  457.                     -- Cycle completions
  458.                     nCompletion = nCompletion - 1
  459.                     if nCompletion < 1 then
  460.                         nCompletion = #tCompletions
  461.                     end
  462.                     redrawLine(y)
  463.  
  464.                 elseif y > 1 then
  465.                     -- Move cursor up
  466.                     setCursor(
  467.                         math.min( x, string.len( tLines[y - 1] ) + 1 ),
  468.                         y - 1
  469.                     )
  470.                 end
  471.             end
  472.  
  473.         elseif param == keys.down then
  474.             -- Down
  475.             if not bMenu then
  476.                 -- Move cursor down
  477.                 if nCompletion then
  478.                     -- Cycle completions
  479.                     nCompletion = nCompletion + 1
  480.                     if nCompletion > #tCompletions then
  481.                         nCompletion = 1
  482.                     end
  483.                     redrawLine(y)
  484.  
  485.                 elseif y < #tLines then
  486.                     -- Move cursor down
  487.                     setCursor(
  488.                         math.min( x, string.len( tLines[y + 1] ) + 1 ),
  489.                         y + 1
  490.                     )
  491.                 end
  492.             end
  493.  
  494.         elseif param == keys.tab then
  495.             -- Tab
  496.             if not bMenu and not bReadOnly then
  497.                 if nCompletion and x == string.len(tLines[y]) + 1 then
  498.                     -- Accept autocomplete
  499.                     acceptCompletion()
  500.                 else
  501.                     -- Indent line
  502.                     local sLine = tLines[y]
  503.                     tLines[y] = string.sub(sLine,1,x-1) .. "  " .. string.sub(sLine,x)
  504.                     setCursor( x + 2, y )
  505.                 end
  506.             end
  507.  
  508.         elseif param == keys.pageUp then
  509.             -- Page Up
  510.             if not bMenu then
  511.                 -- Move up a page
  512.                 local newY
  513.                 if y - (h - 1) >= 1 then
  514.                     newY = y - (h - 1)
  515.                 else
  516.                     newY = 1
  517.                 end
  518.                 setCursor(
  519.                     math.min( x, string.len( tLines[newY] ) + 1 ),
  520.                     newY
  521.                 )
  522.             end
  523.  
  524.         elseif param == keys.pageDown then
  525.             -- Page Down
  526.             if not bMenu then
  527.                 -- Move down a page
  528.                 local newY
  529.                 if y + (h - 1) <= #tLines then
  530.                     newY = y + (h - 1)
  531.                 else
  532.                     newY = #tLines
  533.                 end
  534.                 local newX = math.min( x, string.len( tLines[newY] ) + 1 )
  535.                 setCursor( newX, newY )
  536.             end
  537.  
  538.         elseif param == keys.home then
  539.             -- Home
  540.             if not bMenu then
  541.                 -- Move cursor to the beginning
  542.                 if x > 1 then
  543.                     setCursor(1,y)
  544.                 end
  545.             end
  546.  
  547.         elseif param == keys["end"] then
  548.             -- End
  549.             if not bMenu then
  550.                 -- Move cursor to the end
  551.                 local nLimit = string.len( tLines[y] ) + 1
  552.                 if x < nLimit then
  553.                     setCursor( nLimit, y )
  554.                 end
  555.             end
  556.  
  557.         elseif param == keys.left then
  558.             -- Left
  559.             if not bMenu then
  560.                 if x > 1 then
  561.                     -- Move cursor left
  562.                     setCursor( x - 1, y )
  563.                 elseif x==1 and y>1 then
  564.                     setCursor( string.len( tLines[y-1] ) + 1, y - 1 )
  565.                 end
  566.             else
  567.                 -- Move menu left
  568.                 nMenuItem = nMenuItem - 1
  569.                 if nMenuItem < 1 then
  570.                     nMenuItem = #tMenuItems
  571.                 end
  572.                 redrawMenu()
  573.             end
  574.  
  575.         elseif param == keys.right then
  576.             -- Right
  577.             if not bMenu then
  578.                 local nLimit = string.len( tLines[y] ) + 1
  579.                 if x < nLimit then
  580.                     -- Move cursor right
  581.                     setCursor( x + 1, y )
  582.                 elseif nCompletion and x == string.len(tLines[y]) + 1 then
  583.                     -- Accept autocomplete
  584.                     acceptCompletion()
  585.                 elseif x==nLimit and y<#tLines then
  586.                     -- Go to next line
  587.                     setCursor( 1, y + 1 )
  588.                 end
  589.             else
  590.                 -- Move menu right
  591.                 nMenuItem = nMenuItem + 1
  592.                 if nMenuItem > #tMenuItems then
  593.                     nMenuItem = 1
  594.                 end
  595.                 redrawMenu()
  596.             end
  597.  
  598.         elseif param == keys.delete then
  599.             -- Delete
  600.             if not bMenu and not bReadOnly then
  601.                 local nLimit = string.len( tLines[y] ) + 1
  602.                 if x < nLimit then
  603.                     local sLine = tLines[y]
  604.                     tLines[y] = string.sub(sLine,1,x-1) .. string.sub(sLine,x+1)
  605.                     recomplete()
  606.                     redrawLine(y)
  607.                 elseif y<#tLines then
  608.                     tLines[y] = tLines[y] .. tLines[y+1]
  609.                     table.remove( tLines, y+1 )
  610.                     recomplete()
  611.                     redrawText()
  612.                 end
  613.             end
  614.  
  615.         elseif param == keys.backspace then
  616.             -- Backspace
  617.             if not bMenu and not bReadOnly then
  618.                 if x > 1 then
  619.                     -- Remove character
  620.                     local sLine = tLines[y]
  621.                     tLines[y] = string.sub(sLine,1,x-2) .. string.sub(sLine,x)
  622.                     setCursor( x - 1, y )
  623.                 elseif y > 1 then
  624.                     -- Remove newline
  625.                     local sPrevLen = string.len( tLines[y-1] )
  626.                     tLines[y-1] = tLines[y-1] .. tLines[y]
  627.                     table.remove( tLines, y )
  628.                     setCursor( sPrevLen + 1, y - 1 )
  629.                     redrawText()
  630.                 end
  631.             end
  632.  
  633.         elseif param == keys.enter then
  634.             -- Enter
  635.             if not bMenu and not bReadOnly then
  636.                 -- Newline
  637.                 local sLine = tLines[y]
  638.                 local _,spaces=string.find(sLine,"^[ ]+")
  639.                 if not spaces then
  640.                     spaces=0
  641.                 end
  642.                 tLines[y] = string.sub(sLine,1,x-1)
  643.                 table.insert( tLines, y+1, string.rep(' ',spaces)..string.sub(sLine,x) )
  644.                 setCursor( spaces + 1, y + 1 )
  645.                 redrawText()
  646.  
  647.             elseif bMenu then
  648.                 -- Menu selection
  649.                 doMenuItem( nMenuItem )
  650.  
  651.             end
  652.  
  653.         elseif param == keys.leftCtrl or param == keys.rightCtrl or param == keys.rightAlt then
  654.             -- Menu toggle
  655.             bMenu = not bMenu
  656.             if bMenu then
  657.                 term.setCursorBlink( false )
  658.             else
  659.                 term.setCursorBlink( true )
  660.             end
  661.             redrawMenu()
  662.  
  663.         end
  664.        
  665.     elseif sEvent == "char" then
  666.         if not bMenu and not bReadOnly then
  667.             -- Input text
  668.             local sLine = tLines[y]
  669.             tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)
  670.             setCursor( x + 1, y )
  671.  
  672.         elseif bMenu then
  673.             -- Select menu items
  674.             for n,sMenuItem in ipairs( tMenuItems ) do
  675.                 if string.lower(string.sub(sMenuItem,1,1)) == string.lower(param) then
  676.                     doMenuItem( n )
  677.                     break
  678.                 end
  679.             end
  680.         end
  681.  
  682.     elseif sEvent == "paste" then
  683.         if not bMenu and not bReadOnly then
  684.             -- Input text
  685.             local sLine = tLines[y]
  686.             tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)
  687.             setCursor( x + string.len( param ), y )
  688.         end
  689.        
  690.     elseif sEvent == "mouse_click" then
  691.         if not bMenu then
  692.             if param == 1 then
  693.                 -- Left click
  694.                 local cx,cy = param2, param3
  695.                 if cy < h then
  696.                     local newY = math.min( math.max( scrollY + cy, 1 ), #tLines )
  697.                     local newX = math.min( math.max( scrollX + cx, 1 ), string.len( tLines[newY] ) + 1 )
  698.                     setCursor( newX, newY )
  699.                 end
  700.             end
  701.         end
  702.        
  703.     elseif sEvent == "mouse_scroll" then
  704.         if not bMenu then
  705.             if param == -1 then
  706.                 -- Scroll up
  707.                 if scrollY > 0 then
  708.                     -- Move cursor up
  709.                     scrollY = scrollY - 1
  710.                     redrawText()
  711.                 end
  712.            
  713.             elseif param == 1 then
  714.                 -- Scroll down
  715.                 local nMaxScroll = #tLines - (h-1)
  716.                 if scrollY < nMaxScroll then
  717.                     -- Move cursor down
  718.                     scrollY = scrollY + 1
  719.                     redrawText()
  720.                 end
  721.                
  722.             end
  723.         end
  724.  
  725.     elseif sEvent == "term_resize" then
  726.         w,h = term.getSize()
  727.         setCursor( x, y )
  728.         redrawMenu()
  729.         redrawText()
  730.  
  731.     end
  732. end
  733.  
  734. -- Cleanup
  735. term.clear()
  736. term.setCursorBlink( false )
  737. term.setCursorPos( 1, 1 )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement