Advertisement
programcreator

Add-On Programs: edit by ComputerCraft

Feb 17th, 2015
1,582
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.77 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.black
  27.     textColour = colours.white
  28.     highlightColour = colours.yellow
  29.     keywordColour = colours.yellow
  30.     commentColour = colours.green
  31.     stringColour = colours.red
  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 bReadOnly then
  46.     tMenuItems = { "Exit", "Print" }
  47. else
  48.     tMenuItems = { "Save", "Exit", "Print" }
  49. end
  50.    
  51. local sStatus = "Press Ctrl to access menu"
  52.  
  53. local function load( _sPath )
  54.     tLines = {}
  55.     if fs.exists( _sPath ) then
  56.         local file = io.open( _sPath, "r" )
  57.         local sLine = file:read()
  58.         while sLine do
  59.             table.insert( tLines, sLine )
  60.             sLine = file:read()
  61.         end
  62.         file:close()
  63.     end
  64.    
  65.     if #tLines == 0 then
  66.         table.insert( tLines, "" )
  67.     end
  68. end
  69.  
  70. local function save( _sPath )
  71.     -- Create intervening folder
  72.     local sDir = sPath:sub(1, sPath:len() - fs.getName(sPath):len() )
  73.     if not fs.exists( sDir ) then
  74.         fs.makeDir( sDir )
  75.     end
  76.  
  77.     -- Save
  78.     local file = nil
  79.     local function innerSave()
  80.         file = fs.open( _sPath, "w" )
  81.         if file then
  82.             for n, sLine in ipairs( tLines ) do
  83.                 file.write( sLine .. "\n" )
  84.             end
  85.         else
  86.             error( "Failed to open ".._sPath )
  87.         end
  88.     end
  89.    
  90.     local ok = pcall( innerSave )
  91.     if file then
  92.         file.close()
  93.     end
  94.     return ok
  95. end
  96.  
  97. local tKeywords = {
  98.     ["and"] = true,
  99.     ["break"] = true,
  100.     ["do"] = true,
  101.     ["else"] = true,
  102.     ["elseif"] = true,
  103.     ["end"] = true,
  104.     ["false"] = true,
  105.     ["for"] = true,
  106.     ["function"] = true,
  107.     ["if"] = true,
  108.     ["in"] = true,
  109.     ["local"] = true,
  110.     ["nil"] = true,
  111.     ["not"] = true,
  112.     ["or"] = true,
  113.     ["repeat"] = true,
  114.     ["return"] = true,
  115.     ["then"] = true,
  116.     ["true"] = true,
  117.     ["until"]= true,
  118.     ["while"] = true,
  119. }
  120.  
  121. local function tryWrite( sLine, regex, colour )
  122.     local match = string.match( sLine, regex )
  123.     if match then
  124.         if type(colour) == "number" then
  125.             term.setTextColour( colour )
  126.         else
  127.             term.setTextColour( colour(match) )
  128.         end
  129.         term.write( match )
  130.         term.setTextColour( textColour )
  131.         return string.sub( sLine, string.len(match) + 1 )
  132.     end
  133.     return nil
  134. end
  135.  
  136. local function writeHighlighted( sLine )
  137.     while string.len(sLine) > 0 do 
  138.         sLine =
  139.             tryWrite( sLine, "^%-%-%[%[.-%]%]", commentColour ) or
  140.             tryWrite( sLine, "^%-%-.*", commentColour ) or
  141.             tryWrite( sLine, "^\".-[^\\]\"", stringColour ) or
  142.             tryWrite( sLine, "^\'.-[^\\]\'", stringColour ) or
  143.             tryWrite( sLine, "^%[%[.-%]%]", stringColour ) or
  144.             tryWrite( sLine, "^[%w_]+", function( match )
  145.                 if tKeywords[ match ] then
  146.                     return keywordColour
  147.                 end
  148.                 return textColour
  149.             end ) or
  150.             tryWrite( sLine, "^[^%w_]", textColour )
  151.     end
  152. end
  153.  
  154. local function redrawText()
  155.     for y=1,h-1 do
  156.         term.setCursorPos( 1 - scrollX, y )
  157.         term.clearLine()
  158.  
  159.         local sLine = tLines[ y + scrollY ]
  160.         if sLine ~= nil then
  161.             writeHighlighted( sLine )
  162.         end
  163.     end
  164.     term.setCursorPos( x - scrollX, y - scrollY )
  165. end
  166.  
  167. local function redrawLine(_nY)
  168.     local sLine = tLines[_nY]
  169.     term.setCursorPos( 1 - scrollX, _nY - scrollY )
  170.     term.clearLine()
  171.     writeHighlighted( sLine )
  172.     term.setCursorPos( x - scrollX, _nY - scrollY )
  173. end
  174.  
  175. local function redrawMenu()
  176.     -- Clear line
  177.     term.setCursorPos( 1, h )
  178.     term.clearLine()
  179.  
  180.     -- Draw line numbers
  181.     term.setCursorPos( w - string.len( "Ln "..y ) + 1, h )
  182.     term.setTextColour( highlightColour )
  183.     term.write( "Ln " )
  184.     term.setTextColour( textColour )
  185.     term.write( y )
  186.  
  187.     term.setCursorPos( 1, h )
  188.     if bMenu then
  189.         -- Draw menu
  190.         term.setTextColour( textColour )
  191.         for nItem,sItem in pairs( tMenuItems ) do
  192.             if nItem == nMenuItem then
  193.                 term.setTextColour( highlightColour )
  194.                 term.write( "[" )
  195.                 term.setTextColour( textColour )
  196.                 term.write( sItem )
  197.                 term.setTextColour( highlightColour )
  198.                 term.write( "]" )
  199.                 term.setTextColour( textColour )
  200.             else
  201.                 term.write( " "..sItem.." " )
  202.             end
  203.         end
  204.     else
  205.         -- Draw status
  206.         term.setTextColour( highlightColour )
  207.         term.write( sStatus )
  208.         term.setTextColour( textColour )
  209.     end
  210.  
  211.     -- Reset cursor
  212.     term.setCursorPos( x - scrollX, y - scrollY )
  213. end
  214.  
  215. local tMenuFuncs = {
  216.     Save=function()
  217.         if bReadOnly then
  218.             sStatus = "Access denied"
  219.         else
  220.             local ok, err = save( sPath )
  221.             if ok then
  222.                 sStatus="Saved to "..sPath
  223.             else
  224.                 sStatus="Error saving to "..sPath
  225.             end
  226.         end
  227.         redrawMenu()
  228.     end,
  229.     Print=function()
  230.         local printer = peripheral.find( "printer" )
  231.         if not printer then
  232.             sStatus = "No printer attached"
  233.             return
  234.         end
  235.  
  236.         local nPage = 0
  237.         local sName = fs.getName( sPath )
  238.         if printer.getInkLevel() < 1 then
  239.             sStatus = "Printer out of ink"
  240.             return
  241.         elseif printer.getPaperLevel() < 1 then
  242.             sStatus = "Printer out of paper"
  243.             return
  244.         end
  245.  
  246.         local screenTerminal = term.current()
  247.         local printerTerminal = {
  248.             getCursorPos = printer.getCursorPos,
  249.             setCursorPos = printer.setCursorPos,
  250.             getSize = printer.getPageSize,
  251.             write = printer.write,
  252.         }
  253.         printerTerminal.scroll = function()
  254.             if nPage == 1 then
  255.                 printer.setPageTitle( sName.." (page "..nPage..")" )           
  256.             end
  257.            
  258.             while not printer.newPage() do
  259.                 if printer.getInkLevel() < 1 then
  260.                     sStatus = "Printer out of ink, please refill"
  261.                 elseif printer.getPaperLevel() < 1 then
  262.                     sStatus = "Printer out of paper, please refill"
  263.                 else
  264.                     sStatus = "Printer output tray full, please empty"
  265.                 end
  266.    
  267.                 term.redirect( screenTerminal )
  268.                 redrawMenu()
  269.                 term.redirect( printerTerminal )
  270.                
  271.                 local timer = os.startTimer(0.5)
  272.                 sleep(0.5)
  273.             end
  274.  
  275.             nPage = nPage + 1
  276.             if nPage == 1 then
  277.                 printer.setPageTitle( sName )
  278.             else
  279.                 printer.setPageTitle( sName.." (page "..nPage..")" )
  280.             end
  281.         end
  282.        
  283.         bMenu = false
  284.         term.redirect( printerTerminal )
  285.         local ok, error = pcall( function()
  286.             term.scroll()
  287.             for n, sLine in ipairs( tLines ) do
  288.                 print( sLine )
  289.             end
  290.         end )
  291.         term.redirect( screenTerminal )
  292.         if not ok then
  293.             print( error )
  294.         end
  295.        
  296.         while not printer.endPage() do
  297.             sStatus = "Printer output tray full, please empty"
  298.             redrawMenu()
  299.             sleep( 0.5 )
  300.         end
  301.         bMenu = true
  302.            
  303.         if nPage > 1 then
  304.             sStatus = "Printed "..nPage.." Pages"
  305.         else
  306.             sStatus = "Printed 1 Page"
  307.         end
  308.         redrawMenu()
  309.     end,
  310.     Exit=function()
  311.         bRunning = false
  312.     end
  313. }
  314.  
  315. local function doMenuItem( _n )
  316.     tMenuFuncs[tMenuItems[_n]]()
  317.     if bMenu then
  318.         bMenu = false
  319.         term.setCursorBlink( true )
  320.     end
  321.     redrawMenu()
  322. end
  323.  
  324. local function setCursor( x, y )
  325.     local screenX = x - scrollX
  326.     local screenY = y - scrollY
  327.    
  328.     local bRedraw = false
  329.     if screenX < 1 then
  330.         scrollX = x - 1
  331.         screenX = 1
  332.         bRedraw = true
  333.     elseif screenX > w then
  334.         scrollX = x - w
  335.         screenX = w
  336.         bRedraw = true
  337.     end
  338.    
  339.     if screenY < 1 then
  340.         scrollY = y - 1
  341.         screenY = 1
  342.         bRedraw = true
  343.     elseif screenY > h-1 then
  344.         scrollY = y - (h-1)
  345.         screenY = h-1
  346.         bRedraw = true
  347.     end
  348.    
  349.     if bRedraw then
  350.         redrawText()
  351.     end
  352.     term.setCursorPos( screenX, screenY )
  353.    
  354.     -- Statusbar now pertains to menu, it would probably be safe to redraw the menu on every key event.
  355.     redrawMenu()
  356. end
  357.  
  358. -- Actual program functionality begins
  359. load(sPath)
  360.  
  361. term.setBackgroundColour( bgColour )
  362. term.clear()
  363. term.setCursorPos(x,y)
  364. term.setCursorBlink( true )
  365.  
  366. redrawText()
  367. redrawMenu()
  368.  
  369. -- Handle input
  370. while bRunning do
  371.     local sEvent, param, param2, param3 = os.pullEvent()
  372.     if sEvent == "key" then
  373.         if param == keys.up then
  374.             -- Up
  375.             if not bMenu then
  376.                 if y > 1 then
  377.                     -- Move cursor up
  378.                     y = y - 1
  379.                     x = math.min( x, string.len( tLines[y] ) + 1 )
  380.                     setCursor( x, y )
  381.                 end
  382.             end
  383.         elseif param == keys.down then
  384.             -- Down
  385.             if not bMenu then
  386.                 -- Move cursor down
  387.                 if y < #tLines then
  388.                     y = y + 1
  389.                     x = math.min( x, string.len( tLines[y] ) + 1 )
  390.                     setCursor( x, y )
  391.                 end
  392.             end
  393.         elseif param == keys.tab then
  394.             -- Tab
  395.             if not bMenu and not bReadOnly then
  396.                 -- Indent line
  397.                 tLines[y]="  "..tLines[y]
  398.                 x = x + 2
  399.                 setCursor( x, y )
  400.                 redrawLine(y)
  401.             end
  402.         elseif param == keys.pageUp then
  403.             -- Page Up
  404.             if not bMenu then
  405.                 -- Move up a page
  406.                 if y - (h - 1) >= 1 then
  407.                     y = y - (h - 1)
  408.                 else
  409.                     y = 1
  410.                 end
  411.                 x = math.min( x, string.len( tLines[y] ) + 1 )
  412.                 setCursor( x, y )
  413.             end
  414.         elseif param == keys.pageDown then
  415.             -- Page Down
  416.             if not bMenu then
  417.                 -- Move down a page
  418.                 if y + (h - 1) <= #tLines then
  419.                     y = y + (h - 1)
  420.                 else
  421.                     y = #tLines
  422.                 end
  423.                 x = math.min( x, string.len( tLines[y] ) + 1 )
  424.                 setCursor( x, y )
  425.             end
  426.         elseif param == keys.home then
  427.             -- Home
  428.             if not bMenu then
  429.                 -- Move cursor to the beginning
  430.                 x=1
  431.                 setCursor(x,y)
  432.             end
  433.         elseif param == keys["end"] then
  434.             -- End
  435.             if not bMenu then
  436.                 -- Move cursor to the end
  437.                 x = string.len( tLines[y] ) + 1
  438.                 setCursor(x,y)
  439.             end
  440.         elseif param == keys.left then
  441.             -- Left
  442.             if not bMenu then
  443.                 if x > 1 then
  444.                     -- Move cursor left
  445.                     x = x - 1
  446.                 elseif x==1 and y>1 then
  447.                     x = string.len( tLines[y-1] ) + 1
  448.                     y = y - 1
  449.                 end
  450.                 setCursor( x, y )
  451.             else
  452.                 -- Move menu left
  453.                 nMenuItem = nMenuItem - 1
  454.                 if nMenuItem < 1 then
  455.                     nMenuItem = #tMenuItems
  456.                 end
  457.                 redrawMenu()
  458.             end
  459.         elseif param == keys.right then
  460.             -- Right
  461.             if not bMenu then
  462.                 if x < string.len( tLines[y] ) + 1 then
  463.                     -- Move cursor right
  464.                     x = x + 1
  465.                 elseif x==string.len( tLines[y] ) + 1 and y<#tLines then
  466.                     x = 1
  467.                     y = y + 1
  468.                 end
  469.                 setCursor( x, y )
  470.             else
  471.                 -- Move menu right
  472.                 nMenuItem = nMenuItem + 1
  473.                 if nMenuItem > #tMenuItems then
  474.                     nMenuItem = 1
  475.                 end
  476.                 redrawMenu()
  477.             end
  478.         elseif param == keys.delete then
  479.             -- Delete
  480.             if not bMenu and not bReadOnly then
  481.                 if  x < string.len( tLines[y] ) + 1 then
  482.                     local sLine = tLines[y]
  483.                     tLines[y] = string.sub(sLine,1,x-1) .. string.sub(sLine,x+1)
  484.                     redrawLine(y)
  485.                 elseif y<#tLines then
  486.                     tLines[y] = tLines[y] .. tLines[y+1]
  487.                     table.remove( tLines, y+1 )
  488.                     redrawText()
  489.                     redrawMenu()
  490.                 end
  491.             end
  492.         elseif param == keys.backspace then
  493.             -- Backspace
  494.             if not bMenu and not bReadOnly then
  495.                 if x > 1 then
  496.                     -- Remove character
  497.                     local sLine = tLines[y]
  498.                     tLines[y] = string.sub(sLine,1,x-2) .. string.sub(sLine,x)
  499.                     redrawLine(y)
  500.            
  501.                     x = x - 1
  502.                     setCursor( x, y )
  503.                 elseif y > 1 then
  504.                     -- Remove newline
  505.                     local sPrevLen = string.len( tLines[y-1] )
  506.                     tLines[y-1] = tLines[y-1] .. tLines[y]
  507.                     table.remove( tLines, y )
  508.                     redrawText()
  509.                
  510.                     x = sPrevLen + 1
  511.                     y = y - 1
  512.                     setCursor( x, y )
  513.                 end
  514.             end
  515.         elseif param == keys.enter then
  516.             -- Enter
  517.             if not bMenu and not bReadOnly then
  518.                 -- Newline
  519.                 local sLine = tLines[y]
  520.                 local _,spaces=string.find(sLine,"^[ ]+")
  521.                 if not spaces then
  522.                     spaces=0
  523.                 end
  524.                 tLines[y] = string.sub(sLine,1,x-1)
  525.                 table.insert( tLines, y+1, string.rep(' ',spaces)..string.sub(sLine,x) )
  526.                 redrawText()
  527.            
  528.                 x = spaces+1
  529.                 y = y + 1
  530.                 setCursor( x, y )
  531.             elseif bMenu then
  532.                 -- Menu selection
  533.                 doMenuItem( nMenuItem )
  534.             end
  535.         elseif param == keys.leftCtrl or param == keys.rightCtrl then
  536.             -- Menu toggle
  537.             bMenu = not bMenu
  538.             if bMenu then
  539.                 term.setCursorBlink( false )
  540.             else
  541.                 term.setCursorBlink( true )
  542.             end
  543.             redrawMenu()
  544.         end
  545.        
  546.     elseif sEvent == "char" then
  547.         if not bMenu and not bReadOnly then
  548.             -- Input text
  549.             local sLine = tLines[y]
  550.             tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)
  551.             redrawLine(y)
  552.        
  553.             x = x + 1
  554.             setCursor( x, y )
  555.         elseif bMenu then
  556.             -- Select menu items
  557.             for n,sMenuItem in ipairs( tMenuItems ) do
  558.                 if string.lower(string.sub(sMenuItem,1,1)) == string.lower(param) then
  559.                     doMenuItem( n )
  560.                     break
  561.                 end
  562.             end
  563.         end
  564.  
  565.     elseif sEvent == "paste" then
  566.         if not bMenu and not bReadOnly then
  567.             -- Input text
  568.             local sLine = tLines[y]
  569.             tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)
  570.             redrawLine(y)
  571.  
  572.             x = x + string.len( param )
  573.             setCursor( x, y )
  574.         end
  575.        
  576.     elseif sEvent == "mouse_click" then
  577.         if not bMenu then
  578.             if param == 1 then
  579.                 -- Left click
  580.                 local cx,cy = param2, param3
  581.                 if cy < h then
  582.                     y = math.min( math.max( scrollY + cy, 1 ), #tLines )
  583.                     x = math.min( math.max( scrollX + cx, 1 ), string.len( tLines[y] ) + 1 )
  584.                     setCursor( x, y )
  585.                 end
  586.             end
  587.         end
  588.        
  589.     elseif sEvent == "mouse_scroll" then
  590.         if not bMenu then
  591.             if param == -1 then
  592.                 -- Scroll up
  593.                 if scrollY > 0 then
  594.                     -- Move cursor up
  595.                     scrollY = scrollY - 1
  596.                     redrawText()
  597.                 end
  598.            
  599.             elseif param == 1 then
  600.                 -- Scroll down
  601.                 local nMaxScroll = #tLines - (h-1)
  602.                 if scrollY < nMaxScroll then
  603.                     -- Move cursor down
  604.                     scrollY = scrollY + 1
  605.                     redrawText()
  606.                 end
  607.                
  608.             end
  609.         end
  610.  
  611.     elseif sEvent == "term_resize" then
  612.         w,h = term.getSize()
  613.         setCursor( x, y )
  614.         redrawMenu()
  615.         redrawText()
  616.  
  617.     end
  618. end
  619.  
  620. -- Cleanup
  621. term.clear()
  622. term.setCursorBlink( false )
  623. term.setCursorPos( 1, 1 )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement