Skip to content
59 changes: 54 additions & 5 deletions src/actions/wrap.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,63 @@
from typing import Union
from ..paired_delimiter import paired_delimiters_map
from talon import Module
from talon import Module, actions, app
from ..csv_overrides import init_csv_and_watch_changes


mod = Module()


mod.list("cursorless_wrap_action", desc="Cursorless wrap action")
mod.list("cursorless_wrapper_snippet", desc="Cursorless wrapper snippet")


@mod.capture(rule=("{user.cursorless_paired_delimiter}"))
def cursorless_wrapper(m) -> 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 state": "cursorless.wrapper.ifElseStatementElseBranch",
"if else": "cursorless.wrapper.ifElseStatementIfBranch",
"if state": "cursorless.wrapper.ifStatement",
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this term is configured independently from "if state" the scope type. Could be surprising to users but I wasn't sure of the right approach here because there are multiple ways to wrap something with an if statement and I could see people wanting to wrap differently from how they refer to scope type maybe? Also, fwiw, I have configured this to "if" for myself, because I think I can get away with shorter name than when it's used for a scope type

"try catch": "cursorless.wrapper.tryCatchStatement",
}


@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(
"wrapper_snippets",
{
"wrapper_snippet": wrapper_snippets,
},
allow_unknown_values=True,
default_list_name="wrapper_snippet",
)


app.register("ready", on_ready)
82 changes: 66 additions & 16 deletions src/csv_overrides.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -17,7 +18,9 @@
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,
):
"""
Initialize a cursorless settings csv, creating it if necessary, and watch
Expand All @@ -37,11 +40,14 @@ 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)
super_default_values = get_super_values(default_values)
Expand All @@ -51,20 +57,44 @@ def init_csv_and_watch_changes(
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,
)
update_dicts(default_values, current_values, extra_acceptable_values)

fs.watch(dir_path, 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,
)
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,
)

def unsubscribe():
fs.unwatch(dir_path, on_watch)
Expand All @@ -79,7 +109,9 @@ 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],
):
# Create map with all default values
results_map = {}
Expand All @@ -92,8 +124,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

Expand All @@ -110,9 +148,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()

Expand Down Expand Up @@ -178,7 +221,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)
Expand Down Expand Up @@ -210,7 +256,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
Expand Down
4 changes: 2 additions & 2 deletions src/cursorless.talon
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ app: vscode
{user.cursorless_reformat_action} <user.formatters> at <user.cursorless_target>:
user.cursorless_reformat(cursorless_target, formatters)

<user.cursorless_wrapper> {user.cursorless_wrap_action} <user.cursorless_target>:
user.cursorless_single_target_command_with_arg_list(cursorless_wrap_action, cursorless_target, cursorless_wrapper)
<user.cursorless_wrapper> <user.cursorless_target>:
user.cursorless_wrap(cursorless_wrapper, cursorless_target)

cursorless help: user.cursorless_cheat_sheet_toggle()
cursorless instructions: user.cursorless_open_instructions()