diff --git a/src/actions/actions.py b/src/actions/actions.py index 05f257c9..19c4b9bb 100644 --- a/src/actions/actions.py +++ b/src/actions/actions.py @@ -2,6 +2,7 @@ from dataclasses import dataclass from .homophones import run_homophones_action from .find import run_find_action +from .call import run_call_action mod = Module() @@ -20,8 +21,8 @@ class MakeshiftAction: makeshift_actions = [ - MakeshiftAction("define", "editor.action.revealDefinition"), MakeshiftAction("drink cell", "jupyter.insertCellAbove"), + MakeshiftAction("define", "editor.action.revealDefinition"), MakeshiftAction("hover", "editor.action.showHover"), MakeshiftAction("inspect", "editor.debug.action.showDebugHover"), MakeshiftAction("pour cell", "jupyter.insertCellBelow"), @@ -41,7 +42,8 @@ class CallbackAction: callbacks = [ - CallbackAction("find", "find", run_find_action), + CallbackAction("call", "call", run_call_action), + CallbackAction("scout", "find", run_find_action), CallbackAction("phones", "nextHomophone", run_homophones_action), ] @@ -56,30 +58,30 @@ class CallbackAction: "center": "scrollToCenter", "chuck": "delete", "clear": "clear", + "clone up": "copyLinesUp", + "clone": "copyLinesDown", "comment": "commentLines", "copy": "copy", + "crown": "scrollToTop", "dedent": "outdentLines", "drink": "editNewLineAbove", "drop": "insertEmptyLineAbove", - "clone": "copyLinesDown", - "clone up": "copyLinesUp", "extract": "extractVariable", - "find all": "findInFiles", "float": "insertEmptyLineBelow", "fold": "fold", "indent": "indentLines", - "paste": "paste", + "paste to": "paste", "post": "setSelectionAfter", "pour": "editNewLineBelow", "pre": "setSelectionBefore", "puff": "insertEmptyLinesAround", "reverse": "reverse", + "scout all": "findInFiles", "sort": "sort", "take": "setSelection", - "top": "scrollToTop", "unfold": "unfold", - **{callback.term: callback.action for callback in callbacks}, **{action.term: action.term for action in makeshift_actions}, + **{callback.term: callback.action for callback in callbacks}, } diff --git a/src/actions/call.py b/src/actions/call.py new file mode 100644 index 00000000..7134b539 --- /dev/null +++ b/src/actions/call.py @@ -0,0 +1,9 @@ +from ..primitive_target import STRICT_HERE +from talon import Module, actions + +mod = Module() + + +def run_call_action(target: dict): + targets = [target, STRICT_HERE] + actions.user.cursorless_multiple_target_command("call", targets) diff --git a/src/actions/get_text.py b/src/actions/get_text.py index 933e4692..b99b406e 100644 --- a/src/actions/get_text.py +++ b/src/actions/get_text.py @@ -2,12 +2,12 @@ def get_text( - targets: dict, show_decorations: bool = None, ensure_single_target: bool = None + target: dict, show_decorations: bool = None, ensure_single_target: bool = None ): """Get target texts""" return actions.user.cursorless_single_target_command_get( "getText", - targets, + target, { "showDecorations": show_decorations, "ensureSingleTarget": ensure_single_target, diff --git a/src/actions/replace.py b/src/actions/replace.py index b294478a..7fd080ce 100644 --- a/src/actions/replace.py +++ b/src/actions/replace.py @@ -3,35 +3,8 @@ mod = Module() -@mod.capture(rule="count [from ]") -def cursorless_count(m) -> str: - try: - start = m.number_small - except AttributeError: - start = 0 - return {"start": start} - - -@mod.capture(rule="[] (and )*") -def cursorless_texts(m) -> str: - texts = m.text_list - try: - formatters = m.formatters - texts = list( - map(lambda text: actions.user.formatted_text(text, formatters), texts) - ) - except AttributeError: - pass - return texts - - -@mod.capture(rule=" | ") -def cursorless_replace_value(m) -> str: - return m[0] - - @mod.action_class class Actions: - def cursorless_replace(targets: dict, texts: list[str] or dict): + def cursorless_replace(target: dict, texts: list[str] or dict): """Replaced targets with texts""" - actions.user.cursorless_single_target_command("replace", targets, texts) + actions.user.cursorless_single_target_command("replace", target, texts) diff --git a/src/wrappers.py b/src/actions/wrap.py similarity index 91% rename from src/wrappers.py rename to src/actions/wrap.py index 397c96bf..eebc73d9 100644 --- a/src/wrappers.py +++ b/src/actions/wrap.py @@ -7,18 +7,19 @@ "square": ["[", "]"], "round": ["(", ")"], "curly": ["{", "}"], - "angle": ["<", ">"], + "diamond": ["<", ">"], "twin": ["'", "'"], "quad": ['"', '"'], "brick": ["`", "`"], "escaped twin": ["\\'", "\\'"], "escaped quad": ['\\"', '\\"'], - "space": [" ", " "] + "space": [" ", " "], } mod.list("cursorless_wrapper", desc="Supported wrappers for cursorless wrap action") ctx.lists["self.cursorless_wrapper"] = wrappers.keys() + @mod.capture(rule=("{user.cursorless_wrapper}")) def cursorless_wrapper(m) -> list[str]: return wrappers[m.cursorless_wrapper] diff --git a/src/call.py b/src/call.py deleted file mode 100644 index efcf976f..00000000 --- a/src/call.py +++ /dev/null @@ -1,14 +0,0 @@ -from .primitive_target import STRICT_HERE -from talon import Module - -mod = Module() - - -@mod.capture(rule=("call [on ]")) -def cursorless_call(m) -> str: - target_list = m.cursorless_target_list - - if len(target_list) == 1: - target_list = target_list + [STRICT_HERE] - - return target_list diff --git a/src/compound_targets.py b/src/compound_targets.py index 1c32f3cf..c2f796fe 100644 --- a/src/compound_targets.py +++ b/src/compound_targets.py @@ -1,14 +1,27 @@ from .primitive_target import BASE_TARGET -from talon import Module +from talon import Module, Context mod = Module() +ctx = Context() + +ctx.matches = r""" +tag: user.cursorless +""" + +range_specifier = { + "past", + "until", + "between", +} + +mod.list("cursorless_range_specifier", desc="A symbol that comes in pairs, eg brackets") +ctx.lists["self.cursorless_range_specifier"] = range_specifier @mod.capture( rule=( - " | " - "(past|until|tween) | " - " (past|until|tween) " + "[{user.cursorless_range_specifier}] | " + " {user.cursorless_range_specifier} " ) ) def cursorless_range(m) -> str: @@ -25,8 +38,8 @@ def cursorless_range(m) -> str: "type": "range", "start": start, "end": m[-1], - "excludeStart": modifier == "tween", - "excludeEnd": modifier in ["tween", "until"], + "excludeStart": modifier == "between", + "excludeEnd": modifier in ["between", "until"], } diff --git a/src/cursorless.talon b/src/cursorless.talon index 51b44fff..abb2a6e3 100644 --- a/src/cursorless.talon +++ b/src/cursorless.talon @@ -5,38 +5,20 @@ tag(): user.cursorless {user.cursorless_simple_action} : user.cursorless_simple_action(cursorless_simple_action, cursorless_target) - wrap : - user.cursorless_single_target_command_with_arg_list("wrap", cursorless_target, cursorless_wrapper) - -wrap with funk : - user.cursorless_single_target_command("wrap", cursorless_target, "{code_functions}(", ")") - -replace with $: - user.cursorless_replace(cursorless_target, cursorless_replace_value) - -reformat as : - user.cursorless_reformat(cursorless_target, formatters) - -extract as $: - user.cursorless_single_target_command("extractVariable", cursorless_target) - sleep(300ms) - user.code_public_variable_formatter(text) - key(enter) - : user.cursorless_multiple_target_command("swap", cursorless_swap) {user.cursorless_move_bring_action} : user.cursorless_multiple_target_command(cursorless_move_bring_action, cursorless_move_bring_targets) -: - user.cursorless_multiple_target_command("call", cursorless_call) + wrap : + user.cursorless_single_target_command_with_arg_list("wrap", cursorless_target, cursorless_wrapper) -pour cell: - user.vscode("jupyter.insertCellBelow") +format at : + user.cursorless_reformat(cursorless_target, formatters) -drink cell: - user.vscode("jupyter.insertCellAbove") +pour cell: user.vscode("jupyter.insertCellBelow") +drink cell: user.vscode("jupyter.insertCellAbove") cursorless help: user.cursorless_cheat_sheet_toggle() cursorless instructions: user.cursorless_open_instructions() \ No newline at end of file diff --git a/src/modifiers/containing_scope.py b/src/modifiers/containing_scope.py index 8877822a..83e3a5a9 100644 --- a/src/modifiers/containing_scope.py +++ b/src/modifiers/containing_scope.py @@ -1,5 +1,4 @@ from talon import Context, Module -import copy mod = Module() ctx = Context() @@ -8,16 +7,19 @@ tag: user.cursorless """ -containing_scope_type_map = { + +mod.list("cursorless_scope_type", desc="Supported scope types") +ctx.lists["self.cursorless_scope_type"] = { "arg": "argumentOrParameter", "arrow": "arrowFunction", + "attribute": "attribute", "call": "functionCall", "class name": "className", "class": "class", "comment": "comment", "funk name": "functionName", "funk": "namedFunction", - "if": "ifStatement", + "if state": "ifStatement", "item": "collectionItem", "key": "collectionKey", "lambda": "arrowFunction", @@ -30,37 +32,20 @@ "type": "type", "value": "value", # XML, JSX - "attribute": "xmlAttribute", "element": "xmlElement", "tags": "xmlBothTags", "start tag": "xmlStartTag", "end tag": "xmlEndTag", } -containing_scope_types = { - term: { + +@mod.capture(rule="[every] {user.cursorless_scope_type}") +def cursorless_containing_scope(m) -> str: + """Expand to containing scope""" + return { "modifier": { "type": "containingScope", - "scopeType": containing_scope_type, + "scopeType": m.cursorless_scope_type, + "includeSiblings": m[0] == "every", } } - for term, containing_scope_type in containing_scope_type_map.items() -} - -mod.list("cursorless_containing_scope_type", desc="Supported containing scope types") -ctx.lists["self.cursorless_containing_scope_type"] = containing_scope_types.keys() - - -@mod.capture(rule="{user.cursorless_containing_scope_type}") -def cursorless_containing_scope_type(m) -> str: - return containing_scope_types[m.cursorless_containing_scope_type] - - -@mod.capture(rule=("[every] ")) -def cursorless_containing_scope(m) -> str: - """Supported containing scope types""" - if m[0] == "every": - current_target = copy.deepcopy(m.cursorless_containing_scope_type) - current_target["modifier"]["includeSiblings"] = True - return current_target - return m.cursorless_containing_scope_type diff --git a/src/modifiers/head_tail.py b/src/modifiers/head_tail.py index 7d2d7210..6cb08575 100644 --- a/src/modifiers/head_tail.py +++ b/src/modifiers/head_tail.py @@ -1,8 +1,16 @@ -from talon import Module +from talon import Context, Module mod = Module() +ctx = Context() +ctx.matches = r""" +tag: user.cursorless +""" -@mod.capture(rule="head | tail") +mod.list("cursorless_head_tail", desc="Cursorless modifier for head or tail of line") +ctx.lists["self.cursorless_head_tail"] = {"head", "tail"} + + +@mod.capture(rule="{user.cursorless_head_tail}") def cursorless_head_tail(m) -> str: - return {"modifier": {"type": m[0]}} + return {"modifier": {"type": m.cursorless_head_tail}} diff --git a/src/modifiers/inside_outside.py b/src/modifiers/inside_outside.py deleted file mode 100644 index f44f2bd2..00000000 --- a/src/modifiers/inside_outside.py +++ /dev/null @@ -1,21 +0,0 @@ -from talon import Context, Module - -mod = Module() -ctx = Context() - -ctx.matches = r""" -tag: user.cursorless -""" - -inside_outside = { - "inner": {"insideOutsideType": "inside"}, - "outer": {"insideOutsideType": "outside"}, -} - -mod.list("cursorless_inside_outside", desc="Supported inside/outside types") -ctx.lists["self.cursorless_inside_outside"] = inside_outside.keys() - - -@mod.capture(rule=("{user.cursorless_inside_outside}")) -def cursorless_inside_outside(m) -> str: - return inside_outside[m.cursorless_inside_outside] diff --git a/src/modifiers/matching_pair_symbol.py b/src/modifiers/matching_pair_symbol.py index fe7bd2b8..eea22ca4 100644 --- a/src/modifiers/matching_pair_symbol.py +++ b/src/modifiers/matching_pair_symbol.py @@ -1,12 +1,6 @@ -from talon import Context, Module -from dataclasses import dataclass +from talon import Module mod = Module() -ctx = Context() - -ctx.matches = r""" -tag: user.cursorless -""" @mod.capture(rule="matching") diff --git a/src/modifiers/position.py b/src/modifiers/position.py index 570b1574..325b91bd 100644 --- a/src/modifiers/position.py +++ b/src/modifiers/position.py @@ -1,5 +1,4 @@ from talon import Context, Module -from .selection_type import LINE mod = Module() ctx = Context() @@ -22,6 +21,7 @@ mod.list("cursorless_position", desc="Types of positions") ctx.lists["self.cursorless_position"] = positions.keys() + @mod.capture(rule="{user.cursorless_position}") def cursorless_position(m) -> str: return positions[m.cursorless_position] diff --git a/src/modifiers/selection_type.py b/src/modifiers/selection_type.py index 27ae2b7b..6baa0c9c 100644 --- a/src/modifiers/selection_type.py +++ b/src/modifiers/selection_type.py @@ -8,46 +8,15 @@ tag: user.cursorless """ -SELECTION_TYPE_KEY = "selectionType" - - -@dataclass -class SelectionType: - singular: str - plural: str - json_name: str - rank: int - - @property - def json_repr(self): - return {SELECTION_TYPE_KEY: self.json_name} - - -TOKEN = SelectionType("token", "tokens", "token", 0) -LINE = SelectionType("line", "lines", "line", 1) -BLOCK = SelectionType("block", "blocks", "paragraph", 2) -FILE = SelectionType("file", "files", "document", 3) - -SELECTION_TYPES = [ - TOKEN, - LINE, - BLOCK, - FILE -] - -RANKED_SELECTION_TYPES = { - selection_type.json_name: selection_type.rank for selection_type in SELECTION_TYPES -} - -selection_type_map = { - selection_type.json_name: selection_type.json_repr for selection_type in SELECTION_TYPES -} - mod.list("cursorless_selection_type", desc="Types of selection_types") ctx.lists["self.cursorless_selection_type"] = { - type.singular: type.json_name for type in SELECTION_TYPES + "token": "token", + "line": "line", + "block": "paragraph", + "file": "document", } + @mod.capture(rule="{user.cursorless_selection_type}") def cursorless_selection_type(m) -> str: - return selection_type_map[m.cursorless_selection_type] + return {"selectionType": m.cursorless_selection_type} diff --git a/src/modifiers/surrounding_pair.py b/src/modifiers/surrounding_pair.py index 6368e416..d9036daa 100644 --- a/src/modifiers/surrounding_pair.py +++ b/src/modifiers/surrounding_pair.py @@ -7,36 +7,43 @@ tag: user.cursorless """ -pair_symbols = { - "angle": "angleBrackets", - "diamond": "angleBrackets", + +mod.list("cursorless_pair_symbol", desc="A symbol that comes in pairs, eg brackets") +ctx.lists["self.cursorless_pair_symbol"] = { + "skis": "backtickQuotes", "curly": "curlyBrackets", + "diamond": "angleBrackets", + "quad": "doubleQuotes", "round": "parentheses", "square": "squareBrackets", - "quad": "doubleQuotes", "twin": "singleQuotes", } -mod.list("cursorless_pair_symbol", desc="A symbol that comes in pairs, eg brackets") -ctx.lists["self.cursorless_pair_symbol"] = pair_symbols +mod.list( + "cursorless_delimiter_inclusion", + desc="Whether to include delimiters in surrounding range", +) +ctx.lists["self.cursorless_delimiter_inclusion"] = { + "inside": "excludeDelimiters", + "outside": "includeDelimiters", + "pair": "delimitersOnly", +} @mod.capture( rule=( - "matching | {user.cursorless_pair_symbol} | pair [{user.cursorless_pair_symbol}] " + "[{user.cursorless_delimiter_inclusion}] {user.cursorless_pair_symbol} | " + "{user.cursorless_delimiter_inclusion}" ) ) def cursorless_surrounding_pair(m) -> str: - """Surrounding pair modifiers""" - try: - cursorless_pair_symbol = m.cursorless_pair_symbol - except AttributeError: - cursorless_pair_symbol = None - + """Surrounding pair modifier""" return { "modifier": { "type": "surroundingPair", - "delimiter": cursorless_pair_symbol, - "delimitersOnly": m[0] == "pair", - } + "delimiter": getattr(m, "cursorless_pair_symbol", None), + "delimiterInclusion": getattr( + m, "cursorless_delimiter_inclusion", "includeDelimiters" + ), + }, } diff --git a/src/primitive_target.py b/src/primitive_target.py index 230a5364..11fbd850 100644 --- a/src/primitive_target.py +++ b/src/primitive_target.py @@ -1,13 +1,7 @@ -from dataclasses import dataclass -from talon import Context, Module -from .modifiers.selection_type import SELECTION_TYPE_KEY, RANKED_SELECTION_TYPES +from talon import Module -ctx = Context() -mod = Module() -ctx.matches = r""" -tag: user.cursorless -""" +mod = Module() BASE_TARGET = {"type": "primitive"} STRICT_HERE = { @@ -19,36 +13,30 @@ "insideOutsideType": "inside", } + modifiers = [ "", # before, end of "", # token, line, file + "", # head, tail "", # funk, state, class "", # first past second word - "", # head, tail - "", # inner, outer - "", # curly, round + # "", # matching/pair [curly, round] # "", # matching ] -modifiers_and_mark = ( - f"({'|'.join(modifiers)})* " # 0 or more parameters - "" # 1 mark -) -modifiers_only = f"({'|'.join(modifiers)})+" # 1 or more parameters +@mod.capture(rule="|".join(modifiers)) +def cursorless_modifier(m) -> str: + """Cursorless modifier""" + return m[0] -@mod.capture(rule=f"({modifiers_and_mark}) | ({modifiers_only})") +@mod.capture( + rule="+ [] | " +) def cursorless_primitive_target(m) -> str: """Supported extents for cursorless navigation""" - object = BASE_TARGET.copy() + result = BASE_TARGET.copy() for capture in m: - for key, value in capture.items(): - if ( - key in object - and key == SELECTION_TYPE_KEY - and RANKED_SELECTION_TYPES[value] < RANKED_SELECTION_TYPES[object[key]] - ): - continue - object[key] = value - return object + result.update(capture) + return result