Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
phillco committed Oct 4, 2023
2 parents f202498 + 79b4d59 commit 01c577d
Show file tree
Hide file tree
Showing 193 changed files with 4,742 additions and 695 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @pokey
* @pokey @AndreasArvidsson
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,5 @@ jobs:
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Do nothing
run: "true"
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,5 @@ jobs:
name: dumps
path: ${{ env.VSCODE_CRASH_DIR }}
if: failure()
- name: Forbid TODOs
run: ./scripts/forbid-todo.sh
6 changes: 1 addition & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ repos:
exclude_types: [svg]
exclude: patches/.*\.patch
- id: fix-byte-order-marker
- id: forbid-submodules
- id: mixed-line-ending
- id: trailing-whitespace
# Trailing whitespace breaks yaml files if you use a multiline string
Expand Down Expand Up @@ -83,8 +84,3 @@ repos:
rev: 23.3.0
hooks:
- id: black
- repo: https://github.com/wenkokke/talonfmt
rev: 1.9.5
hooks:
- id: talonfmt
args: ["--in-place"]
1 change: 1 addition & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// for the documentation about the extensions.json format
"recommendations": [
"AndreasArvidsson.andreas-talon",
"charliermarsh.ruff",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"jrieken.vscode-tree-sitter-query",
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ There's too much new stuff in this release to fit in a highlights reel, so we'll
- Improve and unify selection updating behaviour to handle overlapping ranges [\#138](https://github.com/cursorless-dev/cursorless/issues/138)
- Duplicate symbols after VS Code update [\#111](https://github.com/cursorless-dev/cursorless/issues/111)
- Fold action not working properly with multiple list elements [\#39](https://github.com/cursorless-dev/cursorless/issues/39)
- The `clear` command clashes with Knausj commands [\#68](https://github.com/pokey/cursorless-talon/issues/68)
- The `clear` command clashes with community commands [\#68](https://github.com/pokey/cursorless-talon/issues/68)

**Closed issues:**

Expand Down
6 changes: 6 additions & 0 deletions changelog/2023-09-addedArgumentTargetToCallAction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
tags: [enhancement, talon]
pullRequest: 1900
---

- Added optional second target to action `call` to specify argument. eg: `"call air on bat"`.
7 changes: 7 additions & 0 deletions changelog/2023-09-addedInsertPythonAction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
tags: [enhancement, talon]
pullRequest: 1875
mergeDate: 2023-09-10
---

- Added `cursorless_insert` action to the public Talon api. This api enables you to define custom grammars for Cursorless text insertion. See the [talon-side api docs](https://www.cursorless.org/docs/user/customization/#public-talon-actions) for more
5 changes: 5 additions & 0 deletions cursorless-talon-dev/src/cursorless_dev.talon
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mode: command
mode: user.cursorless_spoken_form_test
tag: user.cursorless
-

Expand Down Expand Up @@ -28,3 +30,6 @@ tag: user.cursorless

test snippet make <user.cursorless_target>:
user.private_cursorless_make_snippet_test(cursorless_target)

parse tree <user.cursorless_target>:
user.cursorless_command("private.showParseTree", cursorless_target)
4 changes: 4 additions & 0 deletions cursorless-talon-dev/src/cursorless_test.talon
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ test api command <user.cursorless_target>:
user.cursorless_command("setSelection", cursorless_target)
test api command bring <user.cursorless_target>:
user.cursorless_command("replaceWithTarget", cursorless_target)
test api insert <user.word> <user.cursorless_destination>:
user.cursorless_insert(cursorless_destination, word)
test api insert <user.word> and <user.word> <user.cursorless_destination>:
user.cursorless_insert(cursorless_destination, word_list)
test api insert snippet:
user.cursorless_insert_snippet("Hello, $foo! My name is $bar!")
test api insert snippet by name:
Expand Down
4 changes: 0 additions & 4 deletions cursorless-talon-dev/src/default_vocabulary.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@

# https://github.com/talonhub/community/blob/9acb6c9659bb0c9b794a7b7126d025603b4ed726/core/keys/keys.py#L139C1-L171C2
punctuation_words = {
# TODO: I'm not sure why we need these, I think it has something to do with
# Dragon. Possibly it has been fixed by later improvements to talon? -rntz
# "`": "`",
# ",": ",", # <== these things
"back tick": "`",
"comma": ",",
# Workaround for issue with conformer b-series; see #946
Expand Down
30 changes: 23 additions & 7 deletions cursorless-talon/src/actions/actions.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
from typing import Callable, Union

from talon import Module, actions

from ..targets.target_types import CursorlessTarget, ImplicitDestination
from ..targets.target_types import (
CursorlessDestination,
CursorlessTarget,
ImplicitDestination,
)
from .bring_move import BringMoveTargets
from .call import cursorless_call_action
from .execute_command import cursorless_execute_command_action
from .homophones import cursorless_homophones_action
from .replace import cursorless_replace_action

mod = Module()


mod.list(
"cursorless_simple_action",
desc="Cursorless internal: simple actions",
Expand Down Expand Up @@ -38,11 +43,11 @@
"wrap_action",
"insert_snippet_action",
"reformat_action",
"call_action",
"experimental_action",
]

callback_actions = {
"callAsFunction": cursorless_call_action,
callback_actions: dict[str, Callable[[CursorlessTarget], None]] = {
"findInDocument": actions.user.private_cursorless_find,
"nextHomophone": cursorless_homophones_action,
}
Expand All @@ -64,10 +69,11 @@
"{user.cursorless_simple_action} |"
"{user.cursorless_experimental_action} |"
"{user.cursorless_callback_action} |"
"{user.cursorless_call_action} |"
"{user.cursorless_custom_action}"
)
)
def cursorless_action_or_ide_command(m) -> dict:
def cursorless_action_or_ide_command(m) -> dict[str, str]:
try:
value = m.cursorless_custom_action
type = "ide_command"
Expand All @@ -90,6 +96,8 @@ def cursorless_command(action_name: str, target: CursorlessTarget):
actions.user.private_cursorless_bring_move(
action_name, BringMoveTargets(target, ImplicitDestination())
)
elif action_name == "callAsFunction":
actions.user.private_cursorless_call(target)
elif action_name in no_wait_actions:
action = {"name": action_name, "target": target}
actions.user.private_cursorless_command_no_wait(action)
Expand All @@ -111,8 +119,16 @@ def cursorless_ide_command(command_id: str, target: CursorlessTarget):
"""Perform ide command on cursorless target"""
return cursorless_execute_command_action(command_id, target)

def cursorless_insert(
destination: CursorlessDestination, text: Union[str, list[str]]
):
"""Perform text insertion on Cursorless destination"""
if isinstance(text, str):
text = [text]
cursorless_replace_action(destination, text)

def private_cursorless_action_or_ide_command(
instruction: dict, target: CursorlessTarget
instruction: dict[str, str], target: CursorlessTarget
):
"""Perform cursorless action or ide command on target (internal use only)"""
type = instruction["type"]
Expand Down
27 changes: 18 additions & 9 deletions cursorless-talon/src/actions/call.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
from talon import actions
from talon import Module, actions

from ..targets.target_types import CursorlessTarget, ImplicitTarget

mod = Module()
mod.list("cursorless_call_action", desc="Cursorless call action")

def cursorless_call_action(target: CursorlessTarget):
actions.user.private_cursorless_command_and_wait(
{
"name": "callAsFunction",
"callee": target,
"argument": ImplicitTarget(),
}
)

@mod.action_class
class Actions:
def private_cursorless_call(
callee: CursorlessTarget,
argument: CursorlessTarget = ImplicitTarget(),
):
"""Execute Cursorless call action"""
actions.user.private_cursorless_command_and_wait(
{
"name": "callAsFunction",
"callee": callee,
"argument": argument,
}
)
3 changes: 2 additions & 1 deletion cursorless-talon/src/actions/get_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@

def cursorless_get_text_action(
target: CursorlessTarget,
*,
show_decorations: Optional[bool] = None,
ensure_single_target: Optional[bool] = None,
) -> list[str]:
"""Get target texts"""
options = {}
options: dict[str, bool] = {}

if show_decorations is not None:
options["showDecorations"] = show_decorations
Expand Down
14 changes: 9 additions & 5 deletions cursorless-talon/src/actions/homophones.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
from typing import Optional

from talon import actions, app

from ..targets.target_types import CursorlessTarget, PrimitiveDestination
from .get_text import cursorless_get_text_action
from .replace import cursorless_replace_action


def cursorless_homophones_action(target: dict):
def cursorless_homophones_action(target: CursorlessTarget):
"""Replaced target with next homophone"""
texts = cursorless_get_text_action(target, show_decorations=False)
try:
updated_texts = list(map(get_next_homophone, texts))
except LookupError as e:
app.notify(str(e))
return
cursorless_replace_action(target, updated_texts)
destination = PrimitiveDestination("to", target)
cursorless_replace_action(destination, updated_texts)


def get_next_homophone(word: str):
homophones = actions.user.homophones_get(word)
def get_next_homophone(word: str) -> str:
homophones: Optional[list[str]] = actions.user.homophones_get(word)
if not homophones:
raise LookupError(f"Found no homophones for '{word}'")
index = (homophones.index(word.lower()) + 1) % len(homophones)
homophone = homophones[index]
return format_homophone(word, homophone)


def format_homophone(word: str, homophone: str):
def format_homophone(word: str, homophone: str) -> str:
if word.isupper():
return homophone.upper()
if word == word.capitalize():
Expand Down
5 changes: 3 additions & 2 deletions cursorless-talon/src/actions/reformat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from talon import Module, actions

from ..targets.target_types import CursorlessTarget
from ..targets.target_types import CursorlessTarget, PrimitiveDestination
from .get_text import cursorless_get_text_action
from .replace import cursorless_replace_action

Expand All @@ -15,4 +15,5 @@ def private_cursorless_reformat(target: CursorlessTarget, formatters: str):
"""Execute Cursorless reformat action. Reformat target with formatter"""
texts = cursorless_get_text_action(target, show_decorations=False)
updated_texts = [actions.user.reformat_text(text, formatters) for text in texts]
cursorless_replace_action(target, updated_texts)
destination = PrimitiveDestination("to", target)
cursorless_replace_action(destination, updated_texts)
8 changes: 5 additions & 3 deletions cursorless-talon/src/actions/replace.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
from talon import actions

from ..targets.target_types import CursorlessTarget, PrimitiveDestination
from ..targets.target_types import CursorlessDestination


def cursorless_replace_action(target: CursorlessTarget, replace_with: list[str]):
def cursorless_replace_action(
destination: CursorlessDestination, replace_with: list[str]
):
"""Execute Cursorless replace action. Replace targets with texts"""
actions.user.private_cursorless_command_and_wait(
{
"name": "replace",
"replaceWith": replace_with,
"destination": PrimitiveDestination("to", target),
"destination": destination,
}
)
35 changes: 25 additions & 10 deletions cursorless-talon/src/cheatsheet/get_list.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,48 @@
import re
from collections.abc import Mapping, Sequence
from typing import Optional, TypedDict

from talon import registry

from ..conventions import get_cursorless_list_name


def get_list(name, type, descriptions=None):
class Variation(TypedDict):
spokenForm: str
description: str


class ListItemDescriptor(TypedDict):
id: str
type: str
variations: list[Variation]


def get_list(
name: str, type: str, descriptions: Optional[Mapping[str, str]] = None
) -> list[ListItemDescriptor]:
if descriptions is None:
descriptions = {}

items = get_raw_list(name)
item_dict = items if isinstance(items, dict) else {item: item for item in items}

return make_dict_readable(type, item_dict, descriptions)
return make_dict_readable(type, items, descriptions)


def get_lists(names: list[str], type: str, descriptions=None):
def get_lists(
names: Sequence[str], type: str, descriptions: Optional[Mapping[str, str]] = None
) -> list[ListItemDescriptor]:
return [item for name in names for item in get_list(name, type, descriptions)]


def get_raw_list(name):
def get_raw_list(name: str) -> Mapping[str, str]:
cursorless_list_name = get_cursorless_list_name(name)
return registry.lists[cursorless_list_name][0].copy()


def make_dict_readable(type: str, dict, descriptions=None):
if descriptions is None:
descriptions = {}

def make_dict_readable(
type: str, dict: Mapping[str, str], descriptions: Mapping[str, str]
) -> list[ListItemDescriptor]:
return [
{
"id": value,
Expand All @@ -43,7 +58,7 @@ def make_dict_readable(type: str, dict, descriptions=None):
]


def make_readable(text):
def make_readable(text: str) -> str:
text = text.replace(".", " ")
return de_camel(text).lower().capitalize()

Expand Down
Loading

0 comments on commit 01c577d

Please sign in to comment.