new structure
This commit is contained in:
parent
ddf99d71f6
commit
88f7ab0e8d
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"python.analysis.extraPaths": [
|
|
||||||
"./modes"
|
|
||||||
]
|
|
||||||
}
|
|
Binary file not shown.
BIN
core/__pycache__/mode.cpython-310.pyc
Normal file
BIN
core/__pycache__/mode.cpython-310.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
0
core/buffer.py
Normal file
0
core/buffer.py
Normal file
@ -1,5 +1,29 @@
|
|||||||
def cursor_mode(mode):
|
def cursor_mode(mode):
|
||||||
if mode == "block":
|
if mode == "block":
|
||||||
print("\033[2 q")
|
print("\033[2 q")
|
||||||
|
|
||||||
elif mode == "line":
|
elif mode == "line":
|
||||||
print("\033[6 q")
|
print("\033[6 q")
|
||||||
|
|
||||||
|
elif mode == "hidden":
|
||||||
|
print('\033[? 25l')
|
||||||
|
|
||||||
|
elif mode == "visible":
|
||||||
|
print('\033[? 25h')
|
||||||
|
|
||||||
|
|
||||||
|
def check_cursor(data):
|
||||||
|
data["cursor_x"] = max(2, data["cursor_x"])
|
||||||
|
data["cursor_x"] = min(data["width"] - 1, data["cursor_x"])
|
||||||
|
data["cursor_y"] = max(0, data["cursor_y"])
|
||||||
|
data["cursor_y"] = min(data["height"] - 3, data["cursor_y"])
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def move(stdscr, data):
|
||||||
|
# Calculate a valid cursor position from data
|
||||||
|
data = check_cursor(data)
|
||||||
|
|
||||||
|
# Move the cursor
|
||||||
|
stdscr.move(data["cursor_y"], data["cursor_x"])
|
||||||
|
17
core/mode.py
Normal file
17
core/mode.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from modes import normal, insert, command
|
||||||
|
|
||||||
|
|
||||||
|
def activate(stdscr, data):
|
||||||
|
if data["mode"] == "normal":
|
||||||
|
data["mode_color"] = 6
|
||||||
|
data = normal.activate(stdscr, data)
|
||||||
|
|
||||||
|
elif data["mode"] == "insert":
|
||||||
|
data["mode_color"] = 12
|
||||||
|
data = insert.activate(stdscr, data)
|
||||||
|
|
||||||
|
elif data["mode"] == "command":
|
||||||
|
data["mode_color"] = 6
|
||||||
|
data = command.activate(stdscr, data)
|
||||||
|
|
||||||
|
return data
|
@ -1,35 +1,43 @@
|
|||||||
import curses
|
import curses
|
||||||
|
|
||||||
|
|
||||||
def refresh(stdscr, height, width, mode, colors=None, theme="inverted", icon="λ", file="[No Name]"):
|
def themes(data):
|
||||||
if colors is None:
|
if data["statusbar_theme"] == "bare":
|
||||||
colors = [8, 6, 14, 2]
|
# The theme colors
|
||||||
|
colors = (7, data["mode_color"] - 1, 13, 1)
|
||||||
if theme == "inverted":
|
|
||||||
# Add spaces on either end
|
|
||||||
icon = f" {icon} "
|
|
||||||
mode = f" {mode} "
|
|
||||||
file = f" {file} "
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Subtract one from all colours
|
|
||||||
for index, color in enumerate(colors):
|
|
||||||
colors[index] -= 1
|
|
||||||
|
|
||||||
# Add spaces before each part
|
# Add spaces before each part
|
||||||
icon = f" {icon}"
|
icon = f" {data['icon']}"
|
||||||
mode = f" {mode}"
|
mode = f" {data['mode'].upper()}"
|
||||||
file = f" {file}"
|
file = f" {data['file']}"
|
||||||
|
|
||||||
|
else:
|
||||||
|
# The theme colors
|
||||||
|
colors = (8, data["mode_color"], 14, 2)
|
||||||
|
|
||||||
|
# Add spaces on either end
|
||||||
|
icon = f" {data['icon']} "
|
||||||
|
mode = f" {data['mode'].upper()} "
|
||||||
|
file = f" {data['file']} "
|
||||||
|
|
||||||
|
return colors, icon, mode, file
|
||||||
|
|
||||||
|
|
||||||
|
def refresh(stdscr, data):
|
||||||
|
colors, icon, mode, file = themes(data)
|
||||||
# Render icon
|
# Render icon
|
||||||
stdscr.addstr(height - 2, 0, icon, curses.color_pair(colors[0]) | curses.A_BOLD)
|
stdscr.addstr(data["height"] - 2, 0, icon,
|
||||||
|
curses.color_pair(colors[0]) | curses.A_BOLD)
|
||||||
|
|
||||||
# Render mode
|
# Render mode
|
||||||
stdscr.addstr(height - 2, len(icon), mode, curses.color_pair(colors[1]) | curses.A_BOLD)
|
stdscr.addstr(data["height"] - 2, len(icon), mode,
|
||||||
|
curses.color_pair(colors[1]) | curses.A_BOLD)
|
||||||
|
|
||||||
# Render file name
|
# Render file name
|
||||||
stdscr.addstr(height - 2, len(icon) + len(mode), file, curses.color_pair(colors[2]) | curses.A_BOLD)
|
stdscr.addstr(data["height"] - 2, len(icon) + len(mode), file,
|
||||||
|
curses.color_pair(colors[2]) | curses.A_BOLD)
|
||||||
|
|
||||||
# Rest of the bar
|
# Rest of the bar
|
||||||
stdscr.addstr(height - 2, len(icon) + len(mode) + len(file), " " * (width - (len(icon) + len(mode) + len(file))),
|
stdscr.addstr(data["height"] - 2, len(icon) + len(mode) + len(file),
|
||||||
|
" " * (data["width"] - (len(icon) + len(mode) + len(file))),
|
||||||
curses.color_pair(colors[3]))
|
curses.color_pair(colors[3]))
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
from sys import exit
|
from sys import exit
|
||||||
|
from core import cursor
|
||||||
import curses
|
import curses
|
||||||
|
|
||||||
|
|
||||||
def goodbye(stdscr, height, width):
|
def goodbye(stdscr, data):
|
||||||
# The prompt message
|
# The prompt message
|
||||||
prompt = "Really quit lambda? (y or n): "
|
prompt = "Really quit lambda? (y or n): "
|
||||||
|
|
||||||
# Clear the bottom line
|
# Clear the bottom line
|
||||||
stdscr.addstr(height-1, 0, " " * (width - 1), curses.color_pair(1))
|
stdscr.addstr(data["height"]-1, 0, " " * (data["width"] - 1), curses.color_pair(1))
|
||||||
|
|
||||||
# Print the prompt
|
# Print the prompt
|
||||||
stdscr.addstr(height-1, 0, prompt, curses.color_pair(11))
|
stdscr.addstr(data["height"]-1, 0, prompt, curses.color_pair(11))
|
||||||
|
|
||||||
# Wait for and capture a key press from the user
|
# Wait for and capture a key press from the user
|
||||||
key = stdscr.getch()
|
key = stdscr.getch()
|
||||||
@ -20,9 +21,17 @@ def goodbye(stdscr, height, width):
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
# Clear the bottom line again
|
# Clear the bottom line again
|
||||||
stdscr.addstr(height-1, 0, " " * (width - 1), curses.color_pair(1))
|
stdscr.addstr(data["height"]-1, 0, " " * (data["width"] - 1), curses.color_pair(1))
|
||||||
|
|
||||||
|
|
||||||
def error(stdscr, height, error_msg):
|
def error(stdscr, data, error_msg):
|
||||||
error_msg = f" ERROR {error_msg}"
|
# Print the error message to the bottom line
|
||||||
stdscr.addstr(height-1, 0, error_msg, curses.color_pair(3))
|
error_msg = f"ERROR: {error_msg}"
|
||||||
|
stdscr.addstr(data["height"]-1, 0, error_msg, curses.color_pair(3))
|
||||||
|
stdscr.addstr(data["height"]-1, len(error_msg) + 1, "(press any key) ", curses.color_pair(1))
|
||||||
|
|
||||||
|
# Wait for a key to be pressed
|
||||||
|
stdscr.getch()
|
||||||
|
|
||||||
|
# Clear the bottom line
|
||||||
|
stdscr.addstr(data["height"]-1, 0, " " * (data["width"] - 1), curses.color_pair(1))
|
58
lambda
58
lambda
@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
from core import colors, cursor, mode
|
||||||
import os
|
import os
|
||||||
import curses
|
import curses
|
||||||
from core import colors, cursor
|
import argparse
|
||||||
from modes import normal
|
|
||||||
|
|
||||||
|
|
||||||
def start_screen(stdscr):
|
def start_screen(stdscr):
|
||||||
@ -12,7 +12,7 @@ def start_screen(stdscr):
|
|||||||
# Startup text
|
# Startup text
|
||||||
title = "λ Lambda"
|
title = "λ Lambda"
|
||||||
subtext = [
|
subtext = [
|
||||||
"A performant, efficient and hackable text editor",
|
"Next generation hackable text editor for nerds",
|
||||||
"",
|
"",
|
||||||
"Type :h to open the README.md document",
|
"Type :h to open the README.md document",
|
||||||
"Type :o <file> to open a file and edit",
|
"Type :o <file> to open a file and edit",
|
||||||
@ -33,39 +33,35 @@ def start_screen(stdscr):
|
|||||||
stdscr.addstr(start_y, start_x, text)
|
stdscr.addstr(start_y, start_x, text)
|
||||||
|
|
||||||
|
|
||||||
def start(stdscr):
|
def start(stdscr, file):
|
||||||
# Initialise data before starting
|
# Initialise data before starting
|
||||||
data = {"cursor_y": 0, "cursor_x": 0, "commands": []}
|
data = {
|
||||||
|
"cursor_y": 0,
|
||||||
# Clear and refresh the screen for a blank canvas
|
"cursor_x": 0,
|
||||||
stdscr.clear()
|
"height": 0,
|
||||||
stdscr.refresh()
|
"width": 0,
|
||||||
|
"mode": "normal",
|
||||||
|
"icon": "λ",
|
||||||
|
"file": file,
|
||||||
|
"statusbar_theme": "filled"
|
||||||
|
}
|
||||||
|
|
||||||
# Initialise colors
|
# Initialise colors
|
||||||
colors.init_colors()
|
colors.init_colors()
|
||||||
|
|
||||||
# Load the start screen
|
|
||||||
start_screen(stdscr)
|
|
||||||
|
|
||||||
# Change the cursor shape
|
# Change the cursor shape
|
||||||
cursor.cursor_mode("block")
|
cursor.cursor_mode("block")
|
||||||
|
|
||||||
|
# Start the screen
|
||||||
|
start_screen(stdscr)
|
||||||
|
|
||||||
# Main loop
|
# Main loop
|
||||||
while True:
|
while True:
|
||||||
# Get the height and width of the screen
|
# Get the height and width of the screen
|
||||||
height, width = stdscr.getmaxyx()
|
data["height"], data["width"] = stdscr.getmaxyx()
|
||||||
|
|
||||||
# Activate normal mode
|
# Activate the next mode
|
||||||
data = normal.activate(stdscr, height, width, data)
|
data = mode.activate(stdscr, data)
|
||||||
|
|
||||||
# Calculate a valid cursor position from data
|
|
||||||
cursor_x = max(2, data["cursor_x"])
|
|
||||||
cursor_x = min(width - 1, cursor_x)
|
|
||||||
cursor_y = max(0, data["cursor_y"])
|
|
||||||
cursor_y = min(height - 3, cursor_y)
|
|
||||||
|
|
||||||
# Move the cursor
|
|
||||||
stdscr.move(cursor_y, cursor_x)
|
|
||||||
|
|
||||||
# Refresh and clear the screen
|
# Refresh and clear the screen
|
||||||
stdscr.refresh()
|
stdscr.refresh()
|
||||||
@ -73,12 +69,24 @@ def start(stdscr):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Process some integers.")
|
||||||
|
parser.add_argument("file", metavar="file", type=str, nargs="?",
|
||||||
|
help="File to open")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
# Check the file name
|
||||||
|
if args.file:
|
||||||
|
file = args.file
|
||||||
|
|
||||||
|
else:
|
||||||
|
file = "[No Name]"
|
||||||
|
|
||||||
# Change the escape delay to 25ms
|
# Change the escape delay to 25ms
|
||||||
# Fixes an issue where esc takes too long to press
|
# Fixes an issue where esc takes too long to press
|
||||||
os.environ.setdefault("ESCDELAY", "25")
|
os.environ.setdefault("ESCDELAY", "25")
|
||||||
|
|
||||||
# Initialise the screen
|
# Initialise the screen
|
||||||
curses.wrapper(start)
|
curses.wrapper(start, file)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -2,7 +2,7 @@ from core import statusbar, utils
|
|||||||
import curses
|
import curses
|
||||||
|
|
||||||
|
|
||||||
def execute(stdscr, height, width, commands):
|
def execute(stdscr, data, commands):
|
||||||
if not commands:
|
if not commands:
|
||||||
# Quit if there are no commands, don't check anything
|
# Quit if there are no commands, don't check anything
|
||||||
return
|
return
|
||||||
@ -11,20 +11,32 @@ def execute(stdscr, height, width, commands):
|
|||||||
if command == "w":
|
if command == "w":
|
||||||
# Write to the file
|
# Write to the file
|
||||||
pass
|
pass
|
||||||
|
|
||||||
elif command == "q":
|
elif command == "q":
|
||||||
# Goodbye prompt
|
# Goodbye prompt
|
||||||
utils.goodbye(stdscr, height, width)
|
utils.goodbye(stdscr, data)
|
||||||
|
|
||||||
|
elif command == "t":
|
||||||
|
if data["statusbar_theme"] == "filled":
|
||||||
|
data["statusbar_theme"] = "bare"
|
||||||
|
|
||||||
|
else:
|
||||||
|
data["statusbar_theme"] = "filled"
|
||||||
|
|
||||||
|
else:
|
||||||
|
utils.error(stdscr, data, f"Not an editor command: '{command}'")
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
def activate(stdscr, height, width, data):
|
def activate(stdscr, data):
|
||||||
# Initialise variables
|
# Initialise variables
|
||||||
key = 0
|
|
||||||
commands = []
|
commands = []
|
||||||
|
|
||||||
# Visibly switch to command mode
|
# Visibly switch to command mode
|
||||||
statusbar.refresh(stdscr, height, width, "COMMAND")
|
statusbar.refresh(stdscr, data)
|
||||||
stdscr.addstr(height-1, 0, ":")
|
stdscr.addstr(data["height"]-1, 0, ":")
|
||||||
stdscr.move(height-1, 1)
|
stdscr.move(data["height"]-1, 1)
|
||||||
|
|
||||||
# Main loop
|
# Main loop
|
||||||
while True:
|
while True:
|
||||||
@ -34,38 +46,44 @@ def activate(stdscr, height, width, data):
|
|||||||
# Handle subtracting a key (backspace)
|
# Handle subtracting a key (backspace)
|
||||||
if key == curses.KEY_BACKSPACE:
|
if key == curses.KEY_BACKSPACE:
|
||||||
# Write whitespace over characters to refresh it
|
# Write whitespace over characters to refresh it
|
||||||
stdscr.addstr(height-1, 1, " " * len(commands))
|
stdscr.addstr(data["height"]-1, 1, " " * len(commands))
|
||||||
|
|
||||||
if commands:
|
if commands:
|
||||||
# Subtract a character
|
# Subtract a character
|
||||||
commands.pop()
|
commands.pop()
|
||||||
else:
|
else:
|
||||||
# If there's nothing left, quit the loop
|
# Exit command mode and enter normal mode if there is nothing left
|
||||||
|
data["mode"] = "normal"
|
||||||
return data
|
return data
|
||||||
|
|
||||||
elif key == 27:
|
elif key == 27:
|
||||||
# Quit the loop if escape is pressed
|
# Exit command mode and enter normal mode if "esc" is pressed
|
||||||
|
data["mode"] = "normal"
|
||||||
return data
|
return data
|
||||||
|
|
||||||
elif key in (curses.KEY_ENTER, ord('\n'), ord('\r')):
|
elif key in (curses.KEY_ENTER, ord('\n'), ord('\r'), ord(":"), ord(";")):
|
||||||
# Execute commands
|
# Execute commands
|
||||||
execute(stdscr, height, width, commands)
|
data = execute(stdscr, data, commands)
|
||||||
|
|
||||||
# Clear the bottom bar
|
# Clear the bottom bar
|
||||||
stdscr.addstr(height - 1, 0, " " * (width - 1))
|
stdscr.addstr(data["height"] - 1, 0, " " * (data["width"] - 1))
|
||||||
|
|
||||||
|
# Return to normal mode after executing a command
|
||||||
|
data["mode"] = "normal"
|
||||||
return data
|
return data
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# If any other key is typed, append it
|
# If any other key is typed, append it
|
||||||
# As long as the key is in the valid list
|
# As long as the key is in the valid list
|
||||||
valid = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ!"
|
valid = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ!"
|
||||||
if chr(key) in valid and len(commands) < (width - 2):
|
if chr(key) in valid and len(commands) < (data["width"] - 2):
|
||||||
commands.append(chr(key))
|
commands.append(chr(key))
|
||||||
|
|
||||||
# Join the commands together for visibility on the screen
|
# Join the commands together for visibility on the screen
|
||||||
friendly_command = "".join(commands)
|
friendly_command = "".join(commands)
|
||||||
|
|
||||||
# Write the commands to the screen
|
# Write the commands to the screen
|
||||||
stdscr.addstr(height-1, 1, friendly_command)
|
stdscr.addstr(data["height"]-1, 1, friendly_command)
|
||||||
|
|
||||||
# Move the cursor the end of the commands
|
# Move the cursor the end of the commands
|
||||||
stdscr.move(height-1, len(commands)+1)
|
stdscr.move(data["height"]-1, len(commands)+1)
|
||||||
|
@ -1,17 +1,33 @@
|
|||||||
from core import statusbar
|
from core import statusbar, cursor
|
||||||
|
|
||||||
|
|
||||||
def execute(stdscr, height, width, key):
|
def execute(data, key):
|
||||||
return
|
return data
|
||||||
|
|
||||||
|
|
||||||
def activate(stdscr, height, width, data):
|
def activate(stdscr, data):
|
||||||
# Refresh the status bar with a different colour for insert
|
# Refresh the status bar with a different colour for insert
|
||||||
colors = [8, 12, 14, 2]
|
data["statusbar_colors"] = [8, 12, 14, 2]
|
||||||
statusbar.refresh(stdscr, height, width, "INSERT", colors)
|
statusbar.refresh(stdscr, data)
|
||||||
|
|
||||||
|
# Refresh the status bar
|
||||||
|
statusbar.refresh(stdscr, data)
|
||||||
|
|
||||||
|
# Move the cursor
|
||||||
|
cursor.move(stdscr, data)
|
||||||
|
|
||||||
|
# Switch to a line cursor
|
||||||
|
cursor.cursor_mode("line")
|
||||||
|
|
||||||
while True:
|
|
||||||
# Wait for and capture a key press from the user
|
# Wait for and capture a key press from the user
|
||||||
key = stdscr.getch()
|
key = stdscr.getch()
|
||||||
|
|
||||||
|
# Exit insert mode
|
||||||
|
if key == 27:
|
||||||
|
data["mode"] = "normal"
|
||||||
|
return data
|
||||||
|
|
||||||
|
# Check keybindings
|
||||||
|
data = execute(data, key)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
from modes import command
|
from core import statusbar, cursor
|
||||||
from modes import insert
|
|
||||||
from core import statusbar
|
|
||||||
|
|
||||||
|
|
||||||
def execute(stdscr, height, width, key, data):
|
def execute(data, key):
|
||||||
if key == ord("j"):
|
if key == ord("j"):
|
||||||
# Move the cursor down
|
# Move the cursor down
|
||||||
data["cursor_y"] += 1
|
data["cursor_y"] += 1
|
||||||
@ -21,23 +19,30 @@ def execute(stdscr, height, width, key, data):
|
|||||||
data["cursor_x"] -= 1
|
data["cursor_x"] -= 1
|
||||||
|
|
||||||
elif key == ord("i"):
|
elif key == ord("i"):
|
||||||
# Insert mode
|
# Exit normal mode and enter insert mode
|
||||||
data = insert.activate(stdscr, height, width, data)
|
data["mode"] = "insert"
|
||||||
|
|
||||||
elif key in (ord(":"), ord(";")):
|
elif key in (ord(":"), ord(";")):
|
||||||
# Switch to command mode
|
# Exit normal mode and enter command mode
|
||||||
data = command.activate(stdscr, height, width, data)
|
data["mode"] = "command"
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def activate(stdscr, height, width, data):
|
def activate(stdscr, data):
|
||||||
# Refresh the status bar
|
# Refresh the status bar
|
||||||
statusbar.refresh(stdscr, height, width, "NORMAL")
|
statusbar.refresh(stdscr, data)
|
||||||
|
|
||||||
|
# Move the cursor
|
||||||
|
cursor.move(stdscr, data)
|
||||||
|
|
||||||
|
# Switch the cursor to a block
|
||||||
|
cursor.cursor_mode("block")
|
||||||
|
|
||||||
# Wait for and capture a key press from the user
|
# Wait for and capture a key press from the user
|
||||||
key = stdscr.getch()
|
key = stdscr.getch()
|
||||||
|
|
||||||
# Check against the keybindings
|
# Check against the keybindings
|
||||||
data = execute(stdscr, height, width, key, data)
|
data = execute(data, key)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
Loading…
Reference in New Issue
Block a user