diff --git a/cursorless-everywhere-talon/cursorless_everywhere_talon.py b/cursorless-everywhere-talon/cursorless_everywhere_talon.py index 5cafb4d8fc..2b913248e1 100644 --- a/cursorless-everywhere-talon/cursorless_everywhere_talon.py +++ b/cursorless-everywhere-talon/cursorless_everywhere_talon.py @@ -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() @@ -57,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, @@ -71,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""" diff --git a/cursorless-everywhere-talon/cursorless_everywhere_talon_browser.py b/cursorless-everywhere-talon/cursorless_everywhere_talon_browser.py index e7e7875815..826b52df35 100644 --- a/cursorless-everywhere-talon/cursorless_everywhere_talon_browser.py +++ b/cursorless-everywhere-talon/cursorless_everywhere_talon_browser.py @@ -3,6 +3,7 @@ from .cursorless_everywhere_types import ( EditorEdit, EditorState, + RangeOffsets, SelectionOffsets, ) @@ -61,6 +62,20 @@ def cursorless_everywhere_edit_text( 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) diff --git a/cursorless-everywhere-talon/cursorless_everywhere_types.py b/cursorless-everywhere-talon/cursorless_everywhere_types.py index df1247fe3f..44a41af5c9 100644 --- a/cursorless-everywhere-talon/cursorless_everywhere_types.py +++ b/cursorless-everywhere-talon/cursorless_everywhere_types.py @@ -6,6 +6,11 @@ class SelectionOffsets(TypedDict): active: int +class RangeOffsets(TypedDict): + start: int + end: int + + class EditorState(TypedDict): text: str selections: list[SelectionOffsets] diff --git a/packages/cursorless-everywhere-talon-core/src/ide/TalonJsIDE.ts b/packages/cursorless-everywhere-talon-core/src/ide/TalonJsIDE.ts index eac3f302c4..48c3623ed6 100644 --- a/packages/cursorless-everywhere-talon-core/src/ide/TalonJsIDE.ts +++ b/packages/cursorless-everywhere-talon-core/src/ide/TalonJsIDE.ts @@ -24,14 +24,14 @@ import { Notifier, type KeyValueStore } from "@cursorless/common"; import { pull } from "lodash-es"; import type { Talon } from "../types/talon.types"; import type { EditorState } from "../types/types"; +import { createTextEditor } from "./createTextEditor"; +import { flashRanges } from "./flashRanges"; import { TalonJsCapabilities } from "./TalonJsCapabilities"; import { TalonJsClipboard } from "./TalonJsClipboard"; import { TalonJsConfiguration } from "./TalonJsConfiguration"; import { TalonJsEditor } from "./TalonJsEditor"; -import { TalonJsMessages } from "./TalonJsMessages"; - -import { createTextEditor } from "./createTextEditor"; import { TalonJsKeyValueStore } from "./TalonJsKeyValueStore"; +import { TalonJsMessages } from "./TalonJsMessages"; export class TalonJsIDE implements IDE { configuration: Configuration; @@ -134,8 +134,8 @@ export class TalonJsIDE implements IDE { throw new Error("executeCommand not implemented."); } - flashRanges(_flashDescriptors: FlashDescriptor[]): Promise { - return Promise.resolve(); + flashRanges(flashDescriptors: FlashDescriptor[]): Promise { + return flashRanges(this.talon, flashDescriptors); } setHighlightRanges( diff --git a/packages/cursorless-everywhere-talon-core/src/ide/flashRanges.ts b/packages/cursorless-everywhere-talon-core/src/ide/flashRanges.ts new file mode 100644 index 0000000000..124262d1c1 --- /dev/null +++ b/packages/cursorless-everywhere-talon-core/src/ide/flashRanges.ts @@ -0,0 +1,18 @@ +import type { FlashDescriptor } from "@cursorless/common"; +import type { Talon } from "../types/talon.types"; +import type { RangeOffsets } from "../types/types"; +import { toCharacterRangeOffsets } from "./toCharacterRangeOffsets"; + +export function flashRanges( + talon: Talon, + flashDescriptors: FlashDescriptor[], +): Promise { + const ranges = flashDescriptors.map( + (descriptor): RangeOffsets => + toCharacterRangeOffsets(descriptor.editor, descriptor.range), + ); + + talon.actions.user.cursorless_everywhere_flash_ranges(ranges); + + return Promise.resolve(); +} diff --git a/packages/cursorless-everywhere-talon-core/src/ide/toCharacterRangeOffsets.ts b/packages/cursorless-everywhere-talon-core/src/ide/toCharacterRangeOffsets.ts new file mode 100644 index 0000000000..2feedd6829 --- /dev/null +++ b/packages/cursorless-everywhere-talon-core/src/ide/toCharacterRangeOffsets.ts @@ -0,0 +1,20 @@ +import type { GeneralizedRange, TextEditor } from "@cursorless/common"; +import type { RangeOffsets } from "../types/types"; + +export function toCharacterRangeOffsets( + editor: TextEditor, + range: GeneralizedRange, +): RangeOffsets { + if (range.type === "line") { + const startLine = editor.document.lineAt(range.start).range; + const endLine = editor.document.lineAt(range.end).range; + return { + start: editor.document.offsetAt(startLine.start), + end: editor.document.offsetAt(endLine.end), + }; + } + return { + start: editor.document.offsetAt(range.start), + end: editor.document.offsetAt(range.end), + }; +} diff --git a/packages/cursorless-everywhere-talon-core/src/types/talon.types.ts b/packages/cursorless-everywhere-talon-core/src/types/talon.types.ts index 4cf6e74fdb..2381484745 100644 --- a/packages/cursorless-everywhere-talon-core/src/types/talon.types.ts +++ b/packages/cursorless-everywhere-talon-core/src/types/talon.types.ts @@ -1,4 +1,9 @@ -import type { EditorEdit, EditorState, SelectionOffsets } from "./types"; +import type { + RangeOffsets, + EditorEdit, + EditorState, + SelectionOffsets, +} from "./types"; export type TalonNamespace = "user"; @@ -17,6 +22,7 @@ export interface TalonActions { cursorless_everywhere_get_editor_state(): EditorState; cursorless_everywhere_set_selections(selections: SelectionOffsets[]): void; cursorless_everywhere_edit_text(edit: EditorEdit): void; + cursorless_everywhere_flash_ranges(ranges: RangeOffsets[]): void; }; } diff --git a/packages/cursorless-everywhere-talon-core/src/types/types.ts b/packages/cursorless-everywhere-talon-core/src/types/types.ts index 979b10d8cb..ba2c8c0fb0 100644 --- a/packages/cursorless-everywhere-talon-core/src/types/types.ts +++ b/packages/cursorless-everywhere-talon-core/src/types/types.ts @@ -14,6 +14,11 @@ export interface SelectionOffsets { active: number; } +export interface RangeOffsets { + start: number; + end: number; +} + export interface EditorState { text: string; languageId?: string; diff --git a/packages/cursorless-everywhere-talon-e2e/src/talonMock.ts b/packages/cursorless-everywhere-talon-e2e/src/talonMock.ts index a1ad69876b..94a2c47595 100644 --- a/packages/cursorless-everywhere-talon-e2e/src/talonMock.ts +++ b/packages/cursorless-everywhere-talon-e2e/src/talonMock.ts @@ -1,4 +1,5 @@ import type { + RangeOffsets, EditorEdit, EditorState, SelectionOffsets, @@ -53,6 +54,9 @@ const actions: TalonActions = { } _finalEditorState.text = edit.text; }, + cursorless_everywhere_flash_ranges(_ranges: RangeOffsets[]): void { + // Do nothing + }, }, };