Velocity/core/utils.py

210 lines
6.0 KiB
Python
Raw Normal View History

2022-03-14 07:21:55 +00:00
import curses
2022-03-19 19:19:25 +00:00
import json
import os
2022-04-17 10:48:40 +01:00
import sys
import traceback
from pathlib import Path
2022-04-17 13:32:35 +01:00
from core import cursors
2022-04-17 13:33:41 +01:00
from core.colors import Codes as Col
2022-03-19 19:19:25 +00:00
def gracefully_exit():
# Close the curses window
curses.endwin()
# Finally, exit the program
sys.exit()
def clear_line(instance, y: int, x: int):
# Clear the line at the screen at position y, x
instance.screen.insstr(y, x, " " * (instance.width - x))
def pause_screen(message: str):
2022-04-16 17:18:02 +01:00
# End the curses session
curses.endwin()
# Print the message and wait for enter key
input(f"{message}\n\n Press enter to continue...")
2022-04-17 13:32:35 +01:00
def load_file(file_path: str) -> dict:
# load the json file with read permissions
2022-04-17 13:32:35 +01:00
with open(file_path, "r") as f:
2022-03-19 19:19:25 +00:00
return json.load(f)
2022-04-17 13:32:35 +01:00
def save_file(instance, file_path: str, data: list):
# Save the data to the file
2022-04-17 13:32:35 +01:00
with open(file_path, "w") as f:
try:
# For each line in the file
for index, line in enumerate(data):
if index == len(data) - 1:
# If this is the last line, write it without a newline
f.write(line)
else:
# Otherwise, write the line with a newline
f.write(f"{line}\n")
except (OSError, IOError):
# If the file could not be written, show an error message
2022-04-17 13:32:35 +01:00
error(instance, f"File {file_path} could not be saved.")
def load_config_file() -> dict:
2022-03-19 19:19:25 +00:00
# Parse the path of the config file
2022-04-17 13:32:35 +01:00
config_file_path = f"{Path.home()}/.config/lambda/config.json"
2022-03-19 19:19:25 +00:00
# Only if the config file exists, attempt to load it
2022-04-17 13:32:35 +01:00
if os.path.exists(config_file_path):
# Return the loaded config
2022-04-17 13:32:35 +01:00
return load_file(config_file_path)
2022-03-14 07:21:55 +00:00
def welcome(instance):
2022-03-19 15:13:28 +00:00
# Startup text
title = "λ Lambda"
subtext = [
"Next generation hackable text editor for nerds",
"",
"Type :h to open the README.md document",
"Type :o <file> to open a file and edit",
"Type :q or <C-c> to quit lambda.py"
]
2022-03-14 07:21:55 +00:00
2022-03-19 15:13:28 +00:00
# Centering calculations
start_x_title = int((instance.safe_width // 2) - (len(title) // 2) - len(title) % 2 + 2)
start_y = int((instance.safe_height // 2) - 1)
2022-03-15 22:12:52 +00:00
2022-03-19 15:13:28 +00:00
# Rendering title
instance.screen.addstr(start_y, start_x_title, title, curses.color_pair(7) | curses.A_BOLD)
2022-03-14 07:21:55 +00:00
2022-03-19 15:13:28 +00:00
# Print the subtext
for text in subtext:
start_y += 1
start_x = int((instance.safe_width // 2) - (len(text) // 2) - len(text) % 2 + 2)
instance.screen.addstr(start_y, start_x, text)
2022-03-17 21:47:19 +00:00
2022-03-19 15:13:28 +00:00
def prompt(instance, message: str, color: int = 1) -> (list, None):
# Initialise the input list
inp = []
2022-03-17 21:47:19 +00:00
# Write whitespace over characters to refresh it
clear_line(instance, instance.height - 1, len(message) + len(inp) - 1)
2022-03-19 15:13:28 +00:00
# Write the message to the screen
instance.screen.addstr(instance.height - 1, 0, message, curses.color_pair(color))
2022-03-14 07:21:55 +00:00
2022-03-19 15:13:28 +00:00
while True:
# Wait for a keypress
key = instance.screen.getch()
2022-03-14 07:21:55 +00:00
2022-03-19 15:13:28 +00:00
# Subtracting a key (backspace)
if key in (curses.KEY_BACKSPACE, 127, '\b'):
# Write whitespace over characters to refresh it
clear_line(instance, instance.height - 1, len(message) + len(inp) - 1)
2022-03-14 07:21:55 +00:00
2022-03-19 15:13:28 +00:00
if inp:
# Subtract a character from the input list
inp.pop()
2022-03-14 07:21:55 +00:00
2022-03-19 15:13:28 +00:00
else:
# Exit the prompt without returning the input
return None
2022-03-15 15:37:38 +00:00
2022-03-19 15:13:28 +00:00
elif key == 27:
# Exit the prompt, without returning the input
return None
2022-03-15 15:37:38 +00:00
2022-03-19 15:13:28 +00:00
elif key in (curses.KEY_ENTER, ord('\n'), ord('\r'), ord(":"), ord(";")):
# Return the input list
return inp
else:
# If any other key is typed, append it
# As long as the key is in the valid list
2022-04-17 13:32:35 +01:00
valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!/_-0123456789 "
2022-03-19 15:13:28 +00:00
if chr(key) in valid and len(inp) < (instance.width - 2):
inp.append(chr(key))
# Refresh the screen
instance.refresh()
2022-03-19 15:13:28 +00:00
# Write the message to the screen
instance.screen.addstr(instance.height - 1, 0, message, curses.color_pair(color))
# Join the input together for visibility on the screen
input_text = "".join(inp)
# Write the input text to the screen
instance.screen.addstr(instance.height - 1, len(message), input_text)
2022-04-17 14:12:33 +01:00
def press_key_to_continue(instance, message: str, color: int = 1):
2022-04-16 17:18:02 +01:00
# Hide the cursor
2022-04-17 13:32:35 +01:00
cursors.mode("hidden")
2022-03-19 15:13:28 +00:00
2022-04-16 17:18:02 +01:00
# Clear the bottom of the screen
clear_line(instance, instance.height - 1, 0)
2022-03-19 15:13:28 +00:00
# Write the entire message to the screen
2022-04-17 14:12:33 +01:00
instance.screen.addstr(instance.height - 1, 0, message, curses.color_pair(color))
2022-04-16 17:18:02 +01:00
instance.screen.addstr(instance.height - 1, len(message) + 1, f"(press any key)")
2022-03-19 15:13:28 +00:00
# Wait for a keypress
instance.screen.getch()
# Clear the bottom of the screen
clear_line(instance, instance.height - 1, 0)
2022-03-19 16:34:48 +00:00
2022-04-16 17:18:02 +01:00
# Show the cursor
2022-04-17 13:32:35 +01:00
cursors.mode("visible")
2022-04-16 17:18:02 +01:00
def error(instance, message: str):
# Parse the error message
error_message = f"ERROR: {message}"
# Create a prompt
2022-04-17 10:48:40 +01:00
press_key_to_continue(instance, error_message, 3)
2022-04-16 17:18:02 +01:00
2022-03-19 16:34:48 +00:00
def fatal_error(exception: Exception):
2022-04-16 17:18:02 +01:00
# End the curses session
2022-03-19 16:34:48 +00:00
curses.endwin()
# Print the error message and traceback
2022-04-17 10:48:40 +01:00
print(f"{Col.red}FATAL ERROR:{Col.end} "
f"{Col.yellow}{exception}{Col.end}\n")
2022-03-19 16:34:48 +00:00
print(traceback.format_exc())
# Exit, with an error exit code
sys.exit(0)
2022-04-16 17:18:02 +01:00
def goodbye(instance):
2022-04-17 15:52:15 +01:00
try:
# Confirm before exiting
choice = prompt(instance, "Really quit lambda? (y/n): ", 11)
2022-04-16 17:18:02 +01:00
2022-04-17 15:52:15 +01:00
# If the user confirms, exit
if choice and choice[0] == "y":
gracefully_exit()
# Clear the prompt if the user cancels
else:
clear_line(instance, instance.height - 1, 0)
2022-04-17 15:52:15 +01:00
except KeyboardInterrupt:
# If the user presses Ctrl+C, just exit
gracefully_exit()
except Exception as exception:
# If there is an error, print the error message and traceback
fatal_error(exception)