Full buffer and cursor support

This commit is contained in:
Maddie H 2022-03-19 19:19:25 +00:00
parent 4721cee438
commit 612c706677
7 changed files with 114 additions and 40 deletions

View File

@ -1,3 +1,4 @@
from core import cursors
import os import os
@ -5,7 +6,23 @@ class Buffer:
def __init__(self, path: str, name: str = None, data: list = None): def __init__(self, path: str, name: str = None, data: list = None):
self.path = path self.path = path
self.name = name or "[No Name]" 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 @staticmethod
def remove_char(string: str, index: int) -> str: def remove_char(string: str, index: int) -> str:
@ -31,7 +48,7 @@ def open_file(file_name):
def load_file(file_path=None): def load_file(file_path=None):
# Default settings for a file # Default settings for a file
file_name = "[No Name]" file_name = "[No Name]"
file_data = [[""]] file_data = [""]
if file_path: if file_path:
# Set the file's name # Set the file's name

View File

@ -22,9 +22,14 @@ class StatusBar:
class Components: class Components:
def __init__(self, components: dict = None): def __init__(self, components: dict = None):
self.components = components or { self.components = components or {
"left": [[" "], ["12222"], [""]],
"bottom": [StatusBar], "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): def render(self, instance):
for component in self.components["bottom"]: for component in self.components["bottom"]:
component(instance).render(instance) component(instance).render(instance)

View File

@ -1,50 +1,74 @@
import curses import curses
def cursor_mode(mode: str): def mode(to_mode: str):
if mode == "block": if to_mode == "block":
print("\033[2 q") print("\033[2 q")
elif mode == "line": elif to_mode == "line":
print("\033[6 q") print("\033[6 q")
elif mode == "hidden": elif to_mode == "hidden":
curses.curs_set(0) curses.curs_set(0)
elif mode == "visible": elif to_mode == "visible":
curses.curs_set(1) curses.curs_set(1)
def cursor_push(instance, direction: (int, str)): def push(instance, direction: (int, str)):
if direction in (0, "up", "north"): if direction in (0, "up", "north"):
# Decrease the y position # If the cursor is at the top of the file
instance.cursor[0] -= 1 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"): elif direction in (1, "right", "east"):
# Increase the x position # Increase the x position of the cursor
instance.cursor[1] += 1 if instance.cursor[1] < len(instance.buffer.data[instance.current_line]) - 2:
instance.cursor[1] += 1
elif direction in (2, "down", "south"): elif direction in (2, "down", "south"):
# Increase the y position # Check if the cursor is at the bottom of the screen
instance.cursor[0] += 1 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"): elif direction in (3, "left", "west"):
# Decrease the x position # Decrease the x position of the cursor
instance.cursor[1] -= 1 if instance.cursor[1] != 0:
instance.cursor[1] -= 1
def check_cursor(instance, cursor: list): def check(instance, cursor: list):
cursor[1] = max(2, cursor[1]) # Prevent any values out of bounds (especially important when resizing)
cursor[1] = min(instance.width - 1, cursor[1]) cursor[1] = max(0, cursor[1])
cursor[1] = min(instance.safe_width - 1, cursor[1])
cursor[0] = max(0, cursor[0]) cursor[0] = max(0, cursor[0])
cursor[0] = min(instance.height - 2 - len(instance.components.components["bottom"]), cursor[0]) cursor[0] = min(instance.height - 2 - len(instance.components.components["bottom"]), cursor[0])
return cursor return cursor
def cursor_move(instance): def move(instance):
# Run a final check to see if the cursor is valid # 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 # 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"]))

View File

@ -1,7 +1,25 @@
from core.colors import Codes as c from core.colors import Codes as c
import traceback import traceback
from pathlib import Path
import curses import curses
import json
import sys 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): def clear(instance, y: int, x: int):

32
main.py
View File

@ -8,16 +8,19 @@ import os
class Lambda: class Lambda:
def __init__(self, buffer: Buffer): def __init__(self, buffer: Buffer, config: dict = None):
self.screen = curses.initscr() self.screen = curses.initscr()
self.buffer = buffer self.buffer = buffer
self.cursor = [0, 0] self.cursor = [0, 0]
self.offset = [0, 0]
self.current_line = 0
self.mode = "normal" self.mode = "normal"
self.components = Components() self.components = Components()
self.height = self.screen.getmaxyx()[0] self.height = 0
self.width = self.screen.getmaxyx()[1] self.width = 0
self.safe_height = self.height - len(self.components.components["bottom"]) self.safe_height = 0
self.config = {"icon": "λ"} self.safe_width = 0
self.config = config or {"icon": "λ"}
def update_dimensions(self): def update_dimensions(self):
# Calculate the entire height and width of the terminal # 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 # 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_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): def update(self):
# Update the dimensions # Update the dimensions of the terminal
self.update_dimensions() self.update_dimensions()
# Calculate the current line number
self.current_line = self.cursor[0] + self.offset[0]
# Refresh the on-screen components # Refresh the on-screen components
self.components.render(self) self.components.render(self)
# Move the cursor # Move the cursor
cursors.cursor_move(self) cursors.move(self)
def start(self): def start(self):
# Change the escape key delay to 25ms # Change the escape key delay to 25ms
@ -45,7 +52,7 @@ class Lambda:
colors.init_colors() colors.init_colors()
# Change the cursor shape # Change the cursor shape
cursors.cursor_mode("block") cursors.mode("block")
# Don't echo any key-presses # Don't echo any key-presses
curses.noecho() curses.noecho()
@ -61,9 +68,9 @@ class Lambda:
# The main loop, which runs until the user quits # The main loop, which runs until the user quits
while True: while True:
# Write the buffer to the screen # Write the buffer to the screen
# buffers.write_buffer(screen, buffer) self.buffer.render(self)
# Update the screen # Update the screen variables
self.update() self.update()
# Wait for a keypress # Wait for a keypress
@ -89,8 +96,11 @@ def main():
# Load the file into a Buffer object # Load the file into a Buffer object
buffer = buffers.load_file(args.file) buffer = buffers.load_file(args.file)
# Load the config
config = utils.load_config()
# Load lambda with the buffer object # Load lambda with the buffer object
screen = Lambda(buffer) screen = Lambda(buffer, config)
# Start the screen, this will loop until exit # Start the screen, this will loop until exit
try: try:

View File

@ -10,4 +10,4 @@ def execute(instance, key):
def activate(): def activate():
# Switch the cursor to a line # Switch the cursor to a line
cursors.cursor_mode("line") cursors.mode("line")

View File

@ -6,19 +6,19 @@ from mode import command
def execute(instance, key): def execute(instance, key):
if key == ord("j"): if key == ord("j"):
# Move the cursor down # Move the cursor down
cursors.cursor_push(instance, "down") cursors.push(instance, "down")
elif key == ord("k"): elif key == ord("k"):
# Move the cursor up # Move the cursor up
cursors.cursor_push(instance, "up") cursors.push(instance, "up")
elif key == ord("l"): elif key == ord("l"):
# Move the cursor right # Move the cursor right
cursors.cursor_push(instance, "right") cursors.push(instance, "right")
elif key == ord("h"): elif key == ord("h"):
# Move the cursor left # Move the cursor left
cursors.cursor_push(instance, "left") cursors.push(instance, "left")
elif key == ord("i"): elif key == ord("i"):
# Activate insert mode # Activate insert mode
@ -26,7 +26,7 @@ def execute(instance, key):
elif key == ord("I"): elif key == ord("I"):
# Move the cursor to the right # Move the cursor to the right
cursors.cursor_push(instance, "right") cursors.push(instance, "right")
# Then activate insert mode # Then activate insert mode
modes.activate(instance, "insert") modes.activate(instance, "insert")
@ -38,4 +38,4 @@ def execute(instance, key):
def activate(): def activate():
# Switch the cursor to a block # Switch the cursor to a block
cursors.cursor_mode("block") cursors.mode("block")