diff --git a/core/__pycache__/buffer.cpython-310.pyc b/core/__pycache__/buffer.cpython-310.pyc index 5e606aa..db3cfc6 100644 Binary files a/core/__pycache__/buffer.cpython-310.pyc and b/core/__pycache__/buffer.cpython-310.pyc differ diff --git a/core/__pycache__/cursor.cpython-310.pyc b/core/__pycache__/cursor.cpython-310.pyc index 751bcf6..d8c2dc1 100644 Binary files a/core/__pycache__/cursor.cpython-310.pyc and b/core/__pycache__/cursor.cpython-310.pyc differ diff --git a/core/__pycache__/files.cpython-310.pyc b/core/__pycache__/files.cpython-310.pyc index b7fd14a..0657b9d 100644 Binary files a/core/__pycache__/files.cpython-310.pyc and b/core/__pycache__/files.cpython-310.pyc differ diff --git a/core/__pycache__/mode.cpython-310.pyc b/core/__pycache__/mode.cpython-310.pyc index 27c7be4..3896735 100644 Binary files a/core/__pycache__/mode.cpython-310.pyc and b/core/__pycache__/mode.cpython-310.pyc differ diff --git a/core/__pycache__/statusbar.cpython-310.pyc b/core/__pycache__/statusbar.cpython-310.pyc index d9486d4..259e5ab 100644 Binary files a/core/__pycache__/statusbar.cpython-310.pyc and b/core/__pycache__/statusbar.cpython-310.pyc differ diff --git a/core/__pycache__/utils.cpython-310.pyc b/core/__pycache__/utils.cpython-310.pyc index 7a80db1..060765b 100644 Binary files a/core/__pycache__/utils.cpython-310.pyc and b/core/__pycache__/utils.cpython-310.pyc differ diff --git a/core/__pycache__/welcome.cpython-310.pyc b/core/__pycache__/welcome.cpython-310.pyc index 600f4df..019a5b9 100644 Binary files a/core/__pycache__/welcome.cpython-310.pyc and b/core/__pycache__/welcome.cpython-310.pyc differ diff --git a/core/buffer.py b/core/buffer.py index 8646d9c..20cc9c0 100644 --- a/core/buffer.py +++ b/core/buffer.py @@ -1,9 +1,9 @@ import curses -def write_buffer(stdscr, data): - count = 0 - for line in data["buffer_list"]: - str_line = "".join(line) - stdscr.addstr(count, len(data["info_bar"][0]), str_line + (" " * (len(line) - 1)), curses.color_pair(1)) - count += 1 +def write_buffer(screen, data): + for index, line in enumerate(data["buffer_list"][data["visible_y"]:]): + if index < data["height"] - 2: + str_line = "".join(line) + # [data["visible_x"]:1:data["width"] - data["visible_x"]] + screen.addstr(index, len(data["info_bar"][0]), str_line + (" " * (len(line) - 1)), curses.color_pair(1)) diff --git a/core/cursor.py b/core/cursor.py index 09dc967..e9cadeb 100644 --- a/core/cursor.py +++ b/core/cursor.py @@ -1,3 +1,6 @@ +from core import utils, buffer + + def cursor_mode(mode): if mode == "block": print("\033[2 q") @@ -13,9 +16,15 @@ def cursor_mode(mode): def check_cursor(data): - if data["cursor_y"] == len(data["buffer_list"]): + if data["cursor_y"] + data["visible_y"] == len(data["buffer_list"]): data["cursor_y"] -= 1 + if data["cursor_y"] == data["height"] - 2 and not data["cursor_y"] + data["visible_y"] == len(data["buffer_list"]): + data["visible_y"] += 1 + + elif data["cursor_y"] == 0 and data["buffer_list"][data["visible_y"]] != data["buffer_list"][0]: + data["visible_y"] -= 1 + elif data["cursor_x"] > len(data["buffer_list"][data["cursor_y"]]): data["cursor_x"] = len(data["buffer_list"][data["cursor_y"]]) @@ -27,9 +36,9 @@ def check_cursor(data): return data -def move(stdscr, data): +def move(screen, data): # Calculate a valid cursor position from data data = check_cursor(data) # Move the cursor - stdscr.move(data["cursor_y"], data["cursor_x"]) + screen.move(data["cursor_y"], data["cursor_x"]) diff --git a/core/files.py b/core/files.py index 599a755..d9b60f1 100644 --- a/core/files.py +++ b/core/files.py @@ -1,4 +1,4 @@ -def open_file(file): - with open(file) as f: +def open_file(file_name): + with open(file_name) as f: lines = f.readlines() - return lines + return lines diff --git a/core/mode.py b/core/mode.py index 918464a..55456c1 100644 --- a/core/mode.py +++ b/core/mode.py @@ -1,17 +1,17 @@ from modes import normal, insert, command -def activate(stdscr, data): +def activate(screen, data): if data["mode"] == "normal": data["mode_color"] = 6 - data = normal.activate(stdscr, data) + data = normal.activate(screen, data) elif data["mode"] == "insert": data["mode_color"] = 12 - data = insert.activate(stdscr, data) + data = insert.activate(screen, data) elif data["mode"] == "command": data["mode_color"] = 6 - data = command.activate(stdscr, data) + data = command.activate(screen, data) return data diff --git a/core/statusbar.py b/core/statusbar.py index 53e1da1..6c9ae46 100644 --- a/core/statusbar.py +++ b/core/statusbar.py @@ -23,21 +23,23 @@ def themes(data): return colors, icon, mode, file -def refresh(stdscr, data): +def refresh(screen, data): + # Calculate the theme colors, icon, mode, file = themes(data) + # Render icon - stdscr.addstr(data["height"] - 2, 0, icon, + screen.addstr(data["height"] - 2, 0, icon, curses.color_pair(colors[0]) | curses.A_BOLD) # Render mode - stdscr.addstr(data["height"] - 2, len(icon), mode, + screen.addstr(data["height"] - 2, len(icon), mode, curses.color_pair(colors[1]) | curses.A_BOLD) # Render file name - stdscr.addstr(data["height"] - 2, len(icon) + len(mode), file, + screen.addstr(data["height"] - 2, len(icon) + len(mode), file, curses.color_pair(colors[2]) | curses.A_BOLD) # Rest of the bar - stdscr.addstr(data["height"] - 2, len(icon) + len(mode) + len(file), + screen.addstr(data["height"] - 2, len(icon) + len(mode) + len(file), " " * (data["width"] - (len(icon) + len(mode) + len(file))), curses.color_pair(colors[3])) diff --git a/core/utils.py b/core/utils.py index 5470687..00367e1 100644 --- a/core/utils.py +++ b/core/utils.py @@ -2,36 +2,50 @@ from sys import exit import curses -def goodbye(stdscr, data): - # The prompt message - saved = "All changes are saved." - prompt = "Really quit? (y or n): " +def refresh_window_size(screen, data): + # Get the height and width of the screen + data["height"], data["width"] = screen.getmaxyx() - # Clear the bottom line - stdscr.addstr(data["height"] - 1, 0, " " * (data["width"] - 1), curses.color_pair(1)) + # Return the data as changes may have been made + return data + +def clear_line(screen, data, line): + # Clear the specified line + screen.addstr(line, 0, " " * (data["width"] - 1), curses.color_pair(1)) + + +def prompt(screen, data, text): # Print the prompt - stdscr.addstr(data["height"] - 1, 0, prompt, curses.color_pair(11)) + screen.addstr(data["height"] - 1, 0, text, curses.color_pair(11)) # Wait for and capture a key press from the user - key = stdscr.getch() + return screen.getch() + + +def goodbye(screen, data): + # Create a goodbye prompt + key = prompt(screen, data, "Really quit? (y or n): ") + + # Clear the bottom line + clear_line(screen, data, data["height"] - 1) if key == ord("y"): # Only exit if the key was "y", a confirmation exit() # Clear the bottom line again - stdscr.addstr(data["height"] - 1, 0, " " * (data["width"] - 1), curses.color_pair(1)) + clear_line(screen, data, data["height"] - 1) -def error(stdscr, data, error_msg): +def error(screen, data, error_msg): # Print the error message to the bottom line error_msg = f"ERROR: {error_msg}" - stdscr.addstr(data["height"] - 1, 0, error_msg, curses.color_pair(3)) - stdscr.addstr(data["height"] - 1, len(error_msg) + 1, "(press any key) ", curses.color_pair(1)) + screen.addstr(data["height"] - 1, 0, error_msg, curses.color_pair(3)) + screen.addstr(data["height"] - 1, len(error_msg) + 1, "(press any key) ", curses.color_pair(1)) # Wait for a key to be pressed - stdscr.getch() + screen.getch() # Clear the bottom line - stdscr.addstr(data["height"] - 1, 0, " " * (data["width"] - 1), curses.color_pair(1)) + clear_line(screen, data, data["height"] - 1) diff --git a/core/welcome.py b/core/welcome.py index e12ae98..3bc814f 100644 --- a/core/welcome.py +++ b/core/welcome.py @@ -1,9 +1,9 @@ import curses -def start_screen(stdscr): +def start_screen(screen): # Get window height and width - height, width = stdscr.getmaxyx() + height, width = screen.getmaxyx() # Startup text title = "λ Lambda" @@ -20,10 +20,10 @@ def start_screen(stdscr): start_y = int((height // 2) - 2) # Rendering title - stdscr.addstr(start_y, start_x_title, title, curses.color_pair(7) | curses.A_BOLD) + screen.addstr(start_y, start_x_title, title, curses.color_pair(7) | curses.A_BOLD) # Print the subtext for text in subtext: start_y += 1 start_x = int((width // 2) - (len(text) // 2) - len(text) % 2) - stdscr.addstr(start_y, start_x, text) + screen.addstr(start_y, start_x, text) diff --git a/lambda b/lambda index 6f03d04..8494e3f 100755 --- a/lambda +++ b/lambda @@ -1,3 +1,3 @@ #!/bin/sh -python3 ./lambda.py $@ +python3 ./lambda.py "$@" diff --git a/lambda.py b/lambda.py index 3e1f1ed..233e95c 100755 --- a/lambda.py +++ b/lambda.py @@ -4,7 +4,7 @@ import curses import argparse -def start(stdscr, buffer_name, buffer_list): +def start(screen, buffer_name, buffer_list): # Initialise data before starting data = { "cursor_y": 0, @@ -16,6 +16,8 @@ def start(stdscr, buffer_name, buffer_list): "info_bar": [" "], "buffer_name": buffer_name, "buffer_list": buffer_list, + "visible_y": 0, + "visible_x": 0, "statusbar_theme": "filled" } @@ -27,22 +29,28 @@ def start(stdscr, buffer_name, buffer_list): # Start the screen if data["buffer_name"] == "[No Name]": - welcome.start_screen(stdscr) + welcome.start_screen(screen) # Main loop while True: # Get the height and width of the screen - data["height"], data["width"] = stdscr.getmaxyx() + data["height"], data["width"] = screen.getmaxyx() # Write the buffer to the screen - buffer.write_buffer(stdscr, data) + buffer.write_buffer(screen, data) # Activate the next mode - data = mode.activate(stdscr, data) + data = mode.activate(screen, data) + + # Write the buffer to the screen + buffer.write_buffer(screen, data) # Refresh and clear the screen - stdscr.refresh() - stdscr.clear() + screen.refresh() + screen.clear() + + # Write the buffer to the screen + buffer.write_buffer(screen, data) def main(): diff --git a/modes/__pycache__/command.cpython-310.pyc b/modes/__pycache__/command.cpython-310.pyc index 474b6b8..365f3f3 100644 Binary files a/modes/__pycache__/command.cpython-310.pyc and b/modes/__pycache__/command.cpython-310.pyc differ diff --git a/modes/__pycache__/insert.cpython-310.pyc b/modes/__pycache__/insert.cpython-310.pyc index 80f41fa..8e3c4c6 100644 Binary files a/modes/__pycache__/insert.cpython-310.pyc and b/modes/__pycache__/insert.cpython-310.pyc differ diff --git a/modes/__pycache__/normal.cpython-310.pyc b/modes/__pycache__/normal.cpython-310.pyc index 2b0aad6..cc97c67 100644 Binary files a/modes/__pycache__/normal.cpython-310.pyc and b/modes/__pycache__/normal.cpython-310.pyc differ diff --git a/modes/command.py b/modes/command.py index 0dd0c51..a85f827 100644 --- a/modes/command.py +++ b/modes/command.py @@ -2,10 +2,10 @@ from core import statusbar, utils import curses -def execute(stdscr, data, commands): +def execute(screen, data, commands): if not commands: # Quit if there are no commands, don't check anything - return + return data for command in commands: if command == "w": @@ -14,9 +14,10 @@ def execute(stdscr, data, commands): elif command == "q": # Goodbye prompt - utils.goodbye(stdscr, data) + utils.goodbye(screen, data) elif command == "t": + # Theme switcher if data["statusbar_theme"] == "filled": data["statusbar_theme"] = "bare" @@ -24,29 +25,29 @@ def execute(stdscr, data, commands): data["statusbar_theme"] = "filled" else: - utils.error(stdscr, data, f"Not an editor command: '{command}'") + utils.error(screen, data, f"Not an editor command: '{command}'") return data -def activate(stdscr, data): +def activate(screen, data): # Initialise variables commands = [] # Visibly switch to command mode - statusbar.refresh(stdscr, data) - stdscr.addstr(data["height"]-1, 0, ":") - stdscr.move(data["height"]-1, 1) + statusbar.refresh(screen, data) + screen.addstr(data["height"]-1, 0, ":") + screen.move(data["height"]-1, 1) # Main loop while True: # Get a key inputted by the user - key = stdscr.getch() + key = screen.getch() # Handle subtracting a key (backspace) if key == curses.KEY_BACKSPACE: # Write whitespace over characters to refresh it - stdscr.addstr(data["height"]-1, 1, " " * len(commands)) + screen.addstr(data["height"]-1, 1, " " * len(commands)) if commands: # Subtract a character @@ -63,10 +64,10 @@ def activate(stdscr, data): elif key in (curses.KEY_ENTER, ord('\n'), ord('\r'), ord(":"), ord(";")): # Execute commands - data = execute(stdscr, data, commands) + data = execute(screen, data, commands) # Clear the bottom bar - stdscr.addstr(data["height"] - 1, 0, " " * (data["width"] - 1)) + screen.addstr(data["height"] - 1, 0, " " * (data["width"] - 1)) # Return to normal mode after executing a command data["mode"] = "normal" @@ -83,7 +84,7 @@ def activate(stdscr, data): friendly_command = "".join(commands) # Write the commands to the screen - stdscr.addstr(data["height"]-1, 1, friendly_command) + screen.addstr(data["height"]-1, 1, friendly_command) # Move the cursor the end of the commands - stdscr.move(data["height"]-1, len(commands)+1) + screen.move(data["height"]-1, len(commands)+1) diff --git a/modes/insert.py b/modes/insert.py index 41e2458..71f4252 100644 --- a/modes/insert.py +++ b/modes/insert.py @@ -5,22 +5,22 @@ def execute(data, key): return data -def activate(stdscr, data): +def activate(screen, data): # Refresh the status bar with a different colour for insert data["statusbar_colors"] = [8, 12, 14, 2] - statusbar.refresh(stdscr, data) + statusbar.refresh(screen, data) # Refresh the status bar - statusbar.refresh(stdscr, data) + statusbar.refresh(screen, data) # Move the cursor - cursor.move(stdscr, data) + cursor.move(screen, data) # Switch to a line cursor cursor.cursor_mode("line") # Wait for and capture a key press from the user - key = stdscr.getch() + key = screen.getch() # Exit insert mode if key == 27: diff --git a/modes/normal.py b/modes/normal.py index 0bfedbc..b727a59 100644 --- a/modes/normal.py +++ b/modes/normal.py @@ -1,4 +1,5 @@ from core import statusbar, cursor +import curses def execute(data, key): @@ -34,18 +35,18 @@ def execute(data, key): return data -def activate(stdscr, data): +def activate(screen, data): # Refresh the status bar - statusbar.refresh(stdscr, data) + statusbar.refresh(screen, data) # Move the cursor - cursor.move(stdscr, data) + cursor.move(screen, data) # Switch the cursor to a block cursor.cursor_mode("block") # Wait for and capture a key press from the user - key = stdscr.getch() + key = screen.getch() # Check against the keybindings data = execute(data, key)