From 6b80be6f31b6b43e87850a0d209f25c2a1e561be Mon Sep 17 00:00:00 2001 From: Jeff Knaus Date: Mon, 24 Jul 2023 14:10:14 -0600 Subject: [PATCH] merge in #1199 --- apps/emacs/emacs_commands.py | 11 ++--- core/create_spoken_forms.py | 45 ++++++++++++------- core/file_extension/file_extension.py | 13 +++--- core/system_paths.py | 10 ++--- core/user_settings.py | 63 +++++++++++++++++---------- lang/talon/talon.py | 7 +-- 6 files changed, 84 insertions(+), 65 deletions(-) diff --git a/apps/emacs/emacs_commands.py b/apps/emacs/emacs_commands.py index 9618b7c388..712689f96d 100644 --- a/apps/emacs/emacs_commands.py +++ b/apps/emacs/emacs_commands.py @@ -33,10 +33,9 @@ def emacs_command_short_form(command_name: str) -> Optional[str]: return emacs_commands.get(command_name, Command(command_name)).short -def load_csv(): - filepath = Path(__file__).parents[0] / "emacs_commands.csv" - with resource.open(filepath) as f: - rows = list(csv.reader(f)) +@resource.watch("emacs_commands.csv") +def load_commands(f): + rows = list(csv.reader(f)) # Check headers assert rows[0] == ["Command", " Key binding", " Short form", " Spoken form"] @@ -70,7 +69,3 @@ def load_csv(): if c.spoken: command_list[c.spoken] = c.name ctx.lists["self.emacs_command"] = command_list - - -# TODO: register on change to file! -app.register("ready", load_csv) diff --git a/core/create_spoken_forms.py b/core/create_spoken_forms.py index 02fe77b098..ab731ab06d 100644 --- a/core/create_spoken_forms.py +++ b/core/create_spoken_forms.py @@ -7,33 +7,46 @@ from talon import Module, actions from .abbreviate.abbreviate import abbreviations_list -from .file_extension.file_extension import file_extensions from .keys.keys import symbol_key_words from .numbers.numbers import digits_map, scales, teens, tens +from .user_settings import track_csv_list mod = Module() - DEFAULT_MINIMUM_TERM_LENGTH = 2 EXPLODE_MAX_LEN = 3 FANCY_REGULAR_EXPRESSION = r"[A-Z]?[a-z]+|[A-Z]+(?![a-z])|[0-9]+" -FILE_EXTENSIONS_REGEX = "|".join( - re.escape(file_extension.strip()) + "$" - for file_extension in file_extensions.values() -) SYMBOLS_REGEX = "|".join(re.escape(symbol) for symbol in set(symbol_key_words.values())) -REGEX_NO_SYMBOLS = re.compile( - "|".join( - [ - FANCY_REGULAR_EXPRESSION, - FILE_EXTENSIONS_REGEX, - ] +FILE_EXTENSIONS_REGEX = r'^\b$' +file_extensions = {} + +def update_regex(): + global REGEX_NO_SYMBOLS + global REGEX_WITH_SYMBOLS + REGEX_NO_SYMBOLS = re.compile( + "|".join( + [ + FANCY_REGULAR_EXPRESSION, + FILE_EXTENSIONS_REGEX, + ] + ) + ) + REGEX_WITH_SYMBOLS = re.compile( + "|".join([FANCY_REGULAR_EXPRESSION, FILE_EXTENSIONS_REGEX, SYMBOLS_REGEX]) ) -) -REGEX_WITH_SYMBOLS = re.compile( - "|".join([FANCY_REGULAR_EXPRESSION, FILE_EXTENSIONS_REGEX, SYMBOLS_REGEX]) -) +update_regex() + +@track_csv_list("file_extensions.csv", headers=("File extension", "Name")) +def on_extensions(values): + global FILE_EXTENSIONS_REGEX + global file_extensions + file_extensions = values + FILE_EXTENSIONS_REGEX = "|".join( + re.escape(file_extension.strip()) + "$" + for file_extension in values.values() + ) + update_regex() REVERSE_PRONUNCIATION_MAP = { **{str(value): key for key, value in digits_map.items()}, diff --git a/core/file_extension/file_extension.py b/core/file_extension/file_extension.py index 04a1c23ca2..944ea89ede 100644 --- a/core/file_extension/file_extension.py +++ b/core/file_extension/file_extension.py @@ -1,6 +1,6 @@ from talon import Context, Module -from ..user_settings import get_list_from_csv +from ..user_settings import track_csv_list mod = Module() mod.list("file_extension", desc="A file extension, such as .py") @@ -55,11 +55,8 @@ "dot log": ".log", } -file_extensions = get_list_from_csv( - "file_extensions.csv", - headers=("File extension", "Name"), - default=_file_extensions_defaults, -) - ctx = Context() -ctx.lists["self.file_extension"] = file_extensions + +@track_csv_list("file_extensions.csv", headers=("File extension", "Name"), default=_file_extensions_defaults) +def on_update(values): + ctx.lists["self.file_extension"] = values diff --git a/core/system_paths.py b/core/system_paths.py index c6a7564649..b7e6a5de54 100644 --- a/core/system_paths.py +++ b/core/system_paths.py @@ -7,7 +7,7 @@ from talon import Context, Module, actions, app -from .user_settings import get_list_from_csv +from .user_settings import track_csv_list mod = Module() ctx = Context() @@ -49,11 +49,9 @@ def on_ready(): default_system_paths.update(onedrive_paths) - system_paths = get_list_from_csv( - "system_paths.csv", headers=("Path", "Spoken"), default=default_system_paths - ) - - ctx.lists["user.system_paths"] = system_paths + @track_csv_list("system_paths.csv", headers=("Path", "Spoken"), default=default_system_paths) + def on_csv(system_paths): + ctx.lists["user.system_paths"] = system_paths @mod.capture(rule="{user.system_paths}") diff --git a/core/user_settings.py b/core/user_settings.py index 2630f2c518..017f15ef4c 100644 --- a/core/user_settings.py +++ b/core/user_settings.py @@ -1,35 +1,20 @@ +from pathlib import Path +from typing import Callable, IO import csv import os -from pathlib import Path from talon import resource # NOTE: This method requires this module to be one folder below the top-level # community/knausj folder. SETTINGS_DIR = Path(__file__).parents[1] / "settings" +SETTINGS_DIR.mkdir(exist_ok=True) -if not SETTINGS_DIR.is_dir(): - os.mkdir(SETTINGS_DIR) - - -def get_list_from_csv( - filename: str, headers: tuple[str, str], default: dict[str, str] = {} -): - """Retrieves list from CSV""" - path = SETTINGS_DIR / filename - assert filename.endswith(".csv") - - if not path.is_file(): - with open(path, "w", encoding="utf-8", newline="") as file: - writer = csv.writer(file) - writer.writerow(headers) - for key, value in default.items(): - writer.writerow([key] if key == value else [value, key]) +CallbackT = Callable[[dict[str, str]], None] +DecoratorT = Callable[[CallbackT], CallbackT] - # Now read via resource to take advantage of talon's - # ability to reload this script for us when the resource changes - with resource.open(str(path), "r") as f: - rows = list(csv.reader(f)) +def read_csv_list(f: IO, headers: tuple[str, str]) -> dict[str, str]: + rows = list(csv.reader(f)) # print(str(rows)) mapping = {} @@ -59,6 +44,40 @@ def get_list_from_csv( return mapping +def write_csv_defaults(path: Path, headers: tuple[str, str], default: dict[str, str]=None) -> None: + if not path.is_file() and default is not None: + with open(path, "w", encoding="utf-8", newline="") as file: + writer = csv.writer(file) + writer.writerow(headers) + for key, value in default.items(): + writer.writerow([key] if key == value else [value, key]) + +def track_csv_list(filename: str, headers: tuple[str, str], default: dict[str, str]=None) -> DecoratorT: + assert filename.endswith(".csv") + path = SETTINGS_DIR / filename + write_csv_defaults(path, headers, default) + + def decorator(fn: CallbackT) -> CallbackT: + @resource.watch(str(path)) + def on_update(f): + data = read_csv_list(f, headers) + fn(data) + + return decorator + +# NOTE: this is deprecated, use @track_csv_list instead. +def get_list_from_csv( + filename: str, headers: tuple[str, str], default: dict[str, str] = {} +): + """Retrieves list from CSV""" + assert filename.endswith(".csv") + path = SETTINGS_DIR / filename + write_csv_defaults(path, headers, default) + + # Now read via resource to take advantage of talon's + # ability to reload this script for us when the resource changes + with resource.open(str(path), "r") as f: + return read_csv_list(f, headers) def append_to_csv(filename: str, rows: dict[str, str]): path = SETTINGS_DIR / filename diff --git a/lang/talon/talon.py b/lang/talon/talon.py index 632360084e..864f528751 100644 --- a/lang/talon/talon.py +++ b/lang/talon/talon.py @@ -54,11 +54,8 @@ def on_update_decls(decls): "modes", ]: l = getattr(decls, thing) - ctx_talon_lists.lists[ - f"user.talon_{thing}" - ] = actions.user.create_spoken_forms_from_list( - l.keys(), generate_subsequences=False - ) + values = actions.user.create_spoken_forms_from_list(l.keys(), generate_subsequences=False) + ctx_talon_lists.lists[f"user.talon_{thing}"] = values # print( # "List: {} \n {}".format(thing, str(ctx_talon_lists.lists[f"user.talon_{thing}"])) # )