🤯 Complete reworking of cursor system & fixes!
This commit is contained in:
parent
612c706677
commit
0d06c65a13
@ -1,4 +1,3 @@
|
|||||||
from core import cursors
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
@ -9,13 +8,10 @@ class Buffer:
|
|||||||
self.data = data or [""]
|
self.data = data or [""]
|
||||||
|
|
||||||
def render(self, instance):
|
def render(self, instance):
|
||||||
# Update the screen information
|
|
||||||
instance.update()
|
|
||||||
|
|
||||||
for y, line in enumerate(self.data[instance.offset[0]:]):
|
for y, line in enumerate(self.data[instance.offset[0]:]):
|
||||||
if y < instance.safe_height:
|
if y <= instance.safe_height:
|
||||||
for x, character in enumerate(line[instance.offset[1]:]):
|
for x, character in enumerate(line[instance.offset[1]:]):
|
||||||
if x < instance.safe_width:
|
if x <= instance.safe_width:
|
||||||
instance.screen.addstr(y, x + instance.components.get_component_width(
|
instance.screen.addstr(y, x + instance.components.get_component_width(
|
||||||
instance.components.components["left"]), character)
|
instance.components.components["left"]), character)
|
||||||
|
|
||||||
@ -41,6 +37,10 @@ def open_file(file_name):
|
|||||||
# Convert it into a list of lines
|
# Convert it into a list of lines
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
|
|
||||||
|
# Add a line if the file is empty or if the last line is not empty
|
||||||
|
if lines[-1].endswith("\n") or not len(lines):
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
# Return the list of lines
|
# Return the list of lines
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ 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"], [""]],
|
"left": [" "],
|
||||||
"bottom": [StatusBar],
|
"bottom": [StatusBar],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from core import utils
|
||||||
import curses
|
import curses
|
||||||
|
|
||||||
|
|
||||||
@ -15,60 +16,54 @@ def mode(to_mode: str):
|
|||||||
curses.curs_set(1)
|
curses.curs_set(1)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO
|
||||||
def push(instance, direction: (int, str)):
|
def push(instance, direction: (int, str)):
|
||||||
if direction in (0, "up", "north"):
|
if direction in (0, "up", "north"):
|
||||||
# If the cursor is at the top of the file
|
# If the cursor isn't at the top of the screen
|
||||||
if instance.cursor[0] == 0 and not instance.offset[0] == 0:
|
if instance.raw_cursor[0] > 0:
|
||||||
# Move the buffer up
|
# Move the cursor up
|
||||||
|
instance.raw_cursor[0] -= 1
|
||||||
|
|
||||||
|
# Move the buffer upwards if the cursor is at the top of the screen and not at the top of the buffer
|
||||||
|
if instance.raw_cursor[0] == 0 and instance.cursor[0] == instance.offset[0] and instance.cursor[0] != 0:
|
||||||
instance.offset[0] -= 1
|
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 of the cursor
|
instance.raw_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"):
|
||||||
# Check if the cursor is at the bottom of the screen
|
if instance.raw_cursor[0] == instance.safe_height and instance.cursor[0] != len(instance.buffer.data) - 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
|
instance.offset[0] += 1
|
||||||
|
|
||||||
elif instance.cursor[0] != instance.safe_height - 2:
|
# If the cursor isn't at the bottom of the screen
|
||||||
# Increase the y position of the cursor
|
elif instance.raw_cursor[0] != instance.safe_height and instance.cursor[0] != len(instance.buffer.data) - 1:
|
||||||
instance.cursor[0] += 1
|
# Move the cursor down
|
||||||
|
instance.raw_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 of the cursor
|
if instance.raw_cursor[1] > 0:
|
||||||
if instance.cursor[1] != 0:
|
instance.raw_cursor[1] -= 1
|
||||||
instance.cursor[1] -= 1
|
|
||||||
|
|
||||||
|
|
||||||
def check(instance, cursor: list):
|
def check(instance, cursor: list) -> list:
|
||||||
# Prevent any values out of bounds (especially important when resizing)
|
# Prevent the cursor from going outside the buffer
|
||||||
cursor[1] = max(0, cursor[1])
|
cursor[1] = min(len(instance.buffer.data[instance.cursor[0]]) - 2, cursor[1])
|
||||||
cursor[1] = min(instance.safe_width - 1, cursor[1])
|
|
||||||
|
# Prevent any negative values
|
||||||
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[1] = max(0, cursor[1])
|
||||||
|
|
||||||
|
# Prevent the cursor from going outside the screen
|
||||||
|
cursor[1] = min(instance.safe_width, cursor[1])
|
||||||
|
cursor[0] = min(instance.safe_height, cursor[0])
|
||||||
|
|
||||||
return cursor
|
return cursor
|
||||||
|
|
||||||
|
|
||||||
def 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(instance, instance.cursor)
|
instance.raw_cursor = check(instance, instance.raw_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.raw_cursor[0], instance.raw_cursor[1] +
|
||||||
instance.components.get_component_width(instance.components.components["left"]))
|
instance.components.get_component_width(instance.components.components["left"]))
|
||||||
|
@ -4,7 +4,9 @@ from mode import normal, insert, command
|
|||||||
def activate(instance, mode):
|
def activate(instance, mode):
|
||||||
# Visibly update the mode
|
# Visibly update the mode
|
||||||
instance.mode = mode
|
instance.mode = mode
|
||||||
instance.update()
|
|
||||||
|
# Refresh the screen
|
||||||
|
instance.refresh()
|
||||||
|
|
||||||
if mode == "command":
|
if mode == "command":
|
||||||
# Activate command mode
|
# Activate command mode
|
||||||
|
@ -7,6 +7,14 @@ import sys
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def gracefully_exit():
|
||||||
|
# Close the curses window
|
||||||
|
curses.endwin()
|
||||||
|
|
||||||
|
# Finally, exit the program
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
def load_json(file: str) -> dict:
|
def load_json(file: str) -> dict:
|
||||||
# Load the json file with read permissions
|
# Load the json file with read permissions
|
||||||
with open(file, "r") as f:
|
with open(file, "r") as f:
|
||||||
@ -69,7 +77,7 @@ def prompt(instance, message: str, color: int = 1) -> (list, None):
|
|||||||
# Subtracting a key (backspace)
|
# Subtracting a key (backspace)
|
||||||
if key in (curses.KEY_BACKSPACE, 127, '\b'):
|
if key in (curses.KEY_BACKSPACE, 127, '\b'):
|
||||||
# Write whitespace over characters to refresh it
|
# Write whitespace over characters to refresh it
|
||||||
clear(instance, instance.height - 1, 0)
|
clear(instance, instance.height - 1, len(message) + len(inp) - 1)
|
||||||
|
|
||||||
if inp:
|
if inp:
|
||||||
# Subtract a character from the input list
|
# Subtract a character from the input list
|
||||||
@ -94,6 +102,12 @@ def prompt(instance, message: str, color: int = 1) -> (list, None):
|
|||||||
if chr(key) in valid and len(inp) < (instance.width - 2):
|
if chr(key) in valid and len(inp) < (instance.width - 2):
|
||||||
inp.append(chr(key))
|
inp.append(chr(key))
|
||||||
|
|
||||||
|
# Refresh the screen
|
||||||
|
instance.screen.refresh()
|
||||||
|
|
||||||
|
# Refresh the screen
|
||||||
|
instance.refresh()
|
||||||
|
|
||||||
# Write the message to the screen
|
# Write the message to the screen
|
||||||
instance.screen.addstr(instance.height - 1, 0, message, curses.color_pair(color))
|
instance.screen.addstr(instance.height - 1, 0, message, curses.color_pair(color))
|
||||||
|
|
||||||
|
46
main.py
46
main.py
@ -1,41 +1,44 @@
|
|||||||
|
import argparse
|
||||||
|
import curses
|
||||||
|
import os
|
||||||
|
|
||||||
from core import colors, cursors, buffers, modes, utils
|
from core import colors, cursors, buffers, modes, utils
|
||||||
from core.buffers import Buffer
|
from core.buffers import Buffer
|
||||||
from core.components import Components
|
from core.components import Components
|
||||||
import argparse
|
|
||||||
import curses
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
class Lambda:
|
class Lambda:
|
||||||
def __init__(self, buffer: Buffer, config: dict = None):
|
def __init__(self, buffer: Buffer, config: dict = None):
|
||||||
self.screen = curses.initscr()
|
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.components = Components()
|
||||||
|
self.config = config or {"icon": "λ"}
|
||||||
|
self.buffer = buffer
|
||||||
|
self.mode = "normal"
|
||||||
|
self.cursor = [0, 0]
|
||||||
|
self.raw_cursor = [0, 0]
|
||||||
|
self.offset = [0, 0]
|
||||||
self.height = 0
|
self.height = 0
|
||||||
self.width = 0
|
self.width = 0
|
||||||
self.safe_height = 0
|
self.safe_height = 0
|
||||||
self.safe_width = 0
|
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
|
||||||
self.height, self.width = self.screen.getmaxyx()
|
self.height, self.width = self.screen.getmaxyx()
|
||||||
|
|
||||||
# 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"]) - 2
|
||||||
self.safe_width = self.width - self.components.get_component_width(self.components.components["left"])
|
self.safe_width = self.width - self.components.get_component_width(self.components.components["left"]) - 1
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
# Calculate the real cursor position
|
||||||
|
self.cursor[0], self.cursor[1] = self.raw_cursor[0] + self.offset[0], self.raw_cursor[1] + self.offset[1]
|
||||||
|
|
||||||
def update(self):
|
|
||||||
# Update the dimensions of the terminal
|
# Update the dimensions of the terminal
|
||||||
self.update_dimensions()
|
self.update_dimensions()
|
||||||
|
|
||||||
# Calculate the current line number
|
# Write the buffer to the screen
|
||||||
self.current_line = self.cursor[0] + self.offset[0]
|
self.buffer.render(self)
|
||||||
|
|
||||||
# Refresh the on-screen components
|
# Refresh the on-screen components
|
||||||
self.components.render(self)
|
self.components.render(self)
|
||||||
@ -67,11 +70,8 @@ class Lambda:
|
|||||||
def run(self):
|
def run(self):
|
||||||
# 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
|
|
||||||
self.buffer.render(self)
|
|
||||||
|
|
||||||
# Update the screen variables
|
# Update the screen variables
|
||||||
self.update()
|
self.refresh()
|
||||||
|
|
||||||
# Wait for a keypress
|
# Wait for a keypress
|
||||||
key = self.screen.getch()
|
key = self.screen.getch()
|
||||||
@ -81,7 +81,7 @@ class Lambda:
|
|||||||
|
|
||||||
# Refresh and clear the screen
|
# Refresh and clear the screen
|
||||||
self.screen.refresh()
|
self.screen.refresh()
|
||||||
self.screen.clear()
|
self.screen.erase()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@ -108,11 +108,7 @@ def main():
|
|||||||
|
|
||||||
# KeyboardInterrupt is thrown when <C-c> is pressed (exit)
|
# KeyboardInterrupt is thrown when <C-c> is pressed (exit)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
# Clean up the screen
|
utils.gracefully_exit()
|
||||||
curses.endwin()
|
|
||||||
|
|
||||||
# Then, just exit
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
# Excepts *any* errors that occur
|
# Excepts *any* errors that occur
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
|
@ -11,6 +11,11 @@ def execute(instance, commands):
|
|||||||
# Write to the file
|
# Write to the file
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
elif command == "d":
|
||||||
|
# Load a prompt with debug info
|
||||||
|
utils.prompt(instance, f"Cursor: {instance.cursor} | Raw: {instance.raw_cursor} | Len: {len(instance.buffer.data)}")
|
||||||
|
utils.prompt(instance, f"{len(instance.buffer.data[6])}")
|
||||||
|
|
||||||
# Quit
|
# Quit
|
||||||
elif command == "q":
|
elif command == "q":
|
||||||
# Load a goodbye prompt
|
# Load a goodbye prompt
|
||||||
@ -18,7 +23,7 @@ def execute(instance, commands):
|
|||||||
|
|
||||||
# Unknown command
|
# Unknown command
|
||||||
else:
|
else:
|
||||||
utils.error(instance, f"not an editor command: '{command}'")
|
utils.error(instance, f"invalid command: '{command}'")
|
||||||
|
|
||||||
|
|
||||||
def activate(instance):
|
def activate(instance):
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
from core import cursors, modes
|
from core import cursors, modes
|
||||||
from mode import insert
|
|
||||||
from mode import command
|
|
||||||
|
|
||||||
|
|
||||||
def execute(instance, key):
|
def execute(instance, key):
|
||||||
|
Loading…
Reference in New Issue
Block a user