Skip to content

Commit

Permalink
updates
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreasArvidsson committed Feb 12, 2024
1 parent f2bf6ac commit 3c325d6
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 79 deletions.
90 changes: 60 additions & 30 deletions cursorless-talon/src/fallback.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from typing import Callable
from talon import actions

action_callbacks = {
Expand All @@ -8,69 +9,98 @@
"pasteFromClipboard": actions.edit.paste,
"clearAndSetSelection": actions.edit.delete,
"remove": actions.edit.delete,
"applyFormatter": actions.user.reformat_selection,
"editNewLineBefore": actions.edit.line_insert_up,
"editNewLineAfter": actions.edit.line_insert_down,
"nextHomophone": actions.user.homophones_cycle_selected,
# "replaceWithTarget": replace_with_target,
}

scope_callbacks = {
"cursor": actions.skip,
modifier_callbacks = {
"extendThroughStartOf.line": actions.user.select_line_start,
"extendThroughEndOf.line": actions.user.select_line_end,
"containing.document": actions.edit.select_all,
"containing.paragraph": actions.edit.select_paragraph,
"containing.line": actions.edit.select_line,
"containing.token": actions.edit.select_word,
"containingScope.document": actions.edit.select_all,
"containingScope.paragraph": actions.edit.select_paragraph,
"containingScope.line": actions.edit.select_line,
"containingScope.token": actions.edit.select_word,
}


def callAsFunction(callee: str):
def call_as_function(callee: str):
actions.insert(f"{callee}()")
actions.edit.left()


def wrap_with_paired_delimiter(left: str, right: str):
selected = actions.edit.selected_text()
actions.insert(f"{left}{selected}{right}")
for _ in right:
actions.edit.left()


def containing_token_if_empty():
if actions.edit.selected_text() == "":
actions.edit.select_word()


def perform_fallback(fallback: dict):
try:
scope_callback = get_scope_callback(fallback)
modifier_callbacks = get_modifier_callbacks(fallback)
action_callback = get_action_callback(fallback)
scope_callback()
for callback in reversed(modifier_callbacks):
callback()
return action_callback()
except ValueError as ex:
actions.app.notify(str(ex))


def get_action_callback(fallback: dict):
def get_action_callback(fallback: dict) -> Callable:
action = fallback["action"]

if action in action_callbacks:
return action_callbacks[action]

if action == "insert":
return lambda: actions.insert(fallback["text"])
match action:
case "insert":
return lambda: actions.insert(fallback["text"])
case "callAsFunction":
return lambda: call_as_function(fallback["callee"])
case "wrapWithPairedDelimiter":
return lambda: wrap_with_paired_delimiter(
fallback["left"], fallback["right"]
)

if action == "callAsFunction":
return lambda: callAsFunction(fallback["callee"])
raise ValueError(f"Unknown Cursorless fallback action: {action}")

if action == "wrapWithPairedDelimiter":
return lambda: actions.user.delimiters_pair_wrap_selection_with(
fallback["left"], fallback["right"]
)

raise ValueError(f"Unknown Cursorless fallback action: {action}")
def get_modifier_callbacks(fallback: dict) -> list[Callable]:
modifiers = fallback["modifiers"]
callbacks = []

for modifier in modifiers:
callbacks.append(get_modifier_callback(modifier))

return callbacks


def get_scope_callback(fallback: dict):
if "scope" not in fallback:
return actions.skip
def get_modifier_callback(modifier: dict) -> Callable:
modifier_type = modifier["type"]

scope = fallback["scope"]
match modifier_type:
case "containingTokenIfEmpty":
return containing_token_if_empty
case "containingScope":
scope_type_type = modifier["scopeType"]["type"]
return get_simple_modifier_callback(f"{modifier_type}.{scope_type_type}")
case "extendThroughStartOf":
if "modifiers" not in modifier:
return get_simple_modifier_callback(f"{modifier_type}.line")
case "extendThroughEndOf":
if "modifiers" not in modifier:
return get_simple_modifier_callback(f"{modifier_type}.line")

if scope is None:
return actions.skip
raise ValueError(f"Unknown Cursorless fallback modifier: {modifier_type}")

if scope in scope_callbacks:
return scope_callbacks[scope]

raise ValueError(f"Unknown Cursorless fallback scope: {scope}")
def get_simple_modifier_callback(key: str) -> Callable:
if key in modifier_callbacks:
return modifier_callbacks[key]
raise ValueError(f"Unknown Cursorless fallback modifier: {key}")
18 changes: 13 additions & 5 deletions packages/cursorless-engine/src/api/CursorlessEngineApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Command, HatTokenMap, IDE } from "@cursorless/common";
import {
ActionDescriptor,
Command,
HatTokenMap,
IDE,
Modifier,
} from "@cursorless/common";
import { Snippets } from "../core/Snippets";
import { StoredTargetMap } from "../core/StoredTargets";
import { ScopeProvider } from "@cursorless/common";
Expand Down Expand Up @@ -45,13 +51,15 @@ export interface CommandApi {

export type CommandResponse = { returnValue: unknown } | { fallback: Fallback };

export type FallbackModifier = Modifier | { type: "containingTokenIfEmpty" };

export type Fallback =
| { action: string; scope: string | null }
| { action: "insert"; scope: string | null; text: string }
| { action: "callAsFunction"; scope: string | null; callee: string }
| { action: ActionDescriptor["name"]; modifiers: FallbackModifier[] }
| { action: "insert"; modifiers: FallbackModifier[]; text: string }
| { action: "callAsFunction"; modifiers: FallbackModifier[]; callee: string }
| {
action: "wrapWithPairedDelimiter" | "rewrapWithPairedDelimiter";
scope: string | null;
modifiers: FallbackModifier[];
left: string;
right: string;
};
Expand Down
74 changes: 30 additions & 44 deletions packages/cursorless-engine/src/core/getCommandFallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,20 @@ import {
CommandComplete,
CommandServerApi,
DestinationDescriptor,
LATEST_VERSION,
PartialTargetDescriptor,
} from "@cursorless/common";

import { CommandRunner } from "..";
import { Fallback } from "../api/CursorlessEngineApi";
import { Fallback, FallbackModifier } from "../api/CursorlessEngineApi";
import { CommandRunner } from "../CommandRunner";

export async function getCommandFallback(
commandServerApi: CommandServerApi | null,
commandRunner: CommandRunner,
command: CommandComplete,
): Promise<Fallback | null> {
if (commandServerApi == null) {
return null;
}

const focusedElement = commandServerApi?.getFocusedElementType();

if (focusedElement === "textEditor") {
if (
commandServerApi == null ||
commandServerApi.getFocusedElementType() === "textEditor"
) {
return null;
}

Expand All @@ -32,7 +27,7 @@ export async function getCommandFallback(
Array.isArray(action.replaceWith)
? {
action: "insert",
scope: getScopeFromDestination(action.destination),
modifiers: getModifiersFromDestination(action.destination),
text: action.replaceWith.join("\n"),
}
: null;
Expand All @@ -41,7 +36,7 @@ export async function getCommandFallback(
if (destinationIsSelection(action.destination)) {
return {
action: "insert",
scope: getScopeFromDestination(action.destination),
modifiers: getModifiersFromDestination(action.destination),
text: await getText(
commandRunner,
command.usePrePhraseSnapshot,
Expand All @@ -65,7 +60,7 @@ export async function getCommandFallback(
);
return {
action: "insert",
scope: getScopeFromDestination(action.destination),
modifiers: getModifiersFromDestination(action.destination),
text,
};
}
Expand All @@ -75,7 +70,7 @@ export async function getCommandFallback(
if (targetIsSelection(action.argument)) {
return {
action: action.name,
scope: getScopeFromTarget(action.argument),
modifiers: getModifiersFromTarget(action.argument),
callee: await getText(
commandRunner,
command.usePrePhraseSnapshot,
Expand All @@ -90,7 +85,7 @@ export async function getCommandFallback(
return targetIsSelection(action.target)
? {
action: action.name,
scope: getScopeFromTarget(action.target),
modifiers: getModifiersFromTarget(action.target),
left: action.left,
right: action.right,
}
Expand All @@ -100,7 +95,7 @@ export async function getCommandFallback(
return destinationIsSelection(action.destination)
? {
action: action.name,
scope: getScopeFromDestination(action.destination),
modifiers: getModifiersFromDestination(action.destination),
}
: null;

Expand All @@ -113,7 +108,10 @@ export async function getCommandFallback(

default:
return targetIsSelection(action.target)
? { action: action.name, scope: getScopeFromTarget(action.target) }
? {
action: action.name,
modifiers: getModifiersFromTarget(action.target),
}
: null;
}
}
Expand Down Expand Up @@ -141,40 +139,28 @@ function targetIsSelection(target: PartialTargetDescriptor): boolean {
return false;
}

function getScopeFromDestination(
function getModifiersFromDestination(
destination: DestinationDescriptor,
): string | null {
): FallbackModifier[] {
if (destination.type === "primitive") {
return getScopeFromTarget(destination.target);
return getModifiersFromTarget(destination.target);
}
return null;
return [];
}

function getScopeFromTarget(target: PartialTargetDescriptor): string | null {
function getModifiersFromTarget(
target: PartialTargetDescriptor,
): FallbackModifier[] {
if (target.type === "primitive") {
if (target.modifiers != null && target.modifiers.length > 0) {
const modifier = target.modifiers[0];

switch (modifier.type) {
case "containingScope":
return `containing.${modifier.scopeType.type}`;
case "extendThroughStartOf":
case "extendThroughEndOf":
if (modifier.modifiers == null) {
return `${modifier.type}.line`;
}
}

throw Error(
`Unknown Cursorless fallback modifier type: ${modifier.type}`,
);
return target.modifiers;
}

if (target.mark?.type === "cursor") {
return target.mark.type;
return [{ type: "containingTokenIfEmpty" }];
}
}
return null;
return [];
}

async function getText(
Expand All @@ -183,15 +169,15 @@ async function getText(
target: PartialTargetDescriptor,
): Promise<string> {
const returnValue = await commandRunner.run({
version: LATEST_VERSION,
version: 7,
usePrePhraseSnapshot,
action: {
name: "getText",
target,
},
});
const replaceWith = returnValue as string[];
return replaceWith.join("\n");
const texts = returnValue as string[];
return texts.join("\n");
}

function remove(
Expand All @@ -200,7 +186,7 @@ function remove(
target: PartialTargetDescriptor,
): Promise<unknown> {
return commandRunner.run({
version: LATEST_VERSION,
version: 7,
usePrePhraseSnapshot,
action: {
name: "remove",
Expand Down

0 comments on commit 3c325d6

Please sign in to comment.