· 5 years ago · Nov 08, 2020, 05:38 PM
1tArgs = { ... }
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.
13if not term.isColor() then
14 print("Color must be supported.")
15 return
16end
17
18-- Make sure that we have the proper arguments to continue.
19if #tArgs < 1 then
20 print("Usage: " .. fs.getName(shell.getRunningProgram()) .. " <file_path>")
21 return
22elseif fs.isDir(tArgs[1]) then
23 print("Cannot edit a directory.")
24 return
25elseif fs.isReadOnly(tArgs[1]) then
26 print("Cannot edit a read only file.")
27 return
28end
29
30-- Variables --
31local running = true -- Whether or not the program should be running.
32
33local SHOULD_HIGHLIGHT_SYNTAX = true -- Whether or not the program should highlight syntax.
34local TAB_SIZE = 3 -- The number of spaces inserted into the current line when the tab key is pressed.
35
36local BACKGROUND_COLOR = colors.black
37local CURRENT_LINE_COLOR = colors.gray
38local TEXT_COLOR = colors.white
39
40local LINE_NUMBER_COLOR = colors.white
41local LINE_NUMBER_BACKGROUND_COLOR = colors.gray
42
43local CURSOR_BLINK_TIME = 0.3
44local CURSOR_COLOR = colors.blue
45local CURSOR_TEXT = colors.white
46local shouldCursorBlink = false
47local cursorBlinkTimer = os.startTimer(CURSOR_BLINK_TIME)
48
49local path = tArgs[1] -- The path for the program to load files from or to create a new file at.
50local lines = {} -- All of the lines that are contained within the current file.
51local hasBeenSavedSinceLastChar = true -- Whether or not the file has been saved since the last character modification was made.
52
53local xPosOffset = 3
54local yPosOffset = 3
55
56local WIDTH, HEIGHT = term.getSize() -- The width and height dimensions of the terminal we're working on.
57local scroll = {x = 0, y = 0} -- The amount of characters/lines that are to be scrolled when editing.
58local pos = {x = 3, y = 3} -- The position of the cursor in the file. Scrolling will be handled in another function.
59
60local COLORS = {
61 COMMENT_COLOR = colors.lightGray,
62 STRING_COLOR = colors.red,
63 KEYWORD_COLOR = colors.orange
64}
65
66local 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.
96function 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
141end
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.
146function 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()
162end
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.
168function 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)
182end
183
184-- Code shamelessly stolen from dan200's edit program. ************
185local 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
198end
199
200local 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
216end
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.
221function 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)
240end
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.
246function 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
284end
285
286-- Draws the current line number.
287function 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())))
298end
299
300-- Draws the current line.
301function 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)
318end
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.
323function 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)
356end
357
358-- Draws the cursor using the constant 'CURSOR_COLOR'.
359function 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)
374end
375-- Drawing functions --
376
377-- Menu functions --
378-- Draws the main menu that contains the save, exit, and color config menu.
379function 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, yPos)
402 term.setBackgroundColor(colors.red)
403 term.write(string.char(7))
404
405 term.setBackgroundColor(colors.lightGray)
406 for line = yPos + 1, yPos + #options + 1 do
407 term.setCursorPos(xPos, line)
408 term.write(string.rep(' ', width))
409 end
410 term.setBackgroundColor(colors.gray)
411
412 term.setCursorPos(xPos + 3, yPos + #options)
413 term.write("[ " .. options[1] .. " ]")
414
415 term.setBackgroundColor(colors.lightGray)
416 term.write(" ")
417 term.setBackgroundColor(colors.gray)
418 term.write("[ " .. options[2] .. " ]")
419 end
420
421 -- Handles clicks an other interaction with the menu.
422 -- After any click, the function will return.
423 local function handleClicks(xPos, yPos)
424 while true do
425 local event, p1, p2, p3 = os.pullEvent()
426
427 -- Handle clicks.
428 if event == "mouse_click" then
429 -- If the click was on the save option, then save the current file.
430 if wasClickOnRange(p2, p3, xPos + 3, math.floor(yPos + #options), ("[ " .. options[1] .. " ]"):len()) then
431 saveLinesToFile(path)
432 hasBeenSavedSinceLastChar = true
433 return
434 -- If the click was on the exit option, then exit the program.
435 elseif wasClickOnRange(p2, p3, xPos + 3 + ("[ " .. options[1] .. " ]"):len(), math.floor(yPos + #options), ("[ " .. options[2] .. " ]"):len()) then
436 running = false
437 return
438 -- 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.
439 -- Close the dialog box if the user pressed either of the control keys.
440 elseif wasClickOnRange(p2, p3, xPos + width - 2, math.floor(yPos), 1) then
441 return
442 end
443 -- Handle key presses.
444 elseif event == "key" then
445 -- If the key pressed was one of the control keys, then exit the dialog box.
446 if p1 == 29 or p1 == 157 then
447 return
448 -- If the key pressed was 's' then save the file and close the dialog box.
449 elseif p1 == keys['s'] then
450 -- Catch 's' character event thrown.
451 os.pullEvent()
452 saveLinesToFile(path)
453 hasBeenSavedSinceLastChar = true
454 return
455 -- If the key pressed was 'e' then exit the program.
456 elseif p1 == keys['e'] then
457 -- Catch 'e' character event thrown.
458 os.pullEvent()
459
460 -- Cleanup.
461 running = false
462 return
463 end
464 end
465 end
466 end
467
468 draw()
469 handleClicks(xPos, yPos)
470end
471-- Menu functions --
472
473-- Click functions --
474-- Checks whether or not the given click was on the given range.
475function wasClickOnRange(xClickPos, yClickPos, xPos, yPos, width)
476 return xClickPos >= xPos and xClickPos <= xPos + width and yClickPos == yPos
477end
478-- Click functions --
479
480-- Positioning functions --
481-- Sets the cursor at the stored position and modifies the scroll values accordingly
482-- so everything will be drawn and positioned correctly.
483function updateCursorPos()
484 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.
485 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.
486
487 local shouldRedrawLines = false
488 -- In the case that the cursor goes before 3, then we need to scroll the cursor back one so that we don't
489 -- overwrite the line number and or go off the screen.
490 if xPos < xPosOffset then
491 xPos = xPosOffset
492 scroll.x = pos.x - xPosOffset
493
494 shouldRedrawLines = true
495 -- If the cursor attempts to go beyond the screen limit, then scroll the cursor over by 1.
496 elseif xPos > WIDTH then
497 xPos = WIDTH
498 scroll.x = pos.x - WIDTH
499
500 shouldRedrawLines = true
501 end
502
503 -- In the case that the cursor attempts to go above the screen limit, then scroll the screen up by 1.
504 if yPos < yPosOffset then
505 yPos = yPosOffset
506 scroll.y = scroll.y - 1
507
508 shouldRedrawLines = true
509 -- If the cursor attempts to go below the screen limit, then scroll the screen down by 1.
510 elseif yPos > HEIGHT then
511 yPos = HEIGHT
512 scroll.y = scroll.y + 1
513
514 shouldRedrawLines = true
515 end
516
517 if shouldRedrawLines then
518 drawLines()
519 end
520
521 -- Redraw the text and lines in the file; repsotion the cursor to its proper point.
522 drawAndSetCursor(xPos, yPos)
523end
524-- Positioning functions --
525
526-- Input handling functions --
527-- Waits for an event to be thrown, then handles that event accordingly by adding text to the file,
528-- moving the cursor, and other various things.
529function handleInput()
530 -- Resets the cursor and its blink timer so that the user may see during this update.
531 local function resetCursor()
532 -- Make sure that the user can see the timer by
533 -- setting the blink flag to true and resetting the cursor blink timer.
534 shouldCursorBlink = true
535 cursorBlinkTimer = os.startTimer(CURSOR_BLINK_TIME)
536 end
537
538 -- Grab an event and its parameters to be handled later.
539 local event, p1, p2, p3 = os.pullEvent()
540 local xPos = pos.x - (xPosOffset - 1)
541 local yPos = pos.y - (yPosOffset - 1)
542 local line = lines[yPos]
543
544 -- In the case that the event thrown was a character event, add the chracter to the current line.
545 if event == "char" then
546 -- 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
547 -- position of the cursor onward.
548 lines[yPos] = line:sub(1, xPos - 1) .. p1 .. line:sub(xPos)
549
550 -- Update the cursor by moving it one over to the right.
551 pos.x = pos.x + 1
552
553 -- Since the event thrown was not the cursor blink timer, make sure that the user can see the timer by
554 -- setting the blink flag to true and resetting the cursor blink timer.
555 resetCursor()
556
557 -- A change has been made to the state of the lines in the file, so set the proper flag to alert the user
558 -- that it needs to be saved to keep the changes made.
559 hasBeenSavedSinceLastChar = false
560 drawCurrentLine()
561 -- If the event was a key, then handle that key press appropriately.
562 elseif event == "key" then
563 -- If the key pressed was a backspace, then remove the character before the cursor in the current line.
564 if p1 == keys["backspace"] then
565 -- Make sure the length of the line is greater than 0 before trying to remove a character.
566 if pos.x > xPosOffset then
567 lines[yPos] = line:sub(1, xPos - 2) .. line:sub(xPos)
568 -- Update the cursor position by moving it one backwards.
569 pos.x = pos.x - 1
570 drawCurrentLine()
571 -- If the cursor is at the beginning of the line, then back up the cursor to the previous line (if possible) carrying the
572 -- current line with it.
573 elseif yPos > 1 then
574 -- 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.
575 if #lines == math.pow(10, xPosOffset - 3) then
576 xPosOffset = xPosOffset - 1
577 pos.x = pos.x - 1
578 end
579
580 local previousLine = lines[yPos - 1]
581 lines[yPos - 1] = previousLine .. line
582
583 -- Remove the current line entry and move the cursor to the previous line.
584 table.remove(lines, yPos)
585
586 pos.x = previousLine:len() + xPosOffset -- lines[pos.y - 1]:len() + xPosOffset
587 pos.y = pos.y - 1
588 drawLines()
589 end
590
591 -- A change has been made to the state of the lines in the file, so set the proper flag to alert the user
592 -- that it needs to be saved to keep the changes made.
593 hasBeenSavedSinceLastChar = false
594 -- If the key pressed was the enter key, then create a new line.
595 elseif p1 == keys["return"] then
596 -- Get a substring of the line from the cursor onward on the current line, a substring of the
597 -- current line from the cursor backwards.
598 local lineFromCursor = line:sub(xPos)
599 local lineBeforeCursor = line:sub(1, xPos - 1)
600 -- Compute the number of spaces before any text on the screen to determine the current line's indentation.
601 local _, numSpaces = line:find("^[ ]+")
602 if not numSpaces then
603 numSpaces = 0
604 end
605
606 -- Set the current line to the substring from the cursor backwards.
607 lines[yPos] = lineBeforeCursor
608 -- Insert the line from the cursor onward into the line below all the while shifting the entire
609 -- file down by one line.
610 table.insert(lines, yPos + 1, string.rep(' ', numSpaces) .. lineFromCursor)
611
612 -- 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.
613 if #lines == math.pow(10, xPosOffset - 2) then
614 xPosOffset = xPosOffset + 1
615 end
616
617 -- Update the cursor by moving it down once.
618 pos.x = xPosOffset + numSpaces
619 pos.y = pos.y + 1
620
621 -- A change has been made to the state of the lines in the file, so set the proper flag to alert the user
622 -- that it needs to be saved to keep the changes made.
623 hasBeenSavedSinceLastChar = false
624
625 -- For some reason, the screen won't scroll after you press the enter key. Weird.
626 updateCursorPos()
627 drawLines()
628 -- If the key pressed was a right arrow, then move the cursor one to the right if it can.
629 elseif p1 == keys["right"] then
630 -- Make sure the cursor can move one to the right before actually moving the cursor.
631 if xPos < line:len() + 1 then
632 pos.x = pos.x + 1
633 drawCurrentLine()
634 -- If the current line isn't the last line the file and we're at the end of the line, then move the cursor
635 -- to the next line.
636 elseif yPos < #lines then
637 pos.x = xPosOffset
638 pos.y = pos.y + 1
639 drawLines()
640 end
641 -- If the key pressed wa a left arrow, then move the cursor one to the left if it can.
642 elseif p1 == keys["left"] then
643 -- Make sure the cursor can move one to the left before actually moving it.
644 if xPos > 1 then
645 pos.x = pos.x - 1
646 drawCurrentLine()
647 -- If the current line isn't the first line in the file and we're at the beginning of the line, then move
648 -- the cursor to the previous line.
649 elseif yPos > 1 then
650 pos.x = math.max(lines[yPos - 1]:len(), 1) + xPosOffset
651 pos.y = pos.y - 1
652 drawLines()
653 end
654 -- If the key pressed was the down key, then move the cursor one down if it can.
655 elseif p1 == keys["down"] and yPos < #lines 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 up key, then move the cursor one up if it can.
660 elseif p1 == keys["up"] and yPos > 1 then
661 pos.x = math.min(xPos + xPosOffset - 1, lines[yPos - 1]:len() + xPosOffset)
662 pos.y = pos.y - 1
663 drawLines()
664 -- If the key pressed was the end key, then move the cursor to the end of the current line.
665 elseif p1 == keys["end"] then
666 pos.x = line:len() + xPosOffset
667 drawCurrentLine()
668 -- If the key pressed was the home key, then move they cursor to the beginning of the current line.
669 elseif p1 == keys["home"] then
670 pos.x = xPosOffset
671 drawCurrentLine()
672 -- If the key pressed was the delete key, then delete the character in front of the cursor if possible.
673 elseif p1 == keys["delete"] then
674 -- If the cursor isn't at the end of the line, then remove the character in front of the cursor.
675 if xPos < line:len() + 1 then
676 lines[yPos] = line:sub(1, xPos - 1) .. line:sub(xPos + 1)
677 drawCurrentLine()
678 -- If the cursor is at the end of the current line, then add the next line to the current one.
679 elseif yPos < #lines then
680 -- 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.
681 if #lines == math.pow(10, xPosOffset - 3) then
682 xPosOffset = xPosOffset - 1
683 pos.x = pos.x - 1
684 end
685
686 lines[yPos] = line .. lines[yPos + 1]
687 table.remove(lines, yPos + 1)
688 drawLines()
689 end
690
691 -- A change has been made to the state of the lines in the file, so set the proper flag to alert the user
692 -- that it needs to be saved to keep the changes made.
693 hasBeenSavedSinceLastChar = false
694 -- If the key pressed was the tab key, then insert the proper number of spaces into the current line.
695 elseif p1 == keys["tab"] then
696 lines[yPos] = line:sub(1, xPos - 1) .. string.rep(' ', TAB_SIZE) .. line:sub(xPos)
697 -- Update and reset the curs; redraw the current line.
698 hasBeenSavedSinceLastChar = false
699 pos.x = pos.x + TAB_SIZE
700 resetCursor()
701 drawCurrentLine()
702 -- If the user pressed either of the control keys, then open the file menu.
703 elseif p1 == 29 or p1 == 157 then
704 drawMainMenu()
705 resetCursor()
706 drawLines()
707 end
708
709 -- Since the event thrown was not the cursor blink timer, make sure that the user can see the timer by
710 -- setting the blink flag to true and resetting the cursor blink timer.
711 resetCursor()
712 -- If the event thrown was a timer event and the timer was the cursor blink timer, set the flag for the cursor
713 -- to blink to true and reset the timer.
714 elseif event == "timer" and p1 == cursorBlinkTimer then
715 shouldCursorBlink = not shouldCursorBlink
716 cursorBlinkTimer = os.startTimer(CURSOR_BLINK_TIME)
717 drawCurrentLine()
718 -- If the event was a click on the menu option at the top of the screen, then open the menu.
719 elseif event == "mouse_click" and wasClickOnRange(p2, p3, WIDTH - ("[ File ]"):len(), 2, ("[ File ]"):len()) then
720 drawMainMenu()
721 resetCursor()
722 drawLines()
723 -- Handle mouse clicks.
724 elseif event == "mouse_click" then
725 -- If the event was a click on the 'X' at the top of the screen, then close the program.
726 if p2 == WIDTH and p3 == 1 then
727 running = false
728 -- Handle a click if it was on some line in the code so we may move the cursor to the place of the click.
729 elseif p2 >= xPosOffset and p3 >= yPosOffset then
730 pos.y = math.min(p3 + scroll.y, #lines + scroll.y + yPosOffset - 1)
731 pos.x = math.min(p2 + scroll.x, lines[pos.y - (yPosOffset - 1)]:len() + xPosOffset)
732
733 drawLines()
734 end
735 -- Handle mouse scrolling.
736 elseif event == "mouse_scroll" then
737 -- If the direction is down, then scroll the screen down if it can be scrolled down.
738 if p1 == 1 and pos.y < #lines then
739 pos.y = pos.y + 1
740 --scroll.y = scroll.y + 1
741 drawLines()
742 -- If the direction is up, then scroll the screen up if it can be scrolled up.
743 elseif p1 == -1 and pos.y > yPosOffset then
744 pos.y = pos.y - 1
745 drawLines()
746 end
747 end
748end
749-- Input handling functions --
750
751
752
753-- Initialization --
754-- Set the colors for the editor.
755term.setBackgroundColor(BACKGROUND_COLOR)
756term.setTextColor(TEXT_COLOR)
757
758-- Clear the screen before writing anything.
759term.clear()
760-- Load the file passed as an argument into the lines table.
761lines = loadLinesFromFile(path)
762drawLines()
763-- Initialization --
764
765
766-- Main entry point.
767while running do
768 updateCursorPos()
769 handleInput()
770end
771
772-- Cleanup.
773term.setBackgroundColor(colors.black)
774term.setTextColor(colors.white)
775
776term.clear()
777term.setCursorPos(1, 1)