QuickMuffin8782-Alt

Untitled

Nov 8th, 2020 (edited)
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. tArgs = { ... }
  2.  
  3. --[[
  4.     Note Taker      PaymentOption
  5.                     23 December 2012
  6.                    
  7.     Simple text editor using colors and
  8.     line numbers and a custom cursor.
  9. ]]--
  10.  
  11. -- This program can only be used on an advanced computer, so make sure
  12. -- that the computer we're working on supports color.
  13. if not term.isColor() then
  14.     print("Color must be supported.")
  15.     return
  16. end
  17.  
  18. -- Make sure that we have the proper arguments to continue.
  19. if #tArgs < 1 then
  20.     print("Usage: " .. fs.getName(shell.getRunningProgram()) .. " <file_path>")
  21.     return
  22. elseif fs.isDir(tArgs[1]) then
  23.     print("Cannot edit a directory.")
  24.     return
  25. elseif fs.isReadOnly(tArgs[1]) then
  26.     print("Cannot edit a read only file.")
  27.     return
  28. end
  29.  
  30. -- Variables --
  31. local running = true -- Whether or not the program should be running.
  32.  
  33. local SHOULD_HIGHLIGHT_SYNTAX = true -- Whether or not the program should highlight syntax.
  34. local TAB_SIZE = 3 -- The number of spaces inserted into the current line when the tab key is pressed.
  35.  
  36. local BACKGROUND_COLOR   = colors.black
  37. local CURRENT_LINE_COLOR = colors.gray
  38. local TEXT_COLOR         = colors.white
  39.  
  40. local LINE_NUMBER_COLOR            = colors.white
  41. local LINE_NUMBER_BACKGROUND_COLOR = colors.gray
  42.  
  43. local CURSOR_BLINK_TIME = 0.3
  44. local CURSOR_COLOR      = colors.blue
  45. local CURSOR_TEXT       = colors.white
  46. local shouldCursorBlink = false
  47. local cursorBlinkTimer  = os.startTimer(CURSOR_BLINK_TIME)
  48.  
  49. local path  = tArgs[1] -- The path for the program to load files from or to create a new file at.
  50. local lines = {} -- All of the lines that are contained within the  current file.
  51. local hasBeenSavedSinceLastChar = true -- Whether or not the file has been saved since the last character modification was made.
  52.  
  53. local xPosOffset = 3
  54. local yPosOffset = 3
  55.  
  56. local WIDTH, HEIGHT = term.getSize() -- The width and height dimensions of the terminal we're working on.
  57. local scroll = {x = 0, y = 0} -- The amount of characters/lines that are to be scrolled when editing.
  58. local pos    = {x = 3, y = 3} -- The position of the cursor in the file. Scrolling will be handled in another function.
  59.  
  60. local COLORS = {
  61.     COMMENT_COLOR = colors.lightGray,
  62.     STRING_COLOR  = colors.red,
  63.     KEYWORD_COLOR = colors.orange
  64. }
  65.  
  66. local keywords = { -- All keywords that are to be highlighted when drawing the screen. Also, the colors for each keyword are stored by their
  67.                    -- string index.
  68.     ["local"]    = true,
  69.     ["function"] = true,
  70.     ["if"]       = true,
  71.     ["then"]     = true,
  72.     ["elseif"]   = true,
  73.     ["else"]     = true,
  74.     ["end"]      = true,
  75.     ["repeat"]   = true,
  76.     ["until"]    = true,
  77.     ["for"]      = true,
  78.     ["in"]       = true,
  79.     ["ipairs"]   = true,
  80.     ["pairs"]    = true,
  81.     ["and"]      = true,
  82.     ["or"]       = true,
  83.     ["not"]      = true,
  84.     ["do"]       = true,
  85.     ["true"]     = true,
  86.     ["false"]    = true,
  87.     ["while"]    = true
  88.  
  89. }
  90. -- Variables --
  91.  
  92. -- File functions --
  93. -- Reads all of the lines in the file at 'path' into a table and returns aforementioned table.
  94. -- In the case that the path provided doesn't exist, then a table with a single empty string will
  95. -- be returned so that we can create an empty file for the user to save.
  96. function loadLinesFromFile(path)
  97.     -- If the file doesn't exist, then setup the lines table as a single empty line.
  98.     -- The file will be created when the programs saves.
  99.     if not fs.exists(path) then
  100.         return {""}
  101.     end
  102.    
  103.     -- In the case that the file exists, then load its contents into the lines table.
  104.     local fileHandle = io.open(path, 'r')
  105.     local line       = fileHandle:read()
  106.     local lines      = {}
  107.    
  108.     -- Read a line from the file so long as the line read
  109.     -- isn't nil. (When the line is nil it means that we have reached the end of the file.)
  110.     while line do
  111.         table.insert(lines, line)
  112.         line = fileHandle:read()
  113.     end
  114.     fileHandle:close()
  115.    
  116.     -- Setup the proper xPosOffset for the number of lines loaded.
  117.     if #lines >= 10 then
  118.         -- Find out which power of ten the number of lines is divisible by.
  119.         local power    = 1
  120.         local quotient = #lines / (math.pow(10, power))
  121.        
  122.         -- Divide the number of lines in the file by a power of ten until
  123.         -- the resulting decimal is less than 1.
  124.         -- Once it is less than 1, then we have the greatest power of ten that can
  125.         -- fit into the number of lines, allowing us to calculate the offset of the cursor.
  126.         while quotient >= 1 do
  127.             if #lines / (10^power) >= 1 then
  128.                 quotient = #lines / (10^power)
  129.                 power = power + 1
  130.             else
  131.                 break
  132.             end
  133.         end
  134.        
  135.         -- Update the cursor offset and the cursor position.
  136.         xPosOffset = power + 2
  137.         pos.x      = xPosOffset
  138.     end
  139.    
  140.     return lines
  141. end
  142.  
  143. -- Saves all of the lines in the lines table to the file at 'path'.
  144. -- If the directory that is specified by 'path' doesn't exist, then
  145. -- it will be created so that the file can be saved properly.
  146. function saveLinesToFile(path)
  147.     -- Make sure that the path to the file excluding the name exists.
  148.     -- In the case that it doesn't, then we'll make the directory to
  149.     -- put the file in.
  150.     local pathWithoutName = path:sub(1, path:len() - fs.getName(path):len())
  151.     if not fs.isDir(pathWithoutName) then
  152.         fs.makeDir(pathWithoutName)
  153.     end
  154.    
  155.     -- Save the lines in the lines table to the path provided by looping
  156.     -- through the lines table and writing each entry using fs.writeLine().
  157.     local fileHandle = fs.open(path, 'w')
  158.     for lineNumber, line in ipairs(lines) do
  159.         fileHandle.writeLine(line)
  160.     end
  161.     fileHandle.close()
  162. end
  163. -- File functions --
  164.  
  165. -- Drawing functions --
  166. -- Writes the given text using the x position offset as the cut off for strings appearing on screen.
  167. -- This should be used only for drawing the lines in the file and not for the drawing of the interface.
  168. function writeLimited(text)
  169.     local xPos, yPos = term.getCursorPos()
  170.     local old_xPos   = xPos
  171.     local old_text   = text
  172.    
  173.     if xPos < xPosOffset then
  174.         text = text:sub((xPosOffset - xPos) + 1)
  175.         xPos = xPosOffset
  176.     end
  177.    
  178.     term.setCursorPos(xPos, yPos)
  179.     term.write(text)
  180.    
  181.     term.setCursorPos(old_xPos + old_text:len(), yPos)
  182. end
  183.  
  184. -- Code shamelessly stolen from dan200's edit program. ************
  185. local function tryWrite( sLine, regex, colour )
  186.     local match = string.match( sLine, regex )
  187.     if match then
  188.         if type(colour) == "number" then
  189.             term.setTextColour( colour )
  190.         else
  191.             term.setTextColour( colour(match) )
  192.         end
  193.         writeLimited( match )
  194.         term.setTextColour( TEXT_COLOR )
  195.         return string.sub( sLine, string.len(match) + 1 )
  196.     end
  197.     return nil
  198. end
  199.  
  200. local function writeHighlighted( sLine )
  201.     while string.len(sLine) > 0 do 
  202.         sLine =
  203.             tryWrite( sLine, "^%-%-%[%[.-%]%]", COLORS.COMMENT_COLOR ) or
  204.             tryWrite( sLine, "^%-%-.*", COLORS.COMMENT_COLOR ) or
  205.             tryWrite( sLine, "^\".-[^\\]\"", COLORS.STRING_COLOR ) or
  206.             tryWrite( sLine, "^\'.-[^\\]\'", COLORS.STRING_COLOR ) or
  207.             tryWrite( sLine, "^%[%[.-%]%]", COLORS.STRING_COLOR ) or
  208.             tryWrite( sLine, "^[%w_]+", function( match )
  209.                 if keywords[ match ] then
  210.                     return COLORS.KEYWORD_COLOR
  211.                 end
  212.                 return TEXT_COLOR
  213.             end ) or
  214.             tryWrite( sLine, "^[^%w_]", TEXT_COLOR )
  215.     end
  216. end
  217. -- End shameless code stealing. All credit goes to dan200. *******
  218.  
  219. -- Draws the title bar for the program which includes the name of the program and whether or not it has
  220. -- been saved since it was edited last.
  221. function drawTitleBar()
  222.     -- Get the file path to display that we are editing that file. If the file path is too long,
  223.     -- then truncate it.
  224.     local truncatedFilePath = ((path:len() > (WIDTH - 14)) and path:sub(1, (WIDTH - 14)) .. "..." or path) .. (hasBeenSavedSinceLastChar and '' or '*')
  225.     -- Redraw the title bar at the top of the screen.
  226.     term.setCursorPos(1, 1)
  227.     term.setBackgroundColor(colors.gray)
  228.     term.setTextColor(colors.white)
  229.     term.clearLine()
  230.     term.setCursorPos(2, 1)
  231.     term.write("Speedy Edit v1.0.0 - " .. truncatedFilePath)
  232.     if igp then
  233.         igp.setTitleBarShow(false)
  234.     igp.setDragPoints(1, 1, WIDTH - 1, 1)
  235.     end
  236.     term.setCursorPos(WIDTH, 1)
  237.     term.setTextColor(colors.red)
  238.     term.write(string.char(7))
  239.     term.setTextColor(colors.white)
  240. end
  241.  
  242. -- Draws the line numbers in the file.
  243. -- Accounts for scrolling too.
  244. -- NOTE: This clears the entire line. The line must be redrawn
  245. --       after the line number has been drawn.
  246. function drawLineNumbers()
  247.     drawTitleBar()
  248.    
  249.     -- Redraw the menu option.
  250.     term.setCursorPos(1, 2)
  251.     term.setBackgroundColor(colors.gray)
  252.     term.clearLine()
  253.     term.setBackgroundColor(colors.blue)
  254.     term.setCursorPos(WIDTH - ("[ File ]"):len(), 2)
  255.     term.write("[ File ]")
  256.    
  257.     for lineNumber = yPosOffset + scroll.y, HEIGHT + scroll.y do
  258.         term.setCursorPos(1, lineNumber - scroll.y)
  259.        
  260.         if lineNumber == pos.y then
  261.             term.setBackgroundColor(CURRENT_LINE_COLOR)
  262.         else
  263.             term.setBackgroundColor(BACKGROUND_COLOR)
  264.         end
  265.        
  266.         term.setTextColor(TEXT_COLOR)
  267.         term.clearLine()
  268.         if lineNumber - yPosOffset + 1 <= #lines then
  269.             term.setBackgroundColor(LINE_NUMBER_BACKGROUND_COLOR)
  270.             term.setTextColor(LINE_NUMBER_COLOR)
  271.            
  272.             term.write(tostring(lineNumber - yPosOffset + 1) .. (string.rep(' ', tostring(#lines):len() - tostring(lineNumber - yPosOffset + 1):len())))
  273.         else
  274.             term.setBackgroundColor(LINE_NUMBER_BACKGROUND_COLOR)
  275.             term.setTextColor(LINE_NUMBER_COLOR)
  276.             term.write(string.rep(' ', xPosOffset - 2))
  277.         end
  278.        
  279.         -- Reset the colors for the editor so that only the part
  280.         -- where the line number is drawn has color.
  281.         term.setBackgroundColor(BACKGROUND_COLOR)
  282.         term.setTextColor(TEXT_COLOR)
  283.     end
  284. end
  285.  
  286. -- Draws the current line number.
  287. function drawCurrentLineNumber()
  288.     local lineNumber = pos.y
  289.     term.setCursorPos(1, lineNumber - scroll.y)
  290.    
  291.     term.setBackgroundColor(CURRENT_LINE_COLOR)
  292.     term.clearLine()
  293.    
  294.     term.setCursorPos(1, lineNumber - scroll.y)
  295.     term.setBackgroundColor(LINE_NUMBER_BACKGROUND_COLOR)
  296.     term.setTextColor(LINE_NUMBER_COLOR)
  297.     term.write(tostring(lineNumber - yPosOffset + 1) .. (string.rep(' ', tostring(#lines):len() - tostring(lineNumber - yPosOffset + 1):len())))
  298. end
  299.  
  300. -- Draws the current line.
  301. function drawCurrentLine()
  302.     drawTitleBar()
  303.     drawCurrentLineNumber()
  304.     local lineNumber = pos.y + scroll.y
  305.     local line       = lines[(pos.y - yPosOffset + 1)]
  306.     term.setBackgroundColor(CURRENT_LINE_COLOR)
  307.     term.setTextColor(TEXT_COLOR)
  308.    
  309.     term.setCursorPos(xPosOffset - scroll.x, pos.y - scroll.y)
  310.    
  311.     if SHOULD_HIGHLIGHT_SYNTAX then
  312.         writeHighlighted(line)
  313.     else
  314.         writeLimited(line)
  315.     end
  316.    
  317.     term.setBackgroundColor(BACKGROUND_COLOR)
  318. end
  319.  
  320. -- Draws all of the lines in the file that can fit on the screen
  321. -- depending on how the screen is scrolled. Sets the cursor position
  322. -- in the proper place after redrawing.
  323. function drawLines()
  324.     -- Redraw the line numbers.
  325.     drawLineNumbers()
  326.     -- NOTE: The above function call will have cleared all of the lines on screen.
  327.    
  328.     for lineNumber = yPosOffset + scroll.y, HEIGHT + scroll.y do
  329.         -- Offset the line to threee characters over the compensate
  330.         -- for the line numbers.
  331.         term.setCursorPos(xPosOffset - scroll.x, lineNumber - scroll.y)
  332.        
  333.         -- Get the line at the supposed line number. If the line we selected doesn't exist,
  334.         -- then don't draw it.
  335.         local line = lines[lineNumber - (yPosOffset - 1)]
  336.         if line then
  337.             -- Scroll the line drawn by getting a substring of it from the first
  338.             -- character plus how many characters are to be scrolled until how many
  339.             -- characters will fit on the screen plus how much is to be scrolled.
  340.             if lineNumber == pos.y then
  341.                 term.setBackgroundColor(CURRENT_LINE_COLOR)
  342.             end
  343.            
  344.             if SHOULD_HIGHLIGHT_SYNTAX then
  345.                 writeHighlighted(line)
  346.             else
  347.                 writeLimited(line)
  348.             end
  349.    
  350.             term.setBackgroundColor(BACKGROUND_COLOR)
  351.         end
  352.     end
  353.    
  354.     -- Reset the cursor position to the proper place so that the user may write.
  355.     term.setCursorPos(pos.x - scroll.x, pos.y - scroll.y)
  356. end
  357.  
  358. -- Draws the cursor using the constant 'CURSOR_COLOR'.
  359. function drawAndSetCursor(x, y)
  360.     term.setCursorPos(x, y)
  361.     term.setBackgroundColor(CURSOR_COLOR)
  362.     term.setTextColor(CURSOR_TEXT)
  363.     if shouldCursorBlink then
  364.         local charAtCursor = lines[pos.y - (yPosOffset - 1)]:sub(pos.x - (xPosOffset - 1), pos.x - (xPosOffset - 1))
  365.         if charAtCursor == "" then
  366.             charAtCursor = ' '
  367.         end
  368.        
  369.         term.write(charAtCursor)
  370.     end
  371.     term.setBackgroundColor(BACKGROUND_COLOR)
  372.    
  373.     term.setCursorPos(45, 1)
  374. end
  375. -- Drawing functions --
  376.  
  377. -- Menu functions --
  378. -- Draws the main menu that contains the save, exit, and color config menu.
  379. function drawMainMenu()
  380.     local options = { -- The options in the main menu.
  381.         "  Save  ",
  382.         "  Exit  "
  383.     }
  384.    
  385.     -- Since the length of all of the options as strings are all the same, calculate the x position
  386.     -- where each option should be drawn in order for them to all be in the center of the screen.
  387.     local width = (#options[1] + #options[2]) * 2
  388.     local xPos  = WIDTH/2 - width/2
  389.     local yPos  = HEIGHT/2 - 3
  390.     local index = 1
  391.    
  392.     -- Draws the menu in the center of the screen.
  393.     local function draw()
  394.         term.setBackgroundColor(colors.gray)
  395.         term.setCursorPos(xPos, yPos)
  396.         term.write(string.rep(' ', width))
  397.        
  398.         term.setTextColor(colors.white)
  399.         term.setCursorPos(WIDTH/2 - ("Menu"):len()/2, yPos)
  400.         term.write("Menu")
  401.         term.setCursorPos(xPos + width - 1, yPos)
  402.         term.setTextColor(colors.red)
  403.         term.write(string.char(7))
  404.     term.setTextColor(colors.white)
  405.        
  406.         term.setBackgroundColor(colors.white)
  407.         for line = yPos + 1, yPos + #options + 1 do
  408.             term.setCursorPos(xPos, line)
  409.             term.write(string.rep(' ', width))
  410.         end
  411.         term.setBackgroundColor(colors.gray)
  412.        
  413.         term.setCursorPos(xPos + 3, yPos + #options)
  414.         term.write("[ " .. options[1] .. " ]")
  415.        
  416.         term.setBackgroundColor(colors.white)
  417.         term.write("  ")
  418.         term.setBackgroundColor(colors.gray)
  419.         term.write("[ " .. options[2] .. " ]")
  420.     end
  421.    
  422.     -- Handles clicks an other interaction with the menu.
  423.     -- After any click, the function will return.
  424.     local function handleClicks(xPos, yPos)
  425.         while true do
  426.             local event, p1, p2, p3 = os.pullEvent()
  427.            
  428.             -- Handle clicks.
  429.             if event == "mouse_click" then
  430.                 -- If the click was on the save option, then save the current file.
  431.                 if wasClickOnRange(p2, p3, xPos + 3, math.floor(yPos + #options), ("[ " .. options[1] .. " ]"):len()) then
  432.                     saveLinesToFile(path)
  433.                     hasBeenSavedSinceLastChar = true
  434.                     return
  435.                 -- If the click was on the exit option, then exit the program.
  436.                 elseif wasClickOnRange(p2, p3, xPos + 3 + ("[ " .. options[1] .. " ]"):len(), math.floor(yPos + #options), ("[ " .. options[2] .. " ]"):len()) then
  437.                     running = false
  438.                     return
  439.                 -- If the click was on the red 'X' at the top of the menu dialog, then close the dialog box and return to the editor.
  440.                 -- Close the dialog box if the user pressed either of the control keys.
  441.                 elseif wasClickOnRange(p2, p3, xPos + width - 2, math.floor(yPos), 1) then
  442.                     return
  443.                 end
  444.             -- Handle key presses.
  445.             elseif event == "key" then
  446.                 -- If the key pressed was one of the control keys, then exit the dialog box.
  447.                 if p1 == 29 or p1 == 157 then
  448.                     return
  449.                 -- If the key pressed was 's' then save the file and close the dialog box.
  450.                 elseif p1 == keys['s'] then
  451.                     -- Catch 's' character event thrown.
  452.                     os.pullEvent()
  453.                     saveLinesToFile(path)
  454.                     hasBeenSavedSinceLastChar = true
  455.                     return
  456.                 -- If the key pressed was 'e' then exit the program.
  457.                 elseif p1 == keys['e'] then
  458.                     -- Catch 'e' character event thrown.
  459.                     os.pullEvent()
  460.                    
  461.                     -- Cleanup.
  462.                     running = false
  463.                     return
  464.                 end
  465.             end
  466.         end
  467.     end
  468.    
  469.     draw()
  470.     handleClicks(xPos, yPos)
  471. end
  472. -- Menu functions --
  473.  
  474. -- Click functions --
  475. -- Checks whether or not the given click was on the given range.
  476. function wasClickOnRange(xClickPos, yClickPos, xPos, yPos, width)
  477.     return xClickPos >= xPos and xClickPos <= xPos + width and yClickPos == yPos
  478. end
  479. -- Click functions --
  480.  
  481. -- Positioning functions --
  482. -- Sets the cursor at the stored position and modifies the scroll values accordingly
  483. -- so everything will be drawn and positioned correctly.
  484. function updateCursorPos()
  485.     local xPos = pos.x - scroll.x -- This is to be the position that the cursor will be placed at on the x-axis for scrolling to be proper.
  486.     local yPos = pos.y - scroll.y -- This is to be the position that the cursor will be placed at on the y-axis for scrolling to be proper.
  487.    
  488.     local shouldRedrawLines = false
  489.     -- In the case that the cursor goes before 3, then we need to scroll the cursor back one so that we don't
  490.     -- overwrite the line number and or go off the screen.
  491.     if xPos < xPosOffset then
  492.         xPos = xPosOffset
  493.         scroll.x = pos.x - xPosOffset
  494.        
  495.         shouldRedrawLines = true
  496.     -- If the cursor attempts to go beyond the screen limit, then scroll the cursor over by 1.
  497.     elseif xPos > WIDTH then
  498.         xPos = WIDTH
  499.         scroll.x = pos.x - WIDTH
  500.        
  501.         shouldRedrawLines = true
  502.     end
  503.    
  504.     -- In the case that the cursor attempts to go above the screen limit, then scroll the screen up by 1.
  505.     if yPos < yPosOffset then
  506.         yPos = yPosOffset
  507.         scroll.y = scroll.y - 1
  508.        
  509.         shouldRedrawLines = true
  510.     -- If the cursor attempts to go below the screen limit, then scroll the screen down by 1.
  511.     elseif yPos > HEIGHT then
  512.         yPos = HEIGHT
  513.         scroll.y = scroll.y + 1
  514.        
  515.         shouldRedrawLines = true
  516.     end
  517.    
  518.     if shouldRedrawLines then
  519.         drawLines()
  520.     end
  521.    
  522.     -- Redraw the text and lines in the file; repsotion the cursor to its proper point.
  523.     drawAndSetCursor(xPos, yPos)
  524. end
  525. -- Positioning functions --
  526.  
  527. -- Input handling functions --
  528. -- Waits for an event to be thrown, then handles that event accordingly by adding text to the file,
  529. -- moving the cursor, and other various things.
  530. function handleInput()
  531.     -- Resets the cursor and its blink timer so that the user may see during this update.
  532.     local function resetCursor()
  533.         -- Make sure that the user can see the timer by
  534.         -- setting the blink flag to true and resetting the cursor blink timer.
  535.         shouldCursorBlink = true
  536.         cursorBlinkTimer = os.startTimer(CURSOR_BLINK_TIME)
  537.     end
  538.    
  539.     -- Grab an event and its parameters to be handled later.
  540.     local event, p1, p2, p3 = os.pullEvent()
  541.     local xPos = pos.x - (xPosOffset - 1)
  542.     local yPos = pos.y - (yPosOffset - 1)
  543.     local line = lines[yPos]
  544.    
  545.     -- In the case that the event thrown was a character event, add the chracter to the current line.
  546.     if event == "char" then
  547.         -- Set the line to a substring of itself from the cursor xPos minus one plus the character typed plus the rest of the line from the
  548.         -- position of the cursor onward.
  549.         lines[yPos] = line:sub(1, xPos - 1) .. p1 .. line:sub(xPos)
  550.        
  551.         -- Update the cursor by moving it one over to the right.
  552.         pos.x = pos.x + 1
  553.        
  554.         -- Since the event thrown was not the cursor blink timer, make sure that the user can see the timer by
  555.         -- setting the blink flag to true and resetting the cursor blink timer.
  556.         resetCursor()
  557.        
  558.         -- A change has been made to the state of the lines in the file, so set the proper flag to alert the user
  559.         -- that it needs to be saved to keep the changes made.
  560.         hasBeenSavedSinceLastChar = false
  561.         drawCurrentLine()
  562.     -- If the event was a key, then handle that key press appropriately.
  563.     elseif event == "key" then
  564.         -- If the key pressed was a backspace, then remove the character before the cursor in the current line.
  565.         if p1 == keys["backspace"] then
  566.             -- Make sure the length of the line is greater than 0 before trying to remove a character.
  567.             if pos.x > xPosOffset then
  568.                 lines[yPos] = line:sub(1, xPos - 2) .. line:sub(xPos)
  569.                 -- Update the cursor position by moving it one backwards.
  570.                 pos.x = pos.x - 1
  571.                 drawCurrentLine()
  572.             -- If the cursor is at the beginning of the line, then back up the cursor to the previous line (if possible) carrying the
  573.             -- current line with it.
  574.             elseif yPos > 1 then
  575.                 -- If the number of lines in the file increases by an exponent of 10, then move the offset of the x pos for the cursor up by 1.
  576.                 if #lines == math.pow(10, xPosOffset - 3) then
  577.                     xPosOffset = xPosOffset - 1
  578.                     pos.x      = pos.x - 1
  579.                 end
  580.                
  581.                 local previousLine = lines[yPos - 1]
  582.                 lines[yPos - 1]   = previousLine .. line
  583.                
  584.                 -- Remove the current line entry and move the cursor to the previous line.
  585.                 table.remove(lines, yPos)
  586.                
  587.                 pos.x = previousLine:len() + xPosOffset -- lines[pos.y - 1]:len() + xPosOffset
  588.                 pos.y = pos.y - 1
  589.                 drawLines()
  590.             end
  591.            
  592.             -- A change has been made to the state of the lines in the file, so set the proper flag to alert the user
  593.             -- that it needs to be saved to keep the changes made.
  594.             hasBeenSavedSinceLastChar = false
  595.         -- If the key pressed was the enter key, then create a new line.
  596.         elseif p1 == keys["return"] then
  597.             -- Get a substring of the line from the cursor onward on the current line, a substring of the
  598.             -- current line from the cursor backwards.
  599.             local lineFromCursor   = line:sub(xPos)
  600.             local lineBeforeCursor = line:sub(1, xPos - 1)
  601.             -- Compute the number of spaces before any text on the screen to determine the current line's indentation.
  602.             local _, numSpaces = line:find("^[ ]+")
  603.             if not numSpaces then
  604.                 numSpaces = 0
  605.             end
  606.            
  607.             -- Set the current line to the substring from the cursor backwards.
  608.             lines[yPos]     = lineBeforeCursor
  609.             -- Insert the line from the cursor onward into the line below all the while shifting the entire
  610.             -- file down by one line.
  611.             table.insert(lines, yPos + 1, string.rep(' ', numSpaces) .. lineFromCursor)
  612.            
  613.             -- If the number of lines in the file increases by an exponent of 10, then move the offset of the x pos for the cursor up by 1.
  614.             if #lines == math.pow(10, xPosOffset - 2) then
  615.                 xPosOffset = xPosOffset + 1
  616.             end
  617.            
  618.             -- Update the cursor by moving it down once.
  619.             pos.x = xPosOffset + numSpaces
  620.             pos.y = pos.y + 1
  621.            
  622.             -- A change has been made to the state of the lines in the file, so set the proper flag to alert the user
  623.             -- that it needs to be saved to keep the changes made.
  624.             hasBeenSavedSinceLastChar = false
  625.            
  626.             -- For some reason, the screen won't scroll after you press the enter key. Weird.
  627.             updateCursorPos()
  628.             drawLines()
  629.         -- If the key pressed was a right arrow, then move the cursor one to the right if it can.
  630.         elseif p1 == keys["right"] then
  631.             -- Make sure the cursor can move one to the right before actually moving the cursor.
  632.             if xPos < line:len() + 1 then
  633.                 pos.x = pos.x + 1
  634.                 drawCurrentLine()
  635.             -- If the current line isn't the last line the file and we're at the end of the line, then move the cursor
  636.             -- to the next line.
  637.             elseif yPos < #lines then
  638.                 pos.x = xPosOffset
  639.                 pos.y = pos.y + 1
  640.                 drawLines()
  641.             end
  642.         -- If the key pressed wa a left arrow, then move the cursor one to the left if it can.
  643.         elseif p1 == keys["left"] then
  644.             -- Make sure the cursor can move one to the left before actually moving it.
  645.             if xPos > 1 then
  646.                 pos.x = pos.x - 1
  647.                 drawCurrentLine()
  648.             -- If the current line isn't the first line in the file and we're at the beginning of the line, then move
  649.             -- the cursor to the previous line.
  650.             elseif yPos > 1 then
  651.                 pos.x = math.max(lines[yPos - 1]:len(), 1) + xPosOffset
  652.                 pos.y = pos.y - 1
  653.                 drawLines()
  654.             end
  655.         -- If the key pressed was the down key, then move the cursor one down if it can.
  656.         elseif p1 == keys["down"] and yPos < #lines then
  657.             pos.x = math.min(xPos + xPosOffset - 1, lines[yPos + 1]:len() + xPosOffset)
  658.             pos.y = pos.y + 1
  659.             drawLines()
  660.         -- If the key pressed was the up key, then move the cursor one up if it can.
  661.         elseif p1 == keys["up"] and yPos > 1 then
  662.             pos.x = math.min(xPos + xPosOffset - 1, lines[yPos - 1]:len() + xPosOffset)
  663.             pos.y = pos.y - 1
  664.             drawLines()
  665.         -- If the key pressed was the end key, then move the cursor to the end of the current line.
  666.         elseif p1 == keys["end"] then
  667.             pos.x    = line:len() + xPosOffset
  668.             drawCurrentLine()
  669.         -- If the key pressed was the home key, then move they cursor to the beginning of the current line.
  670.         elseif p1 == keys["home"] then
  671.             pos.x    = xPosOffset
  672.             drawCurrentLine()
  673.         -- If the key pressed was the delete key, then delete the character in front of the cursor if possible.
  674.         elseif p1 == keys["delete"] then
  675.             -- If the cursor isn't at the end of the line, then remove the character in front of the cursor.
  676.             if xPos < line:len() + 1 then
  677.                 lines[yPos] = line:sub(1, xPos - 1) .. line:sub(xPos + 1)
  678.                 drawCurrentLine()
  679.             -- If the cursor is at the end of the current line, then add the next line to the current one.
  680.             elseif yPos < #lines then
  681.                 -- If the number of lines in the file increases by an exponent of 10, then move the offset of the x pos for the cursor up by 1.
  682.                 if #lines == math.pow(10, xPosOffset - 3) then
  683.                     xPosOffset = xPosOffset - 1
  684.                     pos.x      = pos.x - 1
  685.                 end
  686.                
  687.                 lines[yPos] = line .. lines[yPos + 1]
  688.                 table.remove(lines, yPos + 1)
  689.                 drawLines()
  690.             end
  691.            
  692.             -- A change has been made to the state of the lines in the file, so set the proper flag to alert the user
  693.             -- that it needs to be saved to keep the changes made.
  694.             hasBeenSavedSinceLastChar = false
  695.         -- If the key pressed was the tab key, then insert the proper number of spaces into the current line.
  696.         elseif p1 == keys["tab"] then
  697.             lines[yPos] = line:sub(1, xPos - 1) .. string.rep(' ', TAB_SIZE) .. line:sub(xPos)
  698.             -- Update and reset the curs; redraw the current line.
  699.             hasBeenSavedSinceLastChar = false
  700.             pos.x = pos.x + TAB_SIZE
  701.             resetCursor()
  702.             drawCurrentLine()
  703.         -- If the user pressed either of the control keys, then open the file menu.
  704.         elseif p1 == 29 or p1 == 157 then
  705.             drawMainMenu()
  706.             resetCursor()
  707.             drawLines()
  708.         end
  709.        
  710.         -- Since the event thrown was not the cursor blink timer, make sure that the user can see the timer by
  711.         -- setting the blink flag to true and resetting the cursor blink timer.
  712.         resetCursor()
  713.     -- If the event thrown was a timer event and the timer was the cursor blink timer, set the flag for the cursor
  714.     -- to blink to true and reset the timer.
  715.     elseif event == "timer" and p1 == cursorBlinkTimer then
  716.         shouldCursorBlink = not shouldCursorBlink
  717.         cursorBlinkTimer = os.startTimer(CURSOR_BLINK_TIME)
  718.         drawCurrentLine()
  719.     -- If the event was a click on the menu option at the top of the screen, then open the menu.
  720.     elseif event == "mouse_click" and wasClickOnRange(p2, p3, WIDTH - ("[ File ]"):len(), 2, ("[ File ]"):len()) then
  721.         drawMainMenu()
  722.         resetCursor()
  723.         drawLines()
  724.     -- Handle mouse clicks.
  725.     elseif event == "mouse_click" then
  726.         -- If the event was a click on the 'X' at the top of the screen, then close the program.
  727.         if p2 == WIDTH and p3 == 1 then
  728.             running = false
  729.         -- Handle a click if it was on some line in the code so we may move the cursor to the place of the click.
  730.         elseif p2 >= xPosOffset and p3 >= yPosOffset then
  731.             pos.y = math.min(p3 + scroll.y, #lines + scroll.y + yPosOffset - 1)
  732.             pos.x = math.min(p2 + scroll.x, lines[pos.y - (yPosOffset - 1)]:len() + xPosOffset)
  733.            
  734.             drawLines()
  735.         end
  736.     -- Handle mouse scrolling.
  737.     elseif event == "mouse_scroll" then
  738.         -- If the direction is down, then scroll the screen down if it can be scrolled down.
  739.         if p1 == 1 and pos.y < #lines then
  740.             pos.y    = pos.y + 1
  741.             --scroll.y = scroll.y + 1
  742.             drawLines()
  743.         -- If the direction is up, then scroll the screen up if it can be scrolled up.
  744.         elseif p1 == -1 and pos.y > yPosOffset then
  745.             pos.y = pos.y - 1
  746.             drawLines()
  747.         end
  748.     end
  749. end
  750. -- Input handling functions --
  751.  
  752.  
  753.  
  754. -- Initialization --
  755. -- Set the colors for the editor.
  756. term.setBackgroundColor(BACKGROUND_COLOR)
  757. term.setTextColor(TEXT_COLOR)
  758.  
  759. -- Clear the screen before writing anything.
  760. term.clear()
  761. -- Load the file passed as an argument into the lines table.
  762. lines = loadLinesFromFile(path)
  763. drawLines()
  764. -- Initialization --
  765.  
  766.  
  767. -- Main entry point.
  768. while running do
  769.     updateCursorPos()
  770.     handleInput()
  771. end
  772.  
  773. -- Cleanup.
  774. term.setBackgroundColor(colors.black)
  775. term.setTextColor(colors.white)
  776.  
  777. term.clear()
  778. term.setCursorPos(1, 1)
Add Comment
Please, Sign In to add comment