basic visual buffer support

This commit is contained in:
Maddie H 2022-03-17 21:47:19 +00:00
parent 208e66d9fc
commit 3de29be96d
22 changed files with 105 additions and 70 deletions

View File

@ -1,9 +1,9 @@
import curses import curses
def write_buffer(stdscr, data): def write_buffer(screen, data):
count = 0 for index, line in enumerate(data["buffer_list"][data["visible_y"]:]):
for line in data["buffer_list"]: if index < data["height"] - 2:
str_line = "".join(line) str_line = "".join(line)
stdscr.addstr(count, len(data["info_bar"][0]), str_line + (" " * (len(line) - 1)), curses.color_pair(1)) # [data["visible_x"]:1:data["width"] - data["visible_x"]]
count += 1 screen.addstr(index, len(data["info_bar"][0]), str_line + (" " * (len(line) - 1)), curses.color_pair(1))

View File

@ -1,3 +1,6 @@
from core import utils, buffer
def cursor_mode(mode): def cursor_mode(mode):
if mode == "block": if mode == "block":
print("\033[2 q") print("\033[2 q")
@ -13,9 +16,15 @@ def cursor_mode(mode):
def check_cursor(data): def check_cursor(data):
if data["cursor_y"] == len(data["buffer_list"]): if data["cursor_y"] + data["visible_y"] == len(data["buffer_list"]):
data["cursor_y"] -= 1 data["cursor_y"] -= 1
if data["cursor_y"] == data["height"] - 2 and not data["cursor_y"] + data["visible_y"] == len(data["buffer_list"]):
data["visible_y"] += 1
elif data["cursor_y"] == 0 and data["buffer_list"][data["visible_y"]] != data["buffer_list"][0]:
data["visible_y"] -= 1
elif data["cursor_x"] > len(data["buffer_list"][data["cursor_y"]]): elif data["cursor_x"] > len(data["buffer_list"][data["cursor_y"]]):
data["cursor_x"] = len(data["buffer_list"][data["cursor_y"]]) data["cursor_x"] = len(data["buffer_list"][data["cursor_y"]])
@ -27,9 +36,9 @@ def check_cursor(data):
return data return data
def move(stdscr, data): def move(screen, data):
# Calculate a valid cursor position from data # Calculate a valid cursor position from data
data = check_cursor(data) data = check_cursor(data)
# Move the cursor # Move the cursor
stdscr.move(data["cursor_y"], data["cursor_x"]) screen.move(data["cursor_y"], data["cursor_x"])

View File

@ -1,4 +1,4 @@
def open_file(file): def open_file(file_name):
with open(file) as f: with open(file_name) as f:
lines = f.readlines() lines = f.readlines()
return lines return lines

View File

@ -1,17 +1,17 @@
from modes import normal, insert, command from modes import normal, insert, command
def activate(stdscr, data): def activate(screen, data):
if data["mode"] == "normal": if data["mode"] == "normal":
data["mode_color"] = 6 data["mode_color"] = 6
data = normal.activate(stdscr, data) data = normal.activate(screen, data)
elif data["mode"] == "insert": elif data["mode"] == "insert":
data["mode_color"] = 12 data["mode_color"] = 12
data = insert.activate(stdscr, data) data = insert.activate(screen, data)
elif data["mode"] == "command": elif data["mode"] == "command":
data["mode_color"] = 6 data["mode_color"] = 6
data = command.activate(stdscr, data) data = command.activate(screen, data)
return data return data

View File

@ -23,21 +23,23 @@ def themes(data):
return colors, icon, mode, file return colors, icon, mode, file
def refresh(stdscr, data): def refresh(screen, data):
# Calculate the theme
colors, icon, mode, file = themes(data) colors, icon, mode, file = themes(data)
# Render icon # Render icon
stdscr.addstr(data["height"] - 2, 0, icon, screen.addstr(data["height"] - 2, 0, icon,
curses.color_pair(colors[0]) | curses.A_BOLD) curses.color_pair(colors[0]) | curses.A_BOLD)
# Render mode # Render mode
stdscr.addstr(data["height"] - 2, len(icon), mode, screen.addstr(data["height"] - 2, len(icon), mode,
curses.color_pair(colors[1]) | curses.A_BOLD) curses.color_pair(colors[1]) | curses.A_BOLD)
# Render file name # Render file name
stdscr.addstr(data["height"] - 2, len(icon) + len(mode), file, screen.addstr(data["height"] - 2, len(icon) + len(mode), file,
curses.color_pair(colors[2]) | curses.A_BOLD) curses.color_pair(colors[2]) | curses.A_BOLD)
# Rest of the bar # Rest of the bar
stdscr.addstr(data["height"] - 2, len(icon) + len(mode) + len(file), screen.addstr(data["height"] - 2, len(icon) + len(mode) + len(file),
" " * (data["width"] - (len(icon) + len(mode) + len(file))), " " * (data["width"] - (len(icon) + len(mode) + len(file))),
curses.color_pair(colors[3])) curses.color_pair(colors[3]))

