Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for user define custom actions #125

Merged
merged 25 commits into from
Dec 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5516872
Added user defined custom actions
AndreasArvidsson Dec 9, 2021
13570df
Ran black
AndreasArvidsson Dec 9, 2021
59d275b
Refactored four preparations of a public api
AndreasArvidsson Dec 10, 2021
246f646
Added support for custom headers in csv files
AndreasArvidsson Dec 10, 2021
ce55bdc
Makeshift actions use new execute command
AndreasArvidsson Dec 10, 2021
4d506d1
Merge branch 'develop' into custom_actions
AndreasArvidsson Dec 10, 2021
fc9afc8
Update src/actions/actions.py
AndreasArvidsson Dec 14, 2021
d215896
Update src/actions/actions.py
AndreasArvidsson Dec 14, 2021
1f0a7d9
Merge branch 'develop' into custom_actions
AndreasArvidsson Dec 14, 2021
91837f6
Update src/actions/actions.py
AndreasArvidsson Dec 14, 2021
405ead4
Cleanup
AndreasArvidsson Dec 14, 2021
2adb242
Added vscode command arguments
AndreasArvidsson Dec 14, 2021
36cf7c4
Added documentation
AndreasArvidsson Dec 14, 2021
6ef86cc
Update docs/custom-actions.md
AndreasArvidsson Dec 14, 2021
aeba79f
Update docs/custom-actions.md
AndreasArvidsson Dec 14, 2021
299ab13
Update docs/custom-actions.md
AndreasArvidsson Dec 14, 2021
74a004f
Update docs/custom-actions.md
AndreasArvidsson Dec 14, 2021
646932d
Update docs/public-api.md
AndreasArvidsson Dec 14, 2021
96212f8
Update docs/public-api.md
AndreasArvidsson Dec 14, 2021
54e7788
Update docs/public-api.md
AndreasArvidsson Dec 14, 2021
6be003c
Update docs/public-api.md
AndreasArvidsson Dec 14, 2021
bcf7bbf
Moved custom actions into customisation markdown
AndreasArvidsson Dec 14, 2021
fafd629
Update docs/customization.md
AndreasArvidsson Dec 14, 2021
094e14d
Update docs/customization.md
AndreasArvidsson Dec 14, 2021
c9356ee
Moved public api into customization markdown
AndreasArvidsson Dec 14, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion docs/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ reload, as cursorless uses these lines to track disabled spoken forms.
Simply modify the spoken form in the first column of any of the csvs in the
directory above to change the spoken you'd like to use. The new spoken form will be usable immediately.

Multiple spoken forms can be used for the same action using the pipe operator
Multiple spoken forms can be used for the same action using the pipe operator
`remove|delete`

### New features
Expand All @@ -46,3 +46,46 @@ If you'd like to remove an action, scope type, etc, you can simply set the
spoken form in the first column to any thing starting with `-`. Please don't
delete any lines, as that will trigger cursorless to automatically add the
spoken form back on talon restart.

## \[Experimental\] Cursorless custom VSCode actions

You can use Cursorless to run any built-in VSCode command on a specific target.

Just add your custom commands to: `experimental/actions_custom.csv`. For example, if you wanted to be able to say `"push down <T>"` to move the line(s) containing target `<T>` downwards, you could do the following:

```csv
Spoken form, VSCode command
push down, editor.action.moveLinesDownAction
```
AndreasArvidsson marked this conversation as resolved.
Show resolved Hide resolved

Now when you say eg "push down air and bat", cursorless will first select the two tokens with a gray hat over the `a` and `b`, then issue the VSCode command `editor.action.moveLinesDownAction`, and then restore your original selection.

## Cursorless public API

Cursorless exposes a couple talon actions and captures that you can use to define your own custom command grammar leveraging cursorless targets.

### Public Talon captures

- `<user.cursorless_target>`
Represents a cursorless target, such as `"air"`, `"this"`, `"air past bat"`, `"air and bat"`, `"funk air past token bat and class cap"`, etc

### Public Talon actions

- `user.cursorless_command(action_id: str, target: cursorless_target)`
Perform a Cursorless command on the given target
eg: `user.cursorless_command("setSelection", cursorless_target)`
- `user.cursorless_vscode_command(command_id: str, target: cursorless_target)`
Performs a VSCode command on the given target
eg: `user.cursorless_vscode_command("editor.action.addCommentLine", cursorless_target)`

### Example of combining capture and action

```talon
add dock string <user.cursorless_target>:
user.cursorless_command("editNewLineAfter", cursorless_target)
"\"\"\"\"\"\""
key(left:3)

push <user.cursorless_target> down:
user.cursorless_vscode_command("editor.action.moveLinesDownAction", cursorless_target)
```
163 changes: 57 additions & 106 deletions src/actions/actions.py
Original file line number Diff line number Diff line change
@@ -1,130 +1,81 @@
from talon import Module, actions, app
from dataclasses import dataclass
from talon import Module, app, actions
from ..csv_overrides import init_csv_and_watch_changes
from .homophones import run_homophones_action
from .find import run_find_action
from .call import run_call_action
from .actions_simple import simple_action_defaults
from .actions_callback import callback_action_defaults, callback_action_map
from .actions_makeshift import (
makeshift_action_defaults,
makeshift_action_map,
)
from .actions_custom import custom_action_defaults

