Advertisement
Tag365

ComputerCraft Advanced Edit Script

May 25th, 2017
647
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 26.94 KB | None | 0 0
  1. -- Get file to edit.
  2. local tArgs = { ... }
  3. if #tArgs == 0 then
  4.     print( "Usage: edit <path> [line number]" )
  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( sPath.." cannot be opened because it is a directory." )
  13.     return
  14. end
  15. if not fs.exists( sPath ) and bReadOnly then
  16.     print( sPath.." cannot be opened because the path is read only and the file does not exist." )
  17.     return
  18. end
  19.  
  20. -- Set up some variables
  21. local showLineNumberSidebar = true
  22. local x, y = 1, 1
  23. local shiftX = 0
  24.  
  25. if type(tonumber(tArgs[2])) == "number" then
  26.     y = math.floor(math.max(tArgs[2], 1))
  27. end
  28. local w, h = term.getSize()
  29. local scrollX, scrollY = 0, y - 1
  30.  
  31. local tLines = {}
  32. local bRunning = true
  33.  
  34. -- Code Highlighting Colors.
  35. local highlightColor, keywordColor, commentColor, textColor, bgColor, menuBgColor
  36. if term.isColor() then
  37.     menuBgColor = colors.gray
  38.     bgColor = colors.black
  39.     textColor = colors.white
  40.     highlightColor = colors.yellow
  41.     keywordColor = colors.blue
  42.     commentColor = colors.green
  43.     stringColor = colors.red
  44. else
  45.     menuBgColor = colors.black
  46.     bgColor = colors.black
  47.     textColor = colors.white
  48.     highlightColor = colors.white
  49.     keywordColor = colors.white
  50.     commentColor = colors.white
  51.     stringColor = colors.white
  52. end
  53.  
  54. -- Menus.
  55. local bMenu = false
  56. local nMenuItem = 1
  57. local tMenuItems = { "Save As...", "Exit", "Go To", "Test", "Find", "Replace", "Add Bookmark", "Bookmarks" }
  58. if not bReadOnly then
  59.     table.insert( tMenuItems, 1, "Save" )
  60. end
  61. if peripheral.find( "printer" ) then
  62.     table.insert( tMenuItems, "Print" )
  63. end
  64.  
  65. local tSplashStatus = {
  66.     "Press Ctrl to access menu.",
  67.     "Press Ctrl to access the extended menu, with more options than the default Editor included with ComputerCraft.",
  68.     "One of the most important things when creating scripts is to test them. Use the Test function to test your script.",
  69.     "If a script runs into an unprotected error the status window will tell you what error it is.",
  70.     "To add a bookmark, use the Add Bookmark function.",
  71.     "Adding an Editor Bookmark will cause the built in Autocomplete feature to automatically include that line in the list of lines to show.",
  72.     "Bookmarks can be accessed by clicking on the Bookmark button.",
  73.     "To quickly jump to the next line with a certain string in it, use the Find function.",
  74.     "To quickly jump to any line in the script, use the Go To function.",
  75.     "When attempting to edit a read-only file, use the Save As... function to start editing a new file.",
  76.     "The status bar shows the results of an action you made.",
  77. }
  78.  
  79. local sStatus = tSplashStatus[math.random(#tSplashStatus)]
  80. local nStatusScroll = -1
  81.  
  82. -- Clears the terminal screen.
  83. local function resetTerm()
  84.     term.setBackgroundColor(colors.black)
  85.     term.setTextColor(colors.white)
  86.     term.setCursorPos(1, 1)
  87.     term.clear()
  88. end
  89.  
  90. local errors = {
  91.     ["  "] = "   ",
  92.     ["—"] = "-",
  93. }
  94. -- Gets rid of errors in the editing window.
  95. local function repairString(myString)
  96.     local curString = myString
  97.     pcall(function()
  98.         local replaceStart, replaceEnd
  99.         for incorrectText, correctCharacter in pairs(errors) do
  100.             while curString:find(incorrectText, 1, true) do
  101.                 replaceStart, replaceEnd = curString:find(incorrectText, 1, true)
  102.                 curString = curString:sub(1, replaceStart - 1)..correctCharacter..curString:sub(replaceEnd + 1)
  103.             end
  104.             if curString:sub(-#incorrectText) == incorrectText then
  105.                 curString = curString:sub(1, -#incorrectText)
  106.             end
  107.         end
  108.     end)
  109.     return curString
  110. end
  111.  
  112. -- Updates the sidebar's width.
  113. local function updateSidebar()
  114.     -- If line numbers are enabled then fix the shiftX variable
  115.     if showLineNumberSidebar then
  116.         local curPower = #tLines
  117.         local power = 1
  118.         while curPower >= 10 do
  119.             curPower = curPower/10
  120.             power = power + 1
  121.         end
  122.         shiftX = power + 2
  123.     else
  124.         shiftX = 0
  125.     end
  126. end
  127.  
  128. -- Attempts to load the selected file.
  129. local function load( _sPath )
  130.     tLines = {}
  131.     if fs.exists( _sPath ) then
  132.         local file = io.open( _sPath, "r" )
  133.         local sLine = file:read()
  134.         while sLine do
  135.             table.insert( tLines, repairString(sLine) )
  136.             sLine = file:read()
  137.         end
  138.         file:close()
  139.     end
  140.  
  141.     if #tLines == 0 then
  142.         table.insert( tLines, "" )
  143.     end
  144. end
  145.  
  146. -- Attempts to save to the selected path.
  147. local function save( _sPath )
  148.     -- Create intervening folder
  149.     local sDir = sPath:sub(1, sPath:len() - fs.getName(sPath):len() )
  150.     if not fs.exists( sDir ) then
  151.         fs.makeDir( sDir )
  152.     end
  153.  
  154.     -- Save
  155.     local file = nil
  156.     local function innerSave()
  157.         file = fs.open( _sPath, "w" )
  158.         if file then
  159.             for n, sLine in ipairs( tLines ) do
  160.                 file.write( sLine .. "\n" )
  161.             end
  162.         else
  163.             error( "Failed to open ".._sPath )
  164.         end
  165.     end
  166.  
  167.     local ok = pcall( innerSave )
  168.     if file then
  169.         file.close()
  170.     end
  171.     return ok
  172. end
  173.  
  174. -- Lua keywords
  175. local tKeywords = {
  176.     ["and"] = true,
  177.     ["break"] = true,
  178.     ["do"] = true,
  179.     ["else"] = true,
  180.     ["elseif"] = true,
  181.     ["end"] = true,
  182.     ["false"] = true,
  183.     ["for"] = true,
  184.     ["function"] = true,
  185.     ["if"] = true,
  186.     ["in"] = true,
  187.     ["local"] = true,
  188.     ["nil"] = true,
  189.     ["not"] = true,
  190.     ["or"] = true,
  191.     ["repeat"] = true,
  192.     ["return"] = true,
  193.     ["then"] = true,
  194.     ["true"] = true,
  195.     ["until"] = true,
  196.     ["while"] = true,
  197. }
  198.  
  199. local function tryWrite( sLine, regex, color )
  200.     local match = string.match( sLine, regex )
  201.     if match then
  202.         if type(color) == "number" then
  203.             term.setTextColor( color )
  204.         else
  205.             term.setTextColor( color(match) )
  206.         end
  207.         term.write( match )
  208.         term.setTextColor( textColor )
  209.         return string.sub( sLine, string.len(match) + 1 )
  210.     end
  211.     return nil
  212. end
  213.  
  214. local function writeHighlighted( sLine )
  215.     while string.len(sLine) > 0 do
  216.         sLine =
  217.             tryWrite( sLine, "^%-%-%[%[.-%]%]", commentColor ) or
  218.             tryWrite( sLine, "^%-%-.*", commentColor ) or
  219.             tryWrite( sLine, "^\".-[^\\]\"", stringColor ) or
  220.             tryWrite( sLine, "^\'.-[^\\]\'", stringColor ) or
  221.             tryWrite( sLine, "^%[%[.-%]%]", stringColor ) or
  222.             tryWrite( sLine, "^[%w_]+", function( match )
  223.                 if tKeywords[ match ] then
  224.                     return keywordColor
  225.                 end
  226.                 return textColor
  227.             end ) or
  228.             tryWrite( sLine, "^[^%w_]", textColor )
  229.     end
  230. end
  231.  
  232. local tCompletions
  233. local nCompletion
  234.  
  235. local tCompleteEnv = _ENV
  236. local function complete( sLine )
  237.     local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.]+$" )
  238.     if nStartPos then
  239.         sLine = string.sub( sLine, nStartPos )
  240.     end
  241.     if #sLine > 0 then
  242.         return textutils.complete( sLine, tCompleteEnv )
  243.     end
  244.     return nil
  245. end
  246.  
  247. local function recomplete()
  248.     local sLine = tLines[y]
  249.     if not bMenu and not bReadOnly and x == string.len(sLine) + 1 then
  250.         tCompletions = complete( sLine )
  251.         if tCompletions and #tCompletions > 0 then
  252.             nCompletion = 1
  253.         else
  254.             nCompletion = nil
  255.         end
  256.     else
  257.         tCompletions = nil
  258.         nCompletion = nil
  259.     end
  260. end
  261.  
  262. local function eliminatePoint(num)
  263.     local str = tostring(num)
  264.     if str:find(".") then
  265.         return str:sub(1, -1)
  266.     end
  267.     return str
  268. end
  269.  
  270. local function writeCompletion( sLine )
  271.     if nCompletion then
  272.         local sCompletion = tCompletions[ nCompletion ]
  273.         term.setTextColor( colors.white )
  274.         term.setBackgroundColor( colors.gray )
  275.         term.write( sCompletion )
  276.         term.setTextColor( textColor )
  277.         term.setBackgroundColor( bgColor )
  278.     end
  279. end
  280.  
  281. local function redrawText()
  282.     updateSidebar()
  283.     local cursorX, cursorY = x, y
  284.     local sLine
  285.     for y=1,h-1 do
  286.         term.setCursorPos( 1 + (shiftX) - scrollX, y )
  287.         term.clearLine()
  288.  
  289.         sLine = tLines[ y + scrollY ]
  290.         if sLine ~= nil then
  291.             writeHighlighted( sLine )
  292.             if cursorY == y and cursorX == #sLine + 1 then
  293.                 writeCompletion()
  294.             end
  295.         end
  296.         term.setCursorPos( 1, y )
  297.         term.setBackgroundColor( menuBgColor )
  298.         term.write( string.rep(" ", shiftX - 1) )
  299.         term.setCursorPos( shiftX - (1 + #eliminatePoint(y + scrollY)), y )
  300.         if y + scrollY <= #tLines then
  301.             term.write( eliminatePoint(y + scrollY ))
  302.         end
  303.         term.setBackgroundColor( bgColor )
  304.     end
  305.     term.setCursorPos( x + (shiftX) - scrollX, y - scrollY )
  306. end
  307.  
  308. local function redrawLine(_nY)
  309.     updateSidebar()
  310.     local sLine = tLines[_nY]
  311.     if sLine then
  312.         local y = _nY - scrollY
  313.         term.setCursorPos( 1 + (shiftX) - scrollX, y )
  314.         if shiftX <= 0 then
  315.             term.clearLine()
  316.         else
  317.             term.write( string.rep(" ", w) )
  318.             term.setCursorPos( 1 + (shiftX) - scrollX, y )
  319.         end
  320.         writeHighlighted( sLine )
  321.         if _nY == y and x == #sLine + 1 then
  322.             writeCompletion()
  323.         end
  324.  
  325.         -- Attempt to write to the sidebar.
  326.         term.setCursorPos( 1, y )
  327.         term.setBackgroundColor( menuBgColor )
  328.         term.write( string.rep( " ", shiftX - 1 ) )
  329.         term.setCursorPos( shiftX - 1 - #eliminatePoint(y + scrollY), y )
  330.         if y + scrollY <= #tLines then
  331.             term.write( eliminatePoint(y + scrollY) )
  332.         end
  333.  
  334.         -- Write the completion.
  335.         term.setCursorPos( 1 + (shiftX + #sLine) - scrollX, y )
  336.         writeCompletion()
  337.         term.setBackgroundColor( bgColor )
  338.         term.setCursorPos( x + (shiftX) - scrollX, _nY - scrollY )
  339.     end
  340. end
  341.  
  342. local function getScroll(value)
  343.     if value%4 < 1 then
  344.         return value%4
  345.     elseif value%4 >= 1 and value%4 <= 2 then
  346.         return 1
  347.     elseif value%4 <= 3 then
  348.         return math.max(3-(value%4), 0)
  349.     elseif value%4 > 3 then
  350.         return 0.01
  351.     end
  352. end
  353.  
  354. -- Redraws the menu.
  355. local function redrawMenu()
  356.     updateSidebar()
  357.  
  358.     -- Clear line
  359.     term.setCursorPos( 1, h )
  360.     term.setBackgroundColor( menuBgColor )
  361.     term.clearLine()
  362.  
  363.     term.setCursorPos( 1, h )
  364.     if bMenu then
  365.         local startingXPos = 1
  366.         local totalWidthOfOptions = 0
  367.         for nItem, sItem in pairs( tMenuItems ) do
  368.             if nItem >= nMenuItem - ((w > 33 and 1) or 0) then
  369.                 if startingXPos < 1 then
  370.                     startingXPos = startingXPos + 1
  371.                 end
  372.                 break
  373.             end
  374.             startingXPos = startingXPos - (#sItem + 2)
  375.         end
  376.         term.setCursorPos( startingXPos, h )
  377.         -- Draw menu
  378.         term.setTextColor( textColor )
  379.         for nItem, sItem in pairs( tMenuItems ) do
  380.             if nItem == nMenuItem then
  381.                 term.setTextColor( highlightColor )
  382.                 term.write( "[" )
  383.                 term.setTextColor( textColor )
  384.                 term.write( sItem )
  385.                 term.setTextColor( highlightColor )
  386.                 term.write( "]" )
  387.                 term.setTextColor( textColor )
  388.             else
  389.                 term.write( " "..sItem.." " )
  390.             end
  391.             totalWidthOfOptions = totalWidthOfOptions + (#sItem + 2)
  392.         end
  393.  
  394.         -- Draw indicators that there are more options available.
  395.         if startingXPos < 1 then
  396.             term.setCursorPos( 1, h )
  397.             term.write("<")
  398.         end
  399.  
  400.         w, h = term.getSize()
  401.         if -startingXPos + w < totalWidthOfOptions then
  402.             term.setCursorPos( w, h )
  403.             term.write(">")
  404.         end
  405.     else
  406.         local scrollWidth = math.max((#sStatus - (w - 2)) + string.len( "Ln "..y ), 0)
  407.         nStatusScroll = nStatusScroll + math.max(.02, (w/math.max(scrollWidth, 1))/50)
  408.         local statusScroll = getScroll(nStatusScroll)
  409.         term.setCursorPos( 1, h )
  410.         -- Draw status
  411.         term.setTextColor( highlightColor )
  412.         term.write( sStatus:sub( statusScroll*scrollWidth ) )
  413.         term.setTextColor( textColor )
  414.         -- Draw line numbers
  415.         term.setCursorPos( w - string.len( "Ln "..y ), h )
  416.         term.setTextColor( highlightColor )
  417.         term.write( " Ln " )
  418.         term.setTextColor( textColor )
  419.         term.write( y )
  420.     end
  421.  
  422.     term.setBackgroundColor( bgColor )
  423.     -- Reset cursor
  424.     term.setCursorPos( x + (shiftX) - scrollX, y - scrollY )
  425. end
  426.  
  427. -- Gets a response from the user.
  428. local function getUserInput(message, ...)
  429.     term.setCursorPos(1, h)
  430.     term.setTextColor(textColor)
  431.     term.setBackgroundColor(menuBgColor)
  432.     term.clearLine()
  433.  
  434.     term.write(message)
  435.     local input = read(...)
  436.  
  437.     term.setBackgroundColor(bgColor)
  438.     redrawText()
  439.     redrawMenu()
  440.     return input
  441. end
  442.  
  443. -- A list of menu functions.
  444. local tMenuFuncs = {
  445.     Save=function()
  446.         if bReadOnly then
  447.             sStatus = "That location is read only."
  448.         else
  449.             local ok, err = save( sPath )
  450.             if ok then
  451.                 sStatus = "Saved to "..sPath.."."
  452.             else
  453.                 sStatus = "Error saving to "..sPath.."."
  454.             end
  455.         end
  456.         redrawMenu()
  457.     end,
  458.     Test=function()
  459.         -- Convert the table into a string that we can load
  460.         local scriptCode = tLines[1] or ""
  461.         for k, v in ipairs(tLines) do
  462.             if k > 1 then
  463.                 scriptCode = scriptCode.."\n"..v
  464.             end
  465.         end
  466.         -- Run the script
  467.         local fScript, err = loadstring(scriptCode, sPath or "Script")
  468.         if fScript then
  469.             resetTerm()
  470.             local args = {pcall(fScript)}
  471.             resetTerm()
  472.             local ok, msg = args[1], args[2]
  473.             table.remove(args, 1)
  474.             if ok then
  475.                 if #args > 0 then
  476.                     for key, value in ipairs(args) do
  477.                         args[key] = tostring(value)
  478.                     end
  479.                     sStatus = "Script returned "..table.concat(args, ", ").."."
  480.                 else
  481.                     sStatus = "Script ran without errors."
  482.                 end
  483.             else
  484.                 -- Print the error
  485.                 local c1 = (msg:find(']:', 0, true) or 0) + 1
  486.                 sStatus = "Error running script: "..msg:sub(c1)
  487.             end
  488.         else
  489.             if err then
  490.                 local c1 = err:find(']:', 0, true) + 1
  491.                 sStatus = "Error loading script: "..(sPath or "Script")..err:sub(c1)
  492.             else
  493.                 sStatus = "Error loading script."
  494.             end
  495.         end
  496.         redrawText()
  497.     end,
  498.     Find=function()
  499.         -- Ask for user input
  500.         local sSearch = getUserInput("Find what>")
  501.  
  502.         sStatus = "Searching..."
  503.         redrawMenu()
  504.  
  505.         -- Search for the matches
  506.         local matchx, matchy
  507.         local firstmatchx, firstmatchy
  508.  
  509.         -- Find a match.
  510.         for linenumber, v in ipairs(tLines) do
  511.             if linenumber > y then
  512.                 if string.find(v, sSearch) then
  513.                     matchx, matchy = string.find(v, sSearch), linenumber
  514.                     break
  515.                 end
  516.             elseif not firstmatchx then
  517.                 if string.find(v, sSearch) then
  518.                     firstmatchx, firstmatchy = string.find(v, sSearch), linenumber
  519.                 end
  520.             end
  521.         end
  522.  
  523.         -- Move the cursor if we found a match and update the status text.
  524.         if matchx or firstmatchx then
  525.             x, y = matchx or firstmatchx, matchy or firstmatchy
  526.             scrollX, scrollY = math.max(x - 16, 0), y - 1
  527.             sStatus = "Found match at line "..(matchy or firstmatchy).."."
  528.         else
  529.             sStatus = "No matches found for '"..sSearch.."'."
  530.         end
  531.  
  532.         redrawText()
  533.     end,
  534.     Replace=function()
  535.         -- Ask for user input
  536.         local sSearch = getUserInput("Find what>")
  537.         if sSearch == "" then return false end
  538.         local sReplace = getUserInput("Replace with>")
  539.  
  540.         sStatus = "Searching..."
  541.         redrawMenu()
  542.  
  543.         -- Replace all instances of sSearch with sReplace.
  544.         local matchx, matchy
  545.         local matches = 0
  546.         for lineNumber, v in ipairs(tLines) do
  547.             while string.find(tLines[lineNumber], sSearch) do
  548.                 matches = matches + 1
  549.                 matchx, matchy = string.find(tLines[lineNumber], sSearch)
  550.                 tLines[lineNumber] = string.sub(tLines[lineNumber], 1, matchx - 1)..sReplace..string.sub(tLines[lineNumber], matchy + 1)
  551.             end
  552.         end
  553.  
  554.         -- Update the status text.
  555.         if matchx then
  556.             sStatus = "Replaced "..matches..((matches == 1 and " instance") or " instances").." with "..sReplace.."."
  557.         else
  558.             sStatus = "No matches found for '"..sSearch.."'."
  559.         end
  560.  
  561.         redrawText()
  562.     end,
  563.     Print=function()
  564.         local printer = peripheral.find( "printer" )
  565.         if not printer then
  566.             sStatus = "No printer attached."
  567.             return
  568.         end
  569.  
  570.         local nPage = 0
  571.         local sName = fs.getName( sPath )
  572.         if printer.getInkLevel() < 1 then
  573.             sStatus = "Printer out of ink."
  574.             return
  575.         elseif printer.getPaperLevel() < 1 then
  576.             sStatus = "Printer out of paper."
  577.             return
  578.         end
  579.  
  580.         local screenTerminal = term.current()
  581.         local printerTerminal = {
  582.             getCursorPos = printer.getCursorPos,
  583.             setCursorPos = printer.setCursorPos,
  584.             getSize = printer.getPageSize,
  585.             write = printer.write,
  586.         }
  587.         printerTerminal.scroll = function()
  588.             if nPage == 1 then
  589.                 printer.setPageTitle( sName.." (page "..nPage..")" )
  590.             end
  591.  
  592.             local timer
  593.  
  594.             while not printer.newPage() do
  595.                 if printer.getInkLevel() < 1 then
  596.                     sStatus = "Printer out of ink, please refill."
  597.                 elseif printer.getPaperLevel() < 1 then
  598.                     sStatus = "Printer out of paper, please refill."
  599.                 else
  600.                     sStatus = "Printer output tray full, please empty."
  601.                 end
  602.  
  603.                 term.redirect( screenTerminal )
  604.                 redrawMenu()
  605.                 term.redirect( printerTerminal )
  606.  
  607.                 timer = os.startTimer(0.5)
  608.                 sleep(0.5)
  609.             end
  610.  
  611.             nPage = nPage + 1
  612.             if nPage == 1 then
  613.                 printer.setPageTitle( sName )
  614.             else
  615.                 printer.setPageTitle( sName.." (page "..nPage..")" )
  616.             end
  617.         end
  618.  
  619.         bMenu = false
  620.         term.redirect( printerTerminal )
  621.         local ok, error = pcall( function()
  622.             term.scroll()
  623.             for n, sLine in ipairs( tLines ) do
  624.                 print( sLine )
  625.             end
  626.         end )
  627.         term.redirect( screenTerminal )
  628.         if not ok then
  629.             print( error )
  630.         end
  631.  
  632.         while not printer.endPage() do
  633.             sStatus = "Printer output tray full, please empty."
  634.             redrawMenu()
  635.             sleep( 0.5 )
  636.         end
  637.         bMenu = true
  638.  
  639.         if nPage ~= 1 then
  640.             sStatus = "Printed "..nPage.." pages."
  641.         else
  642.             sStatus = "Printed 1 page."
  643.         end
  644.         redrawMenu()
  645.     end,
  646.     Exit=function()
  647.         bRunning = false
  648.     end
  649. }
  650.  
  651. tMenuFuncs["Save As..."] = function()
  652.     sPath = getUserInput("Save to>")
  653.     bReadOnly = fs.isReadOnly(sPath)
  654.     if bReadOnly then
  655.         sStatus = "That location is read only."
  656.     else
  657.         local ok, err = save( sPath )
  658.         if ok then
  659.             sStatus = "Saved to "..sPath.."."
  660.         else
  661.             sStatus = "Error saving to "..sPath.."."
  662.         end
  663.     end
  664.     term.setBackgroundColor(bgColor)
  665. end
  666.  
  667. tMenuFuncs["Go To"] = function()
  668.     -- Ask for user input
  669.     y = math.max(math.min(tonumber(getUserInput("Go to line>")) or y, #tLines), 1)
  670.     redrawText()
  671.  
  672.     -- Jump to the correct line.
  673.     sStatus = "Moved to line "..y.."."
  674.     scrollY = math.max(y - 6, 0)
  675.     term.setBackgroundColor(bgColor)
  676.  
  677.     redrawText()
  678. end
  679.  
  680. tMenuFuncs["Add Bookmark"] = function()
  681.     local y = y
  682.     tLines[y] = tLines[y].." ".."--Editor Bookmark--"
  683.     redrawText()
  684. end
  685.  
  686. tMenuFuncs["Bookmarks"] = function()
  687.     local bookmarks = {}
  688.     for line, lineText in ipairs(tLines) do
  689.         if string.find(lineText, "--Editor Bookmark--") then
  690.             table.insert(bookmarks, line)
  691.         end
  692.     end
  693.     local nextBookmark = bookmarks[1]
  694.     if #bookmarks > 0 then
  695.         for bookmarkKey, line in ipairs(bookmarks) do
  696.             if y < line then
  697.                 nextBookmark = line
  698.                 break
  699.             end
  700.         end
  701.         sStatus = "Bookmark found at line "..nextBookmark.."."
  702.         y = nextBookmark
  703.     else
  704.         sStatus = "No bookmarks found."
  705.     end
  706. end
  707.  
  708. local function doMenuItem( _n )
  709.     if tMenuFuncs[tMenuItems[_n]] then
  710.         tMenuFuncs[tMenuItems[_n]]()
  711.     else
  712.         sStatus = "This function is not implemented."
  713.     end
  714.     if bMenu then
  715.         bMenu = false
  716.         term.setCursorBlink( true )
  717.     end
  718.     redrawMenu()
  719. end
  720.  
  721. local function setCursor( newX, newY )
  722.     local oldX, oldY = x, y
  723.     x, y = newX, newY
  724.     local screenX = x - scrollX
  725.     local screenY = y - scrollY
  726.  
  727.     local bRedraw = false
  728.     if screenX < 1 then
  729.         scrollX = x - 1
  730.         screenX = 1
  731.         bRedraw = true
  732.     elseif screenX > w - shiftX then
  733.         scrollX = x - (w - shiftX)
  734.         screenX = w
  735.         bRedraw = true
  736.     end
  737.  
  738.     if screenY < 1 then
  739.         scrollY = y - 1
  740.         screenY = 1
  741.         bRedraw = true
  742.     elseif screenY > h - 1 then
  743.         scrollY = y - (h - 1)
  744.         screenY = h - 1
  745.         bRedraw = true
  746.     end
  747.  
  748.     recomplete()
  749.     if bRedraw then
  750.         redrawText()
  751.     elseif y ~= oldY then
  752.         redrawLine( oldY )
  753.         redrawLine( y )
  754.     else
  755.         redrawLine( y )
  756.     end
  757.     term.setCursorPos( screenX, screenY )
  758.  
  759.     -- Statusbar now pertains to menu, it would probably be safe to redraw the menu on every key event.
  760.     redrawMenu()
  761. end
  762.  
  763. local function acceptCompletion()
  764.     if nCompletion then
  765.         -- Find the common prefix of all the other suggestions which start with the same letter as the current one
  766.         local sCompletion = tCompletions[ nCompletion ]
  767.         local sFirstLetter = string.sub( sCompletion, 1, 1 )
  768.         local sCommonPrefix = sCompletion
  769.         local sResult
  770.         for n=1,#tCompletions do
  771.             sResult = tCompletions[n]
  772.             if n ~= nCompletion and string.find( sResult, sFirstLetter, 1, true ) == 1 then
  773.                 while #sCommonPrefix > 1 do
  774.                     if string.find( sResult, sCommonPrefix, 1, true ) == 1 then
  775.                         break
  776.                     else
  777.                         sCommonPrefix = string.sub( sCommonPrefix, 1, #sCommonPrefix - 1 )
  778.                     end
  779.                 end
  780.             end
  781.         end
  782.  
  783.         -- Append this string
  784.         tLines[y] = tLines[y] .. sCommonPrefix
  785.         setCursor( x + string.len( sCommonPrefix ), y )
  786.     end
  787. end
  788.  
  789. -- Actual program functionality begins. --
  790. load(sPath)
  791.  
  792. -- Stop it from erroring if you attempt to go to a non-existant line at launch-time.
  793. if #tLines < y then
  794.     y = #tLines
  795.     scrollY = y - 1
  796. end
  797.  
  798. -- Reset the sidebar.
  799. updateSidebar()
  800.  
  801. -- Clear the screen.
  802. term.setBackgroundColor( bgColor )
  803. term.clear()
  804. term.setCursorPos( x, y )
  805.  
  806. -- Draw the menu.
  807. recomplete()
  808. redrawText()
  809. redrawMenu()
  810.  
  811. -- Handle input.
  812. local ok, err = pcall(function()
  813.     while bRunning do
  814.         term.setCursorBlink( true )
  815.         local sEvent, param, param2, param3 = os.pullEvent()
  816.         term.setCursorBlink( false )
  817.         if sEvent == "key" then
  818.             local oldX, oldY = x, y
  819.             if param == keys.up then
  820.                 -- Up
  821.                 if not bMenu then
  822.                     if nCompletion then
  823.                         -- Cycle completions
  824.                         nCompletion = nCompletion - 1
  825.                         if nCompletion < 1 then
  826.                             nCompletion = #tCompletions
  827.                         end
  828.                         redrawLine(y)
  829.  
  830.                     elseif y > 1 then
  831.                         -- Move cursor up
  832.                         y = y - 1
  833.                         x = math.min( x, string.len( tLines[y] ) + 1 )
  834.                         setCursor( x, y )
  835.                     end
  836.                 end
  837.             elseif param == keys.down then
  838.                 -- Down
  839.                 if not bMenu then
  840.                     -- Move cursor down
  841.                     if nCompletion then
  842.                         -- Cycle completions
  843.                         nCompletion = nCompletion + 1
  844.                         if nCompletion > #tCompletions then
  845.                             nCompletion = 1
  846.                         end
  847.                         redrawLine(y)
  848.  
  849.                     elseif y < #tLines then
  850.                         -- Move cursor down
  851.                         y = y + 1
  852.                         x = math.min( x, string.len( tLines[y] ) + 1 )
  853.                         setCursor( x, y )
  854.                     end
  855.                 end
  856.             elseif param == keys.tab then
  857.                 -- Tab key.
  858.                 if not bMenu and not bReadOnly then
  859.                     if nCompletion and x == string.len(tLines[y]) + 1 then
  860.                         -- Accept autocomplete
  861.                         acceptCompletion()
  862.                     else
  863.                         -- Indent line.
  864.                         local sLine = tLines[y]
  865.                         tLines[y] = string.sub(sLine,1,x-1) .. "   " .. string.sub(sLine,x)
  866.                         setCursor( x + 3, y )
  867.                     end
  868.                 end
  869.             elseif param == keys.pageUp then
  870.                 -- Page Up
  871.                 if not bMenu then
  872.                     -- Move up a page
  873.                     if y - (h - 1) >= 1 then
  874.                         y = y - (h - 1)
  875.                     else
  876.                         y = 1
  877.                     end
  878.                     x = math.min( x, string.len( tLines[y] ) + 1 )
  879.                     setCursor( x, y )
  880.                 end
  881.             elseif param == keys.pageDown then
  882.                 -- Page Down
  883.                 if not bMenu then
  884.                     -- Move down a page
  885.                     if y + (h - 1) <= #tLines then
  886.                         y = y + (h - 1)
  887.                     else
  888.                         y = #tLines
  889.                     end
  890.                     x = math.min( x, string.len( tLines[y] ) + 1 )
  891.                     setCursor( x, y )
  892.                 end
  893.             elseif param == keys.home then
  894.                 -- Home
  895.                 if not bMenu then
  896.                     -- Move cursor to the beginning
  897.                     x=1
  898.                     setCursor(x,y)
  899.                 end
  900.             elseif param == keys["end"] then
  901.                 -- End
  902.                 if not bMenu then
  903.                     -- Move cursor to the end
  904.                     x = string.len( tLines[y] ) + 1
  905.                     setCursor(x,y)
  906.                 end
  907.             elseif param == keys.left then
  908.                 -- Left
  909.                 if not bMenu then
  910.                     if x > 1 then
  911.                         -- Move cursor left
  912.                         x = x - 1
  913.                     elseif x==1 and y>1 then
  914.                         x = string.len( tLines[y-1] ) + 1
  915.                         y = y - 1
  916.                     end
  917.                     setCursor( x, y )
  918.                 else
  919.                     -- Move menu left
  920.                     nMenuItem = nMenuItem - 1
  921.                     if nMenuItem < 1 then
  922.                         nMenuItem = #tMenuItems
  923.                     end
  924.                     redrawMenu()
  925.                 end
  926.             elseif param == keys.right then
  927.                 -- Right
  928.                 if not bMenu then
  929.                     if x < string.len( tLines[y] ) + 1 then
  930.                         -- Move cursor right
  931.                         x = x + 1
  932.                     elseif x==string.len( tLines[y] ) + 1 and y<#tLines then
  933.                         x = 1
  934.                         y = y + 1
  935.                     end
  936.                     setCursor( x, y )
  937.                 else
  938.                     -- Move menu right
  939.                     nMenuItem = nMenuItem + 1
  940.                     if nMenuItem > #tMenuItems then
  941.                         nMenuItem = 1
  942.                     end
  943.                     redrawMenu()
  944.                 end
  945.             elseif param == keys.delete then
  946.                 -- Delete
  947.                 if not bMenu and not bReadOnly then
  948.                     if  x < string.len( tLines[y] ) + 1 then
  949.                         local sLine = tLines[y]
  950.                         tLines[y] = string.sub(sLine,1,x-1) .. string.sub(sLine,x+1)
  951.                         redrawLine(y)
  952.                     elseif y<#tLines then
  953.                         tLines[y] = tLines[y] .. tLines[y+1]
  954.                         table.remove( tLines, y+1 )
  955.                         redrawText()
  956.                         redrawMenu()
  957.                     end
  958.                 end
  959.             elseif param == keys.backspace then
  960.                 -- Backspace
  961.                 if not bMenu and not bReadOnly then
  962.                     if x > 1 then
  963.                         -- Remove character
  964.                         local sLine = tLines[y]
  965.                         tLines[y] = string.sub(sLine,1,x-2) .. string.sub(sLine,x)
  966.                         redrawLine(y)
  967.  
  968.                         x = x - 1
  969.                         setCursor( x, y )
  970.                     elseif y > 1 then
  971.                         -- Remove newline
  972.                         local sPrevLen = string.len( tLines[y-1] )
  973.                         tLines[y-1] = tLines[y-1] .. tLines[y]
  974.                         table.remove( tLines, y )
  975.                         redrawText()
  976.  
  977.                         x = sPrevLen + 1
  978.                         y = y - 1
  979.                         setCursor( x, y )
  980.                     end
  981.                 end
  982.             elseif param == keys.enter then
  983.                 -- Enter
  984.                 if not bMenu and not bReadOnly then
  985.                     -- Newline
  986.                     local sLine = tLines[y]
  987.                     local _,spaces = string.find(sLine,"^[ ]+")
  988.                     if not spaces then
  989.                         spaces=0
  990.                     end
  991.                     tLines[y] = string.sub(sLine,1,x-1)
  992.                     table.insert( tLines, y+1, string.rep(' ',spaces)..string.sub(sLine,x) )
  993.  
  994.                     setCursor( spaces + 1, y + 1 )
  995.                     redrawText()
  996.                 elseif bMenu then
  997.                     -- Menu selection
  998.                     doMenuItem( nMenuItem )
  999.                 end
  1000.             elseif param == keys.leftCtrl or param == keys.rightCtrl then
  1001.                 -- Menu toggle
  1002.                 bMenu = not bMenu
  1003.                 if bMenu then
  1004.                     term.setCursorBlink( false )
  1005.                 else
  1006.                     term.setCursorBlink( true )
  1007.                 end
  1008.                 redrawMenu()
  1009.             end
  1010.  
  1011.         elseif sEvent == "char" then
  1012.             if not bMenu and not bReadOnly then
  1013.                 -- Input text
  1014.                 local sLine = tLines[y]
  1015.                 tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)
  1016.                 redrawLine(y)
  1017.  
  1018.                 x = x + 1
  1019.                 setCursor( x, y )
  1020.             elseif bMenu then
  1021.                 -- Select menu items
  1022.                 for n,sMenuItem in ipairs( tMenuItems ) do
  1023.                     if string.lower(string.sub(sMenuItem,1,1)) == string.lower(param) then
  1024.                         doMenuItem( n )
  1025.                         break
  1026.                     end
  1027.                 end
  1028.             end
  1029.  
  1030.         elseif sEvent == "paste" then
  1031.             if not bMenu and not bReadOnly then
  1032.                 -- Input text
  1033.                 local sLine = tLines[y]
  1034.                 tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)
  1035.                 redrawLine(y)
  1036.  
  1037.                 x = x + string.len( param )
  1038.                 setCursor( x, y )
  1039.             end
  1040.  
  1041.         elseif sEvent == "mouse_click" then
  1042.             if not bMenu then
  1043.                 if param == 1 then
  1044.                     -- Left click
  1045.                     local cx, cy = param2, param3
  1046.                     if cy < h then
  1047.                         y = math.min( math.max( scrollY + cy, 1 ), #tLines )
  1048.                         x = math.max(math.min( math.max( scrollX + cx, 1 ), string.len( tLines[y] ) + 1 + shiftX ) - shiftX, 1)
  1049.                         setCursor( x, y )
  1050.                     end
  1051.                 end
  1052.             end
  1053.  
  1054.         elseif sEvent == "mouse_scroll" then
  1055.             if not bMenu then
  1056.                 if param == -1 then
  1057.                     -- Scroll up
  1058.                     if scrollY > 0 then
  1059.                         -- Move cursor up
  1060.                         scrollY = scrollY - 1
  1061.                         redrawText()
  1062.                     end
  1063.  
  1064.                 elseif param == 1 then
  1065.                     -- Scroll down
  1066.                     local nMaxScroll = #tLines - (h-1)
  1067.                     if scrollY < nMaxScroll then
  1068.                         -- Move cursor down
  1069.                         scrollY = scrollY + 1
  1070.                         redrawText()
  1071.                     end
  1072.  
  1073.                 end
  1074.             end
  1075.  
  1076.         elseif sEvent == "term_resize" then
  1077.             w,h = term.getSize()
  1078.             setCursor( x, y )
  1079.             redrawMenu()
  1080.             redrawText()
  1081.         end
  1082.     end
  1083. end)
  1084.  
  1085. -- Cleanup after the program ends.
  1086. resetTerm()
  1087. term.setCursorBlink( false )
  1088.  
  1089. term.setCursorPos( 1, 1 )
  1090. if not ok then
  1091.     printError(err)
  1092. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement