diff --git a/core/buffers.py b/core/buffers.py index aee180a..19501de 100644 --- a/core/buffers.py +++ b/core/buffers.py @@ -1,3 +1,4 @@ +from core import cursors import os @@ -5,7 +6,23 @@ class Buffer: def __init__(self, path: str, name: str = None, data: list = None): self.path = path self.name = name or "[No Name]" - self.data = data or [[""]] + self.data = data or [""] + + def render(self, instance): + # Update the screen information + instance.update() + + for y, line in enumerate(self.data[instance.offset[0]:]): + if y < instance.safe_height: + for x, character in enumerate(line[instance.offset[1]:]): + if x < instance.safe_width: + instance.screen.addstr(y, x + instance.components.get_component_width( + instance.components.components["left"]), character) + + # Write blank spaces for the rest of the line + if instance.safe_width - len(line) > 0: + instance.screen.addstr(y, instance.components.get_component_width( + instance.components.components["left"]) + len(line), " " * (instance.safe_width - len(line))) @staticmethod def remove_char(string: str, index: int) -> str: @@ -31,7 +48,7 @@ def open_file(file_name): def load_file(file_path=None): # Default settings for a file file_name = "[No Name]" - file_data = [[""]] + file_data = [""] if file_path: # Set the file's name diff --git a/core/components.py b/core/components.py index e640b02..53e7551 100644 --- a/core/components.py +++ b/core/components.py @@ -22,9 +22,14 @@ class StatusBar: class Components: def __init__(self, components: dict = None): self.components = components or { + "left": [[" "], ["12222"], [""]], "bottom": [StatusBar], } + @staticmethod + def get_component_width(component: list) -> int: + return sum(len(max(sub_components)) for sub_components in component if max(sub_components)) + def render(self, instance): for component in self.components["bottom"]: component(instance).render(instance) diff --git a/core/cursors.py b/core/cursors.py index ac451de..59a75c2 100644 --- a/core/cursors.py +++ b/core/cursors.py @@ -1,50 +1,74 @@ import curses -def cursor_mode(mode: str): - if mode == "block": +def mode(to_mode: str): + if to_mode == "block": print("\033[2 q") - elif mode == "line": + elif to_mode == "line": print("\033[6 q") - elif mode == "hidden": + elif to_mode == "hidden": curses.curs_set(0) - elif mode == "visible": + elif to_mode == "visible": curses.curs_set(1) -def cursor_push(instance, direction: (int, str)): +def push(instance, direction: (int, str)): if direction in (0, "up", "north"): - # Decrease the y position - instance.cursor[0] -= 1 + # If the cursor is at the top of the file + if instance.cursor[0] == 0 and not instance.offset[0] == 0: + # Move the buffer up + instance.offset[0] -= 1 + + elif instance.cursor[0] != 0: + # Decrease the y position of the cursor + instance.cursor[0] -= 1 + + # Jump to the end of the line + if instance.cursor[1] > len(instance.buffer.data[instance.current_line - 1]) - 2: + instance.cursor[1] = len(instance.buffer.data[instance.current_line - 1]) - 2 elif direction in (1, "right", "east"): - # Increase the x position - instance.cursor[1] += 1 + # Increase the x position of the cursor + if instance.cursor[1] < len(instance.buffer.data[instance.current_line]) - 2: + instance.cursor[1] += 1 elif direction in (2, "down", "south"): - # Increase the y position - instance.cursor[0] += 1 + # Check if the cursor is at the bottom of the screen + if instance.cursor[0] == instance.safe_height - 2 and not instance.current_line == len(instance.buffer.data): + # Move the buffer down + instance.offset[0] += 1 + + elif instance.cursor[0] != instance.safe_height - 2: + # Increase the y position of the cursor + instance.cursor[0] += 1 + + # Jump to the end of the line + if instance.cursor[1] > len(instance.buffer.data[instance.current_line + 1]) - 2: + instance.cursor[1] = len(instance.buffer.data[instance.current_line + 1]) - 2 elif direction in (3, "left", "west"): - # Decrease the x position - instance.cursor[1] -= 1 + # Decrease the x position of the cursor + if instance.cursor[1] != 0: + instance.cursor[1] -= 1 -def check_cursor(instance, cursor: list): - cursor[1] = max(2, cursor[1]) - cursor[1] = min(instance.width - 1, cursor[1]) +def check(instance, cursor: list): + # Prevent any values out of bounds (especially important when resizing) + cursor[1] = max(0, cursor[1]) + cursor[1] = min(instance.safe_width - 1, cursor[1]) cursor[0] = max(0, cursor[0]) cursor[0] = min(instance.height - 2 - len(instance.components.components["bottom"]), cursor[0]) return cursor -def cursor_move(instance): +def move(instance): # Run a final check to see if the cursor is valid - instance.cursor = check_cursor(instance, instance.cursor) + instance.cursor = check(instance, instance.cursor) # Moves the cursor to anywhere on the screen - instance.screen.move(instance.cursor[0], instance.cursor[1]) + instance.screen.move(instance.cursor[0], instance.cursor[1] + + instance.components.get_component_width(instance.components.components["left"])) diff --git a/core/utils.py b/core/utils.py index f092229..6b620c6 100644 --- a/core/utils.py +++ b/core/utils.py @@ -1,7 +1,25 @@ from core.colors import Codes as c import traceback +from pathlib import Path import curses +import json import sys +import os + + +def load_json(file: str) -> dict: + # Load the json file with read permissions + with open(file, "r") as f: + return json.load(f) + + +def load_config() -> dict: + # Parse the path of the config file + config_file = f"{Path.home()}/.config/lambda/config.json" + + # Only if the config file exists, attempt to load it + if os.path.exists(config_file): + return load_json(config_file) def clear(instance, y: int, x: int): diff --git a/main.py b/main.py index e7d0af7..caca69c 100644 --- a/main.py +++ b/main.py @@ -8,16 +8,19 @@ import os class Lambda: - def __init__(self, buffer: Buffer): + def __init__(self, buffer: Buffer, config: dict = None): self.screen = curses.initscr() self.buffer = buffer self.cursor = [0, 0] + self.offset = [0, 0] + self.current_line = 0 self.mode = "normal" self.components = Components() - self.height = self.screen.getmaxyx()[0] - self.width = self.screen.getmaxyx()[1] - self.safe_height = self.height - len(self.components.components["bottom"]) - self.config = {"icon": "λ"} + self.height = 0 + self.width = 0 + self.safe_height = 0 + self.safe_width = 0 + self.config = config or {"icon": "λ"} def update_dimensions(self): # Calculate the entire height and width of the terminal @@ -25,16 +28,20 @@ class Lambda: # Calculate the safe area for the buffer by removing heights & widths of components self.safe_height = self.height - len(self.components.components["bottom"]) + self.safe_width = self.width - self.components.get_component_width(self.components.components["left"]) def update(self): - # Update the dimensions + # Update the dimensions of the terminal self.update_dimensions() + # Calculate the current line number + self.current_line = self.cursor[0] + self.offset[0] + # Refresh the on-screen components self.components.render(self) # Move the cursor - cursors.cursor_move(self) + cursors.move(self) def start(self): # Change the escape key delay to 25ms @@ -45,7 +52,7 @@ class Lambda: colors.init_colors() # Change the cursor shape - cursors.cursor_mode("block") + cursors.mode("block") # Don't echo any key-presses curses.noecho() @@ -61,9 +68,9 @@ class Lambda: # The main loop, which runs until the user quits while True: # Write the buffer to the screen - # buffers.write_buffer(screen, buffer) + self.buffer.render(self) - # Update the screen + # Update the screen variables self.update() # Wait for a keypress @@ -89,8 +96,11 @@ def main(): # Load the file into a Buffer object buffer = buffers.load_file(args.file) + # Load the config + config = utils.load_config() + # Load lambda with the buffer object - screen = Lambda(buffer) + screen = Lambda(buffer, config) # Start the screen, this will loop until exit try: diff --git a/mode/insert.py b/mode/insert.py index c84abc4..18f9f8f 100644 --- a/mode/insert.py +++ b/mode/insert.py @@ -10,4 +10,4 @@ def execute(instance, key): def activate(): # Switch the cursor to a line - cursors.cursor_mode("line") + cursors.mode("line") diff --git a/mode/normal.py b/mode/normal.py index fc35b80..00ed1e2 100644 --- a/mode/normal.py +++ b/mode/normal.py @@ -6,19 +6,19 @@ from mode import command def execute(instance, key): if key == ord("j"): # Move the cursor down - cursors.cursor_push(instance, "down") + cursors.push(instance, "down") elif key == ord("k"): # Move the cursor up - cursors.cursor_push(instance, "up") + cursors.push(instance, "up") elif key == ord("l"): # Move the cursor right - cursors.cursor_push(instance, "right") + cursors.push(instance, "right") elif key == ord("h"): # Move the cursor left - cursors.cursor_push(instance, "left") + cursors.push(instance, "left") elif key == ord("i"): # Activate insert mode @@ -26,7 +26,7 @@ def execute(instance, key): elif key == ord("I"): # Move the cursor to the right - cursors.cursor_push(instance, "right") + cursors.push(instance, "right") # Then activate insert mode modes.activate(instance, "insert") @@ -38,4 +38,4 @@ def execute(instance, key): def activate(): # Switch the cursor to a block - cursors.cursor_mode("block") + cursors.mode("block")