mod = Module()


@dataclass
class MakeshiftAction:
term: str
identifier: str
vscode_command_id: str
pre_command_sleep: float = 0
post_command_sleep: float = 0


# NOTE: Please do not change these dicts. Use the CSVs for customization.
# See https://github.com/pokey/cursorless-talon/blob/main/docs/customization.md
makeshift_actions = [
MakeshiftAction("define", "revealDefinition", "editor.action.revealDefinition"),
MakeshiftAction(
"type deaf", "revealTypeDefinition", "editor.action.goToTypeDefinition"
),
MakeshiftAction("hover", "showHover", "editor.action.showHover"),
MakeshiftAction("inspect", "showDebugHover", "editor.debug.action.showDebugHover"),
MakeshiftAction(
"quick fix", "showQuickFix", "editor.action.quickFix", pre_command_sleep=0.3
),
MakeshiftAction("reference", "showReferences", "references-view.find"),
MakeshiftAction("rename", "rename", "editor.action.rename", post_command_sleep=0.1),
]

makeshift_action_map = {action.identifier: action for action in makeshift_actions}


@dataclass
class CallbackAction:
term: str
identifier: str
callback: callable


# NOTE: Please do not change these dicts. Use the CSVs for customization.
# See https://github.com/pokey/cursorless-talon/blob/main/docs/customization.md
callbacks = [
CallbackAction("call", "callAsFunction", run_call_action),
CallbackAction("scout", "findInDocument", run_find_action),
CallbackAction("phones", "nextHomophone", run_homophones_action),
]

callbacks_map = {callback.identifier: callback.callback for callback in callbacks}


mod.list("cursorless_simple_action", desc="Supported actions for cursorless navigation")


# NOTE: Please do not change these dicts. Use the CSVs for customization.
# See https://github.com/pokey/cursorless-talon/blob/main/docs/customization.md
simple_actions = {
"bottom": "scrollToBottom",
"breakpoint": "toggleLineBreakpoint",
"carve": "cutToClipboard",
"center": "scrollToCenter",
"chuck": "remove",
"change": "clearAndSetSelection",
"clone up": "insertCopyBefore",
"clone": "insertCopyAfter",
"comment": "toggleLineComment",
"copy": "copyToClipboard",
"crown": "scrollToTop",
"dedent": "outdentLine",
"drink": "editNewLineBefore",
"drop": "insertEmptyLineBefore",
"extract": "extractVariable",
"float": "insertEmptyLineAfter",
"fold": "foldRegion",
"give": "deselect",
"indent": "indentLine",
"paste to": "pasteFromClipboard",
"post": "setSelectionAfter",
"pour": "editNewLineAfter",
"pre": "setSelectionBefore",
"puff": "insertEmptyLinesAround",
"reverse": "reverseTargets",
"scout all": "findInWorkspace",
"sort": "sortTargets",
"take": "setSelection",
"unfold": "unfoldRegion",
**{action.term: action.identifier for action in makeshift_actions},
**{callback.term: callback.identifier for callback in callbacks},
}
@mod.capture(
rule=(
"{user.cursorless_simple_action} |"
"{user.cursorless_makeshift_action} |"
"{user.cursorless_callback_action} |"
"{user.cursorless_custom_action}"
)
)
def cursorless_action_or_vscode_command(m) -> dict:
try:
value = m.cursorless_custom_action
type = "vscode_command"
except AttributeError:
value = m[0]
type = "cursorless_action"
return {
"value": value,
"type": type,
}


@mod.action_class
class Actions:
def cursorless_simple_action(action: str, targets: dict):
"""Perform cursorless simple action"""
if action in callbacks_map:
return callbacks_map[action](targets)
elif action in makeshift_action_map:
return run_makeshift_action(action, targets)
def cursorless_command(action_id: str, target: dict):
"""Perform cursorless command on target"""
if action_id in callback_action_map:
return callback_action_map[action_id](target)
elif action_id in makeshift_action_map:
command, arguments = makeshift_action_map[action_id]
return vscode_command(command, target, arguments)
else:
return actions.user.cursorless_single_target_command(action, targets)
return actions.user.cursorless_single_target_command(action_id, target)

def cursorless_vscode_command(command_id: str, target: dict):
"""Perform vscode command on cursorless target"""
return vscode_command(command_id, target)

def cursorless_action_or_vscode_command(instruction: dict, target: dict):
"""Perform cursorless action or vscode command on target (internal use only)"""
type = instruction["type"]
value = instruction["value"]
if type == "cursorless_action":
return actions.user.cursorless_command(value, target)
elif type == "vscode_command":
return actions.user.cursorless_vscode_command(value, target)

