Advertisement
PaymentOption

NoteTaker

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