View File

@ -2,36 +2,50 @@ from sys import exit
import curses import curses
def goodbye(stdscr, data): def refresh_window_size(screen, data):
# The prompt message # Get the height and width of the screen
saved = "All changes are saved." data["height"], data["width"] = screen.getmaxyx()
prompt = "Really quit? (y or n): "
# Clear the bottom line # Return the data as changes may have been made
stdscr.addstr(data["height"] - 1, 0, " " * (data["width"] - 1), curses.color_pair(1)) return data
def clear_line(screen, data, line):
# Clear the specified line
screen.addstr(line, 0, " " * (data["width"] - 1), curses.color_pair(1))
def prompt(screen, data, text):
# Print the prompt # Print the prompt
stdscr.addstr(data["height"] - 1, 0, prompt, curses.color_pair(11)) screen.addstr(data["height"] - 1, 0, text, 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() return screen.getch()
def goodbye(screen, data):
# Create a goodbye prompt
key = prompt(screen, data, "Really quit? (y or n): ")
# Clear the bottom line
clear_line(screen, data, data["height"] - 1)
if key == ord("y"): if key == ord("y"):
# Only exit if the key was "y", a confirmation # Only exit if the key was "y", a confirmation
exit() exit()
# Clear the bottom line again # Clear the bottom line again
stdscr.addstr(data["height"] - 1, 0, " " * (data["width"] - 1), curses.color_pair(1)) clear_line(screen, data, data["height"] - 1)
def error(stdscr, data, error_msg): def error(screen, data, error_msg):
# Print the error message to the bottom line # Print the error message to the bottom line
error_msg = f"ERROR: {error_msg}" error_msg = f"ERROR: {error_msg}"
stdscr.addstr(data["height"] - 1, 0, error_msg, curses.color_pair(3)) screen.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)) screen.addstr(data["height"] - 1, len(error_msg) + 1, "(press any key) ", curses.color_pair(1))
# Wait for a key to be pressed # Wait for a key to be pressed
stdscr.getch() screen.getch()
# Clear the bottom line # Clear the bottom line
stdscr.addstr(data["height"] - 1, 0, " " * (data["width"] - 1), curses.color_pair(1)) clear_line(screen, data, data["height"] - 1)

View File

