Skip to content

Commit

Permalink
Merge branch 'cursorless-dev:main' into cursorless-jetbrains
Browse files Browse the repository at this point in the history
  • Loading branch information
asoee authored Jan 17, 2025
2 parents 43ba5ac + 6db1575 commit 5362b97
Show file tree
Hide file tree
Showing 166 changed files with 3,614 additions and 1,465 deletions.
6 changes: 6 additions & 0 deletions changelog/2025-01-addedCollectionItemScopeProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
tags: [enhancement]
pullRequest: 2683
---

At long last, collection items have been migrated to our next generation scope framework! This means, within a list of items, you can now use relative navigation (`previous item`), absolute navigation via ordinals (`fifth item`), multiple selection (`two items`, optionally preceded with `previous` or `next`), and lastly, requesting multiple items to be individually selected via `every` (`every two items`)!
19 changes: 17 additions & 2 deletions cursorless-everywhere-talon/cursorless_everywhere_talon.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@

from talon import Context, Module, actions

from .cursorless_everywhere_types import EditorEdit, EditorState, SelectionOffsets
from .cursorless_everywhere_types import (
EditorEdit,
EditorState,
RangeOffsets,
SelectionOffsets,
)

mod = Module()

Expand All @@ -14,6 +19,8 @@
tag: user.cursorless_everywhere_talon
"""

ctx.tags = ["user.cursorless"]


@ctx.action_class("user")
class UserActions:
Expand Down Expand Up @@ -55,6 +62,14 @@ def cursorless_everywhere_edit_text(
):
"""Edit focused element text"""

def cursorless_everywhere_flash_ranges(
ranges: list[RangeOffsets], # pyright: ignore [reportGeneralTypeIssues]
):
"""Flash ranges in focused element"""
actions.skip()

# Private actions

def private_cursorless_talonjs_run_and_wait(
command_id: str, # pyright: ignore [reportGeneralTypeIssues]
arg1: Any = None,
Expand All @@ -69,5 +84,5 @@ def private_cursorless_talonjs_run_no_wait(
):
"""Executes a Cursorless command, but does not wait for it to finish, nor return the response"""

def private_cursorless_talonjs_get_response_json() -> str:
def private_cursorless_talonjs_get_response_json() -> str: # pyright: ignore [reportReturnType]
"""Returns the response from the last Cursorless command"""
99 changes: 99 additions & 0 deletions cursorless-everywhere-talon/cursorless_everywhere_talon_browser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from talon import Context, Module, actions

from .cursorless_everywhere_types import (
EditorEdit,
EditorState,
RangeOffsets,
SelectionOffsets,
)

mod = Module()

mod.tag(
"cursorless_everywhere_talon_browser",
desc="Enable RPC to browser extension when using cursorless everywhere in Talon",
)

ctx = Context()
ctx.matches = r"""
tag: user.cursorless_everywhere_talon_browser
"""

RPC_COMMAND = "talonCommand"


@ctx.action_class("user")
class Actions:
def cursorless_everywhere_get_editor_state() -> EditorState:
command = {
"id": "getEditorState",
}
res = rpc_get(command)
if use_fallback(res):
return actions.next()
return res

def cursorless_everywhere_set_selections(
selections: list[SelectionOffsets], # pyright: ignore [reportGeneralTypeIssues]
):
command = {
"id": "setSelections",
"selections": [
js_object_to_python_dict(s, ["anchor", "active"])
for s in js_array_to_python_list(selections)
],
}
res = rpc_get(command)
if use_fallback(res):
actions.next(selections)

def cursorless_everywhere_edit_text(
edit: EditorEdit, # pyright: ignore [reportGeneralTypeIssues]
):
command = {
"id": "editText",
"text": edit["text"],
"changes": [
js_object_to_python_dict(c, ["text", "rangeOffset", "rangeLength"])
for c in js_array_to_python_list(edit["changes"])
],
}
res = rpc_get(command)
if use_fallback(res):
actions.next(edit)

def cursorless_everywhere_flash_ranges(
ranges: list[RangeOffsets], # pyright: ignore [reportGeneralTypeIssues]
):
command = {
"id": "flashRanges",
"ranges": [
js_object_to_python_dict(r, ["start", "end"])
for r in js_array_to_python_list(ranges)
],
}
res = rpc_get(command)
if use_fallback(res):
actions.next(ranges)


def rpc_get(command: dict):
return actions.user.run_rpc_command_get(RPC_COMMAND, command)


def use_fallback(result: dict) -> bool:
return result.get("fallback", False)


def js_array_to_python_list(array) -> list:
result = []
for i in range(array.length):
result.append(array[i])
return result


def js_object_to_python_dict(object, keys: list[str]) -> dict:
result = {}
for key in keys:
result[key] = object[key]
return result
5 changes: 5 additions & 0 deletions cursorless-everywhere-talon/cursorless_everywhere_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ class SelectionOffsets(TypedDict):
active: int


class RangeOffsets(TypedDict):
start: int
end: int


class EditorState(TypedDict):
text: str
selections: list[SelectionOffsets]
Expand Down
1 change: 1 addition & 0 deletions cursorless-talon/.github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Please file pull requests to the cursorless-talon subdirectory in the https://github.com/cursorless-dev/cursorless repo
2 changes: 0 additions & 2 deletions cursorless-talon/src/apps/vscode_settings.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
import traceback
from pathlib import Path
from typing import Any

Expand Down Expand Up @@ -61,7 +60,6 @@ def vscode_get_setting_with_fallback(
return actions.user.vscode_get_setting(key, default_value), False
except Exception:
print(fallback_message)
traceback.print_exc()
return fallback_value, True


Expand Down
37 changes: 37 additions & 0 deletions cursorless-talon/src/check_community_repo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from talon import app, registry

required_captures = [
"number_small",
"user.any_alphanumeric_key",
"user.formatters",
"user.ordinals_small",
]

required_actions = [
"user.homophones_get",
"user.reformat_text",
]


def on_ready():
missing_captures = [
capture for capture in required_captures if capture not in registry.captures
]
missing_actions = [
action for action in required_actions if action not in registry.actions
]
errors = []
if missing_captures:
errors.append(f"Missing captures: {', '.join(missing_captures)}")
if missing_actions:
errors.append(f"Missing actions: {', '.join(missing_actions)}")
if errors:
print("Cursorless community requirements:")
print("\n".join(errors))
app.notify(
"Cursorless: Please install the community repository",
body="https://github.com/talonhub/community",
)


app.register("ready", on_ready)
4 changes: 2 additions & 2 deletions cursorless-talon/src/cursorless.talon
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,5 @@ tutorial (previous | last): user.private_cursorless_tutorial_previous()
tutorial restart: user.private_cursorless_tutorial_restart()
tutorial resume: user.private_cursorless_tutorial_resume()
tutorial (list | close): user.private_cursorless_tutorial_list()
tutorial <user.private_cursorless_number_small>:
user.private_cursorless_tutorial_start_by_number(private_cursorless_number_small)
tutorial <number_small>:
user.private_cursorless_tutorial_start_by_number(number_small)
5 changes: 2 additions & 3 deletions cursorless-talon/src/get_grapheme_spoken_form_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import typing
from collections import defaultdict
from typing import Iterator, Mapping
from uu import Error

from talon import app, registry, scope

Expand Down Expand Up @@ -55,7 +54,7 @@ def generate_lists_from_capture(capture_name) -> Iterator[str]:
try:
# NB: [-1] because the last capture is the active one
rule = registry.captures[capture_name][-1].rule.rule
except Error:
except Exception:
app.notify("Error constructing spoken forms for graphemes")
print(f"Error getting rule for capture {capture_name}")
return
Expand Down Expand Up @@ -86,7 +85,7 @@ def get_id_to_talon_list(list_name: str) -> dict[str, str]:
try:
# NB: [-1] because the last list is the active one
return typing.cast(dict[str, str], registry.lists[list_name][-1]).copy()
except Error:
except Exception:
app.notify(f"Error getting list {list_name}")
return {}

Expand Down
13 changes: 10 additions & 3 deletions cursorless-talon/src/marks/decorated_mark.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,12 @@ def setup_hat_styles_csv(hat_colors: dict[str, str], hat_shapes: dict[str, str])
def init_hats(hat_colors: dict[str, str], hat_shapes: dict[str, str]):
setup_hat_styles_csv(hat_colors, hat_shapes)

vscode_settings_path: Path = actions.user.vscode_settings_path().resolve()
vscode_settings_path: Path | None = None

try:
vscode_settings_path = actions.user.vscode_settings_path().resolve()
except Exception as ex:
print(ex)

def on_watch(path, flags):
global fast_reload_job, slow_reload_job
Expand All @@ -166,10 +171,12 @@ def on_watch(path, flags):
"10s", lambda: setup_hat_styles_csv(hat_colors, hat_shapes)
)

fs.watch(str(vscode_settings_path), on_watch)
if vscode_settings_path is not None:
fs.watch(vscode_settings_path, on_watch)

def unsubscribe():
fs.unwatch(str(vscode_settings_path), on_watch)
if vscode_settings_path is not None:
fs.unwatch(vscode_settings_path, on_watch)
if unsubscribe_hat_styles is not None:
unsubscribe_hat_styles()

Expand Down
6 changes: 3 additions & 3 deletions cursorless-talon/src/marks/lines_number.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ class CustomizableTerm:

@mod.capture(
rule=(
"{user.cursorless_line_direction} <user.private_cursorless_number_small> "
"[<user.cursorless_range_connective> <user.private_cursorless_number_small>]"
"{user.cursorless_line_direction} <number_small> "
"[<user.cursorless_range_connective> <number_small>]"
)
)
def cursorless_line_number(m) -> LineNumber:
direction = directions_map[m.cursorless_line_direction]
numbers: list[int] = m.private_cursorless_number_small_list
numbers: list[int] = m.number_small_list
anchor = create_line_number_mark(direction.type, direction.formatter(numbers[0]))
if len(numbers) > 1:
active = create_line_number_mark(
Expand Down
8 changes: 4 additions & 4 deletions cursorless-talon/src/modifiers/ordinal_scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def cursorless_ordinal_range(m) -> dict[str, Any]:
rule=(
"[{user.cursorless_every_scope_modifier}] "
"({user.cursorless_first_modifier} | {user.cursorless_last_modifier}) "
"<user.private_cursorless_number_small> <user.cursorless_scope_type_plural>"
"<number_small> <user.cursorless_scope_type_plural>"
),
)
def cursorless_first_last(m) -> dict[str, Any]:
Expand All @@ -57,13 +57,13 @@ def cursorless_first_last(m) -> dict[str, Any]:
return create_ordinal_scope_modifier(
m.cursorless_scope_type_plural,
0,
m.private_cursorless_number_small,
m.number_small,
is_every,
)
return create_ordinal_scope_modifier(
m.cursorless_scope_type_plural,
-m.private_cursorless_number_small,
m.private_cursorless_number_small,
-m.number_small,
m.number_small,
is_every,
)

Expand Down
8 changes: 4 additions & 4 deletions cursorless-talon/src/modifiers/relative_scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,28 @@ def cursorless_relative_scope_singular(m) -> dict[str, Any]:


@mod.capture(
rule="[{user.cursorless_every_scope_modifier}] <user.cursorless_relative_direction> <user.private_cursorless_number_small> <user.cursorless_scope_type_plural>"
rule="[{user.cursorless_every_scope_modifier}] <user.cursorless_relative_direction> <number_small> <user.cursorless_scope_type_plural>"
)
def cursorless_relative_scope_plural(m) -> dict[str, Any]:
"""Relative previous/next plural scope. `next three funks`"""
return create_relative_scope_modifier(
m.cursorless_scope_type_plural,
1,
m.private_cursorless_number_small,
m.number_small,
m.cursorless_relative_direction,
hasattr(m, "cursorless_every_scope_modifier"),
)


@mod.capture(
rule="[{user.cursorless_every_scope_modifier}] <user.private_cursorless_number_small> <user.cursorless_scope_type_plural> [{user.cursorless_forward_backward_modifier}]"
rule="[{user.cursorless_every_scope_modifier}] <number_small> <user.cursorless_scope_type_plural> [{user.cursorless_forward_backward_modifier}]"
)
def cursorless_relative_scope_count(m) -> dict[str, Any]:
"""Relative count scope. `three funks`"""
return create_relative_scope_modifier(
m.cursorless_scope_type_plural,
0,
m.private_cursorless_number_small,
m.number_small,
getattr(m, "cursorless_forward_backward_modifier", "forward"),
hasattr(m, "cursorless_every_scope_modifier"),
)
Expand Down
Loading

0 comments on commit 5362b97

Please sign in to comment.