diff --git a/BREAKING_CHANGES.txt b/BREAKING_CHANGES.txt index ba6219fd1e..5cd41b12c2 100644 --- a/BREAKING_CHANGES.txt +++ b/BREAKING_CHANGES.txt @@ -7,6 +7,8 @@ and when the change was applied given the delay between changes being submitted and the time they were reviewed and merged. --- +* 2024-11-24 Deprecated a bunch of symbol commands to insert delimited pairs + ("", '', []) in favor of the new `delimiter_pair` Talon list file. * 2024-09-07 Removed `get_list_from_csv` from `user_settings.py`. Please use the new `track_csv_list` decorator, which leverages Talon's `talon.watch` API for robustness on Talon launch. diff --git a/core/edit/delimiter_pair.py b/core/edit/delimiter_pair.py new file mode 100644 index 0000000000..7b3795eb4e --- /dev/null +++ b/core/edit/delimiter_pair.py @@ -0,0 +1,27 @@ +from talon import Module, actions + +mod = Module() + +mod.list("delimiter_pair", "List of matching pair delimiters") + + +@mod.capture(rule="{user.delimiter_pair}") +def delimiter_pair(m) -> list[str]: + pair = m.delimiter_pair.split() + assert len(pair) == 2 + # "space" requires a special written form because Talon lists are whitespace insensitive + open = pair[0] if pair[0] != "space" else " " + close = pair[1] if pair[1] != "space" else " " + return [open, close] + + +@mod.action_class +class Actions: + def delimiter_pair_insert(pair: list[str]): + """Insert a delimiter pair leaving the cursor in the middle""" + actions.user.insert_between(pair[0], pair[1]) + + def delimiter_pair_wrap_selection(pair: list[str]): + """Wrap selection with delimiter pair """ + selected = actions.edit.selected_text() + actions.user.insert_between(pair[0], pair[1], selected) diff --git a/core/edit/delimiter_pair.talon-list b/core/edit/delimiter_pair.talon-list new file mode 100644 index 0000000000..51aecaa6fa --- /dev/null +++ b/core/edit/delimiter_pair.talon-list @@ -0,0 +1,20 @@ +list: user.delimiter_pair +- + +# SPOKEN_FORM: LEFT_DELIMITER RIGHT_DELIMITER. Example: "round: ( )" +# Special case for whitespace "space". Example: "pad: space space" + +round: ( ) +box: [ ] +diamond: < > +curly: { } +twin: "' '" +quad: '" "' +skis: ` ` +percentages: % % +pad: space space + +escaped quad: '\\" \\"' +escaped twin: "\\' \\'" +escaped round: \( \) +escaped box: \[ \] diff --git a/core/edit/edit.talon b/core/edit/edit.talon index bfb3131f3f..99e7f22f0f 100644 --- a/core/edit/edit.talon +++ b/core/edit/edit.talon @@ -125,7 +125,7 @@ new line above: edit.line_insert_up() new line below | slap: edit.line_insert_down() # Insert padding with optional symbols -(pad | padding): user.insert_between(" ", " ") +(padding): user.insert_between(" ", " ") (pad | padding) +: insert(" ") user.insert_many(symbol_key_list) diff --git a/core/edit/edit_command_actions.py b/core/edit/edit_command_actions.py index 9239ea5a0f..3a0f9f3faa 100644 --- a/core/edit/edit_command_actions.py +++ b/core/edit/edit_command_actions.py @@ -1,36 +1,68 @@ from dataclasses import dataclass -from typing import Callable +from typing import Callable, Union from talon import Module, actions +# "simple" actions are actions that don't require any arguments. Only a type. +# select, copy, delete, etc. @dataclass -class EditAction: +class EditSimpleAction: type: str + def __str__(self): + return self.type + @dataclass -class EditInsertAction(EditAction): +class EditInsertAction: type = "insert" text: str + def __str__(self): + return self.type + + +@dataclass +class EditWrapAction: + type = "wrapWithDelimiterPair" + pair: list[str] + + def __str__(self): + return self.type + @dataclass -class EditFormatAction(EditAction): +class EditFormatAction: type = "applyFormatter" formatters: str + def __str__(self): + return self.type + + +EditAction = Union[ + EditSimpleAction, + EditInsertAction, + EditWrapAction, + EditFormatAction, +] mod = Module() mod.list("edit_action", desc="Actions for the edit command") @mod.capture(rule="{user.edit_action}") -def edit_simple_action(m) -> EditAction: - return EditAction(m.edit_action) +def edit_simple_action(m) -> EditSimpleAction: + return EditSimpleAction(m.edit_action) + +@mod.capture(rule=" wrap") +def edit_wrap_action(m) -> EditWrapAction: + return EditWrapAction(m.delimiter_pair) -@mod.capture(rule="") + +@mod.capture(rule=" | ") def edit_action(m) -> EditAction: return m[0] @@ -62,6 +94,10 @@ def run_action_callback(action: EditAction): assert isinstance(action, EditInsertAction) actions.insert(action.text) + case "wrapWithDelimiterPair": + assert isinstance(action, EditWrapAction) + return lambda: actions.user.delimiter_pair_wrap_selection(action.pair) + case "applyFormatter": assert isinstance(action, EditFormatAction) actions.user.formatters_reformat_selection(action.formatters) diff --git a/core/edit/insert_between.py b/core/edit/insert_between.py index 75885092fb..2440546eac 100644 --- a/core/edit/insert_between.py +++ b/core/edit/insert_between.py @@ -5,8 +5,8 @@ @mod.action_class class module_actions: - def insert_between(before: str, after: str): + def insert_between(before: str, after: str, middle: str = ""): """Insert `before + after`, leaving cursor between `before` and `after`. Not entirely reliable if `after` contains newlines.""" - actions.insert(before + after) + actions.insert(f"{before}{middle}{after}") for _ in after: actions.edit.left() diff --git a/plugin/symbols/symbols.talon b/plugin/symbols/symbols.talon index 251407cc6b..a44fc5a3db 100644 --- a/plugin/symbols/symbols.talon +++ b/plugin/symbols/symbols.talon @@ -1,44 +1,15 @@ new line: "\n" double dash: "--" triple quote: "'''" -(triple grave | triple back tick | gravy): insert("```") +triple grave | triple back tick | gravy: "```" (dot dot | dotdot): ".." ellipsis: "..." (comma and | spamma): ", " arrow: "->" dub arrow: "=>" -empty dub string: user.insert_between('"', '"') -empty escaped (dub string | dub quotes): user.insert_between('\\"', '\\"') -empty string: user.insert_between("'", "'") -empty escaped string: user.insert_between("\\'", "\\'") -(inside parens | args): user.insert_between("(", ")") -inside (squares | brackets | square brackets | list): user.insert_between("[", "]") -inside (braces | curly brackets): user.insert_between("{", "}") -inside percent: user.insert_between("%", "%") -inside (quotes | string): user.insert_between("'", "'") -inside (double quotes | dub quotes): user.insert_between('"', '"') -inside (graves | back ticks): user.insert_between("`", "`") -angle that: - text = edit.selected_text() - user.paste("<{text}>") -(square | bracket | square bracket) that: - text = edit.selected_text() - user.paste("[{text}]") -(brace | curly bracket) that: - text = edit.selected_text() - user.paste("{{{text}}}") -(parens | args) that: - text = edit.selected_text() - user.paste("({text})") -percent that: - text = edit.selected_text() - user.paste("%{text}%") -quote that: - text = edit.selected_text() - user.paste("'{text}'") -(double quote | dub quote) that: - text = edit.selected_text() - user.paste('"{text}"') -(grave | back tick) that: - text = edit.selected_text() - user.paste("`{text}`") + +# Insert delimiter pairs +: user.delimiter_pair_insert(delimiter_pair) + +# Wrap selection with delimiter pairs + that: user.delimiter_pair_wrap_selection(delimiter_pair) diff --git a/plugin/symbols/symbols_deprecated.talon b/plugin/symbols/symbols_deprecated.talon new file mode 100644 index 0000000000..ea2a3c8587 --- /dev/null +++ b/plugin/symbols/symbols_deprecated.talon @@ -0,0 +1,83 @@ +empty dub string: + user.deprecate_command("2024-11-24", "empty dub string", "quad") + user.insert_between('"', '"') + +empty escaped (dub string | dub quotes): + user.deprecate_command("2024-11-24", "empty escaped (dub string | dub quotes)", "escaped quad") + user.insert_between('\\"', '\\"') + +empty string: + user.deprecate_command("2024-11-24", "empty string", "twin") + user.insert_between("'", "'") + +empty escaped string: + user.deprecate_command("2024-11-24", "empty escaped string", "escaped twin") + user.insert_between("\\'", "\\'") + +inside (parens | args): + user.deprecate_command("2024-11-24", "inside (parens | args)", "round") + user.insert_between("(", ")") + +inside (squares | brackets | square brackets | list): + user.deprecate_command("2024-11-24", "inside (squares | brackets | square brackets | list)", "box") + user.insert_between("[", "]") + +inside (braces | curly brackets): + user.deprecate_command("2024-11-24", "inside (braces | curly brackets)", "curly") + user.insert_between("{", "}") + +inside percent: + user.deprecate_command("2024-11-24", "inside percent", "percentages") + user.insert_between("%", "%") + +inside (quotes | string): + user.deprecate_command("2024-11-24", "inside (quotes | string)", "twin") + user.insert_between("'", "'") + +inside (double quotes | dub quotes): + user.deprecate_command("2024-11-24", "inside (double quotes | dub quotes)", "quad") + user.insert_between('"', '"') + +inside (graves | back ticks): + user.deprecate_command("2024-11-24", "inside (graves | back ticks)", "skis") + user.insert_between("`", "`") + +angle that: + user.deprecate_command("2024-11-24", "angle that", "diamond that") + text = edit.selected_text() + user.paste("<{text}>") + +(square | bracket | square bracket) that: + user.deprecate_command("2024-11-24", "(square | bracket | square bracket) that", "box that") + text = edit.selected_text() + user.paste("[{text}]") + +(brace | curly bracket) that: + user.deprecate_command("2024-11-24", "(brace | curly bracket) that", "curly that") + text = edit.selected_text() + user.paste("{{{text}}}") + +(parens | args) that: + user.deprecate_command("2024-11-24", "(parens | args) that", "round that") + text = edit.selected_text() + user.paste("({text})") + +percent that: + user.deprecate_command("2024-11-24", "percent that", "percentages that") + text = edit.selected_text() + user.paste("%{text}%") + +quote that: + user.deprecate_command("2024-11-24", "quote that", "twin that") + text = edit.selected_text() + user.paste("'{text}'") + +(double quote | dub quote) that: + user.deprecate_command("2024-11-24", "(double quote | dub quote) that", "quad that") + text = edit.selected_text() + user.paste('"{text}"') + +(grave | back tick) that: + user.deprecate_command("2024-11-24", "(grave | back tick) that", "skis that") + text = edit.selected_text() + user.paste("`{text}`")