✨ Full buffer and cursor support
This commit is contained in:
parent
4721cee438
commit
612c706677
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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"]))
|
||||||
|
@ -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
32
main.py
@ -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:
|
||||||
|
@ -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")
|
||||||
|
@ -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")
|
||||||
|
Loading…
Reference in New Issue
Block a user