From 164632b7955e6d7c1d8813db8a6ba19ebb3e20f9 Mon Sep 17 00:00:00 2001 From: Pokey Rule Date: Fri, 8 Oct 2021 13:12:33 +0100 Subject: [PATCH 1/2] Patch develop (#89) * Use correct rect setter (#81) * Tweak shape docs (#88) * Tried new SVGs * Revert branch Co-authored-by: Stefan du Fresne --- docs/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/README.md b/docs/README.md index 68e14b61..48265ee3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -106,18 +106,18 @@ minimize syllables. The following shapes are supported: -| Spoken form | Internal ID | Shape | Enabled by default? | -| -------------- | ------------ | -------------------------------------------------------------------------------------------------------- | ------------------- | -| `"ex"` | `ex` | ![Ex](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/ex.svg) | ❌ | -| `"fox"` | `fox` | ![Fox](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/fox.svg) | ❌ | -| `"wing"` | `wing` | ![Wing](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/wing.svg) | ❌ | -| `"hole"` | `hole` | ![Hole](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/hole.svg) | ❌ | -| `"frame"` | `frame` | ![Frame](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/frame.svg) | ❌ | -| `"curve"` | `curve` | ![Curve](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/curve.svg) | ❌ | -| `"eye"` | `eye` | ![Eye](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/eye.svg) | ❌ | -| `"play"` | `play` | ![Play](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/play.svg) | ❌ | -| `"crosshairs"` | `crosshairs` | ![Crosshairs](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/crosshairs.svg) | ❌ | -| `"bolt"` | `bolt` | ![Bolt](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/bolt.svg) | ❌ | +| Spoken form | Internal ID | Shape | Enabled by default? | +| ----------- | ------------ | -------------------------------------------------------------------------------------------------------- | ------------------- | +| `"ex"` | `ex` | ![Ex](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/ex.svg) | ❌ | +| `"fox"` | `fox` | ![Fox](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/fox.svg) | ❌ | +| `"wing"` | `wing` | ![Wing](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/wing.svg) | ❌ | +| `"hole"` | `hole` | ![Hole](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/hole.svg) | ❌ | +| `"frame"` | `frame` | ![Frame](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/frame.svg) | ❌ | +| `"curve"` | `curve` | ![Curve](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/curve.svg) | ❌ | +| `"eye"` | `eye` | ![Eye](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/eye.svg) | ❌ | +| `"play"` | `play` | ![Play](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/play.svg) | ❌ | +| `"cross"` | `crosshairs` | ![Crosshairs](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/crosshairs.svg) | ❌ | +| `"bolt"` | `bolt` | ![Bolt](https://raw.githubusercontent.com/pokey/cursorless-vscode/main/images/hats/bolt.svg) | ❌ | You can enable or disable shapes in your VSCode settings, by searching for `cursorless.hatEnablement.shapes` and checking the box next to the internal ID for the given shape as listed above. From 20471f0104946c35ef34b761e85c7e2a0b027d6e Mon Sep 17 00:00:00 2001 From: Pokey Rule Date: Mon, 25 Oct 2021 13:14:58 +0100 Subject: [PATCH 2/2] Snippet wrap (#94) * Initial working version * Working draft * Fixes * Allow user snippets * Cleanup * Add docs and make experimental * Switch to tag to enable experimental support * tweak snippet name * Fix docs * Doc fix * More doc * Doc * New snippets; fix watcher * Use `.` instead of `/` for snippet placeholders * Update docs * Fix link in docs * Change tag name * Update docs --- docs/README.md | 5 ++ docs/experimental.md | 97 ++++++++++++++++++++++++++++++++++ docs/images/tryWrapFine.gif | Bin 0 -> 46451 bytes src/actions/wrap.py | 69 ++++++++++++++++++++++-- src/csv_overrides.py | 102 +++++++++++++++++++++++++++--------- src/cursorless.talon | 4 +- 6 files changed, 246 insertions(+), 31 deletions(-) create mode 100644 docs/experimental.md create mode 100644 docs/images/tryWrapFine.gif diff --git a/docs/README.md b/docs/README.md index 48265ee3..b598311c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -42,6 +42,7 @@ Note: If you'd like to customize any of the spoken forms, please see the [docume - [Scroll](#scroll) - [Insert/Use/Repeat](#insertuserepeat) - [Wrap](#wrap) + - [\[experimental\] Wrap with snippet](#experimental-wrap-with-snippet) - [Show definition/reference/quick fix](#show-definitionreferencequick-fix) - [Fold/unfold](#foldunfold) - [Extract](#extract) @@ -382,6 +383,10 @@ eg: `square wrap blue air` Wraps the token containing letter 'a' with a blue hat in square brackets. +#### \[experimental\] Wrap with snippet + +See [experimental documentation](experimental.md#wrapper-snippets). + ### Show definition/reference/quick fix - `"def show"` diff --git a/docs/experimental.md b/docs/experimental.md new file mode 100644 index 00000000..9162e7c5 --- /dev/null +++ b/docs/experimental.md @@ -0,0 +1,97 @@ +# Experimental features + +Here we document features which are currently considered experimental. They are generally functional and well tested, but the API is subject change. + +## Wrapper snippets + +![Wrapper snippet demo](images/tryWrapFine.gif) + +In addition to wrapping with paired delimiters (eg `"square wrap"`, `"round wrap"`, etc), we experimentally support wrapping with snippets. Cursorless ships with a few built-in snippets, but users can also use their own snippets. + +### Enabling wrapper snippets + +Add the following line to the end of your `settings.talon` (or any other `.talon` file that will be active when vscode is focused): + +``` +tag(): user.cursorless_experimental_snippets +``` + +### Using wrapper snippets + +#### Command syntax + +The command syntax is as follows: + +``` +" wrap " +``` + +#### Examples + +- `"try wrap air"`: Wrap the statement containing the marked `a` in a try-catch statement +- `"try wrap air past bat"`: Wrap the sequence of statements from the marked `a` to the marked `b` in a try-catch statement + +#### Default scope types + +Each snippet wrapper has a default scope type. When you refer to a target, by default it will expand to the given scope type. This way, for example, when you say `"try wrap air"`, it will refer to the statement containing `a` rather than just the token. + +### Built-in wrapper snippets + +| Default spoken form | Snippet | Default target scope type | +| ------------------- | --------------------------------------------- | ------------------------- | +| `"if wrap"` | If statement | Statement | +| `"else wrap"` | If-else statement; target goes in else branch | Statement | +| `"if else wrap"` | If-else statement; target goes in if branch | Statement | +| `"try wrap"` | Try-catch statement | Statement | + +### Customizing spoken forms + +As usual, the spoken forms for these wrapper snippets can be [customized by csv](customization.md). The csvs are in the file `cursorless-settings/experimental/wrapper_snippets.csv`. + +### Adding your own snippets + +To define your own wrapper snippets, proceed as follows: + +#### Define snippets in vscode + +1. Set the `cursorless.experimental.snippetsDir` setting to a directory in which you'd like to create your snippets. +2. Add snippets to the directory in files ending in `.cursorless-snippets`. See the [documentation](https://github.com/pokey/cursorless-vscode/tree/main/docs/experimental/snippets.md) for the cursorless snippet format. + +#### 2. Add snippet to spoken forms csv + +For each snippet that you'd like to be able to use as a wrapper snippet, add a line to the `cursorless-settings/experimental/wrapper_snippets.csv` csv overrides file. The first column is the desired spoken form, and the second column is of the form `.`, where `name` is the name of the snippet (ie the key in your snippet json file), and `variable` is one of the placeholder variables in your snippet where the target should go. + +### Customizing built-in snippets + +To customize a built-in snippet, just define a custom snippet (as above), but +use the same name as the cursorless core snippet you'd like to change, and give +definitions along with scopes where you'd like your override to be active. Here +is an example: + +```json +{ + "tryCatchStatement": { + "definitions": [ + { + "scope": { + "langIds": [ + "typescript", + "typescriptreact", + "javascript", + "javascriptreact" + ] + }, + "body": [ + "try {", + "\t$body", + "} catch (err) {", + "\t$exceptBody", + "}" + ] + } + ] + } +} +``` + +The above will change the definition of the try-catch statement in typescript. diff --git a/docs/images/tryWrapFine.gif b/docs/images/tryWrapFine.gif new file mode 100644 index 0000000000000000000000000000000000000000..d02b9e13c30ca00d98ec085a955b7953e8bad95f GIT binary patch literal 46451 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 list[str]: - paired_delimiter_info = paired_delimiters_map[m.cursorless_paired_delimiter] - return [paired_delimiter_info.left, paired_delimiter_info.right] + +# NOTE: Please do not change these dicts. Use the CSVs for customization. +# See https://github.com/pokey/cursorless-talon/blob/main/docs/customization.md +wrapper_snippets = { + "else": "ifElseStatement.alternative", + "if else": "ifElseStatement.consequence", + "if": "ifStatement.consequence", + "try": "tryCatchStatement.body", +} + + +@mod.capture( + rule=( + "({user.cursorless_paired_delimiter} | {user.cursorless_wrapper_snippet}) {user.cursorless_wrap_action}" + ) +) +def cursorless_wrapper(m) -> Union[list[str], str]: + try: + paired_delimiter_info = paired_delimiters_map[m.cursorless_paired_delimiter] + return { + "action": "wrapWithPairedDelimiter", + "extra_args": [paired_delimiter_info.left, paired_delimiter_info.right], + } + except AttributeError: + return { + "action": "wrapWithSnippet", + "extra_args": [m.cursorless_wrapper_snippet], + } + + +@mod.action_class +class Actions: + def cursorless_wrap(cursorless_wrapper: dict, targets: dict): + """Perform cursorless wrap action""" + actions.user.cursorless_single_target_command_with_arg_list( + cursorless_wrapper["action"], targets, cursorless_wrapper["extra_args"] + ) + + +def on_ready(): + init_csv_and_watch_changes( + "experimental/wrapper_snippets", + { + "wrapper_snippet": wrapper_snippets, + }, + allow_unknown_values=True, + default_list_name="wrapper_snippet", + ctx=experimental_snippets_ctx, + ) + + +app.register("ready", on_ready) \ No newline at end of file diff --git a/src/csv_overrides.py b/src/csv_overrides.py index ed004b45..bdf445dc 100644 --- a/src/csv_overrides.py +++ b/src/csv_overrides.py @@ -1,3 +1,4 @@ +from typing import Optional from .conventions import get_cursorless_list_name from talon import Context, Module, actions, fs, app, settings from datetime import datetime @@ -5,7 +6,6 @@ mod = Module() -ctx = Context() cursorless_settings_directory = mod.setting( "cursorless_settings_directory", type=str, @@ -17,7 +17,10 @@ def init_csv_and_watch_changes( filename: str, default_values: dict[str, dict], - extra_acceptable_values: list[str] = None, + extra_ignored_values: list[str] = None, + allow_unknown_values: bool = False, + default_list_name: Optional[str] = None, + ctx: Context = Context(), ): """ Initialize a cursorless settings csv, creating it if necessary, and watch @@ -37,37 +40,67 @@ def init_csv_and_watch_changes( `cursorles-settings` dir default_values (dict[str, dict]): The default values for the lists to be customized in the given csv - extra_acceptable_values list[str]: Don't throw an exception if any of - these appear as values + extra_ignored_values list[str]: Don't throw an exception if any of + these appear as values; just ignore them and don't add them to any list + allow_unknown_values bool: If unknown values appear, just put them in the list + default_list_name Optional[str]: If unknown values are allowed, put any + unknown values in this list """ - if extra_acceptable_values is None: - extra_acceptable_values = [] + if extra_ignored_values is None: + extra_ignored_values = [] - dir_path, file_path = get_file_paths(filename) + file_path = get_full_path(filename) super_default_values = get_super_values(default_values) - dir_path.mkdir(parents=True, exist_ok=True) + file_path.parent.mkdir(parents=True, exist_ok=True) def on_watch(path, flags): if file_path.match(path): current_values, has_errors = read_file( - file_path, super_default_values.values(), extra_acceptable_values + file_path, + super_default_values.values(), + extra_ignored_values, + allow_unknown_values, + ) + update_dicts( + default_values, + current_values, + extra_ignored_values, + allow_unknown_values, + default_list_name, + ctx, ) - update_dicts(default_values, current_values, extra_acceptable_values) - fs.watch(dir_path, on_watch) + fs.watch(file_path.parent, on_watch) if file_path.is_file(): current_values = update_file( - file_path, super_default_values, extra_acceptable_values + file_path, + super_default_values, + extra_ignored_values, + allow_unknown_values, + ) + update_dicts( + default_values, + current_values, + extra_ignored_values, + allow_unknown_values, + default_list_name, + ctx, ) - update_dicts(default_values, current_values, extra_acceptable_values) else: create_file(file_path, super_default_values) - update_dicts(default_values, super_default_values, extra_acceptable_values) + update_dicts( + default_values, + super_default_values, + extra_ignored_values, + allow_unknown_values, + default_list_name, + ctx, + ) def unsubscribe(): - fs.unwatch(dir_path, on_watch) + fs.unwatch(file_path.parent, on_watch) return unsubscribe @@ -79,7 +112,10 @@ def is_removed(value: str): def update_dicts( default_values: dict[str, dict], current_values: dict, - extra_acceptable_values: list[str], + extra_ignored_values: list[str], + allow_unknown_values: bool, + default_list_name: Optional[str], + ctx: Context, ): # Create map with all default values results_map = {} @@ -92,8 +128,14 @@ def update_dicts( try: results_map[value]["key"] = key except KeyError: - if value in extra_acceptable_values: + if value in extra_ignored_values: pass + elif allow_unknown_values: + results_map[value] = { + "key": key, + "value": value, + "list": default_list_name, + } else: raise @@ -110,9 +152,14 @@ def update_dicts( ctx.lists[get_cursorless_list_name(list_name)] = dict -def update_file(path: Path, default_values: dict, extra_acceptable_values: list[str]): +def update_file( + path: Path, + default_values: dict, + extra_ignored_values: list[str], + allow_unknown_values: bool, +): current_values, has_errors = read_file( - path, default_values.values(), extra_acceptable_values + path, default_values.values(), extra_ignored_values, allow_unknown_values ) current_identifiers = current_values.values() @@ -178,7 +225,10 @@ def csv_error(path: Path, index: int, message: str, value: str): def read_file( - path: Path, default_identifiers: list[str], extra_acceptable_values: list[str] + path: Path, + default_identifiers: list[str], + extra_ignored_values: list[str], + allow_unknown_values: bool, ): with open(path) as f: lines = list(f) @@ -210,7 +260,11 @@ def read_file( seen_header = True continue - if value not in default_identifiers and value not in extra_acceptable_values: + if ( + value not in default_identifiers + and value not in extra_ignored_values + and not allow_unknown_values + ): has_errors = True csv_error(path, i, "Unknown identifier", value) continue @@ -229,17 +283,17 @@ def read_file( return result, has_errors -def get_file_paths(filename: str): +def get_full_path(filename: str): if not filename.endswith(".csv"): filename = f"{filename}.csv" + user_dir = actions.path.talon_user() settings_directory = Path(cursorless_settings_directory.get()) if not settings_directory.is_absolute(): settings_directory = user_dir / settings_directory - csv_path = Path(settings_directory, filename) - return settings_directory, csv_path + return (settings_directory / filename).resolve() def get_super_values(values: dict[str, dict]): diff --git a/src/cursorless.talon b/src/cursorless.talon index 146b5d50..0b0a7c75 100644 --- a/src/cursorless.talon +++ b/src/cursorless.talon @@ -13,8 +13,8 @@ app: vscode {user.cursorless_reformat_action} at : user.cursorless_reformat(cursorless_target, formatters) - {user.cursorless_wrap_action} : - user.cursorless_single_target_command_with_arg_list(cursorless_wrap_action, cursorless_target, cursorless_wrapper) + : + user.cursorless_wrap(cursorless_wrapper, cursorless_target) cursorless help: user.cursorless_cheat_sheet_toggle() cursorless instructions: user.cursorless_open_instructions() \ No newline at end of file