@ -1,9 +1,9 @@
import curses import curses
def start_screen(stdscr): def start_screen(screen):
# Get window height and width # Get window height and width
height, width = stdscr.getmaxyx() height, width = screen.getmaxyx()
# Startup text # Startup text
title = "λ Lambda" title = "λ Lambda"
@ -20,10 +20,10 @@ def start_screen(stdscr):
start_y = int((height // 2) - 2) start_y = int((height // 2) - 2)
# Rendering title # Rendering title
stdscr.addstr(start_y, start_x_title, title, curses.color_pair(7) | curses.A_BOLD) screen.addstr(start_y, start_x_title, title, curses.color_pair(7) | curses.A_BOLD)
# Print the subtext # Print the subtext
for text in subtext: for text in subtext:
start_y += 1 start_y += 1
start_x = int((width // 2) - (len(text) // 2) - len(text) % 2) start_x = int((width // 2) - (len(text) // 2) - len(text) % 2)
stdscr.addstr(start_y, start_x, text) screen.addstr(start_y, start_x, text)

2
lambda
View File

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

View File

@ -4,7 +4,7 @@ import curses
import argparse import argparse
def start(stdscr, buffer_name, buffer_list): def start(screen, buffer_name, buffer_list):
# Initialise data before starting # Initialise data before starting
data = { data = {
"cursor_y": 0, "cursor_y": 0,
@ -16,6 +16,8 @@ def start(stdscr, buffer_name, buffer_list):
"info_bar": [" "], "info_bar": [" "],
"buffer_name": buffer_name, "buffer_name": buffer_name,
"buffer_list": buffer_list, "buffer_list": buffer_list,
"visible_y": 0,
"visible_x": 0,
"statusbar_theme": "filled" "statusbar_theme": "filled"
} }
@ -27,22 +29,28 @@ def start(stdscr, buffer_name, buffer_list):
# Start the screen # Start the screen
if data["buffer_name"] == "[No Name]": if data["buffer_name"] == "[No Name]":
welcome.start_screen(stdscr) welcome.start_screen(screen)
# Main loop # Main loop
while True: while True:
# Get the height and width of the screen # Get the height and width of the screen
data["height"], data["width"] = stdscr.getmaxyx() data["height"], data["width"] = screen.getmaxyx()
# Write the buffer to the screen # Write the buffer to the screen
buffer.write_buffer(stdscr, data) buffer.write_buffer(screen, data)
# Activate the next mode # Activate the next mode
data = mode.activate(stdscr, data) data = mode.activate(screen, data)
# Write the buffer to the screen
buffer.write_buffer(screen, data)
# Refresh and clear the screen # Refresh and clear the screen
stdscr.refresh() screen.refresh()
stdscr.clear() screen.clear()
# Write the buffer to the screen
buffer.write_buffer(screen, data)
def main(): def main():

View File

@ -2,10 +2,10 @@ from core import statusbar, utils
import curses import curses
def execute(stdscr, data, commands): def execute(screen, 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 data
for command in commands: for command in commands:
if command == "w": if command == "w":
@ -14,9 +14,10 @@ def execute(stdscr, data, commands):
elif command == "q": elif command == "q":
# Goodbye prompt # Goodbye prompt
utils.goodbye(stdscr, data) utils.goodbye(screen, data)
elif command == "t": elif command == "t":
# Theme switcher
if data["statusbar_theme"] == "filled": if data["statusbar_theme"] == "filled":
data["statusbar_theme"] = "bare" data["statusbar_theme"] = "bare"
@ -24,29 +25,29 @@ def execute(stdscr, data, commands):
data["statusbar_theme"] = "filled" data["statusbar_theme"] = "filled"
else: else:
utils.error(stdscr, data, f"Not an editor command: '{command}'") utils.error(screen, data, f"Not an editor command: '{command}'")
return data return data
def activate(stdscr, data): def activate(screen, data):
# Initialise variables # Initialise variables
commands = [] commands = []
# Visibly switch to command mode # Visibly switch to command mode
statusbar.refresh(stdscr, data) statusbar.refresh(screen, data)
stdscr.addstr(data["height"]-1, 0, ":") screen.addstr(data["height"]-1, 0, ":")
stdscr.move(data["height"]-1, 1) screen.move(data["height"]-1, 1)
# Main loop # Main loop
while True: while True:
# Get a key inputted by the user # Get a key inputted by the user
key = stdscr.getch() key = screen.getch()
# 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(data["height"]-1, 1, " " * len(commands)) screen.addstr(data["height"]-1, 1, " " * len(commands))
if commands: if commands:
# Subtract a character # Subtract a character
@ -63,10 +64,10 @@ def activate(stdscr, data):
elif key in (curses.KEY_ENTER, ord('\n'), ord('\r'), ord(":"), ord(";")): elif key in (curses.KEY_ENTER, ord('\n'), ord('\r'), ord(":"), ord(";")):
# Execute commands # Execute commands
data = execute(stdscr, data, commands) data = execute(screen, data, commands)
# Clear the bottom bar # Clear the bottom bar
stdscr.addstr(data["height"] - 1, 0, " " * (data["width"] - 1)) screen.addstr(data["height"] - 1, 0, " " * (data["width"] - 1))
# Return to normal mode after executing a command # Return to normal mode after executing a command
data["mode"] = "normal" data["mode"] = "normal"
@ -83,7 +84,7 @@ def activate(stdscr, data):
friendly_command = "".join(commands) friendly_command = "".join(commands)
# Write the commands to the screen # Write the commands to the screen
stdscr.addstr(data["height"]-1, 1, friendly_command) screen.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(data["height"]-1, len(commands)+1) screen.move(data["height"]-1, len(commands)+1)

View File

@ -5,22 +5,22 @@ def execute(data, key):
return data return data
def activate(stdscr, data): def activate(screen, data):
# Refresh the status bar with a different colour for insert # Refresh the status bar with a different colour for insert
data["statusbar_colors"] = [8, 12, 14, 2] data["statusbar_colors"] = [8, 12, 14, 2]
statusbar.refresh(stdscr, data) statusbar.refresh(screen, data)
# Refresh the status bar # Refresh the status bar
statusbar.refresh(stdscr, data) statusbar.refresh(screen, data)
# Move the cursor # Move the cursor
cursor.move(stdscr, data) cursor.move(screen, data)
# Switch to a line cursor # Switch to a line cursor
cursor.cursor_mode("line") cursor.cursor_mode("line")
# Wait for and capture a key press from the user # Wait for and capture a key press from the user
key = stdscr.getch() key = screen.getch()
# Exit insert mode # Exit insert mode
if key == 27: if key == 27:

View File

@ -1,4 +1,5 @@
from core import statusbar, cursor from core import statusbar, cursor
import curses
def execute(data, key): def execute(data, key):
@ -34,18 +35,18 @@ def execute(data, key):
return data return data
def activate(stdscr, data): def activate(screen, data):
# Refresh the status bar # Refresh the status bar
statusbar.refresh(stdscr, data) statusbar.refresh(screen, data)
# Move the cursor # Move the cursor
cursor.move(stdscr, data) cursor.move(screen, data)
# Switch the cursor to a block # Switch the cursor to a block
cursor.cursor_mode("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 = screen.getch()
# Check against the keybindings # Check against the keybindings
data = execute(data, key) data = execute(data, key)