Quality of life code changes

This commit is contained in:
Maddie H 2022-03-19 15:41:40 +00:00
parent 0c65f9c0b3
commit e3ee63f39e
9 changed files with 80 additions and 68 deletions

4
.gitignore vendored
View File

@ -1,2 +1,4 @@
/target __pycache__
core/__pycache__
mode/__pycache__
.idea .idea

View File

@ -1 +1,14 @@
# lambda # λ lambda
Next generation hackable text editor for nerds.
### Overview
Lambda is a similar text editor to `vim` or `kakoune`.<br>
However, it has a few key differences:
- Lambda is written in Python, so it is easy to hack and learn.
- Lambda is incredibly modular, so you can easily add new features.
- Lambda is written in a way that makes it easy to use in a terminal.
- Lambda follows the unix philosophy of "do one thing and do it well."
- It has no bloated features, like splits or tabs
- It contains the bare necessities and provides a few extra modules
- Lambda is extremely fast and makes use of efficient memory management.
- Lambda has much better default keybindings than other text editors.

View File

@ -4,19 +4,18 @@ import curses
class StatusBar: class StatusBar:
def __init__(self, instance): def __init__(self, instance):
self.instance = instance
self.mode = instance.mode.upper() self.mode = instance.mode.upper()
self.file = instance.buffer.name or "[No Name]" self.file = instance.buffer.name or "[No Name]"
self.icon = instance.config["icon"] or "λ" self.icon = instance.config["icon"] or "λ"
self.colors = (7, 5, 13) self.colors = (7, 5, 13)
self.components = [self.icon, self.mode, self.file] self.components = [self.icon, self.mode, self.file]
def render(self): def render(self, instance):
x = 1 x = 1
utils.clear(self.instance, self.instance.height - 2, 0) utils.clear(instance, instance.height - 2, 0)
for count, component in enumerate(self.components): for count, component in enumerate(self.components):
self.instance.screen.addstr(self.instance.height - 2, x, component, instance.screen.addstr(instance.height - 2, x, component,
curses.color_pair(self.colors[count]) | curses.A_BOLD) curses.color_pair(self.colors[count]) | curses.A_BOLD)
x += len(component) + 1 x += len(component) + 1
@ -28,4 +27,4 @@ class Components:
def render(self, instance): def render(self, instance):
for component in self.components["bottom"]: for component in self.components["bottom"]:
component(instance).render() component(instance).render(instance)

View File

@ -1,33 +1,29 @@
from mode import normal, insert, command from mode import normal, insert, command
def activate(instance, mode) -> object: def activate(instance, mode):
# Visibly update the mode # Visibly update the mode
instance.mode = mode instance.mode = mode
instance.update() instance.update()
if mode == "command": if mode == "command":
instance = command.activate(instance) # Activate command mode
command.activate(instance)
elif mode == "insert": elif mode == "insert":
instance = insert.activate(instance) # Activate insert mode
insert.activate(instance)
elif mode == "normal": elif mode == "normal":
instance = normal.activate(instance) # Activate normal mode
normal.activate(instance)
return instance
def handle_key(instance, key): def handle_key(instance, key):
# Normal mode - default keybindings
if instance.mode == "normal": if instance.mode == "normal":
instance = normal.execute(instance, key) normal.execute(instance, key)
# Insert mode - inserting text to the buffer # Insert mode - inserting text to the buffer
elif instance.mode == "insert": elif instance.mode == "insert":
instance = insert.execute(instance, key) insert.execute(instance, key)
# Command mode - extra commands for lambda
elif instance.mode == "command":
instance = command.activate(instance)
return instance

2
lambda
View File

@ -1,3 +1,3 @@
#!/bin/sh #!/bin/sh
python3 ./lambda.py "$@" python3 ./main.py "$@"

27
main.py
View File

