diff --git a/cursorless-talon/src/actions/actions.py b/cursorless-talon/src/actions/actions.py index a1c81d3c04..3d558c308a 100644 --- a/cursorless-talon/src/actions/actions.py +++ b/cursorless-talon/src/actions/actions.py @@ -4,7 +4,11 @@ from .actions_callback import callback_action_defaults, callback_action_map from .actions_custom import custom_action_defaults from .actions_makeshift import makeshift_action_defaults, makeshift_action_map -from .actions_simple import simple_action_defaults +from .actions_simple import ( + no_wait_actions, + positional_action_defaults, + simple_action_defaults, +) mod = Module() @@ -83,6 +87,7 @@ def vscode_command_no_wait(command_id: str, target: dict, command_options: dict default_values = { "simple_action": simple_action_defaults, + "positional_action": positional_action_defaults, "callback_action": callback_action_defaults, "makeshift_action": makeshift_action_defaults, "custom_action": custom_action_defaults, diff --git a/cursorless-talon/src/actions/actions_simple.py b/cursorless-talon/src/actions/actions_simple.py index e1f218ae3c..b123335705 100644 --- a/cursorless-talon/src/actions/actions_simple.py +++ b/cursorless-talon/src/actions/actions_simple.py @@ -24,7 +24,6 @@ "give": "deselect", "highlight": "highlight", "indent": "indentLine", - "paste to": "pasteFromClipboard", "post": "setSelectionAfter", "pour": "editNewLineAfter", "pre": "setSelectionBefore", @@ -43,8 +42,18 @@ "generateSnippet", ] +# NOTE: Please do not change these dicts. Use the CSVs for customization. +# See https://github.com/pokey/cursorless-talon/blob/main/docs/customization.md +positional_action_defaults = { + "paste": "pasteFromClipboard", +} + mod = Module() mod.list( "cursorless_simple_action", desc="Supported simple actions for cursorless navigation", ) +mod.list( + "cursorless_positional_action", + desc="Supported actions for cursorless that expect a positional target", +) diff --git a/cursorless-talon/src/actions/call.py b/cursorless-talon/src/actions/call.py index f29e61fab1..d12aff0172 100644 --- a/cursorless-talon/src/actions/call.py +++ b/cursorless-talon/src/actions/call.py @@ -6,5 +6,5 @@ def run_call_action(target: dict): - targets = [target, IMPLICIT_TARGET] + targets = [target, IMPLICIT_TARGET.copy()] actions.user.cursorless_multiple_target_command("callAsFunction", targets) diff --git a/cursorless-talon/src/actions/move_bring.py b/cursorless-talon/src/actions/move_bring.py index 021fffa174..41871a78c8 100644 --- a/cursorless-talon/src/actions/move_bring.py +++ b/cursorless-talon/src/actions/move_bring.py @@ -4,7 +4,7 @@ mod = Module() mod.list( - "cursorless_source_destination_connective", + "cursorless_positional_connective", desc="The connective used to separate source and destination targets", ) @@ -12,15 +12,13 @@ mod.list("cursorless_move_bring_action", desc="Cursorless move or bring actions") -@mod.capture( - rule=( - " [{user.cursorless_source_destination_connective} ]" - ) -) +@mod.capture(rule=(" []")) def cursorless_move_bring_targets(m) -> list[dict]: - target_list = m.cursorless_target_list + target_list = [m.cursorless_target] - if len(target_list) == 1: - target_list = target_list + [IMPLICIT_TARGET] + try: + target_list += [m.cursorless_positional_target] + except AttributeError: + target_list += [IMPLICIT_TARGET.copy()] return target_list diff --git a/cursorless-talon/src/cheatsheet/sections/actions.py b/cursorless-talon/src/cheatsheet/sections/actions.py index 113c620a0e..e48e6dbb0d 100644 --- a/cursorless-talon/src/cheatsheet/sections/actions.py +++ b/cursorless-talon/src/cheatsheet/sections/actions.py @@ -27,9 +27,9 @@ def get_actions(): } swap_connective = list(get_raw_list("swap_connective").keys())[0] - source_destination_connective = list( - get_raw_list("source_destination_connective").keys() - )[0] + positional_connectives = { + value: key for key, value in get_raw_list("positional_connective").items() + } make_dict_readable( simple_actions, @@ -39,10 +39,14 @@ def get_actions(): ) return { **simple_actions, - f"{complex_actions['replaceWithTarget']} {source_destination_connective} ": "Replace T2 with T1", - f"{complex_actions['replaceWithTarget']} ": "Replace S with T", - f"{complex_actions['moveToTarget']} {source_destination_connective} ": "Move T1 to T2", - f"{complex_actions['moveToTarget']} ": "Move T to S", + f"{complex_actions['replaceWithTarget']} T1 {positional_connectives['contentConnective']} T2": "Replace T2 with T1", + f"{complex_actions['replaceWithTarget']} T1 {positional_connectives['afterConnective']} T2": "Copy T2 after T1", + f"{complex_actions['replaceWithTarget']} T1 {positional_connectives['beforeConnective']} T2": "Copy T2 before T1", + f"{complex_actions['replaceWithTarget']} T": "Replace S with T", + f"{complex_actions['moveToTarget']} T1 {positional_connectives['contentConnective']} T2": "Move T1 to T2", + f"{complex_actions['moveToTarget']} T1 {positional_connectives['afterConnective']} T2": "Move T1 after T2", + f"{complex_actions['moveToTarget']} T1 {positional_connectives['beforeConnective']} T2": "Move T1 before T2", + f"{complex_actions['moveToTarget']} T": "Move T to S", f"{complex_actions['swapTargets']} {swap_connective} ": "Swap T1 with T2", f"{complex_actions['swapTargets']} {swap_connective} ": "Swap S with T", f"{complex_actions['applyFormatter']} at ": "Reformat T as F", diff --git a/cursorless-talon/src/command.py b/cursorless-talon/src/command.py index a8141a7dc6..b4ab8dffce 100644 --- a/cursorless-talon/src/command.py +++ b/cursorless-talon/src/command.py @@ -56,7 +56,7 @@ def cursorless_this_command( ): """Execute cursorless command, passing `this` as single target""" actions.user.cursorless_multiple_target_command( - action, [IMPLICIT_TARGET], arg1, arg2, arg3 + action, [IMPLICIT_TARGET.copy()], arg1, arg2, arg3 ) def cursorless_single_target_command_with_arg_list( diff --git a/cursorless-talon/src/connective.py b/cursorless-talon/src/connective.py index 1d9570955f..347e4ec5d7 100644 --- a/cursorless-talon/src/connective.py +++ b/cursorless-talon/src/connective.py @@ -11,6 +11,14 @@ "until": "rangeExcludingEnd", } +# NOTE: Please do not change these dicts. Use the CSVs for customization. +# See https://github.com/pokey/cursorless-talon/blob/main/docs/customization.md +positional_connectives = { + "after": "afterConnective", + "before": "beforeConnective", + "to": "contentConnective", +} + default_range_connective = "rangeInclusive" @@ -21,7 +29,7 @@ def on_ready(): "range_connective": range_connectives, "list_connective": {"and": "listConnective"}, "swap_connective": {"with": "swapConnective"}, - "source_destination_connective": {"to": "sourceDestinationConnective"}, + "positional_connective": positional_connectives, }, ) diff --git a/cursorless-talon/src/cursorless-snippets.talon b/cursorless-talon/src/cursorless-snippets.talon index e82d013b2e..d5217228a3 100644 --- a/cursorless-talon/src/cursorless-snippets.talon +++ b/cursorless-talon/src/cursorless-snippets.talon @@ -5,8 +5,8 @@ tag: user.cursorless_experimental_snippets {user.cursorless_insert_snippet_action} : user.cursorless_this_command(cursorless_insert_snippet_action, cursorless_insertion_snippet) -{user.cursorless_insert_snippet_action} : - user.cursorless_single_target_command(cursorless_insert_snippet_action, cursorless_target, cursorless_insertion_snippet) +{user.cursorless_insert_snippet_action} : + user.cursorless_single_target_command(cursorless_insert_snippet_action, cursorless_positional_target, cursorless_insertion_snippet) {user.cursorless_insert_snippet_action} {user.cursorless_insertion_snippet_single_phrase} [halt]: user.cursorless_insert_snippet_with_phrase(cursorless_insert_snippet_action, cursorless_insertion_snippet_single_phrase, text) diff --git a/cursorless-talon/src/cursorless.talon b/cursorless-talon/src/cursorless.talon index 1ebb807a85..52563585a8 100644 --- a/cursorless-talon/src/cursorless.talon +++ b/cursorless-talon/src/cursorless.talon @@ -4,6 +4,9 @@ app: vscode : user.cursorless_action_or_vscode_command(cursorless_action_or_vscode_command, cursorless_target) +{user.cursorless_positional_action} : + user.cursorless_single_target_command(cursorless_positional_action, cursorless_positional_target) + {user.cursorless_swap_action} : user.cursorless_multiple_target_command(cursorless_swap_action, cursorless_swap_targets) diff --git a/cursorless-talon/src/modifiers/position.py b/cursorless-talon/src/modifiers/position.py index 830d00b68a..0472ff7432 100644 --- a/cursorless-talon/src/modifiers/position.py +++ b/cursorless-talon/src/modifiers/position.py @@ -5,8 +5,6 @@ positions = { - "after": {"position": "after"}, - "before": {"position": "before"}, "start of": {"position": "before", "insideOutsideType": "inside"}, "end of": {"position": "after", "insideOutsideType": "inside"}, # Disabled for now because "below" can misrecognize with "blue" and we may move away from allowing positional modifiers in arbitrary places anyway diff --git a/cursorless-talon/src/positional_target.py b/cursorless-talon/src/positional_target.py new file mode 100644 index 0000000000..a792212d9b --- /dev/null +++ b/cursorless-talon/src/positional_target.py @@ -0,0 +1,39 @@ +from talon import Module + +mod = Module() + + +@mod.capture(rule=("{user.cursorless_positional_connective} ")) +def cursorless_positional_target(m) -> list[dict]: + return process_positional_connective( + m.cursorless_positional_connective, m.cursorless_target + ) + + +def process_positional_connective(cursorless_positional_connective: str, target: dict): + if cursorless_positional_connective == "afterConnective": + return update_first_primitive_target(target, {"position": "after"}) + elif cursorless_positional_connective == "beforeConnective": + return update_first_primitive_target(target, {"position": "before"}) + + return target + + +def update_first_primitive_target(target: dict, fields: dict): + if target["type"] == "primitive": + return {**target, **fields} + elif target["type"] == "range": + return { + **target, + "start": update_first_primitive_target(target["start"], fields), + } + else: + elements = target["elements"] + + return { + **target, + "elements": [ + update_first_primitive_target(elements[0], fields), + *elements[1:], + ], + }