def run_makeshift_action(action: str, targets: dict):
"""Execute makeshift action"""
makeshift_action = makeshift_action_map[action]
actions.user.cursorless_single_target_command("setSelection", targets)
actions.sleep(makeshift_action.pre_command_sleep)
actions.user.vscode(makeshift_action.vscode_command_id)
actions.sleep(makeshift_action.post_command_sleep)

def vscode_command(command_id: str, target: dict, arguments: dict = {}):
return actions.user.cursorless_single_target_command(
"executeCommand", target, command_id, arguments
)


# NOTE: Please do not change these dicts. Use the CSVs for customization.
# See https://github.com/pokey/cursorless-talon/blob/main/docs/customization.md
default_values = {
"simple_action": simple_actions,
"simple_action": simple_action_defaults,
"callback_action": callback_action_defaults,
"makeshift_action": makeshift_action_defaults,
"custom_action": custom_action_defaults,
"swap_action": {"swap": "swapTargets"},
"move_bring_action": {"bring": "replaceWithTarget", "move": "moveToTarget"},
"wrap_action": {"wrap": "wrapWithPairedDelimiter", "repack": "rewrap"},
"reformat_action": {"format": "applyFormatter"},
}


ACTION_LIST_NAMES = default_values.keys()


Expand Down
32 changes: 32 additions & 0 deletions src/actions/actions_callback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from talon import Module
from dataclasses import dataclass
from .homophones import run_homophones_action
from .find import run_find_action
from .call import run_call_action


@dataclass
class CallbackAction:
term: str
identifier: str
callback: callable


# NOTE: Please do not change these dicts. Use the CSVs for customization.
# See https://github.com/pokey/cursorless-talon/blob/main/docs/customization.md
callbacks = [
CallbackAction("call", "callAsFunction", run_call_action),
CallbackAction("scout", "findInDocument", run_find_action),
CallbackAction("phones", "nextHomophone", run_homophones_action),
]

callback_action_defaults = {
callback.term: callback.identifier for callback in callbacks
}
callback_action_map = {callback.identifier: callback.callback for callback in callbacks}

mod = Module()
mod.list(
"cursorless_callback_action",
desc="Supported callback actions for cursorless navigation",
)
24 changes: 24 additions & 0 deletions src/actions/actions_custom.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from talon import Module, app
from ..csv_overrides import init_csv_and_watch_changes, SPOKEN_FORM_HEADER

custom_action_defaults = {}
AndreasArvidsson marked this conversation as resolved.
Show resolved Hide resolved


mod = Module()
mod.list(
"cursorless_custom_action",
desc="Supported custom actions for cursorless navigation",
)


def on_ready():
init_csv_and_watch_changes(
"experimental/actions_custom",
custom_action_defaults,
headers=[SPOKEN_FORM_HEADER, "VSCode command"],
allow_unknown_values=True,
default_list_name="custom_action",
)


app.register("ready", on_ready)
61 changes: 61 additions & 0 deletions src/actions/actions_makeshift.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from talon import Module
from dataclasses import dataclass


@dataclass
class MakeshiftAction:
term: str
identifier: str
vscode_command_id: str
vscode_command_args: list = None
restore_selection: bool = False
pre_command_sleep: int = None
post_command_sleep: int = None


# NOTE: Please do not change these dicts. Use the CSVs for customization.
# See https://github.com/pokey/cursorless-talon/blob/main/docs/customization.md
makeshift_actions = [
MakeshiftAction("define", "revealDefinition", "editor.action.revealDefinition"),
MakeshiftAction(
"type deaf", "revealTypeDefinition", "editor.action.goToTypeDefinition"
),
MakeshiftAction("hover", "showHover", "editor.action.showHover"),
MakeshiftAction("inspect", "showDebugHover", "editor.debug.action.showDebugHover"),
MakeshiftAction(
"quick fix", "showQuickFix", "editor.action.quickFix", restore_selection=True
),
MakeshiftAction(
"reference", "showReferences", "references-view.find", restore_selection=True
),
MakeshiftAction("rename", "rename", "editor.action.rename", restore_selection=True),
]

makeshift_action_defaults = {
action.term: action.identifier for action in makeshift_actions
}

mod = Module()
mod.list(
"cursorless_makeshift_action",
desc="Supported makeshift actions for cursorless navigation",
)


def get_parameters(action: MakeshiftAction):
command = action.vscode_command_id
arguments = {
"restoreSelection": action.restore_selection,
}
if action.vscode_command_args:
arguments["commandArgs"] = action.vscode_command_args
if action.pre_command_sleep:
arguments["preCommandSleep"] = action.pre_command_sleep
if action.post_command_sleep:
arguments["postCommandSleep"] = action.post_command_sleep
return command, arguments


makeshift_action_map = {
action.identifier: get_parameters(action) for action in makeshift_actions
}
Loading