dotfiles/talon/cursorless-talon/src/snippets.py

224 lines
6.6 KiB
Python

from dataclasses import dataclass
from typing import Any, Optional, Union
from talon import Module, actions
from .targets.target_types import (
CursorlessDestination,
CursorlessTarget,
ImplicitDestination,
)
@dataclass
class InsertionSnippet:
name: str
destination: CursorlessDestination
@dataclass
class CommunityInsertionSnippet:
body: str
scopes: list[str] | None = None
@dataclass
class CommunityWrapperSnippet:
body: str
variable_name: str
scope: str | None = None
mod = Module()
mod.list("cursorless_insert_snippet_action", desc="Cursorless insert snippet action")
# Deprecated tag; we should probably remove this and notify users that they
# should get rid of it, but I don't think it's worth the effort right now
mod.tag(
"cursorless_experimental_snippets",
desc="tag for enabling experimental snippet support",
)
mod.tag(
"cursorless_use_community_snippets",
"If active use community snippets instead of Cursorless snippets",
)
mod.list("cursorless_wrapper_snippet", desc="Cursorless wrapper snippet")
mod.list(
"cursorless_insertion_snippet_no_phrase",
desc="Cursorless insertion snippets that don't accept a phrase",
)
mod.list(
"cursorless_insertion_snippet_single_phrase",
desc="Cursorless insertion snippet that can accept a single phrase",
)
mod.list("cursorless_phrase_terminator", "Contains term used to terminate a phrase")
@mod.capture(
rule="({user.cursorless_insertion_snippet_no_phrase} | {user.cursorless_insertion_snippet_single_phrase}) [<user.cursorless_destination>]"
)
def cursorless_insertion_snippet(m) -> InsertionSnippet:
try:
name = m.cursorless_insertion_snippet_no_phrase
except AttributeError:
name = m.cursorless_insertion_snippet_single_phrase.split(".")[0]
try:
destination = m.cursorless_destination
except AttributeError:
destination = ImplicitDestination()
return InsertionSnippet(name, destination)
def wrap_with_snippet(snippet_description: dict, target: CursorlessTarget):
actions.user.private_cursorless_command_and_wait(
{
"name": "wrapWithSnippet",
"snippetDescription": snippet_description,
"target": target,
},
)
def insert_snippet(snippet_description: dict, destination: CursorlessDestination):
actions.user.private_cursorless_command_and_wait(
{
"name": "insertSnippet",
"snippetDescription": snippet_description,
"destination": destination,
},
)
def insert_named_snippet(
name: str,
destination: CursorlessDestination,
substitutions: Optional[dict] = None,
):
snippet: dict = {
"type": "named",
"name": name,
}
if substitutions is not None:
snippet["substitutions"] = substitutions
insert_snippet(snippet, destination)
def insert_custom_snippet(
body: str,
destination: CursorlessDestination,
scope_types: Optional[list[dict]] = None,
):
snippet: dict = {
"type": "custom",
"body": body,
}
if scope_types:
snippet["scopeTypes"] = scope_types
insert_snippet(snippet, destination)
@mod.action_class
class Actions:
def private_cursorless_insert_snippet(insertion_snippet: InsertionSnippet): # pyright: ignore [reportGeneralTypeIssues]
"""Execute Cursorless insert snippet action"""
insert_named_snippet(
insertion_snippet.name,
insertion_snippet.destination,
)
def private_cursorless_insert_snippet_with_phrase(
snippet_description: str, # pyright: ignore [reportGeneralTypeIssues]
text: str,
):
"""Cursorless: Insert snippet <snippet_description> with phrase <text>"""
snippet_name, snippet_variable = snippet_description.split(".")
insert_named_snippet(
snippet_name,
ImplicitDestination(),
{snippet_variable: text},
)
def cursorless_insert_snippet_by_name(name: str): # pyright: ignore [reportGeneralTypeIssues]
"""Cursorless: Insert named snippet <name>"""
insert_named_snippet(
name,
ImplicitDestination(),
)
def cursorless_insert_snippet(
body: str, # pyright: ignore [reportGeneralTypeIssues]
destination: CursorlessDestination = ImplicitDestination(),
scope_type: Optional[Union[str, list[str]]] = None,
):
"""Cursorless: Insert custom snippet <body>"""
if isinstance(scope_type, str):
scope_type = [scope_type]
if scope_type is not None:
scope_types = [{"type": st} for st in scope_type]
else:
scope_types = None
insert_custom_snippet(body, destination, scope_types)
def cursorless_wrap_with_snippet_by_name(
name: str, # pyright: ignore [reportGeneralTypeIssues]
variable_name: str,
target: CursorlessTarget,
):
"""Cursorless: Wrap target with a named snippet <name>"""
wrap_with_snippet(
{
"type": "named",
"name": name,
"variableName": variable_name,
},
target,
)
def cursorless_wrap_with_snippet(
body: str, # pyright: ignore [reportGeneralTypeIssues]
target: CursorlessTarget,
variable_name: Optional[str] = None,
scope: Optional[str] = None,
):
"""Cursorless: Wrap target with custom snippet <body>"""
snippet_arg: dict[str, Any] = {
"type": "custom",
"body": body,
}
if scope is not None:
snippet_arg["scopeType"] = {"type": scope}
if variable_name is not None:
snippet_arg["variableName"] = variable_name
wrap_with_snippet(
snippet_arg,
target,
)
def private_cursorless_insert_community_snippet(
name: str, # pyright: ignore [reportGeneralTypeIssues]
destination: CursorlessDestination,
):
"""Cursorless: Insert community snippet <name>"""
snippet: CommunityInsertionSnippet = actions.user.get_insertion_snippet(name)
actions.user.cursorless_insert_snippet(
snippet.body, destination, snippet.scopes
)
def private_cursorless_wrap_with_community_snippet(
name: str, # pyright: ignore [reportGeneralTypeIssues]
target: CursorlessTarget,
):
"""Cursorless: Wrap target with community snippet <name>"""
snippet: CommunityWrapperSnippet = actions.user.get_wrapper_snippet(name)
actions.user.cursorless_wrap_with_snippet(
snippet.body, target, snippet.variable_name, snippet.scope
)