✨ Quality of life code changes
This commit is contained in:
parent
0c65f9c0b3
commit
e3ee63f39e
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,2 +1,4 @@
|
|||||||
/target
|
__pycache__
|
||||||
|
core/__pycache__
|
||||||
|
mode/__pycache__
|
||||||
.idea
|
.idea
|
15
README.md
15
README.md
@ -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.
|
||||||
|
@ -4,18 +4,17 @@ 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)
|
||||||
|
@ -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
2
lambda
@ -1,3 +1,3 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
python3 ./lambda.py "$@"
|
python3 ./main.py "$@"
|
||||||
|
27
main.py
27
main.py
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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:
|
for command in commands:
|
||||||
|
# Write
|
||||||
if command == "w":
|
if command == "w":
|
||||||
# Write to the file
|
# Write to the file
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Quit
|
||||||
elif command == "q":
|
elif command == "q":
|
||||||
# Load a goodbye prompt
|
# Load a goodbye prompt
|
||||||
utils.goodbye(instance)
|
utils.goodbye(instance)
|
||||||
|
|
||||||
|
# Unknown command
|
||||||
else:
|
else:
|
||||||
utils.error(instance, f"not an editor command: '{command}'")
|
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
|
|
||||||
|
@ -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
|
|
||||||
|
@ -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
|
|
Loading…
Reference in New Issue
Block a user