@ -21,7 +21,10 @@ class Lambda:
self.config = {"icon": "λ"} self.config = {"icon": "λ"}
def update_dimensions(self): def update_dimensions(self):
# 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
self.safe_height = self.height - len(self.components.components["bottom"]) self.safe_height = self.height - len(self.components.components["bottom"])
def update(self): def update(self):
@ -41,7 +44,7 @@ class Lambda:
# Change the cursor shape # Change the cursor shape
cursors.cursor_mode("block") cursors.cursor_mode("block")
# Turn no echo on # Don't echo any key-presses
curses.noecho() curses.noecho()
# Show a welcome message if lambda opens with no file # Show a welcome message if lambda opens with no file
@ -52,6 +55,7 @@ class Lambda:
self.run() self.run()
def run(self): def run(self):
# 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) # buffers.write_buffer(screen, buffer)
@ -71,38 +75,47 @@ class Lambda:
def main(): def main():
# Command line arguments # Shell arguments
parser = argparse.ArgumentParser(description="Next generation hackable text editor for nerds.") parser = argparse.ArgumentParser(description="Next generation hackable text editor for nerds.")
parser.add_argument("file", metavar="file", type=str, nargs="?", parser.add_argument("file", metavar="file", type=str, nargs="?",
help="The name of a file for lambda to open") help="The name of a file for lambda to open")
# Collect the arguments passed into lambda # Collect the arguments passed into lambda at the shell
args = parser.parse_args() args = parser.parse_args()
# Load the file into buffer # Load the file into a Buffer object
buffer = buffers.load_file(args.file) buffer = buffers.load_file(args.file)
# Change the escape delay to 25ms # Change the escape delay to 25ms
# Fixes an issue where esc takes way too long to press # Fixes an issue where esc takes way too long to press
os.environ.setdefault("ESCDELAY", "25") os.environ.setdefault("ESCDELAY", "25")
# Load lambda with the buffer # Load lambda with the buffer object
screen = Lambda(buffer) screen = Lambda(buffer)
# Start the screen # Start the screen, this will loop until exit
try: try:
screen.start() screen.start()
# KeyboardInterrupt is thrown when ctrl+c is pressed # KeyboardInterrupt is thrown when <C-c> is pressed (exit)
except KeyboardInterrupt: except KeyboardInterrupt:
# Clean up the screen
curses.endwin() curses.endwin()
# Then, just exit
sys.exit() sys.exit()
# Excepts *any* errors that occur
except Exception as exception: except Exception as exception:
# Clean up the screen
curses.endwin() curses.endwin()
# Print the error message and traceback
print(f"{colors.Codes.red}FATAL ERROR:{colors.Codes.end} " print(f"{colors.Codes.red}FATAL ERROR:{colors.Codes.end} "
f"{colors.Codes.yellow}{exception}{colors.Codes.end}\n") f"{colors.Codes.yellow}{exception}{colors.Codes.end}\n")
print(traceback.format_exc()) print(traceback.format_exc())
# Exit, with an error code
sys.exit(0) sys.exit(0)

View File

@ -2,33 +2,31 @@ from core import utils
def execute(instance, commands): def execute(instance, commands):
if not commands: # Only if commands are given
# Quit if there are no commands, don't check anything if commands:
return instance # Check each command in the list of commands
for command in commands:
# Write
if command == "w":
# Write to the file
pass
for command in commands: # Quit
if command == "w": elif command == "q":
# Write to the file # Load a goodbye prompt
pass utils.goodbye(instance)
elif command == "q": # Unknown command
# Load a goodbye prompt else:
utils.goodbye(instance) utils.error(instance, f"not an editor command: '{command}'")
else:
utils.error(instance, f"not an editor command: '{command}'")
return instance
def activate(instance): def activate(instance):
# Start the prompt # Create a prompt, which returns the input (commands)
commands = utils.prompt(instance, ":") commands = utils.prompt(instance, ":")
# Execute the commands # Execute the commands given
instance = execute(instance, commands) execute(instance, commands)
# Return to normal mode # Return to normal mode once all commands are executed
instance.mode = "normal" instance.mode = "normal"
return instance

View File

@ -1,18 +1,13 @@
from core import cursors from core import cursors, modes
from mode import normal
def execute(instance, key): def execute(instance, key):
# Enter key
if key == 27: if key == 27:
# Switch to normal mode # Switch to normal mode
instance.mode = "normal" modes.activate(instance, "normal")
normal.activate(instance)
return instance
def activate(instance): def activate(instance):
# Switch the cursor to a line # Switch the cursor to a line
cursors.cursor_mode("line") cursors.cursor_mode("line")
return instance

View File

@ -22,24 +22,20 @@ def execute(instance, key):
elif key == ord("i"): elif key == ord("i"):
# Activate insert mode # Activate insert mode
instance = modes.activate(instance, "insert") modes.activate(instance, "insert")
elif key == ord("I"): elif key == ord("I"):
# Move the cursor to the right # Move the cursor to the right
instance.cursor = cursors.cursor_push(instance.cursor, "right") instance.cursor = cursors.cursor_push(instance.cursor, "right")
# Then activate insert mode # Then activate insert mode
instance = modes.activate(instance, "insert") modes.activate(instance, "insert")
elif key in (ord(":"), ord(";")): elif key in (ord(":"), ord(";")):
# Activate command mode # Activate command mode
instance = modes.activate(instance, "command") modes.activate(instance, "command")
return instance
def activate(instance): def activate():
# Switch the cursor to a block # Switch the cursor to a block
cursors.cursor_mode("block") cursors.cursor_mode("block")
return instance