From 03761b27b30ad9c4e2ab93586163f0b310c71fa0 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sat, 8 Jul 2023 17:00:47 +0200 Subject: [PATCH 01/99] Started working on destination migration --- packages/common/src/index.ts | 4 +- .../src/types/command/CommandV6.types.ts | 30 ++ .../command/PartialTargetDescriptor.types.ts | 4 +- .../common/src/types/command/command.types.ts | 8 +- .../types/command/legacy/ActionCommandV5.ts | 69 ++++ .../command/{ => legacy}/CommandV5.types.ts | 4 +- .../legacy/PartialTargetDescriptorV5.types.ts | 371 ++++++++++++++++++ .../canonicalizeAndValidateCommand.ts | 4 + .../upgradeV5ToV6/index.ts | 1 + .../upgradeV5ToV6/upgradeV5ToV6.ts | 83 ++++ .../src/core/inferFullTargets.ts | 4 +- 11 files changed, 573 insertions(+), 9 deletions(-) create mode 100644 packages/common/src/types/command/CommandV6.types.ts create mode 100644 packages/common/src/types/command/legacy/ActionCommandV5.ts rename packages/common/src/types/command/{ => legacy}/CommandV5.types.ts (90%) create mode 100644 packages/common/src/types/command/legacy/PartialTargetDescriptorV5.types.ts create mode 100644 packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/index.ts create mode 100644 packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 9949e463f79..be8fb3a0165 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -71,7 +71,9 @@ export * from "./types/command/legacy/CommandV2.types"; export * from "./types/command/legacy/CommandV3.types"; export * from "./types/command/legacy/CommandV4.types"; export * from "./types/command/legacy/targetDescriptorV2.types"; -export * from "./types/command/CommandV5.types"; +export * from "./types/command/legacy/CommandV5.types"; +export * from "./types/command/legacy/PartialTargetDescriptorV5.types"; +export * from "./types/command/CommandV6.types"; export * from "./types/command/legacy/PartialTargetDescriptorV3.types"; export * from "./types/command/legacy/PartialTargetDescriptorV4.types"; export * from "./types/CommandServerApi"; diff --git a/packages/common/src/types/command/CommandV6.types.ts b/packages/common/src/types/command/CommandV6.types.ts new file mode 100644 index 00000000000..5ff0b312dd7 --- /dev/null +++ b/packages/common/src/types/command/CommandV6.types.ts @@ -0,0 +1,30 @@ +import type { ActionCommand } from "./ActionCommand"; +import type { PartialTargetOrDestinationDescriptor } from "./PartialTargetDescriptor.types"; + +export interface CommandV6 { + /** + * The version number of the command API + */ + version: 6; + + /** + * The spoken form of the command if issued from a voice command system + */ + spokenForm?: string; + + /** + * If the command is issued from a voice command system, this boolean indicates + * whether we should use the pre phrase snapshot. Only set this to true if the + * voice command system issues a pre phrase signal at the start of every + * phrase. + */ + usePrePhraseSnapshot: boolean; + + action: ActionCommand; + + /** + * A list of targets expected by the action. Inference will be run on the + * targets + */ + targets: PartialTargetOrDestinationDescriptor[]; +} diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index c95b25cd01b..34cf5c431a6 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -267,7 +267,8 @@ export interface InferPreviousMarkModifier { type: "inferPreviousMark"; } -export type TargetPosition = "before" | "after" | "start" | "end"; +export type TargetPosition = "start" | "end"; +export type InsertionMode = "before" | "after" | "to"; export interface PositionModifier { type: "position"; @@ -277,6 +278,7 @@ export interface PositionModifier { export interface PartialPrimitiveTargetDescriptor { type: "primitive"; mark?: PartialMark; + destination?: InsertionMode; modifiers?: Modifier[]; } diff --git a/packages/common/src/types/command/command.types.ts b/packages/common/src/types/command/command.types.ts index 665a9baca80..a2350b386e4 100644 --- a/packages/common/src/types/command/command.types.ts +++ b/packages/common/src/types/command/command.types.ts @@ -1,14 +1,15 @@ import type { ActionCommand } from "./ActionCommand"; -import type { CommandV5 } from "./CommandV5.types"; +import { CommandV6 } from "./CommandV6.types"; import type { CommandV0, CommandV1 } from "./legacy/CommandV0V1.types"; import type { CommandV2 } from "./legacy/CommandV2.types"; import type { CommandV3 } from "./legacy/CommandV3.types"; import type { CommandV4 } from "./legacy/CommandV4.types"; +import type { CommandV5 } from "./legacy/CommandV5.types"; export type CommandComplete = Required> & Pick & { action: Required }; -export const LATEST_VERSION = 5 as const; +export const LATEST_VERSION = 6 as const; export type CommandLatest = Command & { version: typeof LATEST_VERSION; @@ -20,4 +21,5 @@ export type Command = | CommandV2 | CommandV3 | CommandV4 - | CommandV5; + | CommandV5 + | CommandV6; diff --git a/packages/common/src/types/command/legacy/ActionCommandV5.ts b/packages/common/src/types/command/legacy/ActionCommandV5.ts new file mode 100644 index 00000000000..d79ce3e4681 --- /dev/null +++ b/packages/common/src/types/command/legacy/ActionCommandV5.ts @@ -0,0 +1,69 @@ +export const actionNames = [ + "callAsFunction", + "clearAndSetSelection", + "copyToClipboard", + "cutToClipboard", + "deselect", + "editNew", + "editNewLineAfter", + "editNewLineBefore", + "executeCommand", + "extractVariable", + "findInWorkspace", + "foldRegion", + "followLink", + "generateSnippet", + "getText", + "highlight", + "indentLine", + "insertCopyAfter", + "insertCopyBefore", + "insertEmptyLineAfter", + "insertEmptyLineBefore", + "insertEmptyLinesAround", + "insertSnippet", + "moveToTarget", + "outdentLine", + "pasteFromClipboard", + "randomizeTargets", + "remove", + "rename", + "replace", + "replaceWithTarget", + "revealDefinition", + "revealTypeDefinition", + "reverseTargets", + "rewrapWithPairedDelimiter", + "experimental.setInstanceReference", + "scrollToBottom", + "scrollToCenter", + "scrollToTop", + "setSelection", + "setSelectionAfter", + "setSelectionBefore", + "showDebugHover", + "showHover", + "showQuickFix", + "showReferences", + "sortTargets", + "swapTargets", + "toggleLineBreakpoint", + "toggleLineComment", + "unfoldRegion", + "wrapWithPairedDelimiter", + "wrapWithSnippet", +] as const; + +export type ActionType = (typeof actionNames)[number]; + +export interface ActionCommand { + /** + * The action to run + */ + name: ActionType; + + /** + * A list of arguments expected by the given action. + */ + args?: unknown[]; +} diff --git a/packages/common/src/types/command/CommandV5.types.ts b/packages/common/src/types/command/legacy/CommandV5.types.ts similarity index 90% rename from packages/common/src/types/command/CommandV5.types.ts rename to packages/common/src/types/command/legacy/CommandV5.types.ts index 33bd6c2d79c..84383020b77 100644 --- a/packages/common/src/types/command/CommandV5.types.ts +++ b/packages/common/src/types/command/legacy/CommandV5.types.ts @@ -1,5 +1,5 @@ -import type { PartialTargetDescriptor } from "./PartialTargetDescriptor.types"; -import type { ActionCommand } from "./ActionCommand"; +import type { PartialTargetDescriptor } from "./PartialTargetDescriptorV5.types"; +import type { ActionCommand } from "./ActionCommandV5"; export interface CommandV5 { /** diff --git a/packages/common/src/types/command/legacy/PartialTargetDescriptorV5.types.ts b/packages/common/src/types/command/legacy/PartialTargetDescriptorV5.types.ts new file mode 100644 index 00000000000..9226d93874f --- /dev/null +++ b/packages/common/src/types/command/legacy/PartialTargetDescriptorV5.types.ts @@ -0,0 +1,371 @@ +interface CursorMark { + type: "cursor"; +} + +interface ThatMark { + type: "that"; +} + +interface SourceMark { + type: "source"; +} + +interface NothingMark { + type: "nothing"; +} + +interface LastCursorPositionMark { + type: "lastCursorPosition"; +} + +interface DecoratedSymbolMark { + type: "decoratedSymbol"; + symbolColor: string; + character: string; +} + +type LineNumberType = "absolute" | "relative" | "modulo100"; + +interface LineNumberMark { + type: "lineNumber"; + lineNumberType: LineNumberType; + lineNumber: number; +} + +/** + * Constructs a range between {@link anchor} and {@link active} + */ +interface RangeMark { + type: "range"; + anchor: PartialMark; + active: PartialMark; + excludeAnchor?: boolean; + excludeActive?: boolean; +} + +type PartialMark = + | CursorMark + | ThatMark + | SourceMark + | DecoratedSymbolMark + | NothingMark + | LineNumberMark + | RangeMark; + +type SimpleSurroundingPairName = + | "angleBrackets" + | "backtickQuotes" + | "curlyBrackets" + | "doubleQuotes" + | "escapedDoubleQuotes" + | "escapedParentheses" + | "escapedSquareBrackets" + | "escapedSingleQuotes" + | "parentheses" + | "singleQuotes" + | "squareBrackets"; +type ComplexSurroundingPairName = "string" | "any" | "collectionBoundary"; +type SurroundingPairName = + | SimpleSurroundingPairName + | ComplexSurroundingPairName; + +type SimpleScopeTypeType = + | "argumentOrParameter" + | "anonymousFunction" + | "attribute" + | "branch" + | "class" + | "className" + | "collectionItem" + | "collectionKey" + | "comment" + | "functionCall" + | "functionCallee" + | "functionName" + | "ifStatement" + | "instance" + | "list" + | "map" + | "name" + | "namedFunction" + | "regularExpression" + | "statement" + | "string" + | "type" + | "value" + | "condition" + | "section" + | "sectionLevelOne" + | "sectionLevelTwo" + | "sectionLevelThree" + | "sectionLevelFour" + | "sectionLevelFive" + | "sectionLevelSix" + | "selector" + | "switchStatementSubject" + | "unit" + | "xmlBothTags" + | "xmlElement" + | "xmlEndTag" + | "xmlStartTag" + // Latex scope types + | "part" + | "chapter" + | "subSection" + | "subSubSection" + | "namedParagraph" + | "subParagraph" + | "environment" + // Text based scopes + | "token" + | "line" + | "notebookCell" + | "paragraph" + | "document" + | "character" + | "word" + | "identifier" + | "nonWhitespaceSequence" + | "boundedNonWhitespaceSequence" + | "url"; + +interface SimpleScopeType { + type: SimpleScopeTypeType; +} + +interface CustomRegexScopeType { + type: "customRegex"; + regex: string; +} + +type SurroundingPairDirection = "left" | "right"; +interface SurroundingPairScopeType { + type: "surroundingPair"; + delimiter: SurroundingPairName; + forceDirection?: SurroundingPairDirection; + + /** + * If `true`, then only accept pairs where the pair completely contains the + * selection, ie without the edges touching. + */ + requireStrongContainment?: boolean; +} + +interface OneOfScopeType { + type: "oneOf"; + scopeTypes: ScopeType[]; +} + +type ScopeType = + | SimpleScopeType + | SurroundingPairScopeType + | CustomRegexScopeType + | OneOfScopeType; + +interface ContainingSurroundingPairModifier extends ContainingScopeModifier { + scopeType: SurroundingPairScopeType; +} + +interface EverySurroundingPairModifier extends EveryScopeModifier { + scopeType: SurroundingPairScopeType; +} + +type SurroundingPairModifier = + | ContainingSurroundingPairModifier + | EverySurroundingPairModifier; + +interface InteriorOnlyModifier { + type: "interiorOnly"; +} + +interface ExcludeInteriorModifier { + type: "excludeInterior"; +} + +interface ContainingScopeModifier { + type: "containingScope"; + scopeType: ScopeType; + ancestorIndex?: number; +} + +interface EveryScopeModifier { + type: "everyScope"; + scopeType: ScopeType; +} + +/** + * Refer to scopes by absolute index relative to iteration scope, eg "first + * funk" to refer to the first function in a class. + */ +interface OrdinalScopeModifier { + type: "ordinalScope"; + + scopeType: ScopeType; + + /** The start of the range. Start from end of iteration scope if `start` is negative */ + start: number; + + /** The number of scopes to include. Will always be positive. If greater than 1, will include scopes after {@link start} */ + length: number; +} + +type Direction = "forward" | "backward"; + +/** + * Refer to scopes by offset relative to input target, eg "next + * funk" to refer to the first function after the function containing the target input. + */ +interface RelativeScopeModifier { + type: "relativeScope"; + + scopeType: ScopeType; + + /** Indicates how many scopes away to start relative to the input target. + * Note that if {@link direction} is `"backward"`, then this scope will be the + * end of the output range. */ + offset: number; + + /** The number of scopes to include. Will always be positive. If greater + * than 1, will include scopes in the direction of {@link direction} */ + length: number; + + /** Indicates which direction both {@link offset} and {@link length} go + * relative to input target */ + direction: Direction; +} + +/** + * Converts its input to a raw selection with no type information so for + * example if it is the destination of a bring or move it should inherit the + * type information such as delimiters from its source. + */ +interface RawSelectionModifier { + type: "toRawSelection"; +} + +interface LeadingModifier { + type: "leading"; +} + +interface TrailingModifier { + type: "trailing"; +} + +interface KeepContentFilterModifier { + type: "keepContentFilter"; +} + +interface KeepEmptyFilterModifier { + type: "keepEmptyFilter"; +} + +interface InferPreviousMarkModifier { + type: "inferPreviousMark"; +} + +type TargetPosition = "before" | "after" | "start" | "end"; + +export interface PositionModifierV5 { + type: "position"; + position: TargetPosition; +} + +export interface PartialPrimitiveTargetDescriptorV5 { + type: "primitive"; + mark?: PartialMark; + modifiers?: ModifierV5[]; +} + +interface HeadTailModifier { + type: "extendThroughStartOf" | "extendThroughEndOf"; + modifiers?: ModifierV5[]; +} + +/** + * Runs {@link modifier} if the target has no explicit scope type, ie if + * {@link Target.hasExplicitScopeType} is `false`. + */ +interface ModifyIfUntypedModifier { + type: "modifyIfUntyped"; + + /** + * The modifier to apply if the target is untyped + */ + modifier: ModifierV5; +} + +/** + * Tries each of the modifiers in {@link modifiers} in turn until one of them + * doesn't throw an error, returning the output from the first modifier not + * throwing an error. + */ +interface CascadingModifier { + type: "cascading"; + + /** + * The modifiers to try in turn + */ + modifiers: ModifierV5[]; +} + +/** + * First applies {@link anchor} to input, then independently applies + * {@link active}, and forms a range between the two resulting targets + */ +interface RangeModifier { + type: "range"; + anchor: ModifierV5; + active: ModifierV5; + excludeAnchor?: boolean; + excludeActive?: boolean; +} + +export type ModifierV5 = + | PositionModifierV5 + | InteriorOnlyModifier + | ExcludeInteriorModifier + | ContainingScopeModifier + | EveryScopeModifier + | OrdinalScopeModifier + | RelativeScopeModifier + | HeadTailModifier + | LeadingModifier + | TrailingModifier + | RawSelectionModifier + | ModifyIfUntypedModifier + | CascadingModifier + | RangeModifier + | KeepContentFilterModifier + | KeepEmptyFilterModifier + | InferPreviousMarkModifier; + +// continuous is one single continuous selection between the two targets +// vertical puts a selection on each line vertically between the two targets +type PartialRangeType = "continuous" | "vertical"; + +export interface PartialRangeTargetDescriptorV5 { + type: "range"; + anchor: PartialPrimitiveTargetDescriptorV5 | ImplicitTargetDescriptorV5; + active: PartialPrimitiveTargetDescriptorV5; + excludeAnchor: boolean; + excludeActive: boolean; + rangeType?: PartialRangeType; +} + +export interface PartialListTargetDescriptorV5 { + type: "list"; + elements: ( + | PartialPrimitiveTargetDescriptorV5 + | PartialRangeTargetDescriptorV5 + )[]; +} + +export interface ImplicitTargetDescriptorV5 { + type: "implicit"; +} + +export type PartialTargetDescriptorV5 = + | PartialPrimitiveTargetDescriptorV5 + | PartialRangeTargetDescriptorV5 + | PartialListTargetDescriptorV5 + | ImplicitTargetDescriptorV5; diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts index e428649eda7..d0d5d49dd40 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts @@ -20,6 +20,7 @@ import { upgradeV1ToV2 } from "./upgradeV1ToV2"; import { upgradeV2ToV3 } from "./upgradeV2ToV3"; import { upgradeV3ToV4 } from "./upgradeV3ToV4"; import { upgradeV4ToV5 } from "./upgradeV4ToV5/upgradeV4ToV5"; +import { upgradeV5ToV6 } from "./upgradeV5ToV6"; /** * Given a command argument which comes from the client, normalize it so that it @@ -78,6 +79,9 @@ function upgradeCommand(command: Command): CommandLatest { case 4: command = upgradeV4ToV5(command); break; + case 5: + command = upgradeV5ToV6(command); + break; default: throw new Error( `Can't upgrade from unknown version ${command.version}`, diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/index.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/index.ts new file mode 100644 index 00000000000..fb04f2a3a53 --- /dev/null +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/index.ts @@ -0,0 +1 @@ +export * from "./upgradeV5ToV6"; diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts new file mode 100644 index 00000000000..e5c2ecb0050 --- /dev/null +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -0,0 +1,83 @@ +import { + CommandV5, + CommandV6, + ImplicitTargetDescriptor, + InsertionMode, + Modifier, + ModifierV5, + PartialPrimitiveTargetDescriptor, + PartialPrimitiveTargetDescriptorV5, + PartialRangeTargetDescriptor, + PartialTargetDescriptor, + PartialTargetDescriptorV5, + PositionModifierV5, +} from "@cursorless/common"; + +export function upgradeV5ToV6(command: CommandV5): CommandV6 { + return { + ...command, + version: 6, + targets: command.targets.map(updateTarget), + }; +} + +function updateTarget( + target: PartialTargetDescriptorV5, +): PartialTargetDescriptor { + switch (target.type) { + case "list": + return { + ...target, + elements: target.elements.map( + (target) => + updateTarget(target) as + | PartialPrimitiveTargetDescriptor + | PartialRangeTargetDescriptor, + ), + }; + case "range": { + const { anchor, active, ...rest } = target; + return { + ...rest, + anchor: updateTarget(anchor) as + | PartialPrimitiveTargetDescriptor + | ImplicitTargetDescriptor, + active: updateTarget(active) as PartialPrimitiveTargetDescriptor, + }; + } + case "primitive": + return upgradePrimitiveTarget(target); + case "implicit": + return target; + } +} + +function upgradePrimitiveTarget( + target: PartialPrimitiveTargetDescriptorV5, +): PartialPrimitiveTargetDescriptor { + return { + ...target, + destination: getDestination(target.modifiers), + modifiers: getModifiers(target.modifiers), + }; +} + +function getDestination(modifiers?: ModifierV5[]): InsertionMode | undefined { + const positionModifier = modifiers?.find( + (m): m is PositionModifierV5 => m.type === "position", + ); + return positionModifier?.position === "before" || + positionModifier?.position === "after" + ? positionModifier.position + : undefined; +} + +function getModifiers(modifiers?: ModifierV5[]): Modifier[] | undefined { + return modifiers + ? modifiers.filter( + (m): m is Modifier => + m.type !== "position" || + (m.position !== "before" && m.position !== "after"), + ) + : undefined; +} diff --git a/packages/cursorless-engine/src/core/inferFullTargets.ts b/packages/cursorless-engine/src/core/inferFullTargets.ts index 73174e45b21..573200d1d7d 100644 --- a/packages/cursorless-engine/src/core/inferFullTargets.ts +++ b/packages/cursorless-engine/src/core/inferFullTargets.ts @@ -1,7 +1,7 @@ import { ImplicitTargetDescriptor, Modifier, - PartialListTargetDescriptor, + PartialListTargetDescriptorV5, PartialPrimitiveTargetDescriptor, PartialRangeTargetDescriptor, PartialTargetDescriptor, @@ -48,7 +48,7 @@ function inferTarget( } function inferListTarget( - target: PartialListTargetDescriptor, + target: PartialListTargetDescriptorV5, previousTargets: PartialTargetDescriptor[], ): TargetDescriptor { return { From 397a80caea20457322b049a9c713f1855d38553e Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sat, 8 Jul 2023 17:10:35 +0200 Subject: [PATCH 02/99] stuff --- packages/common/src/types/command/CommandV6.types.ts | 4 ++-- packages/common/src/types/command/legacy/CommandV5.types.ts | 4 ++-- packages/cursorless-engine/src/core/inferFullTargets.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/common/src/types/command/CommandV6.types.ts b/packages/common/src/types/command/CommandV6.types.ts index 5ff0b312dd7..b4ba92e0ddc 100644 --- a/packages/common/src/types/command/CommandV6.types.ts +++ b/packages/common/src/types/command/CommandV6.types.ts @@ -1,5 +1,5 @@ import type { ActionCommand } from "./ActionCommand"; -import type { PartialTargetOrDestinationDescriptor } from "./PartialTargetDescriptor.types"; +import type { PartialTargetDescriptor } from "./PartialTargetDescriptor.types"; export interface CommandV6 { /** @@ -26,5 +26,5 @@ export interface CommandV6 { * A list of targets expected by the action. Inference will be run on the * targets */ - targets: PartialTargetOrDestinationDescriptor[]; + targets: PartialTargetDescriptor[]; } diff --git a/packages/common/src/types/command/legacy/CommandV5.types.ts b/packages/common/src/types/command/legacy/CommandV5.types.ts index 84383020b77..584914d220e 100644 --- a/packages/common/src/types/command/legacy/CommandV5.types.ts +++ b/packages/common/src/types/command/legacy/CommandV5.types.ts @@ -1,4 +1,4 @@ -import type { PartialTargetDescriptor } from "./PartialTargetDescriptorV5.types"; +import type { PartialTargetDescriptorV5 } from "./PartialTargetDescriptorV5.types"; import type { ActionCommand } from "./ActionCommandV5"; export interface CommandV5 { @@ -26,5 +26,5 @@ export interface CommandV5 { * A list of targets expected by the action. Inference will be run on the * targets */ - targets: PartialTargetDescriptor[]; + targets: PartialTargetDescriptorV5[]; } diff --git a/packages/cursorless-engine/src/core/inferFullTargets.ts b/packages/cursorless-engine/src/core/inferFullTargets.ts index 573200d1d7d..73174e45b21 100644 --- a/packages/cursorless-engine/src/core/inferFullTargets.ts +++ b/packages/cursorless-engine/src/core/inferFullTargets.ts @@ -1,7 +1,7 @@ import { ImplicitTargetDescriptor, Modifier, - PartialListTargetDescriptorV5, + PartialListTargetDescriptor, PartialPrimitiveTargetDescriptor, PartialRangeTargetDescriptor, PartialTargetDescriptor, @@ -48,7 +48,7 @@ function inferTarget( } function inferListTarget( - target: PartialListTargetDescriptorV5, + target: PartialListTargetDescriptor, previousTargets: PartialTargetDescriptor[], ): TargetDescriptor { return { From 5793b28a8f999b66ecd3f7f3fa44b6e1bd21ad0b Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sat, 8 Jul 2023 18:26:25 +0200 Subject: [PATCH 03/99] more work --- .../command/PartialTargetDescriptor.types.ts | 6 +++ .../src/actions/EditNew/EditNew.ts | 14 +++--- .../actions/EditNew/runNotebookCellTargets.ts | 2 +- .../upgradeV5ToV6/upgradeV5ToV6.ts | 6 +-- .../src/core/handleHoistedModifiers.ts | 4 +- .../src/core/inferFullTargets.ts | 48 +++++++------------ .../ModifierStageFactoryImpl.ts | 5 +- .../processTargets/TargetPipelineRunner.ts | 8 +--- .../modifiers/DestinationStage.ts | 11 +++++ .../processTargets/modifiers/PositionStage.ts | 16 ++++++- .../modifiers/toPositionTarget.ts | 44 ++++++----------- .../src/processTargets/targets/BaseTarget.ts | 8 ++-- .../targets/NotebookCellTarget.ts | 16 +++---- .../processTargets/targets/PositionTarget.ts | 43 ++++++++++------- .../src/typings/TargetDescriptor.ts | 8 ++-- .../src/typings/target.types.ts | 6 +-- 16 files changed, 125 insertions(+), 120 deletions(-) create mode 100644 packages/cursorless-engine/src/processTargets/modifiers/DestinationStage.ts diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 34cf5c431a6..1e74a9f14b1 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -275,6 +275,11 @@ export interface PositionModifier { position: TargetPosition; } +export interface DestinationModifier { + type: "destination"; + insertionMode: InsertionMode; +} + export interface PartialPrimitiveTargetDescriptor { type: "primitive"; mark?: PartialMark; @@ -328,6 +333,7 @@ export interface RangeModifier { export type Modifier = | PositionModifier + | DestinationModifier | InteriorOnlyModifier | ExcludeInteriorModifier | ContainingScopeModifier diff --git a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts index 498d8a64167..7d172f3a24f 100644 --- a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts +++ b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts @@ -1,6 +1,6 @@ import { RangeUpdater } from "../../core/updateSelections/RangeUpdater"; import { containingLineIfUntypedModifier } from "../../processTargets/modifiers/commonContainingScopeIfUntypedModifiers"; -import PositionStage from "../../processTargets/modifiers/PositionStage"; +import DestinationStage from "../../processTargets/modifiers/DestinationStage"; import { ModifierStageFactory } from "../../processTargets/ModifierStageFactory"; import { ModifierStage } from "../../processTargets/PipelineStages.types"; import { ide } from "../../singletons/ide.singleton"; @@ -71,9 +71,9 @@ export class EditNewBefore extends EditNew { getFinalStages() { return [ ...super.getFinalStages(), - new PositionStage({ - type: "position", - position: "before", + new DestinationStage({ + type: "destination", + insertionMode: "before", }), ]; } @@ -83,9 +83,9 @@ export class EditNewAfter extends EditNew { getFinalStages() { return [ ...super.getFinalStages(), - new PositionStage({ - type: "position", - position: "after", + new DestinationStage({ + type: "destination", + insertionMode: "after", }), ]; } diff --git a/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts b/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts index 8587d654841..129c87f06df 100644 --- a/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts +++ b/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts @@ -14,7 +14,7 @@ export async function runEditNewNotebookCellTargets( // multiple cells, which is unsupported in VSCode const target = ensureSingleTarget(targets) as NotebookCellPositionTarget; const editor = ide().getEditableTextEditor(target.editor); - const isAbove = target.position === "before"; + const isAbove = target.insertionMode === "before"; await actions.setSelection.run([targets]); diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index e5c2ecb0050..8376a07422e 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -74,10 +74,10 @@ function getDestination(modifiers?: ModifierV5[]): InsertionMode | undefined { function getModifiers(modifiers?: ModifierV5[]): Modifier[] | undefined { return modifiers - ? modifiers.filter( - (m): m is Modifier => + ? (modifiers.filter( + (m) => m.type !== "position" || (m.position !== "before" && m.position !== "after"), - ) + ) as Modifier[]) : undefined; } diff --git a/packages/cursorless-engine/src/core/handleHoistedModifiers.ts b/packages/cursorless-engine/src/core/handleHoistedModifiers.ts index 635d25132c4..99e3fc95b8e 100644 --- a/packages/cursorless-engine/src/core/handleHoistedModifiers.ts +++ b/packages/cursorless-engine/src/core/handleHoistedModifiers.ts @@ -84,7 +84,7 @@ export function handleHoistedModifiers( : { type: "primitive", mark: anchor.mark, - positionModifier: undefined, + destination: undefined, modifiers: unhoistedModifiers, }, // Remove the hoisted modifier (and everything before it) from the @@ -111,7 +111,7 @@ export function handleHoistedModifiers( type: "target", target: pipelineInputDescriptor, }, - positionModifier: anchor.positionModifier, + destination: anchor.destination, modifiers: hoistedModifiers, }; } diff --git a/packages/cursorless-engine/src/core/inferFullTargets.ts b/packages/cursorless-engine/src/core/inferFullTargets.ts index 73174e45b21..f217e5ab396 100644 --- a/packages/cursorless-engine/src/core/inferFullTargets.ts +++ b/packages/cursorless-engine/src/core/inferFullTargets.ts @@ -1,11 +1,11 @@ import { ImplicitTargetDescriptor, + InsertionMode, Modifier, PartialListTargetDescriptor, PartialPrimitiveTargetDescriptor, PartialRangeTargetDescriptor, PartialTargetDescriptor, - PositionModifier, } from "@cursorless/common"; import { Mark, @@ -111,9 +111,6 @@ function inferPrimitiveTarget( target: PartialPrimitiveTargetDescriptor, previousTargets: PartialTargetDescriptor[], ): PrimitiveTargetDescriptor { - const ownPositionModifier = getPositionModifier(target); - const ownModifiers = getPreservedModifiers(target); - const mark = target.mark ?? (shouldInferPreviousMark(target) ? getPreviousMark(previousTargets) @@ -122,39 +119,23 @@ function inferPrimitiveTarget( }; const modifiers = - ownModifiers ?? getPreviousPreservedModifiers(previousTargets) ?? []; + getPreservedModifiers(target) ?? + getPreviousPreservedModifiers(previousTargets) ?? + []; - const positionModifier = - ownPositionModifier ?? getPreviousPositionModifier(previousTargets); + const destination = + target.destination ?? getPreviousDestination(previousTargets); return { type: target.type, mark, modifiers, - positionModifier, + destination: destination + ? { type: "destination", insertionMode: destination } + : undefined, }; } -function getPositionModifier( - target: PartialPrimitiveTargetDescriptor, -): PositionModifier | undefined { - if (target.modifiers == null) { - return undefined; - } - - const positionModifierIndex = target.modifiers.findIndex( - (modifier) => modifier.type === "position", - ); - - if (positionModifierIndex > 0) { - throw Error("Position modifiers must be at the start of a modifier chain"); - } - - return positionModifierIndex === -1 - ? undefined - : (target.modifiers[positionModifierIndex] as PositionModifier); -} - function shouldInferPreviousMark( target: PartialPrimitiveTargetDescriptor, ): boolean { @@ -178,7 +159,7 @@ function getPreservedModifiers( ): Modifier[] | undefined { const preservedModifiers = target.modifiers?.filter( - (modifier) => !["position", "inferPreviousMark"].includes(modifier.type), + (modifier) => !["inferPreviousMark"].includes(modifier.type), ) ?? []; if (preservedModifiers.length !== 0) { return preservedModifiers; @@ -228,10 +209,13 @@ function getPreviousPreservedModifiers( return getPreviousTargetAttribute(previousTargets, getPreservedModifiers); } -function getPreviousPositionModifier( +function getPreviousDestination( previousTargets: PartialTargetDescriptor[], -): PositionModifier | undefined { - return getPreviousTargetAttribute(previousTargets, getPositionModifier); +): InsertionMode | undefined { + return getPreviousTargetAttribute( + previousTargets, + (target) => target.destination, + ); } /** diff --git a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts index fb27ebdd176..3df1ada04fd 100644 --- a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts @@ -4,12 +4,14 @@ import { Modifier, SurroundingPairModifier, } from "@cursorless/common"; +import { StoredTargetMap } from ".."; import { LanguageDefinitions } from "../languages/LanguageDefinitions"; import { ModifierStageFactory } from "./ModifierStageFactory"; import { ModifierStage } from "./PipelineStages.types"; import CascadingStage from "./modifiers/CascadingStage"; import { ModifyIfUntypedStage } from "./modifiers/ConditionalModifierStages"; import { ContainingScopeStage } from "./modifiers/ContainingScopeStage"; +import DestinationStage from "./modifiers/DestinationStage"; import { EveryScopeStage } from "./modifiers/EveryScopeStage"; import { KeepContentFilterStage, @@ -36,7 +38,6 @@ import ContainingSyntaxScopeStage, { SimpleEveryScopeModifier, } from "./modifiers/scopeTypeStages/ContainingSyntaxScopeStage"; import NotebookCellStage from "./modifiers/scopeTypeStages/NotebookCellStage"; -import { StoredTargetMap } from ".."; export class ModifierStageFactoryImpl implements ModifierStageFactory { constructor( @@ -51,6 +52,8 @@ export class ModifierStageFactoryImpl implements ModifierStageFactory { switch (modifier.type) { case "position": return new PositionStage(modifier); + case "destination": + return new DestinationStage(modifier); case "extendThroughStartOf": return new HeadStage(this, modifier); case "extendThroughEndOf": diff --git a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts index 0757071290d..a2cc3f0d2cf 100644 --- a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts @@ -215,13 +215,9 @@ class TargetPipeline { } else { markStage = this.markStageFactory.create(targetDescriptor.mark); positionModifierStages = - targetDescriptor.positionModifier == null + targetDescriptor.destination == null ? [] - : [ - this.modifierStageFactory.create( - targetDescriptor.positionModifier, - ), - ]; + : [this.modifierStageFactory.create(targetDescriptor.destination)]; nonPositionModifierStages = getModifierStagesFromTargetModifiers( this.modifierStageFactory, targetDescriptor.modifiers, diff --git a/packages/cursorless-engine/src/processTargets/modifiers/DestinationStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/DestinationStage.ts new file mode 100644 index 00000000000..7c16dc6012e --- /dev/null +++ b/packages/cursorless-engine/src/processTargets/modifiers/DestinationStage.ts @@ -0,0 +1,11 @@ +import { DestinationModifier } from "@cursorless/common"; +import { Target } from "../../typings/target.types"; +import { ModifierStage } from "../PipelineStages.types"; + +export default class DestinationStage implements ModifierStage { + constructor(private modifier: DestinationModifier) {} + + run(target: Target): Target[] { + return [target.toPositionTarget(this.modifier.insertionMode)]; + } +} diff --git a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts index 6f3982b0bc5..30e9ef20457 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts @@ -1,11 +1,23 @@ +import { PositionModifier, Range } from "@cursorless/common"; import { Target } from "../../typings/target.types"; -import { PositionModifier } from "@cursorless/common"; import { ModifierStage } from "../PipelineStages.types"; +import { PlainTarget } from "../targets"; export default class PositionStage implements ModifierStage { constructor(private modifier: PositionModifier) {} run(target: Target): Target[] { - return [target.toPositionTarget(this.modifier.position)]; + const position = + this.modifier.position === "start" + ? target.contentRange.start + : target.contentRange.end; + + return [ + new PlainTarget({ + editor: target.editor, + isReversed: target.isReversed, + contentRange: new Range(position, position), + }), + ]; } } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/toPositionTarget.ts b/packages/cursorless-engine/src/processTargets/modifiers/toPositionTarget.ts index dd06fedca92..f17cdebc51c 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/toPositionTarget.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/toPositionTarget.ts @@ -1,45 +1,29 @@ -import { Range } from "@cursorless/common"; +import { InsertionMode, Range } from "@cursorless/common"; import { Target } from "../../typings/target.types"; -import { TargetPosition } from "@cursorless/common"; import { PositionTarget } from "../targets"; export function toPositionTarget( target: Target, - position: TargetPosition, + insertionMode: InsertionMode, ): Target { - const { start, end } = target.contentRange; - let contentRange: Range; - let insertionDelimiter: string; - - switch (position) { - case "before": - contentRange = new Range(start, start); - insertionDelimiter = target.insertionDelimiter; - break; - - case "after": - contentRange = new Range(end, end); - insertionDelimiter = target.insertionDelimiter; - break; - - case "start": - contentRange = new Range(start, start); - insertionDelimiter = ""; - break; - - case "end": - contentRange = new Range(end, end); - insertionDelimiter = ""; - break; - } + const contentRange = (() => { + switch (insertionMode) { + case "before": + return new Range(target.contentRange.start, target.contentRange.start); + case "after": + return new Range(target.contentRange.end, target.contentRange.end); + case "to": + return target.contentRange; + } + })(); return new PositionTarget({ editor: target.editor, isReversed: target.isReversed, contentRange, thatTarget: target, - position, - insertionDelimiter, + insertionMode, + insertionDelimiter: target.insertionDelimiter, isRaw: target.isRaw, }); } diff --git a/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts b/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts index 1da00d9cfc4..be7f3c2863f 100644 --- a/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts @@ -1,3 +1,4 @@ +import type { InsertionMode } from "@cursorless/common"; import { NoContainingScopeError, Range, @@ -5,9 +6,8 @@ import { TextEditor, } from "@cursorless/common"; import { isEqual } from "lodash"; -import type { TargetPosition } from "@cursorless/common"; -import type { EditNewActionType, Target } from "../../typings/target.types"; import type { EditWithRangeUpdater } from "../../typings/Types"; +import type { EditNewActionType, Target } from "../../typings/target.types"; import { isSameType } from "../../util/typeUtils"; import { toPositionTarget } from "../modifiers/toPositionTarget"; import { @@ -190,8 +190,8 @@ export default abstract class BaseTarget< }; } - toPositionTarget(position: TargetPosition): Target { - return toPositionTarget(this, position); + toPositionTarget(insertionMode: InsertionMode): Target { + return toPositionTarget(this, insertionMode); } abstract get insertionDelimiter(): string; diff --git a/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts b/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts index 6147d2a2d40..704378df33f 100644 --- a/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts @@ -1,4 +1,4 @@ -import { TargetPosition } from "@cursorless/common"; +import { InsertionMode, TargetPosition } from "@cursorless/common"; import { BaseTarget, CommonTargetParameters, @@ -22,37 +22,37 @@ export default class NotebookCellTarget extends BaseTarget { insertionDelimiter = "\n"; isNotebookCell = true; - public position: TargetPosition; + public insertionMode: InsertionMode; constructor(parameters: NotebookCellPositionTargetParameters) { super(parameters); - this.position = parameters.position; + this.insertionMode = parameters.insertionMode; } getLeadingDelimiterTarget = () => undefined; getTrailingDelimiterTarget = () => undefined; - getRemovalRange = () => removalUnsupportedForPosition(this.position); + getRemovalRange = () => removalUnsupportedForPosition(this.insertionMode); protected getCloneParameters(): NotebookCellPositionTargetParameters { return { ...this.state, - position: this.position, + insertionMode: this.insertionMode, }; } } diff --git a/packages/cursorless-engine/src/processTargets/targets/PositionTarget.ts b/packages/cursorless-engine/src/processTargets/targets/PositionTarget.ts index f86147b9fb7..3753c68bced 100644 --- a/packages/cursorless-engine/src/processTargets/targets/PositionTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/PositionTarget.ts @@ -1,11 +1,15 @@ -import { Range, TextEditor, UnsupportedError } from "@cursorless/common"; +import { + InsertionMode, + Range, + TextEditor, + UnsupportedError, +} from "@cursorless/common"; import { BaseTarget, CommonTargetParameters } from "."; -import { TargetPosition } from "@cursorless/common"; -import { EditNewActionType } from "../../typings/target.types"; import { EditWithRangeUpdater } from "../../typings/Types"; +import { EditNewActionType } from "../../typings/target.types"; interface PositionTargetParameters extends CommonTargetParameters { - readonly position: TargetPosition; + readonly insertionMode: InsertionMode; readonly insertionDelimiter: string; readonly isRaw: boolean; } @@ -13,17 +17,17 @@ interface PositionTargetParameters extends CommonTargetParameters { export default class PositionTarget extends BaseTarget { insertionDelimiter: string; isRaw: boolean; - private position: TargetPosition; + private insertionMode: InsertionMode; private isLineDelimiter: boolean; private isBefore: boolean; private indentationString: string; constructor(parameters: PositionTargetParameters) { super(parameters); - this.position = parameters.position; + this.insertionMode = parameters.insertionMode; this.insertionDelimiter = parameters.insertionDelimiter; this.isRaw = parameters.isRaw; - this.isBefore = parameters.position === "before"; + this.isBefore = parameters.insertionDelimiter === "before"; // It's only considered a line if the delimiter is only new line symbols this.isLineDelimiter = /^(\n)+$/.test(parameters.insertionDelimiter); // This calculation must be done here since that that target is not updated by our range updater @@ -38,12 +42,12 @@ export default class PositionTarget extends BaseTarget getLeadingDelimiterTarget = () => undefined; getTrailingDelimiterTarget = () => undefined; - getRemovalRange = () => removalUnsupportedForPosition(this.position); + getRemovalRange = () => removalUnsupportedForPosition(this.insertionMode); getEditNewActionType(): EditNewActionType { if ( this.insertionDelimiter === "\n" && - this.position === "after" && + this.insertionMode === "after" && this.state.thatTarget!.contentRange.isSingleLine ) { // If the target that we're wrapping is not a single line, then we @@ -60,7 +64,7 @@ export default class PositionTarget extends BaseTarget } constructChangeEdit(text: string): EditWithRangeUpdater { - return this.position === "before" || this.position === "after" + return this.insertionMode === "before" || this.insertionMode === "after" ? this.constructEditWithDelimiters(text) : this.constructEditWithoutDelimiters(text); } @@ -68,7 +72,7 @@ export default class PositionTarget extends BaseTarget protected getCloneParameters(): PositionTargetParameters { return { ...this.state, - position: this.position, + insertionMode: this.insertionMode, insertionDelimiter: this.insertionDelimiter, isRaw: this.isRaw, }; @@ -85,7 +89,7 @@ export default class PositionTarget extends BaseTarget return { range, text: editText, - isReplace: this.position === "after", + isReplace: this.insertionMode === "after", updateRange, }; } @@ -150,12 +154,19 @@ export default class PositionTarget extends BaseTarget } } -export function removalUnsupportedForPosition(position: string): Range { - const preferredModifier = - position === "after" || position === "end" ? "trailing" : "leading"; +export function removalUnsupportedForPosition( + insertionMode: InsertionMode, +): Range { + if (insertionMode === "to") { + throw new UnsupportedError( + `Removal is not supported for "${insertionMode}"`, + ); + } + + const preferredModifier = insertionMode === "after" ? "trailing" : "leading"; throw new UnsupportedError( - `Please use "${preferredModifier}" modifier; removal is not supported for "${position}"`, + `Please use "${preferredModifier}" modifier; removal is not supported for "${insertionMode}"`, ); } diff --git a/packages/cursorless-engine/src/typings/TargetDescriptor.ts b/packages/cursorless-engine/src/typings/TargetDescriptor.ts index 0d4d76c92ba..0dc069ed5be 100644 --- a/packages/cursorless-engine/src/typings/TargetDescriptor.ts +++ b/packages/cursorless-engine/src/typings/TargetDescriptor.ts @@ -1,9 +1,9 @@ import { + DestinationModifier, ImplicitTargetDescriptor, Modifier, PartialMark, PartialRangeType, - PositionModifier, ScopeType, } from "@cursorless/common"; @@ -28,11 +28,9 @@ export interface PrimitiveTargetDescriptor { modifiers: Modifier[]; /** - * We separate the positional modifier from the other modifiers because it - * behaves differently and and makes the target behave like a destination for - * example for bring. This change is the first step toward #803 + * Different nodes of insertion: eg "before", "after", "to" */ - positionModifier?: PositionModifier; + destination?: DestinationModifier; } /** diff --git a/packages/cursorless-engine/src/typings/target.types.ts b/packages/cursorless-engine/src/typings/target.types.ts index 5ec834289d3..0acd1cfae8f 100644 --- a/packages/cursorless-engine/src/typings/target.types.ts +++ b/packages/cursorless-engine/src/typings/target.types.ts @@ -7,13 +7,14 @@ import type { ModifyIfUntypedStage } from "../processTargets/modifiers/ConditionalModifierStages"; // eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports import type { + InsertionMode, Range, Selection, - TextEditor, // eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports Snippet, // eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports SnippetVariable, + TextEditor, } from "@cursorless/common"; import type { // eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports @@ -23,7 +24,6 @@ import type { // eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports UntypedTarget, } from "../processTargets/targets"; -import type { TargetPosition } from "@cursorless/common"; import type { EditWithRangeUpdater } from "./Types"; export type EditNewActionType = "edit" | "insertLineAfter"; @@ -154,5 +154,5 @@ export interface Target { * Construct a position target with the given position. * @param position The position to use, eg `start`, `end`, `before`, `after` */ - toPositionTarget(position: TargetPosition): Target; + toPositionTarget(insertionMode: InsertionMode): Target; } From 09be83f1679efcabe9541fa7026d801da776d31d Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sat, 8 Jul 2023 19:46:50 +0200 Subject: [PATCH 04/99] Test passes --- .../processTargets/modifiers/PositionStage.ts | 20 +++++++++++++------ .../processTargets/targets/PositionTarget.ts | 2 +- .../recorded/positions/chuckEndOfWhale.yml | 6 +++++- .../recorded/positions/chuckStartOfWhale.yml | 6 +++++- .../recorded/testCaseRecorder/takeHarp.yml | 2 +- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts index 30e9ef20457..7f4fd80ce49 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts @@ -1,7 +1,11 @@ import { PositionModifier, Range } from "@cursorless/common"; import { Target } from "../../typings/target.types"; import { ModifierStage } from "../PipelineStages.types"; -import { PlainTarget } from "../targets"; +import { + CommonTargetParameters, + PlainTarget, + RawSelectionTarget, +} from "../targets"; export default class PositionStage implements ModifierStage { constructor(private modifier: PositionModifier) {} @@ -12,12 +16,16 @@ export default class PositionStage implements ModifierStage { ? target.contentRange.start : target.contentRange.end; + const parameters: CommonTargetParameters = { + editor: target.editor, + isReversed: target.isReversed, + contentRange: new Range(position, position), + }; + return [ - new PlainTarget({ - editor: target.editor, - isReversed: target.isReversed, - contentRange: new Range(position, position), - }), + target.isRaw + ? new RawSelectionTarget(parameters) + : new PlainTarget(parameters), ]; } } diff --git a/packages/cursorless-engine/src/processTargets/targets/PositionTarget.ts b/packages/cursorless-engine/src/processTargets/targets/PositionTarget.ts index 3753c68bced..d914c121e88 100644 --- a/packages/cursorless-engine/src/processTargets/targets/PositionTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/PositionTarget.ts @@ -27,7 +27,7 @@ export default class PositionTarget extends BaseTarget this.insertionMode = parameters.insertionMode; this.insertionDelimiter = parameters.insertionDelimiter; this.isRaw = parameters.isRaw; - this.isBefore = parameters.insertionDelimiter === "before"; + this.isBefore = parameters.insertionMode === "before"; // It's only considered a line if the delimiter is only new line symbols this.isLineDelimiter = /^(\n)+$/.test(parameters.insertionDelimiter); // This calculation must be done here since that that target is not updated by our range updater diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckEndOfWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckEndOfWhale.yml index 58b7e67cb83..92d17566dd2 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckEndOfWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckEndOfWhale.yml @@ -19,4 +19,8 @@ initialState: start: {line: 0, character: 6} end: {line: 0, character: 11} fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, modifiers: [{type: position, position: end}]}] -thrownError: {name: UnsupportedError} +finalState: + documentContents: hello world whatever + selections: + - anchor: {line: 0, character: 20} + active: {line: 0, character: 20} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckStartOfWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckStartOfWhale.yml index 27f9d485ad7..e7023fdce2d 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckStartOfWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckStartOfWhale.yml @@ -19,4 +19,8 @@ initialState: start: {line: 0, character: 6} end: {line: 0, character: 11} fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, modifiers: [{type: position, position: start}]}] -thrownError: {name: UnsupportedError} +finalState: + documentContents: hello world whatever + selections: + - anchor: {line: 0, character: 20} + active: {line: 0, character: 20} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/testCaseRecorder/takeHarp.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/testCaseRecorder/takeHarp.yml index 03f59220770..c6ee6e59852 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/testCaseRecorder/takeHarp.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/testCaseRecorder/takeHarp.yml @@ -1,6 +1,6 @@ languageId: plaintext command: - version: 5 + version: 6 spokenForm: take harp action: {name: setSelection} targets: From 40e5301e445e529960d239a6d3fb85158ca06b84 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sat, 8 Jul 2023 19:52:38 +0200 Subject: [PATCH 05/99] Is not a token --- .../src/processTargets/modifiers/PositionStage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts index 7f4fd80ce49..88d1508ae0a 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts @@ -25,7 +25,7 @@ export default class PositionStage implements ModifierStage { return [ target.isRaw ? new RawSelectionTarget(parameters) - : new PlainTarget(parameters), + : new PlainTarget({ ...parameters, isToken: false }), ]; } } From 4803508decd8f6668811c6e099a6ba878a4081f4 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 9 Jul 2023 11:50:04 +0200 Subject: [PATCH 06/99] Add destination during command version upgrade dependent on action name --- packages/common/src/index.ts | 1 + .../types/command/legacy/ActionCommandV5.ts | 8 +-- .../types/command/legacy/CommandV5.types.ts | 4 +- .../upgradeV5ToV6/upgradeV5ToV6.ts | 61 +++++++++++++++---- .../src/core/inferFullTargets.ts | 4 +- .../processTargets/TargetPipelineRunner.ts | 24 +++++--- .../src/typings/TargetDescriptor.ts | 12 ++-- .../updateSelections/bringFineAfterThis.yml | 2 +- .../updateSelections/bringWhaleBeforeThis.yml | 2 +- 9 files changed, 79 insertions(+), 39 deletions(-) diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index be8fb3a0165..30a9c4892f9 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -71,6 +71,7 @@ export * from "./types/command/legacy/CommandV2.types"; export * from "./types/command/legacy/CommandV3.types"; export * from "./types/command/legacy/CommandV4.types"; export * from "./types/command/legacy/targetDescriptorV2.types"; +export * from "./types/command/legacy/ActionCommandV5"; export * from "./types/command/legacy/CommandV5.types"; export * from "./types/command/legacy/PartialTargetDescriptorV5.types"; export * from "./types/command/CommandV6.types"; diff --git a/packages/common/src/types/command/legacy/ActionCommandV5.ts b/packages/common/src/types/command/legacy/ActionCommandV5.ts index d79ce3e4681..c41469d4ba6 100644 --- a/packages/common/src/types/command/legacy/ActionCommandV5.ts +++ b/packages/common/src/types/command/legacy/ActionCommandV5.ts @@ -1,4 +1,4 @@ -export const actionNames = [ +const actionNames = [ "callAsFunction", "clearAndSetSelection", "copyToClipboard", @@ -54,13 +54,13 @@ export const actionNames = [ "wrapWithSnippet", ] as const; -export type ActionType = (typeof actionNames)[number]; +export type ActionTypeV5 = (typeof actionNames)[number]; -export interface ActionCommand { +export interface ActionCommandV5 { /** * The action to run */ - name: ActionType; + name: ActionTypeV5; /** * A list of arguments expected by the given action. diff --git a/packages/common/src/types/command/legacy/CommandV5.types.ts b/packages/common/src/types/command/legacy/CommandV5.types.ts index 584914d220e..a7244088e33 100644 --- a/packages/common/src/types/command/legacy/CommandV5.types.ts +++ b/packages/common/src/types/command/legacy/CommandV5.types.ts @@ -1,5 +1,5 @@ import type { PartialTargetDescriptorV5 } from "./PartialTargetDescriptorV5.types"; -import type { ActionCommand } from "./ActionCommandV5"; +import type { ActionCommandV5 } from "./ActionCommandV5"; export interface CommandV5 { /** @@ -20,7 +20,7 @@ export interface CommandV5 { */ usePrePhraseSnapshot: boolean; - action: ActionCommand; + action: ActionCommandV5; /** * A list of targets expected by the action. Inference will be run on the diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index 8376a07422e..8ac24c1da1f 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -1,4 +1,5 @@ import { + ActionTypeV5, CommandV5, CommandV6, ImplicitTargetDescriptor, @@ -17,20 +18,39 @@ export function upgradeV5ToV6(command: CommandV5): CommandV6 { return { ...command, version: 6, - targets: command.targets.map(updateTarget), + targets: upgradeTargets(command.targets, command.action.name), }; } -function updateTarget( +function upgradeTargets( + targets: PartialTargetDescriptorV5[], + action: ActionTypeV5, +): PartialTargetDescriptor[] { + switch (action) { + case "pasteFromClipboard": + return [upgradeTarget(targets[0], true)]; + case "replaceWithTarget": + case "moveToTarget": + return [ + upgradeTarget(targets[0], false), + upgradeTarget(targets[1], true), + ]; + } + + return targets.map((t) => upgradeTarget(t, false)); +} + +function upgradeTarget( target: PartialTargetDescriptorV5, + isDestination: boolean, ): PartialTargetDescriptor { switch (target.type) { case "list": return { ...target, elements: target.elements.map( - (target) => - updateTarget(target) as + (target, i) => + upgradeTarget(target, isDestination && i === 0) as | PartialPrimitiveTargetDescriptor | PartialRangeTargetDescriptor, ), @@ -39,14 +59,17 @@ function updateTarget( const { anchor, active, ...rest } = target; return { ...rest, - anchor: updateTarget(anchor) as + anchor: upgradeTarget(anchor, isDestination) as | PartialPrimitiveTargetDescriptor | ImplicitTargetDescriptor, - active: updateTarget(active) as PartialPrimitiveTargetDescriptor, + active: upgradeTarget( + active, + false, + ) as PartialPrimitiveTargetDescriptor, }; } case "primitive": - return upgradePrimitiveTarget(target); + return upgradePrimitiveTarget(target, isDestination); case "implicit": return target; } @@ -54,22 +77,34 @@ function updateTarget( function upgradePrimitiveTarget( target: PartialPrimitiveTargetDescriptorV5, + isDestination: boolean, ): PartialPrimitiveTargetDescriptor { return { ...target, - destination: getDestination(target.modifiers), + destination: getDestination(target.modifiers, isDestination), modifiers: getModifiers(target.modifiers), }; } -function getDestination(modifiers?: ModifierV5[]): InsertionMode | undefined { +function getDestination( + modifiers: ModifierV5[] | undefined, + isDestination: boolean, +): InsertionMode | undefined { const positionModifier = modifiers?.find( (m): m is PositionModifierV5 => m.type === "position", ); - return positionModifier?.position === "before" || - positionModifier?.position === "after" - ? positionModifier.position - : undefined; + if (positionModifier != null) { + if (modifiers!.indexOf(positionModifier) !== 0) { + throw Error("Position modifier has to be at first index"); + } + if ( + positionModifier?.position === "before" || + positionModifier?.position === "after" + ) { + return positionModifier.position; + } + } + return isDestination ? "to" : undefined; } function getModifiers(modifiers?: ModifierV5[]): Modifier[] | undefined { diff --git a/packages/cursorless-engine/src/core/inferFullTargets.ts b/packages/cursorless-engine/src/core/inferFullTargets.ts index f217e5ab396..75a796a618e 100644 --- a/packages/cursorless-engine/src/core/inferFullTargets.ts +++ b/packages/cursorless-engine/src/core/inferFullTargets.ts @@ -128,11 +128,9 @@ function inferPrimitiveTarget( return { type: target.type, + destination, mark, modifiers, - destination: destination - ? { type: "destination", insertionMode: destination } - : undefined, }; } diff --git a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts index a2cc3f0d2cf..4d427faf3f7 100644 --- a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts @@ -205,20 +205,25 @@ class TargetPipeline { targetDescriptor: PrimitiveTargetDescriptor | ImplicitTargetDescriptor, ): Target[] { let markStage: MarkStage; - let nonPositionModifierStages: ModifierStage[]; - let positionModifierStages: ModifierStage[]; + let nonDestinationModifierStages: ModifierStage[]; + let destinationModifierStages: ModifierStage[]; if (targetDescriptor.type === "implicit") { markStage = new ImplicitStage(); - nonPositionModifierStages = []; - positionModifierStages = []; + nonDestinationModifierStages = []; + destinationModifierStages = []; } else { markStage = this.markStageFactory.create(targetDescriptor.mark); - positionModifierStages = + destinationModifierStages = targetDescriptor.destination == null ? [] - : [this.modifierStageFactory.create(targetDescriptor.destination)]; - nonPositionModifierStages = getModifierStagesFromTargetModifiers( + : [ + this.modifierStageFactory.create({ + type: "destination", + insertionMode: targetDescriptor.destination, + }), + ]; + nonDestinationModifierStages = getModifierStagesFromTargetModifiers( this.modifierStageFactory, targetDescriptor.modifiers, ); @@ -231,14 +236,15 @@ class TargetPipeline { * The modifier pipeline that will be applied to construct our final targets */ const modifierStages = [ - ...nonPositionModifierStages, + ...nonDestinationModifierStages, ...this.actionPrePositionStages, - ...positionModifierStages, ...this.actionFinalStages, // This performs auto-expansion to token when you say eg "take this" with an // empty selection new ContainingTokenIfUntypedEmptyStage(this.modifierStageFactory), + + ...destinationModifierStages, ]; // Run all targets through the modifier stages diff --git a/packages/cursorless-engine/src/typings/TargetDescriptor.ts b/packages/cursorless-engine/src/typings/TargetDescriptor.ts index 0dc069ed5be..b04f782b05d 100644 --- a/packages/cursorless-engine/src/typings/TargetDescriptor.ts +++ b/packages/cursorless-engine/src/typings/TargetDescriptor.ts @@ -1,6 +1,6 @@ import { - DestinationModifier, ImplicitTargetDescriptor, + InsertionMode, Modifier, PartialMark, PartialRangeType, @@ -12,6 +12,11 @@ export type Mark = PartialMark | TargetMark; export interface PrimitiveTargetDescriptor { type: "primitive"; + /** + * Different nodes of insertion: eg "before", "after", "to" + */ + destination?: InsertionMode; + /** * The mark, eg "air", "this", "that", etc */ @@ -26,11 +31,6 @@ export interface PrimitiveTargetDescriptor { * character of the name. */ modifiers: Modifier[]; - - /** - * Different nodes of insertion: eg "before", "after", "to" - */ - destination?: DestinationModifier; } /** diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringFineAfterThis.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringFineAfterThis.yml index 0916a15c40b..886dd623b0c 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringFineAfterThis.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringFineAfterThis.yml @@ -19,7 +19,7 @@ initialState: start: {line: 0, character: 0} end: {line: 0, character: 3} finalState: - documentContents: foo fooworld + documentContents: foo world foo selections: - anchor: {line: 0, character: 4} active: {line: 0, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringWhaleBeforeThis.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringWhaleBeforeThis.yml index a6fd1913c61..f72d0345220 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringWhaleBeforeThis.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringWhaleBeforeThis.yml @@ -19,7 +19,7 @@ initialState: start: {line: 0, character: 4} end: {line: 0, character: 9} finalState: - documentContents: fooworld world + documentContents: world foo world selections: - anchor: {line: 0, character: 9} active: {line: 0, character: 9} From 38b85ede6d4f2b4e1d33b4495473dbfaba626102 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 9 Jul 2023 12:49:48 +0200 Subject: [PATCH 07/99] cleanup --- .../command/PartialTargetDescriptor.types.ts | 37 ++++++++++++------- .../canonicalizeAndValidateCommand.ts | 4 +- .../upgradeV5ToV6/upgradeV5ToV6.ts | 24 ++++++++---- .../ModifierStageFactoryImpl.ts | 8 ++-- .../processTargets/modifiers/HeadTailStage.ts | 11 ++++-- .../processTargets/modifiers/PositionStage.ts | 35 +++++++++++++----- .../targets/NotebookCellTarget.ts | 2 +- 7 files changed, 82 insertions(+), 39 deletions(-) diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 1e74a9f14b1..99a164974a7 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -267,28 +267,28 @@ export interface InferPreviousMarkModifier { type: "inferPreviousMark"; } -export type TargetPosition = "start" | "end"; export type InsertionMode = "before" | "after" | "to"; -export interface PositionModifier { - type: "position"; - position: TargetPosition; -} - export interface DestinationModifier { type: "destination"; insertionMode: InsertionMode; } -export interface PartialPrimitiveTargetDescriptor { - type: "primitive"; - mark?: PartialMark; - destination?: InsertionMode; +export interface StartOfModifier { + type: "startOf"; +} + +export interface EndOfModifier { + type: "endOf"; +} + +export interface HeadModifier { + type: "extendThroughStartOf"; modifiers?: Modifier[]; } -export interface HeadTailModifier { - type: "extendThroughStartOf" | "extendThroughEndOf"; +export interface TailModifier { + type: "extendThroughEndOf"; modifiers?: Modifier[]; } @@ -332,15 +332,17 @@ export interface RangeModifier { } export type Modifier = - | PositionModifier | DestinationModifier + | StartOfModifier + | EndOfModifier | InteriorOnlyModifier | ExcludeInteriorModifier | ContainingScopeModifier | EveryScopeModifier | OrdinalScopeModifier | RelativeScopeModifier - | HeadTailModifier + | HeadModifier + | TailModifier | LeadingModifier | TrailingModifier | RawSelectionModifier @@ -355,6 +357,13 @@ export type Modifier = // vertical puts a selection on each line vertically between the two targets export type PartialRangeType = "continuous" | "vertical"; +export interface PartialPrimitiveTargetDescriptor { + type: "primitive"; + mark?: PartialMark; + destination?: InsertionMode; + modifiers?: Modifier[]; +} + export interface PartialRangeTargetDescriptor { type: "range"; anchor: PartialPrimitiveTargetDescriptor | ImplicitTargetDescriptor; diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts index d0d5d49dd40..4d2fe8a92f2 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts @@ -130,7 +130,9 @@ export function checkForOldInference( return ( target.type === "range" && target.active.mark == null && - target.active.modifiers?.some((m) => m.type === "position") && + target.active.modifiers?.some( + (m) => m.type === "startOf" || m.type === "endOf", + ) && !target.active.modifiers?.some((m) => m.type === "inferPreviousMark") ); }); diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index 8ac24c1da1f..196413ec4c8 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -108,11 +108,21 @@ function getDestination( } function getModifiers(modifiers?: ModifierV5[]): Modifier[] | undefined { - return modifiers - ? (modifiers.filter( - (m) => - m.type !== "position" || - (m.position !== "before" && m.position !== "after"), - ) as Modifier[]) - : undefined; + const result: Modifier[] = []; + + if (modifiers != null) { + for (const modifier of modifiers) { + if (modifier.type === "position") { + if (modifier.position === "start") { + result.push({ type: "startOf" }); + } else if (modifier.position === "end") { + result.push({ type: "endOf" }); + } + } else { + result.push(modifier as Modifier); + } + } + } + + return result.length > 0 ? result : undefined; } diff --git a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts index 3df1ada04fd..d3f987e6e88 100644 --- a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts @@ -26,7 +26,7 @@ import { import ItemStage from "./modifiers/ItemStage"; import { LeadingStage, TrailingStage } from "./modifiers/LeadingTrailingStages"; import { OrdinalScopeStage } from "./modifiers/OrdinalScopeStage"; -import PositionStage from "./modifiers/PositionStage"; +import { EndOfStage, StartOfStage } from "./modifiers/PositionStage"; import RangeModifierStage from "./modifiers/RangeModifierStage"; import RawSelectionStage from "./modifiers/RawSelectionStage"; import RelativeScopeStage from "./modifiers/RelativeScopeStage"; @@ -50,10 +50,12 @@ export class ModifierStageFactoryImpl implements ModifierStageFactory { create(modifier: Modifier): ModifierStage { switch (modifier.type) { - case "position": - return new PositionStage(modifier); case "destination": return new DestinationStage(modifier); + case "startOf": + return new StartOfStage(modifier); + case "endOf": + return new EndOfStage(modifier); case "extendThroughStartOf": return new HeadStage(this, modifier); case "extendThroughEndOf": diff --git a/packages/cursorless-engine/src/processTargets/modifiers/HeadTailStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/HeadTailStage.ts index b8353b9c2a9..055eec292d2 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/HeadTailStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/HeadTailStage.ts @@ -1,4 +1,9 @@ -import { HeadTailModifier, Modifier, Range } from "@cursorless/common"; +import { + HeadModifier, + Modifier, + Range, + TailModifier, +} from "@cursorless/common"; import { Target } from "../../typings/target.types"; import { ModifierStageFactory } from "../ModifierStageFactory"; import { ModifierStage } from "../PipelineStages.types"; @@ -52,7 +57,7 @@ abstract class HeadTailStage implements ModifierStage { export class HeadStage extends HeadTailStage { constructor( modifierStageFactory: ModifierStageFactory, - modifier: HeadTailModifier, + modifier: HeadModifier, ) { super(modifierStageFactory, true, modifier.modifiers); } @@ -65,7 +70,7 @@ export class HeadStage extends HeadTailStage { export class TailStage extends HeadTailStage { constructor( modifierStageFactory: ModifierStageFactory, - modifier: HeadTailModifier, + modifier: TailModifier, ) { super(modifierStageFactory, false, modifier.modifiers); } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts index 88d1508ae0a..46bb6929b8b 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts @@ -1,4 +1,4 @@ -import { PositionModifier, Range } from "@cursorless/common"; +import { EndOfModifier, Range, StartOfModifier } from "@cursorless/common"; import { Target } from "../../typings/target.types"; import { ModifierStage } from "../PipelineStages.types"; import { @@ -7,19 +7,12 @@ import { RawSelectionTarget, } from "../targets"; -export default class PositionStage implements ModifierStage { - constructor(private modifier: PositionModifier) {} - +abstract class StartEndOfStage implements ModifierStage { run(target: Target): Target[] { - const position = - this.modifier.position === "start" - ? target.contentRange.start - : target.contentRange.end; - const parameters: CommonTargetParameters = { editor: target.editor, isReversed: target.isReversed, - contentRange: new Range(position, position), + contentRange: this.constructContentRange(target.contentRange), }; return [ @@ -28,4 +21,26 @@ export default class PositionStage implements ModifierStage { : new PlainTarget({ ...parameters, isToken: false }), ]; } + + protected abstract constructContentRange(contentRange: Range): Range; +} + +export class StartOfStage extends StartEndOfStage { + constructor(_modifier: StartOfModifier) { + super(); + } + + protected constructContentRange(contentRange: Range): Range { + return contentRange.start.toEmptyRange(); + } +} + +export class EndOfStage extends StartEndOfStage { + constructor(_modifier: EndOfModifier) { + super(); + } + + protected constructContentRange(contentRange: Range): Range { + return contentRange.end.toEmptyRange(); + } } diff --git a/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts b/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts index 704378df33f..9bc8a6c47e2 100644 --- a/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts @@ -1,4 +1,4 @@ -import { InsertionMode, TargetPosition } from "@cursorless/common"; +import { InsertionMode } from "@cursorless/common"; import { BaseTarget, CommonTargetParameters, From e52df85aedb58a875c8067338cb80f3cace03b1d Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 9 Jul 2023 17:29:26 +0200 Subject: [PATCH 08/99] Removed position target and implemented destination --- .../command/PartialTargetDescriptor.types.ts | 10 +- .../src/actions/EditNew/EditNew.ts | 27 +- .../src/actions/InsertCopy.ts | 2 +- .../ModifierStageFactoryImpl.ts | 3 - .../processTargets/TargetPipelineRunner.ts | 236 ++++++++++++------ .../processTargets/marks/TargetMarkStage.ts | 8 +- .../modifiers/DestinationStage.ts | 11 - .../modifiers/toPositionTarget.ts | 29 --- .../src/processTargets/targets/BaseTarget.ts | 12 +- .../{PositionTarget.ts => DestinationImpl.ts} | 87 ++++--- .../src/processTargets/targets/index.ts | 4 +- .../src/typings/target.types.ts | 14 +- 12 files changed, 249 insertions(+), 194 deletions(-) delete mode 100644 packages/cursorless-engine/src/processTargets/modifiers/DestinationStage.ts delete mode 100644 packages/cursorless-engine/src/processTargets/modifiers/toPositionTarget.ts rename packages/cursorless-engine/src/processTargets/targets/{PositionTarget.ts => DestinationImpl.ts} (74%) diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 99a164974a7..07c5a405004 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -267,13 +267,6 @@ export interface InferPreviousMarkModifier { type: "inferPreviousMark"; } -export type InsertionMode = "before" | "after" | "to"; - -export interface DestinationModifier { - type: "destination"; - insertionMode: InsertionMode; -} - export interface StartOfModifier { type: "startOf"; } @@ -332,7 +325,6 @@ export interface RangeModifier { } export type Modifier = - | DestinationModifier | StartOfModifier | EndOfModifier | InteriorOnlyModifier @@ -357,6 +349,8 @@ export type Modifier = // vertical puts a selection on each line vertically between the two targets export type PartialRangeType = "continuous" | "vertical"; +export type InsertionMode = "before" | "after" | "to"; + export interface PartialPrimitiveTargetDescriptor { type: "primitive"; mark?: PartialMark; diff --git a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts index 7d172f3a24f..53f92e17bc6 100644 --- a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts +++ b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts @@ -1,10 +1,9 @@ import { RangeUpdater } from "../../core/updateSelections/RangeUpdater"; import { containingLineIfUntypedModifier } from "../../processTargets/modifiers/commonContainingScopeIfUntypedModifiers"; -import DestinationStage from "../../processTargets/modifiers/DestinationStage"; import { ModifierStageFactory } from "../../processTargets/ModifierStageFactory"; import { ModifierStage } from "../../processTargets/PipelineStages.types"; import { ide } from "../../singletons/ide.singleton"; -import { Target } from "../../typings/target.types"; +import { Destination, Target } from "../../typings/target.types"; import { setSelectionsAndFocusEditor } from "../../util/setSelectionsAndFocusEditor"; import { createThatMark, ensureSingleEditor } from "../../util/targetUtils"; import { Actions } from "../Actions"; @@ -65,28 +64,20 @@ export class EditNew implements Action { thatSelections: createThatMark(state.targets, state.thatRanges), }; } + + protected getDestination(target: Target): Destination { + return target.toDestination("to"); + } } export class EditNewBefore extends EditNew { - getFinalStages() { - return [ - ...super.getFinalStages(), - new DestinationStage({ - type: "destination", - insertionMode: "before", - }), - ]; + protected getDestination(target: Target): Destination { + return target.toDestination("before"); } } export class EditNewAfter extends EditNew { - getFinalStages() { - return [ - ...super.getFinalStages(), - new DestinationStage({ - type: "destination", - insertionMode: "after", - }), - ]; + protected getDestination(target: Target): Destination { + return target.toDestination("after"); } } diff --git a/packages/cursorless-engine/src/actions/InsertCopy.ts b/packages/cursorless-engine/src/actions/InsertCopy.ts index 18ceacb8eef..93827e8d0b4 100644 --- a/packages/cursorless-engine/src/actions/InsertCopy.ts +++ b/packages/cursorless-engine/src/actions/InsertCopy.ts @@ -55,7 +55,7 @@ class InsertCopy implements Action { // isBefore is inverted because we want the selections to stay with what is to the user the "copy" const position = this.isBefore ? "after" : "before"; const edits = targets.flatMap((target) => - target.toPositionTarget(position).constructChangeEdit(target.contentText), + target.toDestination(position).constructChangeEdit(target.contentText), ); const cursorSelections = { selections: editor.selections }; diff --git a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts index d3f987e6e88..e172a5d9e91 100644 --- a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts @@ -11,7 +11,6 @@ import { ModifierStage } from "./PipelineStages.types"; import CascadingStage from "./modifiers/CascadingStage"; import { ModifyIfUntypedStage } from "./modifiers/ConditionalModifierStages"; import { ContainingScopeStage } from "./modifiers/ContainingScopeStage"; -import DestinationStage from "./modifiers/DestinationStage"; import { EveryScopeStage } from "./modifiers/EveryScopeStage"; import { KeepContentFilterStage, @@ -50,8 +49,6 @@ export class ModifierStageFactoryImpl implements ModifierStageFactory { create(modifier: Modifier): ModifierStage { switch (modifier.type) { - case "destination": - return new DestinationStage(modifier); case "startOf": return new StartOfStage(modifier); case "endOf": diff --git a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts index 4d427faf3f7..345167a0a98 100644 --- a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts @@ -1,17 +1,24 @@ -import { ImplicitTargetDescriptor, Modifier, Range } from "@cursorless/common"; +import { + Direction, + ImplicitTargetDescriptor, + InsertionMode, + Modifier, + Range, + ScopeType, +} from "@cursorless/common"; import { uniqWith, zip } from "lodash"; import { PrimitiveTargetDescriptor, RangeTargetDescriptor, TargetDescriptor, } from "../typings/TargetDescriptor"; -import { Target } from "../typings/target.types"; +import { Destination, Target } from "../typings/target.types"; import { MarkStageFactory } from "./MarkStageFactory"; import { ModifierStageFactory } from "./ModifierStageFactory"; import { MarkStage, ModifierStage } from "./PipelineStages.types"; import ImplicitStage from "./marks/ImplicitStage"; import { ContainingTokenIfUntypedEmptyStage } from "./modifiers/ConditionalModifierStages"; -import { PlainTarget, PositionTarget } from "./targets"; +import { DestinationImpl, PlainTarget } from "./targets"; export class TargetPipelineRunner { constructor( @@ -37,7 +44,7 @@ export class TargetPipelineRunner { target: TargetDescriptor, actionPrePositionStages?: ModifierStage[], actionFinalStages?: ModifierStage[], - ): Target[] { + ): (Target | Destination)[] { return new TargetPipeline( this.modifierStageFactory, this.markStageFactory, @@ -71,11 +78,11 @@ class TargetPipeline { * containing it, and potentially rich context information such as how to remove * the target */ - run(): Target[] { + run(): (Target | Destination)[] { return uniqTargets(this.processTarget(this.target)); } - processTarget(target: TargetDescriptor): Target[] { + processTarget(target: TargetDescriptor): (Target | Destination)[] { switch (target.type) { case "list": return target.elements.flatMap((element) => @@ -89,7 +96,9 @@ class TargetPipeline { } } - processRangeTarget(targetDesc: RangeTargetDescriptor): Target[] { + processRangeTarget( + targetDesc: RangeTargetDescriptor, + ): (Target | Destination)[] { const anchorTargets = this.processPrimitiveTarget(targetDesc.anchor); const activeTargets = this.processPrimitiveTarget(targetDesc.active); @@ -121,21 +130,27 @@ class TargetPipeline { } processContinuousRangeTarget( - anchorTarget: Target, - activeTarget: Target, + anchorTargetOrDestination: Target | Destination, + activeTargetOrDestination: Target | Destination, { excludeAnchor, excludeActive, exclusionScopeType }: RangeTargetDescriptor, - ): Target[] { + ): (Target | Destination)[] { if (exclusionScopeType == null) { return [ - targetsToContinuousTarget( - anchorTarget, - activeTarget, + targetsOrDestinationsToContinuousTarget( + anchorTargetOrDestination, + activeTargetOrDestination, excludeAnchor, excludeActive, ), ]; } + const { target: anchorTarget } = separateDestinationAndTarget( + anchorTargetOrDestination, + ); + const { target: activeTarget } = separateDestinationAndTarget( + activeTargetOrDestination, + ); const isReversed = calcIsReversed(anchorTarget, activeTarget); // For "every" range targets, we need to be smart about how we handle @@ -150,32 +165,23 @@ class TargetPipeline { // again. So instead, we use the equivalent of "previous funk name" to find // our endpoint and then just use an inclusive range ending with that target. return [ - targetsToContinuousTarget( + targetsOrDestinationsToContinuousTarget( excludeAnchor - ? this.modifierStageFactory - .create({ - type: "relativeScope", - scopeType: exclusionScopeType, - direction: isReversed ? "backward" : "forward", - length: 1, - offset: 1, - }) - // NB: The following line assumes that content range is always - // contained by domain, so that "every" will properly reconstruct - // the target from the content range. - .run(anchorTarget)[0] - : anchorTarget, + ? excludeScope( + this.modifierStageFactory, + anchorTargetOrDestination, + exclusionScopeType, + isReversed ? "backward" : "forward", + ) + : anchorTargetOrDestination, excludeActive - ? this.modifierStageFactory - .create({ - type: "relativeScope", - scopeType: exclusionScopeType, - direction: isReversed ? "forward" : "backward", - length: 1, - offset: 1, - }) - .run(activeTarget)[0] - : activeTarget, + ? excludeScope( + this.modifierStageFactory, + activeTargetOrDestination, + exclusionScopeType, + isReversed ? "forward" : "backward", + ) + : activeTargetOrDestination, false, false, ), @@ -203,30 +209,22 @@ class TargetPipeline { */ processPrimitiveTarget( targetDescriptor: PrimitiveTargetDescriptor | ImplicitTargetDescriptor, - ): Target[] { + ): (Target | Destination)[] { let markStage: MarkStage; - let nonDestinationModifierStages: ModifierStage[]; - let destinationModifierStages: ModifierStage[]; + let nonModifierStages: ModifierStage[]; + let destination: InsertionMode | undefined; if (targetDescriptor.type === "implicit") { markStage = new ImplicitStage(); - nonDestinationModifierStages = []; - destinationModifierStages = []; + nonModifierStages = []; + destination = undefined; } else { markStage = this.markStageFactory.create(targetDescriptor.mark); - destinationModifierStages = - targetDescriptor.destination == null - ? [] - : [ - this.modifierStageFactory.create({ - type: "destination", - insertionMode: targetDescriptor.destination, - }), - ]; - nonDestinationModifierStages = getModifierStagesFromTargetModifiers( + nonModifierStages = getModifierStagesFromTargetModifiers( this.modifierStageFactory, targetDescriptor.modifiers, ); + destination = targetDescriptor.destination; } // First, get the targets output by the mark @@ -236,19 +234,21 @@ class TargetPipeline { * The modifier pipeline that will be applied to construct our final targets */ const modifierStages = [ - ...nonDestinationModifierStages, + ...nonModifierStages, ...this.actionPrePositionStages, ...this.actionFinalStages, // This performs auto-expansion to token when you say eg "take this" with an // empty selection new ContainingTokenIfUntypedEmptyStage(this.modifierStageFactory), - - ...destinationModifierStages, ]; // Run all targets through the modifier stages - return processModifierStages(modifierStages, markOutputTargets); + const targets = processModifierStages(modifierStages, markOutputTargets); + + return destination != null + ? targets.map((target) => target.toDestination(destination!)) + : targets; } } @@ -278,6 +278,54 @@ export function processModifierStages( return targets; } +function excludeScope( + modifierStageFactory: ModifierStageFactory, + targetOrDestination: Target | Destination, + exclusionScopeType: ScopeType, + direction: Direction, +): Target | Destination { + const { target, destination } = + separateDestinationAndTarget(targetOrDestination); + + const resultTarget = modifierStageFactory + .create({ + type: "relativeScope", + scopeType: exclusionScopeType, + direction, + length: 1, + offset: 1, + }) + // NB: The following line assumes that content range is always + // contained by domain, so that "every" will properly reconstruct + // the target from the content range. + .run(target)[0]; + + return destination != null + ? destination.withTarget(resultTarget) + : resultTarget; +} + +function separateDestinationAndTarget( + targetOrDestination: Target | Destination, +): { target: Target; destination?: Destination } { + if (isDestination(targetOrDestination)) { + return { + target: targetOrDestination.target, + destination: targetOrDestination, + }; + } + return { + target: targetOrDestination as Target, + destination: undefined, + }; +} + +export function isDestination( + targetOrDestination: Target | Destination, +): targetOrDestination is Destination { + return targetOrDestination instanceof DestinationImpl; +} + function calcIsReversed(anchor: Target, active: Target) { if (anchor.contentRange.start.isAfter(active.contentRange.start)) { return true; @@ -288,8 +336,20 @@ function calcIsReversed(anchor: Target, active: Target) { return anchor.contentRange.end.isAfter(active.contentRange.end); } -function uniqTargets(array: Target[]): Target[] { - return uniqWith(array, (a, b) => a.isEqual(b)); +function uniqTargets( + array: (Target | Destination)[], +): (Target | Destination)[] { + return uniqWith(array, (a, b) => isEqual(a, b)); +} + +function isEqual(a: Target | Destination, b: Target | Destination): boolean { + if (isDestination(a) || isDestination(b)) { + if (!isDestination(a) || !isDestination(b)) { + return false; + } + return a.isEqual(b); + } + return a.isEqual(b); } function ensureSingleEditor(anchorTarget: Target, activeTarget: Target) { @@ -298,6 +358,31 @@ function ensureSingleEditor(anchorTarget: Target, activeTarget: Target) { } } +function targetsOrDestinationsToContinuousTarget( + anchorTargetOrDestination: Target | Destination, + activeTargetOrDestination: Target | Destination, + excludeAnchor: boolean = false, + excludeActive: boolean = false, +): Target | Destination { + const { target: anchorTarget, destination } = separateDestinationAndTarget( + anchorTargetOrDestination, + ); + const { target: activeTarget } = separateDestinationAndTarget( + activeTargetOrDestination, + ); + + const continuesRangeTarget = targetsToContinuousTarget( + anchorTarget, + activeTarget, + excludeAnchor, + excludeActive, + ); + + return destination != null + ? continuesRangeTarget.toDestination(destination.insertionMode) + : continuesRangeTarget; +} + export function targetsToContinuousTarget( anchorTarget: Target, activeTarget: Target, @@ -321,11 +406,18 @@ export function targetsToContinuousTarget( } function targetsToVerticalTarget( - anchorTarget: Target, - activeTarget: Target, + anchorTargetOrDestination: Target | Destination, + activeTargetOrDestination: Target | Destination, excludeAnchor: boolean, excludeActive: boolean, -): Target[] { +): (Target | Destination)[] { + const { target: anchorTarget, destination } = separateDestinationAndTarget( + anchorTargetOrDestination, + ); + const { target: activeTarget } = separateDestinationAndTarget( + activeTargetOrDestination, + ); + ensureSingleEditor(anchorTarget, activeTarget); const isReversed = calcIsReversed(anchorTarget, activeTarget); @@ -340,7 +432,7 @@ function targetsToVerticalTarget( : activeTarget.contentRange.end; const activeLine = activePosition.line - (excludeActive ? delta : 0); - const results: Target[] = []; + const results: (Target | Destination)[] = []; for (let i = anchorLine; true; i += delta) { const contentRange = new Range( i, @@ -349,17 +441,15 @@ function targetsToVerticalTarget( anchorTarget.contentRange.end.character, ); - if (anchorTarget instanceof PositionTarget) { - results.push(anchorTarget.withContentRange(contentRange)); - } else { - results.push( - new PlainTarget({ - editor: anchorTarget.editor, - isReversed: anchorTarget.isReversed, - contentRange, - }), - ); - } + const newTarget = new PlainTarget({ + editor: anchorTarget.editor, + isReversed: anchorTarget.isReversed, + contentRange, + }); + + results.push( + destination != null ? destination.withTarget(newTarget) : newTarget, + ); if (i === activeLine) { return results; diff --git a/packages/cursorless-engine/src/processTargets/marks/TargetMarkStage.ts b/packages/cursorless-engine/src/processTargets/marks/TargetMarkStage.ts index f9190b518d0..10ec07cdfa3 100644 --- a/packages/cursorless-engine/src/processTargets/marks/TargetMarkStage.ts +++ b/packages/cursorless-engine/src/processTargets/marks/TargetMarkStage.ts @@ -1,4 +1,4 @@ -import { TargetPipelineRunner } from ".."; +import { TargetPipelineRunner, isDestination } from ".."; import { TargetMark } from "../../typings/TargetDescriptor"; import { Target } from "../../typings/target.types"; import { MarkStage } from "../PipelineStages.types"; @@ -10,6 +10,10 @@ export class TargetMarkStage implements MarkStage { ) {} run(): Target[] { - return this.targetPipelineRunner.run(this.mark.target); + const targets = this.targetPipelineRunner.run(this.mark.target); + if (targets.some(isDestination)) { + throw Error("Target mark stage doesn't support destinations"); + } + return targets as Target[]; } } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/DestinationStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/DestinationStage.ts deleted file mode 100644 index 7c16dc6012e..00000000000 --- a/packages/cursorless-engine/src/processTargets/modifiers/DestinationStage.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { DestinationModifier } from "@cursorless/common"; -import { Target } from "../../typings/target.types"; -import { ModifierStage } from "../PipelineStages.types"; - -export default class DestinationStage implements ModifierStage { - constructor(private modifier: DestinationModifier) {} - - run(target: Target): Target[] { - return [target.toPositionTarget(this.modifier.insertionMode)]; - } -} diff --git a/packages/cursorless-engine/src/processTargets/modifiers/toPositionTarget.ts b/packages/cursorless-engine/src/processTargets/modifiers/toPositionTarget.ts deleted file mode 100644 index f17cdebc51c..00000000000 --- a/packages/cursorless-engine/src/processTargets/modifiers/toPositionTarget.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { InsertionMode, Range } from "@cursorless/common"; -import { Target } from "../../typings/target.types"; -import { PositionTarget } from "../targets"; - -export function toPositionTarget( - target: Target, - insertionMode: InsertionMode, -): Target { - const contentRange = (() => { - switch (insertionMode) { - case "before": - return new Range(target.contentRange.start, target.contentRange.start); - case "after": - return new Range(target.contentRange.end, target.contentRange.end); - case "to": - return target.contentRange; - } - })(); - - return new PositionTarget({ - editor: target.editor, - isReversed: target.isReversed, - contentRange, - thatTarget: target, - insertionMode, - insertionDelimiter: target.insertionDelimiter, - isRaw: target.isRaw, - }); -} diff --git a/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts b/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts index be7f3c2863f..06aceefd3d2 100644 --- a/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts @@ -7,13 +7,17 @@ import { } from "@cursorless/common"; import { isEqual } from "lodash"; import type { EditWithRangeUpdater } from "../../typings/Types"; -import type { EditNewActionType, Target } from "../../typings/target.types"; +import type { + Destination, + EditNewActionType, + Target, +} from "../../typings/target.types"; import { isSameType } from "../../util/typeUtils"; -import { toPositionTarget } from "../modifiers/toPositionTarget"; import { createContinuousRange, createContinuousRangeUntypedTarget, } from "../targetUtil/createContinuousRange"; +import DestinationImpl from "./DestinationImpl"; /** Parameters supported by all target classes */ export interface MinimumTargetParameters { @@ -190,8 +194,8 @@ export default abstract class BaseTarget< }; } - toPositionTarget(insertionMode: InsertionMode): Target { - return toPositionTarget(this, insertionMode); + toDestination(insertionMode: InsertionMode): Destination { + return new DestinationImpl(this, insertionMode); } abstract get insertionDelimiter(): string; diff --git a/packages/cursorless-engine/src/processTargets/targets/PositionTarget.ts b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts similarity index 74% rename from packages/cursorless-engine/src/processTargets/targets/PositionTarget.ts rename to packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts index d914c121e88..b1cda4b9dd3 100644 --- a/packages/cursorless-engine/src/processTargets/targets/PositionTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts @@ -4,51 +4,45 @@ import { TextEditor, UnsupportedError, } from "@cursorless/common"; -import { BaseTarget, CommonTargetParameters } from "."; import { EditWithRangeUpdater } from "../../typings/Types"; -import { EditNewActionType } from "../../typings/target.types"; - -interface PositionTargetParameters extends CommonTargetParameters { - readonly insertionMode: InsertionMode; - readonly insertionDelimiter: string; - readonly isRaw: boolean; -} - -export default class PositionTarget extends BaseTarget { - insertionDelimiter: string; - isRaw: boolean; - private insertionMode: InsertionMode; - private isLineDelimiter: boolean; - private isBefore: boolean; - private indentationString: string; - - constructor(parameters: PositionTargetParameters) { - super(parameters); - this.insertionMode = parameters.insertionMode; - this.insertionDelimiter = parameters.insertionDelimiter; - this.isRaw = parameters.isRaw; - this.isBefore = parameters.insertionMode === "before"; +import { + Destination, + EditNewActionType, + Target, +} from "../../typings/target.types"; + +export default class DestinationImpl implements Destination { + private readonly contentRange: Range; + private readonly isLineDelimiter: boolean; + private readonly isBefore: boolean; + private readonly indentationString: string; + + constructor(public target: Target, public insertionMode: InsertionMode) { + this.contentRange = getContentRange(target.contentRange, insertionMode); + this.isBefore = insertionMode === "before"; // It's only considered a line if the delimiter is only new line symbols - this.isLineDelimiter = /^(\n)+$/.test(parameters.insertionDelimiter); - // This calculation must be done here since that that target is not updated by our range updater + this.isLineDelimiter = /^(\n)+$/.test(target.insertionDelimiter); this.indentationString = this.isLineDelimiter - ? getIndentationString( - parameters.editor, - parameters.thatTarget!.contentRange, - ) + ? getIndentationString(target.editor, target.contentRange) : ""; } - getLeadingDelimiterTarget = () => undefined; - getTrailingDelimiterTarget = () => undefined; + isEqual(destination: Destination): boolean { + return ( + this.insertionMode === destination.insertionMode && + this.target.isEqual(destination.target) + ); + } - getRemovalRange = () => removalUnsupportedForPosition(this.insertionMode); + withTarget(target: Target): Destination { + return new DestinationImpl(target, this.insertionMode); + } getEditNewActionType(): EditNewActionType { if ( this.insertionDelimiter === "\n" && this.insertionMode === "after" && - this.state.thatTarget!.contentRange.isSingleLine + this.target.contentRange.isSingleLine ) { // If the target that we're wrapping is not a single line, then we // want to compute indentation based on the entire target. Otherwise, @@ -69,13 +63,12 @@ export default class PositionTarget extends BaseTarget : this.constructEditWithoutDelimiters(text); } - protected getCloneParameters(): PositionTargetParameters { - return { - ...this.state, - insertionMode: this.insertionMode, - insertionDelimiter: this.insertionDelimiter, - isRaw: this.isRaw, - }; + private get editor(): TextEditor { + return this.target.editor; + } + + private get insertionDelimiter(): string { + return this.target.insertionDelimiter; } private constructEditWithDelimiters(text: string): EditWithRangeUpdater { @@ -186,3 +179,17 @@ function getIndentationString(editor: TextEditor, range: Range) { } return indentationString; } + +function getContentRange( + contentRange: Range, + insertionMode: InsertionMode, +): Range { + switch (insertionMode) { + case "before": + return contentRange.start.toEmptyRange(); + case "after": + return contentRange.end.toEmptyRange(); + case "to": + return contentRange; + } +} diff --git a/packages/cursorless-engine/src/processTargets/targets/index.ts b/packages/cursorless-engine/src/processTargets/targets/index.ts index bb85ada5bea..f9f32a14276 100644 --- a/packages/cursorless-engine/src/processTargets/targets/index.ts +++ b/packages/cursorless-engine/src/processTargets/targets/index.ts @@ -10,8 +10,8 @@ export * from "./ParagraphTarget"; export { default as ParagraphTarget } from "./ParagraphTarget"; export * from "./PlainTarget"; export { default as PlainTarget } from "./PlainTarget"; -export * from "./PositionTarget"; -export { default as PositionTarget } from "./PositionTarget"; +export * from "./DestinationImpl"; +export { default as DestinationImpl } from "./DestinationImpl"; export * from "./RawSelectionTarget"; export { default as RawSelectionTarget } from "./RawSelectionTarget"; export * from "./ScopeTypeTarget"; diff --git a/packages/cursorless-engine/src/typings/target.types.ts b/packages/cursorless-engine/src/typings/target.types.ts index 0acd1cfae8f..c5e5fc86909 100644 --- a/packages/cursorless-engine/src/typings/target.types.ts +++ b/packages/cursorless-engine/src/typings/target.types.ts @@ -151,8 +151,16 @@ export interface Target { constructRemovalEdit(): EditWithRangeUpdater; isEqual(target: Target): boolean; /** - * Construct a position target with the given position. - * @param position The position to use, eg `start`, `end`, `before`, `after` + * Construct a destination with the given insertion mode. + * @param position The insertion modes to use, eg `before`, `after`, `to` */ - toPositionTarget(insertionMode: InsertionMode): Target; + toDestination(insertionMode: InsertionMode): Destination; +} + +export interface Destination { + insertionMode: InsertionMode; + target: Target; + constructChangeEdit(text: string): EditWithRangeUpdater; + withTarget(target: Target): Destination; + isEqual(destination: Destination): boolean; } From 3c6bb5dce77524eef5daf444129e34ad67b0f7e1 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 10 Jul 2023 07:32:17 +0200 Subject: [PATCH 09/99] cleanup --- .../src/core/commandRunner/CommandRunnerImpl.ts | 11 +++++++---- .../cursorless-engine/src/core/inferFullTargets.ts | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 223c7d3c45e..1e6264fc9f4 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -7,7 +7,7 @@ import { SelectionWithEditor } from "../../typings/Types"; import { Target } from "../../typings/target.types"; import { Debug } from "../Debug"; import { checkForOldInference } from "../commandVersionUpgrades/canonicalizeAndValidateCommand"; -import inferFullTargets from "../inferFullTargets"; +import inferFullTargetDescriptors from "../inferFullTargets"; import { selectionToStoredTarget } from "./selectionToStoredTarget"; export class CommandRunnerImpl implements CommandRunner { @@ -24,7 +24,7 @@ export class CommandRunnerImpl implements CommandRunner { * 1. Perform inference on targets to fill in details left out using things * like previous targets. For example we would automatically infer that * `"take funk air and bat"` is equivalent to `"take funk air and funk - * bat"`. See {@link inferFullTargets} for details of how this is done. + * bat"`. See {@link inferFullTargetDescriptors} for details of how this is done. * 2. Call {@link processTargets} to map each abstract {@link Target} object * to a concrete list of {@link Target} objects. * 3. Run the requested action on the given selections. The mapping from @@ -40,11 +40,14 @@ export class CommandRunnerImpl implements CommandRunner { targets: partialTargetDescriptors, }: CommandComplete): Promise { checkForOldInference(partialTargetDescriptors); - const targetDescriptors = inferFullTargets(partialTargetDescriptors); + + const targetDescriptors = inferFullTargetDescriptors( + partialTargetDescriptors, + ); if (this.debug.active) { this.debug.log("Full targets:"); - this.debug.log(JSON.stringify(targetDescriptors, null, 3)); + this.debug.log(JSON.stringify(targetDescriptors, null, 2)); } const action = this.actions[actionName]; diff --git a/packages/cursorless-engine/src/core/inferFullTargets.ts b/packages/cursorless-engine/src/core/inferFullTargets.ts index 75a796a618e..de54599fcd1 100644 --- a/packages/cursorless-engine/src/core/inferFullTargets.ts +++ b/packages/cursorless-engine/src/core/inferFullTargets.ts @@ -24,7 +24,7 @@ import { handleHoistedModifiers } from "./handleHoistedModifiers"; * @param targets The partial targets which need to be completed by inference. * @returns Target objects fully filled out and ready to be processed by {@link processTargets}. */ -export default function inferFullTargets( +export default function inferFullTargetDescriptors( targets: PartialTargetDescriptor[], ): TargetDescriptor[] { return targets.map((target, index) => From 6c530de1ec54e0bab39eb07d97eff61a1f5df289 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 10 Jul 2023 15:18:31 +0200 Subject: [PATCH 10/99] Added new action types --- .../common/src/types/command/ActionCommand.ts | 65 ++++-- .../src/types/command/CommandV6.types.ts | 11 +- .../command/PartialTargetDescriptor.types.ts | 16 +- .../upgradeV5ToV6/upgradeV5ToV6.ts | 210 ++++++++++++++---- .../src/core/inferFullTargets.ts | 202 +++++++++++++---- .../src/typings/TargetDescriptor.ts | 20 +- .../src/util/getPrimitiveTargets.ts | 20 ++ 7 files changed, 418 insertions(+), 126 deletions(-) diff --git a/packages/common/src/types/command/ActionCommand.ts b/packages/common/src/types/command/ActionCommand.ts index d79ce3e4681..ac9090b15a0 100644 --- a/packages/common/src/types/command/ActionCommand.ts +++ b/packages/common/src/types/command/ActionCommand.ts @@ -1,4 +1,9 @@ -export const actionNames = [ +import { + PartialDestinationDescriptor, + PartialTargetDescriptor, +} from "./PartialTargetDescriptor.types"; + +const simpleActionNames = [ "callAsFunction", "clearAndSetSelection", "copyToClipboard", @@ -22,14 +27,11 @@ export const actionNames = [ "insertEmptyLineBefore", "insertEmptyLinesAround", "insertSnippet", - "moveToTarget", "outdentLine", - "pasteFromClipboard", "randomizeTargets", "remove", "rename", "replace", - "replaceWithTarget", "revealDefinition", "revealTypeDefinition", "reverseTargets", @@ -46,24 +48,57 @@ export const actionNames = [ "showQuickFix", "showReferences", "sortTargets", - "swapTargets", "toggleLineBreakpoint", "toggleLineComment", "unfoldRegion", - "wrapWithPairedDelimiter", "wrapWithSnippet", ] as const; +const complexActionNames = [ + "replaceWithTarget", + "moveToTarget", + "swapTargets", + "pasteFromClipboard", + "wrapWithPairedDelimiter", +] as const; + +const actionNames = [...simpleActionNames, ...complexActionNames] as const; + +export type SimpleActionName = (typeof simpleActionNames)[number]; export type ActionType = (typeof actionNames)[number]; -export interface ActionCommand { - /** - * The action to run - */ - name: ActionType; +export interface PartialSimpleActionDescriptor { + name: SimpleActionName; + target: PartialTargetDescriptor; +} + +export interface PartialBringMoveActionDescriptor { + name: "replaceWithTarget" | "moveToTarget"; + source: PartialTargetDescriptor; + destination: PartialDestinationDescriptor; +} - /** - * A list of arguments expected by the given action. - */ - args?: unknown[]; +export interface PartialSwapActionDescriptor { + name: "swapTargets"; + target1: PartialTargetDescriptor; + target2: PartialTargetDescriptor; } + +export interface PartialPasteActionDescriptor { + name: "pasteFromClipboard"; + destination: PartialDestinationDescriptor; +} + +export interface PartialWrapWithPairedDelimiterActionDescriptor { + name: "wrapWithPairedDelimiter"; + left: string; + right: string; + target: PartialTargetDescriptor; +} + +export type PartialActionDescriptor = + | PartialSimpleActionDescriptor + | PartialBringMoveActionDescriptor + | PartialSwapActionDescriptor + | PartialPasteActionDescriptor + | PartialWrapWithPairedDelimiterActionDescriptor; diff --git a/packages/common/src/types/command/CommandV6.types.ts b/packages/common/src/types/command/CommandV6.types.ts index b4ba92e0ddc..33ca4c4b274 100644 --- a/packages/common/src/types/command/CommandV6.types.ts +++ b/packages/common/src/types/command/CommandV6.types.ts @@ -1,5 +1,4 @@ -import type { ActionCommand } from "./ActionCommand"; -import type { PartialTargetDescriptor } from "./PartialTargetDescriptor.types"; +import type { PartialActionDescriptor } from "./ActionCommand"; export interface CommandV6 { /** @@ -20,11 +19,5 @@ export interface CommandV6 { */ usePrePhraseSnapshot: boolean; - action: ActionCommand; - - /** - * A list of targets expected by the action. Inference will be run on the - * targets - */ - targets: PartialTargetDescriptor[]; + action: PartialActionDescriptor; } diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 07c5a405004..97ea42c363a 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -354,7 +354,6 @@ export type InsertionMode = "before" | "after" | "to"; export interface PartialPrimitiveTargetDescriptor { type: "primitive"; mark?: PartialMark; - destination?: InsertionMode; modifiers?: Modifier[]; } @@ -381,3 +380,18 @@ export type PartialTargetDescriptor = | PartialRangeTargetDescriptor | PartialListTargetDescriptor | ImplicitTargetDescriptor; + +export interface PartialPrimitiveDestinationDescriptor { + type: "destination"; + insertionMode: InsertionMode; + target: PartialTargetDescriptor; +} + +export interface PartialListDestinationDescriptor { + type: "destinationList"; + destinations: PartialPrimitiveDestinationDescriptor[]; +} + +export type PartialDestinationDescriptor = + | PartialPrimitiveDestinationDescriptor + | PartialListDestinationDescriptor; diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index 196413ec4c8..fc5c36d0b28 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -1,14 +1,20 @@ import { - ActionTypeV5, + ActionCommandV5, CommandV5, CommandV6, ImplicitTargetDescriptor, InsertionMode, Modifier, ModifierV5, + PartialActionDescriptor, + PartialDestinationDescriptor, + PartialListTargetDescriptor, + PartialListTargetDescriptorV5, + PartialPrimitiveDestinationDescriptor, PartialPrimitiveTargetDescriptor, PartialPrimitiveTargetDescriptorV5, PartialRangeTargetDescriptor, + PartialRangeTargetDescriptorV5, PartialTargetDescriptor, PartialTargetDescriptorV5, PositionModifierV5, @@ -16,85 +22,195 @@ import { export function upgradeV5ToV6(command: CommandV5): CommandV6 { return { - ...command, version: 6, - targets: upgradeTargets(command.targets, command.action.name), + spokenForm: command.spokenForm, + usePrePhraseSnapshot: command.usePrePhraseSnapshot, + action: upgradeAction(command.action, command.targets), }; } -function upgradeTargets( +function upgradeAction( + action: ActionCommandV5, targets: PartialTargetDescriptorV5[], - action: ActionTypeV5, -): PartialTargetDescriptor[] { - switch (action) { - case "pasteFromClipboard": - return [upgradeTarget(targets[0], true)]; +): PartialActionDescriptor { + switch (action.name) { case "replaceWithTarget": case "moveToTarget": - return [ - upgradeTarget(targets[0], false), - upgradeTarget(targets[1], true), - ]; + return { + name: action.name, + source: upgradeTarget(targets[0]), + destination: targetToDestination(targets[1]), + }; + case "swapTargets": + return { + name: action.name, + target1: upgradeTarget(targets[0]), + target2: upgradeTarget(targets[1]), + }; + case "pasteFromClipboard": + return { + name: action.name, + destination: targetToDestination(targets[0]), + }; + case "wrapWithPairedDelimiter": + return { + name: action.name, + left: action.args![0] as string, + right: action.args![1] as string, + target: upgradeTarget(targets[0]), + }; + default: + return { + name: action.name, + target: upgradeTarget(targets[0]), + }; } - - return targets.map((t) => upgradeTarget(t, false)); } function upgradeTarget( target: PartialTargetDescriptorV5, - isDestination: boolean, ): PartialTargetDescriptor { switch (target.type) { case "list": - return { - ...target, - elements: target.elements.map( - (target, i) => - upgradeTarget(target, isDestination && i === 0) as - | PartialPrimitiveTargetDescriptor - | PartialRangeTargetDescriptor, - ), - }; - case "range": { - const { anchor, active, ...rest } = target; - return { - ...rest, - anchor: upgradeTarget(anchor, isDestination) as - | PartialPrimitiveTargetDescriptor - | ImplicitTargetDescriptor, - active: upgradeTarget( - active, - false, - ) as PartialPrimitiveTargetDescriptor, - }; - } + return upgradeListTarget(target); + case "range": + return upgradeRangeTarget(target); case "primitive": - return upgradePrimitiveTarget(target, isDestination); + return upgradePrimitiveTarget(target); case "implicit": return target; } } +function upgradeListTarget( + target: PartialListTargetDescriptorV5, +): PartialListTargetDescriptor { + return { + ...target, + elements: target.elements.map((element) => { + switch (element.type) { + case "range": + return upgradeRangeTarget(element); + case "primitive": + return upgradePrimitiveTarget(element); + } + }), + }; +} + +function upgradeRangeTarget( + target: PartialRangeTargetDescriptorV5, +): PartialRangeTargetDescriptor { + const { anchor, active } = target; + return { + ...target, + anchor: + anchor.type === "implicit" ? anchor : upgradePrimitiveTarget(anchor), + active: upgradePrimitiveTarget(active), + }; +} + function upgradePrimitiveTarget( target: PartialPrimitiveTargetDescriptorV5, - isDestination: boolean, ): PartialPrimitiveTargetDescriptor { return { ...target, - destination: getDestination(target.modifiers, isDestination), modifiers: getModifiers(target.modifiers), }; } -function getDestination( - modifiers: ModifierV5[] | undefined, - isDestination: boolean, +function targetToDestination( + target: PartialTargetDescriptorV5, +): PartialDestinationDescriptor { + switch (target.type) { + case "list": + return listTargetToDestination(target); + case "range": + return rangeTargetToDestination(target); + case "primitive": + return primitiveTargetToDestination(target); + case "implicit": + return implicitTargetToDestination(target); + } +} + +function listTargetToDestination( + target: PartialListTargetDescriptorV5, +): PartialDestinationDescriptor { + const destinations: PartialPrimitiveDestinationDescriptor[] = []; + target.elements.forEach((element) => { + const insertionMode = getInsertionMode(element); + if (insertionMode != null || destinations.length === 0) { + destinations.push({ + type: "destination", + insertionMode: insertionMode ?? "to", + target: upgradeTarget(element), + }); + } + }); + if (destinations.length > 1) { + return { + type: "destinationList", + destinations, + }; + } + return destinations[0]; +} + +function rangeTargetToDestination( + target: PartialRangeTargetDescriptorV5, +): PartialPrimitiveDestinationDescriptor { + return { + type: "destination", + insertionMode: getInsertionMode(target.anchor) ?? "to", + target: upgradeRangeTarget(target), + }; +} + +function primitiveTargetToDestination( + target: PartialPrimitiveTargetDescriptorV5, +): PartialPrimitiveDestinationDescriptor { + return { + type: "destination", + insertionMode: getInsertionMode(target) ?? "to", + target: upgradePrimitiveTarget(target), + }; +} + +function implicitTargetToDestination( + target: ImplicitTargetDescriptor, +): PartialPrimitiveDestinationDescriptor { + return { + type: "destination", + insertionMode: "to", + target, + }; +} + +function getInsertionMode( + target: + | PartialPrimitiveTargetDescriptorV5 + | PartialRangeTargetDescriptorV5 + | ImplicitTargetDescriptor, +): InsertionMode | undefined { + switch (target.type) { + case "implicit": + return "to"; + case "primitive": + return getInsertionModeFromPrimitive(target); + case "range": + return getInsertionMode(target.anchor); + } +} + +function getInsertionModeFromPrimitive( + target: PartialPrimitiveTargetDescriptorV5, ): InsertionMode | undefined { - const positionModifier = modifiers?.find( + const positionModifier = target.modifiers?.find( (m): m is PositionModifierV5 => m.type === "position", ); if (positionModifier != null) { - if (modifiers!.indexOf(positionModifier) !== 0) { + if (target.modifiers!.indexOf(positionModifier) !== 0) { throw Error("Position modifier has to be at first index"); } if ( @@ -104,7 +220,7 @@ function getDestination( return positionModifier.position; } } - return isDestination ? "to" : undefined; + return undefined; } function getModifiers(modifiers?: ModifierV5[]): Modifier[] | undefined { diff --git a/packages/cursorless-engine/src/core/inferFullTargets.ts b/packages/cursorless-engine/src/core/inferFullTargets.ts index de54599fcd1..87721a81b9f 100644 --- a/packages/cursorless-engine/src/core/inferFullTargets.ts +++ b/packages/cursorless-engine/src/core/inferFullTargets.ts @@ -1,15 +1,19 @@ import { - ImplicitTargetDescriptor, InsertionMode, Modifier, PartialListTargetDescriptor, + PartialPrimitiveDestinationDescriptor, PartialPrimitiveTargetDescriptor, + PartialRangeDestinationDescriptor, PartialRangeTargetDescriptor, PartialTargetDescriptor, } from "@cursorless/common"; import { + ListTargetDescriptor, Mark, + PrimitiveDestinationDescriptor, PrimitiveTargetDescriptor, + RangeDestinationDescriptor, RangeTargetDescriptor, TargetDescriptor, } from "../typings/TargetDescriptor"; @@ -40,8 +44,11 @@ function inferTarget( case "list": return inferListTarget(target, previousTargets); case "range": + case "rangeDestination": + return inferRangeTargetOrDestinationWithHoist(target, previousTargets); case "primitive": - return inferNonListTarget(target, previousTargets); + case "primitiveDestination": + return inferPrimitiveTargetOrDestination(target, previousTargets); case "implicit": return target; } @@ -50,61 +57,126 @@ function inferTarget( function inferListTarget( target: PartialListTargetDescriptor, previousTargets: PartialTargetDescriptor[], -): TargetDescriptor { +): ListTargetDescriptor { return { ...target, - elements: target.elements.map((element, index) => - inferNonListTarget( - element, - previousTargets.concat(target.elements.slice(0, index)), - ), - ), + elements: target.elements.map((element, index) => { + const elementPreviousTargets = previousTargets.concat( + target.elements.slice(0, index), + ); + switch (element.type) { + case "range": + case "rangeDestination": + return inferRangeTargetOrDestinationWithHoist( + element, + elementPreviousTargets, + ); + case "primitive": + case "primitiveDestination": + return inferPrimitiveTargetOrDestination( + element, + elementPreviousTargets, + ); + } + }), }; } -function inferNonListTarget( - target: PartialPrimitiveTargetDescriptor | PartialRangeTargetDescriptor, +function inferRangeTargetOrDestinationWithHoist( + targetOrDestination: + | PartialRangeTargetDescriptor + | PartialRangeDestinationDescriptor, previousTargets: PartialTargetDescriptor[], -): PrimitiveTargetDescriptor | RangeTargetDescriptor { - switch (target.type) { - case "primitive": - return inferPrimitiveTarget(target, previousTargets); - case "range": - return inferRangeTarget(target, previousTargets); +): + | RangeTargetDescriptor + | RangeDestinationDescriptor + | PrimitiveTargetDescriptor + | PrimitiveDestinationDescriptor { + const [target, insertionMode] = ((): [ + PartialRangeTargetDescriptor, + InsertionMode | undefined, + ] => { + if (targetOrDestination.type === "rangeDestination") { + return [targetOrDestination.target, targetOrDestination.insertionMode]; + } + return [targetOrDestination, getPreviousInsertionMode(previousTargets)]; + })(); + + const fullTarget = inferRangeTarget(target, previousTargets); + + const isAnchorMarkImplicit = + target.anchor.type === "implicit" || target.anchor.mark == null; + + const hoistedTarget = handleHoistedModifiers( + fullTarget, + isAnchorMarkImplicit, + ); + + if (insertionMode != null) { + if (hoistedTarget.type === "range") { + return { + type: "rangeDestination", + insertionMode, + target: hoistedTarget, + }; + } + return { + type: "primitiveDestination", + insertionMode, + target: hoistedTarget, + }; } + + return hoistedTarget; } function inferRangeTarget( target: PartialRangeTargetDescriptor, previousTargets: PartialTargetDescriptor[], -): PrimitiveTargetDescriptor | RangeTargetDescriptor { - const fullTarget: RangeTargetDescriptor = { +): RangeTargetDescriptor { + return { type: "range", rangeType: target.rangeType ?? "continuous", excludeAnchor: target.excludeAnchor ?? false, excludeActive: target.excludeActive ?? false, - anchor: inferPossiblyImplicitTarget(target.anchor, previousTargets), + anchor: + target.anchor.type === "implicit" + ? target.anchor + : inferPrimitiveTarget(target.anchor, previousTargets), active: inferPrimitiveTarget( target.active, previousTargets.concat(target.anchor), ), }; - - const isAnchorMarkImplicit = - target.anchor.type === "implicit" || target.anchor.mark == null; - - return handleHoistedModifiers(fullTarget, isAnchorMarkImplicit); } -function inferPossiblyImplicitTarget( - target: PartialPrimitiveTargetDescriptor | ImplicitTargetDescriptor, +function inferPrimitiveTargetOrDestination( + targetOrDestination: + | PartialPrimitiveTargetDescriptor + | PartialPrimitiveDestinationDescriptor, previousTargets: PartialTargetDescriptor[], -): PrimitiveTargetDescriptor | ImplicitTargetDescriptor { - if (target.type === "implicit") { - return target; +): PrimitiveTargetDescriptor | PrimitiveDestinationDescriptor { + const [target, insertionMode] = ((): [ + PartialPrimitiveTargetDescriptor, + InsertionMode | undefined, + ] => { + if (targetOrDestination.type === "primitiveDestination") { + return [targetOrDestination.target, targetOrDestination.insertionMode]; + } + return [targetOrDestination, getPreviousInsertionMode(previousTargets)]; + })(); + + const fullTarget = inferPrimitiveTarget(target, previousTargets); + + if (insertionMode != null) { + return { + type: "primitiveDestination", + insertionMode, + target: fullTarget, + }; } - return inferPrimitiveTarget(target, previousTargets); + return fullTarget; } function inferPrimitiveTarget( @@ -123,12 +195,8 @@ function inferPrimitiveTarget( getPreviousPreservedModifiers(previousTargets) ?? []; - const destination = - target.destination ?? getPreviousDestination(previousTargets); - return { type: target.type, - destination, mark, modifiers, }; @@ -195,25 +263,25 @@ function isLineNumberMark(target: PartialPrimitiveTargetDescriptor): boolean { function getPreviousMark( previousTargets: PartialTargetDescriptor[], ): Mark | undefined { - return getPreviousTargetAttribute( - previousTargets, - (target: PartialPrimitiveTargetDescriptor) => target.mark, - ); + return getPreviousTargetAttribute(previousTargets, { + target: (target: PartialPrimitiveTargetDescriptor) => target.mark, + }); } function getPreviousPreservedModifiers( previousTargets: PartialTargetDescriptor[], ): Modifier[] | undefined { - return getPreviousTargetAttribute(previousTargets, getPreservedModifiers); + return getPreviousTargetAttribute(previousTargets, { + target: getPreservedModifiers, + }); } -function getPreviousDestination( +function getPreviousInsertionMode( previousTargets: PartialTargetDescriptor[], ): InsertionMode | undefined { - return getPreviousTargetAttribute( - previousTargets, - (target) => target.destination, - ); + return getPreviousTargetAttribute(previousTargets, { + destination: (insertionMode: InsertionMode) => insertionMode, + }); } /** @@ -228,16 +296,36 @@ function getPreviousDestination( */ function getPreviousTargetAttribute( previousTargets: PartialTargetDescriptor[], - getAttribute: (target: PartialPrimitiveTargetDescriptor) => T | undefined, + getAttribute: { + target?: (target: PartialPrimitiveTargetDescriptor) => T | undefined; + destination?: (insertionMode: InsertionMode) => T | undefined; + }, ): T | undefined { // Search from back(last) to front(first) for (let i = previousTargets.length - 1; i > -1; --i) { const target = previousTargets[i]; switch (target.type) { case "primitive": { - const attributeValue = getAttribute(target); - if (attributeValue != null) { - return attributeValue; + if (getAttribute.target != null) { + const attributeValue = getAttribute.target(target); + if (attributeValue != null) { + return attributeValue; + } + } + break; + } + case "primitiveDestination": { + if (getAttribute.destination != null) { + const attributeValue = getAttribute.destination(target.insertionMode); + if (attributeValue != null) { + return attributeValue; + } + } + if (getAttribute.target != null) { + const attributeValue = getAttribute.target(target.target); + if (attributeValue != null) { + return attributeValue; + } } break; } @@ -251,6 +339,22 @@ function getPreviousTargetAttribute( } break; } + case "rangeDestination": { + if (getAttribute.destination != null) { + const attributeValue = getAttribute.destination(target.insertionMode); + if (attributeValue != null) { + return attributeValue; + } + } + const attributeValue = getPreviousTargetAttribute( + [target.target.anchor], + getAttribute, + ); + if (attributeValue != null) { + return attributeValue; + } + break; + } case "list": { const attributeValue = getPreviousTargetAttribute( target.elements, diff --git a/packages/cursorless-engine/src/typings/TargetDescriptor.ts b/packages/cursorless-engine/src/typings/TargetDescriptor.ts index b04f782b05d..227d8bd9bde 100644 --- a/packages/cursorless-engine/src/typings/TargetDescriptor.ts +++ b/packages/cursorless-engine/src/typings/TargetDescriptor.ts @@ -12,11 +12,6 @@ export type Mark = PartialMark | TargetMark; export interface PrimitiveTargetDescriptor { type: "primitive"; - /** - * Different nodes of insertion: eg "before", "after", "to" - */ - destination?: InsertionMode; - /** * The mark, eg "air", "this", "that", etc */ @@ -76,3 +71,18 @@ export type TargetDescriptor = | RangeTargetDescriptor | ListTargetDescriptor | ImplicitTargetDescriptor; + +export interface PrimitiveDestinationDescriptor { + type: "destination"; + insertionMode: InsertionMode; + target: TargetDescriptor; +} + +export interface ListDestinationDescriptor { + type: "destinationList"; + destinations: PrimitiveDestinationDescriptor[]; +} + +export type DestinationDescriptor = + | PrimitiveDestinationDescriptor + | ListDestinationDescriptor; diff --git a/packages/cursorless-engine/src/util/getPrimitiveTargets.ts b/packages/cursorless-engine/src/util/getPrimitiveTargets.ts index 6a59b71ccc8..cef29c0f808 100644 --- a/packages/cursorless-engine/src/util/getPrimitiveTargets.ts +++ b/packages/cursorless-engine/src/util/getPrimitiveTargets.ts @@ -22,12 +22,18 @@ function getPartialPrimitiveTargetsHelper( switch (target.type) { case "primitive": return [target]; + case "primitiveDestination": + return [target.target]; case "list": return target.elements.flatMap(getPartialPrimitiveTargetsHelper); case "range": return [target.anchor, target.active].flatMap( getPartialPrimitiveTargetsHelper, ); + case "rangeDestination": + return [target.target.anchor, target.target.active].flatMap( + getPartialPrimitiveTargetsHelper, + ); case "implicit": return []; } @@ -60,6 +66,8 @@ function transformPartialPrimitiveTargetsHelper( switch (target.type) { case "primitive": return func(target); + case "primitiveDestination": + return { ...target, target: func(target.target) }; case "implicit": return target; case "list": @@ -80,5 +88,17 @@ function transformPartialPrimitiveTargetsHelper( | ImplicitTargetDescriptor, active: func(target.active), }; + case "rangeDestination": + return { + ...target, + target: { + ...target.target, + anchor: transformPartialPrimitiveTargetsHelper( + target.target.anchor, + func, + ) as PartialPrimitiveTargetDescriptor | ImplicitTargetDescriptor, + active: func(target.target.active), + }, + }; } } From a42ab5cc61405f95a870543585558f785406e6fb Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 10 Jul 2023 15:38:27 +0200 Subject: [PATCH 11/99] cleanup --- .../common/src/types/command/command.types.ts | 6 +- .../upgradeV4ToV5/upgradeV4ToV5.ts | 4 +- .../upgradeV5ToV6/upgradeV5ToV6.ts | 6 + .../src/core/handleHoistedModifiers.ts | 2 - .../src/core/inferFullTargets.ts | 155 ++---------------- .../processTargets/TargetPipelineRunner.ts | 10 +- .../transformations/upgrade.ts | 6 +- .../src/testCaseRecorder/TestCase.ts | 2 +- .../src/testUtil/cleanUpTestCaseCommand.ts | 11 -- .../src/util/getPrimitiveTargets.ts | 21 +-- 10 files changed, 32 insertions(+), 191 deletions(-) delete mode 100644 packages/cursorless-engine/src/testUtil/cleanUpTestCaseCommand.ts diff --git a/packages/common/src/types/command/command.types.ts b/packages/common/src/types/command/command.types.ts index a2350b386e4..45bc1a80b8a 100644 --- a/packages/common/src/types/command/command.types.ts +++ b/packages/common/src/types/command/command.types.ts @@ -1,4 +1,4 @@ -import type { ActionCommand } from "./ActionCommand"; +import type { PartialActionDescriptor } from "./ActionCommand"; import { CommandV6 } from "./CommandV6.types"; import type { CommandV0, CommandV1 } from "./legacy/CommandV0V1.types"; import type { CommandV2 } from "./legacy/CommandV2.types"; @@ -7,7 +7,9 @@ import type { CommandV4 } from "./legacy/CommandV4.types"; import type { CommandV5 } from "./legacy/CommandV5.types"; export type CommandComplete = Required> & - Pick & { action: Required }; + Pick & { + action: Required; + }; export const LATEST_VERSION = 6 as const; diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV4ToV5/upgradeV4ToV5.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV4ToV5/upgradeV4ToV5.ts index 0c2c74aa56e..233bdd33920 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV4ToV5/upgradeV4ToV5.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV4ToV5/upgradeV4ToV5.ts @@ -1,6 +1,6 @@ import { - ActionCommand, ActionCommandV4, + ActionCommandV5, CommandV4, CommandV5, } from "@cursorless/common"; @@ -13,7 +13,7 @@ export function upgradeV4ToV5(command: CommandV4): CommandV5 { }; } -function upgradeAction(action: ActionCommandV4): ActionCommand { +function upgradeAction(action: ActionCommandV4): ActionCommandV5 { switch (action.name) { case "wrapWithSnippet": { const [name, variableName] = parseSnippetLocation( diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index fc5c36d0b28..3cffd63f96b 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -146,6 +146,12 @@ function listTargetToDestination( insertionMode: insertionMode ?? "to", target: upgradeTarget(element), }); + } else { + destinations.push({ + type: "destination", + insertionMode: destinations[destinations.length - 1].insertionMode, + target: upgradeTarget(element), + }); } }); if (destinations.length > 1) { diff --git a/packages/cursorless-engine/src/core/handleHoistedModifiers.ts b/packages/cursorless-engine/src/core/handleHoistedModifiers.ts index 99e3fc95b8e..e3d0f7e1c2f 100644 --- a/packages/cursorless-engine/src/core/handleHoistedModifiers.ts +++ b/packages/cursorless-engine/src/core/handleHoistedModifiers.ts @@ -84,7 +84,6 @@ export function handleHoistedModifiers( : { type: "primitive", mark: anchor.mark, - destination: undefined, modifiers: unhoistedModifiers, }, // Remove the hoisted modifier (and everything before it) from the @@ -111,7 +110,6 @@ export function handleHoistedModifiers( type: "target", target: pipelineInputDescriptor, }, - destination: anchor.destination, modifiers: hoistedModifiers, }; } diff --git a/packages/cursorless-engine/src/core/inferFullTargets.ts b/packages/cursorless-engine/src/core/inferFullTargets.ts index 87721a81b9f..c9a99588592 100644 --- a/packages/cursorless-engine/src/core/inferFullTargets.ts +++ b/packages/cursorless-engine/src/core/inferFullTargets.ts @@ -1,19 +1,14 @@ import { - InsertionMode, Modifier, PartialListTargetDescriptor, - PartialPrimitiveDestinationDescriptor, PartialPrimitiveTargetDescriptor, - PartialRangeDestinationDescriptor, PartialRangeTargetDescriptor, PartialTargetDescriptor, } from "@cursorless/common"; import { ListTargetDescriptor, Mark, - PrimitiveDestinationDescriptor, PrimitiveTargetDescriptor, - RangeDestinationDescriptor, RangeTargetDescriptor, TargetDescriptor, } from "../typings/TargetDescriptor"; @@ -44,11 +39,9 @@ function inferTarget( case "list": return inferListTarget(target, previousTargets); case "range": - case "rangeDestination": - return inferRangeTargetOrDestinationWithHoist(target, previousTargets); + return inferRangeTarget(target, previousTargets); case "primitive": - case "primitiveDestination": - return inferPrimitiveTargetOrDestination(target, previousTargets); + return inferPrimitiveTarget(target, previousTargets); case "implicit": return target; } @@ -66,68 +59,24 @@ function inferListTarget( ); switch (element.type) { case "range": - case "rangeDestination": - return inferRangeTargetOrDestinationWithHoist( - element, - elementPreviousTargets, - ); + return inferRangeTargetWithHoist(element, elementPreviousTargets); case "primitive": - case "primitiveDestination": - return inferPrimitiveTargetOrDestination( - element, - elementPreviousTargets, - ); + return inferPrimitiveTarget(element, elementPreviousTargets); } }), }; } -function inferRangeTargetOrDestinationWithHoist( - targetOrDestination: - | PartialRangeTargetDescriptor - | PartialRangeDestinationDescriptor, +function inferRangeTargetWithHoist( + target: PartialRangeTargetDescriptor, previousTargets: PartialTargetDescriptor[], -): - | RangeTargetDescriptor - | RangeDestinationDescriptor - | PrimitiveTargetDescriptor - | PrimitiveDestinationDescriptor { - const [target, insertionMode] = ((): [ - PartialRangeTargetDescriptor, - InsertionMode | undefined, - ] => { - if (targetOrDestination.type === "rangeDestination") { - return [targetOrDestination.target, targetOrDestination.insertionMode]; - } - return [targetOrDestination, getPreviousInsertionMode(previousTargets)]; - })(); - +): RangeTargetDescriptor | PrimitiveTargetDescriptor { const fullTarget = inferRangeTarget(target, previousTargets); const isAnchorMarkImplicit = target.anchor.type === "implicit" || target.anchor.mark == null; - const hoistedTarget = handleHoistedModifiers( - fullTarget, - isAnchorMarkImplicit, - ); - - if (insertionMode != null) { - if (hoistedTarget.type === "range") { - return { - type: "rangeDestination", - insertionMode, - target: hoistedTarget, - }; - } - return { - type: "primitiveDestination", - insertionMode, - target: hoistedTarget, - }; - } - - return hoistedTarget; + return handleHoistedModifiers(fullTarget, isAnchorMarkImplicit); } function inferRangeTarget( @@ -150,35 +99,6 @@ function inferRangeTarget( }; } -function inferPrimitiveTargetOrDestination( - targetOrDestination: - | PartialPrimitiveTargetDescriptor - | PartialPrimitiveDestinationDescriptor, - previousTargets: PartialTargetDescriptor[], -): PrimitiveTargetDescriptor | PrimitiveDestinationDescriptor { - const [target, insertionMode] = ((): [ - PartialPrimitiveTargetDescriptor, - InsertionMode | undefined, - ] => { - if (targetOrDestination.type === "primitiveDestination") { - return [targetOrDestination.target, targetOrDestination.insertionMode]; - } - return [targetOrDestination, getPreviousInsertionMode(previousTargets)]; - })(); - - const fullTarget = inferPrimitiveTarget(target, previousTargets); - - if (insertionMode != null) { - return { - type: "primitiveDestination", - insertionMode, - target: fullTarget, - }; - } - - return fullTarget; -} - function inferPrimitiveTarget( target: PartialPrimitiveTargetDescriptor, previousTargets: PartialTargetDescriptor[], @@ -263,25 +183,16 @@ function isLineNumberMark(target: PartialPrimitiveTargetDescriptor): boolean { function getPreviousMark( previousTargets: PartialTargetDescriptor[], ): Mark | undefined { - return getPreviousTargetAttribute(previousTargets, { - target: (target: PartialPrimitiveTargetDescriptor) => target.mark, - }); + return getPreviousTargetAttribute( + previousTargets, + (target: PartialPrimitiveTargetDescriptor) => target.mark, + ); } function getPreviousPreservedModifiers( previousTargets: PartialTargetDescriptor[], ): Modifier[] | undefined { - return getPreviousTargetAttribute(previousTargets, { - target: getPreservedModifiers, - }); -} - -function getPreviousInsertionMode( - previousTargets: PartialTargetDescriptor[], -): InsertionMode | undefined { - return getPreviousTargetAttribute(previousTargets, { - destination: (insertionMode: InsertionMode) => insertionMode, - }); + return getPreviousTargetAttribute(previousTargets, getPreservedModifiers); } /** @@ -296,33 +207,15 @@ function getPreviousInsertionMode( */ function getPreviousTargetAttribute( previousTargets: PartialTargetDescriptor[], - getAttribute: { - target?: (target: PartialPrimitiveTargetDescriptor) => T | undefined; - destination?: (insertionMode: InsertionMode) => T | undefined; - }, + getAttribute: (target: PartialPrimitiveTargetDescriptor) => T | undefined, ): T | undefined { // Search from back(last) to front(first) for (let i = previousTargets.length - 1; i > -1; --i) { const target = previousTargets[i]; switch (target.type) { case "primitive": { - if (getAttribute.target != null) { - const attributeValue = getAttribute.target(target); - if (attributeValue != null) { - return attributeValue; - } - } - break; - } - case "primitiveDestination": { - if (getAttribute.destination != null) { - const attributeValue = getAttribute.destination(target.insertionMode); - if (attributeValue != null) { - return attributeValue; - } - } - if (getAttribute.target != null) { - const attributeValue = getAttribute.target(target.target); + if (getAttribute != null) { + const attributeValue = getAttribute(target); if (attributeValue != null) { return attributeValue; } @@ -339,22 +232,6 @@ function getPreviousTargetAttribute( } break; } - case "rangeDestination": { - if (getAttribute.destination != null) { - const attributeValue = getAttribute.destination(target.insertionMode); - if (attributeValue != null) { - return attributeValue; - } - } - const attributeValue = getPreviousTargetAttribute( - [target.target.anchor], - getAttribute, - ); - if (attributeValue != null) { - return attributeValue; - } - break; - } case "list": { const attributeValue = getPreviousTargetAttribute( target.elements, diff --git a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts index 345167a0a98..b584cf56492 100644 --- a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts @@ -1,7 +1,6 @@ import { Direction, ImplicitTargetDescriptor, - InsertionMode, Modifier, Range, ScopeType, @@ -212,19 +211,16 @@ class TargetPipeline { ): (Target | Destination)[] { let markStage: MarkStage; let nonModifierStages: ModifierStage[]; - let destination: InsertionMode | undefined; if (targetDescriptor.type === "implicit") { markStage = new ImplicitStage(); nonModifierStages = []; - destination = undefined; } else { markStage = this.markStageFactory.create(targetDescriptor.mark); nonModifierStages = getModifierStagesFromTargetModifiers( this.modifierStageFactory, targetDescriptor.modifiers, ); - destination = targetDescriptor.destination; } // First, get the targets output by the mark @@ -244,11 +240,7 @@ class TargetPipeline { ]; // Run all targets through the modifier stages - const targets = processModifierStages(modifierStages, markOutputTargets); - - return destination != null - ? targets.map((target) => target.toDestination(destination!)) - : targets; + return processModifierStages(modifierStages, markOutputTargets); } } diff --git a/packages/cursorless-engine/src/scripts/transformRecordedTests/transformations/upgrade.ts b/packages/cursorless-engine/src/scripts/transformRecordedTests/transformations/upgrade.ts index 74fb84b0fd2..ec303ddbc16 100644 --- a/packages/cursorless-engine/src/scripts/transformRecordedTests/transformations/upgrade.ts +++ b/packages/cursorless-engine/src/scripts/transformRecordedTests/transformations/upgrade.ts @@ -1,7 +1,6 @@ import { TestCaseFixture, TestCaseFixtureLegacy } from "@cursorless/common"; import { flow } from "lodash"; import { canonicalizeAndValidateCommand } from "../../../core/commandVersionUpgrades/canonicalizeAndValidateCommand"; -import { cleanUpTestCaseCommand } from "../../../testUtil/cleanUpTestCaseCommand"; import { reorderFields } from "./reorderFields"; export const upgrade = flow(upgradeCommand, reorderFields); @@ -9,9 +8,6 @@ export const upgrade = flow(upgradeCommand, reorderFields); function upgradeCommand(fixture: TestCaseFixtureLegacy): TestCaseFixture { return { ...fixture, - command: flow( - canonicalizeAndValidateCommand, - cleanUpTestCaseCommand, - )(fixture.command), + command: flow(canonicalizeAndValidateCommand)(fixture.command), }; } diff --git a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts index 95eb9d2446c..033d0afe6b5 100644 --- a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts +++ b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts @@ -48,7 +48,7 @@ export class TestCase { private extraSnapshotFields?: ExtraSnapshotField[], ) { const activeEditor = ide().activeTextEditor!; - this.command = cleanUpTestCaseCommand(command); + this.command = command; this.targetKeys = command.targets.map(extractTargetKeys).flat(); diff --git a/packages/cursorless-engine/src/testUtil/cleanUpTestCaseCommand.ts b/packages/cursorless-engine/src/testUtil/cleanUpTestCaseCommand.ts deleted file mode 100644 index 4eddf5dbf6f..00000000000 --- a/packages/cursorless-engine/src/testUtil/cleanUpTestCaseCommand.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { CommandLatest } from "@cursorless/common"; -import { merge } from "lodash"; - -export function cleanUpTestCaseCommand(command: CommandLatest): CommandLatest { - const { args } = command.action; - const result = merge({}, command); - if (args == null || args.length === 0) { - result.action.args = undefined; - } - return result; -} diff --git a/packages/cursorless-engine/src/util/getPrimitiveTargets.ts b/packages/cursorless-engine/src/util/getPrimitiveTargets.ts index cef29c0f808..40a53b142bd 100644 --- a/packages/cursorless-engine/src/util/getPrimitiveTargets.ts +++ b/packages/cursorless-engine/src/util/getPrimitiveTargets.ts @@ -22,18 +22,13 @@ function getPartialPrimitiveTargetsHelper( switch (target.type) { case "primitive": return [target]; - case "primitiveDestination": - return [target.target]; + case "list": return target.elements.flatMap(getPartialPrimitiveTargetsHelper); case "range": return [target.anchor, target.active].flatMap( getPartialPrimitiveTargetsHelper, ); - case "rangeDestination": - return [target.target.anchor, target.target.active].flatMap( - getPartialPrimitiveTargetsHelper, - ); case "implicit": return []; } @@ -66,8 +61,6 @@ function transformPartialPrimitiveTargetsHelper( switch (target.type) { case "primitive": return func(target); - case "primitiveDestination": - return { ...target, target: func(target.target) }; case "implicit": return target; case "list": @@ -88,17 +81,5 @@ function transformPartialPrimitiveTargetsHelper( | ImplicitTargetDescriptor, active: func(target.active), }; - case "rangeDestination": - return { - ...target, - target: { - ...target.target, - anchor: transformPartialPrimitiveTargetsHelper( - target.target.anchor, - func, - ) as PartialPrimitiveTargetDescriptor | ImplicitTargetDescriptor, - active: func(target.target.active), - }, - }; } } From a9215ef13fbb117aa4aa882afc84ef3b0f783427 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 10 Jul 2023 16:29:07 +0200 Subject: [PATCH 12/99] clean up validation --- packages/common/src/index.ts | 2 +- .../{ActionCommand.ts => ActionDescriptor.ts} | 5 +- .../src/types/command/CommandV6.types.ts | 2 +- .../common/src/types/command/command.types.ts | 2 +- .../canonicalizeAndValidateCommand.ts | 23 ++++---- .../canonicalizeTargets.ts | 45 ++++++++-------- .../transformRecordedTests/checkMarks.ts | 11 ++-- .../src/testCaseRecorder/TestCase.ts | 12 ++--- .../src/util/getPartialTargetDescriptors.1.ts | 53 +++++++++++++++++++ .../src/util/getPrimitiveTargets.ts | 1 - 10 files changed, 104 insertions(+), 52 deletions(-) rename packages/common/src/types/command/{ActionCommand.ts => ActionDescriptor.ts} (96%) create mode 100644 packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 30a9c4892f9..4313108eb67 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -65,7 +65,7 @@ export * from "./util/CompositeKeyMap"; export * from "./ide/normalized/NormalizedIDE"; export * from "./types/command/command.types"; export * from "./types/command/PartialTargetDescriptor.types"; -export * from "./types/command/ActionCommand"; +export * from "./types/command/ActionDescriptor"; export * from "./types/command/legacy/CommandV0V1.types"; export * from "./types/command/legacy/CommandV2.types"; export * from "./types/command/legacy/CommandV3.types"; diff --git a/packages/common/src/types/command/ActionCommand.ts b/packages/common/src/types/command/ActionDescriptor.ts similarity index 96% rename from packages/common/src/types/command/ActionCommand.ts rename to packages/common/src/types/command/ActionDescriptor.ts index ac9090b15a0..b9556f6b010 100644 --- a/packages/common/src/types/command/ActionCommand.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -62,7 +62,10 @@ const complexActionNames = [ "wrapWithPairedDelimiter", ] as const; -const actionNames = [...simpleActionNames, ...complexActionNames] as const; +export const actionNames = [ + ...simpleActionNames, + ...complexActionNames, +] as const; export type SimpleActionName = (typeof simpleActionNames)[number]; export type ActionType = (typeof actionNames)[number]; diff --git a/packages/common/src/types/command/CommandV6.types.ts b/packages/common/src/types/command/CommandV6.types.ts index 33ca4c4b274..19e9373dc2f 100644 --- a/packages/common/src/types/command/CommandV6.types.ts +++ b/packages/common/src/types/command/CommandV6.types.ts @@ -1,4 +1,4 @@ -import type { PartialActionDescriptor } from "./ActionCommand"; +import type { PartialActionDescriptor } from "./ActionDescriptor"; export interface CommandV6 { /** diff --git a/packages/common/src/types/command/command.types.ts b/packages/common/src/types/command/command.types.ts index 45bc1a80b8a..27bb4d086ab 100644 --- a/packages/common/src/types/command/command.types.ts +++ b/packages/common/src/types/command/command.types.ts @@ -1,4 +1,4 @@ -import type { PartialActionDescriptor } from "./ActionCommand"; +import type { PartialActionDescriptor } from "./ActionDescriptor"; import { CommandV6 } from "./CommandV6.types"; import type { CommandV0, CommandV1 } from "./legacy/CommandV0V1.types"; import type { CommandV2 } from "./legacy/CommandV2.types"; diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts index 4d2fe8a92f2..c508817d751 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts @@ -3,15 +3,16 @@ import { Command, CommandComplete, CommandLatest, - EnforceUndefined, LATEST_VERSION, Modifier, OutdatedExtensionError, + PartialActionDescriptor, PartialTargetDescriptor, showWarning, SimpleScopeTypeType, } from "@cursorless/common"; import { ide } from "../../singletons/ide.singleton"; +import { getPartialTargets } from "../../util/getPartialTargetDescriptors.1"; import { getPartialPrimitiveTargets } from "../../util/getPrimitiveTargets"; import canonicalizeActionName from "./canonicalizeActionName"; import canonicalizeTargets from "./canonicalizeTargets"; @@ -31,28 +32,24 @@ import { upgradeV5ToV6 } from "./upgradeV5ToV6"; */ export function canonicalizeAndValidateCommand( command: Command, -): EnforceUndefined { + // ): EnforceUndefined { +): CommandComplete { const commandUpgraded = upgradeCommand(command); - const { - action, - targets: inputPartialTargets, - usePrePhraseSnapshot = false, - spokenForm, - } = commandUpgraded; + const { action, usePrePhraseSnapshot = false, spokenForm } = commandUpgraded; const actionName = canonicalizeActionName(action.name); - const partialTargets = canonicalizeTargets(inputPartialTargets); + const partialTargets = getPartialTargets(commandUpgraded); + canonicalizeTargets(partialTargets); validateCommand(actionName, partialTargets); return { version: LATEST_VERSION, spokenForm, action: { + ...action, name: actionName, - args: action.args ?? [], - }, - targets: partialTargets, + } as PartialActionDescriptor, usePrePhraseSnapshot, }; } @@ -99,7 +96,7 @@ function upgradeCommand(command: Command): CommandLatest { function validateCommand( actionName: ActionType, partialTargets: PartialTargetDescriptor[], -) { +): void { if ( usesScopeType("notebookCell", partialTargets) && !["editNewLineBefore", "editNewLineAfter"].includes(actionName) diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargets.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargets.ts index ee3b3f3430e..2040cb6466a 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargets.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargets.ts @@ -1,12 +1,10 @@ -import { HatStyleName } from "@cursorless/common"; -import update from "immutability-helper"; -import { flow } from "lodash"; -import { transformPartialPrimitiveTargets } from "../../util/getPrimitiveTargets"; import { + HatStyleName, PartialPrimitiveTargetDescriptor, PartialTargetDescriptor, SimpleScopeTypeType, } from "@cursorless/common"; +import { getPartialPrimitiveTargets } from "../../util/getPrimitiveTargets"; const SCOPE_TYPE_CANONICALIZATION_MAPPING: Record = { @@ -19,9 +17,9 @@ const COLOR_CANONICALIZATION_MAPPING: Record = { purple: "pink", }; -const canonicalizeScopeTypes = ( +function canonicalizeScopeTypes( target: PartialPrimitiveTargetDescriptor, -): PartialPrimitiveTargetDescriptor => { +): void { target.modifiers?.forEach((mod) => { if (mod.type === "containingScope" || mod.type === "everyScope") { mod.scopeType.type = @@ -29,26 +27,25 @@ const canonicalizeScopeTypes = ( mod.scopeType.type; } }); - return target; -}; +} -const canonicalizeColors = ( - target: PartialPrimitiveTargetDescriptor, -): PartialPrimitiveTargetDescriptor => - target.mark?.type === "decoratedSymbol" - ? update(target, { - mark: { - symbolColor: (symbolColor: string) => - COLOR_CANONICALIZATION_MAPPING[symbolColor] ?? symbolColor, - }, - }) - : target; +function canonicalizeColors(target: PartialPrimitiveTargetDescriptor): void { + if (target.mark?.type === "decoratedSymbol") { + target.mark = { + ...target.mark, + symbolColor: + COLOR_CANONICALIZATION_MAPPING[target.mark.symbolColor] ?? + target.mark.symbolColor, + }; + } +} export default function canonicalizeTargets( partialTargets: PartialTargetDescriptor[], -) { - return transformPartialPrimitiveTargets( - partialTargets, - flow(canonicalizeScopeTypes, canonicalizeColors), - ); +): void { + const partialPrimitiveTargets = getPartialPrimitiveTargets(partialTargets); + partialPrimitiveTargets.forEach((target) => { + canonicalizeScopeTypes(target); + canonicalizeColors(target); + }); } diff --git a/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts b/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts index d0bb955dfc6..2c7a952e3f7 100644 --- a/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts +++ b/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts @@ -1,10 +1,11 @@ import { FakeIDE, TestCaseFixtureLegacy } from "@cursorless/common"; +import { uniq } from "lodash"; +import { injectIde } from "../../singletons/ide.singleton"; +import tokenGraphemeSplitter from "../../singletons/tokenGraphemeSplitter.singleton"; import { extractTargetKeys } from "../../testUtil/extractTargetKeys"; +import { getPartialTargets } from "../../util/getPartialTargetDescriptors.1"; import { upgrade } from "./transformations/upgrade"; import assert = require("assert"); -import { uniq } from "lodash"; -import tokenGraphemeSplitter from "../../singletons/tokenGraphemeSplitter.singleton"; -import { injectIde } from "../../singletons/ide.singleton"; export function checkMarks(originalFixture: TestCaseFixtureLegacy): undefined { const command = upgrade(originalFixture).command; @@ -12,7 +13,9 @@ export function checkMarks(originalFixture: TestCaseFixtureLegacy): undefined { injectIde(new FakeIDE()); const graphemeSplitter = tokenGraphemeSplitter(); - const targetedMarks = command.targets.map(extractTargetKeys).flat(); + const targetedMarks = getPartialTargets(command) + .map(extractTargetKeys) + .flat(); const normalizeGraphemes = (key: string): string => graphemeSplitter .getTokenGraphemes(key) diff --git a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts index 033d0afe6b5..4bdd63a6365 100644 --- a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts +++ b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts @@ -17,11 +17,11 @@ import { Token, } from "@cursorless/common"; import { pick } from "lodash"; +import { StoredTargetMap } from ".."; import { ide } from "../singletons/ide.singleton"; -import { cleanUpTestCaseCommand } from "../testUtil/cleanUpTestCaseCommand"; import { extractTargetKeys } from "../testUtil/extractTargetKeys"; import { takeSnapshot } from "../testUtil/takeSnapshot"; -import { StoredTargetMap } from ".."; +import { getPartialTargets as getPartialTargetDescriptors } from "../util/getPartialTargetDescriptors.1"; export class TestCase { private languageId: string; @@ -49,11 +49,11 @@ export class TestCase { ) { const activeEditor = ide().activeTextEditor!; this.command = command; - - this.targetKeys = command.targets.map(extractTargetKeys).flat(); - + this.partialTargetDescriptors = getPartialTargetDescriptors(command); + this.targetKeys = this.partialTargetDescriptors + .map(extractTargetKeys) + .flat(); this.languageId = activeEditor.document.languageId; - this.partialTargetDescriptors = command.targets; this._awaitingFinalMarkInfo = isHatTokenMapTest; } diff --git a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts new file mode 100644 index 00000000000..42ddf7caf00 --- /dev/null +++ b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts @@ -0,0 +1,53 @@ +import { + CommandLatest, + PartialDestinationDescriptor, + PartialPrimitiveTargetDescriptor, + PartialRangeTargetDescriptor, + PartialTargetDescriptor, +} from "@cursorless/common"; + +export function getPartialTargets( + command: CommandLatest, +): PartialTargetDescriptor[] { + switch (command.action.name) { + case "replaceWithTarget": + case "moveToTarget": + return [ + command.action.source, + getTargetsFromDestination(command.action.destination), + ]; + case "swapTargets": + return [command.action.target1, command.action.target2]; + case "pasteFromClipboard": + return [getTargetsFromDestination(command.action.destination)]; + default: + return [command.action.target]; + } +} + +function getTargetsFromDestination( + destination: PartialDestinationDescriptor, +): PartialTargetDescriptor { + if (destination.type === "destinationList") { + const elements: ( + | PartialPrimitiveTargetDescriptor + | PartialRangeTargetDescriptor + )[] = []; + destination.destinations.forEach((destination) => { + if (destination.target.type === "list") { + elements.push(...destination.target.elements); + } else { + elements.push( + destination.target as + | PartialPrimitiveTargetDescriptor + | PartialRangeTargetDescriptor, + ); + } + }); + return { + type: "list", + elements, + }; + } + return destination.target; +} diff --git a/packages/cursorless-engine/src/util/getPrimitiveTargets.ts b/packages/cursorless-engine/src/util/getPrimitiveTargets.ts index 40a53b142bd..6a59b71ccc8 100644 --- a/packages/cursorless-engine/src/util/getPrimitiveTargets.ts +++ b/packages/cursorless-engine/src/util/getPrimitiveTargets.ts @@ -22,7 +22,6 @@ function getPartialPrimitiveTargetsHelper( switch (target.type) { case "primitive": return [target]; - case "list": return target.elements.flatMap(getPartialPrimitiveTargetsHelper); case "range": From a855becb1769c470417820c3c192756a8a588cc5 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 10 Jul 2023 17:43:47 +0200 Subject: [PATCH 13/99] stuff --- .../core/commandRunner/CommandRunnerImpl.ts | 118 ++++++++++++++---- .../canonicalizeAndValidateCommand.ts | 37 +----- .../transformRecordedTests/checkMarks.ts | 4 +- .../src/testCaseRecorder/TestCase.ts | 2 +- .../src/util/getPartialTargetDescriptors.1.ts | 10 +- .../src/keyboard/KeyboardCommandsTargeted.ts | 109 +++++++++------- 6 files changed, 168 insertions(+), 112 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 1e6264fc9f4..f51d56d05d9 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -1,13 +1,18 @@ -import { CommandComplete } from "@cursorless/common"; +import { + CommandComplete, + PartialActionDescriptor, + PartialTargetDescriptor, +} from "@cursorless/common"; import { CommandRunner } from "../../CommandRunner"; -import { ActionRecord } from "../../actions/actions.types"; +import { ActionRecord, ActionReturnValue } from "../../actions/actions.types"; import { StoredTargetMap } from "../../index"; import { TargetPipelineRunner } from "../../processTargets"; +import { TargetDescriptor } from "../../typings/TargetDescriptor"; import { SelectionWithEditor } from "../../typings/Types"; import { Target } from "../../typings/target.types"; +import { getPartialTargetDescriptorFromDestination } from "../../util/getPartialTargetDescriptors.1"; import { Debug } from "../Debug"; -import { checkForOldInference } from "../commandVersionUpgrades/canonicalizeAndValidateCommand"; -import inferFullTargetDescriptors from "../inferFullTargets"; +import { default as inferFullTargetDescriptors } from "../inferFullTargets"; import { selectionToStoredTarget } from "./selectionToStoredTarget"; export class CommandRunnerImpl implements CommandRunner { @@ -35,30 +40,25 @@ export class CommandRunnerImpl implements CommandRunner { * action, and returns the desired return value indicated by the action, if * it has one. */ - async run({ - action: { name: actionName, args: actionArgs }, - targets: partialTargetDescriptors, - }: CommandComplete): Promise { - checkForOldInference(partialTargetDescriptors); - - const targetDescriptors = inferFullTargetDescriptors( - partialTargetDescriptors, - ); + async run({ action }: CommandComplete): Promise { + // const targetDescriptors = inferFullTargetDescriptors( + // partialTargetDescriptors, + // ); - if (this.debug.active) { - this.debug.log("Full targets:"); - this.debug.log(JSON.stringify(targetDescriptors, null, 2)); - } + // if (this.debug.active) { + // this.debug.log("Full targets:"); + // this.debug.log(JSON.stringify(targetDescriptors, null, 2)); + // } - const action = this.actions[actionName]; + // const action = this.actions[actionName]; - const prePositionStages = - action.getPrePositionStages?.(...actionArgs) ?? []; - const finalStages = action.getFinalStages?.(...actionArgs) ?? []; + // const prePositionStages = + // action.getPrePositionStages?.(...actionArgs) ?? []; + // const finalStages = action.getFinalStages?.(...actionArgs) ?? []; - const targets = targetDescriptors.map((targetDescriptor) => - this.pipelineRunner.run(targetDescriptor, prePositionStages, finalStages), - ); + // const targets = targetDescriptors.map((targetDescriptor) => + // this.pipelineRunner.run(targetDescriptor, prePositionStages, finalStages), + // ); const { returnValue, @@ -67,7 +67,7 @@ export class CommandRunnerImpl implements CommandRunner { sourceSelections: newSourceSelections, sourceTargets: newSourceTargets, instanceReferenceTargets: newInstanceReferenceTargets, - } = await action.run(targets, ...actionArgs); + } = await this.runAction(action); this.storedTargets.set( "that", @@ -81,6 +81,74 @@ export class CommandRunnerImpl implements CommandRunner { return returnValue; } + + private runAction( + partialActionDescriptor: PartialActionDescriptor, + ): Promise { + const action = this.actions[partialActionDescriptor.name]; + + switch (partialActionDescriptor.name) { + case "replaceWithTarget": + case "moveToTarget": + { + const [sourceDescriptor, destinationDescriptor] = + this.inferFullTargetDescriptors([ + partialActionDescriptor.source, + getPartialTargetDescriptorFromDestination( + partialActionDescriptor.destination, + ), + ]); + throw Error("stuff"); + // Note: we don't need to worry about final stages here because bring and move don't use them! + // const source = this.pipelineRunner.run(sourceDescriptor); + // const destination = pipelineRunner + // .run(destinationTargetDescriptor) + // .getDestination(partialActionDescriptor.destination.insertionMode); + + // return action.run(source, destina41tion); + } + break; + default: + throw Error("stuff"); + // case "wrapWithPairedDelimiter": + // // Some duplication with default case, but not tooo bad + // const [targetDescriptor] = inferFullTargets([ + // partialActionDescriptor.target, + // ]); + // const target = pipelineRunner.run(targetDescriptor); + + // return action.run( + // target, + // partialActionDescriptor.left, + // partialActionDescriptor.right, + // ); + // default: + // // TODO: Could prob expose `inferFullTarget` (singular) that takes single descriptor with no previousTargets + // // eg const targetDescriptor = inferFullTarget(partialActionDescriptor.target) + // const [targetDescriptor] = inferFullTargets([ + // partialActionDescriptor.target, + // ]); + + // // TODO: Do we want to handle final stages in the generic case? Most + // // actions don't have them anyway so we might want special cases for them + // const target = pipelineRunner.run(targetDescriptor); + + // return action.run(target); + } + } + + private inferFullTargetDescriptors( + targets: PartialTargetDescriptor[], + ): TargetDescriptor[] { + const targetDescriptors = inferFullTargetDescriptors(targets); + + if (this.debug.active) { + this.debug.log("Full targets:"); + this.debug.log(JSON.stringify(targetDescriptors, null, 2)); + } + + return targetDescriptors; + } } function constructStoredTarget( diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts index c508817d751..120779cd471 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts @@ -12,7 +12,7 @@ import { SimpleScopeTypeType, } from "@cursorless/common"; import { ide } from "../../singletons/ide.singleton"; -import { getPartialTargets } from "../../util/getPartialTargetDescriptors.1"; +import { getPartialTargetDescriptors } from "../../util/getPartialTargetDescriptors.1"; import { getPartialPrimitiveTargets } from "../../util/getPrimitiveTargets"; import canonicalizeActionName from "./canonicalizeActionName"; import canonicalizeTargets from "./canonicalizeTargets"; @@ -38,7 +38,7 @@ export function canonicalizeAndValidateCommand( const { action, usePrePhraseSnapshot = false, spokenForm } = commandUpgraded; const actionName = canonicalizeActionName(action.name); - const partialTargets = getPartialTargets(commandUpgraded); + const partialTargets = getPartialTargetDescriptors(commandUpgraded); canonicalizeTargets(partialTargets); validateCommand(actionName, partialTargets); @@ -119,36 +119,3 @@ function usesScopeType( ), ); } - -export function checkForOldInference( - partialTargets: PartialTargetDescriptor[], -) { - const hasOldInference = partialTargets.some((target) => { - return ( - target.type === "range" && - target.active.mark == null && - target.active.modifiers?.some( - (m) => m.type === "startOf" || m.type === "endOf", - ) && - !target.active.modifiers?.some((m) => m.type === "inferPreviousMark") - ); - }); - - if (hasOldInference) { - const { globalState, messages } = ide(); - const hideInferenceWarning = globalState.get("hideInferenceWarning"); - - if (!hideInferenceWarning) { - showWarning( - messages, - "deprecatedPositionInference", - 'The "past start of" / "past end of" form has changed behavior. For the old behavior, update cursorless-talon (https://www.cursorless.org/docs/user/updating/), and then you can now say "past start of its" / "past end of its". For example, "take air past end of its line". You may also consider using "head" / "tail" instead; see https://www.cursorless.org/docs/#head-and-tail', - "Don't show again", - ).then((pressed) => { - if (pressed) { - globalState.set("hideInferenceWarning", true); - } - }); - } - } -} diff --git a/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts b/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts index 2c7a952e3f7..c721645f82f 100644 --- a/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts +++ b/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts @@ -3,7 +3,7 @@ import { uniq } from "lodash"; import { injectIde } from "../../singletons/ide.singleton"; import tokenGraphemeSplitter from "../../singletons/tokenGraphemeSplitter.singleton"; import { extractTargetKeys } from "../../testUtil/extractTargetKeys"; -import { getPartialTargets } from "../../util/getPartialTargetDescriptors.1"; +import { getPartialTargetDescriptors } from "../../util/getPartialTargetDescriptors.1"; import { upgrade } from "./transformations/upgrade"; import assert = require("assert"); @@ -13,7 +13,7 @@ export function checkMarks(originalFixture: TestCaseFixtureLegacy): undefined { injectIde(new FakeIDE()); const graphemeSplitter = tokenGraphemeSplitter(); - const targetedMarks = getPartialTargets(command) + const targetedMarks = getPartialTargetDescriptors(command) .map(extractTargetKeys) .flat(); const normalizeGraphemes = (key: string): string => diff --git a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts index 4bdd63a6365..6d99307caeb 100644 --- a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts +++ b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts @@ -21,7 +21,7 @@ import { StoredTargetMap } from ".."; import { ide } from "../singletons/ide.singleton"; import { extractTargetKeys } from "../testUtil/extractTargetKeys"; import { takeSnapshot } from "../testUtil/takeSnapshot"; -import { getPartialTargets as getPartialTargetDescriptors } from "../util/getPartialTargetDescriptors.1"; +import { getPartialTargetDescriptors as getPartialTargetDescriptors } from "../util/getPartialTargetDescriptors.1"; export class TestCase { private languageId: string; diff --git a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts index 42ddf7caf00..8fab1e2ce24 100644 --- a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts +++ b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts @@ -6,7 +6,7 @@ import { PartialTargetDescriptor, } from "@cursorless/common"; -export function getPartialTargets( +export function getPartialTargetDescriptors( command: CommandLatest, ): PartialTargetDescriptor[] { switch (command.action.name) { @@ -14,18 +14,20 @@ export function getPartialTargets( case "moveToTarget": return [ command.action.source, - getTargetsFromDestination(command.action.destination), + getPartialTargetDescriptorFromDestination(command.action.destination), ]; case "swapTargets": return [command.action.target1, command.action.target2]; case "pasteFromClipboard": - return [getTargetsFromDestination(command.action.destination)]; + return [ + getPartialTargetDescriptorFromDestination(command.action.destination), + ]; default: return [command.action.target]; } } -function getTargetsFromDestination( +export function getPartialTargetDescriptorFromDestination( destination: PartialDestinationDescriptor, ): PartialTargetDescriptor { if (destination.type === "destinationList") { diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index 94b562c9bad..d4240b0665b 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -3,14 +3,15 @@ import { CommandLatest, ImplicitTargetDescriptor, LATEST_VERSION, + PartialDestinationDescriptor, PartialPrimitiveTargetDescriptor, PartialTargetDescriptor, SimpleScopeTypeType, } from "@cursorless/common"; import { runCursorlessCommand } from "@cursorless/vscode-common"; import * as vscode from "vscode"; -import { getStyleName } from "../ide/vscode/hats/getStyleName"; import type { HatColor, HatShape } from "../ide/vscode/hatStyles.types"; +import { getStyleName } from "../ide/vscode/hats/getStyleName"; import KeyboardCommandsModal from "./KeyboardCommandsModal"; import KeyboardHandler from "./KeyboardHandler"; @@ -122,8 +123,8 @@ export default class KeyboardCommandsTargeted { return await executeCursorlessCommand({ action: { name: "highlight", + target, }, - targets: [target], }); }; @@ -139,9 +140,7 @@ export default class KeyboardCommandsTargeted { await executeCursorlessCommand({ action: { name: "highlight", - }, - targets: [ - { + target: { type: "primitive", modifiers: [ { @@ -155,22 +154,20 @@ export default class KeyboardCommandsTargeted { type: "that", }, }, - ], + }, }); private highlightTarget = () => executeCursorlessCommand({ action: { name: "highlight", - }, - targets: [ - { + target: { type: "primitive", mark: { type: "that", }, }, - ], + }, }); /** @@ -179,31 +176,53 @@ export default class KeyboardCommandsTargeted { * @returns A promise that resolves to the result of the cursorless command */ performActionOnTarget = async (action: ActionType) => { - const targets: ( - | PartialPrimitiveTargetDescriptor - | ImplicitTargetDescriptor - )[] = [ - { - type: "primitive", - mark: { - type: "that", - }, + const target: PartialPrimitiveTargetDescriptor = { + type: "primitive", + mark: { + type: "that", }, - ]; + }; - if (MULTIPLE_TARGET_ACTIONS.includes(action)) { - // For multi-target actiosn (eg "bring"), we just use implicit destination - targets.push({ - type: "implicit", - }); - } + let returnValue: unknown; - const returnValue = await executeCursorlessCommand({ - action: { - name: action, - }, - targets, - }); + switch (action) { + case "wrapWithPairedDelimiter": + throw Error(`Unsupported keyboard action: ${action}`); + case "replaceWithTarget": + case "moveToTarget": + returnValue = await executeCursorlessCommand({ + action: { + name: action, + source: target, + destination: toDestination({ type: "implicit" }), + }, + }); + break; + case "swapTargets": + returnValue = await executeCursorlessCommand({ + action: { + name: action, + target1: target, + target2: { type: "implicit" }, + }, + }); + break; + case "pasteFromClipboard": + returnValue = await executeCursorlessCommand({ + action: { + name: action, + destination: toDestination(target), + }, + }); + break; + default: + returnValue = await executeCursorlessCommand({ + action: { + name: action, + target, + }, + }); + } await this.highlightTarget(); @@ -224,16 +243,14 @@ export default class KeyboardCommandsTargeted { executeCursorlessCommand({ action: { name: "highlight", - }, - targets: [ - { + target: { type: "primitive", mark: { type: "cursor", }, modifiers: [{ type: "toRawSelection" }], }, - ], + }, }); /** @@ -244,15 +261,13 @@ export default class KeyboardCommandsTargeted { executeCursorlessCommand({ action: { name: "highlight", - }, - targets: [ - { + target: { type: "primitive", mark: { type: "nothing", }, }, - ], + }, }); } @@ -266,11 +281,15 @@ function executeCursorlessCommand( }); } -const MULTIPLE_TARGET_ACTIONS: ActionType[] = [ - "replaceWithTarget", - "moveToTarget", - "swapTargets", -]; +function toDestination( + target: PartialPrimitiveTargetDescriptor | ImplicitTargetDescriptor, +): PartialDestinationDescriptor { + return { + type: "destination", + insertionMode: "to", + target, + }; +} const EXIT_CURSORLESS_MODE_ACTIONS: ActionType[] = [ "setSelectionBefore", From c0b72e5aea1a28031919e70ebcde41e0f7d64a39 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 10 Jul 2023 18:03:20 +0200 Subject: [PATCH 14/99] tidy tidy --- .../core/commandRunner/CommandRunnerImpl.ts | 15 +- .../processTargets/TargetPipelineRunner.ts | 170 +++++------------- .../processTargets/marks/TargetMarkStage.ts | 8 +- 3 files changed, 56 insertions(+), 137 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index f51d56d05d9..02faeb89423 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -91,21 +91,24 @@ export class CommandRunnerImpl implements CommandRunner { case "replaceWithTarget": case "moveToTarget": { - const [sourceDescriptor, destinationDescriptor] = + const [sourceDescriptor, destinationTargetDescriptor] = this.inferFullTargetDescriptors([ partialActionDescriptor.source, getPartialTargetDescriptorFromDestination( partialActionDescriptor.destination, ), ]); - throw Error("stuff"); + // Note: we don't need to worry about final stages here because bring and move don't use them! - // const source = this.pipelineRunner.run(sourceDescriptor); - // const destination = pipelineRunner - // .run(destinationTargetDescriptor) + const source = this.pipelineRunner.run(sourceDescriptor); + const destination = this.pipelineRunner.run( + destinationTargetDescriptor, + ); + // map((target, index) => target.toDestination) // .getDestination(partialActionDescriptor.destination.insertionMode); + throw Error("stuff"); - // return action.run(source, destina41tion); + // return action.run(source, destination); } break; default: diff --git a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts index b584cf56492..1825f01b273 100644 --- a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts @@ -11,13 +11,13 @@ import { RangeTargetDescriptor, TargetDescriptor, } from "../typings/TargetDescriptor"; -import { Destination, Target } from "../typings/target.types"; +import { Target } from "../typings/target.types"; import { MarkStageFactory } from "./MarkStageFactory"; import { ModifierStageFactory } from "./ModifierStageFactory"; import { MarkStage, ModifierStage } from "./PipelineStages.types"; import ImplicitStage from "./marks/ImplicitStage"; import { ContainingTokenIfUntypedEmptyStage } from "./modifiers/ConditionalModifierStages"; -import { DestinationImpl, PlainTarget } from "./targets"; +import { PlainTarget } from "./targets"; export class TargetPipelineRunner { constructor( @@ -43,7 +43,7 @@ export class TargetPipelineRunner { target: TargetDescriptor, actionPrePositionStages?: ModifierStage[], actionFinalStages?: ModifierStage[], - ): (Target | Destination)[] { + ): Target[] { return new TargetPipeline( this.modifierStageFactory, this.markStageFactory, @@ -77,11 +77,11 @@ class TargetPipeline { * containing it, and potentially rich context information such as how to remove * the target */ - run(): (Target | Destination)[] { + run(): Target[] { return uniqTargets(this.processTarget(this.target)); } - processTarget(target: TargetDescriptor): (Target | Destination)[] { + processTarget(target: TargetDescriptor): Target[] { switch (target.type) { case "list": return target.elements.flatMap((element) => @@ -95,9 +95,7 @@ class TargetPipeline { } } - processRangeTarget( - targetDesc: RangeTargetDescriptor, - ): (Target | Destination)[] { + processRangeTarget(targetDesc: RangeTargetDescriptor): Target[] { const anchorTargets = this.processPrimitiveTarget(targetDesc.anchor); const activeTargets = this.processPrimitiveTarget(targetDesc.active); @@ -129,27 +127,21 @@ class TargetPipeline { } processContinuousRangeTarget( - anchorTargetOrDestination: Target | Destination, - activeTargetOrDestination: Target | Destination, + anchorTarget: Target, + activeTarget: Target, { excludeAnchor, excludeActive, exclusionScopeType }: RangeTargetDescriptor, - ): (Target | Destination)[] { + ): Target[] { if (exclusionScopeType == null) { return [ - targetsOrDestinationsToContinuousTarget( - anchorTargetOrDestination, - activeTargetOrDestination, + targetsToContinuousTarget( + anchorTarget, + activeTarget, excludeAnchor, excludeActive, ), ]; } - const { target: anchorTarget } = separateDestinationAndTarget( - anchorTargetOrDestination, - ); - const { target: activeTarget } = separateDestinationAndTarget( - activeTargetOrDestination, - ); const isReversed = calcIsReversed(anchorTarget, activeTarget); // For "every" range targets, we need to be smart about how we handle @@ -164,23 +156,23 @@ class TargetPipeline { // again. So instead, we use the equivalent of "previous funk name" to find // our endpoint and then just use an inclusive range ending with that target. return [ - targetsOrDestinationsToContinuousTarget( + targetsToContinuousTarget( excludeAnchor ? excludeScope( this.modifierStageFactory, - anchorTargetOrDestination, + anchorTarget, exclusionScopeType, isReversed ? "backward" : "forward", ) - : anchorTargetOrDestination, + : anchorTarget, excludeActive ? excludeScope( this.modifierStageFactory, - activeTargetOrDestination, + activeTarget, exclusionScopeType, isReversed ? "forward" : "backward", ) - : activeTargetOrDestination, + : activeTarget, false, false, ), @@ -208,7 +200,7 @@ class TargetPipeline { */ processPrimitiveTarget( targetDescriptor: PrimitiveTargetDescriptor | ImplicitTargetDescriptor, - ): (Target | Destination)[] { + ): Target[] { let markStage: MarkStage; let nonModifierStages: ModifierStage[]; @@ -272,50 +264,24 @@ export function processModifierStages( function excludeScope( modifierStageFactory: ModifierStageFactory, - targetOrDestination: Target | Destination, + target: Target, exclusionScopeType: ScopeType, direction: Direction, -): Target | Destination { - const { target, destination } = - separateDestinationAndTarget(targetOrDestination); - - const resultTarget = modifierStageFactory - .create({ - type: "relativeScope", - scopeType: exclusionScopeType, - direction, - length: 1, - offset: 1, - }) - // NB: The following line assumes that content range is always - // contained by domain, so that "every" will properly reconstruct - // the target from the content range. - .run(target)[0]; - - return destination != null - ? destination.withTarget(resultTarget) - : resultTarget; -} - -function separateDestinationAndTarget( - targetOrDestination: Target | Destination, -): { target: Target; destination?: Destination } { - if (isDestination(targetOrDestination)) { - return { - target: targetOrDestination.target, - destination: targetOrDestination, - }; - } - return { - target: targetOrDestination as Target, - destination: undefined, - }; -} - -export function isDestination( - targetOrDestination: Target | Destination, -): targetOrDestination is Destination { - return targetOrDestination instanceof DestinationImpl; +): Target { + return ( + modifierStageFactory + .create({ + type: "relativeScope", + scopeType: exclusionScopeType, + direction, + length: 1, + offset: 1, + }) + // NB: The following line assumes that content range is always + // contained by domain, so that "every" will properly reconstruct + // the target from the content range. + .run(target)[0] + ); } function calcIsReversed(anchor: Target, active: Target) { @@ -328,20 +294,8 @@ function calcIsReversed(anchor: Target, active: Target) { return anchor.contentRange.end.isAfter(active.contentRange.end); } -function uniqTargets( - array: (Target | Destination)[], -): (Target | Destination)[] { - return uniqWith(array, (a, b) => isEqual(a, b)); -} - -function isEqual(a: Target | Destination, b: Target | Destination): boolean { - if (isDestination(a) || isDestination(b)) { - if (!isDestination(a) || !isDestination(b)) { - return false; - } - return a.isEqual(b); - } - return a.isEqual(b); +function uniqTargets(array: Target[]): Target[] { + return uniqWith(array, (a, b) => a.isEqual(b)); } function ensureSingleEditor(anchorTarget: Target, activeTarget: Target) { @@ -350,31 +304,6 @@ function ensureSingleEditor(anchorTarget: Target, activeTarget: Target) { } } -function targetsOrDestinationsToContinuousTarget( - anchorTargetOrDestination: Target | Destination, - activeTargetOrDestination: Target | Destination, - excludeAnchor: boolean = false, - excludeActive: boolean = false, -): Target | Destination { - const { target: anchorTarget, destination } = separateDestinationAndTarget( - anchorTargetOrDestination, - ); - const { target: activeTarget } = separateDestinationAndTarget( - activeTargetOrDestination, - ); - - const continuesRangeTarget = targetsToContinuousTarget( - anchorTarget, - activeTarget, - excludeAnchor, - excludeActive, - ); - - return destination != null - ? continuesRangeTarget.toDestination(destination.insertionMode) - : continuesRangeTarget; -} - export function targetsToContinuousTarget( anchorTarget: Target, activeTarget: Target, @@ -398,18 +327,11 @@ export function targetsToContinuousTarget( } function targetsToVerticalTarget( - anchorTargetOrDestination: Target | Destination, - activeTargetOrDestination: Target | Destination, + anchorTarget: Target, + activeTarget: Target, excludeAnchor: boolean, excludeActive: boolean, -): (Target | Destination)[] { - const { target: anchorTarget, destination } = separateDestinationAndTarget( - anchorTargetOrDestination, - ); - const { target: activeTarget } = separateDestinationAndTarget( - activeTargetOrDestination, - ); - +): Target[] { ensureSingleEditor(anchorTarget, activeTarget); const isReversed = calcIsReversed(anchorTarget, activeTarget); @@ -424,7 +346,7 @@ function targetsToVerticalTarget( : activeTarget.contentRange.end; const activeLine = activePosition.line - (excludeActive ? delta : 0); - const results: (Target | Destination)[] = []; + const results: Target[] = []; for (let i = anchorLine; true; i += delta) { const contentRange = new Range( i, @@ -433,14 +355,12 @@ function targetsToVerticalTarget( anchorTarget.contentRange.end.character, ); - const newTarget = new PlainTarget({ - editor: anchorTarget.editor, - isReversed: anchorTarget.isReversed, - contentRange, - }); - results.push( - destination != null ? destination.withTarget(newTarget) : newTarget, + new PlainTarget({ + editor: anchorTarget.editor, + isReversed: anchorTarget.isReversed, + contentRange, + }), ); if (i === activeLine) { diff --git a/packages/cursorless-engine/src/processTargets/marks/TargetMarkStage.ts b/packages/cursorless-engine/src/processTargets/marks/TargetMarkStage.ts index 10ec07cdfa3..f9190b518d0 100644 --- a/packages/cursorless-engine/src/processTargets/marks/TargetMarkStage.ts +++ b/packages/cursorless-engine/src/processTargets/marks/TargetMarkStage.ts @@ -1,4 +1,4 @@ -import { TargetPipelineRunner, isDestination } from ".."; +import { TargetPipelineRunner } from ".."; import { TargetMark } from "../../typings/TargetDescriptor"; import { Target } from "../../typings/target.types"; import { MarkStage } from "../PipelineStages.types"; @@ -10,10 +10,6 @@ export class TargetMarkStage implements MarkStage { ) {} run(): Target[] { - const targets = this.targetPipelineRunner.run(this.mark.target); - if (targets.some(isDestination)) { - throw Error("Target mark stage doesn't support destinations"); - } - return targets as Target[]; + return this.targetPipelineRunner.run(this.mark.target); } } From 558baf7b5c9d37fd9e1b89bb63d4f16ebc1f9d8c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 16:08:32 +0000 Subject: [PATCH 15/99] [pre-commit.ci lite] apply automatic fixes --- .../commandVersionUpgrades/canonicalizeAndValidateCommand.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts index 120779cd471..8f526b63b9d 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts @@ -8,10 +8,8 @@ import { OutdatedExtensionError, PartialActionDescriptor, PartialTargetDescriptor, - showWarning, SimpleScopeTypeType, } from "@cursorless/common"; -import { ide } from "../../singletons/ide.singleton"; import { getPartialTargetDescriptors } from "../../util/getPartialTargetDescriptors.1"; import { getPartialPrimitiveTargets } from "../../util/getPrimitiveTargets"; import canonicalizeActionName from "./canonicalizeActionName"; From e8cbd25629c10aa1d4d1fc26a2ec9caa23d0fe67 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 10 Jul 2023 18:31:25 +0200 Subject: [PATCH 16/99] stuff --- .../src/types/command/PartialTargetDescriptor.types.ts | 9 ++++++++- .../src/util/getPartialTargetDescriptors.1.ts | 6 +----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 7fc87ddc658..bf7891ac688 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -390,7 +390,14 @@ export interface PartialPrimitiveDestinationDescriptor { export interface PartialListDestinationDescriptor { type: "destinationList"; - destinations: PartialPrimitiveDestinationDescriptor[]; + destinations: { + type: "destination"; + insertionMode: InsertionMode; + target: + | PartialPrimitiveTargetDescriptor + | PartialRangeTargetDescriptor + | PartialListTargetDescriptor; + }[]; } export type PartialDestinationDescriptor = diff --git a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts index 8fab1e2ce24..b5ee10b7497 100644 --- a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts +++ b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts @@ -39,11 +39,7 @@ export function getPartialTargetDescriptorFromDestination( if (destination.target.type === "list") { elements.push(...destination.target.elements); } else { - elements.push( - destination.target as - | PartialPrimitiveTargetDescriptor - | PartialRangeTargetDescriptor, - ); + elements.push(destination.target); } }); return { From 648a12caf2300db781acd5f7af476d293d94afb5 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 10 Jul 2023 19:20:02 +0200 Subject: [PATCH 17/99] stuff2 --- .../upgradeV5ToV6/upgradeV5ToV6.ts | 3 ++- .../src/typings/TargetDescriptor.ts | 15 --------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index 3cffd63f96b..7669f4ef3cb 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -8,6 +8,7 @@ import { ModifierV5, PartialActionDescriptor, PartialDestinationDescriptor, + PartialListDestinationDescriptor, PartialListTargetDescriptor, PartialListTargetDescriptorV5, PartialPrimitiveDestinationDescriptor, @@ -158,7 +159,7 @@ function listTargetToDestination( return { type: "destinationList", destinations, - }; + } as PartialListDestinationDescriptor; } return destinations[0]; } diff --git a/packages/cursorless-engine/src/typings/TargetDescriptor.ts b/packages/cursorless-engine/src/typings/TargetDescriptor.ts index 227d8bd9bde..f690380c479 100644 --- a/packages/cursorless-engine/src/typings/TargetDescriptor.ts +++ b/packages/cursorless-engine/src/typings/TargetDescriptor.ts @@ -71,18 +71,3 @@ export type TargetDescriptor = | RangeTargetDescriptor | ListTargetDescriptor | ImplicitTargetDescriptor; - -export interface PrimitiveDestinationDescriptor { - type: "destination"; - insertionMode: InsertionMode; - target: TargetDescriptor; -} - -export interface ListDestinationDescriptor { - type: "destinationList"; - destinations: PrimitiveDestinationDescriptor[]; -} - -export type DestinationDescriptor = - | PrimitiveDestinationDescriptor - | ListDestinationDescriptor; From 3800c91381a1524298cd48175bffc6f77d53e425 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 17:21:57 +0000 Subject: [PATCH 18/99] [pre-commit.ci lite] apply automatic fixes --- packages/cursorless-engine/src/typings/TargetDescriptor.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cursorless-engine/src/typings/TargetDescriptor.ts b/packages/cursorless-engine/src/typings/TargetDescriptor.ts index f690380c479..bcf96035fab 100644 --- a/packages/cursorless-engine/src/typings/TargetDescriptor.ts +++ b/packages/cursorless-engine/src/typings/TargetDescriptor.ts @@ -1,6 +1,5 @@ import { ImplicitTargetDescriptor, - InsertionMode, Modifier, PartialMark, PartialRangeType, From 42fa23a860674ffd81aaa8685f279b01876a9686 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 10 Jul 2023 20:26:58 +0200 Subject: [PATCH 19/99] clean --- .../src/processTargets/targets/DestinationImpl.ts | 4 ---- packages/cursorless-engine/src/typings/target.types.ts | 1 - 2 files changed, 5 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts index b1cda4b9dd3..2213cecaa43 100644 --- a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts +++ b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts @@ -34,10 +34,6 @@ export default class DestinationImpl implements Destination { ); } - withTarget(target: Target): Destination { - return new DestinationImpl(target, this.insertionMode); - } - getEditNewActionType(): EditNewActionType { if ( this.insertionDelimiter === "\n" && diff --git a/packages/cursorless-engine/src/typings/target.types.ts b/packages/cursorless-engine/src/typings/target.types.ts index fce10829527..1ff0fcb83da 100644 --- a/packages/cursorless-engine/src/typings/target.types.ts +++ b/packages/cursorless-engine/src/typings/target.types.ts @@ -169,6 +169,5 @@ export interface Destination { insertionMode: InsertionMode; target: Target; constructChangeEdit(text: string): EditWithRangeUpdater; - withTarget(target: Target): Destination; isEqual(destination: Destination): boolean; } From a4144073b184ee7f20a93def6bcce40a071bcbff Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Mon, 10 Jul 2023 19:48:57 +0100 Subject: [PATCH 20/99] Rough draft of inference on destinations --- .../core/commandRunner/CommandRunnerImpl.ts | 61 ++++++++++++++----- .../src/core/inferFullTargets.ts | 4 +- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 02faeb89423..e0f9250f543 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -1,6 +1,7 @@ import { CommandComplete, PartialActionDescriptor, + PartialPrimitiveDestinationDescriptor, PartialTargetDescriptor, } from "@cursorless/common"; import { CommandRunner } from "../../CommandRunner"; @@ -10,9 +11,8 @@ import { TargetPipelineRunner } from "../../processTargets"; import { TargetDescriptor } from "../../typings/TargetDescriptor"; import { SelectionWithEditor } from "../../typings/Types"; import { Target } from "../../typings/target.types"; -import { getPartialTargetDescriptorFromDestination } from "../../util/getPartialTargetDescriptors.1"; import { Debug } from "../Debug"; -import { default as inferFullTargetDescriptors } from "../inferFullTargets"; +import { inferFullTargetDescriptor } from "../inferFullTargets"; import { selectionToStoredTarget } from "./selectionToStoredTarget"; export class CommandRunnerImpl implements CommandRunner { @@ -86,26 +86,32 @@ export class CommandRunnerImpl implements CommandRunner { partialActionDescriptor: PartialActionDescriptor, ): Promise { const action = this.actions[partialActionDescriptor.name]; + const inferenceContext = new InferenceContext(); switch (partialActionDescriptor.name) { case "replaceWithTarget": case "moveToTarget": { - const [sourceDescriptor, destinationTargetDescriptor] = - this.inferFullTargetDescriptors([ - partialActionDescriptor.source, - getPartialTargetDescriptorFromDestination( - partialActionDescriptor.destination, - ), - ]); + const sourceTargetDescriptor = inferenceContext.run( + partialActionDescriptor.source, + ); + const source = this.pipelineRunner.run(sourceTargetDescriptor); + let destination: Destination[]; + if (partialActionDescriptor.destination.type === "destination") { + destination = this.getDestinationTargets( + inferenceContext, + partialActionDescriptor.destination, + ); + } else { + destination = + partialActionDescriptor.destination.destinations.flatMap( + (destination) => + this.getDestinationTargets(inferenceContext, destination), + ); + } // Note: we don't need to worry about final stages here because bring and move don't use them! - const source = this.pipelineRunner.run(sourceDescriptor); - const destination = this.pipelineRunner.run( - destinationTargetDescriptor, - ); - // map((target, index) => target.toDestination) - // .getDestination(partialActionDescriptor.destination.insertionMode); + throw Error("stuff"); // return action.run(source, destination); @@ -140,6 +146,21 @@ export class CommandRunnerImpl implements CommandRunner { } } + private getDestinationTargets( + inferenceContext: InferenceContext, + partialDestinationDescriptor: PartialPrimitiveDestinationDescriptor, + ) { + const destinationTargetDescriptor = inferenceContext.run( + partialDestinationDescriptor.target, + ); + + return this.pipelineRunner + .run(destinationTargetDescriptor) + .map((target) => + target.toDestination(partialDestinationDescriptor.insertionMode), + ); + } + private inferFullTargetDescriptors( targets: PartialTargetDescriptor[], ): TargetDescriptor[] { @@ -154,6 +175,16 @@ export class CommandRunnerImpl implements CommandRunner { } } +class InferenceContext { + private previousTargets: PartialTargetDescriptor[] = []; + + run(target: PartialTargetDescriptor) { + const ret = inferFullTargetDescriptor(target, this.previousTargets); + this.previousTargets.push(target); + return ret; + } +} + function constructStoredTarget( targets: Target[] | undefined, selections: SelectionWithEditor[] | undefined, diff --git a/packages/cursorless-engine/src/core/inferFullTargets.ts b/packages/cursorless-engine/src/core/inferFullTargets.ts index c9a99588592..9ebff1980d1 100644 --- a/packages/cursorless-engine/src/core/inferFullTargets.ts +++ b/packages/cursorless-engine/src/core/inferFullTargets.ts @@ -27,11 +27,11 @@ export default function inferFullTargetDescriptors( targets: PartialTargetDescriptor[], ): TargetDescriptor[] { return targets.map((target, index) => - inferTarget(target, targets.slice(0, index)), + inferFullTargetDescriptor(target, targets.slice(0, index)), ); } -function inferTarget( +export function inferFullTargetDescriptor( target: PartialTargetDescriptor, previousTargets: PartialTargetDescriptor[], ): TargetDescriptor { From dbbae33add6405251b94c0802a307e8db2fdde6b Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 10 Jul 2023 21:23:25 +0200 Subject: [PATCH 21/99] bring move swap is working --- .../src/actions/BringMoveSwap.ts | 128 ++++++++++++++---- .../src/actions/actions.types.ts | 22 ++- .../core/commandRunner/CommandRunnerImpl.ts | 93 +++++++------ .../processTargets/targets/DestinationImpl.ts | 8 +- .../src/typings/target.types.ts | 7 +- 5 files changed, 185 insertions(+), 73 deletions(-) diff --git a/packages/cursorless-engine/src/actions/BringMoveSwap.ts b/packages/cursorless-engine/src/actions/BringMoveSwap.ts index 03bba75f3d0..347779395ed 100644 --- a/packages/cursorless-engine/src/actions/BringMoveSwap.ts +++ b/packages/cursorless-engine/src/actions/BringMoveSwap.ts @@ -12,7 +12,7 @@ import { } from "../core/updateSelections/updateSelections"; import { ide } from "../singletons/ide.singleton"; import { EditWithRangeUpdater } from "../typings/Types"; -import { Target } from "../typings/target.types"; +import { Destination, Target } from "../typings/target.types"; import { setSelectionsWithoutFocusingEditor } from "../util/setSelectionsAndFocusEditor"; import { flashTargets, @@ -20,7 +20,7 @@ import { runForEachEditor, } from "../util/targetUtils"; import { unifyRemovalTargets } from "../util/unifyRanges"; -import { Action, ActionReturnValue } from "./actions.types"; +import { ActionReturnValue } from "./actions.types"; type ActionType = "bring" | "move" | "swap"; @@ -38,13 +38,13 @@ interface MarkEntry { target: Target; } -class BringMoveSwap implements Action { +class BringMoveSwap { constructor(private rangeUpdater: RangeUpdater, private type: ActionType) { - this.run = this.run.bind(this); + // this.run = this.run.bind(this); } - private broadcastSource(sources: Target[], destinations: Target[]) { - if (sources.length === 1 && this.type !== "swap") { + protected broadcastSource(sources: Target[], destinations: Destination[]) { + if (sources.length === 1) { // If there is only one source target, expand it to same length as // destination target return Array(destinations.length).fill(sources[0]); @@ -74,7 +74,7 @@ class BringMoveSwap implements Action { }; } - private async decorateTargets(sources: Target[], destinations: Target[]) { + protected async decorateTargets(sources: Target[], destinations: Target[]) { const decorationContext = this.getDecorationContext(); await Promise.all([ flashTargets( @@ -87,13 +87,14 @@ class BringMoveSwap implements Action { ]); } - private getEdits(sources: Target[], destinations: Target[]): ExtendedEdit[] { + protected getEditsBringMove( + sources: Target[], + destinations: Destination[], + ): ExtendedEdit[] { const usedSources: Target[] = []; const results: ExtendedEdit[] = []; const zipSources = - sources.length !== destinations.length && - destinations.length === 1 && - this.type !== "swap"; + sources.length !== destinations.length && destinations.length === 1; sources.forEach((source, i) => { let destination = destinations[i]; @@ -120,7 +121,7 @@ class BringMoveSwap implements Action { results.push({ edit: destination.constructChangeEdit(text), editor: destination.editor, - originalTarget: destination, + originalTarget: destination.target, isSource: false, }); } else { @@ -133,7 +134,7 @@ class BringMoveSwap implements Action { usedSources.push(source); if (this.type !== "move") { results.push({ - edit: source.constructChangeEdit(destination.contentText), + edit: source.constructChangeEdit(destination.target.contentText), editor: source.editor, originalTarget: source, isSource: true, @@ -157,7 +158,7 @@ class BringMoveSwap implements Action { return results; } - private async performEditsAndComputeThatMark( + protected async performEditsAndComputeThatMark( edits: ExtendedEdit[], ): Promise { return flatten( @@ -270,7 +271,7 @@ class BringMoveSwap implements Action { }); } - private async decorateThatMark(thatMark: MarkEntry[]) { + protected async decorateThatMark(thatMark: MarkEntry[]) { const decorationContext = this.getDecorationContext(); const getRange = (target: Target) => thatMark.find((t) => t.target === target)!.selection; @@ -292,7 +293,7 @@ class BringMoveSwap implements Action { ]); } - private calculateMarks(markEntries: MarkEntry[]) { + protected calculateMarks(markEntries: MarkEntry[]) { // Only swap has sources as a "that" mark const thatMark = this.type === "swap" @@ -307,16 +308,25 @@ class BringMoveSwap implements Action { return { thatMark, sourceMark }; } +} - async run([sources, destinations]: [ - Target[], - Target[], - ]): Promise { +export class Bring extends BringMoveSwap { + constructor(rangeUpdater: RangeUpdater) { + super(rangeUpdater, "bring"); + } + + async run( + sources: Target[], + destinations: Destination[], + ): Promise { sources = this.broadcastSource(sources, destinations); - await this.decorateTargets(sources, destinations); + await this.decorateTargets( + sources, + destinations.map((d) => d.target), + ); - const edits = this.getEdits(sources, destinations); + const edits = this.getEditsBringMove(sources, destinations); const markEntries = await this.performEditsAndComputeThatMark(edits); @@ -328,22 +338,84 @@ class BringMoveSwap implements Action { } } -export class Bring extends BringMoveSwap { - constructor(rangeUpdater: RangeUpdater) { - super(rangeUpdater, "bring"); - } -} - export class Move extends BringMoveSwap { constructor(rangeUpdater: RangeUpdater) { super(rangeUpdater, "move"); } + + async run( + sources: Target[], + destinations: Destination[], + ): Promise { + sources = this.broadcastSource(sources, destinations); + + await this.decorateTargets( + sources, + destinations.map((d) => d.target), + ); + + const edits = this.getEditsBringMove(sources, destinations); + + const markEntries = await this.performEditsAndComputeThatMark(edits); + + const { thatMark, sourceMark } = this.calculateMarks(markEntries); + + await this.decorateThatMark(thatMark); + + return { thatSelections: thatMark, sourceSelections: sourceMark }; + } } export class Swap extends BringMoveSwap { constructor(rangeUpdater: RangeUpdater) { super(rangeUpdater, "swap"); } + + async run( + targets1: Target[], + targets2: Target[], + ): Promise { + await this.decorateTargets(targets1, targets2); + + const edits = this.getEditsSwap(targets1, targets2); + + const markEntries = await this.performEditsAndComputeThatMark(edits); + + const { thatMark, sourceMark } = this.calculateMarks(markEntries); + + await this.decorateThatMark(thatMark); + + return { thatSelections: thatMark, sourceSelections: sourceMark }; + } + + private getEditsSwap(targets1: Target[], targets2: Target[]): ExtendedEdit[] { + const results: ExtendedEdit[] = []; + + targets1.forEach((source, i) => { + const destination = targets2[i]; + if (source == null || destination == null) { + throw new Error("Targets must have same number of args"); + } + + // Add destination edit + results.push({ + edit: destination.constructChangeEdit(source.contentText), + editor: destination.editor, + originalTarget: destination, + isSource: false, + }); + + // Add source edit + results.push({ + edit: source.constructChangeEdit(destination.contentText), + editor: source.editor, + originalTarget: source, + isSource: true, + }); + }); + + return results; + } } function getRemovalHighlightRange(target: Target) { diff --git a/packages/cursorless-engine/src/actions/actions.types.ts b/packages/cursorless-engine/src/actions/actions.types.ts index 39efc19fd55..8395ac5dc1a 100644 --- a/packages/cursorless-engine/src/actions/actions.types.ts +++ b/packages/cursorless-engine/src/actions/actions.types.ts @@ -1,7 +1,7 @@ +import type { SimpleActionName } from "@cursorless/common"; import type { ModifierStage } from "../processTargets/PipelineStages.types"; -import type { Target } from "../typings/target.types"; import type { SelectionWithEditor } from "../typings/Types"; -import type { ActionType } from "@cursorless/common"; +import type { Destination, Target } from "../typings/target.types"; /** * To be returned by {@link Action.run} @@ -65,4 +65,20 @@ export interface Action { /** * Keeps a map from action names to objects that implement the given action */ -export type ActionRecord = Record; +export interface ActionRecord extends Record { + replaceWithTarget: { + run( + sources: Target[], + destinations: Destination[], + ): Promise; + }; + moveToTarget: { + run( + sources: Target[], + destinations: Destination[], + ): Promise; + }; + swapTargets: { + run(target1: Target[], target2: Target[]): Promise; + }; +} diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index e0f9250f543..d674faeabd1 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -1,6 +1,7 @@ import { CommandComplete, PartialActionDescriptor, + PartialDestinationDescriptor, PartialPrimitiveDestinationDescriptor, PartialTargetDescriptor, } from "@cursorless/common"; @@ -8,7 +9,6 @@ import { CommandRunner } from "../../CommandRunner"; import { ActionRecord, ActionReturnValue } from "../../actions/actions.types"; import { StoredTargetMap } from "../../index"; import { TargetPipelineRunner } from "../../processTargets"; -import { TargetDescriptor } from "../../typings/TargetDescriptor"; import { SelectionWithEditor } from "../../typings/Types"; import { Target } from "../../typings/target.types"; import { Debug } from "../Debug"; @@ -85,38 +85,34 @@ export class CommandRunnerImpl implements CommandRunner { private runAction( partialActionDescriptor: PartialActionDescriptor, ): Promise { - const action = this.actions[partialActionDescriptor.name]; const inferenceContext = new InferenceContext(); switch (partialActionDescriptor.name) { case "replaceWithTarget": - case "moveToTarget": - { - const sourceTargetDescriptor = inferenceContext.run( - partialActionDescriptor.source, - ); - const source = this.pipelineRunner.run(sourceTargetDescriptor); - let destination: Destination[]; - if (partialActionDescriptor.destination.type === "destination") { - destination = this.getDestinationTargets( - inferenceContext, - partialActionDescriptor.destination, - ); - } else { - destination = - partialActionDescriptor.destination.destinations.flatMap( - (destination) => - this.getDestinationTargets(inferenceContext, destination), - ); - } - - // Note: we don't need to worry about final stages here because bring and move don't use them! - - throw Error("stuff"); - - // return action.run(source, destination); - } - break; + case "moveToTarget": { + const sources = this.getTargets( + inferenceContext, + partialActionDescriptor.source, + ); + const destinations = this.getDestinations( + inferenceContext, + partialActionDescriptor.destination, + ); + return partialActionDescriptor.name === "replaceWithTarget" + ? this.actions.replaceWithTarget.run(sources, destinations) + : this.actions.moveToTarget.run(sources, destinations); + } + case "swapTargets": { + const target1 = this.getTargets( + inferenceContext, + partialActionDescriptor.target1, + ); + const target2 = this.getTargets( + inferenceContext, + partialActionDescriptor.target2, + ); + return this.actions.swapTargets.run(target1, target2); + } default: throw Error("stuff"); // case "wrapWithPairedDelimiter": @@ -143,10 +139,37 @@ export class CommandRunnerImpl implements CommandRunner { // const target = pipelineRunner.run(targetDescriptor); // return action.run(target); + // const action = this.actions[partialActionDescriptor.name]; } } - private getDestinationTargets( + private getTargets( + inferenceContext: InferenceContext, + partialTargetsDescriptor: PartialTargetDescriptor, + ) { + const sourceTargetDescriptor = inferenceContext.run( + partialTargetsDescriptor, + ); + return this.pipelineRunner.run(sourceTargetDescriptor); + } + + private getDestinations( + inferenceContext: InferenceContext, + partialDestinationDescriptor: PartialDestinationDescriptor, + ) { + if (partialDestinationDescriptor.type === "destination") { + return this.getDestinationTargetsFromPrimitive( + inferenceContext, + partialDestinationDescriptor, + ); + } else { + return partialDestinationDescriptor.destinations.flatMap((destination) => + this.getDestinationTargetsFromPrimitive(inferenceContext, destination), + ); + } + } + + private getDestinationTargetsFromPrimitive( inferenceContext: InferenceContext, partialDestinationDescriptor: PartialPrimitiveDestinationDescriptor, ) { @@ -161,17 +184,11 @@ export class CommandRunnerImpl implements CommandRunner { ); } - private inferFullTargetDescriptors( - targets: PartialTargetDescriptor[], - ): TargetDescriptor[] { - const targetDescriptors = inferFullTargetDescriptors(targets); - + private debugTargets(targets: Target[]) { if (this.debug.active) { this.debug.log("Full targets:"); - this.debug.log(JSON.stringify(targetDescriptors, null, 2)); + this.debug.log(JSON.stringify(targets, null, 2)); } - - return targetDescriptors; } } diff --git a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts index 2213cecaa43..d46dbddf1a9 100644 --- a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts +++ b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts @@ -59,14 +59,18 @@ export default class DestinationImpl implements Destination { : this.constructEditWithoutDelimiters(text); } - private get editor(): TextEditor { + get editor(): TextEditor { return this.target.editor; } - private get insertionDelimiter(): string { + get insertionDelimiter(): string { return this.target.insertionDelimiter; } + get isRaw(): boolean { + return this.target.isRaw; + } + private constructEditWithDelimiters(text: string): EditWithRangeUpdater { const range = this.getEditRange(); const editText = this.getEditText(text); diff --git a/packages/cursorless-engine/src/typings/target.types.ts b/packages/cursorless-engine/src/typings/target.types.ts index 1ff0fcb83da..85cf264464e 100644 --- a/packages/cursorless-engine/src/typings/target.types.ts +++ b/packages/cursorless-engine/src/typings/target.types.ts @@ -166,8 +166,11 @@ export interface Target { } export interface Destination { - insertionMode: InsertionMode; - target: Target; + readonly insertionMode: InsertionMode; + readonly editor: TextEditor; + readonly target: Target; + readonly isRaw: boolean; + readonly insertionDelimiter: string; constructChangeEdit(text: string): EditWithRangeUpdater; isEqual(destination: Destination): boolean; } From e6877dbb5e6b9bf44f24ca95d5f8c7d94a507210 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Mon, 10 Jul 2023 22:08:13 +0200 Subject: [PATCH 22/99] update --- .../src/actions/BringMoveSwap.ts | 94 ++++++++----------- 1 file changed, 41 insertions(+), 53 deletions(-) diff --git a/packages/cursorless-engine/src/actions/BringMoveSwap.ts b/packages/cursorless-engine/src/actions/BringMoveSwap.ts index 347779395ed..5ff260bb768 100644 --- a/packages/cursorless-engine/src/actions/BringMoveSwap.ts +++ b/packages/cursorless-engine/src/actions/BringMoveSwap.ts @@ -1,5 +1,6 @@ import { FlashStyle, + Range, RangeExpansionBehavior, Selection, TextEditor, @@ -38,10 +39,14 @@ interface MarkEntry { target: Target; } -class BringMoveSwap { - constructor(private rangeUpdater: RangeUpdater, private type: ActionType) { - // this.run = this.run.bind(this); - } +abstract class BringMoveSwap { + protected abstract decoration: { + sourceStyle: FlashStyle; + destinationStyle: FlashStyle; + getSourceRangeCallback: (target: Target) => Range; + }; + + constructor(private rangeUpdater: RangeUpdater, private type: ActionType) {} protected broadcastSource(sources: Target[], destinations: Destination[]) { if (sources.length === 1) { @@ -52,38 +57,15 @@ class BringMoveSwap { return sources; } - private getDecorationContext() { - let sourceStyle: FlashStyle; - let getSourceRangeCallback; - if (this.type === "bring") { - sourceStyle = FlashStyle.referenced; - getSourceRangeCallback = getContentRange; - } else if (this.type === "move") { - sourceStyle = FlashStyle.pendingDelete; - getSourceRangeCallback = getRemovalHighlightRange; - } - // NB this.type === "swap" - else { - sourceStyle = FlashStyle.pendingModification1; - getSourceRangeCallback = getContentRange; - } - return { - sourceStyle, - destinationStyle: FlashStyle.pendingModification0, - getSourceRangeCallback, - }; - } - protected async decorateTargets(sources: Target[], destinations: Target[]) { - const decorationContext = this.getDecorationContext(); await Promise.all([ flashTargets( ide(), sources, - decorationContext.sourceStyle, - decorationContext.getSourceRangeCallback, + this.decoration.sourceStyle, + this.decoration.getSourceRangeCallback, ), - flashTargets(ide(), destinations, decorationContext.destinationStyle), + flashTargets(ide(), destinations, this.decoration.destinationStyle), ]); } @@ -272,14 +254,13 @@ class BringMoveSwap { } protected async decorateThatMark(thatMark: MarkEntry[]) { - const decorationContext = this.getDecorationContext(); const getRange = (target: Target) => thatMark.find((t) => t.target === target)!.selection; return Promise.all([ flashTargets( ide(), thatMark.filter(({ isSource }) => isSource).map(({ target }) => target), - decorationContext.sourceStyle, + this.decoration.sourceStyle, getRange, ), flashTargets( @@ -287,30 +268,27 @@ class BringMoveSwap { thatMark .filter(({ isSource }) => !isSource) .map(({ target }) => target), - decorationContext.destinationStyle, + this.decoration.destinationStyle, getRange, ), ]); } - protected calculateMarks(markEntries: MarkEntry[]) { - // Only swap has sources as a "that" mark - const thatMark = - this.type === "swap" - ? markEntries - : markEntries.filter(({ isSource }) => !isSource); - - // Only swap doesn't have a source mark - const sourceMark = - this.type === "swap" - ? [] - : markEntries.filter(({ isSource }) => isSource); - - return { thatMark, sourceMark }; + protected calculateMarksBringMove(markEntries: MarkEntry[]) { + return { + thatMark: markEntries.filter(({ isSource }) => !isSource), + sourceMark: markEntries.filter(({ isSource }) => isSource), + }; } } export class Bring extends BringMoveSwap { + decoration = { + sourceStyle: FlashStyle.referenced, + destinationStyle: FlashStyle.pendingModification0, + getSourceRangeCallback: getContentRange, + }; + constructor(rangeUpdater: RangeUpdater) { super(rangeUpdater, "bring"); } @@ -330,7 +308,7 @@ export class Bring extends BringMoveSwap { const markEntries = await this.performEditsAndComputeThatMark(edits); - const { thatMark, sourceMark } = this.calculateMarks(markEntries); + const { thatMark, sourceMark } = this.calculateMarksBringMove(markEntries); await this.decorateThatMark(thatMark); @@ -339,6 +317,12 @@ export class Bring extends BringMoveSwap { } export class Move extends BringMoveSwap { + decoration = { + sourceStyle: FlashStyle.pendingDelete, + destinationStyle: FlashStyle.pendingModification0, + getSourceRangeCallback: getRemovalHighlightRange, + }; + constructor(rangeUpdater: RangeUpdater) { super(rangeUpdater, "move"); } @@ -358,7 +342,7 @@ export class Move extends BringMoveSwap { const markEntries = await this.performEditsAndComputeThatMark(edits); - const { thatMark, sourceMark } = this.calculateMarks(markEntries); + const { thatMark, sourceMark } = this.calculateMarksBringMove(markEntries); await this.decorateThatMark(thatMark); @@ -367,6 +351,12 @@ export class Move extends BringMoveSwap { } export class Swap extends BringMoveSwap { + decoration = { + sourceStyle: FlashStyle.pendingModification1, + destinationStyle: FlashStyle.pendingModification0, + getSourceRangeCallback: getContentRange, + }; + constructor(rangeUpdater: RangeUpdater) { super(rangeUpdater, "swap"); } @@ -381,11 +371,9 @@ export class Swap extends BringMoveSwap { const markEntries = await this.performEditsAndComputeThatMark(edits); - const { thatMark, sourceMark } = this.calculateMarks(markEntries); - - await this.decorateThatMark(thatMark); + await this.decorateThatMark(markEntries); - return { thatSelections: thatMark, sourceSelections: sourceMark }; + return { thatSelections: markEntries }; } private getEditsSwap(targets1: Target[], targets2: Target[]): ExtendedEdit[] { From 04206def90c6c7ba8be2479bdcd41699a79070d3 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 00:19:42 +0200 Subject: [PATCH 23/99] Started migrating actions --- .../src/types/command/ActionDescriptor.ts | 67 ++++++++-- .../src/actions/BringMoveSwap.ts | 15 ++- .../cursorless-engine/src/actions/Call.ts | 12 +- .../src/actions/CallbackAction.ts | 2 +- .../cursorless-engine/src/actions/Clear.ts | 4 +- .../src/actions/CutToClipboard.ts | 6 +- .../cursorless-engine/src/actions/Deselect.ts | 2 +- .../src/actions/EditNew/EditNew.ts | 39 ++++-- .../src/actions/EditNew/EditNew.types.ts | 14 +- .../src/actions/EditNew/runEditTargets.ts | 22 ++-- .../EditNew/runInsertLineAfterTargets.ts | 31 +++-- .../actions/EditNew/runNotebookCellTargets.ts | 22 ++-- .../src/actions/ExecuteCommand.ts | 2 +- .../cursorless-engine/src/actions/Find.ts | 6 +- .../src/actions/FollowLink.ts | 4 +- .../GenerateSnippet/GenerateSnippet.ts | 2 +- .../cursorless-engine/src/actions/GetText.ts | 2 +- .../src/actions/Highlight.ts | 2 +- .../src/actions/InsertCopy.ts | 2 +- .../src/actions/InsertEmptyLines.ts | 2 +- .../src/actions/InsertSnippet.ts | 24 +--- .../src/actions/PasteFromClipboard.ts | 10 +- .../cursorless-engine/src/actions/Remove.ts | 2 +- .../cursorless-engine/src/actions/Replace.ts | 4 +- .../cursorless-engine/src/actions/Rewrap.ts | 6 +- .../cursorless-engine/src/actions/Scroll.ts | 2 +- .../src/actions/SetInstanceReference.ts | 2 +- .../src/actions/SetSelection.ts | 2 +- .../src/actions/SimpleIdeCommandActions.ts | 2 +- .../cursorless-engine/src/actions/Sort.ts | 6 +- .../src/actions/ToggleBreakpoint.ts | 2 +- .../cursorless-engine/src/actions/Wrap.ts | 6 +- .../src/actions/WrapWithSnippet.ts | 21 +-- .../src/actions/actions.types.ts | 56 ++++++-- .../core/commandRunner/CommandRunnerImpl.ts | 123 +++++++++++------- .../upgradeV5ToV6/upgradeV5ToV6.ts | 17 ++- .../src/processTargets/targets/BaseTarget.ts | 18 +-- .../processTargets/targets/DestinationImpl.ts | 9 +- .../targets/NotebookCellTarget.ts | 61 +++++---- .../src/typings/target.types.ts | 7 +- .../src/keyboard/KeyboardCommandsTargeted.ts | 6 +- 41 files changed, 375 insertions(+), 269 deletions(-) diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index b9556f6b010..512cbc5d9db 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -1,18 +1,18 @@ import { PartialDestinationDescriptor, PartialTargetDescriptor, + ScopeType, } from "./PartialTargetDescriptor.types"; const simpleActionNames = [ - "callAsFunction", "clearAndSetSelection", "copyToClipboard", "cutToClipboard", "deselect", - "editNew", "editNewLineAfter", "editNewLineBefore", "executeCommand", + "experimental.setInstanceReference", "extractVariable", "findInWorkspace", "foldRegion", @@ -26,7 +26,6 @@ const simpleActionNames = [ "insertEmptyLineAfter", "insertEmptyLineBefore", "insertEmptyLinesAround", - "insertSnippet", "outdentLine", "randomizeTargets", "remove", @@ -35,8 +34,6 @@ const simpleActionNames = [ "revealDefinition", "revealTypeDefinition", "reverseTargets", - "rewrapWithPairedDelimiter", - "experimental.setInstanceReference", "scrollToBottom", "scrollToCenter", "scrollToTop", @@ -51,15 +48,19 @@ const simpleActionNames = [ "toggleLineBreakpoint", "toggleLineComment", "unfoldRegion", - "wrapWithSnippet", ] as const; const complexActionNames = [ - "replaceWithTarget", + "callAsFunction", + "editNew", + "insertSnippet", "moveToTarget", - "swapTargets", "pasteFromClipboard", + "replaceWithTarget", + "rewrapWithPairedDelimiter", + "swapTargets", "wrapWithPairedDelimiter", + "wrapWithSnippet", ] as const; export const actionNames = [ @@ -87,15 +88,53 @@ export interface PartialSwapActionDescriptor { target2: PartialTargetDescriptor; } +export interface PartialWrapWithPairedDelimiterActionDescriptor { + name: "wrapWithPairedDelimiter" | "rewrapWithPairedDelimiter"; + left: string; + right: string; + target: PartialTargetDescriptor; +} + export interface PartialPasteActionDescriptor { name: "pasteFromClipboard"; destination: PartialDestinationDescriptor; } -export interface PartialWrapWithPairedDelimiterActionDescriptor { - name: "wrapWithPairedDelimiter"; - left: string; - right: string; +interface NamedSnippetArg { + type: "named"; + name: string; + substitutions?: Record; +} +interface CustomSnippetArg { + type: "custom"; + body: string; + scopeType?: ScopeType; + substitutions?: Record; +} +export type InsertSnippetArg = NamedSnippetArg | CustomSnippetArg; + +export interface PartialInsertSnippetActionDescriptor { + name: "insertSnippet"; + snippetDescription: InsertSnippetArg; + target: PartialTargetDescriptor; +} + +interface NamedSnippetArg { + type: "named"; + name: string; + variableName: string; +} +interface CustomSnippetArg { + type: "custom"; + body: string; + variableName?: string; + scopeType?: ScopeType; +} +export type WrapWithSnippetArg = NamedSnippetArg | CustomSnippetArg; + +export interface PartialWrapSnippetActionDescriptor { + name: "wrapWithSnippet"; + snippetDescription: WrapWithSnippetArg; target: PartialTargetDescriptor; } @@ -103,5 +142,7 @@ export type PartialActionDescriptor = | PartialSimpleActionDescriptor | PartialBringMoveActionDescriptor | PartialSwapActionDescriptor + | PartialWrapWithPairedDelimiterActionDescriptor | PartialPasteActionDescriptor - | PartialWrapWithPairedDelimiterActionDescriptor; + | PartialInsertSnippetActionDescriptor + | PartialWrapSnippetActionDescriptor; diff --git a/packages/cursorless-engine/src/actions/BringMoveSwap.ts b/packages/cursorless-engine/src/actions/BringMoveSwap.ts index 5ff260bb768..e4b1cc5d50f 100644 --- a/packages/cursorless-engine/src/actions/BringMoveSwap.ts +++ b/packages/cursorless-engine/src/actions/BringMoveSwap.ts @@ -116,7 +116,9 @@ abstract class BringMoveSwap { usedSources.push(source); if (this.type !== "move") { results.push({ - edit: source.constructChangeEdit(destination.target.contentText), + edit: source + .toDestination("to") + .constructChangeEdit(destination.target.contentText), editor: source.editor, originalTarget: source, isSource: true, @@ -291,6 +293,7 @@ export class Bring extends BringMoveSwap { constructor(rangeUpdater: RangeUpdater) { super(rangeUpdater, "bring"); + this.run = this.run.bind(this); } async run( @@ -325,6 +328,7 @@ export class Move extends BringMoveSwap { constructor(rangeUpdater: RangeUpdater) { super(rangeUpdater, "move"); + this.run = this.run.bind(this); } async run( @@ -359,6 +363,7 @@ export class Swap extends BringMoveSwap { constructor(rangeUpdater: RangeUpdater) { super(rangeUpdater, "swap"); + this.run = this.run.bind(this); } async run( @@ -387,7 +392,9 @@ export class Swap extends BringMoveSwap { // Add destination edit results.push({ - edit: destination.constructChangeEdit(source.contentText), + edit: destination + .toDestination("to") + .constructChangeEdit(source.contentText), editor: destination.editor, originalTarget: destination, isSource: false, @@ -395,7 +402,9 @@ export class Swap extends BringMoveSwap { // Add source edit results.push({ - edit: source.constructChangeEdit(destination.contentText), + edit: source + .toDestination("to") + .constructChangeEdit(destination.contentText), editor: source.editor, originalTarget: source, isSource: true, diff --git a/packages/cursorless-engine/src/actions/Call.ts b/packages/cursorless-engine/src/actions/Call.ts index e75788fe2c9..7b9e44037be 100644 --- a/packages/cursorless-engine/src/actions/Call.ts +++ b/packages/cursorless-engine/src/actions/Call.ts @@ -8,20 +8,20 @@ export default class Call implements Action { this.run = this.run.bind(this); } - async run([sources, destinations]: [ - Target[], - Target[], - ]): Promise { + async run( + sources: Target[], + destinations: Target[], + ): Promise { ensureSingleTarget(sources); - const { returnValue: texts } = await this.actions.getText.run([sources], { + const { returnValue: texts } = await this.actions.getText.run(sources, { showDecorations: false, }); // NB: We unwrap and then rewrap the return value here so that we don't include the source mark const { thatSelections: thatMark } = await this.actions.wrapWithPairedDelimiter.run( - [destinations], + destinations, texts[0] + "(", ")", ); diff --git a/packages/cursorless-engine/src/actions/CallbackAction.ts b/packages/cursorless-engine/src/actions/CallbackAction.ts index ad280629d15..40a2109626c 100644 --- a/packages/cursorless-engine/src/actions/CallbackAction.ts +++ b/packages/cursorless-engine/src/actions/CallbackAction.ts @@ -38,7 +38,7 @@ export class CallbackAction implements Action { } async run( - [targets]: [Target[]], + targets: Target[], options: CallbackOptions, ): Promise { if (options.showDecorations) { diff --git a/packages/cursorless-engine/src/actions/Clear.ts b/packages/cursorless-engine/src/actions/Clear.ts index 9d6ca5879c9..7739923a2a7 100644 --- a/packages/cursorless-engine/src/actions/Clear.ts +++ b/packages/cursorless-engine/src/actions/Clear.ts @@ -11,7 +11,7 @@ export default class Clear implements Action { this.run = this.run.bind(this); } - async run([targets]: [Target[]]): Promise { + async run(targets: Target[]): Promise { const editor = ensureSingleEditor(targets); // Convert to plain targets so that the remove action just removes the // content range instead of the removal range @@ -24,7 +24,7 @@ export default class Clear implements Action { }), ); - const { thatTargets } = await this.actions.remove.run([plainTargets]); + const { thatTargets } = await this.actions.remove.run(plainTargets); if (thatTargets != null) { await setSelectionsAndFocusEditor( diff --git a/packages/cursorless-engine/src/actions/CutToClipboard.ts b/packages/cursorless-engine/src/actions/CutToClipboard.ts index bde872b38ea..082bf7cf3af 100644 --- a/packages/cursorless-engine/src/actions/CutToClipboard.ts +++ b/packages/cursorless-engine/src/actions/CutToClipboard.ts @@ -15,7 +15,7 @@ export class CutToClipboard implements Action { this.run = this.run.bind(this); } - async run([targets]: [Target[]]): Promise { + async run(targets: Target[]): Promise { await ide().flashRanges( targets.flatMap((target) => { const { editor, contentRange } = target; @@ -55,9 +55,9 @@ export class CutToClipboard implements Action { const options = { showDecorations: false }; - await this.actions.copyToClipboard.run([targets], options); + await this.actions.copyToClipboard.run(targets, options); - const { thatTargets } = await this.actions.remove.run([targets], options); + const { thatTargets } = await this.actions.remove.run(targets, options); return { thatTargets }; } diff --git a/packages/cursorless-engine/src/actions/Deselect.ts b/packages/cursorless-engine/src/actions/Deselect.ts index 9595712a241..fc28082dba8 100644 --- a/packages/cursorless-engine/src/actions/Deselect.ts +++ b/packages/cursorless-engine/src/actions/Deselect.ts @@ -9,7 +9,7 @@ export default class Deselect implements Action { this.run = this.run.bind(this); } - async run([targets]: [Target[]]): Promise { + async run(targets: Target[]): Promise { await runOnTargetsForEachEditor(targets, async (editor, targets) => { // Remove selections with a non-empty intersection const newSelections = editor.selections.filter( diff --git a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts index 53f92e17bc6..bc6b5229b35 100644 --- a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts +++ b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts @@ -26,16 +26,24 @@ export class EditNew implements Action { this.run = this.run.bind(this); } - async run([targets]: [Target[]]): Promise { - if (targets.some((target) => target.isNotebookCell)) { + async run(targets: Target[]): Promise { + return this.runDestinations( + targets.map((target) => this.toDestination(target)), + ); + } + + async runDestinations( + destinations: Destination[], + ): Promise { + if (destinations.some(({ target }) => target.isNotebookCell)) { // It is not possible to "pour" a notebook cell and something else, // because each notebook cell is its own editor, and you can't have // cursors in multiple editors. - return runEditNewNotebookCellTargets(this.actions, targets); + return runEditNewNotebookCellTargets(this.actions, destinations); } const editableEditor = ide().getEditableTextEditor( - ensureSingleEditor(targets), + ensureSingleEditor(destinations.map((d) => d.target)), ); /** @@ -43,9 +51,11 @@ export class EditNew implements Action { * perform the necessary commands and edits. */ let state: State = { - targets, - thatRanges: targets.map(({ thatTarget }) => thatTarget.contentRange), - cursorRanges: new Array(targets.length).fill(undefined) as undefined[], + destinations, + thatRanges: destinations.map(({ target }) => target.contentRange), + cursorRanges: new Array(destinations.length).fill( + undefined, + ) as undefined[], }; state = await runInsertLineAfterTargets( @@ -55,29 +65,32 @@ export class EditNew implements Action { ); state = await runEditTargets(this.rangeUpdater, editableEditor, state); - const newSelections = state.targets.map((target, index) => - state.cursorRanges[index]!.toSelection(target.isReversed), + const newSelections = state.destinations.map((destination, index) => + state.cursorRanges[index]!.toSelection(destination.target.isReversed), ); await setSelectionsAndFocusEditor(editableEditor, newSelections); return { - thatSelections: createThatMark(state.targets, state.thatRanges), + thatSelections: createThatMark( + state.destinations.map((d) => d.target), + state.thatRanges, + ), }; } - protected getDestination(target: Target): Destination { + protected toDestination(target: Target): Destination { return target.toDestination("to"); } } export class EditNewBefore extends EditNew { - protected getDestination(target: Target): Destination { + protected toDestination(target: Target): Destination { return target.toDestination("before"); } } export class EditNewAfter extends EditNew { - protected getDestination(target: Target): Destination { + protected toDestination(target: Target): Destination { return target.toDestination("after"); } } diff --git a/packages/cursorless-engine/src/actions/EditNew/EditNew.types.ts b/packages/cursorless-engine/src/actions/EditNew/EditNew.types.ts index 80abf0c6ffa..fc29e2a8d46 100644 --- a/packages/cursorless-engine/src/actions/EditNew/EditNew.types.ts +++ b/packages/cursorless-engine/src/actions/EditNew/EditNew.types.ts @@ -1,12 +1,12 @@ import type { Range } from "@cursorless/common"; -import type { Target } from "../../typings/target.types"; +import type { Destination } from "../../typings/target.types"; /** - * Internal type to be used for storing a reference to a target that will use an - * edit action to insert a new target + * Internal type to be used for storing a reference to a destination that will use an + * edit action to insert a new destination */ -export interface EditTarget { - target: Target; +export interface EditDestination { + destination: Destination; /** * The original index of this target in the original list of targets passed @@ -25,9 +25,9 @@ export interface EditTarget { */ export interface State { /** - * This field stores the original targets. + * This field stores the original destinations. */ - targets: Target[]; + destinations: Destination[]; /** * We use this field to track the desired `thatMark` at the end, updating it diff --git a/packages/cursorless-engine/src/actions/EditNew/runEditTargets.ts b/packages/cursorless-engine/src/actions/EditNew/runEditTargets.ts index eca669d4060..6eb7756924c 100644 --- a/packages/cursorless-engine/src/actions/EditNew/runEditTargets.ts +++ b/packages/cursorless-engine/src/actions/EditNew/runEditTargets.ts @@ -6,7 +6,7 @@ import { import { zip } from "lodash"; import { RangeUpdater } from "../../core/updateSelections/RangeUpdater"; import { performEditsAndUpdateSelectionsWithBehavior } from "../../core/updateSelections/updateSelections"; -import { EditTarget, State } from "./EditNew.types"; +import { EditDestination, State } from "./EditNew.types"; /** * Handle targets that will use an edit action to insert a new target, and @@ -26,23 +26,25 @@ export async function runEditTargets( editor: EditableTextEditor, state: State, ): Promise { - const targets: EditTarget[] = state.targets - .map((target, index) => { - const actionType = target.getEditNewActionType(); + const destinations: EditDestination[] = state.destinations + .map((destination, index) => { + const actionType = destination.getEditNewActionType(); if (actionType === "edit") { return { - target, + destination, index, }; } }) - .filter((target): target is EditTarget => !!target); + .filter((destination): destination is EditDestination => !!destination); - if (targets.length === 0) { + if (destinations.length === 0) { return state; } - const edits = targets.map((target) => target.target.constructChangeEdit("")); + const edits = destinations.map((destination) => + destination.destination.constructChangeEdit(""), + ); const thatSelections = { selections: state.thatRanges.map((r) => r.toSelection(false)), @@ -86,14 +88,14 @@ export async function runEditTargets( }); // Add cursor positions for our edit targets. - targets.forEach((delimiterTarget, index) => { + destinations.forEach((delimiterTarget, index) => { const edit = edits[index]; const range = edit.updateRange(updatedEditSelections[index]); updatedCursorRanges[delimiterTarget.index] = range; }); return { - targets: state.targets, + destinations: state.destinations, thatRanges: updatedThatSelections, cursorRanges: updatedCursorRanges, }; diff --git a/packages/cursorless-engine/src/actions/EditNew/runInsertLineAfterTargets.ts b/packages/cursorless-engine/src/actions/EditNew/runInsertLineAfterTargets.ts index 0b4c88f2021..4659896e6b6 100644 --- a/packages/cursorless-engine/src/actions/EditNew/runInsertLineAfterTargets.ts +++ b/packages/cursorless-engine/src/actions/EditNew/runInsertLineAfterTargets.ts @@ -1,7 +1,7 @@ import { EditableTextEditor } from "@cursorless/common"; import { RangeUpdater } from "../../core/updateSelections/RangeUpdater"; import { callFunctionAndUpdateRanges } from "../../core/updateSelections/updateSelections"; -import { EditTarget, State } from "./EditNew.types"; +import { EditDestination, State } from "./EditNew.types"; /** * Handle targets that will use a VSCode command to insert a new target, eg @@ -19,43 +19,50 @@ export async function runInsertLineAfterTargets( editor: EditableTextEditor, state: State, ): Promise { - const targets: EditTarget[] = state.targets - .map((target, index) => { - const actionType = target.getEditNewActionType(); + const destinations: EditDestination[] = state.destinations + .map((destination, index) => { + const actionType = destination.getEditNewActionType(); if (actionType === "insertLineAfter") { return { - target, + destination, index, }; } }) - .filter((target): target is EditTarget => !!target); + .filter((destination): destination is EditDestination => !!destination); - if (targets.length === 0) { + if (destinations.length === 0) { return state; } - const contentRanges = targets.map(({ target }) => target.contentRange); + const contentRanges = destinations.map( + ({ destination }) => destination.contentRange, + ); const [updatedTargetRanges, updatedThatRanges] = await callFunctionAndUpdateRanges( rangeUpdater, () => editor.insertLineAfter(contentRanges), editor.document, - [state.targets.map(({ contentRange }) => contentRange), state.thatRanges], + [ + state.destinations.map(({ contentRange }) => contentRange), + state.thatRanges, + ], ); // For each of the given command targets, the cursor will go where it ended // up after running the command. We add it to the state so that any // potential edit targets can update them after we return from this function. const cursorRanges = [...state.cursorRanges]; - targets.forEach((commandTarget, index) => { + destinations.forEach((commandTarget, index) => { cursorRanges[commandTarget.index] = editor.selections[index]; }); return { - targets: state.targets.map((target, index) => - target.withContentRange(updatedTargetRanges[index]), + destinations: state.destinations.map((destination, index) => + destination.target + .withContentRange(updatedTargetRanges[index]) + .toDestination(destination.insertionMode), ), thatRanges: updatedThatRanges, cursorRanges, diff --git a/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts b/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts index 129c87f06df..af9c41784f8 100644 --- a/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts +++ b/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts @@ -1,22 +1,28 @@ import { Selection } from "@cursorless/common"; -import { NotebookCellPositionTarget } from "../../processTargets/targets"; import { ide } from "../../singletons/ide.singleton"; -import { Target } from "../../typings/target.types"; +import { Destination } from "../../typings/target.types"; import { createThatMark, ensureSingleTarget } from "../../util/targetUtils"; import { Actions } from "../Actions"; import { ActionReturnValue } from "../actions.types"; export async function runEditNewNotebookCellTargets( actions: Actions, - targets: Target[], + destinations: Destination[], ): Promise { // Can only run on one target because otherwise we'd end up with cursors in // multiple cells, which is unsupported in VSCode - const target = ensureSingleTarget(targets) as NotebookCellPositionTarget; - const editor = ide().getEditableTextEditor(target.editor); - const isAbove = target.insertionMode === "before"; + ensureSingleTarget(destinations.map((d) => d.target)); + const destination = destinations[0]; + const editor = ide().getEditableTextEditor(destination.editor); + const isAbove = destination.insertionMode === "before"; - await actions.setSelection.run([targets]); + if (destination.insertionMode === "to") { + throw Error( + `Unsupported insertion mode '${destination.insertionMode}' for notebookcapell`, + ); + } + + await actions.setSelection.run([destination.target]); let modifyThatMark = (selection: Selection) => selection; if (isAbove) { @@ -25,7 +31,7 @@ export async function runEditNewNotebookCellTargets( await editor.editNewNotebookCellBelow(); } - const thatMark = createThatMark([target.thatTarget]); + const thatMark = createThatMark([destination.target]); // Apply horrible hack to work around the fact that in vscode the promise // resolves before the edits have actually been performed. diff --git a/packages/cursorless-engine/src/actions/ExecuteCommand.ts b/packages/cursorless-engine/src/actions/ExecuteCommand.ts index 5ea9743e08c..1cbd236acef 100644 --- a/packages/cursorless-engine/src/actions/ExecuteCommand.ts +++ b/packages/cursorless-engine/src/actions/ExecuteCommand.ts @@ -27,7 +27,7 @@ export default class ExecuteCommand implements Action { } async run( - targets: [Target[]], + targets: Target[], commandId: string, { commandArgs, diff --git a/packages/cursorless-engine/src/actions/Find.ts b/packages/cursorless-engine/src/actions/Find.ts index c3318450e72..f5bb8eec50e 100644 --- a/packages/cursorless-engine/src/actions/Find.ts +++ b/packages/cursorless-engine/src/actions/Find.ts @@ -10,12 +10,12 @@ export class FindInWorkspace implements Action { this.run = this.run.bind(this); } - async run([targets]: [Target[]]): Promise { + async run(targets: Target[]): Promise { ensureSingleTarget(targets); - const { returnValue, thatTargets } = await this.actions.getText.run([ + const { returnValue, thatTargets } = await this.actions.getText.run( targets, - ]); + ); const [text] = returnValue as [string]; let query: string; diff --git a/packages/cursorless-engine/src/actions/FollowLink.ts b/packages/cursorless-engine/src/actions/FollowLink.ts index 54575b4b4d8..e54423fd5e7 100644 --- a/packages/cursorless-engine/src/actions/FollowLink.ts +++ b/packages/cursorless-engine/src/actions/FollowLink.ts @@ -14,7 +14,7 @@ export default class FollowLink implements Action { this.run = this.run.bind(this); } - async run([targets]: [Target[]]): Promise { + async run(targets: Target[]): Promise { const target = ensureSingleTarget(targets); await flashTargets(ide(), targets, FlashStyle.referenced); @@ -25,7 +25,7 @@ export default class FollowLink implements Action { if (!openedLink) { await this.actions.executeCommand.run( - [targets], + targets, "editor.action.revealDefinition", { restoreSelection: false }, ); diff --git a/packages/cursorless-engine/src/actions/GenerateSnippet/GenerateSnippet.ts b/packages/cursorless-engine/src/actions/GenerateSnippet/GenerateSnippet.ts index 899ca5e7257..5474d1a5eaf 100644 --- a/packages/cursorless-engine/src/actions/GenerateSnippet/GenerateSnippet.ts +++ b/packages/cursorless-engine/src/actions/GenerateSnippet/GenerateSnippet.ts @@ -51,7 +51,7 @@ export default class GenerateSnippet implements Action { } async run( - [targets]: [Target[]], + targets: Target[], snippetName?: string, ): Promise { const target = ensureSingleTarget(targets); diff --git a/packages/cursorless-engine/src/actions/GetText.ts b/packages/cursorless-engine/src/actions/GetText.ts index 2d5121d2c31..08e8efaa4fe 100644 --- a/packages/cursorless-engine/src/actions/GetText.ts +++ b/packages/cursorless-engine/src/actions/GetText.ts @@ -10,7 +10,7 @@ export default class GetText implements Action { } async run( - [targets]: [Target[]], + targets: Target[], { showDecorations = true, ensureSingleTarget: doEnsureSingleTarget = false, diff --git a/packages/cursorless-engine/src/actions/Highlight.ts b/packages/cursorless-engine/src/actions/Highlight.ts index bc59a7df587..249874eef85 100644 --- a/packages/cursorless-engine/src/actions/Highlight.ts +++ b/packages/cursorless-engine/src/actions/Highlight.ts @@ -13,7 +13,7 @@ export default class Highlight implements Action { } async run( - [targets]: [Target[]], + targets: Target[], highlightId?: HighlightId, ): Promise { if (ide().capabilities.commands["highlight"] == null) { diff --git a/packages/cursorless-engine/src/actions/InsertCopy.ts b/packages/cursorless-engine/src/actions/InsertCopy.ts index 93827e8d0b4..4576c198ebb 100644 --- a/packages/cursorless-engine/src/actions/InsertCopy.ts +++ b/packages/cursorless-engine/src/actions/InsertCopy.ts @@ -30,7 +30,7 @@ class InsertCopy implements Action { this.runForEditor = this.runForEditor.bind(this); } - async run([targets]: [Target[]]): Promise { + async run(targets: Target[]): Promise { const results = flatten( await runOnTargetsForEachEditor(targets, this.runForEditor), ); diff --git a/packages/cursorless-engine/src/actions/InsertEmptyLines.ts b/packages/cursorless-engine/src/actions/InsertEmptyLines.ts index e324917148b..987ae55800d 100644 --- a/packages/cursorless-engine/src/actions/InsertEmptyLines.ts +++ b/packages/cursorless-engine/src/actions/InsertEmptyLines.ts @@ -41,7 +41,7 @@ class InsertEmptyLines implements Action { })); } - async run([targets]: [Target[]]): Promise { + async run(targets: Target[]): Promise { const results = flatten( await runOnTargetsForEachEditor(targets, async (editor, targets) => { const ranges = this.getRanges(targets); diff --git a/packages/cursorless-engine/src/actions/InsertSnippet.ts b/packages/cursorless-engine/src/actions/InsertSnippet.ts index 948a0a85062..5677adcd0ea 100644 --- a/packages/cursorless-engine/src/actions/InsertSnippet.ts +++ b/packages/cursorless-engine/src/actions/InsertSnippet.ts @@ -1,4 +1,5 @@ import { + InsertSnippetArg, RangeExpansionBehavior, ScopeType, Snippet, @@ -13,6 +14,7 @@ import { } from "../core/updateSelections/updateSelections"; import { ModifierStageFactory } from "../processTargets/ModifierStageFactory"; import { ModifyIfUntypedExplicitStage } from "../processTargets/modifiers/ConditionalModifierStages"; +import { UntypedTarget } from "../processTargets/targets"; import { ide } from "../singletons/ide.singleton"; import { findMatchingSnippetDefinitionStrict, @@ -22,23 +24,9 @@ import { SnippetParser } from "../snippets/vendor/vscodeSnippet/snippetParser"; import { Target } from "../typings/target.types"; import { ensureSingleEditor } from "../util/targetUtils"; import { Actions } from "./Actions"; -import { Action, ActionReturnValue } from "./actions.types"; -import { UntypedTarget } from "../processTargets/targets"; - -interface NamedSnippetArg { - type: "named"; - name: string; - substitutions?: Record; -} -interface CustomSnippetArg { - type: "custom"; - body: string; - scopeType?: ScopeType; - substitutions?: Record; -} -type InsertSnippetArg = NamedSnippetArg | CustomSnippetArg; +import { ActionReturnValue } from "./actions.types"; -export default class InsertSnippet implements Action { +export default class InsertSnippet { private snippetParser = new SnippetParser(); constructor( @@ -121,12 +109,12 @@ export default class InsertSnippet implements Action { } async run( - [targets]: [Target[]], + targets: Target[], snippetDescription: InsertSnippetArg, ): Promise { const editor = ide().getEditableTextEditor(ensureSingleEditor(targets)); - await this.actions.editNew.run([targets]); + await this.actions.editNew.run(targets); const targetSelectionInfos = editor.selections.map((selection) => getSelectionInfo( diff --git a/packages/cursorless-engine/src/actions/PasteFromClipboard.ts b/packages/cursorless-engine/src/actions/PasteFromClipboard.ts index ec8f7e83802..ec3cdce8006 100644 --- a/packages/cursorless-engine/src/actions/PasteFromClipboard.ts +++ b/packages/cursorless-engine/src/actions/PasteFromClipboard.ts @@ -9,7 +9,7 @@ import { callFunctionAndUpdateSelectionsWithBehavior, } from "../core/updateSelections/updateSelections"; import { ide } from "../singletons/ide.singleton"; -import { Target } from "../typings/target.types"; +import { Destination } from "../typings/target.types"; import { setSelectionsWithoutFocusingEditor } from "../util/setSelectionsAndFocusEditor"; import { ensureSingleEditor } from "../util/targetUtils"; import { Actions } from "./Actions"; @@ -18,8 +18,10 @@ import { ActionReturnValue } from "./actions.types"; export class PasteFromClipboard { constructor(private rangeUpdater: RangeUpdater, private actions: Actions) {} - async run([targets]: [Target[]]): Promise { - const editor = ide().getEditableTextEditor(ensureSingleEditor(targets)); + async run(destinations: Destination[]): Promise { + const editor = ide().getEditableTextEditor( + ensureSingleEditor(destinations.map((d) => d.target)), + ); const originalEditor = ide().activeEditableTextEditor; // First call editNew in order to insert delimiters if necessary and leave @@ -28,7 +30,7 @@ export class PasteFromClipboard { const [originalCursorSelections] = await callFunctionAndUpdateSelections( this.rangeUpdater, async () => { - await this.actions.editNew.run([targets]); + await this.actions.editNew.runDestinations(destinations); }, editor.document, [editor.selections], diff --git a/packages/cursorless-engine/src/actions/Remove.ts b/packages/cursorless-engine/src/actions/Remove.ts index 6650b74bb56..66ea2ec4602 100644 --- a/packages/cursorless-engine/src/actions/Remove.ts +++ b/packages/cursorless-engine/src/actions/Remove.ts @@ -15,7 +15,7 @@ export default class Delete implements Action { } async run( - [targets]: [Target[]], + targets: Target[], { showDecorations = true } = {}, ): Promise { // Unify overlapping targets because of overlapping leading and trailing delimiters. diff --git a/packages/cursorless-engine/src/actions/Replace.ts b/packages/cursorless-engine/src/actions/Replace.ts index 3678807f83e..c2497e9e9c7 100644 --- a/packages/cursorless-engine/src/actions/Replace.ts +++ b/packages/cursorless-engine/src/actions/Replace.ts @@ -33,7 +33,7 @@ export default class Replace implements Action { } async run( - [targets]: [Target[]], + targets: Target[], replaceWith: string[] | RangeGenerator, ): Promise { await flashTargets(ide(), targets, FlashStyle.pendingModification0); @@ -45,7 +45,7 @@ export default class Replace implements Action { } const edits = zip(targets, texts).map(([target, text]) => ({ - edit: target!.constructChangeEdit(text!), + edit: target!.toDestination("to").constructChangeEdit(text!), editor: target!.editor, })); diff --git a/packages/cursorless-engine/src/actions/Rewrap.ts b/packages/cursorless-engine/src/actions/Rewrap.ts index 54431e1455c..878c7f26718 100644 --- a/packages/cursorless-engine/src/actions/Rewrap.ts +++ b/packages/cursorless-engine/src/actions/Rewrap.ts @@ -10,9 +10,9 @@ import { flashTargets, runOnTargetsForEachEditor, } from "../util/targetUtils"; -import { Action, ActionReturnValue } from "./actions.types"; +import { ActionReturnValue } from "./actions.types"; -export default class Rewrap implements Action { +export default class Rewrap { getFinalStages = () => [ this.modifierStageFactory.create( containingSurroundingPairIfUntypedModifier, @@ -27,7 +27,7 @@ export default class Rewrap implements Action { } async run( - [targets]: [Target[]], + targets: Target[], left: string, right: string, ): Promise { diff --git a/packages/cursorless-engine/src/actions/Scroll.ts b/packages/cursorless-engine/src/actions/Scroll.ts index 93d20c85e70..e005f0af1fa 100644 --- a/packages/cursorless-engine/src/actions/Scroll.ts +++ b/packages/cursorless-engine/src/actions/Scroll.ts @@ -13,7 +13,7 @@ class Scroll implements Action { this.run = this.run.bind(this); } - async run([targets]: [Target[]]): Promise { + async run(targets: Target[]): Promise { const selectionGroups = groupBy(targets, (t: Target) => t.editor); const lines = Array.from(selectionGroups, ([editor, targets]) => { diff --git a/packages/cursorless-engine/src/actions/SetInstanceReference.ts b/packages/cursorless-engine/src/actions/SetInstanceReference.ts index a9afae0f9a7..e146215c4bf 100644 --- a/packages/cursorless-engine/src/actions/SetInstanceReference.ts +++ b/packages/cursorless-engine/src/actions/SetInstanceReference.ts @@ -6,7 +6,7 @@ export class SetInstanceReference implements Action { this.run = this.run.bind(this); } - async run([targets]: [Target[]]): Promise { + async run(targets: Target[]): Promise { return { thatTargets: targets, instanceReferenceTargets: targets, diff --git a/packages/cursorless-engine/src/actions/SetSelection.ts b/packages/cursorless-engine/src/actions/SetSelection.ts index cd55754e44c..e359264982a 100644 --- a/packages/cursorless-engine/src/actions/SetSelection.ts +++ b/packages/cursorless-engine/src/actions/SetSelection.ts @@ -14,7 +14,7 @@ export class SetSelection implements Action { return target.contentSelection; } - async run([targets]: [Target[]]): Promise { + async run(targets: Target[]): Promise { const editor = ensureSingleEditor(targets); const selections = targets.map(this.getSelection); diff --git a/packages/cursorless-engine/src/actions/SimpleIdeCommandActions.ts b/packages/cursorless-engine/src/actions/SimpleIdeCommandActions.ts index 08bb56a6b12..ea9a8a494f2 100644 --- a/packages/cursorless-engine/src/actions/SimpleIdeCommandActions.ts +++ b/packages/cursorless-engine/src/actions/SimpleIdeCommandActions.ts @@ -31,7 +31,7 @@ abstract class SimpleIdeCommandAction { } async run( - targets: [Target[]], + targets: Target[], { showDecorations }: Options = {}, ): Promise { const capabilities = ide().capabilities.commands[this.command]; diff --git a/packages/cursorless-engine/src/actions/Sort.ts b/packages/cursorless-engine/src/actions/Sort.ts index b583d04a150..635fd57ff72 100644 --- a/packages/cursorless-engine/src/actions/Sort.ts +++ b/packages/cursorless-engine/src/actions/Sort.ts @@ -10,14 +10,14 @@ abstract class SortBase implements Action { protected abstract sortTexts(texts: string[]): string[]; - async run([targets]: [Target[]]): Promise { + async run(targets: Target[]): Promise { // First sort target by document order const sortedTargets = targets .slice() .sort((a, b) => a.contentRange.start.compareTo(b.contentRange.start)); const { returnValue: unsortedTexts } = await this.actions.getText.run( - [sortedTargets], + sortedTargets, { showDecorations: false, }, @@ -25,7 +25,7 @@ abstract class SortBase implements Action { const sortedTexts = this.sortTexts(unsortedTexts); - return this.actions.replace.run([sortedTargets], sortedTexts); + return this.actions.replace.run(sortedTargets, sortedTexts); } } diff --git a/packages/cursorless-engine/src/actions/ToggleBreakpoint.ts b/packages/cursorless-engine/src/actions/ToggleBreakpoint.ts index 3b7d4b88cb9..07258b8edae 100644 --- a/packages/cursorless-engine/src/actions/ToggleBreakpoint.ts +++ b/packages/cursorless-engine/src/actions/ToggleBreakpoint.ts @@ -15,7 +15,7 @@ export default class ToggleBreakpoint implements Action { this.run = this.run.bind(this); } - async run([targets]: [Target[], Target[]]): Promise { + async run(targets: Target[]): Promise { const thatTargets = targets.map(({ thatTarget }) => thatTarget); await flashTargets(ide(), thatTargets, FlashStyle.referenced); diff --git a/packages/cursorless-engine/src/actions/Wrap.ts b/packages/cursorless-engine/src/actions/Wrap.ts index d2b2c0997e4..53f320b436b 100644 --- a/packages/cursorless-engine/src/actions/Wrap.ts +++ b/packages/cursorless-engine/src/actions/Wrap.ts @@ -15,15 +15,15 @@ import { Target } from "../typings/target.types"; import { FullSelectionInfo } from "../typings/updateSelections"; import { setSelectionsWithoutFocusingEditor } from "../util/setSelectionsAndFocusEditor"; import { runOnTargetsForEachEditor } from "../util/targetUtils"; -import { Action, ActionReturnValue } from "./actions.types"; +import { ActionReturnValue } from "./actions.types"; -export default class Wrap implements Action { +export default class Wrap { constructor(private rangeUpdater: RangeUpdater) { this.run = this.run.bind(this); } async run( - [targets]: [Target[]], + targets: Target[], left: string, right: string, ): Promise { diff --git a/packages/cursorless-engine/src/actions/WrapWithSnippet.ts b/packages/cursorless-engine/src/actions/WrapWithSnippet.ts index 2bec5d6af28..56ca6942aec 100644 --- a/packages/cursorless-engine/src/actions/WrapWithSnippet.ts +++ b/packages/cursorless-engine/src/actions/WrapWithSnippet.ts @@ -1,4 +1,4 @@ -import { FlashStyle, ScopeType } from "@cursorless/common"; +import { FlashStyle, ScopeType, WrapWithSnippetArg } from "@cursorless/common"; import { Snippets } from "../core/Snippets"; import { RangeUpdater } from "../core/updateSelections/RangeUpdater"; import { callFunctionAndUpdateSelections } from "../core/updateSelections/updateSelections"; @@ -12,22 +12,9 @@ import { import { SnippetParser } from "../snippets/vendor/vscodeSnippet/snippetParser"; import { Target } from "../typings/target.types"; import { ensureSingleEditor, flashTargets } from "../util/targetUtils"; -import { Action, ActionReturnValue } from "./actions.types"; +import { ActionReturnValue } from "./actions.types"; -interface NamedSnippetArg { - type: "named"; - name: string; - variableName: string; -} -interface CustomSnippetArg { - type: "custom"; - body: string; - variableName?: string; - scopeType?: ScopeType; -} -type WrapWithSnippetArg = NamedSnippetArg | CustomSnippetArg; - -export default class WrapWithSnippet implements Action { +export default class WrapWithSnippet { private snippetParser = new SnippetParser(); constructor( @@ -98,7 +85,7 @@ export default class WrapWithSnippet implements Action { } async run( - [targets]: [Target[]], + targets: Target[], snippetDescription: WrapWithSnippetArg, ): Promise { const editor = ide().getEditableTextEditor(ensureSingleEditor(targets)); diff --git a/packages/cursorless-engine/src/actions/actions.types.ts b/packages/cursorless-engine/src/actions/actions.types.ts index 8395ac5dc1a..0b970cde3cf 100644 --- a/packages/cursorless-engine/src/actions/actions.types.ts +++ b/packages/cursorless-engine/src/actions/actions.types.ts @@ -1,4 +1,8 @@ -import type { SimpleActionName } from "@cursorless/common"; +import type { + InsertSnippetArg, + SimpleActionName, + WrapWithSnippetArg, +} from "@cursorless/common"; import type { ModifierStage } from "../processTargets/PipelineStages.types"; import type { SelectionWithEditor } from "../typings/Types"; import type { Destination, Target } from "../typings/target.types"; @@ -47,25 +51,22 @@ export interface ActionReturnValue { } export interface Action { - run(targets: Target[][], ...args: any[]): Promise; - - /** - * Used to define stages that should be run before the final positional stage, if there is one - * @param args Extra args to command - */ - getPrePositionStages?(...args: any[]): ModifierStage[]; + run(targets: Target[], ...args: any[]): Promise; /** * Used to define final stages that should be run at the end of the pipeline before the action * @param args Extra args to command */ - getFinalStages?(...args: any[]): ModifierStage[]; + getFinalStages?(): ModifierStage[]; } /** * Keeps a map from action names to objects that implement the given action */ export interface ActionRecord extends Record { + callAsFunction: { + run(sources: Target[], destinations: Target[]): Promise; + }; replaceWithTarget: { run( sources: Target[], @@ -79,6 +80,41 @@ export interface ActionRecord extends Record { ): Promise; }; swapTargets: { - run(target1: Target[], target2: Target[]): Promise; + run(targets1: Target[], targets2: Target[]): Promise; + }; + wrapWithPairedDelimiter: { + run( + targets: Target[], + left: string, + right: string, + ): Promise; + }; + rewrapWithPairedDelimiter: { + run( + targets: Target[], + left: string, + right: string, + ): Promise; + }; + pasteFromClipboard: { + run(destinations: Destination[]): Promise; + }; + insertSnippet: { + run( + targets: Target[], + snippetDescription: InsertSnippetArg, + ): Promise; + getPrePositionStages(snippetDescription: InsertSnippetArg): ModifierStage[]; + }; + wrapWithSnippet: { + run( + targets: Target[], + snippetDescription: WrapWithSnippetArg, + ): Promise; + getFinalStages(snippetDescription: WrapWithSnippetArg): ModifierStage[]; + }; + editNew: { + run(targets: Target[]): Promise; + runDestinations(destinations: Destination[]): Promise; }; } diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index d674faeabd1..925e4f01e92 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -9,6 +9,7 @@ import { CommandRunner } from "../../CommandRunner"; import { ActionRecord, ActionReturnValue } from "../../actions/actions.types"; import { StoredTargetMap } from "../../index"; import { TargetPipelineRunner } from "../../processTargets"; +import { ModifierStage } from "../../processTargets/PipelineStages.types"; import { SelectionWithEditor } from "../../typings/Types"; import { Target } from "../../typings/target.types"; import { Debug } from "../Debug"; @@ -41,25 +42,6 @@ export class CommandRunnerImpl implements CommandRunner { * it has one. */ async run({ action }: CommandComplete): Promise { - // const targetDescriptors = inferFullTargetDescriptors( - // partialTargetDescriptors, - // ); - - // if (this.debug.active) { - // this.debug.log("Full targets:"); - // this.debug.log(JSON.stringify(targetDescriptors, null, 2)); - // } - - // const action = this.actions[actionName]; - - // const prePositionStages = - // action.getPrePositionStages?.(...actionArgs) ?? []; - // const finalStages = action.getFinalStages?.(...actionArgs) ?? []; - - // const targets = targetDescriptors.map((targetDescriptor) => - // this.pipelineRunner.run(targetDescriptor, prePositionStages, finalStages), - // ); - const { returnValue, thatSelections: newThatSelections, @@ -103,54 +85,95 @@ export class CommandRunnerImpl implements CommandRunner { : this.actions.moveToTarget.run(sources, destinations); } case "swapTargets": { - const target1 = this.getTargets( + const targets1 = this.getTargets( inferenceContext, partialActionDescriptor.target1, ); - const target2 = this.getTargets( + const targets2 = this.getTargets( inferenceContext, partialActionDescriptor.target2, ); - return this.actions.swapTargets.run(target1, target2); + return this.actions.swapTargets.run(targets1, targets2); + } + case "wrapWithPairedDelimiter": + case "rewrapWithPairedDelimiter": { + const targets = this.getTargets( + inferenceContext, + partialActionDescriptor.target, + ); + return partialActionDescriptor.name === "wrapWithPairedDelimiter" + ? this.actions.wrapWithPairedDelimiter.run( + targets, + partialActionDescriptor.left, + partialActionDescriptor.right, + ) + : this.actions.rewrapWithPairedDelimiter.run( + targets, + partialActionDescriptor.left, + partialActionDescriptor.right, + ); + } + case "pasteFromClipboard": { + const destinations = this.getDestinations( + inferenceContext, + partialActionDescriptor.destination, + ); + return this.actions.pasteFromClipboard.run(destinations); + } + case "insertSnippet": { + const targets = this.getTargets( + inferenceContext, + partialActionDescriptor.target, + this.actions.insertSnippet.getPrePositionStages( + partialActionDescriptor.snippetDescription, + ), + ); + return this.actions.insertSnippet.run( + targets, + partialActionDescriptor.snippetDescription, + ); + } + case "wrapWithSnippet": { + const targets = this.getTargets( + inferenceContext, + partialActionDescriptor.target, + undefined, + this.actions.wrapWithSnippet.getFinalStages( + partialActionDescriptor.snippetDescription, + ), + ); + return this.actions.wrapWithSnippet.run( + targets, + partialActionDescriptor.snippetDescription, + ); + } + default: { + const action = this.actions[partialActionDescriptor.name]; + const targets = this.getTargets( + inferenceContext, + partialActionDescriptor.target, + undefined, + action.getFinalStages?.(), + ); + return action.run(targets); } - default: - throw Error("stuff"); - // case "wrapWithPairedDelimiter": - // // Some duplication with default case, but not tooo bad - // const [targetDescriptor] = inferFullTargets([ - // partialActionDescriptor.target, - // ]); - // const target = pipelineRunner.run(targetDescriptor); - - // return action.run( - // target, - // partialActionDescriptor.left, - // partialActionDescriptor.right, - // ); - // default: - // // TODO: Could prob expose `inferFullTarget` (singular) that takes single descriptor with no previousTargets - // // eg const targetDescriptor = inferFullTarget(partialActionDescriptor.target) - // const [targetDescriptor] = inferFullTargets([ - // partialActionDescriptor.target, - // ]); - - // // TODO: Do we want to handle final stages in the generic case? Most - // // actions don't have them anyway so we might want special cases for them - // const target = pipelineRunner.run(targetDescriptor); - - // return action.run(target); - // const action = this.actions[partialActionDescriptor.name]; } } private getTargets( inferenceContext: InferenceContext, partialTargetsDescriptor: PartialTargetDescriptor, + actionPrePositionStages?: ModifierStage[], + actionFinalStages?: ModifierStage[], ) { const sourceTargetDescriptor = inferenceContext.run( partialTargetsDescriptor, ); - return this.pipelineRunner.run(sourceTargetDescriptor); + return this.pipelineRunner.run( + sourceTargetDescriptor, + actionPrePositionStages, + actionFinalStages, + ); } private getDestinations( diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index 7669f4ef3cb..e518c6802fc 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -3,6 +3,7 @@ import { CommandV5, CommandV6, ImplicitTargetDescriptor, + InsertSnippetArg, InsertionMode, Modifier, ModifierV5, @@ -19,6 +20,7 @@ import { PartialTargetDescriptor, PartialTargetDescriptorV5, PositionModifierV5, + WrapWithSnippetArg, } from "@cursorless/common"; export function upgradeV5ToV6(command: CommandV5): CommandV6 { @@ -54,17 +56,30 @@ function upgradeAction( destination: targetToDestination(targets[0]), }; case "wrapWithPairedDelimiter": + case "rewrapWithPairedDelimiter": return { name: action.name, left: action.args![0] as string, right: action.args![1] as string, target: upgradeTarget(targets[0]), }; - default: + case "insertSnippet": + return { + name: action.name, + snippetDescription: action.args![0] as InsertSnippetArg, + target: upgradeTarget(targets[0]), + }; + case "wrapWithSnippet": return { name: action.name, + snippetDescription: action.args![0] as WrapWithSnippetArg, target: upgradeTarget(targets[0]), }; + default: + return { + name: action.name, + target: upgradeTarget(targets[0]), + } as PartialActionDescriptor; // TODO: } } diff --git a/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts b/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts index d429c2a7bb8..ddced4a4438 100644 --- a/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts @@ -8,11 +8,7 @@ import { } from "@cursorless/common"; import { isEqual } from "lodash"; import type { EditWithRangeUpdater } from "../../typings/Types"; -import type { - Destination, - EditNewActionType, - Target, -} from "../../typings/target.types"; +import type { Destination, Target } from "../../typings/target.types"; import { isSameType } from "../../util/typeUtils"; import { createContinuousRange, @@ -91,14 +87,6 @@ export default abstract class BaseTarget< return this.state.contentRange; } - constructChangeEdit(text: string): EditWithRangeUpdater { - return { - range: this.contentRange, - text, - updateRange: (range) => range, - }; - } - constructRemovalEdit(): EditWithRangeUpdater { return { range: this.getRemovalRange(), @@ -107,10 +95,6 @@ export default abstract class BaseTarget< }; } - getEditNewActionType(): EditNewActionType { - return "edit"; - } - getRemovalHighlightRange(): Range { return this.getRemovalRange(); } diff --git a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts index d46dbddf1a9..5b3c05c31c1 100644 --- a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts +++ b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts @@ -12,7 +12,7 @@ import { } from "../../typings/target.types"; export default class DestinationImpl implements Destination { - private readonly contentRange: Range; + public readonly contentRange: Range; private readonly isLineDelimiter: boolean; private readonly isBefore: boolean; private readonly indentationString: string; @@ -27,13 +27,6 @@ export default class DestinationImpl implements Destination { : ""; } - isEqual(destination: Destination): boolean { - return ( - this.insertionMode === destination.insertionMode && - this.target.isEqual(destination.target) - ); - } - getEditNewActionType(): EditNewActionType { if ( this.insertionDelimiter === "\n" && diff --git a/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts b/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts index 1db62c0de86..e6f3369ed81 100644 --- a/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts @@ -1,10 +1,7 @@ -import { InsertionMode } from "@cursorless/common"; -import { - BaseTarget, - CommonTargetParameters, - removalUnsupportedForPosition, -} from "."; -import { Target } from "../../typings/target.types"; +import { InsertionMode, Range, TextEditor } from "@cursorless/common"; +import { BaseTarget, CommonTargetParameters } from "."; +import { EditWithRangeUpdater } from "../../typings/Types"; +import { Destination, EditNewActionType } from "../../typings/target.types"; export default class NotebookCellTarget extends BaseTarget { type = "NotebookCellTarget"; @@ -23,38 +20,38 @@ export default class NotebookCellTarget extends BaseTarget { - type = "NotebookCellPositionTarget"; - insertionDelimiter = "\n"; - isNotebookCell = true; - public insertionMode: InsertionMode; + get editor(): TextEditor { + return this.target.editor; + } - constructor(parameters: NotebookCellPositionTargetParameters) { - super(parameters); - this.insertionMode = parameters.insertionMode; + get contentRange(): Range { + return this.target.contentRange; } - getLeadingDelimiterTarget = () => undefined; - getTrailingDelimiterTarget = () => undefined; - getRemovalRange = () => removalUnsupportedForPosition(this.insertionMode); + get insertionDelimiter(): string { + return this.target.insertionDelimiter; + } + + get isRaw(): boolean { + return this.target.isRaw; + } + + getEditNewActionType(): EditNewActionType { + throw new Error("Method not implemented."); + } - protected getCloneParameters(): NotebookCellPositionTargetParameters { - return { - ...this.state, - insertionMode: this.insertionMode, - }; + constructChangeEdit(_text: string): EditWithRangeUpdater { + throw new Error("Method not implemented."); } } diff --git a/packages/cursorless-engine/src/typings/target.types.ts b/packages/cursorless-engine/src/typings/target.types.ts index 85cf264464e..414b0fafd99 100644 --- a/packages/cursorless-engine/src/typings/target.types.ts +++ b/packages/cursorless-engine/src/typings/target.types.ts @@ -137,7 +137,6 @@ export interface Target { getTrailingDelimiterTarget(): Target | undefined; getRemovalRange(): Range; getRemovalHighlightRange(): Range; - getEditNewActionType(): EditNewActionType; withThatTarget(thatTarget: Target): Target; withContentRange(contentRange: Range): Target; createContinuousRangeTarget( @@ -146,8 +145,6 @@ export interface Target { includeStart: boolean, includeEnd: boolean, ): Target; - /** Constructs change/insertion edit. Adds delimiter before/after if needed */ - constructChangeEdit(text: string): EditWithRangeUpdater; /** Constructs removal edit */ constructRemovalEdit(): EditWithRangeUpdater; isEqual(target: Target): boolean; @@ -169,8 +166,10 @@ export interface Destination { readonly insertionMode: InsertionMode; readonly editor: TextEditor; readonly target: Target; + readonly contentRange: Range; readonly isRaw: boolean; readonly insertionDelimiter: string; + getEditNewActionType(): EditNewActionType; + /** Constructs change/insertion edit. Adds delimiter before/after if needed */ constructChangeEdit(text: string): EditWithRangeUpdater; - isEqual(destination: Destination): boolean; } diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index d4240b0665b..d2fc08652f1 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -3,6 +3,7 @@ import { CommandLatest, ImplicitTargetDescriptor, LATEST_VERSION, + PartialActionDescriptor, PartialDestinationDescriptor, PartialPrimitiveTargetDescriptor, PartialTargetDescriptor, @@ -187,6 +188,9 @@ export default class KeyboardCommandsTargeted { switch (action) { case "wrapWithPairedDelimiter": + case "rewrapWithPairedDelimiter": + case "insertSnippet": + case "wrapWithSnippet": throw Error(`Unsupported keyboard action: ${action}`); case "replaceWithTarget": case "moveToTarget": @@ -220,7 +224,7 @@ export default class KeyboardCommandsTargeted { action: { name: action, target, - }, + } as PartialActionDescriptor, // TODO: }); } From 6d181605479c106f25849d33d69ba790d4671179 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 00:45:52 +0200 Subject: [PATCH 24/99] more changes --- .../src/types/command/ActionDescriptor.ts | 31 +++++++++++++++++-- .../cursorless-engine/src/actions/Call.ts | 4 +-- .../src/actions/CallbackAction.ts | 4 +-- .../src/actions/ExecuteCommand.ts | 17 +++------- .../cursorless-engine/src/actions/Replace.ts | 15 +++------ .../src/actions/actions.types.ts | 17 +++++++++- .../core/commandRunner/CommandRunnerImpl.ts | 21 +++++++++++++ .../canonicalizeAndValidateCommand.ts | 4 +-- .../upgradeV5ToV6/upgradeV5ToV6.ts | 15 +++++++++ .../src/keyboard/KeyboardCommandsTargeted.ts | 2 ++ 10 files changed, 98 insertions(+), 32 deletions(-) diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index 512cbc5d9db..66b1eb9d512 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -11,7 +11,6 @@ const simpleActionNames = [ "deselect", "editNewLineAfter", "editNewLineBefore", - "executeCommand", "experimental.setInstanceReference", "extractVariable", "findInWorkspace", @@ -30,7 +29,6 @@ const simpleActionNames = [ "randomizeTargets", "remove", "rename", - "replace", "revealDefinition", "revealTypeDefinition", "reverseTargets", @@ -53,9 +51,11 @@ const simpleActionNames = [ const complexActionNames = [ "callAsFunction", "editNew", + "executeCommand", "insertSnippet", "moveToTarget", "pasteFromClipboard", + "replace", "replaceWithTarget", "rewrapWithPairedDelimiter", "swapTargets", @@ -138,6 +138,29 @@ export interface PartialWrapSnippetActionDescriptor { target: PartialTargetDescriptor; } +export interface ExecuteCommandOptions { + commandArgs?: any[]; + ensureSingleEditor?: boolean; + ensureSingleTarget?: boolean; + restoreSelection?: boolean; + showDecorations?: boolean; +} + +export interface PartialExecuteCommandActionDescriptor { + name: "executeCommand"; + commandId: string; + options: ExecuteCommandOptions; // TODO: make optional? + target: PartialTargetDescriptor; +} + +export type ReplaceWith = string[] | { start: number }; + +export interface PartialReplaceActionDescriptor { + name: "replace"; + replaceWith: ReplaceWith; + target: PartialTargetDescriptor; +} + export type PartialActionDescriptor = | PartialSimpleActionDescriptor | PartialBringMoveActionDescriptor @@ -145,4 +168,6 @@ export type PartialActionDescriptor = | PartialWrapWithPairedDelimiterActionDescriptor | PartialPasteActionDescriptor | PartialInsertSnippetActionDescriptor - | PartialWrapSnippetActionDescriptor; + | PartialWrapSnippetActionDescriptor + | PartialExecuteCommandActionDescriptor + | PartialReplaceActionDescriptor; diff --git a/packages/cursorless-engine/src/actions/Call.ts b/packages/cursorless-engine/src/actions/Call.ts index 7b9e44037be..ad2d87b22a6 100644 --- a/packages/cursorless-engine/src/actions/Call.ts +++ b/packages/cursorless-engine/src/actions/Call.ts @@ -1,9 +1,9 @@ import { Target } from "../typings/target.types"; import { ensureSingleTarget } from "../util/targetUtils"; import { Actions } from "./Actions"; -import { Action, ActionReturnValue } from "./actions.types"; +import { ActionReturnValue } from "./actions.types"; -export default class Call implements Action { +export default class Call { constructor(private actions: Actions) { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/CallbackAction.ts b/packages/cursorless-engine/src/actions/CallbackAction.ts index 40a2109626c..e3b08102920 100644 --- a/packages/cursorless-engine/src/actions/CallbackAction.ts +++ b/packages/cursorless-engine/src/actions/CallbackAction.ts @@ -16,7 +16,7 @@ import { runOnTargetsForEachEditor, runOnTargetsForEachEditorSequentially, } from "../util/targetUtils"; -import { Action, ActionReturnValue } from "./actions.types"; +import { ActionReturnValue } from "./actions.types"; interface CallbackOptions { callback: (editor: EditableTextEditor, targets: Target[]) => Promise; @@ -32,7 +32,7 @@ interface CallbackOptions { * It takes a {@link CallbackOptions.callback callback} that is called once for * each editor, receiving all the targets that are in the given editor. */ -export class CallbackAction implements Action { +export class CallbackAction { constructor(private rangeUpdater: RangeUpdater) { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/ExecuteCommand.ts b/packages/cursorless-engine/src/actions/ExecuteCommand.ts index 1cbd236acef..9a64d6a0b93 100644 --- a/packages/cursorless-engine/src/actions/ExecuteCommand.ts +++ b/packages/cursorless-engine/src/actions/ExecuteCommand.ts @@ -1,25 +1,18 @@ +import { ExecuteCommandOptions } from "@cursorless/common"; import { RangeUpdater } from "../core/updateSelections/RangeUpdater"; import { ide } from "../singletons/ide.singleton"; import { Target } from "../typings/target.types"; -import { Action, ActionReturnValue } from "./actions.types"; import { CallbackAction } from "./CallbackAction"; - -interface Options { - commandArgs?: any[]; - ensureSingleEditor?: boolean; - ensureSingleTarget?: boolean; - restoreSelection?: boolean; - showDecorations?: boolean; -} +import { ActionReturnValue } from "./actions.types"; /** * This action can be used to execute a built-in ide command on one or more * targets by first setting the selection to those targets and then running the * action, restoring the selections if - * {@link Options.restoreSelection restoreSelection} is `true`. Internally, most + * {@link ExecuteCommandOptions.restoreSelection restoreSelection} is `true`. Internally, most * of the heavy lifting is done by {@link CallbackAction}. */ -export default class ExecuteCommand implements Action { +export default class ExecuteCommand { private callbackAction: CallbackAction; constructor(rangeUpdater: RangeUpdater) { this.callbackAction = new CallbackAction(rangeUpdater); @@ -35,7 +28,7 @@ export default class ExecuteCommand implements Action { ensureSingleTarget, restoreSelection, showDecorations, - }: Options = {}, + }: ExecuteCommandOptions = {}, ): Promise { const args = commandArgs ?? []; diff --git a/packages/cursorless-engine/src/actions/Replace.ts b/packages/cursorless-engine/src/actions/Replace.ts index c2497e9e9c7..e7f1a858f34 100644 --- a/packages/cursorless-engine/src/actions/Replace.ts +++ b/packages/cursorless-engine/src/actions/Replace.ts @@ -1,23 +1,18 @@ -import { FlashStyle } from "@cursorless/common"; +import { FlashStyle, ReplaceWith } from "@cursorless/common"; import { flatten, zip } from "lodash"; import { RangeUpdater } from "../core/updateSelections/RangeUpdater"; import { performEditsAndUpdateSelections } from "../core/updateSelections/updateSelections"; import { ide } from "../singletons/ide.singleton"; import { Target } from "../typings/target.types"; import { flashTargets, runForEachEditor } from "../util/targetUtils"; -import { Action, ActionReturnValue } from "./actions.types"; +import { ActionReturnValue } from "./actions.types"; -type RangeGenerator = { start: number }; - -export default class Replace implements Action { +export default class Replace { constructor(private rangeUpdater: RangeUpdater) { this.run = this.run.bind(this); } - private getTexts( - targets: Target[], - replaceWith: string[] | RangeGenerator, - ): string[] { + private getTexts(targets: Target[], replaceWith: ReplaceWith): string[] { if (Array.isArray(replaceWith)) { // Broadcast single text to each target if (replaceWith.length === 1) { @@ -34,7 +29,7 @@ export default class Replace implements Action { async run( targets: Target[], - replaceWith: string[] | RangeGenerator, + replaceWith: ReplaceWith, ): Promise { await flashTargets(ide(), targets, FlashStyle.pendingModification0); diff --git a/packages/cursorless-engine/src/actions/actions.types.ts b/packages/cursorless-engine/src/actions/actions.types.ts index 0b970cde3cf..5bcf60364ff 100644 --- a/packages/cursorless-engine/src/actions/actions.types.ts +++ b/packages/cursorless-engine/src/actions/actions.types.ts @@ -1,5 +1,7 @@ import type { + ExecuteCommandOptions, InsertSnippetArg, + ReplaceWith, SimpleActionName, WrapWithSnippetArg, } from "@cursorless/common"; @@ -51,7 +53,7 @@ export interface ActionReturnValue { } export interface Action { - run(targets: Target[], ...args: any[]): Promise; + run(targets: Target[]): Promise; /** * Used to define final stages that should be run at the end of the pipeline before the action @@ -117,4 +119,17 @@ export interface ActionRecord extends Record { run(targets: Target[]): Promise; runDestinations(destinations: Destination[]): Promise; }; + executeCommand: { + run( + targets: Target[], + commandId: string, + options?: ExecuteCommandOptions, + ): Promise; + }; + replace: { + run( + targets: Target[], + replaceWith: ReplaceWith, + ): Promise; + }; } diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 925e4f01e92..482782f2339 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -120,6 +120,27 @@ export class CommandRunnerImpl implements CommandRunner { ); return this.actions.pasteFromClipboard.run(destinations); } + case "executeCommand": { + const targets = this.getTargets( + inferenceContext, + partialActionDescriptor.target, + ); + return this.actions.executeCommand.run( + targets, + partialActionDescriptor.commandId, + partialActionDescriptor.options, + ); + } + case "replace": { + const targets = this.getTargets( + inferenceContext, + partialActionDescriptor.target, + ); + return this.actions.replace.run( + targets, + partialActionDescriptor.replaceWith, + ); + } case "insertSnippet": { const targets = this.getTargets( inferenceContext, diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts index 8f526b63b9d..3b90f39a7af 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts @@ -30,7 +30,7 @@ import { upgradeV5ToV6 } from "./upgradeV5ToV6"; */ export function canonicalizeAndValidateCommand( command: Command, - // ): EnforceUndefined { + // ): EnforceUndefined {// TODO: ): CommandComplete { const commandUpgraded = upgradeCommand(command); const { action, usePrePhraseSnapshot = false, spokenForm } = commandUpgraded; @@ -47,7 +47,7 @@ export function canonicalizeAndValidateCommand( action: { ...action, name: actionName, - } as PartialActionDescriptor, + } as PartialActionDescriptor, // TODO: usePrePhraseSnapshot, }; } diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index e518c6802fc..ef5d4221b69 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -2,6 +2,7 @@ import { ActionCommandV5, CommandV5, CommandV6, + ExecuteCommandOptions, ImplicitTargetDescriptor, InsertSnippetArg, InsertionMode, @@ -20,6 +21,7 @@ import { PartialTargetDescriptor, PartialTargetDescriptorV5, PositionModifierV5, + ReplaceWith, WrapWithSnippetArg, } from "@cursorless/common"; @@ -75,6 +77,19 @@ function upgradeAction( snippetDescription: action.args![0] as WrapWithSnippetArg, target: upgradeTarget(targets[0]), }; + case "executeCommand": + return { + name: action.name, + commandId: action.args![0] as string, + options: action.args?.[1] as ExecuteCommandOptions, + target: upgradeTarget(targets[0]), + }; + case "replace": + return { + name: action.name, + replaceWith: action.args![0] as ReplaceWith, + target: upgradeTarget(targets[0]), + }; default: return { name: action.name, diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index d2fc08652f1..67e7f4dc6f7 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -191,6 +191,8 @@ export default class KeyboardCommandsTargeted { case "rewrapWithPairedDelimiter": case "insertSnippet": case "wrapWithSnippet": + case "executeCommand": + case "replace": throw Error(`Unsupported keyboard action: ${action}`); case "replaceWithTarget": case "moveToTarget": From 9fffc11ae67eaabebcadaf3c79c844e1920d2834 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 01:41:24 +0200 Subject: [PATCH 25/99] bug fixes --- .../src/types/command/ActionDescriptor.ts | 18 ++++++- .../common/src/types/command/command.types.ts | 12 +++-- .../GenerateSnippet/GenerateSnippet.ts | 4 +- .../src/actions/actions.types.ts | 4 ++ .../core/commandRunner/CommandRunnerImpl.ts | 51 ++++++++++++++----- .../canonicalizeAndValidateCommand.ts | 4 +- .../upgradeV5ToV6/upgradeV5ToV6.ts | 12 +++++ .../src/util/getPartialTargetDescriptors.1.ts | 2 + .../src/keyboard/KeyboardCommandsTargeted.ts | 9 ++++ 9 files changed, 93 insertions(+), 23 deletions(-) diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index 66b1eb9d512..f394bd15fae 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -16,7 +16,6 @@ const simpleActionNames = [ "findInWorkspace", "foldRegion", "followLink", - "generateSnippet", "getText", "highlight", "indentLine", @@ -52,6 +51,7 @@ const complexActionNames = [ "callAsFunction", "editNew", "executeCommand", + "generateSnippet", "insertSnippet", "moveToTarget", "pasteFromClipboard", @@ -82,6 +82,12 @@ export interface PartialBringMoveActionDescriptor { destination: PartialDestinationDescriptor; } +export interface PartialCallActionDescriptor { + name: "callAsFunction"; + source: PartialTargetDescriptor; + destination: PartialTargetDescriptor; +} + export interface PartialSwapActionDescriptor { name: "swapTargets"; target1: PartialTargetDescriptor; @@ -100,6 +106,12 @@ export interface PartialPasteActionDescriptor { destination: PartialDestinationDescriptor; } +export interface PartialGenerateSnippetActionDescriptor { + name: "generateSnippet"; + snippetName?: string; + target: PartialTargetDescriptor; +} + interface NamedSnippetArg { type: "named"; name: string; @@ -149,7 +161,7 @@ export interface ExecuteCommandOptions { export interface PartialExecuteCommandActionDescriptor { name: "executeCommand"; commandId: string; - options: ExecuteCommandOptions; // TODO: make optional? + options?: ExecuteCommandOptions; target: PartialTargetDescriptor; } @@ -165,8 +177,10 @@ export type PartialActionDescriptor = | PartialSimpleActionDescriptor | PartialBringMoveActionDescriptor | PartialSwapActionDescriptor + | PartialCallActionDescriptor | PartialWrapWithPairedDelimiterActionDescriptor | PartialPasteActionDescriptor + | PartialGenerateSnippetActionDescriptor | PartialInsertSnippetActionDescriptor | PartialWrapSnippetActionDescriptor | PartialExecuteCommandActionDescriptor diff --git a/packages/common/src/types/command/command.types.ts b/packages/common/src/types/command/command.types.ts index 27bb4d086ab..1fba0888e2c 100644 --- a/packages/common/src/types/command/command.types.ts +++ b/packages/common/src/types/command/command.types.ts @@ -1,4 +1,3 @@ -import type { PartialActionDescriptor } from "./ActionDescriptor"; import { CommandV6 } from "./CommandV6.types"; import type { CommandV0, CommandV1 } from "./legacy/CommandV0V1.types"; import type { CommandV2 } from "./legacy/CommandV2.types"; @@ -6,11 +5,14 @@ import type { CommandV3 } from "./legacy/CommandV3.types"; import type { CommandV4 } from "./legacy/CommandV4.types"; import type { CommandV5 } from "./legacy/CommandV5.types"; -export type CommandComplete = Required> & - Pick & { - action: Required; - }; +// TODO: +// export type CommandComplete = Required> & +// Pick & { +// action: Required; +// }; +export type CommandComplete = Required> & + Pick; export const LATEST_VERSION = 6 as const; export type CommandLatest = Command & { diff --git a/packages/cursorless-engine/src/actions/GenerateSnippet/GenerateSnippet.ts b/packages/cursorless-engine/src/actions/GenerateSnippet/GenerateSnippet.ts index 5474d1a5eaf..b9e78aa141d 100644 --- a/packages/cursorless-engine/src/actions/GenerateSnippet/GenerateSnippet.ts +++ b/packages/cursorless-engine/src/actions/GenerateSnippet/GenerateSnippet.ts @@ -4,7 +4,7 @@ import { ide } from "../../singletons/ide.singleton"; import { Target } from "../../typings/target.types"; import { matchAll } from "../../util/regex"; import { ensureSingleTarget, flashTargets } from "../../util/targetUtils"; -import { Action, ActionReturnValue } from "../actions.types"; +import { ActionReturnValue } from "../actions.types"; import { constructSnippetBody } from "./constructSnippetBody"; import { editText } from "./editText"; import { openNewSnippetFile } from "./openNewSnippetFile"; @@ -45,7 +45,7 @@ import Substituter from "./Substituter"; * very similar to snippet placeholders, so we would end up with lots of * confusing escaping. */ -export default class GenerateSnippet implements Action { +export default class GenerateSnippet { constructor() { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/actions.types.ts b/packages/cursorless-engine/src/actions/actions.types.ts index 5bcf60364ff..a4f0553c78a 100644 --- a/packages/cursorless-engine/src/actions/actions.types.ts +++ b/packages/cursorless-engine/src/actions/actions.types.ts @@ -97,10 +97,14 @@ export interface ActionRecord extends Record { left: string, right: string, ): Promise; + getFinalStages(): ModifierStage[]; }; pasteFromClipboard: { run(destinations: Destination[]): Promise; }; + generateSnippet: { + run(targets: Target[], snippetName?: string): Promise; + }; insertSnippet: { run( targets: Target[], diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 482782f2339..7dfe3564fba 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -95,23 +95,40 @@ export class CommandRunnerImpl implements CommandRunner { ); return this.actions.swapTargets.run(targets1, targets2); } - case "wrapWithPairedDelimiter": + case "callAsFunction": { + const sources = this.getTargets( + inferenceContext, + partialActionDescriptor.source, + ); + const destinations = this.getTargets( + inferenceContext, + partialActionDescriptor.destination, + ); + return this.actions.callAsFunction.run(sources, destinations); + } + case "wrapWithPairedDelimiter": { + const targets = this.getTargets( + inferenceContext, + partialActionDescriptor.target, + ); + return this.actions.wrapWithPairedDelimiter.run( + targets, + partialActionDescriptor.left, + partialActionDescriptor.right, + ); + } case "rewrapWithPairedDelimiter": { const targets = this.getTargets( inferenceContext, partialActionDescriptor.target, + undefined, + this.actions.rewrapWithPairedDelimiter.getFinalStages(), + ); + return this.actions.rewrapWithPairedDelimiter.run( + targets, + partialActionDescriptor.left, + partialActionDescriptor.right, ); - return partialActionDescriptor.name === "wrapWithPairedDelimiter" - ? this.actions.wrapWithPairedDelimiter.run( - targets, - partialActionDescriptor.left, - partialActionDescriptor.right, - ) - : this.actions.rewrapWithPairedDelimiter.run( - targets, - partialActionDescriptor.left, - partialActionDescriptor.right, - ); } case "pasteFromClipboard": { const destinations = this.getDestinations( @@ -141,6 +158,16 @@ export class CommandRunnerImpl implements CommandRunner { partialActionDescriptor.replaceWith, ); } + case "generateSnippet": { + const targets = this.getTargets( + inferenceContext, + partialActionDescriptor.target, + ); + return this.actions.generateSnippet.run( + targets, + partialActionDescriptor.snippetName, + ); + } case "insertSnippet": { const targets = this.getTargets( inferenceContext, diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts index 3b90f39a7af..0d05c9f23ed 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts @@ -3,6 +3,7 @@ import { Command, CommandComplete, CommandLatest, + EnforceUndefined, LATEST_VERSION, Modifier, OutdatedExtensionError, @@ -30,8 +31,7 @@ import { upgradeV5ToV6 } from "./upgradeV5ToV6"; */ export function canonicalizeAndValidateCommand( command: Command, - // ): EnforceUndefined {// TODO: -): CommandComplete { +): EnforceUndefined { const commandUpgraded = upgradeCommand(command); const { action, usePrePhraseSnapshot = false, spokenForm } = commandUpgraded; diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index ef5d4221b69..e427928b33e 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -52,6 +52,12 @@ function upgradeAction( target1: upgradeTarget(targets[0]), target2: upgradeTarget(targets[1]), }; + case "callAsFunction": + return { + name: action.name, + source: upgradeTarget(targets[0]), + destination: upgradeTarget(targets[1]), + }; case "pasteFromClipboard": return { name: action.name, @@ -65,6 +71,12 @@ function upgradeAction( right: action.args![1] as string, target: upgradeTarget(targets[0]), }; + case "generateSnippet": + return { + name: action.name, + snippetName: action.args?.[0] as string, + target: upgradeTarget(targets[0]), + }; case "insertSnippet": return { name: action.name, diff --git a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts index b5ee10b7497..266855f0cde 100644 --- a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts +++ b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts @@ -10,6 +10,8 @@ export function getPartialTargetDescriptors( command: CommandLatest, ): PartialTargetDescriptor[] { switch (command.action.name) { + case "callAsFunction": + return [command.action.source, command.action.destination]; case "replaceWithTarget": case "moveToTarget": return [ diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index 67e7f4dc6f7..913191524d3 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -213,6 +213,15 @@ export default class KeyboardCommandsTargeted { }, }); break; + case "callAsFunction": + returnValue = await executeCursorlessCommand({ + action: { + name: action, + source: target, + destination: { type: "implicit" }, + }, + }); + break; case "pasteFromClipboard": returnValue = await executeCursorlessCommand({ action: { From cf85117697e392f40893cf52a5043f1709107f3d Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 08:29:45 +0200 Subject: [PATCH 26/99] stuff --- .../src/types/command/ActionDescriptor.ts | 16 +++++++++--- .../src/actions/BringMoveSwap.ts | 22 ++++++++-------- .../cursorless-engine/src/actions/Call.ts | 15 +++-------- .../src/actions/Highlight.ts | 4 +-- .../src/actions/actions.types.ts | 9 ++++++- .../core/commandRunner/CommandRunnerImpl.ts | 26 ++++++++++++------- .../upgradeV5ToV6/upgradeV5ToV6.ts | 11 ++++++-- .../src/util/getPartialTargetDescriptors.1.ts | 2 +- .../src/keyboard/KeyboardCommandsTargeted.ts | 4 +-- 9 files changed, 66 insertions(+), 43 deletions(-) diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index f394bd15fae..a8121797241 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -1,3 +1,4 @@ +import { HighlightId } from "../.."; import { PartialDestinationDescriptor, PartialTargetDescriptor, @@ -17,7 +18,6 @@ const simpleActionNames = [ "foldRegion", "followLink", "getText", - "highlight", "indentLine", "insertCopyAfter", "insertCopyBefore", @@ -52,6 +52,7 @@ const complexActionNames = [ "editNew", "executeCommand", "generateSnippet", + "highlight", "insertSnippet", "moveToTarget", "pasteFromClipboard", @@ -84,8 +85,8 @@ export interface PartialBringMoveActionDescriptor { export interface PartialCallActionDescriptor { name: "callAsFunction"; - source: PartialTargetDescriptor; - destination: PartialTargetDescriptor; + callees: PartialTargetDescriptor; + args: PartialTargetDescriptor; } export interface PartialSwapActionDescriptor { @@ -173,6 +174,12 @@ export interface PartialReplaceActionDescriptor { target: PartialTargetDescriptor; } +export interface PartialHighlightActionDescriptor { + name: "highlight"; + highlightId?: HighlightId; + target: PartialTargetDescriptor; +} + export type PartialActionDescriptor = | PartialSimpleActionDescriptor | PartialBringMoveActionDescriptor @@ -184,4 +191,5 @@ export type PartialActionDescriptor = | PartialInsertSnippetActionDescriptor | PartialWrapSnippetActionDescriptor | PartialExecuteCommandActionDescriptor - | PartialReplaceActionDescriptor; + | PartialReplaceActionDescriptor + | PartialHighlightActionDescriptor; diff --git a/packages/cursorless-engine/src/actions/BringMoveSwap.ts b/packages/cursorless-engine/src/actions/BringMoveSwap.ts index e4b1cc5d50f..dd448117eff 100644 --- a/packages/cursorless-engine/src/actions/BringMoveSwap.ts +++ b/packages/cursorless-engine/src/actions/BringMoveSwap.ts @@ -384,29 +384,29 @@ export class Swap extends BringMoveSwap { private getEditsSwap(targets1: Target[], targets2: Target[]): ExtendedEdit[] { const results: ExtendedEdit[] = []; - targets1.forEach((source, i) => { - const destination = targets2[i]; - if (source == null || destination == null) { + targets1.forEach((target1, i) => { + const target2 = targets2[i]; + if (target1 == null || target2 == null) { throw new Error("Targets must have same number of args"); } // Add destination edit results.push({ - edit: destination + edit: target2 .toDestination("to") - .constructChangeEdit(source.contentText), - editor: destination.editor, - originalTarget: destination, + .constructChangeEdit(target1.contentText), + editor: target2.editor, + originalTarget: target2, isSource: false, }); // Add source edit results.push({ - edit: source + edit: target1 .toDestination("to") - .constructChangeEdit(destination.contentText), - editor: source.editor, - originalTarget: source, + .constructChangeEdit(target2.contentText), + editor: target1.editor, + originalTarget: target1, isSource: true, }); }); diff --git a/packages/cursorless-engine/src/actions/Call.ts b/packages/cursorless-engine/src/actions/Call.ts index ad2d87b22a6..501bd4dbc60 100644 --- a/packages/cursorless-engine/src/actions/Call.ts +++ b/packages/cursorless-engine/src/actions/Call.ts @@ -8,23 +8,16 @@ export default class Call { this.run = this.run.bind(this); } - async run( - sources: Target[], - destinations: Target[], - ): Promise { - ensureSingleTarget(sources); + async run(callees: Target[], args: Target[]): Promise { + ensureSingleTarget(callees); - const { returnValue: texts } = await this.actions.getText.run(sources, { + const { returnValue: texts } = await this.actions.getText.run(callees, { showDecorations: false, }); // NB: We unwrap and then rewrap the return value here so that we don't include the source mark const { thatSelections: thatMark } = - await this.actions.wrapWithPairedDelimiter.run( - destinations, - texts[0] + "(", - ")", - ); + await this.actions.wrapWithPairedDelimiter.run(args, texts[0] + "(", ")"); return { thatSelections: thatMark }; } diff --git a/packages/cursorless-engine/src/actions/Highlight.ts b/packages/cursorless-engine/src/actions/Highlight.ts index 249874eef85..ae418ae5bb4 100644 --- a/packages/cursorless-engine/src/actions/Highlight.ts +++ b/packages/cursorless-engine/src/actions/Highlight.ts @@ -5,9 +5,9 @@ import { runOnTargetsForEachEditor, toGeneralizedRange, } from "../util/targetUtils"; -import { Action, ActionReturnValue } from "./actions.types"; +import { ActionReturnValue } from "./actions.types"; -export default class Highlight implements Action { +export default class Highlight { constructor() { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/actions.types.ts b/packages/cursorless-engine/src/actions/actions.types.ts index a4f0553c78a..3bea47b43f7 100644 --- a/packages/cursorless-engine/src/actions/actions.types.ts +++ b/packages/cursorless-engine/src/actions/actions.types.ts @@ -1,5 +1,6 @@ import type { ExecuteCommandOptions, + HighlightId, InsertSnippetArg, ReplaceWith, SimpleActionName, @@ -67,7 +68,7 @@ export interface Action { */ export interface ActionRecord extends Record { callAsFunction: { - run(sources: Target[], destinations: Target[]): Promise; + run(callees: Target[], args: Target[]): Promise; }; replaceWithTarget: { run( @@ -136,4 +137,10 @@ export interface ActionRecord extends Record { replaceWith: ReplaceWith, ): Promise; }; + highlight: { + run( + targets: Target[], + highlightId?: HighlightId, + ): Promise; + }; } diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 7dfe3564fba..d42bad5e18d 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -96,15 +96,15 @@ export class CommandRunnerImpl implements CommandRunner { return this.actions.swapTargets.run(targets1, targets2); } case "callAsFunction": { - const sources = this.getTargets( + const callees = this.getTargets( inferenceContext, - partialActionDescriptor.source, + partialActionDescriptor.callees, ); - const destinations = this.getTargets( + const args = this.getTargets( inferenceContext, - partialActionDescriptor.destination, + partialActionDescriptor.args, ); - return this.actions.callAsFunction.run(sources, destinations); + return this.actions.callAsFunction.run(callees, args); } case "wrapWithPairedDelimiter": { const targets = this.getTargets( @@ -158,6 +158,16 @@ export class CommandRunnerImpl implements CommandRunner { partialActionDescriptor.replaceWith, ); } + case "highlight": { + const targets = this.getTargets( + inferenceContext, + partialActionDescriptor.target, + ); + return this.actions.highlight.run( + targets, + partialActionDescriptor.highlightId, + ); + } case "generateSnippet": { const targets = this.getTargets( inferenceContext, @@ -214,11 +224,9 @@ export class CommandRunnerImpl implements CommandRunner { actionPrePositionStages?: ModifierStage[], actionFinalStages?: ModifierStage[], ) { - const sourceTargetDescriptor = inferenceContext.run( - partialTargetsDescriptor, - ); + const targetDescriptor = inferenceContext.run(partialTargetsDescriptor); return this.pipelineRunner.run( - sourceTargetDescriptor, + targetDescriptor, actionPrePositionStages, actionFinalStages, ); diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index e427928b33e..d81f0ec70ba 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -3,6 +3,7 @@ import { CommandV5, CommandV6, ExecuteCommandOptions, + HighlightId, ImplicitTargetDescriptor, InsertSnippetArg, InsertionMode, @@ -55,8 +56,8 @@ function upgradeAction( case "callAsFunction": return { name: action.name, - source: upgradeTarget(targets[0]), - destination: upgradeTarget(targets[1]), + callees: upgradeTarget(targets[0]), + args: upgradeTarget(targets[1]), }; case "pasteFromClipboard": return { @@ -102,6 +103,12 @@ function upgradeAction( replaceWith: action.args![0] as ReplaceWith, target: upgradeTarget(targets[0]), }; + case "highlight": + return { + name: action.name, + highlightId: action.args?.[0] as HighlightId, + target: upgradeTarget(targets[0]), + }; default: return { name: action.name, diff --git a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts index 266855f0cde..00d64e4a09c 100644 --- a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts +++ b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts @@ -11,7 +11,7 @@ export function getPartialTargetDescriptors( ): PartialTargetDescriptor[] { switch (command.action.name) { case "callAsFunction": - return [command.action.source, command.action.destination]; + return [command.action.callees, command.action.args]; case "replaceWithTarget": case "moveToTarget": return [ diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index 913191524d3..6a6a91b457c 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -217,8 +217,8 @@ export default class KeyboardCommandsTargeted { returnValue = await executeCursorlessCommand({ action: { name: action, - source: target, - destination: { type: "implicit" }, + callees: target, + args: { type: "implicit" }, }, }); break; From 4f55a0a2da8109c448c32a12da6114c15e41d1b5 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 08:44:26 +0200 Subject: [PATCH 27/99] update snippet --- .../src/types/command/ActionDescriptor.ts | 2 +- .../src/actions/EditNew/EditNew.ts | 2 +- .../actions/EditNew/runNotebookCellTargets.ts | 3 +-- .../src/actions/InsertSnippet.ts | 10 +++++--- .../src/actions/PasteFromClipboard.ts | 2 +- .../src/actions/actions.types.ts | 2 +- .../core/commandRunner/CommandRunnerImpl.ts | 25 +++++++++++++++---- .../upgradeV5ToV6/upgradeV5ToV6.ts | 2 +- .../src/util/getPartialTargetDescriptors.1.ts | 1 + .../cursorless-engine/src/util/targetUtils.ts | 10 +++++--- 10 files changed, 40 insertions(+), 19 deletions(-) diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index a8121797241..4a29b18a37a 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -129,7 +129,7 @@ export type InsertSnippetArg = NamedSnippetArg | CustomSnippetArg; export interface PartialInsertSnippetActionDescriptor { name: "insertSnippet"; snippetDescription: InsertSnippetArg; - target: PartialTargetDescriptor; + destination: PartialDestinationDescriptor; } interface NamedSnippetArg { diff --git a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts index bc6b5229b35..e51752522c4 100644 --- a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts +++ b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts @@ -43,7 +43,7 @@ export class EditNew implements Action { } const editableEditor = ide().getEditableTextEditor( - ensureSingleEditor(destinations.map((d) => d.target)), + ensureSingleEditor(destinations), ); /** diff --git a/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts b/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts index af9c41784f8..617b1ae110f 100644 --- a/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts +++ b/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts @@ -11,8 +11,7 @@ export async function runEditNewNotebookCellTargets( ): Promise { // Can only run on one target because otherwise we'd end up with cursors in // multiple cells, which is unsupported in VSCode - ensureSingleTarget(destinations.map((d) => d.target)); - const destination = destinations[0]; + const destination = ensureSingleTarget(destinations); const editor = ide().getEditableTextEditor(destination.editor); const isAbove = destination.insertionMode === "before"; diff --git a/packages/cursorless-engine/src/actions/InsertSnippet.ts b/packages/cursorless-engine/src/actions/InsertSnippet.ts index 5677adcd0ea..706f08221bf 100644 --- a/packages/cursorless-engine/src/actions/InsertSnippet.ts +++ b/packages/cursorless-engine/src/actions/InsertSnippet.ts @@ -21,7 +21,7 @@ import { transformSnippetVariables, } from "../snippets/snippet"; import { SnippetParser } from "../snippets/vendor/vscodeSnippet/snippetParser"; -import { Target } from "../typings/target.types"; +import { Destination, Target } from "../typings/target.types"; import { ensureSingleEditor } from "../util/targetUtils"; import { Actions } from "./Actions"; import { ActionReturnValue } from "./actions.types"; @@ -109,12 +109,14 @@ export default class InsertSnippet { } async run( - targets: Target[], + destinations: Destination[], snippetDescription: InsertSnippetArg, ): Promise { - const editor = ide().getEditableTextEditor(ensureSingleEditor(targets)); + const editor = ide().getEditableTextEditor( + ensureSingleEditor(destinations), + ); - await this.actions.editNew.run(targets); + await this.actions.editNew.runDestinations(destinations); const targetSelectionInfos = editor.selections.map((selection) => getSelectionInfo( diff --git a/packages/cursorless-engine/src/actions/PasteFromClipboard.ts b/packages/cursorless-engine/src/actions/PasteFromClipboard.ts index ec3cdce8006..5e7dfd1d623 100644 --- a/packages/cursorless-engine/src/actions/PasteFromClipboard.ts +++ b/packages/cursorless-engine/src/actions/PasteFromClipboard.ts @@ -20,7 +20,7 @@ export class PasteFromClipboard { async run(destinations: Destination[]): Promise { const editor = ide().getEditableTextEditor( - ensureSingleEditor(destinations.map((d) => d.target)), + ensureSingleEditor(destinations), ); const originalEditor = ide().activeEditableTextEditor; diff --git a/packages/cursorless-engine/src/actions/actions.types.ts b/packages/cursorless-engine/src/actions/actions.types.ts index 3bea47b43f7..5783861c0df 100644 --- a/packages/cursorless-engine/src/actions/actions.types.ts +++ b/packages/cursorless-engine/src/actions/actions.types.ts @@ -108,7 +108,7 @@ export interface ActionRecord extends Record { }; insertSnippet: { run( - targets: Target[], + destinations: Destination[], snippetDescription: InsertSnippetArg, ): Promise; getPrePositionStages(snippetDescription: InsertSnippetArg): ModifierStage[]; diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index d42bad5e18d..ef737a94fa2 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -179,15 +179,15 @@ export class CommandRunnerImpl implements CommandRunner { ); } case "insertSnippet": { - const targets = this.getTargets( + const destinations = this.getDestinations( inferenceContext, - partialActionDescriptor.target, + partialActionDescriptor.destination, this.actions.insertSnippet.getPrePositionStages( partialActionDescriptor.snippetDescription, ), ); return this.actions.insertSnippet.run( - targets, + destinations, partialActionDescriptor.snippetDescription, ); } @@ -235,15 +235,24 @@ export class CommandRunnerImpl implements CommandRunner { private getDestinations( inferenceContext: InferenceContext, partialDestinationDescriptor: PartialDestinationDescriptor, + actionPrePositionStages?: ModifierStage[], + actionFinalStages?: ModifierStage[], ) { if (partialDestinationDescriptor.type === "destination") { return this.getDestinationTargetsFromPrimitive( inferenceContext, partialDestinationDescriptor, + actionPrePositionStages, + actionFinalStages, ); } else { return partialDestinationDescriptor.destinations.flatMap((destination) => - this.getDestinationTargetsFromPrimitive(inferenceContext, destination), + this.getDestinationTargetsFromPrimitive( + inferenceContext, + destination, + actionPrePositionStages, + actionFinalStages, + ), ); } } @@ -251,13 +260,19 @@ export class CommandRunnerImpl implements CommandRunner { private getDestinationTargetsFromPrimitive( inferenceContext: InferenceContext, partialDestinationDescriptor: PartialPrimitiveDestinationDescriptor, + actionPrePositionStages?: ModifierStage[], + actionFinalStages?: ModifierStage[], ) { const destinationTargetDescriptor = inferenceContext.run( partialDestinationDescriptor.target, ); return this.pipelineRunner - .run(destinationTargetDescriptor) + .run( + destinationTargetDescriptor, + actionPrePositionStages, + actionFinalStages, + ) .map((target) => target.toDestination(partialDestinationDescriptor.insertionMode), ); diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index d81f0ec70ba..a3754dc65d1 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -82,7 +82,7 @@ function upgradeAction( return { name: action.name, snippetDescription: action.args![0] as InsertSnippetArg, - target: upgradeTarget(targets[0]), + destination: targetToDestination(targets[0]), }; case "wrapWithSnippet": return { diff --git a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts index 00d64e4a09c..406305a0a35 100644 --- a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts +++ b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts @@ -21,6 +21,7 @@ export function getPartialTargetDescriptors( case "swapTargets": return [command.action.target1, command.action.target2]; case "pasteFromClipboard": + case "insertSnippet": return [ getPartialTargetDescriptorFromDestination(command.action.destination), ]; diff --git a/packages/cursorless-engine/src/util/targetUtils.ts b/packages/cursorless-engine/src/util/targetUtils.ts index 4e0e33a7014..fc08a1bd4f8 100644 --- a/packages/cursorless-engine/src/util/targetUtils.ts +++ b/packages/cursorless-engine/src/util/targetUtils.ts @@ -11,10 +11,12 @@ import { toLineRange, } from "@cursorless/common"; import { zip } from "lodash"; -import { Target } from "../typings/target.types"; +import { Destination, Target } from "../typings/target.types"; import { SelectionWithEditor } from "../typings/Types"; -export function ensureSingleEditor(targets: Target[]): TextEditor { +export function ensureSingleEditor( + targets: Target[] | Destination[], +): TextEditor { if (targets.length === 0) { throw new Error("Require at least one target with this action"); } @@ -28,7 +30,9 @@ export function ensureSingleEditor(targets: Target[]): TextEditor { return editors[0]; } -export function ensureSingleTarget(targets: Target[]) { +export function ensureSingleTarget( + targets: T[], +): T { if (targets.length !== 1) { throw new Error("Can only have one target with this action"); } From 10743c92344dd47e025233801f3249d3edb0ca42 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 09:00:22 +0200 Subject: [PATCH 28/99] fix --- packages/common/src/types/command/ActionDescriptor.ts | 3 +-- packages/cursorless-engine/src/actions/EditNew/EditNew.ts | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index 4a29b18a37a..a5922178bbc 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -1,4 +1,3 @@ -import { HighlightId } from "../.."; import { PartialDestinationDescriptor, PartialTargetDescriptor, @@ -176,7 +175,7 @@ export interface PartialReplaceActionDescriptor { export interface PartialHighlightActionDescriptor { name: "highlight"; - highlightId?: HighlightId; + highlightId?: string; target: PartialTargetDescriptor; } diff --git a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts index e51752522c4..be72b86dd9f 100644 --- a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts +++ b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts @@ -52,7 +52,9 @@ export class EditNew implements Action { */ let state: State = { destinations, - thatRanges: destinations.map(({ target }) => target.contentRange), + thatRanges: destinations.map( + ({ target }) => target.thatTarget.contentRange, + ), cursorRanges: new Array(destinations.length).fill( undefined, ) as undefined[], From c2e1ea38b5a89edd834ddd0237bb1c8ea5c60210 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 09:21:51 +0200 Subject: [PATCH 29/99] update --- .../src/actions/BringMoveSwap.ts | 2 +- .../recorded/actions/giveAfterDot.yml | 37 ------------------- .../recorded/actions/giveBeforeDot.yml | 37 ------------------- .../recorded/testCaseRecorder/takeHarp.yml | 2 +- 4 files changed, 2 insertions(+), 76 deletions(-) delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveAfterDot.yml delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveBeforeDot.yml diff --git a/packages/cursorless-engine/src/actions/BringMoveSwap.ts b/packages/cursorless-engine/src/actions/BringMoveSwap.ts index dd448117eff..bc6045d648a 100644 --- a/packages/cursorless-engine/src/actions/BringMoveSwap.ts +++ b/packages/cursorless-engine/src/actions/BringMoveSwap.ts @@ -378,7 +378,7 @@ export class Swap extends BringMoveSwap { await this.decorateThatMark(markEntries); - return { thatSelections: markEntries }; + return { thatSelections: markEntries, sourceSelections: [] }; } private getEditsSwap(targets1: Target[], targets2: Target[]): ExtendedEdit[] { diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveAfterDot.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveAfterDot.yml deleted file mode 100644 index 9b3da19638f..00000000000 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveAfterDot.yml +++ /dev/null @@ -1,37 +0,0 @@ -languageId: typescript -command: - version: 1 - spokenForm: give after dot - action: deselect - targets: - - type: primitive - position: after - mark: {type: decoratedSymbol, symbolColor: default, character: .} -initialState: - documentContents: a b.c - selections: - - anchor: {line: 0, character: 1} - active: {line: 0, character: 1} - - anchor: {line: 0, character: 3} - active: {line: 0, character: 3} - - anchor: {line: 0, character: 4} - active: {line: 0, character: 4} - marks: - default..: - start: {line: 0, character: 3} - end: {line: 0, character: 4} -finalState: - documentContents: a b.c - selections: - - anchor: {line: 0, character: 1} - active: {line: 0, character: 1} - - anchor: {line: 0, character: 3} - active: {line: 0, character: 3} - thatMark: - - type: PositionTarget - contentRange: - start: {line: 0, character: 4} - end: {line: 0, character: 4} - isReversed: false - hasExplicitRange: true -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: .}, selectionType: token, position: after, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveBeforeDot.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveBeforeDot.yml deleted file mode 100644 index 7bf8e70d4d5..00000000000 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveBeforeDot.yml +++ /dev/null @@ -1,37 +0,0 @@ -languageId: typescript -command: - version: 1 - spokenForm: give before dot - action: deselect - targets: - - type: primitive - position: before - mark: {type: decoratedSymbol, symbolColor: default, character: .} -initialState: - documentContents: a b.c - selections: - - anchor: {line: 0, character: 1} - active: {line: 0, character: 1} - - anchor: {line: 0, character: 3} - active: {line: 0, character: 3} - - anchor: {line: 0, character: 4} - active: {line: 0, character: 4} - marks: - default..: - start: {line: 0, character: 3} - end: {line: 0, character: 4} -finalState: - documentContents: a b.c - selections: - - anchor: {line: 0, character: 1} - active: {line: 0, character: 1} - - anchor: {line: 0, character: 4} - active: {line: 0, character: 4} - thatMark: - - type: PositionTarget - contentRange: - start: {line: 0, character: 3} - end: {line: 0, character: 3} - isReversed: false - hasExplicitRange: true -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: .}, selectionType: token, position: before, insideOutsideType: inside, modifier: {type: identity}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/testCaseRecorder/takeHarp.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/testCaseRecorder/takeHarp.yml index c6ee6e59852..03f59220770 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/testCaseRecorder/takeHarp.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/testCaseRecorder/takeHarp.yml @@ -1,6 +1,6 @@ languageId: plaintext command: - version: 6 + version: 5 spokenForm: take harp action: {name: setSelection} targets: From 69ccb777fcc2f804e57f3648cd5721daa974de7f Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 09:34:35 +0200 Subject: [PATCH 30/99] remove tests --- .../recorded/positions/chuckAfterWhale.yml | 22 ------------------- .../recorded/positions/chuckBeforeWhale.yml | 22 ------------------- 2 files changed, 44 deletions(-) delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckAfterWhale.yml delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckBeforeWhale.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckAfterWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckAfterWhale.yml deleted file mode 100644 index 11f62f65be5..00000000000 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckAfterWhale.yml +++ /dev/null @@ -1,22 +0,0 @@ -languageId: plaintext -command: - spokenForm: chuck after whale - version: 2 - targets: - - type: primitive - mark: {type: decoratedSymbol, symbolColor: default, character: w} - modifiers: - - {type: position, position: after} - usePrePhraseSnapshot: true - action: {name: remove} -initialState: - documentContents: hello world whatever - selections: - - anchor: {line: 0, character: 20} - active: {line: 0, character: 20} - marks: - default.w: - start: {line: 0, character: 6} - end: {line: 0, character: 11} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, modifiers: [{type: position, position: after}]}] -thrownError: {name: UnsupportedError} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckBeforeWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckBeforeWhale.yml deleted file mode 100644 index f81c2850464..00000000000 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckBeforeWhale.yml +++ /dev/null @@ -1,22 +0,0 @@ -languageId: plaintext -command: - spokenForm: chuck before whale - version: 2 - targets: - - type: primitive - mark: {type: decoratedSymbol, symbolColor: default, character: w} - modifiers: - - {type: position, position: before} - usePrePhraseSnapshot: true - action: {name: remove} -initialState: - documentContents: hello world whatever - selections: - - anchor: {line: 0, character: 20} - active: {line: 0, character: 20} - marks: - default.w: - start: {line: 0, character: 6} - end: {line: 0, character: 11} -fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, modifiers: [{type: position, position: before}]}] -thrownError: {name: UnsupportedError} From c43be2502abcf3663e3cd3c0f877f0789ffeb294 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 09:55:06 +0200 Subject: [PATCH 31/99] stuff --- .../src/types/command/ActionDescriptor.ts | 2 +- .../cursorless-engine/src/actions/Replace.ts | 31 ++++++++----- .../cursorless-engine/src/actions/Sort.ts | 5 ++- .../src/actions/actions.types.ts | 2 +- .../core/commandRunner/CommandRunnerImpl.ts | 6 +-- .../upgradeV5ToV6/upgradeV5ToV6.ts | 2 +- .../processTargets/targets/DestinationImpl.ts | 5 +++ .../targets/NotebookCellDestination.ts | 44 +++++++++++++++++++ .../targets/NotebookCellTarget.ts | 37 ++-------------- .../src/processTargets/targets/index.ts | 1 + .../src/typings/target.types.ts | 1 + .../src/util/getPartialTargetDescriptors.1.ts | 1 + .../recorded/positions/takeAfterWhale.yml | 26 ----------- .../recorded/positions/takeBeforeWhale.yml | 26 ----------- 14 files changed, 84 insertions(+), 105 deletions(-) create mode 100644 packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/takeAfterWhale.yml delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/takeBeforeWhale.yml diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index a5922178bbc..e276187ab49 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -170,7 +170,7 @@ export type ReplaceWith = string[] | { start: number }; export interface PartialReplaceActionDescriptor { name: "replace"; replaceWith: ReplaceWith; - target: PartialTargetDescriptor; + destination: PartialDestinationDescriptor; } export interface PartialHighlightActionDescriptor { diff --git a/packages/cursorless-engine/src/actions/Replace.ts b/packages/cursorless-engine/src/actions/Replace.ts index e7f1a858f34..a4b7a9b192d 100644 --- a/packages/cursorless-engine/src/actions/Replace.ts +++ b/packages/cursorless-engine/src/actions/Replace.ts @@ -3,7 +3,7 @@ import { flatten, zip } from "lodash"; import { RangeUpdater } from "../core/updateSelections/RangeUpdater"; import { performEditsAndUpdateSelections } from "../core/updateSelections/updateSelections"; import { ide } from "../singletons/ide.singleton"; -import { Target } from "../typings/target.types"; +import { Destination } from "../typings/target.types"; import { flashTargets, runForEachEditor } from "../util/targetUtils"; import { ActionReturnValue } from "./actions.types"; @@ -12,36 +12,43 @@ export default class Replace { this.run = this.run.bind(this); } - private getTexts(targets: Target[], replaceWith: ReplaceWith): string[] { + private getTexts( + destinations: Destination[], + replaceWith: ReplaceWith, + ): string[] { if (Array.isArray(replaceWith)) { // Broadcast single text to each target if (replaceWith.length === 1) { - return Array(targets.length).fill(replaceWith[0]); + return Array(destinations.length).fill(replaceWith[0]); } return replaceWith; } const numbers = []; - for (let i = 0; i < targets.length; ++i) { + for (let i = 0; i < destinations.length; ++i) { numbers[i] = (replaceWith.start + i).toString(); } return numbers; } async run( - targets: Target[], + destinations: Destination[], replaceWith: ReplaceWith, ): Promise { - await flashTargets(ide(), targets, FlashStyle.pendingModification0); + await flashTargets( + ide(), + destinations.map((d) => d.target), + FlashStyle.pendingModification0, + ); - const texts = this.getTexts(targets, replaceWith); + const texts = this.getTexts(destinations, replaceWith); - if (targets.length !== texts.length) { + if (destinations.length !== texts.length) { throw new Error("Targets and texts must have same length"); } - const edits = zip(targets, texts).map(([target, text]) => ({ - edit: target!.toDestination("to").constructChangeEdit(text!), - editor: target!.editor, + const edits = zip(destinations, texts).map(([destination, text]) => ({ + edit: destination!.constructChangeEdit(text!), + editor: destination!.editor, })); const thatMark = flatten( @@ -53,7 +60,7 @@ export default class Replace { this.rangeUpdater, ide().getEditableTextEditor(editor), edits.map(({ edit }) => edit), - [targets.map((target) => target.contentSelection)], + [destinations.map((destination) => destination.contentSelection)], ); return updatedSelections.map((selection) => ({ diff --git a/packages/cursorless-engine/src/actions/Sort.ts b/packages/cursorless-engine/src/actions/Sort.ts index 635fd57ff72..882dc04b96c 100644 --- a/packages/cursorless-engine/src/actions/Sort.ts +++ b/packages/cursorless-engine/src/actions/Sort.ts @@ -25,7 +25,10 @@ abstract class SortBase implements Action { const sortedTexts = this.sortTexts(unsortedTexts); - return this.actions.replace.run(sortedTargets, sortedTexts); + return this.actions.replace.run( + sortedTargets.map((target) => target.toDestination("to")), + sortedTexts, + ); } } diff --git a/packages/cursorless-engine/src/actions/actions.types.ts b/packages/cursorless-engine/src/actions/actions.types.ts index 5783861c0df..972f068fbde 100644 --- a/packages/cursorless-engine/src/actions/actions.types.ts +++ b/packages/cursorless-engine/src/actions/actions.types.ts @@ -133,7 +133,7 @@ export interface ActionRecord extends Record { }; replace: { run( - targets: Target[], + destinations: Destination[], replaceWith: ReplaceWith, ): Promise; }; diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index ef737a94fa2..f68c5204aca 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -149,12 +149,12 @@ export class CommandRunnerImpl implements CommandRunner { ); } case "replace": { - const targets = this.getTargets( + const destinations = this.getDestinations( inferenceContext, - partialActionDescriptor.target, + partialActionDescriptor.destination, ); return this.actions.replace.run( - targets, + destinations, partialActionDescriptor.replaceWith, ); } diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index a3754dc65d1..7da4d4eba10 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -101,7 +101,7 @@ function upgradeAction( return { name: action.name, replaceWith: action.args![0] as ReplaceWith, - target: upgradeTarget(targets[0]), + destination: targetToDestination(targets[0]), }; case "highlight": return { diff --git a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts index 5b3c05c31c1..9a4ef87b217 100644 --- a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts +++ b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts @@ -1,6 +1,7 @@ import { InsertionMode, Range, + Selection, TextEditor, UnsupportedError, } from "@cursorless/common"; @@ -52,6 +53,10 @@ export default class DestinationImpl implements Destination { : this.constructEditWithoutDelimiters(text); } + get contentSelection(): Selection { + return this.contentRange.toSelection(this.target.isReversed); + } + get editor(): TextEditor { return this.target.editor; } diff --git a/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts b/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts new file mode 100644 index 00000000000..fa6d006821c --- /dev/null +++ b/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts @@ -0,0 +1,44 @@ +import { + InsertionMode, + Range, + Selection, + TextEditor, +} from "@cursorless/common"; +import { EditWithRangeUpdater } from "../../typings/Types"; +import { Destination, EditNewActionType } from "../../typings/target.types"; +import NotebookCellTarget from "./NotebookCellTarget"; + +export default class NotebookCellDestination implements Destination { + constructor( + public target: NotebookCellTarget, + public insertionMode: InsertionMode, + ) {} + + get editor(): TextEditor { + return this.target.editor; + } + + get contentRange(): Range { + return this.target.contentRange; + } + + get contentSelection(): Selection { + return this.target.contentSelection; + } + + get insertionDelimiter(): string { + return this.target.insertionDelimiter; + } + + get isRaw(): boolean { + return this.target.isRaw; + } + + getEditNewActionType(): EditNewActionType { + throw new Error("Method not implemented."); + } + + constructChangeEdit(_text: string): EditWithRangeUpdater { + throw new Error("Method not implemented."); + } +} diff --git a/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts b/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts index e6f3369ed81..e96cbfab60d 100644 --- a/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts @@ -1,7 +1,7 @@ -import { InsertionMode, Range, TextEditor } from "@cursorless/common"; +import { InsertionMode } from "@cursorless/common"; import { BaseTarget, CommonTargetParameters } from "."; -import { EditWithRangeUpdater } from "../../typings/Types"; -import { Destination, EditNewActionType } from "../../typings/target.types"; +import { Destination } from "../../typings/target.types"; +import NotebookCellDestination from "./NotebookCellDestination"; export default class NotebookCellTarget extends BaseTarget { type = "NotebookCellTarget"; @@ -24,34 +24,3 @@ export default class NotebookCellTarget extends BaseTarget Date: Tue, 11 Jul 2023 10:13:38 +0200 Subject: [PATCH 32/99] stuff --- packages/cursorless-engine/src/core/inferFullTargets.ts | 2 +- .../recorded/compoundTargets/clearJustThisPastEndOfToken.yml | 3 --- .../recorded/compoundTargets/clearJustThisPastStartOfToken.yml | 3 --- .../fixtures/recorded/compoundTargets/clearPastEndOfToken.yml | 3 --- .../recorded/compoundTargets/clearPastStartOfToken.yml | 3 --- .../recorded/compoundTargets/clearThisPastEndOfToken.yml | 3 --- .../recorded/compoundTargets/clearThisPastStartOfToken.yml | 3 --- .../suite/fixtures/recorded/positions/chuckPastEndOfLine.yml | 3 --- 8 files changed, 1 insertion(+), 22 deletions(-) diff --git a/packages/cursorless-engine/src/core/inferFullTargets.ts b/packages/cursorless-engine/src/core/inferFullTargets.ts index 9ebff1980d1..5754c91384d 100644 --- a/packages/cursorless-engine/src/core/inferFullTargets.ts +++ b/packages/cursorless-engine/src/core/inferFullTargets.ts @@ -39,7 +39,7 @@ export function inferFullTargetDescriptor( case "list": return inferListTarget(target, previousTargets); case "range": - return inferRangeTarget(target, previousTargets); + return inferRangeTargetWithHoist(target, previousTargets); case "primitive": return inferPrimitiveTarget(target, previousTargets); case "implicit": diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearJustThisPastEndOfToken.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearJustThisPastEndOfToken.yml index 2d4657dcccd..4614fdd395e 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearJustThisPastEndOfToken.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearJustThisPastEndOfToken.yml @@ -31,6 +31,3 @@ finalState: - anchor: {line: 0, character: 8} active: {line: 0, character: 8} fullTargets: [{type: range, excludeAnchor: false, excludeActive: false, rangeType: continuous, anchor: {type: primitive, mark: {type: cursor}, modifiers: [{type: toRawSelection}]}, active: {type: primitive, mark: {type: cursor}, modifiers: [{type: containingScope, scopeType: {type: token}}], positionModifier: {type: position, position: end}}}] -ide: - messages: - - {type: warning, id: deprecatedPositionInference} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearJustThisPastStartOfToken.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearJustThisPastStartOfToken.yml index be7f945bd10..0cbded716ad 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearJustThisPastStartOfToken.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearJustThisPastStartOfToken.yml @@ -31,6 +31,3 @@ finalState: - anchor: {line: 0, character: 6} active: {line: 0, character: 6} fullTargets: [{type: range, excludeAnchor: false, excludeActive: false, rangeType: continuous, anchor: {type: primitive, mark: {type: cursor}, modifiers: [{type: toRawSelection}]}, active: {type: primitive, mark: {type: cursor}, modifiers: [{type: containingScope, scopeType: {type: token}}], positionModifier: {type: position, position: start}}}] -ide: - messages: - - {type: warning, id: deprecatedPositionInference} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearPastEndOfToken.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearPastEndOfToken.yml index 4d44f505202..fab37a1e398 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearPastEndOfToken.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearPastEndOfToken.yml @@ -21,6 +21,3 @@ finalState: - anchor: {line: 0, character: 8} active: {line: 0, character: 8} fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: cursorToken}, selectionType: token, position: after, modifier: {type: identity}, insideOutsideType: inside}}] -ide: - messages: - - {type: warning, id: deprecatedPositionInference} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearPastStartOfToken.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearPastStartOfToken.yml index 1090f47ea53..38db7ab4b03 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearPastStartOfToken.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearPastStartOfToken.yml @@ -21,6 +21,3 @@ finalState: - anchor: {line: 0, character: 6} active: {line: 0, character: 6} fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: cursorToken}, selectionType: token, position: before, modifier: {type: identity}, insideOutsideType: inside}}] -ide: - messages: - - {type: warning, id: deprecatedPositionInference} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearThisPastEndOfToken.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearThisPastEndOfToken.yml index cf1c0aef797..c84ec95bc76 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearThisPastEndOfToken.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearThisPastEndOfToken.yml @@ -29,6 +29,3 @@ finalState: - anchor: {line: 0, character: 6} active: {line: 0, character: 6} fullTargets: [{type: range, excludeAnchor: false, excludeActive: false, rangeType: continuous, anchor: {type: primitive, mark: {type: cursor}, modifiers: []}, active: {type: primitive, mark: {type: cursor}, modifiers: [{type: containingScope, scopeType: {type: token}}], positionModifier: {type: position, position: end}}}] -ide: - messages: - - {type: warning, id: deprecatedPositionInference} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearThisPastStartOfToken.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearThisPastStartOfToken.yml index 953682de652..4762249e672 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearThisPastStartOfToken.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/compoundTargets/clearThisPastStartOfToken.yml @@ -29,6 +29,3 @@ finalState: - anchor: {line: 0, character: 6} active: {line: 0, character: 6} fullTargets: [{type: range, excludeAnchor: false, excludeActive: false, rangeType: continuous, anchor: {type: primitive, mark: {type: cursor}, modifiers: []}, active: {type: primitive, mark: {type: cursor}, modifiers: [{type: containingScope, scopeType: {type: token}}], positionModifier: {type: position, position: start}}}] -ide: - messages: - - {type: warning, id: deprecatedPositionInference} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckPastEndOfLine.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckPastEndOfLine.yml index da9602b7425..e6a368963a3 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckPastEndOfLine.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/positions/chuckPastEndOfLine.yml @@ -21,6 +21,3 @@ finalState: - anchor: {line: 0, character: 2} active: {line: 0, character: 2} fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: outside}, end: {type: primitive, mark: {type: cursor}, selectionType: line, position: after, modifier: {type: identity}, insideOutsideType: inside}}] -ide: - messages: - - {type: warning, id: deprecatedPositionInference} From 1134171984faf2d84ef8b0da11334eaf9da2535d Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 10:30:49 +0200 Subject: [PATCH 33/99] Added vertical range target --- .../processTargets/TargetPipelineRunner.ts | 5 +-- .../targets/VerticalRangeTarget.ts | 31 +++++++++++++++++++ .../src/processTargets/targets/index.ts | 2 ++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 packages/cursorless-engine/src/processTargets/targets/VerticalRangeTarget.ts diff --git a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts index 1825f01b273..485ca6df7ff 100644 --- a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts @@ -17,7 +17,7 @@ import { ModifierStageFactory } from "./ModifierStageFactory"; import { MarkStage, ModifierStage } from "./PipelineStages.types"; import ImplicitStage from "./marks/ImplicitStage"; import { ContainingTokenIfUntypedEmptyStage } from "./modifiers/ConditionalModifierStages"; -import { PlainTarget } from "./targets"; +import { VerticalRangeTarget } from "./targets"; export class TargetPipelineRunner { constructor( @@ -356,10 +356,11 @@ function targetsToVerticalTarget( ); results.push( - new PlainTarget({ + new VerticalRangeTarget({ editor: anchorTarget.editor, isReversed: anchorTarget.isReversed, contentRange, + insertionDelimiter: anchorTarget.insertionDelimiter, }), ); diff --git a/packages/cursorless-engine/src/processTargets/targets/VerticalRangeTarget.ts b/packages/cursorless-engine/src/processTargets/targets/VerticalRangeTarget.ts new file mode 100644 index 00000000000..98e0eccc5cf --- /dev/null +++ b/packages/cursorless-engine/src/processTargets/targets/VerticalRangeTarget.ts @@ -0,0 +1,31 @@ +import { BaseTarget, CommonTargetParameters } from "."; + +interface VerticalRangeTargetParameters extends CommonTargetParameters { + readonly insertionDelimiter: string; +} + +/** + * A target that has no leading or trailing delimiters so it's removal range + * just consists of the content itself. Its insertion delimiter is given by argument. + */ +export default class VerticalRangeTarget extends BaseTarget { + type = "VerticalRangeTarget"; + isToken = false; + insertionDelimiter: string; + + constructor(parameters: VerticalRangeTargetParameters) { + super(parameters); + this.insertionDelimiter = parameters.insertionDelimiter; + } + + getLeadingDelimiterTarget = () => undefined; + getTrailingDelimiterTarget = () => undefined; + getRemovalRange = () => this.contentRange; + + protected getCloneParameters() { + return { + ...this.state, + insertionDelimiter: this.insertionDelimiter, + }; + } +} diff --git a/packages/cursorless-engine/src/processTargets/targets/index.ts b/packages/cursorless-engine/src/processTargets/targets/index.ts index 68fec06b05e..13b4244bc2b 100644 --- a/packages/cursorless-engine/src/processTargets/targets/index.ts +++ b/packages/cursorless-engine/src/processTargets/targets/index.ts @@ -11,6 +11,8 @@ export * from "./ParagraphTarget"; export { default as ParagraphTarget } from "./ParagraphTarget"; export * from "./PlainTarget"; export { default as PlainTarget } from "./PlainTarget"; +export * from "./VerticalRangeTarget"; +export { default as VerticalRangeTarget } from "./VerticalRangeTarget"; export * from "./DestinationImpl"; export { default as DestinationImpl } from "./DestinationImpl"; export * from "./RawSelectionTarget"; From 3f7b40d1808f52a70a98f1276fd0884a99e58f79 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 11:21:21 +0200 Subject: [PATCH 34/99] stuff --- .../EditNew/runInsertLineAfterTargets.ts | 6 +- .../processTargets/targets/DestinationImpl.ts | 60 ++++++++++++------- .../targets/NotebookCellDestination.ts | 4 ++ .../src/typings/target.types.ts | 1 + 4 files changed, 48 insertions(+), 23 deletions(-) diff --git a/packages/cursorless-engine/src/actions/EditNew/runInsertLineAfterTargets.ts b/packages/cursorless-engine/src/actions/EditNew/runInsertLineAfterTargets.ts index 4659896e6b6..b816d79df97 100644 --- a/packages/cursorless-engine/src/actions/EditNew/runInsertLineAfterTargets.ts +++ b/packages/cursorless-engine/src/actions/EditNew/runInsertLineAfterTargets.ts @@ -60,9 +60,9 @@ export async function runInsertLineAfterTargets( return { destinations: state.destinations.map((destination, index) => - destination.target - .withContentRange(updatedTargetRanges[index]) - .toDestination(destination.insertionMode), + destination.withTarget( + destination.target.withContentRange(updatedTargetRanges[index]), + ), ), thatRanges: updatedThatRanges, cursorRanges, diff --git a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts index 9a4ef87b217..64c79f8418d 100644 --- a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts +++ b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts @@ -18,14 +18,50 @@ export default class DestinationImpl implements Destination { private readonly isBefore: boolean; private readonly indentationString: string; - constructor(public target: Target, public insertionMode: InsertionMode) { + constructor( + public target: Target, + public insertionMode: InsertionMode, + indentationString?: string, + ) { this.contentRange = getContentRange(target.contentRange, insertionMode); this.isBefore = insertionMode === "before"; // It's only considered a line if the delimiter is only new line symbols this.isLineDelimiter = /^(\n)+$/.test(target.insertionDelimiter); - this.indentationString = this.isLineDelimiter - ? getIndentationString(target.editor, target.contentRange) - : ""; + this.indentationString = + indentationString ?? this.isLineDelimiter + ? getIndentationString(target.editor, target.contentRange) + : ""; + } + + get contentSelection(): Selection { + return this.contentRange.toSelection(this.target.isReversed); + } + + get editor(): TextEditor { + return this.target.editor; + } + + get insertionDelimiter(): string { + return this.target.insertionDelimiter; + } + + get isRaw(): boolean { + return this.target.isRaw; + } + + /** + * Creates a new destination with the given target while preserving insertion + * mode and indentation string from this destination. This is important + * because our "edit new" code updates the content range of the target when + * multiple edits are performed in the same document, but we want to insert + * the original indentation. + */ + withTarget(target: Target): Destination { + return new DestinationImpl( + target, + this.insertionMode, + this.indentationString, + ); } getEditNewActionType(): EditNewActionType { @@ -53,22 +89,6 @@ export default class DestinationImpl implements Destination { : this.constructEditWithoutDelimiters(text); } - get contentSelection(): Selection { - return this.contentRange.toSelection(this.target.isReversed); - } - - get editor(): TextEditor { - return this.target.editor; - } - - get insertionDelimiter(): string { - return this.target.insertionDelimiter; - } - - get isRaw(): boolean { - return this.target.isRaw; - } - private constructEditWithDelimiters(text: string): EditWithRangeUpdater { const range = this.getEditRange(); const editText = this.getEditText(text); diff --git a/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts b/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts index fa6d006821c..fe909f44e0e 100644 --- a/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts +++ b/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts @@ -34,6 +34,10 @@ export default class NotebookCellDestination implements Destination { return this.target.isRaw; } + withTarget(target: NotebookCellTarget): Destination { + return new NotebookCellDestination(target, this.insertionMode); + } + getEditNewActionType(): EditNewActionType { throw new Error("Method not implemented."); } diff --git a/packages/cursorless-engine/src/typings/target.types.ts b/packages/cursorless-engine/src/typings/target.types.ts index 515020d4571..101c12f54cb 100644 --- a/packages/cursorless-engine/src/typings/target.types.ts +++ b/packages/cursorless-engine/src/typings/target.types.ts @@ -170,6 +170,7 @@ export interface Destination { readonly contentSelection: Selection; readonly isRaw: boolean; readonly insertionDelimiter: string; + withTarget(target: Target): Destination; getEditNewActionType(): EditNewActionType; /** Constructs change/insertion edit. Adds delimiter before/after if needed */ constructChangeEdit(text: string): EditWithRangeUpdater; From 3b8917bb877e78f170857e8d400250798713c87b Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 11:26:52 +0200 Subject: [PATCH 35/99] remove --- .../inference/takeAfterVestPastAir.yml | 42 ------------------ .../inference/takeAfterVestPastBeforeAir.yml | 43 ------------------- .../inference/takeVestPastBeforeAir.yml | 42 ------------------ 3 files changed, 127 deletions(-) delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/inference/takeAfterVestPastAir.yml delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/inference/takeAfterVestPastBeforeAir.yml delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/inference/takeVestPastBeforeAir.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/inference/takeAfterVestPastAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/inference/takeAfterVestPastAir.yml deleted file mode 100644 index 3c387c26e70..00000000000 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/inference/takeAfterVestPastAir.yml +++ /dev/null @@ -1,42 +0,0 @@ -languageId: typescript -command: - version: 1 - spokenForm: take after vest past air - action: setSelection - targets: - - type: range - start: - type: primitive - position: after - mark: {type: decoratedSymbol, symbolColor: default, character: v} - end: - type: primitive - mark: {type: decoratedSymbol, symbolColor: default, character: a} - excludeStart: false - excludeEnd: false -initialState: - documentContents: | - - const value = "Hello world"; - - const value = "Hello world"; - selections: - - anchor: {line: 0, character: 0} - active: {line: 0, character: 0} - marks: - default.v: - start: {line: 1, character: 6} - end: {line: 1, character: 11} - default.a: - start: {line: 3, character: 6} - end: {line: 3, character: 11} -finalState: - documentContents: | - - const value = "Hello world"; - - const value = "Hello world"; - selections: - - anchor: {line: 1, character: 11} - active: {line: 3, character: 11} -fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: v}, selectionType: token, position: after, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/inference/takeAfterVestPastBeforeAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/inference/takeAfterVestPastBeforeAir.yml deleted file mode 100644 index 392dae5407c..00000000000 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/inference/takeAfterVestPastBeforeAir.yml +++ /dev/null @@ -1,43 +0,0 @@ -languageId: typescript -command: - version: 1 - spokenForm: take after vest past before air - action: setSelection - targets: - - type: range - start: - type: primitive - position: after - mark: {type: decoratedSymbol, symbolColor: default, character: v} - end: - type: primitive - position: before - mark: {type: decoratedSymbol, symbolColor: default, character: a} - excludeStart: false - excludeEnd: false -initialState: - documentContents: | - - const value = "Hello world"; - - const value = "Hello world"; - selections: - - anchor: {line: 0, character: 0} - active: {line: 0, character: 0} - marks: - default.v: - start: {line: 1, character: 6} - end: {line: 1, character: 11} - default.a: - start: {line: 3, character: 6} - end: {line: 3, character: 11} -finalState: - documentContents: | - - const value = "Hello world"; - - const value = "Hello world"; - selections: - - anchor: {line: 1, character: 11} - active: {line: 3, character: 6} -fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: v}, selectionType: token, position: after, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: token, position: before, modifier: {type: identity}, insideOutsideType: inside}}] diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/inference/takeVestPastBeforeAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/inference/takeVestPastBeforeAir.yml deleted file mode 100644 index c4243a7715b..00000000000 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/inference/takeVestPastBeforeAir.yml +++ /dev/null @@ -1,42 +0,0 @@ -languageId: typescript -command: - version: 1 - spokenForm: take vest past before air - action: setSelection - targets: - - type: range - start: - type: primitive - mark: {type: decoratedSymbol, symbolColor: default, character: v} - end: - type: primitive - position: before - mark: {type: decoratedSymbol, symbolColor: default, character: a} - excludeStart: false - excludeEnd: false -initialState: - documentContents: | - - const value = "Hello world"; - - const value = "Hello world"; - selections: - - anchor: {line: 0, character: 0} - active: {line: 0, character: 0} - marks: - default.v: - start: {line: 1, character: 6} - end: {line: 1, character: 11} - default.a: - start: {line: 3, character: 6} - end: {line: 3, character: 11} -finalState: - documentContents: | - - const value = "Hello world"; - - const value = "Hello world"; - selections: - - anchor: {line: 1, character: 6} - active: {line: 3, character: 6} -fullTargets: [{type: range, excludeStart: false, excludeEnd: false, start: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: v}, selectionType: token, position: contents, modifier: {type: identity}, insideOutsideType: inside}, end: {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, selectionType: token, position: before, modifier: {type: identity}, insideOutsideType: inside}}] From bbf01f32e4520579160c4294f962ec0f6f81de06 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 11:33:52 +0200 Subject: [PATCH 36/99] blob --- packages/common/src/types/command/ActionDescriptor.ts | 2 +- .../src/core/commandRunner/CommandRunnerImpl.ts | 2 +- .../core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts | 2 +- .../cursorless-engine/src/util/getPartialTargetDescriptors.1.ts | 2 +- .../cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index e276187ab49..9eb8041b036 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -84,7 +84,7 @@ export interface PartialBringMoveActionDescriptor { export interface PartialCallActionDescriptor { name: "callAsFunction"; - callees: PartialTargetDescriptor; + callee: PartialTargetDescriptor; args: PartialTargetDescriptor; } diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index f68c5204aca..404480993a8 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -98,7 +98,7 @@ export class CommandRunnerImpl implements CommandRunner { case "callAsFunction": { const callees = this.getTargets( inferenceContext, - partialActionDescriptor.callees, + partialActionDescriptor.callee, ); const args = this.getTargets( inferenceContext, diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index 7da4d4eba10..5ede28b76ea 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -56,7 +56,7 @@ function upgradeAction( case "callAsFunction": return { name: action.name, - callees: upgradeTarget(targets[0]), + callee: upgradeTarget(targets[0]), args: upgradeTarget(targets[1]), }; case "pasteFromClipboard": diff --git a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts index ac7b8ce5729..517f47724e3 100644 --- a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts +++ b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts @@ -11,7 +11,7 @@ export function getPartialTargetDescriptors( ): PartialTargetDescriptor[] { switch (command.action.name) { case "callAsFunction": - return [command.action.callees, command.action.args]; + return [command.action.callee, command.action.args]; case "replaceWithTarget": case "moveToTarget": return [ diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index 6a6a91b457c..b5ce475638f 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -217,7 +217,7 @@ export default class KeyboardCommandsTargeted { returnValue = await executeCursorlessCommand({ action: { name: action, - callees: target, + callee: target, args: { type: "implicit" }, }, }); From ea300bc74d6d2c8eb05ee4002e24e9cb1b8bb1c9 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 12:03:02 +0200 Subject: [PATCH 37/99] Update the test case recorder tests --- .../recorded/testCaseRecorder/takeHarp.yml | 9 +++-- .../src/suite/testCaseRecorder.vscode.test.ts | 39 +++++++++++-------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/testCaseRecorder/takeHarp.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/testCaseRecorder/takeHarp.yml index 03f59220770..6d55e46b538 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/testCaseRecorder/takeHarp.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/testCaseRecorder/takeHarp.yml @@ -1,10 +1,11 @@ languageId: plaintext command: - version: 5 + version: 6 spokenForm: take harp - action: {name: setSelection} - targets: - - type: primitive + action: + name: setSelection + target: + type: primitive mark: {type: decoratedSymbol, symbolColor: default, character: h} usePrePhraseSnapshot: false initialState: diff --git a/packages/cursorless-vscode-e2e/src/suite/testCaseRecorder.vscode.test.ts b/packages/cursorless-vscode-e2e/src/suite/testCaseRecorder.vscode.test.ts index 13bb4bed675..d4fa52b4220 100644 --- a/packages/cursorless-vscode-e2e/src/suite/testCaseRecorder.vscode.test.ts +++ b/packages/cursorless-vscode-e2e/src/suite/testCaseRecorder.vscode.test.ts @@ -1,8 +1,9 @@ import { - ActionType, getFixturePath, getRecordedTestsDirPath, HatTokenMap, + LATEST_VERSION, + SimpleActionName, } from "@cursorless/common"; import { getCursorlessApi, @@ -12,12 +13,18 @@ import { import { assert } from "chai"; import * as crypto from "crypto"; import { mkdir, readdir, readFile, rm } from "fs/promises"; -import * as path from "path"; import * as os from "os"; +import * as path from "path"; import { basename } from "path"; import * as vscode from "vscode"; import { endToEndTestSetup } from "../endToEndTestSetup"; +/* + * All tests in this file are running against the latest version of the command + * and needs to be manually updated on every command of migration. + * This includes the file: fixtures/recorded/testCaseRecorder/takeHarp + */ + // Ensure that the test case recorder works suite("testCaseRecorder", async function () { endToEndTestSetup(this); @@ -70,18 +77,18 @@ async function testCaseRecorderGracefulError() { try { await runCursorlessCommand({ - version: 5, - action: { name: "badActionName" as ActionType }, - targets: [ - { + version: LATEST_VERSION, + spokenForm: "bad command", + usePrePhraseSnapshot: false, + action: { + name: "badActionName" as SimpleActionName, + target: { type: "primitive", mark: { type: "cursor", }, }, - ], - usePrePhraseSnapshot: false, - spokenForm: "bad command", + }, }); } catch (err) { // Ignore error @@ -129,10 +136,12 @@ async function stopRecording() { async function takeHarp() { await runCursorlessCommand({ - version: 4, - action: { name: "setSelection" }, - targets: [ - { + version: LATEST_VERSION, + spokenForm: "take harp", + usePrePhraseSnapshot: false, + action: { + name: "setSelection", + target: { type: "primitive", mark: { type: "decoratedSymbol", @@ -140,9 +149,7 @@ async function takeHarp() { character: "h", }, }, - ], - usePrePhraseSnapshot: false, - spokenForm: "take harp", + }, }); } From 5accb8832585a9211c274427e834318fa05a5093 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 12:06:55 +0200 Subject: [PATCH 38/99] Updated type destination to primitive destination --- .../src/types/command/PartialTargetDescriptor.types.ts | 2 +- .../upgradeV5ToV6/upgradeV5ToV6.ts | 10 +++++----- .../src/keyboard/KeyboardCommandsTargeted.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index bf7891ac688..6c1f0c983ad 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -383,7 +383,7 @@ export type PartialTargetDescriptor = | ImplicitTargetDescriptor; export interface PartialPrimitiveDestinationDescriptor { - type: "destination"; + type: "primitiveDestination"; insertionMode: InsertionMode; target: PartialTargetDescriptor; } diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index 5ede28b76ea..a6b2128857f 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -192,13 +192,13 @@ function listTargetToDestination( const insertionMode = getInsertionMode(element); if (insertionMode != null || destinations.length === 0) { destinations.push({ - type: "destination", + type: "primitiveDestination", insertionMode: insertionMode ?? "to", target: upgradeTarget(element), }); } else { destinations.push({ - type: "destination", + type: "primitiveDestination", insertionMode: destinations[destinations.length - 1].insertionMode, target: upgradeTarget(element), }); @@ -217,7 +217,7 @@ function rangeTargetToDestination( target: PartialRangeTargetDescriptorV5, ): PartialPrimitiveDestinationDescriptor { return { - type: "destination", + type: "primitiveDestination", insertionMode: getInsertionMode(target.anchor) ?? "to", target: upgradeRangeTarget(target), }; @@ -227,7 +227,7 @@ function primitiveTargetToDestination( target: PartialPrimitiveTargetDescriptorV5, ): PartialPrimitiveDestinationDescriptor { return { - type: "destination", + type: "primitiveDestination", insertionMode: getInsertionMode(target) ?? "to", target: upgradePrimitiveTarget(target), }; @@ -237,7 +237,7 @@ function implicitTargetToDestination( target: ImplicitTargetDescriptor, ): PartialPrimitiveDestinationDescriptor { return { - type: "destination", + type: "primitiveDestination", insertionMode: "to", target, }; diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index b5ce475638f..618c6fa23b3 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -300,7 +300,7 @@ function toDestination( target: PartialPrimitiveTargetDescriptor | ImplicitTargetDescriptor, ): PartialDestinationDescriptor { return { - type: "destination", + type: "primitiveDestination", insertionMode: "to", target, }; From 6c95c6d7da735a3f12e0976a17326e86a44109fa Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 12:09:04 +0200 Subject: [PATCH 39/99] fix --- .../common/src/types/command/PartialTargetDescriptor.types.ts | 2 +- .../src/core/commandRunner/CommandRunnerImpl.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 6c1f0c983ad..172171e7efe 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -391,7 +391,7 @@ export interface PartialPrimitiveDestinationDescriptor { export interface PartialListDestinationDescriptor { type: "destinationList"; destinations: { - type: "destination"; + type: "primitiveDestination"; insertionMode: InsertionMode; target: | PartialPrimitiveTargetDescriptor diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 404480993a8..498e563a083 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -238,7 +238,7 @@ export class CommandRunnerImpl implements CommandRunner { actionPrePositionStages?: ModifierStage[], actionFinalStages?: ModifierStage[], ) { - if (partialDestinationDescriptor.type === "destination") { + if (partialDestinationDescriptor.type === "primitiveDestination") { return this.getDestinationTargetsFromPrimitive( inferenceContext, partialDestinationDescriptor, From 2bef7b38d35e4f8be188b36e3e78f6ded66db9f9 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 12:17:25 +0200 Subject: [PATCH 40/99] cleanup --- .../src/suite/testCaseRecorder.vscode.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-vscode-e2e/src/suite/testCaseRecorder.vscode.test.ts b/packages/cursorless-vscode-e2e/src/suite/testCaseRecorder.vscode.test.ts index d4fa52b4220..b88d9f5c84f 100644 --- a/packages/cursorless-vscode-e2e/src/suite/testCaseRecorder.vscode.test.ts +++ b/packages/cursorless-vscode-e2e/src/suite/testCaseRecorder.vscode.test.ts @@ -21,7 +21,7 @@ import { endToEndTestSetup } from "../endToEndTestSetup"; /* * All tests in this file are running against the latest version of the command - * and needs to be manually updated on every command of migration. + * and needs to be manually updated on every command migration. * This includes the file: fixtures/recorded/testCaseRecorder/takeHarp */ From 379736ab97666764ba0ae8d94e9de90c88129da9 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 13:10:21 +0200 Subject: [PATCH 41/99] updated test --- packages/cursorless-engine/src/actions/BringMoveSwap.ts | 2 +- .../bringPointAndHarpToEndOfThisAndEndOfWhaleTakeWhale.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cursorless-engine/src/actions/BringMoveSwap.ts b/packages/cursorless-engine/src/actions/BringMoveSwap.ts index bc6045d648a..3eacdefbbd9 100644 --- a/packages/cursorless-engine/src/actions/BringMoveSwap.ts +++ b/packages/cursorless-engine/src/actions/BringMoveSwap.ts @@ -114,7 +114,7 @@ abstract class BringMoveSwap { // Prevent multiple instances of the same expanded source. if (!usedSources.includes(source)) { usedSources.push(source); - if (this.type !== "move") { + if (this.type === "bring") { results.push({ edit: source .toDestination("to") diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/hatTokenMap/bringPointAndHarpToEndOfThisAndEndOfWhaleTakeWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/hatTokenMap/bringPointAndHarpToEndOfThisAndEndOfWhaleTakeWhale.yml index 0a88a0f36fc..01f52abab03 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/hatTokenMap/bringPointAndHarpToEndOfThisAndEndOfWhaleTakeWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/hatTokenMap/bringPointAndHarpToEndOfThisAndEndOfWhaleTakeWhale.yml @@ -50,5 +50,5 @@ finalState: end: {line: 0, character: 5} default.w: start: {line: 0, character: 7} - end: {line: 0, character: 12} + end: {line: 0, character: 18} fullTargets: [{type: list, elements: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: .}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: h}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}}]}, {type: list, elements: [{type: primitive, mark: {type: cursor}, selectionType: token, position: after, insideOutsideType: inside, modifier: {type: identity}}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, selectionType: token, position: after, insideOutsideType: inside, modifier: {type: identity}}]}] From 2d3c828bd08f3b2c32221e1fce1f029ae48b62b1 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 13:24:39 +0200 Subject: [PATCH 42/99] fix --- .../src/actions/InsertSnippet.ts | 2 +- .../src/actions/actions.types.ts | 2 +- .../core/commandRunner/CommandRunnerImpl.ts | 26 ++++--------------- .../processTargets/TargetPipelineRunner.ts | 9 +------ 4 files changed, 8 insertions(+), 31 deletions(-) diff --git a/packages/cursorless-engine/src/actions/InsertSnippet.ts b/packages/cursorless-engine/src/actions/InsertSnippet.ts index 706f08221bf..bed83809318 100644 --- a/packages/cursorless-engine/src/actions/InsertSnippet.ts +++ b/packages/cursorless-engine/src/actions/InsertSnippet.ts @@ -38,7 +38,7 @@ export default class InsertSnippet { this.run = this.run.bind(this); } - getPrePositionStages(snippetDescription: InsertSnippetArg) { + getFinalStages(snippetDescription: InsertSnippetArg) { const defaultScopeTypes = this.getScopeTypes(snippetDescription); return defaultScopeTypes.length === 0 diff --git a/packages/cursorless-engine/src/actions/actions.types.ts b/packages/cursorless-engine/src/actions/actions.types.ts index 972f068fbde..01b6b65b8ff 100644 --- a/packages/cursorless-engine/src/actions/actions.types.ts +++ b/packages/cursorless-engine/src/actions/actions.types.ts @@ -111,7 +111,7 @@ export interface ActionRecord extends Record { destinations: Destination[], snippetDescription: InsertSnippetArg, ): Promise; - getPrePositionStages(snippetDescription: InsertSnippetArg): ModifierStage[]; + getFinalStages(snippetDescription: InsertSnippetArg): ModifierStage[]; }; wrapWithSnippet: { run( diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 498e563a083..be73a5565f2 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -121,7 +121,6 @@ export class CommandRunnerImpl implements CommandRunner { const targets = this.getTargets( inferenceContext, partialActionDescriptor.target, - undefined, this.actions.rewrapWithPairedDelimiter.getFinalStages(), ); return this.actions.rewrapWithPairedDelimiter.run( @@ -182,7 +181,7 @@ export class CommandRunnerImpl implements CommandRunner { const destinations = this.getDestinations( inferenceContext, partialActionDescriptor.destination, - this.actions.insertSnippet.getPrePositionStages( + this.actions.insertSnippet.getFinalStages( partialActionDescriptor.snippetDescription, ), ); @@ -195,7 +194,6 @@ export class CommandRunnerImpl implements CommandRunner { const targets = this.getTargets( inferenceContext, partialActionDescriptor.target, - undefined, this.actions.wrapWithSnippet.getFinalStages( partialActionDescriptor.snippetDescription, ), @@ -210,7 +208,6 @@ export class CommandRunnerImpl implements CommandRunner { const targets = this.getTargets( inferenceContext, partialActionDescriptor.target, - undefined, action.getFinalStages?.(), ); return action.run(targets); @@ -221,28 +218,21 @@ export class CommandRunnerImpl implements CommandRunner { private getTargets( inferenceContext: InferenceContext, partialTargetsDescriptor: PartialTargetDescriptor, - actionPrePositionStages?: ModifierStage[], actionFinalStages?: ModifierStage[], ) { const targetDescriptor = inferenceContext.run(partialTargetsDescriptor); - return this.pipelineRunner.run( - targetDescriptor, - actionPrePositionStages, - actionFinalStages, - ); + return this.pipelineRunner.run(targetDescriptor, actionFinalStages); } private getDestinations( inferenceContext: InferenceContext, partialDestinationDescriptor: PartialDestinationDescriptor, - actionPrePositionStages?: ModifierStage[], - actionFinalStages?: ModifierStage[], + actionFinalStages: ModifierStage[] = [], ) { if (partialDestinationDescriptor.type === "primitiveDestination") { return this.getDestinationTargetsFromPrimitive( inferenceContext, partialDestinationDescriptor, - actionPrePositionStages, actionFinalStages, ); } else { @@ -250,7 +240,6 @@ export class CommandRunnerImpl implements CommandRunner { this.getDestinationTargetsFromPrimitive( inferenceContext, destination, - actionPrePositionStages, actionFinalStages, ), ); @@ -260,19 +249,14 @@ export class CommandRunnerImpl implements CommandRunner { private getDestinationTargetsFromPrimitive( inferenceContext: InferenceContext, partialDestinationDescriptor: PartialPrimitiveDestinationDescriptor, - actionPrePositionStages?: ModifierStage[], - actionFinalStages?: ModifierStage[], + actionFinalStages: ModifierStage[], ) { const destinationTargetDescriptor = inferenceContext.run( partialDestinationDescriptor.target, ); return this.pipelineRunner - .run( - destinationTargetDescriptor, - actionPrePositionStages, - actionFinalStages, - ) + .run(destinationTargetDescriptor, actionFinalStages) .map((target) => target.toDestination(partialDestinationDescriptor.insertionMode), ); diff --git a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts index 485ca6df7ff..72a4ceee799 100644 --- a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts @@ -39,16 +39,11 @@ export class TargetPipelineRunner { * document containing it, and potentially rich context information such as * how to remove the target */ - run( - target: TargetDescriptor, - actionPrePositionStages?: ModifierStage[], - actionFinalStages?: ModifierStage[], - ): Target[] { + run(target: TargetDescriptor, actionFinalStages?: ModifierStage[]): Target[] { return new TargetPipeline( this.modifierStageFactory, this.markStageFactory, target, - actionPrePositionStages ?? [], actionFinalStages ?? [], ).run(); } @@ -59,7 +54,6 @@ class TargetPipeline { private modifierStageFactory: ModifierStageFactory, private markStageFactory: MarkStageFactory, private target: TargetDescriptor, - private actionPrePositionStages: ModifierStage[], private actionFinalStages: ModifierStage[], ) {} @@ -223,7 +217,6 @@ class TargetPipeline { */ const modifierStages = [ ...nonModifierStages, - ...this.actionPrePositionStages, ...this.actionFinalStages, // This performs auto-expansion to token when you say eg "take this" with an From 1fbb31c66d7a450dce88864f08be1e9e503622cf Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 13:38:05 +0200 Subject: [PATCH 43/99] fix --- .../commandVersionUpgrades/canonicalizeAndValidateCommand.ts | 2 +- .../commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts index 0d05c9f23ed..0dd6fefb634 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts @@ -47,7 +47,7 @@ export function canonicalizeAndValidateCommand( action: { ...action, name: actionName, - } as PartialActionDescriptor, // TODO: + } as PartialActionDescriptor, usePrePhraseSnapshot, }; } diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index a6b2128857f..bb7cffe1557 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -23,6 +23,7 @@ import { PartialTargetDescriptorV5, PositionModifierV5, ReplaceWith, + SimpleActionName, WrapWithSnippetArg, } from "@cursorless/common"; @@ -111,9 +112,9 @@ function upgradeAction( }; default: return { - name: action.name, + name: action.name as SimpleActionName, target: upgradeTarget(targets[0]), - } as PartialActionDescriptor; // TODO: + }; } } From 41f25d6655c3c3d5865b1994b217ea6154f7128b Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 13:42:32 +0200 Subject: [PATCH 44/99] cleanup --- .../src/core/commandRunner/CommandRunnerImpl.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index be73a5565f2..c474886ef5f 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -229,13 +229,7 @@ export class CommandRunnerImpl implements CommandRunner { partialDestinationDescriptor: PartialDestinationDescriptor, actionFinalStages: ModifierStage[] = [], ) { - if (partialDestinationDescriptor.type === "primitiveDestination") { - return this.getDestinationTargetsFromPrimitive( - inferenceContext, - partialDestinationDescriptor, - actionFinalStages, - ); - } else { + if (partialDestinationDescriptor.type === "destinationList") { return partialDestinationDescriptor.destinations.flatMap((destination) => this.getDestinationTargetsFromPrimitive( inferenceContext, @@ -244,6 +238,11 @@ export class CommandRunnerImpl implements CommandRunner { ), ); } + return this.getDestinationTargetsFromPrimitive( + inferenceContext, + partialDestinationDescriptor, + actionFinalStages, + ); } private getDestinationTargetsFromPrimitive( From b16529990d9f201c0366b4cc7b7d8d0fdff71be4 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 13:46:52 +0200 Subject: [PATCH 45/99] renamed file --- .../commandVersionUpgrades/canonicalizeAndValidateCommand.ts | 2 +- .../src/scripts/transformRecordedTests/checkMarks.ts | 2 +- packages/cursorless-engine/src/testCaseRecorder/TestCase.ts | 2 +- ...ialTargetDescriptors.1.ts => getPartialTargetDescriptors.ts} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename packages/cursorless-engine/src/util/{getPartialTargetDescriptors.1.ts => getPartialTargetDescriptors.ts} (100%) diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts index 0dd6fefb634..613b22fae03 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts @@ -11,7 +11,7 @@ import { PartialTargetDescriptor, SimpleScopeTypeType, } from "@cursorless/common"; -import { getPartialTargetDescriptors } from "../../util/getPartialTargetDescriptors.1"; +import { getPartialTargetDescriptors } from "../../util/getPartialTargetDescriptors"; import { getPartialPrimitiveTargets } from "../../util/getPrimitiveTargets"; import canonicalizeActionName from "./canonicalizeActionName"; import canonicalizeTargets from "./canonicalizeTargets"; diff --git a/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts b/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts index c721645f82f..c0a5855c69a 100644 --- a/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts +++ b/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts @@ -3,7 +3,7 @@ import { uniq } from "lodash"; import { injectIde } from "../../singletons/ide.singleton"; import tokenGraphemeSplitter from "../../singletons/tokenGraphemeSplitter.singleton"; import { extractTargetKeys } from "../../testUtil/extractTargetKeys"; -import { getPartialTargetDescriptors } from "../../util/getPartialTargetDescriptors.1"; +import { getPartialTargetDescriptors } from "../../util/getPartialTargetDescriptors"; import { upgrade } from "./transformations/upgrade"; import assert = require("assert"); diff --git a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts index 6d99307caeb..73b09d9e3f8 100644 --- a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts +++ b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts @@ -21,7 +21,7 @@ import { StoredTargetMap } from ".."; import { ide } from "../singletons/ide.singleton"; import { extractTargetKeys } from "../testUtil/extractTargetKeys"; import { takeSnapshot } from "../testUtil/takeSnapshot"; -import { getPartialTargetDescriptors as getPartialTargetDescriptors } from "../util/getPartialTargetDescriptors.1"; +import { getPartialTargetDescriptors as getPartialTargetDescriptors } from "../util/getPartialTargetDescriptors"; export class TestCase { private languageId: string; diff --git a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts similarity index 100% rename from packages/cursorless-engine/src/util/getPartialTargetDescriptors.1.ts rename to packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts From 6d6ee1b6373c85329ff020b1fd593e9095cc13f2 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 11 Jul 2023 13:50:28 +0100 Subject: [PATCH 46/99] jsdocs / renames --- .../src/types/command/ActionDescriptor.ts | 27 ++++++++++++++----- .../core/commandRunner/CommandRunnerImpl.ts | 2 +- .../upgradeV5ToV6/upgradeV5ToV6.ts | 2 +- .../src/util/getPartialTargetDescriptors.ts | 2 +- .../src/keyboard/KeyboardCommandsTargeted.ts | 2 +- 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index 9eb8041b036..a41db98cb33 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -71,6 +71,9 @@ export const actionNames = [ export type SimpleActionName = (typeof simpleActionNames)[number]; export type ActionType = (typeof actionNames)[number]; +/** + * A simple action takes only a single target and no other arguments. + */ export interface PartialSimpleActionDescriptor { name: SimpleActionName; target: PartialTargetDescriptor; @@ -84,8 +87,16 @@ export interface PartialBringMoveActionDescriptor { export interface PartialCallActionDescriptor { name: "callAsFunction"; + + /** + * The target to use as the function to be called. + */ callee: PartialTargetDescriptor; - args: PartialTargetDescriptor; + + /** + * The target to wrap in a function call. + */ + argument: PartialTargetDescriptor; } export interface PartialSwapActionDescriptor { @@ -112,18 +123,18 @@ export interface PartialGenerateSnippetActionDescriptor { target: PartialTargetDescriptor; } -interface NamedSnippetArg { +interface NamedInsertSnippetArg { type: "named"; name: string; substitutions?: Record; } -interface CustomSnippetArg { +interface CustomInsertSnippetArg { type: "custom"; body: string; scopeType?: ScopeType; substitutions?: Record; } -export type InsertSnippetArg = NamedSnippetArg | CustomSnippetArg; +export type InsertSnippetArg = NamedInsertSnippetArg | CustomInsertSnippetArg; export interface PartialInsertSnippetActionDescriptor { name: "insertSnippet"; @@ -131,18 +142,20 @@ export interface PartialInsertSnippetActionDescriptor { destination: PartialDestinationDescriptor; } -interface NamedSnippetArg { +interface NamedWrapWithSnippetArg { type: "named"; name: string; variableName: string; } -interface CustomSnippetArg { +interface CustomWrapWithSnippetArg { type: "custom"; body: string; variableName?: string; scopeType?: ScopeType; } -export type WrapWithSnippetArg = NamedSnippetArg | CustomSnippetArg; +export type WrapWithSnippetArg = + | NamedWrapWithSnippetArg + | CustomWrapWithSnippetArg; export interface PartialWrapSnippetActionDescriptor { name: "wrapWithSnippet"; diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index c474886ef5f..558c7cb5167 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -102,7 +102,7 @@ export class CommandRunnerImpl implements CommandRunner { ); const args = this.getTargets( inferenceContext, - partialActionDescriptor.args, + partialActionDescriptor.argument, ); return this.actions.callAsFunction.run(callees, args); } diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index bb7cffe1557..cba06069778 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -58,7 +58,7 @@ function upgradeAction( return { name: action.name, callee: upgradeTarget(targets[0]), - args: upgradeTarget(targets[1]), + argument: upgradeTarget(targets[1]), }; case "pasteFromClipboard": return { diff --git a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts index 517f47724e3..a6a2e141af4 100644 --- a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts +++ b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts @@ -11,7 +11,7 @@ export function getPartialTargetDescriptors( ): PartialTargetDescriptor[] { switch (command.action.name) { case "callAsFunction": - return [command.action.callee, command.action.args]; + return [command.action.callee, command.action.argument]; case "replaceWithTarget": case "moveToTarget": return [ diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index 618c6fa23b3..c9ec2d789cf 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -218,7 +218,7 @@ export default class KeyboardCommandsTargeted { action: { name: action, callee: target, - args: { type: "implicit" }, + argument: { type: "implicit" }, }, }); break; From c55d1dcadad5043bbaeb7b7924ae5a27bd29e725 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 11 Jul 2023 14:50:57 +0100 Subject: [PATCH 47/99] tweak jsdocs --- .../src/processTargets/targets/DestinationImpl.ts | 4 ---- packages/cursorless-engine/src/typings/target.types.ts | 6 ++++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts index 64c79f8418d..3c39595bdd9 100644 --- a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts +++ b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts @@ -73,10 +73,6 @@ export default class DestinationImpl implements Destination { // If the target that we're wrapping is not a single line, then we // want to compute indentation based on the entire target. Otherwise, // we allow the editor to determine how to perform indentation. - // Note that we use `this.state.thatTarget` rather than `this.thatTarget` - // because we don't really want the transitive `thatTarget` behaviour, as - // it's not really the "that" target that we're after; it's the target that - // we're wrapping. Should rework this stuff as part of #803. return "insertLineAfter"; } diff --git a/packages/cursorless-engine/src/typings/target.types.ts b/packages/cursorless-engine/src/typings/target.types.ts index 101c12f54cb..2310901037f 100644 --- a/packages/cursorless-engine/src/typings/target.types.ts +++ b/packages/cursorless-engine/src/typings/target.types.ts @@ -162,6 +162,12 @@ export interface Target { toPlainObject(): TargetPlainObject; } +/** + * A destination is a wrapper around a target that can be used for inserting new + * text. It represents things like "after funk", "before air", "to bat", etc. in + * commands like "bring funk air to bat", "paste after line", etc. Destinations + * are also created implicitly for actions like "drink" and "pour". + */ export interface Destination { readonly insertionMode: InsertionMode; readonly editor: TextEditor; From 38c6fa301bbb25e84d71173cd5c95fc4892681aa Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 11 Jul 2023 15:13:56 +0100 Subject: [PATCH 48/99] cleanup --- .../cursorless-engine/src/actions/actions.types.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/cursorless-engine/src/actions/actions.types.ts b/packages/cursorless-engine/src/actions/actions.types.ts index 01b6b65b8ff..52d509bd163 100644 --- a/packages/cursorless-engine/src/actions/actions.types.ts +++ b/packages/cursorless-engine/src/actions/actions.types.ts @@ -70,21 +70,25 @@ export interface ActionRecord extends Record { callAsFunction: { run(callees: Target[], args: Target[]): Promise; }; + replaceWithTarget: { run( sources: Target[], destinations: Destination[], ): Promise; }; + moveToTarget: { run( sources: Target[], destinations: Destination[], ): Promise; }; + swapTargets: { run(targets1: Target[], targets2: Target[]): Promise; }; + wrapWithPairedDelimiter: { run( targets: Target[], @@ -92,6 +96,7 @@ export interface ActionRecord extends Record { right: string, ): Promise; }; + rewrapWithPairedDelimiter: { run( targets: Target[], @@ -100,12 +105,15 @@ export interface ActionRecord extends Record { ): Promise; getFinalStages(): ModifierStage[]; }; + pasteFromClipboard: { run(destinations: Destination[]): Promise; }; + generateSnippet: { run(targets: Target[], snippetName?: string): Promise; }; + insertSnippet: { run( destinations: Destination[], @@ -113,6 +121,7 @@ export interface ActionRecord extends Record { ): Promise; getFinalStages(snippetDescription: InsertSnippetArg): ModifierStage[]; }; + wrapWithSnippet: { run( targets: Target[], @@ -120,10 +129,12 @@ export interface ActionRecord extends Record { ): Promise; getFinalStages(snippetDescription: WrapWithSnippetArg): ModifierStage[]; }; + editNew: { run(targets: Target[]): Promise; runDestinations(destinations: Destination[]): Promise; }; + executeCommand: { run( targets: Target[], @@ -131,12 +142,14 @@ export interface ActionRecord extends Record { options?: ExecuteCommandOptions, ): Promise; }; + replace: { run( destinations: Destination[], replaceWith: ReplaceWith, ): Promise; }; + highlight: { run( targets: Target[], From edd5f2aca6be2685fb0f3600647d773368c8a9bf Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 11 Jul 2023 15:30:39 +0100 Subject: [PATCH 49/99] inline arguments in `CommandRunnerImpl` --- .../core/commandRunner/CommandRunnerImpl.ts | 149 ++++++++---------- 1 file changed, 63 insertions(+), 86 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 558c7cb5167..1f71f391ad0 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -15,6 +15,7 @@ import { Target } from "../../typings/target.types"; import { Debug } from "../Debug"; import { inferFullTargetDescriptor } from "../inferFullTargets"; import { selectionToStoredTarget } from "./selectionToStoredTarget"; +import { TargetDescriptor } from "../../typings/TargetDescriptor"; export class CommandRunnerImpl implements CommandRunner { constructor( @@ -71,146 +72,122 @@ export class CommandRunnerImpl implements CommandRunner { switch (partialActionDescriptor.name) { case "replaceWithTarget": - case "moveToTarget": { - const sources = this.getTargets( - inferenceContext, - partialActionDescriptor.source, + return this.actions.replaceWithTarget.run( + this.getTargets(inferenceContext, partialActionDescriptor.source), + this.getDestinations( + inferenceContext, + partialActionDescriptor.destination, + ), ); - const destinations = this.getDestinations( - inferenceContext, - partialActionDescriptor.destination, + case "moveToTarget": { + return this.actions.moveToTarget.run( + this.getTargets(inferenceContext, partialActionDescriptor.source), + this.getDestinations( + inferenceContext, + partialActionDescriptor.destination, + ), ); - return partialActionDescriptor.name === "replaceWithTarget" - ? this.actions.replaceWithTarget.run(sources, destinations) - : this.actions.moveToTarget.run(sources, destinations); } case "swapTargets": { - const targets1 = this.getTargets( - inferenceContext, - partialActionDescriptor.target1, + return this.actions.swapTargets.run( + this.getTargets(inferenceContext, partialActionDescriptor.target1), + this.getTargets(inferenceContext, partialActionDescriptor.target2), ); - const targets2 = this.getTargets( - inferenceContext, - partialActionDescriptor.target2, - ); - return this.actions.swapTargets.run(targets1, targets2); } case "callAsFunction": { - const callees = this.getTargets( - inferenceContext, - partialActionDescriptor.callee, + return this.actions.callAsFunction.run( + this.getTargets(inferenceContext, partialActionDescriptor.callee), + this.getTargets(inferenceContext, partialActionDescriptor.argument), ); - const args = this.getTargets( - inferenceContext, - partialActionDescriptor.argument, - ); - return this.actions.callAsFunction.run(callees, args); } case "wrapWithPairedDelimiter": { - const targets = this.getTargets( - inferenceContext, - partialActionDescriptor.target, - ); return this.actions.wrapWithPairedDelimiter.run( - targets, + this.getTargets(inferenceContext, partialActionDescriptor.target), partialActionDescriptor.left, partialActionDescriptor.right, ); } case "rewrapWithPairedDelimiter": { - const targets = this.getTargets( - inferenceContext, - partialActionDescriptor.target, - this.actions.rewrapWithPairedDelimiter.getFinalStages(), - ); return this.actions.rewrapWithPairedDelimiter.run( - targets, + this.getTargets( + inferenceContext, + partialActionDescriptor.target, + this.actions.rewrapWithPairedDelimiter.getFinalStages(), + ), partialActionDescriptor.left, partialActionDescriptor.right, ); } case "pasteFromClipboard": { - const destinations = this.getDestinations( - inferenceContext, - partialActionDescriptor.destination, + return this.actions.pasteFromClipboard.run( + this.getDestinations( + inferenceContext, + partialActionDescriptor.destination, + ), ); - return this.actions.pasteFromClipboard.run(destinations); } case "executeCommand": { - const targets = this.getTargets( - inferenceContext, - partialActionDescriptor.target, - ); return this.actions.executeCommand.run( - targets, + this.getTargets(inferenceContext, partialActionDescriptor.target), partialActionDescriptor.commandId, partialActionDescriptor.options, ); } case "replace": { - const destinations = this.getDestinations( - inferenceContext, - partialActionDescriptor.destination, - ); return this.actions.replace.run( - destinations, + this.getDestinations( + inferenceContext, + partialActionDescriptor.destination, + ), partialActionDescriptor.replaceWith, ); } case "highlight": { - const targets = this.getTargets( - inferenceContext, - partialActionDescriptor.target, - ); return this.actions.highlight.run( - targets, + this.getTargets(inferenceContext, partialActionDescriptor.target), partialActionDescriptor.highlightId, ); } case "generateSnippet": { - const targets = this.getTargets( - inferenceContext, - partialActionDescriptor.target, - ); return this.actions.generateSnippet.run( - targets, + this.getTargets(inferenceContext, partialActionDescriptor.target), partialActionDescriptor.snippetName, ); } case "insertSnippet": { - const destinations = this.getDestinations( - inferenceContext, - partialActionDescriptor.destination, - this.actions.insertSnippet.getFinalStages( - partialActionDescriptor.snippetDescription, - ), - ); return this.actions.insertSnippet.run( - destinations, + this.getDestinations( + inferenceContext, + partialActionDescriptor.destination, + this.actions.insertSnippet.getFinalStages( + partialActionDescriptor.snippetDescription, + ), + ), partialActionDescriptor.snippetDescription, ); } case "wrapWithSnippet": { - const targets = this.getTargets( - inferenceContext, - partialActionDescriptor.target, - this.actions.wrapWithSnippet.getFinalStages( - partialActionDescriptor.snippetDescription, - ), - ); return this.actions.wrapWithSnippet.run( - targets, + this.getTargets( + inferenceContext, + partialActionDescriptor.target, + this.actions.wrapWithSnippet.getFinalStages( + partialActionDescriptor.snippetDescription, + ), + ), partialActionDescriptor.snippetDescription, ); } default: { const action = this.actions[partialActionDescriptor.name]; - const targets = this.getTargets( - inferenceContext, - partialActionDescriptor.target, - action.getFinalStages?.(), + + return action.run( + this.getTargets( + inferenceContext, + partialActionDescriptor.target, + action.getFinalStages?.(), + ), ); - return action.run(targets); } } } @@ -231,21 +208,21 @@ export class CommandRunnerImpl implements CommandRunner { ) { if (partialDestinationDescriptor.type === "destinationList") { return partialDestinationDescriptor.destinations.flatMap((destination) => - this.getDestinationTargetsFromPrimitive( + this.getDestinationsFromPrimitive( inferenceContext, destination, actionFinalStages, ), ); } - return this.getDestinationTargetsFromPrimitive( + return this.getDestinationsFromPrimitive( inferenceContext, partialDestinationDescriptor, actionFinalStages, ); } - private getDestinationTargetsFromPrimitive( + private getDestinationsFromPrimitive( inferenceContext: InferenceContext, partialDestinationDescriptor: PartialPrimitiveDestinationDescriptor, actionFinalStages: ModifierStage[], @@ -261,7 +238,7 @@ export class CommandRunnerImpl implements CommandRunner { ); } - private debugTargets(targets: Target[]) { + private debugTargets(targets: TargetDescriptor[]) { if (this.debug.active) { this.debug.log("Full targets:"); this.debug.log(JSON.stringify(targets, null, 2)); From dde32473268c51d16bf602961f2cb8ab9608fae1 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 11 Jul 2023 15:33:51 +0100 Subject: [PATCH 50/99] Resurrect post-inference debugging --- .../src/core/commandRunner/CommandRunnerImpl.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 1f71f391ad0..86532d49904 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -15,7 +15,6 @@ import { Target } from "../../typings/target.types"; import { Debug } from "../Debug"; import { inferFullTargetDescriptor } from "../inferFullTargets"; import { selectionToStoredTarget } from "./selectionToStoredTarget"; -import { TargetDescriptor } from "../../typings/TargetDescriptor"; export class CommandRunnerImpl implements CommandRunner { constructor( @@ -237,20 +236,21 @@ export class CommandRunnerImpl implements CommandRunner { target.toDestination(partialDestinationDescriptor.insertionMode), ); } - - private debugTargets(targets: TargetDescriptor[]) { - if (this.debug.active) { - this.debug.log("Full targets:"); - this.debug.log(JSON.stringify(targets, null, 2)); - } - } } class InferenceContext { private previousTargets: PartialTargetDescriptor[] = []; + constructor(private debug: Debug) {} + run(target: PartialTargetDescriptor) { const ret = inferFullTargetDescriptor(target, this.previousTargets); + + if (this.debug.active) { + this.debug.log("Full target:"); + this.debug.log(JSON.stringify(ret, null, 2)); + } + this.previousTargets.push(target); return ret; } From 9c3366a2e4f71d7cb96e69fb3a342dd18618e185 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 16:44:22 +0200 Subject: [PATCH 51/99] Update packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> --- .../src/actions/EditNew/runNotebookCellTargets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts b/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts index 617b1ae110f..2cfeaf548e9 100644 --- a/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts +++ b/packages/cursorless-engine/src/actions/EditNew/runNotebookCellTargets.ts @@ -30,7 +30,7 @@ export async function runEditNewNotebookCellTargets( await editor.editNewNotebookCellBelow(); } - const thatMark = createThatMark([destination.target]); + const thatMark = createThatMark([destination.target.thatTarget]); // Apply horrible hack to work around the fact that in vscode the promise // resolves before the edits have actually been performed. From 63b945b8f2b3c734baffeb963420bde6fe17f9e1 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 16:52:28 +0200 Subject: [PATCH 52/99] Ked fixed bug --- .../src/core/commandRunner/CommandRunnerImpl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 86532d49904..bbf26476666 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -67,7 +67,7 @@ export class CommandRunnerImpl implements CommandRunner { private runAction( partialActionDescriptor: PartialActionDescriptor, ): Promise { - const inferenceContext = new InferenceContext(); + const inferenceContext = new InferenceContext(this.debug); switch (partialActionDescriptor.name) { case "replaceWithTarget": From 701d974bce42241d5784b450fb8c2fd58acfd63a Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 17:00:35 +0200 Subject: [PATCH 53/99] Rename --- packages/cursorless-engine/src/actions/Clear.ts | 4 ++-- packages/cursorless-engine/src/actions/CutToClipboard.ts | 4 ++-- packages/cursorless-engine/src/actions/Deselect.ts | 4 ++-- packages/cursorless-engine/src/actions/EditNew/EditNew.ts | 4 ++-- packages/cursorless-engine/src/actions/Find.ts | 4 ++-- packages/cursorless-engine/src/actions/FollowLink.ts | 4 ++-- packages/cursorless-engine/src/actions/GetText.ts | 4 ++-- packages/cursorless-engine/src/actions/InsertCopy.ts | 4 ++-- packages/cursorless-engine/src/actions/InsertEmptyLines.ts | 4 ++-- packages/cursorless-engine/src/actions/Remove.ts | 4 ++-- packages/cursorless-engine/src/actions/Scroll.ts | 4 ++-- .../cursorless-engine/src/actions/SetInstanceReference.ts | 4 ++-- packages/cursorless-engine/src/actions/SetSelection.ts | 4 ++-- packages/cursorless-engine/src/actions/Sort.ts | 4 ++-- packages/cursorless-engine/src/actions/ToggleBreakpoint.ts | 4 ++-- packages/cursorless-engine/src/actions/actions.types.ts | 6 +++--- 16 files changed, 33 insertions(+), 33 deletions(-) diff --git a/packages/cursorless-engine/src/actions/Clear.ts b/packages/cursorless-engine/src/actions/Clear.ts index 7739923a2a7..8540d1c5335 100644 --- a/packages/cursorless-engine/src/actions/Clear.ts +++ b/packages/cursorless-engine/src/actions/Clear.ts @@ -4,9 +4,9 @@ import { Target } from "../typings/target.types"; import { setSelectionsAndFocusEditor } from "../util/setSelectionsAndFocusEditor"; import { ensureSingleEditor } from "../util/targetUtils"; import { Actions } from "./Actions"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -export default class Clear implements Action { +export default class Clear implements SimpleAction { constructor(private actions: Actions) { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/CutToClipboard.ts b/packages/cursorless-engine/src/actions/CutToClipboard.ts index 082bf7cf3af..5e92832dde7 100644 --- a/packages/cursorless-engine/src/actions/CutToClipboard.ts +++ b/packages/cursorless-engine/src/actions/CutToClipboard.ts @@ -8,9 +8,9 @@ import { import { ide } from "../singletons/ide.singleton"; import { Target } from "../typings/target.types"; import { Actions } from "./Actions"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -export class CutToClipboard implements Action { +export class CutToClipboard implements SimpleAction { constructor(private actions: Actions) { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/Deselect.ts b/packages/cursorless-engine/src/actions/Deselect.ts index fc28082dba8..7534e77de05 100644 --- a/packages/cursorless-engine/src/actions/Deselect.ts +++ b/packages/cursorless-engine/src/actions/Deselect.ts @@ -2,9 +2,9 @@ import { ide } from "../singletons/ide.singleton"; import { Target } from "../typings/target.types"; import { setSelectionsWithoutFocusingEditor } from "../util/setSelectionsAndFocusEditor"; import { runOnTargetsForEachEditor } from "../util/targetUtils"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -export default class Deselect implements Action { +export default class Deselect implements SimpleAction { constructor() { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts index be72b86dd9f..e1541fd67b2 100644 --- a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts +++ b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts @@ -7,13 +7,13 @@ import { Destination, Target } from "../../typings/target.types"; import { setSelectionsAndFocusEditor } from "../../util/setSelectionsAndFocusEditor"; import { createThatMark, ensureSingleEditor } from "../../util/targetUtils"; import { Actions } from "../Actions"; -import { Action, ActionReturnValue } from "../actions.types"; +import { SimpleAction, ActionReturnValue } from "../actions.types"; import { State } from "./EditNew.types"; import { runEditTargets } from "./runEditTargets"; import { runInsertLineAfterTargets } from "./runInsertLineAfterTargets"; import { runEditNewNotebookCellTargets } from "./runNotebookCellTargets"; -export class EditNew implements Action { +export class EditNew implements SimpleAction { getFinalStages(): ModifierStage[] { return [this.modifierStageFactory.create(containingLineIfUntypedModifier)]; } diff --git a/packages/cursorless-engine/src/actions/Find.ts b/packages/cursorless-engine/src/actions/Find.ts index f5bb8eec50e..bca407a9038 100644 --- a/packages/cursorless-engine/src/actions/Find.ts +++ b/packages/cursorless-engine/src/actions/Find.ts @@ -3,9 +3,9 @@ import { ide } from "../singletons/ide.singleton"; import { Target } from "../typings/target.types"; import { ensureSingleTarget } from "../util/targetUtils"; import { Actions } from "./Actions"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -export class FindInWorkspace implements Action { +export class FindInWorkspace implements SimpleAction { constructor(private actions: Actions) { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/FollowLink.ts b/packages/cursorless-engine/src/actions/FollowLink.ts index e54423fd5e7..2a3509d7c60 100644 --- a/packages/cursorless-engine/src/actions/FollowLink.ts +++ b/packages/cursorless-engine/src/actions/FollowLink.ts @@ -7,9 +7,9 @@ import { flashTargets, } from "../util/targetUtils"; import { Actions } from "./Actions"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -export default class FollowLink implements Action { +export default class FollowLink implements SimpleAction { constructor(private actions: Actions) { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/GetText.ts b/packages/cursorless-engine/src/actions/GetText.ts index 08e8efaa4fe..6cbee53691e 100644 --- a/packages/cursorless-engine/src/actions/GetText.ts +++ b/packages/cursorless-engine/src/actions/GetText.ts @@ -2,9 +2,9 @@ import { FlashStyle } from "@cursorless/common"; import { ide } from "../singletons/ide.singleton"; import { Target } from "../typings/target.types"; import { ensureSingleTarget, flashTargets } from "../util/targetUtils"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -export default class GetText implements Action { +export default class GetText implements SimpleAction { constructor() { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/InsertCopy.ts b/packages/cursorless-engine/src/actions/InsertCopy.ts index 4576c198ebb..a4310e7aa0a 100644 --- a/packages/cursorless-engine/src/actions/InsertCopy.ts +++ b/packages/cursorless-engine/src/actions/InsertCopy.ts @@ -14,9 +14,9 @@ import { ide } from "../singletons/ide.singleton"; import { Target } from "../typings/target.types"; import { setSelectionsWithoutFocusingEditor } from "../util/setSelectionsAndFocusEditor"; import { createThatMark, runOnTargetsForEachEditor } from "../util/targetUtils"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -class InsertCopy implements Action { +class InsertCopy implements SimpleAction { getFinalStages = () => [ this.modifierStageFactory.create(containingLineIfUntypedModifier), ]; diff --git a/packages/cursorless-engine/src/actions/InsertEmptyLines.ts b/packages/cursorless-engine/src/actions/InsertEmptyLines.ts index 987ae55800d..109673a9a45 100644 --- a/packages/cursorless-engine/src/actions/InsertEmptyLines.ts +++ b/packages/cursorless-engine/src/actions/InsertEmptyLines.ts @@ -6,9 +6,9 @@ import { ide } from "../singletons/ide.singleton"; import { Target } from "../typings/target.types"; import { setSelectionsWithoutFocusingEditor } from "../util/setSelectionsAndFocusEditor"; import { runOnTargetsForEachEditor } from "../util/targetUtils"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -class InsertEmptyLines implements Action { +class InsertEmptyLines implements SimpleAction { constructor( private rangeUpdater: RangeUpdater, private insertAbove: boolean, diff --git a/packages/cursorless-engine/src/actions/Remove.ts b/packages/cursorless-engine/src/actions/Remove.ts index 66ea2ec4602..aeaf93d6200 100644 --- a/packages/cursorless-engine/src/actions/Remove.ts +++ b/packages/cursorless-engine/src/actions/Remove.ts @@ -7,9 +7,9 @@ import { ide } from "../singletons/ide.singleton"; import { Target } from "../typings/target.types"; import { flashTargets, runOnTargetsForEachEditor } from "../util/targetUtils"; import { unifyRemovalTargets } from "../util/unifyRanges"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -export default class Delete implements Action { +export default class Delete implements SimpleAction { constructor(private rangeUpdater: RangeUpdater) { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/Scroll.ts b/packages/cursorless-engine/src/actions/Scroll.ts index e005f0af1fa..4be888e1b5c 100644 --- a/packages/cursorless-engine/src/actions/Scroll.ts +++ b/packages/cursorless-engine/src/actions/Scroll.ts @@ -6,9 +6,9 @@ import { } from "@cursorless/common"; import { ide } from "../singletons/ide.singleton"; import { Target } from "../typings/target.types"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -class Scroll implements Action { +class Scroll implements SimpleAction { constructor(private at: RevealLineAt) { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/SetInstanceReference.ts b/packages/cursorless-engine/src/actions/SetInstanceReference.ts index e146215c4bf..da2617b2ae1 100644 --- a/packages/cursorless-engine/src/actions/SetInstanceReference.ts +++ b/packages/cursorless-engine/src/actions/SetInstanceReference.ts @@ -1,7 +1,7 @@ import { Target } from "../typings/target.types"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -export class SetInstanceReference implements Action { +export class SetInstanceReference implements SimpleAction { constructor() { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/SetSelection.ts b/packages/cursorless-engine/src/actions/SetSelection.ts index e359264982a..77baac9822e 100644 --- a/packages/cursorless-engine/src/actions/SetSelection.ts +++ b/packages/cursorless-engine/src/actions/SetSelection.ts @@ -3,9 +3,9 @@ import { ide } from "../singletons/ide.singleton"; import { Target } from "../typings/target.types"; import { setSelectionsAndFocusEditor } from "../util/setSelectionsAndFocusEditor"; import { ensureSingleEditor } from "../util/targetUtils"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -export class SetSelection implements Action { +export class SetSelection implements SimpleAction { constructor() { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/Sort.ts b/packages/cursorless-engine/src/actions/Sort.ts index 882dc04b96c..d587ae43519 100644 --- a/packages/cursorless-engine/src/actions/Sort.ts +++ b/packages/cursorless-engine/src/actions/Sort.ts @@ -1,9 +1,9 @@ import { shuffle } from "lodash"; import { Target } from "../typings/target.types"; import { Actions } from "./Actions"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -abstract class SortBase implements Action { +abstract class SortBase implements SimpleAction { constructor(private actions: Actions) { this.run = this.run.bind(this); } diff --git a/packages/cursorless-engine/src/actions/ToggleBreakpoint.ts b/packages/cursorless-engine/src/actions/ToggleBreakpoint.ts index 07258b8edae..b49988ad32d 100644 --- a/packages/cursorless-engine/src/actions/ToggleBreakpoint.ts +++ b/packages/cursorless-engine/src/actions/ToggleBreakpoint.ts @@ -4,9 +4,9 @@ import { containingLineIfUntypedModifier } from "../processTargets/modifiers/com import { ide } from "../singletons/ide.singleton"; import { Target } from "../typings/target.types"; import { flashTargets, runOnTargetsForEachEditor } from "../util/targetUtils"; -import { Action, ActionReturnValue } from "./actions.types"; +import { SimpleAction, ActionReturnValue } from "./actions.types"; -export default class ToggleBreakpoint implements Action { +export default class ToggleBreakpoint implements SimpleAction { getFinalStages = () => [ this.modifierStageFactory.create(containingLineIfUntypedModifier), ]; diff --git a/packages/cursorless-engine/src/actions/actions.types.ts b/packages/cursorless-engine/src/actions/actions.types.ts index 52d509bd163..965cc7bc8ed 100644 --- a/packages/cursorless-engine/src/actions/actions.types.ts +++ b/packages/cursorless-engine/src/actions/actions.types.ts @@ -11,7 +11,7 @@ import type { SelectionWithEditor } from "../typings/Types"; import type { Destination, Target } from "../typings/target.types"; /** - * To be returned by {@link Action.run} + * To be returned by {@link SimpleAction.run} */ export interface ActionReturnValue { /** @@ -53,7 +53,7 @@ export interface ActionReturnValue { instanceReferenceTargets?: Target[]; } -export interface Action { +export interface SimpleAction { run(targets: Target[]): Promise; /** @@ -66,7 +66,7 @@ export interface Action { /** * Keeps a map from action names to objects that implement the given action */ -export interface ActionRecord extends Record { +export interface ActionRecord extends Record { callAsFunction: { run(callees: Target[], args: Target[]): Promise; }; From 0c6498bdaf1557e83c13273312ee61ac6f84bbb9 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 17:52:39 +0200 Subject: [PATCH 54/99] cleanup --- .../core/commandRunner/CommandRunnerImpl.ts | 36 +++++++------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index bbf26476666..da677533738 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -78,7 +78,7 @@ export class CommandRunnerImpl implements CommandRunner { partialActionDescriptor.destination, ), ); - case "moveToTarget": { + case "moveToTarget": return this.actions.moveToTarget.run( this.getTargets(inferenceContext, partialActionDescriptor.source), this.getDestinations( @@ -86,27 +86,23 @@ export class CommandRunnerImpl implements CommandRunner { partialActionDescriptor.destination, ), ); - } - case "swapTargets": { + case "swapTargets": return this.actions.swapTargets.run( this.getTargets(inferenceContext, partialActionDescriptor.target1), this.getTargets(inferenceContext, partialActionDescriptor.target2), ); - } - case "callAsFunction": { + case "callAsFunction": return this.actions.callAsFunction.run( this.getTargets(inferenceContext, partialActionDescriptor.callee), this.getTargets(inferenceContext, partialActionDescriptor.argument), ); - } - case "wrapWithPairedDelimiter": { + case "wrapWithPairedDelimiter": return this.actions.wrapWithPairedDelimiter.run( this.getTargets(inferenceContext, partialActionDescriptor.target), partialActionDescriptor.left, partialActionDescriptor.right, ); - } - case "rewrapWithPairedDelimiter": { + case "rewrapWithPairedDelimiter": return this.actions.rewrapWithPairedDelimiter.run( this.getTargets( inferenceContext, @@ -116,23 +112,20 @@ export class CommandRunnerImpl implements CommandRunner { partialActionDescriptor.left, partialActionDescriptor.right, ); - } - case "pasteFromClipboard": { + case "pasteFromClipboard": return this.actions.pasteFromClipboard.run( this.getDestinations( inferenceContext, partialActionDescriptor.destination, ), ); - } - case "executeCommand": { + case "executeCommand": return this.actions.executeCommand.run( this.getTargets(inferenceContext, partialActionDescriptor.target), partialActionDescriptor.commandId, partialActionDescriptor.options, ); - } - case "replace": { + case "replace": return this.actions.replace.run( this.getDestinations( inferenceContext, @@ -140,20 +133,17 @@ export class CommandRunnerImpl implements CommandRunner { ), partialActionDescriptor.replaceWith, ); - } - case "highlight": { + case "highlight": return this.actions.highlight.run( this.getTargets(inferenceContext, partialActionDescriptor.target), partialActionDescriptor.highlightId, ); - } - case "generateSnippet": { + case "generateSnippet": return this.actions.generateSnippet.run( this.getTargets(inferenceContext, partialActionDescriptor.target), partialActionDescriptor.snippetName, ); - } - case "insertSnippet": { + case "insertSnippet": return this.actions.insertSnippet.run( this.getDestinations( inferenceContext, @@ -164,8 +154,7 @@ export class CommandRunnerImpl implements CommandRunner { ), partialActionDescriptor.snippetDescription, ); - } - case "wrapWithSnippet": { + case "wrapWithSnippet": return this.actions.wrapWithSnippet.run( this.getTargets( inferenceContext, @@ -176,7 +165,6 @@ export class CommandRunnerImpl implements CommandRunner { ), partialActionDescriptor.snippetDescription, ); - } default: { const action = this.actions[partialActionDescriptor.name]; From fc6f6498c9890ec6e5bfd32ee16a13658611fc6f Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 11 Jul 2023 16:58:56 +0100 Subject: [PATCH 55/99] rename --- packages/cursorless-engine/src/actions/BringMoveSwap.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cursorless-engine/src/actions/BringMoveSwap.ts b/packages/cursorless-engine/src/actions/BringMoveSwap.ts index 3eacdefbbd9..14d4109fa46 100644 --- a/packages/cursorless-engine/src/actions/BringMoveSwap.ts +++ b/packages/cursorless-engine/src/actions/BringMoveSwap.ts @@ -75,18 +75,18 @@ abstract class BringMoveSwap { ): ExtendedEdit[] { const usedSources: Target[] = []; const results: ExtendedEdit[] = []; - const zipSources = + const shouldJoinSources = sources.length !== destinations.length && destinations.length === 1; sources.forEach((source, i) => { let destination = destinations[i]; - if ((source == null || destination == null) && !zipSources) { + if ((source == null || destination == null) && !shouldJoinSources) { throw new Error("Targets must have same number of args"); } if (destination != null) { let text: string; - if (zipSources) { + if (shouldJoinSources) { text = sources .map((source, i) => { const text = source.contentText; From f45aa3644ced833b3c8b82ca52d4f4c2e1a86fa0 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 11 Jul 2023 17:45:44 +0100 Subject: [PATCH 56/99] tweak view --- packages/cursorless-engine/src/runCommand.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/runCommand.ts b/packages/cursorless-engine/src/runCommand.ts index 7ec7eb98b47..d6d0a02a149 100644 --- a/packages/cursorless-engine/src/runCommand.ts +++ b/packages/cursorless-engine/src/runCommand.ts @@ -37,7 +37,7 @@ export async function runCommand( ): Promise { if (debug.active) { debug.log(`command:`); - debug.log(JSON.stringify(command, null, 3)); + debug.log(JSON.stringify(command, null, 2)); } const commandComplete = canonicalizeAndValidateCommand(command); From 75063cee955219864154fe7d4d848fdc1ed1ca77 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 11 Jul 2023 17:56:01 +0100 Subject: [PATCH 57/99] remove comment --- packages/common/src/types/command/command.types.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/common/src/types/command/command.types.ts b/packages/common/src/types/command/command.types.ts index 1fba0888e2c..6dc0f9cad51 100644 --- a/packages/common/src/types/command/command.types.ts +++ b/packages/common/src/types/command/command.types.ts @@ -5,12 +5,6 @@ import type { CommandV3 } from "./legacy/CommandV3.types"; import type { CommandV4 } from "./legacy/CommandV4.types"; import type { CommandV5 } from "./legacy/CommandV5.types"; -// TODO: -// export type CommandComplete = Required> & -// Pick & { -// action: Required; -// }; - export type CommandComplete = Required> & Pick; export const LATEST_VERSION = 6 as const; From 5a95c6517d27e8b590285986c7c9cea3f4e31c8f Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 20:20:21 +0200 Subject: [PATCH 58/99] always use raw target for position stage --- .../src/processTargets/modifiers/PositionStage.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts index 46bb6929b8b..fd254ef26ec 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts @@ -1,11 +1,7 @@ import { EndOfModifier, Range, StartOfModifier } from "@cursorless/common"; import { Target } from "../../typings/target.types"; import { ModifierStage } from "../PipelineStages.types"; -import { - CommonTargetParameters, - PlainTarget, - RawSelectionTarget, -} from "../targets"; +import { CommonTargetParameters, RawSelectionTarget } from "../targets"; abstract class StartEndOfStage implements ModifierStage { run(target: Target): Target[] { @@ -15,11 +11,7 @@ abstract class StartEndOfStage implements ModifierStage { contentRange: this.constructContentRange(target.contentRange), }; - return [ - target.isRaw - ? new RawSelectionTarget(parameters) - : new PlainTarget({ ...parameters, isToken: false }), - ]; + return [new RawSelectionTarget(parameters)]; } protected abstract constructContentRange(contentRange: Range): Range; From b4043a9456b4198f575d7c555857f0cae3f3f9ed Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 20:58:34 +0200 Subject: [PATCH 59/99] Added implicit destination --- packages/common/src/index.ts | 1 + .../src/types/command/ActionDescriptor.ts | 10 +-- .../command/DestinationDescriptor.types.ts | 29 +++++++++ .../command/PartialTargetDescriptor.types.ts | 22 ------- .../core/commandRunner/CommandRunnerImpl.ts | 60 ++++++++++-------- .../upgradeV5ToV6/upgradeV5ToV6.ts | 61 +++++++++++-------- .../src/util/getPartialTargetDescriptors.ts | 43 +++++++------ .../src/keyboard/KeyboardCommandsTargeted.ts | 19 +++--- 8 files changed, 140 insertions(+), 105 deletions(-) create mode 100644 packages/common/src/types/command/DestinationDescriptor.types.ts diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 4313108eb67..a4b6aabba0f 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -65,6 +65,7 @@ export * from "./util/CompositeKeyMap"; export * from "./ide/normalized/NormalizedIDE"; export * from "./types/command/command.types"; export * from "./types/command/PartialTargetDescriptor.types"; +export * from "./types/command/DestinationDescriptor.types"; export * from "./types/command/ActionDescriptor"; export * from "./types/command/legacy/CommandV0V1.types"; export * from "./types/command/legacy/CommandV2.types"; diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index a41db98cb33..a5db109720b 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -1,8 +1,8 @@ import { - PartialDestinationDescriptor, PartialTargetDescriptor, ScopeType, } from "./PartialTargetDescriptor.types"; +import { DestinationDescriptor } from "./DestinationDescriptor.types"; const simpleActionNames = [ "clearAndSetSelection", @@ -82,7 +82,7 @@ export interface PartialSimpleActionDescriptor { export interface PartialBringMoveActionDescriptor { name: "replaceWithTarget" | "moveToTarget"; source: PartialTargetDescriptor; - destination: PartialDestinationDescriptor; + destination: DestinationDescriptor; } export interface PartialCallActionDescriptor { @@ -114,7 +114,7 @@ export interface PartialWrapWithPairedDelimiterActionDescriptor { export interface PartialPasteActionDescriptor { name: "pasteFromClipboard"; - destination: PartialDestinationDescriptor; + destination: DestinationDescriptor; } export interface PartialGenerateSnippetActionDescriptor { @@ -139,7 +139,7 @@ export type InsertSnippetArg = NamedInsertSnippetArg | CustomInsertSnippetArg; export interface PartialInsertSnippetActionDescriptor { name: "insertSnippet"; snippetDescription: InsertSnippetArg; - destination: PartialDestinationDescriptor; + destination: DestinationDescriptor; } interface NamedWrapWithSnippetArg { @@ -183,7 +183,7 @@ export type ReplaceWith = string[] | { start: number }; export interface PartialReplaceActionDescriptor { name: "replace"; replaceWith: ReplaceWith; - destination: PartialDestinationDescriptor; + destination: DestinationDescriptor; } export interface PartialHighlightActionDescriptor { diff --git a/packages/common/src/types/command/DestinationDescriptor.types.ts b/packages/common/src/types/command/DestinationDescriptor.types.ts new file mode 100644 index 00000000000..a835199f38c --- /dev/null +++ b/packages/common/src/types/command/DestinationDescriptor.types.ts @@ -0,0 +1,29 @@ +import { + InsertionMode, + PartialListTargetDescriptor, + PartialPrimitiveTargetDescriptor, + PartialRangeTargetDescriptor, +} from "./PartialTargetDescriptor.types"; + +export interface PrimitiveDestinationDescriptor { + type: "primitive"; + insertionMode: InsertionMode; + target: + | PartialPrimitiveTargetDescriptor + | PartialRangeTargetDescriptor + | PartialListTargetDescriptor; +} + +export interface ListDestinationDescriptor { + type: "list"; + destinations: PrimitiveDestinationDescriptor[]; +} + +export interface ImplicitDestinationDescriptor { + type: "implicit"; +} + +export type DestinationDescriptor = + | ListDestinationDescriptor + | PrimitiveDestinationDescriptor + | ImplicitDestinationDescriptor; diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 172171e7efe..cb4991a37ca 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -381,25 +381,3 @@ export type PartialTargetDescriptor = | PartialRangeTargetDescriptor | PartialListTargetDescriptor | ImplicitTargetDescriptor; - -export interface PartialPrimitiveDestinationDescriptor { - type: "primitiveDestination"; - insertionMode: InsertionMode; - target: PartialTargetDescriptor; -} - -export interface PartialListDestinationDescriptor { - type: "destinationList"; - destinations: { - type: "primitiveDestination"; - insertionMode: InsertionMode; - target: - | PartialPrimitiveTargetDescriptor - | PartialRangeTargetDescriptor - | PartialListTargetDescriptor; - }[]; -} - -export type PartialDestinationDescriptor = - | PartialPrimitiveDestinationDescriptor - | PartialListDestinationDescriptor; diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index da677533738..8f4af21016d 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -1,8 +1,8 @@ import { CommandComplete, + DestinationDescriptor, + InsertionMode, PartialActionDescriptor, - PartialDestinationDescriptor, - PartialPrimitiveDestinationDescriptor, PartialTargetDescriptor, } from "@cursorless/common"; import { CommandRunner } from "../../CommandRunner"; @@ -11,7 +11,7 @@ import { StoredTargetMap } from "../../index"; import { TargetPipelineRunner } from "../../processTargets"; import { ModifierStage } from "../../processTargets/PipelineStages.types"; import { SelectionWithEditor } from "../../typings/Types"; -import { Target } from "../../typings/target.types"; +import { Destination, Target } from "../../typings/target.types"; import { Debug } from "../Debug"; import { inferFullTargetDescriptor } from "../inferFullTargets"; import { selectionToStoredTarget } from "./selectionToStoredTarget"; @@ -183,46 +183,52 @@ export class CommandRunnerImpl implements CommandRunner { inferenceContext: InferenceContext, partialTargetsDescriptor: PartialTargetDescriptor, actionFinalStages?: ModifierStage[], - ) { + ): Target[] { const targetDescriptor = inferenceContext.run(partialTargetsDescriptor); return this.pipelineRunner.run(targetDescriptor, actionFinalStages); } private getDestinations( inferenceContext: InferenceContext, - partialDestinationDescriptor: PartialDestinationDescriptor, + destinationDescriptor: DestinationDescriptor, actionFinalStages: ModifierStage[] = [], - ) { - if (partialDestinationDescriptor.type === "destinationList") { - return partialDestinationDescriptor.destinations.flatMap((destination) => - this.getDestinationsFromPrimitive( + ): Destination[] { + switch (destinationDescriptor.type) { + case "list": + return destinationDescriptor.destinations.flatMap((destination) => + this.getDestinations( + inferenceContext, + destination, + actionFinalStages, + ), + ); + case "primitive": + return this.getDestinationsFromTarget( inferenceContext, - destination, + destinationDescriptor.target, + destinationDescriptor.insertionMode, actionFinalStages, - ), - ); + ); + case "implicit": + return this.getDestinationsFromTarget( + inferenceContext, + destinationDescriptor, + "to", + actionFinalStages, + ); } - return this.getDestinationsFromPrimitive( - inferenceContext, - partialDestinationDescriptor, - actionFinalStages, - ); } - private getDestinationsFromPrimitive( + private getDestinationsFromTarget( inferenceContext: InferenceContext, - partialDestinationDescriptor: PartialPrimitiveDestinationDescriptor, + partialTargetDescriptor: PartialTargetDescriptor, + insertionMode: InsertionMode, actionFinalStages: ModifierStage[], ) { - const destinationTargetDescriptor = inferenceContext.run( - partialDestinationDescriptor.target, - ); - + const targetDescriptor = inferenceContext.run(partialTargetDescriptor); return this.pipelineRunner - .run(destinationTargetDescriptor, actionFinalStages) - .map((target) => - target.toDestination(partialDestinationDescriptor.insertionMode), - ); + .run(targetDescriptor, actionFinalStages) + .map((target) => target.toDestination(insertionMode)); } } diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index cba06069778..1921ecd3f89 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -2,19 +2,19 @@ import { ActionCommandV5, CommandV5, CommandV6, + DestinationDescriptor, ExecuteCommandOptions, HighlightId, + ImplicitDestinationDescriptor, ImplicitTargetDescriptor, InsertSnippetArg, InsertionMode, + ListDestinationDescriptor, Modifier, ModifierV5, PartialActionDescriptor, - PartialDestinationDescriptor, - PartialListDestinationDescriptor, PartialListTargetDescriptor, PartialListTargetDescriptorV5, - PartialPrimitiveDestinationDescriptor, PartialPrimitiveTargetDescriptor, PartialPrimitiveTargetDescriptorV5, PartialRangeTargetDescriptor, @@ -22,6 +22,7 @@ import { PartialTargetDescriptor, PartialTargetDescriptorV5, PositionModifierV5, + PrimitiveDestinationDescriptor, ReplaceWith, SimpleActionName, WrapWithSnippetArg, @@ -133,6 +134,22 @@ function upgradeTarget( } } +function upgradeNonImplicitTarget( + target: + | PartialPrimitiveTargetDescriptorV5 + | PartialRangeTargetDescriptorV5 + | PartialListTargetDescriptorV5, +) { + switch (target.type) { + case "list": + return upgradeListTarget(target); + case "range": + return upgradeRangeTarget(target); + case "primitive": + return upgradePrimitiveTarget(target); + } +} + function upgradeListTarget( target: PartialListTargetDescriptorV5, ): PartialListTargetDescriptor { @@ -172,7 +189,7 @@ function upgradePrimitiveTarget( function targetToDestination( target: PartialTargetDescriptorV5, -): PartialDestinationDescriptor { +): DestinationDescriptor { switch (target.type) { case "list": return listTargetToDestination(target); @@ -181,44 +198,44 @@ function targetToDestination( case "primitive": return primitiveTargetToDestination(target); case "implicit": - return implicitTargetToDestination(target); + return implicitTargetToDestination(); } } function listTargetToDestination( target: PartialListTargetDescriptorV5, -): PartialDestinationDescriptor { - const destinations: PartialPrimitiveDestinationDescriptor[] = []; +): DestinationDescriptor { + const destinations: PrimitiveDestinationDescriptor[] = []; target.elements.forEach((element) => { const insertionMode = getInsertionMode(element); if (insertionMode != null || destinations.length === 0) { destinations.push({ - type: "primitiveDestination", + type: "primitive", insertionMode: insertionMode ?? "to", - target: upgradeTarget(element), + target: upgradeNonImplicitTarget(element), }); } else { destinations.push({ - type: "primitiveDestination", + type: "primitive", insertionMode: destinations[destinations.length - 1].insertionMode, - target: upgradeTarget(element), + target: upgradeNonImplicitTarget(element), }); } }); if (destinations.length > 1) { return { - type: "destinationList", + type: "list", destinations, - } as PartialListDestinationDescriptor; + } as ListDestinationDescriptor; } return destinations[0]; } function rangeTargetToDestination( target: PartialRangeTargetDescriptorV5, -): PartialPrimitiveDestinationDescriptor { +): PrimitiveDestinationDescriptor { return { - type: "primitiveDestination", + type: "primitive", insertionMode: getInsertionMode(target.anchor) ?? "to", target: upgradeRangeTarget(target), }; @@ -226,22 +243,16 @@ function rangeTargetToDestination( function primitiveTargetToDestination( target: PartialPrimitiveTargetDescriptorV5, -): PartialPrimitiveDestinationDescriptor { +): PrimitiveDestinationDescriptor { return { - type: "primitiveDestination", + type: "primitive", insertionMode: getInsertionMode(target) ?? "to", target: upgradePrimitiveTarget(target), }; } -function implicitTargetToDestination( - target: ImplicitTargetDescriptor, -): PartialPrimitiveDestinationDescriptor { - return { - type: "primitiveDestination", - insertionMode: "to", - target, - }; +function implicitTargetToDestination(): ImplicitDestinationDescriptor { + return { type: "implicit" }; } function getInsertionMode( diff --git a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts index a6a2e141af4..5da39a28bd4 100644 --- a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts +++ b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts @@ -1,6 +1,6 @@ import { CommandLatest, - PartialDestinationDescriptor, + DestinationDescriptor, PartialPrimitiveTargetDescriptor, PartialRangeTargetDescriptor, PartialTargetDescriptor, @@ -32,24 +32,29 @@ export function getPartialTargetDescriptors( } export function getPartialTargetDescriptorFromDestination( - destination: PartialDestinationDescriptor, + destination: DestinationDescriptor, ): PartialTargetDescriptor { - if (destination.type === "destinationList") { - const elements: ( - | PartialPrimitiveTargetDescriptor - | PartialRangeTargetDescriptor - )[] = []; - destination.destinations.forEach((destination) => { - if (destination.target.type === "list") { - elements.push(...destination.target.elements); - } else { - elements.push(destination.target); - } - }); - return { - type: "list", - elements, - }; + switch (destination.type) { + case "list": { + const elements: ( + | PartialPrimitiveTargetDescriptor + | PartialRangeTargetDescriptor + )[] = []; + destination.destinations.forEach((destination) => { + if (destination.target.type === "list") { + elements.push(...destination.target.elements); + } else { + elements.push(destination.target); + } + }); + return { + type: "list", + elements, + }; + } + case "primitive": + return destination.target; + case "implicit": + return destination; } - return destination.target; } diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index c9ec2d789cf..5929ddbb190 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -4,7 +4,7 @@ import { ImplicitTargetDescriptor, LATEST_VERSION, PartialActionDescriptor, - PartialDestinationDescriptor, + DestinationDescriptor, PartialPrimitiveTargetDescriptor, PartialTargetDescriptor, SimpleScopeTypeType, @@ -298,12 +298,17 @@ function executeCursorlessCommand( function toDestination( target: PartialPrimitiveTargetDescriptor | ImplicitTargetDescriptor, -): PartialDestinationDescriptor { - return { - type: "primitiveDestination", - insertionMode: "to", - target, - }; +): DestinationDescriptor { + switch (target.type) { + case "primitive": + return { + type: "primitive", + insertionMode: "to", + target, + }; + case "implicit": + return target + } } const EXIT_CURSORLESS_MODE_ACTIONS: ActionType[] = [ From 4f9618d160114c03dd5d4be4c0bfd3b383eeef23 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Tue, 11 Jul 2023 19:00:37 +0000 Subject: [PATCH 60/99] [pre-commit.ci lite] apply automatic fixes --- .../src/keyboard/KeyboardCommandsTargeted.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index 5929ddbb190..51a93c11d6c 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -306,8 +306,8 @@ function toDestination( insertionMode: "to", target, }; - case "implicit": - return target + case "implicit": + return target; } } From fb87903cdb9e8baca0cb671e3118ea5275c07fbf Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 21:02:34 +0200 Subject: [PATCH 61/99] clean --- .../src/keyboard/KeyboardCommandsTargeted.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index 5929ddbb190..1be4e9cbac9 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -1,12 +1,12 @@ import { ActionType, CommandLatest, + DestinationDescriptor, ImplicitTargetDescriptor, LATEST_VERSION, - PartialActionDescriptor, - DestinationDescriptor, PartialPrimitiveTargetDescriptor, PartialTargetDescriptor, + SimpleActionName, SimpleScopeTypeType, } from "@cursorless/common"; import { runCursorlessCommand } from "@cursorless/vscode-common"; @@ -233,9 +233,9 @@ export default class KeyboardCommandsTargeted { default: returnValue = await executeCursorlessCommand({ action: { - name: action, + name: action as SimpleActionName, target, - } as PartialActionDescriptor, // TODO: + }, }); } @@ -306,8 +306,8 @@ function toDestination( insertionMode: "to", target, }; - case "implicit": - return target + case "implicit": + return target; } } From f9e562872b9116740fc131249764549d4624dda7 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 11 Jul 2023 21:26:52 +0100 Subject: [PATCH 62/99] Cleanup --- .../core/commandRunner/CommandRunnerImpl.ts | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 8f4af21016d..501445c7df1 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -1,7 +1,6 @@ import { CommandComplete, DestinationDescriptor, - InsertionMode, PartialActionDescriptor, PartialTargetDescriptor, } from "@cursorless/common"; @@ -203,33 +202,18 @@ export class CommandRunnerImpl implements CommandRunner { ), ); case "primitive": - return this.getDestinationsFromTarget( + return this.getTargets( inferenceContext, destinationDescriptor.target, - destinationDescriptor.insertionMode, - actionFinalStages, + ).map((target) => + target.toDestination(destinationDescriptor.insertionMode), ); case "implicit": - return this.getDestinationsFromTarget( - inferenceContext, - destinationDescriptor, - "to", - actionFinalStages, + return this.getTargets(inferenceContext, { type: "implicit" }).map( + (target) => target.toDestination("to"), ); } } - - private getDestinationsFromTarget( - inferenceContext: InferenceContext, - partialTargetDescriptor: PartialTargetDescriptor, - insertionMode: InsertionMode, - actionFinalStages: ModifierStage[], - ) { - const targetDescriptor = inferenceContext.run(partialTargetDescriptor); - return this.pipelineRunner - .run(targetDescriptor, actionFinalStages) - .map((target) => target.toDestination(insertionMode)); - } } class InferenceContext { From da816eb6ee9bfb22adfad0fe6c721db20ef45c1c Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 11 Jul 2023 21:29:52 +0100 Subject: [PATCH 63/99] whoops --- .../src/core/commandRunner/CommandRunnerImpl.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 501445c7df1..5b0e6498f2b 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -205,13 +205,16 @@ export class CommandRunnerImpl implements CommandRunner { return this.getTargets( inferenceContext, destinationDescriptor.target, + actionFinalStages, ).map((target) => target.toDestination(destinationDescriptor.insertionMode), ); case "implicit": - return this.getTargets(inferenceContext, { type: "implicit" }).map( - (target) => target.toDestination("to"), - ); + return this.getTargets( + inferenceContext, + { type: "implicit" }, + actionFinalStages, + ).map((target) => target.toDestination("to")); } } } From 94c2ecdc9c95b9984f159f46946d86e50ba3db09 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 11 Jul 2023 21:31:23 +0100 Subject: [PATCH 64/99] more tweaks --- .../src/core/commandRunner/CommandRunnerImpl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 5b0e6498f2b..3c34cba310f 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -190,7 +190,7 @@ export class CommandRunnerImpl implements CommandRunner { private getDestinations( inferenceContext: InferenceContext, destinationDescriptor: DestinationDescriptor, - actionFinalStages: ModifierStage[] = [], + actionFinalStages?: ModifierStage[], ): Destination[] { switch (destinationDescriptor.type) { case "list": From bd02d1b6bbec7873a6f78fcad788409b4e94bf38 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 11 Jul 2023 23:29:19 +0200 Subject: [PATCH 65/99] Removed the word partial from action descriptors --- .../src/types/command/ActionDescriptor.ts | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index a5db109720b..dcb4f05d3e0 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -74,18 +74,18 @@ export type ActionType = (typeof actionNames)[number]; /** * A simple action takes only a single target and no other arguments. */ -export interface PartialSimpleActionDescriptor { +export interface SimpleActionDescriptor { name: SimpleActionName; target: PartialTargetDescriptor; } -export interface PartialBringMoveActionDescriptor { +export interface BringMoveActionDescriptor { name: "replaceWithTarget" | "moveToTarget"; source: PartialTargetDescriptor; destination: DestinationDescriptor; } -export interface PartialCallActionDescriptor { +export interface CallActionDescriptor { name: "callAsFunction"; /** @@ -99,25 +99,25 @@ export interface PartialCallActionDescriptor { argument: PartialTargetDescriptor; } -export interface PartialSwapActionDescriptor { +export interface SwapActionDescriptor { name: "swapTargets"; target1: PartialTargetDescriptor; target2: PartialTargetDescriptor; } -export interface PartialWrapWithPairedDelimiterActionDescriptor { +export interface WrapWithPairedDelimiterActionDescriptor { name: "wrapWithPairedDelimiter" | "rewrapWithPairedDelimiter"; left: string; right: string; target: PartialTargetDescriptor; } -export interface PartialPasteActionDescriptor { +export interface PasteActionDescriptor { name: "pasteFromClipboard"; destination: DestinationDescriptor; } -export interface PartialGenerateSnippetActionDescriptor { +export interface GenerateSnippetActionDescriptor { name: "generateSnippet"; snippetName?: string; target: PartialTargetDescriptor; @@ -136,7 +136,7 @@ interface CustomInsertSnippetArg { } export type InsertSnippetArg = NamedInsertSnippetArg | CustomInsertSnippetArg; -export interface PartialInsertSnippetActionDescriptor { +export interface InsertSnippetActionDescriptor { name: "insertSnippet"; snippetDescription: InsertSnippetArg; destination: DestinationDescriptor; @@ -157,7 +157,7 @@ export type WrapWithSnippetArg = | NamedWrapWithSnippetArg | CustomWrapWithSnippetArg; -export interface PartialWrapSnippetActionDescriptor { +export interface WrapWithSnippetActionDescriptor { name: "wrapWithSnippet"; snippetDescription: WrapWithSnippetArg; target: PartialTargetDescriptor; @@ -171,7 +171,7 @@ export interface ExecuteCommandOptions { showDecorations?: boolean; } -export interface PartialExecuteCommandActionDescriptor { +export interface ExecuteCommandActionDescriptor { name: "executeCommand"; commandId: string; options?: ExecuteCommandOptions; @@ -180,28 +180,28 @@ export interface PartialExecuteCommandActionDescriptor { export type ReplaceWith = string[] | { start: number }; -export interface PartialReplaceActionDescriptor { +export interface ReplaceActionDescriptor { name: "replace"; replaceWith: ReplaceWith; destination: DestinationDescriptor; } -export interface PartialHighlightActionDescriptor { +export interface HighlightActionDescriptor { name: "highlight"; highlightId?: string; target: PartialTargetDescriptor; } export type PartialActionDescriptor = - | PartialSimpleActionDescriptor - | PartialBringMoveActionDescriptor - | PartialSwapActionDescriptor - | PartialCallActionDescriptor - | PartialWrapWithPairedDelimiterActionDescriptor - | PartialPasteActionDescriptor - | PartialGenerateSnippetActionDescriptor - | PartialInsertSnippetActionDescriptor - | PartialWrapSnippetActionDescriptor - | PartialExecuteCommandActionDescriptor - | PartialReplaceActionDescriptor - | PartialHighlightActionDescriptor; + | SimpleActionDescriptor + | BringMoveActionDescriptor + | SwapActionDescriptor + | CallActionDescriptor + | PasteActionDescriptor + | ExecuteCommandActionDescriptor + | ReplaceActionDescriptor + | HighlightActionDescriptor + | GenerateSnippetActionDescriptor + | InsertSnippetActionDescriptor + | WrapWithSnippetActionDescriptor + | WrapWithPairedDelimiterActionDescriptor; From 0f5818c746c64c12d7fcefee2f6b98855bac93a6 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 11 Jul 2023 22:56:22 +0100 Subject: [PATCH 66/99] Remove one more `Partial` --- .../src/types/command/ActionDescriptor.ts | 2 +- .../src/types/command/CommandV6.types.ts | 4 +- .../core/commandRunner/CommandRunnerImpl.ts | 82 ++++++++----------- .../canonicalizeAndValidateCommand.ts | 4 +- .../upgradeV5ToV6/upgradeV5ToV6.ts | 4 +- 5 files changed, 42 insertions(+), 54 deletions(-) diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index dcb4f05d3e0..fd01816eb1c 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -192,7 +192,7 @@ export interface HighlightActionDescriptor { target: PartialTargetDescriptor; } -export type PartialActionDescriptor = +export type ActionDescriptor = | SimpleActionDescriptor | BringMoveActionDescriptor | SwapActionDescriptor diff --git a/packages/common/src/types/command/CommandV6.types.ts b/packages/common/src/types/command/CommandV6.types.ts index 19e9373dc2f..bd21525aec2 100644 --- a/packages/common/src/types/command/CommandV6.types.ts +++ b/packages/common/src/types/command/CommandV6.types.ts @@ -1,4 +1,4 @@ -import type { PartialActionDescriptor } from "./ActionDescriptor"; +import type { ActionDescriptor } from "./ActionDescriptor"; export interface CommandV6 { /** @@ -19,5 +19,5 @@ export interface CommandV6 { */ usePrePhraseSnapshot: boolean; - action: PartialActionDescriptor; + action: ActionDescriptor; } diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 3c34cba310f..604fc0d06e9 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -1,7 +1,7 @@ import { CommandComplete, DestinationDescriptor, - PartialActionDescriptor, + ActionDescriptor, PartialTargetDescriptor, } from "@cursorless/common"; import { CommandRunner } from "../../CommandRunner"; @@ -64,113 +64,101 @@ export class CommandRunnerImpl implements CommandRunner { } private runAction( - partialActionDescriptor: PartialActionDescriptor, + actionDescriptor: ActionDescriptor, ): Promise { const inferenceContext = new InferenceContext(this.debug); - switch (partialActionDescriptor.name) { + switch (actionDescriptor.name) { case "replaceWithTarget": return this.actions.replaceWithTarget.run( - this.getTargets(inferenceContext, partialActionDescriptor.source), - this.getDestinations( - inferenceContext, - partialActionDescriptor.destination, - ), + this.getTargets(inferenceContext, actionDescriptor.source), + this.getDestinations(inferenceContext, actionDescriptor.destination), ); case "moveToTarget": return this.actions.moveToTarget.run( - this.getTargets(inferenceContext, partialActionDescriptor.source), - this.getDestinations( - inferenceContext, - partialActionDescriptor.destination, - ), + this.getTargets(inferenceContext, actionDescriptor.source), + this.getDestinations(inferenceContext, actionDescriptor.destination), ); case "swapTargets": return this.actions.swapTargets.run( - this.getTargets(inferenceContext, partialActionDescriptor.target1), - this.getTargets(inferenceContext, partialActionDescriptor.target2), + this.getTargets(inferenceContext, actionDescriptor.target1), + this.getTargets(inferenceContext, actionDescriptor.target2), ); case "callAsFunction": return this.actions.callAsFunction.run( - this.getTargets(inferenceContext, partialActionDescriptor.callee), - this.getTargets(inferenceContext, partialActionDescriptor.argument), + this.getTargets(inferenceContext, actionDescriptor.callee), + this.getTargets(inferenceContext, actionDescriptor.argument), ); case "wrapWithPairedDelimiter": return this.actions.wrapWithPairedDelimiter.run( - this.getTargets(inferenceContext, partialActionDescriptor.target), - partialActionDescriptor.left, - partialActionDescriptor.right, + this.getTargets(inferenceContext, actionDescriptor.target), + actionDescriptor.left, + actionDescriptor.right, ); case "rewrapWithPairedDelimiter": return this.actions.rewrapWithPairedDelimiter.run( this.getTargets( inferenceContext, - partialActionDescriptor.target, + actionDescriptor.target, this.actions.rewrapWithPairedDelimiter.getFinalStages(), ), - partialActionDescriptor.left, - partialActionDescriptor.right, + actionDescriptor.left, + actionDescriptor.right, ); case "pasteFromClipboard": return this.actions.pasteFromClipboard.run( - this.getDestinations( - inferenceContext, - partialActionDescriptor.destination, - ), + this.getDestinations(inferenceContext, actionDescriptor.destination), ); case "executeCommand": return this.actions.executeCommand.run( - this.getTargets(inferenceContext, partialActionDescriptor.target), - partialActionDescriptor.commandId, - partialActionDescriptor.options, + this.getTargets(inferenceContext, actionDescriptor.target), + actionDescriptor.commandId, + actionDescriptor.options, ); case "replace": return this.actions.replace.run( - this.getDestinations( - inferenceContext, - partialActionDescriptor.destination, - ), - partialActionDescriptor.replaceWith, + this.getDestinations(inferenceContext, actionDescriptor.destination), + actionDescriptor.replaceWith, ); case "highlight": return this.actions.highlight.run( - this.getTargets(inferenceContext, partialActionDescriptor.target), - partialActionDescriptor.highlightId, + this.getTargets(inferenceContext, actionDescriptor.target), + actionDescriptor.highlightId, ); case "generateSnippet": return this.actions.generateSnippet.run( - this.getTargets(inferenceContext, partialActionDescriptor.target), - partialActionDescriptor.snippetName, + this.getTargets(inferenceContext, actionDescriptor.target), + actionDescriptor.snippetName, ); case "insertSnippet": return this.actions.insertSnippet.run( this.getDestinations( inferenceContext, - partialActionDescriptor.destination, + actionDescriptor.destination, this.actions.insertSnippet.getFinalStages( - partialActionDescriptor.snippetDescription, + actionDescriptor.snippetDescription, ), ), - partialActionDescriptor.snippetDescription, + actionDescriptor.snippetDescription, ); case "wrapWithSnippet": return this.actions.wrapWithSnippet.run( this.getTargets( inferenceContext, - partialActionDescriptor.target, + actionDescriptor.target, this.actions.wrapWithSnippet.getFinalStages( - partialActionDescriptor.snippetDescription, + actionDescriptor.snippetDescription, ), ), - partialActionDescriptor.snippetDescription, + actionDescriptor.snippetDescription, ); default: { - const action = this.actions[partialActionDescriptor.name]; + const action = this.actions[actionDescriptor.name]; return action.run( this.getTargets( inferenceContext, - partialActionDescriptor.target, + actionDescriptor.target, action.getFinalStages?.(), ), ); diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts index 613b22fae03..61c489c682a 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts @@ -7,7 +7,7 @@ import { LATEST_VERSION, Modifier, OutdatedExtensionError, - PartialActionDescriptor, + ActionDescriptor, PartialTargetDescriptor, SimpleScopeTypeType, } from "@cursorless/common"; @@ -47,7 +47,7 @@ export function canonicalizeAndValidateCommand( action: { ...action, name: actionName, - } as PartialActionDescriptor, + } as ActionDescriptor, usePrePhraseSnapshot, }; } diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index 1921ecd3f89..54db7a0fc11 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -12,7 +12,7 @@ import { ListDestinationDescriptor, Modifier, ModifierV5, - PartialActionDescriptor, + ActionDescriptor, PartialListTargetDescriptor, PartialListTargetDescriptorV5, PartialPrimitiveTargetDescriptor, @@ -40,7 +40,7 @@ export function upgradeV5ToV6(command: CommandV5): CommandV6 { function upgradeAction( action: ActionCommandV5, targets: PartialTargetDescriptorV5[], -): PartialActionDescriptor { +): ActionDescriptor { switch (action.name) { case "replaceWithTarget": case "moveToTarget": From 157b0fddc6104708882acdd2cc98e48a63c08978 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 11:56:01 +0100 Subject: [PATCH 67/99] More cleanup --- .../core/commandRunner/CommandRunnerImpl.ts | 70 +++++++++---------- .../src/core/inferFullTargets.ts | 8 +-- .../processTargets/TargetPipelineRunner.ts | 8 +-- 3 files changed, 39 insertions(+), 47 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 604fc0d06e9..ad9cf9e0b8c 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -16,12 +16,16 @@ import { inferFullTargetDescriptor } from "../inferFullTargets"; import { selectionToStoredTarget } from "./selectionToStoredTarget"; export class CommandRunnerImpl implements CommandRunner { + private inferenceContext: InferenceContext; + constructor( private debug: Debug, private storedTargets: StoredTargetMap, private pipelineRunner: TargetPipelineRunner, private actions: ActionRecord, - ) {} + ) { + this.inferenceContext = new InferenceContext(this.debug); + } /** * Runs a Cursorless command. We proceed as follows: @@ -66,39 +70,38 @@ export class CommandRunnerImpl implements CommandRunner { private runAction( actionDescriptor: ActionDescriptor, ): Promise { - const inferenceContext = new InferenceContext(this.debug); + this.inferenceContext.reset(); switch (actionDescriptor.name) { case "replaceWithTarget": return this.actions.replaceWithTarget.run( - this.getTargets(inferenceContext, actionDescriptor.source), - this.getDestinations(inferenceContext, actionDescriptor.destination), + this.getTargets(actionDescriptor.source), + this.getDestinations(actionDescriptor.destination), ); case "moveToTarget": return this.actions.moveToTarget.run( - this.getTargets(inferenceContext, actionDescriptor.source), - this.getDestinations(inferenceContext, actionDescriptor.destination), + this.getTargets(actionDescriptor.source), + this.getDestinations(actionDescriptor.destination), ); case "swapTargets": return this.actions.swapTargets.run( - this.getTargets(inferenceContext, actionDescriptor.target1), - this.getTargets(inferenceContext, actionDescriptor.target2), + this.getTargets(actionDescriptor.target1), + this.getTargets(actionDescriptor.target2), ); case "callAsFunction": return this.actions.callAsFunction.run( - this.getTargets(inferenceContext, actionDescriptor.callee), - this.getTargets(inferenceContext, actionDescriptor.argument), + this.getTargets(actionDescriptor.callee), + this.getTargets(actionDescriptor.argument), ); case "wrapWithPairedDelimiter": return this.actions.wrapWithPairedDelimiter.run( - this.getTargets(inferenceContext, actionDescriptor.target), + this.getTargets(actionDescriptor.target), actionDescriptor.left, actionDescriptor.right, ); case "rewrapWithPairedDelimiter": return this.actions.rewrapWithPairedDelimiter.run( this.getTargets( - inferenceContext, actionDescriptor.target, this.actions.rewrapWithPairedDelimiter.getFinalStages(), ), @@ -107,33 +110,32 @@ export class CommandRunnerImpl implements CommandRunner { ); case "pasteFromClipboard": return this.actions.pasteFromClipboard.run( - this.getDestinations(inferenceContext, actionDescriptor.destination), + this.getDestinations(actionDescriptor.destination), ); case "executeCommand": return this.actions.executeCommand.run( - this.getTargets(inferenceContext, actionDescriptor.target), + this.getTargets(actionDescriptor.target), actionDescriptor.commandId, actionDescriptor.options, ); case "replace": return this.actions.replace.run( - this.getDestinations(inferenceContext, actionDescriptor.destination), + this.getDestinations(actionDescriptor.destination), actionDescriptor.replaceWith, ); case "highlight": return this.actions.highlight.run( - this.getTargets(inferenceContext, actionDescriptor.target), + this.getTargets(actionDescriptor.target), actionDescriptor.highlightId, ); case "generateSnippet": return this.actions.generateSnippet.run( - this.getTargets(inferenceContext, actionDescriptor.target), + this.getTargets(actionDescriptor.target), actionDescriptor.snippetName, ); case "insertSnippet": return this.actions.insertSnippet.run( this.getDestinations( - inferenceContext, actionDescriptor.destination, this.actions.insertSnippet.getFinalStages( actionDescriptor.snippetDescription, @@ -144,7 +146,6 @@ export class CommandRunnerImpl implements CommandRunner { case "wrapWithSnippet": return this.actions.wrapWithSnippet.run( this.getTargets( - inferenceContext, actionDescriptor.target, this.actions.wrapWithSnippet.getFinalStages( actionDescriptor.snippetDescription, @@ -156,53 +157,42 @@ export class CommandRunnerImpl implements CommandRunner { const action = this.actions[actionDescriptor.name]; return action.run( - this.getTargets( - inferenceContext, - actionDescriptor.target, - action.getFinalStages?.(), - ), + this.getTargets(actionDescriptor.target, action.getFinalStages?.()), ); } } } private getTargets( - inferenceContext: InferenceContext, partialTargetsDescriptor: PartialTargetDescriptor, actionFinalStages?: ModifierStage[], ): Target[] { - const targetDescriptor = inferenceContext.run(partialTargetsDescriptor); + const targetDescriptor = this.inferenceContext.run( + partialTargetsDescriptor, + ); return this.pipelineRunner.run(targetDescriptor, actionFinalStages); } private getDestinations( - inferenceContext: InferenceContext, destinationDescriptor: DestinationDescriptor, actionFinalStages?: ModifierStage[], ): Destination[] { switch (destinationDescriptor.type) { case "list": return destinationDescriptor.destinations.flatMap((destination) => - this.getDestinations( - inferenceContext, - destination, - actionFinalStages, - ), + this.getDestinations(destination, actionFinalStages), ); case "primitive": return this.getTargets( - inferenceContext, destinationDescriptor.target, actionFinalStages, ).map((target) => target.toDestination(destinationDescriptor.insertionMode), ); case "implicit": - return this.getTargets( - inferenceContext, - { type: "implicit" }, - actionFinalStages, - ).map((target) => target.toDestination("to")); + return this.getTargets({ type: "implicit" }, actionFinalStages).map( + (target) => target.toDestination("to"), + ); } } } @@ -223,6 +213,10 @@ class InferenceContext { this.previousTargets.push(target); return ret; } + + reset() { + this.previousTargets = []; + } } function constructStoredTarget( diff --git a/packages/cursorless-engine/src/core/inferFullTargets.ts b/packages/cursorless-engine/src/core/inferFullTargets.ts index 5754c91384d..01316024369 100644 --- a/packages/cursorless-engine/src/core/inferFullTargets.ts +++ b/packages/cursorless-engine/src/core/inferFullTargets.ts @@ -129,10 +129,8 @@ function shouldInferPreviousMark( } /** - * Return a list of modifiers that should not be removed during inference. - * Today, we remove positional modifiers, because they have their own field on - * the full targets. We also remove modifiers that only impact inference, such - * as `inferPreviousMark`. + * Return a list of modifiers that should not be removed during inference. We + * remove modifiers that only impact inference, such as `inferPreviousMark`. * * We return `undefined` if there are no preserved modifiers. Note that we will * never return an empty list; we will always return `undefined` if there are no @@ -145,7 +143,7 @@ function getPreservedModifiers( ): Modifier[] | undefined { const preservedModifiers = target.modifiers?.filter( - (modifier) => !["inferPreviousMark"].includes(modifier.type), + (modifier) => modifier.type !== "inferPreviousMark", ) ?? []; if (preservedModifiers.length !== 0) { return preservedModifiers; diff --git a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts index 72a4ceee799..2f3c0337b20 100644 --- a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts @@ -196,14 +196,14 @@ class TargetPipeline { targetDescriptor: PrimitiveTargetDescriptor | ImplicitTargetDescriptor, ): Target[] { let markStage: MarkStage; - let nonModifierStages: ModifierStage[]; + let targetModifierStages: ModifierStage[]; if (targetDescriptor.type === "implicit") { markStage = new ImplicitStage(); - nonModifierStages = []; + targetModifierStages = []; } else { markStage = this.markStageFactory.create(targetDescriptor.mark); - nonModifierStages = getModifierStagesFromTargetModifiers( + targetModifierStages = getModifierStagesFromTargetModifiers( this.modifierStageFactory, targetDescriptor.modifiers, ); @@ -216,7 +216,7 @@ class TargetPipeline { * The modifier pipeline that will be applied to construct our final targets */ const modifierStages = [ - ...nonModifierStages, + ...targetModifierStages, ...this.actionFinalStages, // This performs auto-expansion to token when you say eg "take this" with an From 31c789ff083d6ee0cf273ac6d1f3f3aebdbcbcf6 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 12:01:53 +0100 Subject: [PATCH 68/99] More simplification --- .../core/commandRunner/CommandRunnerImpl.ts | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index ad9cf9e0b8c..af7f36d41b9 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -17,6 +17,7 @@ import { selectionToStoredTarget } from "./selectionToStoredTarget"; export class CommandRunnerImpl implements CommandRunner { private inferenceContext: InferenceContext; + private finalStages: ModifierStage[] = []; constructor( private debug: Debug, @@ -71,6 +72,7 @@ export class CommandRunnerImpl implements CommandRunner { actionDescriptor: ActionDescriptor, ): Promise { this.inferenceContext.reset(); + this.finalStages = []; switch (actionDescriptor.name) { case "replaceWithTarget": @@ -100,11 +102,11 @@ export class CommandRunnerImpl implements CommandRunner { actionDescriptor.right, ); case "rewrapWithPairedDelimiter": + this.finalStages = + this.actions.rewrapWithPairedDelimiter.getFinalStages(); + return this.actions.rewrapWithPairedDelimiter.run( - this.getTargets( - actionDescriptor.target, - this.actions.rewrapWithPairedDelimiter.getFinalStages(), - ), + this.getTargets(actionDescriptor.target), actionDescriptor.left, actionDescriptor.right, ); @@ -134,64 +136,57 @@ export class CommandRunnerImpl implements CommandRunner { actionDescriptor.snippetName, ); case "insertSnippet": + this.finalStages = this.actions.insertSnippet.getFinalStages( + actionDescriptor.snippetDescription, + ); + return this.actions.insertSnippet.run( - this.getDestinations( - actionDescriptor.destination, - this.actions.insertSnippet.getFinalStages( - actionDescriptor.snippetDescription, - ), - ), + this.getDestinations(actionDescriptor.destination), actionDescriptor.snippetDescription, ); case "wrapWithSnippet": + this.finalStages = this.actions.wrapWithSnippet.getFinalStages( + actionDescriptor.snippetDescription, + ); + return this.actions.wrapWithSnippet.run( - this.getTargets( - actionDescriptor.target, - this.actions.wrapWithSnippet.getFinalStages( - actionDescriptor.snippetDescription, - ), - ), + this.getTargets(actionDescriptor.target), actionDescriptor.snippetDescription, ); default: { const action = this.actions[actionDescriptor.name]; + this.finalStages = action.getFinalStages?.() ?? []; - return action.run( - this.getTargets(actionDescriptor.target, action.getFinalStages?.()), - ); + return action.run(this.getTargets(actionDescriptor.target)); } } } private getTargets( partialTargetsDescriptor: PartialTargetDescriptor, - actionFinalStages?: ModifierStage[], ): Target[] { const targetDescriptor = this.inferenceContext.run( partialTargetsDescriptor, ); - return this.pipelineRunner.run(targetDescriptor, actionFinalStages); + + return this.pipelineRunner.run(targetDescriptor, this.finalStages); } private getDestinations( destinationDescriptor: DestinationDescriptor, - actionFinalStages?: ModifierStage[], ): Destination[] { switch (destinationDescriptor.type) { case "list": return destinationDescriptor.destinations.flatMap((destination) => - this.getDestinations(destination, actionFinalStages), + this.getDestinations(destination), ); case "primitive": - return this.getTargets( - destinationDescriptor.target, - actionFinalStages, - ).map((target) => + return this.getTargets(destinationDescriptor.target).map((target) => target.toDestination(destinationDescriptor.insertionMode), ); case "implicit": - return this.getTargets({ type: "implicit" }, actionFinalStages).map( - (target) => target.toDestination("to"), + return this.getTargets({ type: "implicit" }).map((target) => + target.toDestination("to"), ); } } From c899a61fe45f203cbca096bf7daa8d501b1218c8 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 12:04:28 +0100 Subject: [PATCH 69/99] more cleanup --- .../src/core/commandRunner/CommandRunnerImpl.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index af7f36d41b9..9e03427e92d 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -80,83 +80,92 @@ export class CommandRunnerImpl implements CommandRunner { this.getTargets(actionDescriptor.source), this.getDestinations(actionDescriptor.destination), ); + case "moveToTarget": return this.actions.moveToTarget.run( this.getTargets(actionDescriptor.source), this.getDestinations(actionDescriptor.destination), ); + case "swapTargets": return this.actions.swapTargets.run( this.getTargets(actionDescriptor.target1), this.getTargets(actionDescriptor.target2), ); + case "callAsFunction": return this.actions.callAsFunction.run( this.getTargets(actionDescriptor.callee), this.getTargets(actionDescriptor.argument), ); + case "wrapWithPairedDelimiter": return this.actions.wrapWithPairedDelimiter.run( this.getTargets(actionDescriptor.target), actionDescriptor.left, actionDescriptor.right, ); + case "rewrapWithPairedDelimiter": this.finalStages = this.actions.rewrapWithPairedDelimiter.getFinalStages(); - return this.actions.rewrapWithPairedDelimiter.run( this.getTargets(actionDescriptor.target), actionDescriptor.left, actionDescriptor.right, ); + case "pasteFromClipboard": return this.actions.pasteFromClipboard.run( this.getDestinations(actionDescriptor.destination), ); + case "executeCommand": return this.actions.executeCommand.run( this.getTargets(actionDescriptor.target), actionDescriptor.commandId, actionDescriptor.options, ); + case "replace": return this.actions.replace.run( this.getDestinations(actionDescriptor.destination), actionDescriptor.replaceWith, ); + case "highlight": return this.actions.highlight.run( this.getTargets(actionDescriptor.target), actionDescriptor.highlightId, ); + case "generateSnippet": return this.actions.generateSnippet.run( this.getTargets(actionDescriptor.target), actionDescriptor.snippetName, ); + case "insertSnippet": this.finalStages = this.actions.insertSnippet.getFinalStages( actionDescriptor.snippetDescription, ); - return this.actions.insertSnippet.run( this.getDestinations(actionDescriptor.destination), actionDescriptor.snippetDescription, ); + case "wrapWithSnippet": this.finalStages = this.actions.wrapWithSnippet.getFinalStages( actionDescriptor.snippetDescription, ); - return this.actions.wrapWithSnippet.run( this.getTargets(actionDescriptor.target), actionDescriptor.snippetDescription, ); + default: { const action = this.actions[actionDescriptor.name]; this.finalStages = action.getFinalStages?.() ?? []; - return action.run(this.getTargets(actionDescriptor.target)); } } From 467315ebdcc3b81a47fd9cb037ebacf742b26ca0 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 15:31:15 +0100 Subject: [PATCH 70/99] Improve typing; cleanup `editNew` --- .../src/types/command/ActionDescriptor.ts | 8 +- .../cursorless-engine/src/actions/Actions.ts | 8 +- .../src/actions/EditNew/EditNew.ts | 63 ++++----- .../src/actions/InsertSnippet.ts | 2 +- .../src/actions/PasteFromClipboard.ts | 2 +- .../src/actions/actions.types.ts | 3 +- .../core/commandRunner/CommandRunnerImpl.ts | 5 + .../src/util/getPartialTargetDescriptors.ts | 1 + .../src/keyboard/KeyboardCommandsTargeted.ts | 121 ++++++++---------- 9 files changed, 104 insertions(+), 109 deletions(-) diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index fd01816eb1c..3328426f7ff 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -192,6 +192,11 @@ export interface HighlightActionDescriptor { target: PartialTargetDescriptor; } +export interface EditNewActionDescriptor { + name: "editNew"; + destination: DestinationDescriptor; +} + export type ActionDescriptor = | SimpleActionDescriptor | BringMoveActionDescriptor @@ -204,4 +209,5 @@ export type ActionDescriptor = | GenerateSnippetActionDescriptor | InsertSnippetActionDescriptor | WrapWithSnippetActionDescriptor - | WrapWithPairedDelimiterActionDescriptor; + | WrapWithPairedDelimiterActionDescriptor + | EditNewActionDescriptor; diff --git a/packages/cursorless-engine/src/actions/Actions.ts b/packages/cursorless-engine/src/actions/Actions.ts index dde3e0007c3..4b2587037ae 100644 --- a/packages/cursorless-engine/src/actions/Actions.ts +++ b/packages/cursorless-engine/src/actions/Actions.ts @@ -71,14 +71,12 @@ export class Actions implements ActionRecord { copyToClipboard = new CopyToClipboard(this.rangeUpdater); cutToClipboard = new CutToClipboard(this); deselect = new Deselect(); - editNew = new EditNew(this.rangeUpdater, this, this.modifierStageFactory); - editNewLineAfter = new EditNewAfter( - this.rangeUpdater, + editNew = new EditNew(this.rangeUpdater, this); + editNewLineAfter: EditNewAfter = new EditNewAfter( this, this.modifierStageFactory, ); - editNewLineBefore = new EditNewBefore( - this.rangeUpdater, + editNewLineBefore: EditNewBefore = new EditNewBefore( this, this.modifierStageFactory, ); diff --git a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts index e1541fd67b2..7d61e3d7dd7 100644 --- a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts +++ b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts @@ -1,40 +1,28 @@ import { RangeUpdater } from "../../core/updateSelections/RangeUpdater"; -import { containingLineIfUntypedModifier } from "../../processTargets/modifiers/commonContainingScopeIfUntypedModifiers"; import { ModifierStageFactory } from "../../processTargets/ModifierStageFactory"; import { ModifierStage } from "../../processTargets/PipelineStages.types"; +import { containingLineIfUntypedModifier } from "../../processTargets/modifiers/commonContainingScopeIfUntypedModifiers"; import { ide } from "../../singletons/ide.singleton"; import { Destination, Target } from "../../typings/target.types"; import { setSelectionsAndFocusEditor } from "../../util/setSelectionsAndFocusEditor"; import { createThatMark, ensureSingleEditor } from "../../util/targetUtils"; import { Actions } from "../Actions"; -import { SimpleAction, ActionReturnValue } from "../actions.types"; +import { + ActionRecord, + ActionReturnValue, + SimpleAction, +} from "../actions.types"; import { State } from "./EditNew.types"; import { runEditTargets } from "./runEditTargets"; import { runInsertLineAfterTargets } from "./runInsertLineAfterTargets"; import { runEditNewNotebookCellTargets } from "./runNotebookCellTargets"; -export class EditNew implements SimpleAction { - getFinalStages(): ModifierStage[] { - return [this.modifierStageFactory.create(containingLineIfUntypedModifier)]; - } - - constructor( - private rangeUpdater: RangeUpdater, - private actions: Actions, - private modifierStageFactory: ModifierStageFactory, - ) { +export class EditNew { + constructor(private rangeUpdater: RangeUpdater, private actions: Actions) { this.run = this.run.bind(this); } - async run(targets: Target[]): Promise { - return this.runDestinations( - targets.map((target) => this.toDestination(target)), - ); - } - - async runDestinations( - destinations: Destination[], - ): Promise { + async run(destinations: Destination[]): Promise { if (destinations.some(({ target }) => target.isNotebookCell)) { // It is not possible to "pour" a notebook cell and something else, // because each notebook cell is its own editor, and you can't have @@ -79,20 +67,33 @@ export class EditNew implements SimpleAction { ), }; } +} - protected toDestination(target: Target): Destination { - return target.toDestination("to"); +abstract class EditNewLineAction implements SimpleAction { + getFinalStages(): ModifierStage[] { + return [this.modifierStageFactory.create(containingLineIfUntypedModifier)]; } -} -export class EditNewBefore extends EditNew { - protected toDestination(target: Target): Destination { - return target.toDestination("before"); + protected abstract insertionMode: "before" | "after"; + + constructor( + private actions: ActionRecord, + private modifierStageFactory: ModifierStageFactory, + ) { + this.run = this.run.bind(this); } -} -export class EditNewAfter extends EditNew { - protected toDestination(target: Target): Destination { - return target.toDestination("after"); + run(targets: Target[]): Promise { + return this.actions.editNew.run( + targets.map((target) => target.toDestination(this.insertionMode)), + ); } } + +export class EditNewBefore extends EditNewLineAction { + protected insertionMode = "before" as const; +} + +export class EditNewAfter extends EditNewLineAction { + protected insertionMode = "after" as const; +} diff --git a/packages/cursorless-engine/src/actions/InsertSnippet.ts b/packages/cursorless-engine/src/actions/InsertSnippet.ts index bed83809318..f0fb49dbc8c 100644 --- a/packages/cursorless-engine/src/actions/InsertSnippet.ts +++ b/packages/cursorless-engine/src/actions/InsertSnippet.ts @@ -116,7 +116,7 @@ export default class InsertSnippet { ensureSingleEditor(destinations), ); - await this.actions.editNew.runDestinations(destinations); + await this.actions.editNew.run(destinations); const targetSelectionInfos = editor.selections.map((selection) => getSelectionInfo( diff --git a/packages/cursorless-engine/src/actions/PasteFromClipboard.ts b/packages/cursorless-engine/src/actions/PasteFromClipboard.ts index 5e7dfd1d623..bd7963832fd 100644 --- a/packages/cursorless-engine/src/actions/PasteFromClipboard.ts +++ b/packages/cursorless-engine/src/actions/PasteFromClipboard.ts @@ -30,7 +30,7 @@ export class PasteFromClipboard { const [originalCursorSelections] = await callFunctionAndUpdateSelections( this.rangeUpdater, async () => { - await this.actions.editNew.runDestinations(destinations); + await this.actions.editNew.run(destinations); }, editor.document, [editor.selections], diff --git a/packages/cursorless-engine/src/actions/actions.types.ts b/packages/cursorless-engine/src/actions/actions.types.ts index 965cc7bc8ed..a394e0470fc 100644 --- a/packages/cursorless-engine/src/actions/actions.types.ts +++ b/packages/cursorless-engine/src/actions/actions.types.ts @@ -131,8 +131,7 @@ export interface ActionRecord extends Record { }; editNew: { - run(targets: Target[]): Promise; - runDestinations(destinations: Destination[]): Promise; + run(destinations: Destination[]): Promise; }; executeCommand: { diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 9e03427e92d..7b9f25aa61c 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -163,6 +163,11 @@ export class CommandRunnerImpl implements CommandRunner { actionDescriptor.snippetDescription, ); + case "editNew": + return this.actions.editNew.run( + this.getDestinations(actionDescriptor.destination), + ); + default: { const action = this.actions[actionDescriptor.name]; this.finalStages = action.getFinalStages?.() ?? []; diff --git a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts index 5da39a28bd4..c04b3d6277c 100644 --- a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts +++ b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts @@ -23,6 +23,7 @@ export function getPartialTargetDescriptors( case "pasteFromClipboard": case "insertSnippet": case "replace": + case "editNew": return [ getPartialTargetDescriptorFromDestination(command.action.destination), ]; diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index 1be4e9cbac9..0919f672138 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -1,12 +1,11 @@ import { + ActionDescriptor, ActionType, - CommandLatest, DestinationDescriptor, ImplicitTargetDescriptor, LATEST_VERSION, PartialPrimitiveTargetDescriptor, PartialTargetDescriptor, - SimpleActionName, SimpleScopeTypeType, } from "@cursorless/common"; import { runCursorlessCommand } from "@cursorless/vscode-common"; @@ -122,10 +121,8 @@ export default class KeyboardCommandsTargeted { } return await executeCursorlessCommand({ - action: { - name: "highlight", - target, - }, + name: "highlight", + target, }); }; @@ -139,34 +136,30 @@ export default class KeyboardCommandsTargeted { type = "containingScope", }: TargetScopeTypeArgument) => await executeCursorlessCommand({ - action: { - name: "highlight", - target: { - type: "primitive", - modifiers: [ - { - type, - scopeType: { - type: scopeType, - }, + name: "highlight", + target: { + type: "primitive", + modifiers: [ + { + type, + scopeType: { + type: scopeType, }, - ], - mark: { - type: "that", }, + ], + mark: { + type: "that", }, }, }); private highlightTarget = () => executeCursorlessCommand({ - action: { - name: "highlight", - target: { - type: "primitive", - mark: { - type: "that", - }, + name: "highlight", + target: { + type: "primitive", + mark: { + type: "that", }, }, }); @@ -193,49 +186,47 @@ export default class KeyboardCommandsTargeted { case "wrapWithSnippet": case "executeCommand": case "replace": + case "editNew": throw Error(`Unsupported keyboard action: ${action}`); case "replaceWithTarget": case "moveToTarget": returnValue = await executeCursorlessCommand({ - action: { - name: action, - source: target, - destination: toDestination({ type: "implicit" }), - }, + name: action, + source: target, + destination: toDestination({ type: "implicit" }), }); break; case "swapTargets": returnValue = await executeCursorlessCommand({ - action: { - name: action, - target1: target, - target2: { type: "implicit" }, - }, + name: action, + target1: target, + target2: { type: "implicit" }, }); break; case "callAsFunction": returnValue = await executeCursorlessCommand({ - action: { - name: action, - callee: target, - argument: { type: "implicit" }, - }, + name: action, + callee: target, + argument: { type: "implicit" }, }); break; case "pasteFromClipboard": returnValue = await executeCursorlessCommand({ - action: { - name: action, - destination: toDestination(target), - }, + name: action, + destination: toDestination(target), + }); + break; + case "generateSnippet": + case "highlight": + returnValue = await executeCursorlessCommand({ + name: action, + target, }); break; default: returnValue = await executeCursorlessCommand({ - action: { - name: action as SimpleActionName, - target, - }, + name: action, + target, }); } @@ -256,15 +247,13 @@ export default class KeyboardCommandsTargeted { */ targetSelection = () => executeCursorlessCommand({ - action: { - name: "highlight", - target: { - type: "primitive", - mark: { - type: "cursor", - }, - modifiers: [{ type: "toRawSelection" }], + name: "highlight", + target: { + type: "primitive", + mark: { + type: "cursor", }, + modifiers: [{ type: "toRawSelection" }], }, }); @@ -274,23 +263,19 @@ export default class KeyboardCommandsTargeted { */ clearTarget = () => executeCursorlessCommand({ - action: { - name: "highlight", - target: { - type: "primitive", - mark: { - type: "nothing", - }, + name: "highlight", + target: { + type: "primitive", + mark: { + type: "nothing", }, }, }); } -function executeCursorlessCommand( - command: Omit, -) { +function executeCursorlessCommand(action: ActionDescriptor) { return runCursorlessCommand({ - ...command, + action, version: LATEST_VERSION, usePrePhraseSnapshot: false, }); From 8c886b61fe7f6051480665d5945a5e21d3c380e1 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 15:42:42 +0100 Subject: [PATCH 71/99] Cleanup typing and make validate avoid mutation --- .../canonicalizeAndValidateCommand.ts | 21 ++++------ ...rgets.ts => canonicalizeTargetsInPlace.ts} | 12 +++--- .../canonicalizeActionName.ts | 0 .../upgradeV5ToV6/upgradeV5ToV6.ts | 38 +++++++++++-------- .../transformRecordedTests/checkMarks.ts | 2 +- .../src/testCaseRecorder/TestCase.ts | 2 +- .../src/util/getPartialTargetDescriptors.ts | 20 +++++----- 7 files changed, 49 insertions(+), 46 deletions(-) rename packages/cursorless-engine/src/core/commandVersionUpgrades/{canonicalizeTargets.ts => canonicalizeTargetsInPlace.ts} (82%) rename packages/cursorless-engine/src/core/commandVersionUpgrades/{ => upgradeV5ToV6}/canonicalizeActionName.ts (100%) diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts index 61c489c682a..fdb59af638c 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts @@ -7,20 +7,19 @@ import { LATEST_VERSION, Modifier, OutdatedExtensionError, - ActionDescriptor, PartialTargetDescriptor, SimpleScopeTypeType, } from "@cursorless/common"; import { getPartialTargetDescriptors } from "../../util/getPartialTargetDescriptors"; import { getPartialPrimitiveTargets } from "../../util/getPrimitiveTargets"; -import canonicalizeActionName from "./canonicalizeActionName"; -import canonicalizeTargets from "./canonicalizeTargets"; +import canonicalizeTargetsInPlace from "./canonicalizeTargetsInPlace"; import { upgradeV0ToV1 } from "./upgradeV0ToV1"; import { upgradeV1ToV2 } from "./upgradeV1ToV2"; import { upgradeV2ToV3 } from "./upgradeV2ToV3"; import { upgradeV3ToV4 } from "./upgradeV3ToV4"; import { upgradeV4ToV5 } from "./upgradeV4ToV5/upgradeV4ToV5"; import { upgradeV5ToV6 } from "./upgradeV5ToV6"; +import produce from "immer"; /** * Given a command argument which comes from the client, normalize it so that it @@ -35,19 +34,15 @@ export function canonicalizeAndValidateCommand( const commandUpgraded = upgradeCommand(command); const { action, usePrePhraseSnapshot = false, spokenForm } = commandUpgraded; - const actionName = canonicalizeActionName(action.name); - const partialTargets = getPartialTargetDescriptors(commandUpgraded); - - canonicalizeTargets(partialTargets); - validateCommand(actionName, partialTargets); - return { version: LATEST_VERSION, spokenForm, - action: { - ...action, - name: actionName, - } as ActionDescriptor, + action: produce(action, (draft) => { + const partialTargets = getPartialTargetDescriptors(draft); + + canonicalizeTargetsInPlace(partialTargets); + validateCommand(action.name, partialTargets); + }), usePrePhraseSnapshot, }; } diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargets.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargetsInPlace.ts similarity index 82% rename from packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargets.ts rename to packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargetsInPlace.ts index 2040cb6466a..df5d4cc7936 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargets.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargetsInPlace.ts @@ -17,7 +17,7 @@ const COLOR_CANONICALIZATION_MAPPING: Record = { purple: "pink", }; -function canonicalizeScopeTypes( +function canonicalizeScopeTypesInPlace( target: PartialPrimitiveTargetDescriptor, ): void { target.modifiers?.forEach((mod) => { @@ -29,7 +29,9 @@ function canonicalizeScopeTypes( }); } -function canonicalizeColors(target: PartialPrimitiveTargetDescriptor): void { +function canonicalizeColorsInPlace( + target: PartialPrimitiveTargetDescriptor, +): void { if (target.mark?.type === "decoratedSymbol") { target.mark = { ...target.mark, @@ -40,12 +42,12 @@ function canonicalizeColors(target: PartialPrimitiveTargetDescriptor): void { } } -export default function canonicalizeTargets( +export default function canonicalizeTargetsInPlace( partialTargets: PartialTargetDescriptor[], ): void { const partialPrimitiveTargets = getPartialPrimitiveTargets(partialTargets); partialPrimitiveTargets.forEach((target) => { - canonicalizeScopeTypes(target); - canonicalizeColors(target); + canonicalizeScopeTypesInPlace(target); + canonicalizeColorsInPlace(target); }); } diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeActionName.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/canonicalizeActionName.ts similarity index 100% rename from packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeActionName.ts rename to packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/canonicalizeActionName.ts diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index 54db7a0fc11..be22cd59a49 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -1,5 +1,6 @@ import { ActionCommandV5, + ActionDescriptor, CommandV5, CommandV6, DestinationDescriptor, @@ -12,7 +13,6 @@ import { ListDestinationDescriptor, Modifier, ModifierV5, - ActionDescriptor, PartialListTargetDescriptor, PartialListTargetDescriptorV5, PartialPrimitiveTargetDescriptor, @@ -24,9 +24,9 @@ import { PositionModifierV5, PrimitiveDestinationDescriptor, ReplaceWith, - SimpleActionName, WrapWithSnippetArg, } from "@cursorless/common"; +import canonicalizeActionName from "./canonicalizeActionName"; export function upgradeV5ToV6(command: CommandV5): CommandV6 { return { @@ -41,79 +41,87 @@ function upgradeAction( action: ActionCommandV5, targets: PartialTargetDescriptorV5[], ): ActionDescriptor { - switch (action.name) { + // We canonicalize once and for all + const name = canonicalizeActionName(action.name); + + switch (name) { case "replaceWithTarget": case "moveToTarget": return { - name: action.name, + name, source: upgradeTarget(targets[0]), destination: targetToDestination(targets[1]), }; case "swapTargets": return { - name: action.name, + name, target1: upgradeTarget(targets[0]), target2: upgradeTarget(targets[1]), }; case "callAsFunction": return { - name: action.name, + name, callee: upgradeTarget(targets[0]), argument: upgradeTarget(targets[1]), }; case "pasteFromClipboard": return { - name: action.name, + name, destination: targetToDestination(targets[0]), }; case "wrapWithPairedDelimiter": case "rewrapWithPairedDelimiter": return { - name: action.name, + name, left: action.args![0] as string, right: action.args![1] as string, target: upgradeTarget(targets[0]), }; case "generateSnippet": return { - name: action.name, + name, snippetName: action.args?.[0] as string, target: upgradeTarget(targets[0]), }; case "insertSnippet": return { - name: action.name, + name, snippetDescription: action.args![0] as InsertSnippetArg, destination: targetToDestination(targets[0]), }; case "wrapWithSnippet": return { - name: action.name, + name, snippetDescription: action.args![0] as WrapWithSnippetArg, target: upgradeTarget(targets[0]), }; case "executeCommand": return { - name: action.name, + name, commandId: action.args![0] as string, options: action.args?.[1] as ExecuteCommandOptions, target: upgradeTarget(targets[0]), }; case "replace": return { - name: action.name, + name, replaceWith: action.args![0] as ReplaceWith, destination: targetToDestination(targets[0]), }; case "highlight": return { - name: action.name, + name, highlightId: action.args?.[0] as HighlightId, target: upgradeTarget(targets[0]), }; + case "editNew": + return { + name, + destination: targetToDestination(targets[0]), + }; default: return { - name: action.name as SimpleActionName, + name, target: upgradeTarget(targets[0]), }; } diff --git a/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts b/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts index c0a5855c69a..e0e5dc01967 100644 --- a/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts +++ b/packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts @@ -13,7 +13,7 @@ export function checkMarks(originalFixture: TestCaseFixtureLegacy): undefined { injectIde(new FakeIDE()); const graphemeSplitter = tokenGraphemeSplitter(); - const targetedMarks = getPartialTargetDescriptors(command) + const targetedMarks = getPartialTargetDescriptors(command.action) .map(extractTargetKeys) .flat(); const normalizeGraphemes = (key: string): string => diff --git a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts index 73b09d9e3f8..e84bd905739 100644 --- a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts +++ b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts @@ -49,7 +49,7 @@ export class TestCase { ) { const activeEditor = ide().activeTextEditor!; this.command = command; - this.partialTargetDescriptors = getPartialTargetDescriptors(command); + this.partialTargetDescriptors = getPartialTargetDescriptors(command.action); this.targetKeys = this.partialTargetDescriptors .map(extractTargetKeys) .flat(); diff --git a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts index c04b3d6277c..4f7f3f64b0b 100644 --- a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts +++ b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts @@ -1,5 +1,5 @@ import { - CommandLatest, + ActionDescriptor, DestinationDescriptor, PartialPrimitiveTargetDescriptor, PartialRangeTargetDescriptor, @@ -7,28 +7,26 @@ import { } from "@cursorless/common"; export function getPartialTargetDescriptors( - command: CommandLatest, + action: ActionDescriptor, ): PartialTargetDescriptor[] { - switch (command.action.name) { + switch (action.name) { case "callAsFunction": - return [command.action.callee, command.action.argument]; + return [action.callee, action.argument]; case "replaceWithTarget": case "moveToTarget": return [ - command.action.source, - getPartialTargetDescriptorFromDestination(command.action.destination), + action.source, + getPartialTargetDescriptorFromDestination(action.destination), ]; case "swapTargets": - return [command.action.target1, command.action.target2]; + return [action.target1, action.target2]; case "pasteFromClipboard": case "insertSnippet": case "replace": case "editNew": - return [ - getPartialTargetDescriptorFromDestination(command.action.destination), - ]; + return [getPartialTargetDescriptorFromDestination(action.destination)]; default: - return [command.action.target]; + return [action.target]; } } From c6930624cdfa4d6f8388d2b6f217d89fdafedb6e Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 15:53:06 +0100 Subject: [PATCH 72/99] Js doc --- packages/common/src/types/command/ActionDescriptor.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index 3328426f7ff..de3b24847f9 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -4,6 +4,9 @@ import { } from "./PartialTargetDescriptor.types"; import { DestinationDescriptor } from "./DestinationDescriptor.types"; +/** + * A simple action takes only a single target and no other arguments. + */ const simpleActionNames = [ "clearAndSetSelection", "copyToClipboard", From afaff47824eb2573698d6bfc6d2becc286244cdb Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 15:54:33 +0100 Subject: [PATCH 73/99] Js doc --- packages/common/src/types/command/CommandV6.types.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/common/src/types/command/CommandV6.types.ts b/packages/common/src/types/command/CommandV6.types.ts index bd21525aec2..08b6fe1f306 100644 --- a/packages/common/src/types/command/CommandV6.types.ts +++ b/packages/common/src/types/command/CommandV6.types.ts @@ -19,5 +19,9 @@ export interface CommandV6 { */ usePrePhraseSnapshot: boolean; + /** + * The action to perform. This field contains everything necessary to actually + * perform the action. The other fields are just metadata. + */ action: ActionDescriptor; } From 8bff62ca0c2554f4bf0bf61ba2702454189100b8 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 16:04:10 +0100 Subject: [PATCH 74/99] Ja stock --- .../command/DestinationDescriptor.types.ts | 28 ++++++++++++++++++- .../command/PartialTargetDescriptor.types.ts | 2 -- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/packages/common/src/types/command/DestinationDescriptor.types.ts b/packages/common/src/types/command/DestinationDescriptor.types.ts index a835199f38c..7d04a8b0a60 100644 --- a/packages/common/src/types/command/DestinationDescriptor.types.ts +++ b/packages/common/src/types/command/DestinationDescriptor.types.ts @@ -1,24 +1,50 @@ import { - InsertionMode, PartialListTargetDescriptor, PartialPrimitiveTargetDescriptor, PartialRangeTargetDescriptor, } from "./PartialTargetDescriptor.types"; +/** + * The insertion mode to use when inserting relative to a target. + * - `before` inserts before the target. Depending on the target, a delimiter + * may be inserted after the inserted text. + * - `after` inserts after the target. Depending on the target, a delimiter may + * be inserted before the inserted text. + * - `to` replaces the target. However, this insertion mode may also be used + * when the target is really only a pseudo-target. For example, you could say + * `"bring type air to bat"` even if `bat` doesn't already have a type. In + * that case, `"take type bat"` wouldn't work, so `"type bat"` is really just + * a pseudo-target in that situation. + */ +export type InsertionMode = "before" | "after" | "to"; + export interface PrimitiveDestinationDescriptor { type: "primitive"; + + /** + * The insertion mode to use when inserting relative to {@link target}. + */ insertionMode: InsertionMode; + target: | PartialPrimitiveTargetDescriptor | PartialRangeTargetDescriptor | PartialListTargetDescriptor; } +/** + * A list of destinations. This is used when the user uses more than one insertion mode + * in a single command. For example, `"bring air after bat and before cap"`. + */ export interface ListDestinationDescriptor { type: "list"; destinations: PrimitiveDestinationDescriptor[]; } +/** + * An implicit destination. This is used for e.g. `"bring air"` (note the user + * doesn't explicitly specify the destination), or `"snip funk"`. + */ export interface ImplicitDestinationDescriptor { type: "implicit"; } diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index cb4991a37ca..556442bdf65 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -350,8 +350,6 @@ export type Modifier = // vertical puts a selection on each line vertically between the two targets export type PartialRangeType = "continuous" | "vertical"; -export type InsertionMode = "before" | "after" | "to"; - export interface PartialPrimitiveTargetDescriptor { type: "primitive"; mark?: PartialMark; From 02747e0826b8a30df5d547170e0e950029bf8eb0 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 16:10:52 +0100 Subject: [PATCH 75/99] move edit new stuff to new file --- .../cursorless-engine/src/actions/Actions.ts | 3 +- .../src/actions/EditNew/EditNew.ts | 40 +------------------ .../src/actions/EditNewLineAction.ts | 34 ++++++++++++++++ 3 files changed, 38 insertions(+), 39 deletions(-) create mode 100644 packages/cursorless-engine/src/actions/EditNewLineAction.ts diff --git a/packages/cursorless-engine/src/actions/Actions.ts b/packages/cursorless-engine/src/actions/Actions.ts index 4b2587037ae..e65cc10de41 100644 --- a/packages/cursorless-engine/src/actions/Actions.ts +++ b/packages/cursorless-engine/src/actions/Actions.ts @@ -6,7 +6,8 @@ import Call from "./Call"; import Clear from "./Clear"; import { CutToClipboard } from "./CutToClipboard"; import Deselect from "./Deselect"; -import { EditNew, EditNewAfter, EditNewBefore } from "./EditNew"; +import { EditNew } from "./EditNew"; +import { EditNewAfter, EditNewBefore } from "./EditNewLineAction"; import ExecuteCommand from "./ExecuteCommand"; import { FindInWorkspace } from "./Find"; import FollowLink from "./FollowLink"; diff --git a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts index 7d61e3d7dd7..4c58c90a81c 100644 --- a/packages/cursorless-engine/src/actions/EditNew/EditNew.ts +++ b/packages/cursorless-engine/src/actions/EditNew/EditNew.ts @@ -1,17 +1,10 @@ import { RangeUpdater } from "../../core/updateSelections/RangeUpdater"; -import { ModifierStageFactory } from "../../processTargets/ModifierStageFactory"; -import { ModifierStage } from "../../processTargets/PipelineStages.types"; -import { containingLineIfUntypedModifier } from "../../processTargets/modifiers/commonContainingScopeIfUntypedModifiers"; import { ide } from "../../singletons/ide.singleton"; -import { Destination, Target } from "../../typings/target.types"; +import { Destination } from "../../typings/target.types"; import { setSelectionsAndFocusEditor } from "../../util/setSelectionsAndFocusEditor"; import { createThatMark, ensureSingleEditor } from "../../util/targetUtils"; import { Actions } from "../Actions"; -import { - ActionRecord, - ActionReturnValue, - SimpleAction, -} from "../actions.types"; +import { ActionReturnValue } from "../actions.types"; import { State } from "./EditNew.types"; import { runEditTargets } from "./runEditTargets"; import { runInsertLineAfterTargets } from "./runInsertLineAfterTargets"; @@ -68,32 +61,3 @@ export class EditNew { }; } } - -abstract class EditNewLineAction implements SimpleAction { - getFinalStages(): ModifierStage[] { - return [this.modifierStageFactory.create(containingLineIfUntypedModifier)]; - } - - protected abstract insertionMode: "before" | "after"; - - constructor( - private actions: ActionRecord, - private modifierStageFactory: ModifierStageFactory, - ) { - this.run = this.run.bind(this); - } - - run(targets: Target[]): Promise { - return this.actions.editNew.run( - targets.map((target) => target.toDestination(this.insertionMode)), - ); - } -} - -export class EditNewBefore extends EditNewLineAction { - protected insertionMode = "before" as const; -} - -export class EditNewAfter extends EditNewLineAction { - protected insertionMode = "after" as const; -} diff --git a/packages/cursorless-engine/src/actions/EditNewLineAction.ts b/packages/cursorless-engine/src/actions/EditNewLineAction.ts new file mode 100644 index 00000000000..624bcc786e9 --- /dev/null +++ b/packages/cursorless-engine/src/actions/EditNewLineAction.ts @@ -0,0 +1,34 @@ +import { ModifierStageFactory } from "../processTargets/ModifierStageFactory"; +import { ModifierStage } from "../processTargets/PipelineStages.types"; +import { containingLineIfUntypedModifier } from "../processTargets/modifiers/commonContainingScopeIfUntypedModifiers"; +import { Target } from "../typings/target.types"; +import { ActionRecord, ActionReturnValue, SimpleAction } from "./actions.types"; + +abstract class EditNewLineAction implements SimpleAction { + getFinalStages(): ModifierStage[] { + return [this.modifierStageFactory.create(containingLineIfUntypedModifier)]; + } + + protected abstract insertionMode: "before" | "after"; + + constructor( + private actions: ActionRecord, + private modifierStageFactory: ModifierStageFactory, + ) { + this.run = this.run.bind(this); + } + + run(targets: Target[]): Promise { + return this.actions.editNew.run( + targets.map((target) => target.toDestination(this.insertionMode)), + ); + } +} + +export class EditNewBefore extends EditNewLineAction { + protected insertionMode = "before" as const; +} + +export class EditNewAfter extends EditNewLineAction { + protected insertionMode = "after" as const; +} From aef20ebd972fcc02dff9d1e948b624c38f013329 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 16:18:41 +0100 Subject: [PATCH 76/99] Continues splitting up bring move swap --- .../src/actions/BringMoveSwap.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/cursorless-engine/src/actions/BringMoveSwap.ts b/packages/cursorless-engine/src/actions/BringMoveSwap.ts index 14d4109fa46..74698b6f1ff 100644 --- a/packages/cursorless-engine/src/actions/BringMoveSwap.ts +++ b/packages/cursorless-engine/src/actions/BringMoveSwap.ts @@ -48,15 +48,6 @@ abstract class BringMoveSwap { constructor(private rangeUpdater: RangeUpdater, private type: ActionType) {} - protected broadcastSource(sources: Target[], destinations: Destination[]) { - if (sources.length === 1) { - // If there is only one source target, expand it to same length as - // destination target - return Array(destinations.length).fill(sources[0]); - } - return sources; - } - protected async decorateTargets(sources: Target[], destinations: Target[]) { await Promise.all([ flashTargets( @@ -284,6 +275,15 @@ abstract class BringMoveSwap { } } +function broadcastSource(sources: Target[], destinations: Destination[]) { + if (sources.length === 1) { + // If there is only one source target, expand it to same length as + // destination target + return Array(destinations.length).fill(sources[0]); + } + return sources; +} + export class Bring extends BringMoveSwap { decoration = { sourceStyle: FlashStyle.referenced, @@ -300,7 +300,7 @@ export class Bring extends BringMoveSwap { sources: Target[], destinations: Destination[], ): Promise { - sources = this.broadcastSource(sources, destinations); + sources = broadcastSource(sources, destinations); await this.decorateTargets( sources, @@ -335,7 +335,7 @@ export class Move extends BringMoveSwap { sources: Target[], destinations: Destination[], ): Promise { - sources = this.broadcastSource(sources, destinations); + sources = broadcastSource(sources, destinations); await this.decorateTargets( sources, From d360790376041d66440bedc5ff572463ceb2352b Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 16:28:09 +0100 Subject: [PATCH 77/99] Comments --- .../src/core/commandRunner/CommandRunnerImpl.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 7b9f25aa61c..31834e5a9e5 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -71,6 +71,8 @@ export class CommandRunnerImpl implements CommandRunner { private runAction( actionDescriptor: ActionDescriptor, ): Promise { + // Prepare to run the action by resetting the inference context and + // defaulting the final stages to an empty array this.inferenceContext.reset(); this.finalStages = []; @@ -206,6 +208,13 @@ export class CommandRunnerImpl implements CommandRunner { } } +/** + * Keeps track of the previous targets that have been passed to + * {@link InferenceContext.run} so that we can infer things like `"bring funk + * air to bat"` -> `"bring funk air to funk bat"`. In this case, there will be + * two calls to {@link InferenceContext.run}, first with the source `"funk air"` + * and then with the destination `"bat"`. + */ class InferenceContext { private previousTargets: PartialTargetDescriptor[] = []; From 41b72e819a127a6cd140c93cc794c9a434a27cd5 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 16:46:10 +0100 Subject: [PATCH 78/99] Add comments --- .../upgradeV5ToV6/upgradeV5ToV6.ts | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index be22cd59a49..cd3b8267a25 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -4,6 +4,7 @@ import { CommandV5, CommandV6, DestinationDescriptor, + EnforceUndefined, ExecuteCommandOptions, HighlightId, ImplicitDestinationDescriptor, @@ -28,7 +29,7 @@ import { } from "@cursorless/common"; import canonicalizeActionName from "./canonicalizeActionName"; -export function upgradeV5ToV6(command: CommandV5): CommandV6 { +export function upgradeV5ToV6(command: CommandV5): EnforceUndefined { return { version: 6, spokenForm: command.spokenForm, @@ -40,7 +41,7 @@ export function upgradeV5ToV6(command: CommandV5): CommandV6 { function upgradeAction( action: ActionCommandV5, targets: PartialTargetDescriptorV5[], -): ActionDescriptor { +): EnforceUndefined { // We canonicalize once and for all const name = canonicalizeActionName(action.name); @@ -80,7 +81,7 @@ function upgradeAction( case "generateSnippet": return { name, - snippetName: action.args?.[0] as string, + snippetName: action.args?.[0] as string | undefined, target: upgradeTarget(targets[0]), }; case "insertSnippet": @@ -99,7 +100,7 @@ function upgradeAction( return { name, commandId: action.args![0] as string, - options: action.args?.[1] as ExecuteCommandOptions, + options: action.args?.[1] as ExecuteCommandOptions | undefined, target: upgradeTarget(targets[0]), }; case "replace": @@ -111,7 +112,7 @@ function upgradeAction( case "highlight": return { name, - highlightId: action.args?.[0] as HighlightId, + highlightId: action.args?.[0] as HighlightId | undefined, target: upgradeTarget(targets[0]), }; case "editNew": @@ -132,11 +133,9 @@ function upgradeTarget( ): PartialTargetDescriptor { switch (target.type) { case "list": - return upgradeListTarget(target); case "range": - return upgradeRangeTarget(target); case "primitive": - return upgradePrimitiveTarget(target); + return upgradeNonImplicitTarget(target); case "implicit": return target; } @@ -191,7 +190,7 @@ function upgradePrimitiveTarget( ): PartialPrimitiveTargetDescriptor { return { ...target, - modifiers: getModifiers(target.modifiers), + modifiers: upgradeModifiers(target.modifiers), }; } @@ -223,6 +222,11 @@ function listTargetToDestination( target: upgradeNonImplicitTarget(element), }); } else { + // We handle `"before air and bat"` by distributing the `"before"` to all + // elements, so that it becomes `"before air and before bat"`. Note that + // when these destinatinos are actually constructed by Talon in non-legacy + // cursorless-talon, `"before air and bat"` would be a single destination + // with a compound target. destinations.push({ type: "primitive", insertionMode: destinations[destinations.length - 1].insertionMode, @@ -295,11 +299,13 @@ function getInsertionModeFromPrimitive( ) { return positionModifier.position; } + // "start" and "end" modifiers don't affect insertion mode; they remain as + // modifiers } return undefined; } -function getModifiers(modifiers?: ModifierV5[]): Modifier[] | undefined { +function upgradeModifiers(modifiers?: ModifierV5[]): Modifier[] | undefined { const result: Modifier[] = []; if (modifiers != null) { @@ -310,6 +316,8 @@ function getModifiers(modifiers?: ModifierV5[]): Modifier[] | undefined { } else if (modifier.position === "end") { result.push({ type: "endOf" }); } + + // Drop "before" and "after" modifiers } else { result.push(modifier as Modifier); } From 416db506865b45859565ce18bcfc6433ec6f0011 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 17:13:01 +0100 Subject: [PATCH 79/99] Make upgrade code more closely match Talon --- .../upgradeV5ToV6/upgradeV5ToV6.ts | 83 +++++++++++++------ .../processTargets/modifiers/PositionStage.ts | 12 ++- ...dHarpToEndOfThisAndEndOfWhaleTakeWhale.yml | 2 +- 3 files changed, 68 insertions(+), 29 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index cd3b8267a25..530810480f6 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -151,9 +151,8 @@ function upgradeNonImplicitTarget( case "list": return upgradeListTarget(target); case "range": - return upgradeRangeTarget(target); case "primitive": - return upgradePrimitiveTarget(target); + return upgradeRangeOrPrimitiveTarget(target); } } @@ -162,17 +161,21 @@ function upgradeListTarget( ): PartialListTargetDescriptor { return { ...target, - elements: target.elements.map((element) => { - switch (element.type) { - case "range": - return upgradeRangeTarget(element); - case "primitive": - return upgradePrimitiveTarget(element); - } - }), + elements: target.elements.map(upgradeRangeOrPrimitiveTarget), }; } +function upgradeRangeOrPrimitiveTarget( + target: PartialPrimitiveTargetDescriptorV5 | PartialRangeTargetDescriptorV5, +) { + switch (target.type) { + case "range": + return upgradeRangeTarget(target); + case "primitive": + return upgradePrimitiveTarget(target); + } +} + function upgradeRangeTarget( target: PartialRangeTargetDescriptorV5, ): PartialRangeTargetDescriptor { @@ -209,37 +212,65 @@ function targetToDestination( } } +/** + * Converts a list target to a destination. This is a bit tricky because we need + * to split the list into multiple destinations if there is more than one insertion + * mode. + * @param target The target to convert + * @returns The converted destination + */ function listTargetToDestination( target: PartialListTargetDescriptorV5, ): DestinationDescriptor { const destinations: PrimitiveDestinationDescriptor[] = []; + let currentElements: ( + | PartialPrimitiveTargetDescriptor + | PartialRangeTargetDescriptor + )[] = []; + let currentInsertionMode: InsertionMode | undefined = undefined; + target.elements.forEach((element) => { const insertionMode = getInsertionMode(element); - if (insertionMode != null || destinations.length === 0) { - destinations.push({ - type: "primitive", - insertionMode: insertionMode ?? "to", - target: upgradeNonImplicitTarget(element), - }); + + if (insertionMode != null) { + // If the insertion mode has changed, we need to create a new destination + // with the elements and insertion mode seen so far + if (currentElements.length > 0) { + destinations.push({ + type: "primitive", + insertionMode: currentInsertionMode ?? "to", + target: { + type: "list", + elements: currentElements, + }, + }); + } + + currentElements = [upgradeRangeOrPrimitiveTarget(element)]; + currentInsertionMode = insertionMode; } else { - // We handle `"before air and bat"` by distributing the `"before"` to all - // elements, so that it becomes `"before air and before bat"`. Note that - // when these destinatinos are actually constructed by Talon in non-legacy - // cursorless-talon, `"before air and bat"` would be a single destination - // with a compound target. - destinations.push({ - type: "primitive", - insertionMode: destinations[destinations.length - 1].insertionMode, - target: upgradeNonImplicitTarget(element), - }); + currentElements.push(upgradeRangeOrPrimitiveTarget(element)); } }); + + if (currentElements.length > 0) { + destinations.push({ + type: "primitive", + insertionMode: currentInsertionMode ?? "to", + target: { + type: "list", + elements: currentElements, + }, + }); + } + if (destinations.length > 1) { return { type: "list", destinations, } as ListDestinationDescriptor; } + return destinations[0]; } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts index fd254ef26ec..46bb6929b8b 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts @@ -1,7 +1,11 @@ import { EndOfModifier, Range, StartOfModifier } from "@cursorless/common"; import { Target } from "../../typings/target.types"; import { ModifierStage } from "../PipelineStages.types"; -import { CommonTargetParameters, RawSelectionTarget } from "../targets"; +import { + CommonTargetParameters, + PlainTarget, + RawSelectionTarget, +} from "../targets"; abstract class StartEndOfStage implements ModifierStage { run(target: Target): Target[] { @@ -11,7 +15,11 @@ abstract class StartEndOfStage implements ModifierStage { contentRange: this.constructContentRange(target.contentRange), }; - return [new RawSelectionTarget(parameters)]; + return [ + target.isRaw + ? new RawSelectionTarget(parameters) + : new PlainTarget({ ...parameters, isToken: false }), + ]; } protected abstract constructContentRange(contentRange: Range): Range; diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/hatTokenMap/bringPointAndHarpToEndOfThisAndEndOfWhaleTakeWhale.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/hatTokenMap/bringPointAndHarpToEndOfThisAndEndOfWhaleTakeWhale.yml index 01f52abab03..0a88a0f36fc 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/hatTokenMap/bringPointAndHarpToEndOfThisAndEndOfWhaleTakeWhale.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/hatTokenMap/bringPointAndHarpToEndOfThisAndEndOfWhaleTakeWhale.yml @@ -50,5 +50,5 @@ finalState: end: {line: 0, character: 5} default.w: start: {line: 0, character: 7} - end: {line: 0, character: 18} + end: {line: 0, character: 12} fullTargets: [{type: list, elements: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: .}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: h}, selectionType: token, position: contents, insideOutsideType: null, modifier: {type: identity}}]}, {type: list, elements: [{type: primitive, mark: {type: cursor}, selectionType: token, position: after, insideOutsideType: inside, modifier: {type: identity}}, {type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: w}, selectionType: token, position: after, insideOutsideType: inside, modifier: {type: identity}}]}] From ea7b2fe437640d2a756c372c402a1d6939f8c08f Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 20:11:45 +0100 Subject: [PATCH 80/99] more cleanup --- .../canonicalizeTargetsInPlace.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargetsInPlace.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargetsInPlace.ts index df5d4cc7936..0e1446dd7d6 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargetsInPlace.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeTargetsInPlace.ts @@ -33,20 +33,16 @@ function canonicalizeColorsInPlace( target: PartialPrimitiveTargetDescriptor, ): void { if (target.mark?.type === "decoratedSymbol") { - target.mark = { - ...target.mark, - symbolColor: - COLOR_CANONICALIZATION_MAPPING[target.mark.symbolColor] ?? - target.mark.symbolColor, - }; + target.mark.symbolColor = + COLOR_CANONICALIZATION_MAPPING[target.mark.symbolColor] ?? + target.mark.symbolColor; } } export default function canonicalizeTargetsInPlace( partialTargets: PartialTargetDescriptor[], ): void { - const partialPrimitiveTargets = getPartialPrimitiveTargets(partialTargets); - partialPrimitiveTargets.forEach((target) => { + getPartialPrimitiveTargets(partialTargets).forEach((target) => { canonicalizeScopeTypesInPlace(target); canonicalizeColorsInPlace(target); }); From b059c7e8392050d65d624fd6ebae11d4f2a85b9b Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Wed, 12 Jul 2023 20:17:44 +0100 Subject: [PATCH 81/99] cleanup inference comments --- .../core/commandRunner/CommandRunnerImpl.ts | 2 +- ...argets.ts => inferFullTargetDescriptor.ts} | 25 ++++++++----------- .../processTargets/TargetPipelineRunner.ts | 3 +-- 3 files changed, 13 insertions(+), 17 deletions(-) rename packages/cursorless-engine/src/core/{inferFullTargets.ts => inferFullTargetDescriptor.ts} (91%) diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 31834e5a9e5..24019028b8a 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -12,7 +12,7 @@ import { ModifierStage } from "../../processTargets/PipelineStages.types"; import { SelectionWithEditor } from "../../typings/Types"; import { Destination, Target } from "../../typings/target.types"; import { Debug } from "../Debug"; -import { inferFullTargetDescriptor } from "../inferFullTargets"; +import { inferFullTargetDescriptor } from "../inferFullTargetDescriptor"; import { selectionToStoredTarget } from "./selectionToStoredTarget"; export class CommandRunnerImpl implements CommandRunner { diff --git a/packages/cursorless-engine/src/core/inferFullTargets.ts b/packages/cursorless-engine/src/core/inferFullTargetDescriptor.ts similarity index 91% rename from packages/cursorless-engine/src/core/inferFullTargets.ts rename to packages/cursorless-engine/src/core/inferFullTargetDescriptor.ts index 01316024369..2f593a40275 100644 --- a/packages/cursorless-engine/src/core/inferFullTargets.ts +++ b/packages/cursorless-engine/src/core/inferFullTargetDescriptor.ts @@ -15,22 +15,19 @@ import { import { handleHoistedModifiers } from "./handleHoistedModifiers"; /** - * Performs inference on the partial targets provided by the user, using - * previous targets, global defaults, and action-specific defaults to fill out - * any details that may have been omitted in the spoken form. - * For example, we would automatically infer that `"take funk air and bat"` is - * equivalent to `"take funk air and funk bat"`. + * Performs inference on the partial target provided by the user, using previous + * targets, global defaults, and action-specific defaults to fill out any + * details that may have been omitted in the spoken form. For example, we would + * automatically infer that `"take funk air and bat"` is equivalent to `"take + * funk air and funk bat"`. * @param targets The partial targets which need to be completed by inference. - * @returns Target objects fully filled out and ready to be processed by {@link processTargets}. + * @param previousTargets The targets that precede the target we are trying to + * infer. We look in these targets and their descendants when doing inference so + * that eg "bring funk air to bat" will be expanded to "bring funk air to funk + * bat". + * @returns Target objects fully filled out and ready to be processed by the + * target pipeline runner. */ -export default function inferFullTargetDescriptors( - targets: PartialTargetDescriptor[], -): TargetDescriptor[] { - return targets.map((target, index) => - inferFullTargetDescriptor(target, targets.slice(0, index)), - ); -} - export function inferFullTargetDescriptor( target: PartialTargetDescriptor, previousTargets: PartialTargetDescriptor[], diff --git a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts index 2f3c0337b20..272e33ea101 100644 --- a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts @@ -30,8 +30,7 @@ export class TargetPipelineRunner { * concrete representation usable by actions. Conceptually, the input will be * something like "the function call argument containing the cursor" and the * output will be something like "line 3, characters 5 through 10". - * @param targets The abstract target representations provided by the user - * @param actionPrePositionStages Modifier stages contributed by the action + * @param target The abstract target representations provided by the user * @param actionFinalStages Modifier stages contributed by the action that * should run at the end of the modifier pipeline * @returns A list of lists of typed selections, one list per input target. From 4d252fdff888a4dd606ac76af3c97a1b1ce4dd66 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 13 Jul 2023 10:51:31 +0200 Subject: [PATCH 82/99] Added type for get text action --- .../common/src/types/command/ActionDescriptor.ts | 16 ++++++++++++++-- .../cursorless-engine/src/actions/GetText.ts | 8 ++++---- .../src/actions/actions.types.ts | 8 ++++++++ .../src/core/commandRunner/CommandRunnerImpl.ts | 6 ++++++ .../upgradeV5ToV6/upgradeV5ToV6.ts | 7 +++++++ .../src/keyboard/KeyboardCommandsTargeted.ts | 1 + 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/packages/common/src/types/command/ActionDescriptor.ts b/packages/common/src/types/command/ActionDescriptor.ts index de3b24847f9..690b808ee93 100644 --- a/packages/common/src/types/command/ActionDescriptor.ts +++ b/packages/common/src/types/command/ActionDescriptor.ts @@ -19,7 +19,6 @@ const simpleActionNames = [ "findInWorkspace", "foldRegion", "followLink", - "getText", "indentLine", "insertCopyAfter", "insertCopyBefore", @@ -54,6 +53,7 @@ const complexActionNames = [ "editNew", "executeCommand", "generateSnippet", + "getText", "highlight", "insertSnippet", "moveToTarget", @@ -200,6 +200,17 @@ export interface EditNewActionDescriptor { destination: DestinationDescriptor; } +export interface GetTextActionOptions { + showDecorations?: boolean; + ensureSingleTarget?: boolean; +} + +export interface GetTextActionDescriptor { + name: "getText"; + options?: GetTextActionOptions; + target: PartialTargetDescriptor; +} + export type ActionDescriptor = | SimpleActionDescriptor | BringMoveActionDescriptor @@ -213,4 +224,5 @@ export type ActionDescriptor = | InsertSnippetActionDescriptor | WrapWithSnippetActionDescriptor | WrapWithPairedDelimiterActionDescriptor - | EditNewActionDescriptor; + | EditNewActionDescriptor + | GetTextActionDescriptor; diff --git a/packages/cursorless-engine/src/actions/GetText.ts b/packages/cursorless-engine/src/actions/GetText.ts index 6cbee53691e..fdfb0efe297 100644 --- a/packages/cursorless-engine/src/actions/GetText.ts +++ b/packages/cursorless-engine/src/actions/GetText.ts @@ -1,10 +1,10 @@ -import { FlashStyle } from "@cursorless/common"; +import { FlashStyle, GetTextActionOptions } from "@cursorless/common"; import { ide } from "../singletons/ide.singleton"; import { Target } from "../typings/target.types"; import { ensureSingleTarget, flashTargets } from "../util/targetUtils"; -import { SimpleAction, ActionReturnValue } from "./actions.types"; +import { ActionReturnValue } from "./actions.types"; -export default class GetText implements SimpleAction { +export default class GetText { constructor() { this.run = this.run.bind(this); } @@ -14,7 +14,7 @@ export default class GetText implements SimpleAction { { showDecorations = true, ensureSingleTarget: doEnsureSingleTarget = false, - } = {}, + }: GetTextActionOptions = {}, ): Promise { if (showDecorations) { await flashTargets(ide(), targets, FlashStyle.referenced); diff --git a/packages/cursorless-engine/src/actions/actions.types.ts b/packages/cursorless-engine/src/actions/actions.types.ts index a394e0470fc..560c3bdb354 100644 --- a/packages/cursorless-engine/src/actions/actions.types.ts +++ b/packages/cursorless-engine/src/actions/actions.types.ts @@ -1,5 +1,6 @@ import type { ExecuteCommandOptions, + GetTextActionOptions, HighlightId, InsertSnippetArg, ReplaceWith, @@ -155,4 +156,11 @@ export interface ActionRecord extends Record { highlightId?: HighlightId, ): Promise; }; + + getText: { + run( + target: Target[], + options?: GetTextActionOptions, + ): Promise; + }; } diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 24019028b8a..90a7ce9a0b9 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -170,6 +170,12 @@ export class CommandRunnerImpl implements CommandRunner { this.getDestinations(actionDescriptor.destination), ); + case "getText": + return this.actions.getText.run( + this.getTargets(actionDescriptor.target), + actionDescriptor.options, + ); + default: { const action = this.actions[actionDescriptor.name]; this.finalStages = action.getFinalStages?.() ?? []; diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts index 530810480f6..12d1dfb1881 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/upgradeV5ToV6/upgradeV5ToV6.ts @@ -6,6 +6,7 @@ import { DestinationDescriptor, EnforceUndefined, ExecuteCommandOptions, + GetTextActionOptions, HighlightId, ImplicitDestinationDescriptor, ImplicitTargetDescriptor, @@ -120,6 +121,12 @@ function upgradeAction( name, destination: targetToDestination(targets[0]), }; + case "getText": + return { + name, + options: action.args?.[0] as GetTextActionOptions | undefined, + target: upgradeTarget(targets[0]), + }; default: return { name, diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index 0919f672138..a666f8a60b9 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -187,6 +187,7 @@ export default class KeyboardCommandsTargeted { case "executeCommand": case "replace": case "editNew": + case "getText": throw Error(`Unsupported keyboard action: ${action}`); case "replaceWithTarget": case "moveToTarget": From e4817109f379f1c95331932988edf7bead49c1ae Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 13 Jul 2023 10:56:02 +0200 Subject: [PATCH 83/99] Added get text test --- .../src/suite/getText.vscode.test.ts | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 packages/cursorless-vscode-e2e/src/suite/getText.vscode.test.ts diff --git a/packages/cursorless-vscode-e2e/src/suite/getText.vscode.test.ts b/packages/cursorless-vscode-e2e/src/suite/getText.vscode.test.ts new file mode 100644 index 00000000000..ad1dd415d0d --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/getText.vscode.test.ts @@ -0,0 +1,37 @@ +import { + getCursorlessApi, + openNewEditor, + runCursorlessCommand, +} from "@cursorless/vscode-common"; +import * as assert from "assert"; +import { endToEndTestSetup } from "../endToEndTestSetup"; + +suite("getText", async function () { + endToEndTestSetup(this); + + test("getText", getText); +}); + +async function getText() { + const { hatTokenMap } = (await getCursorlessApi()).testHelpers!; + await openNewEditor("foo bar baz"); + await hatTokenMap.allocateHats(); + + const result = await runCursorlessCommand({ + version: 6, + usePrePhraseSnapshot: false, + action: { + name: "getText", + target: { + type: "primitive", + mark: { + type: "decoratedSymbol", + symbolColor: "default", + character: "b", + }, + }, + }, + }); + + assert.equal(result, "bar"); +} From a387d9719fa28f89ad1c3535d1eef015368a5aad Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Thu, 13 Jul 2023 12:24:52 +0200 Subject: [PATCH 84/99] Updated get text test --- .../fixtures/recorded/actions/getTextAir.yml | 36 ++++++++++++++++++ .../src/suite/getText.vscode.test.ts | 37 ------------------- 2 files changed, 36 insertions(+), 37 deletions(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/getTextAir.yml delete mode 100644 packages/cursorless-vscode-e2e/src/suite/getText.vscode.test.ts diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/getTextAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/getTextAir.yml new file mode 100644 index 00000000000..bb13cd67405 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/getTextAir.yml @@ -0,0 +1,36 @@ +languageId: plaintext +command: + version: 6 + action: + name: getText + options: {showDecorations: false, ensureSingleTarget: null} + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: a} + usePrePhraseSnapshot: true +initialState: + documentContents: | + foo bar baz + selections: + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} + marks: + default.a: + start: {line: 0, character: 4} + end: {line: 0, character: 7} +finalState: + documentContents: | + foo bar baz + selections: + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} + thatMark: + - type: UntypedTarget + contentRange: + start: {line: 0, character: 4} + end: {line: 0, character: 7} + isReversed: false + hasExplicitRange: false +returnValue: [bar] +ide: + flashes: [] diff --git a/packages/cursorless-vscode-e2e/src/suite/getText.vscode.test.ts b/packages/cursorless-vscode-e2e/src/suite/getText.vscode.test.ts deleted file mode 100644 index ad1dd415d0d..00000000000 --- a/packages/cursorless-vscode-e2e/src/suite/getText.vscode.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - getCursorlessApi, - openNewEditor, - runCursorlessCommand, -} from "@cursorless/vscode-common"; -import * as assert from "assert"; -import { endToEndTestSetup } from "../endToEndTestSetup"; - -suite("getText", async function () { - endToEndTestSetup(this); - - test("getText", getText); -}); - -async function getText() { - const { hatTokenMap } = (await getCursorlessApi()).testHelpers!; - await openNewEditor("foo bar baz"); - await hatTokenMap.allocateHats(); - - const result = await runCursorlessCommand({ - version: 6, - usePrePhraseSnapshot: false, - action: { - name: "getText", - target: { - type: "primitive", - mark: { - type: "decoratedSymbol", - symbolColor: "default", - character: "b", - }, - }, - }, - }); - - assert.equal(result, "bar"); -} From 62c4b95bed3d6e492786c1c75b241e3d5a161ad3 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:03:14 +0100 Subject: [PATCH 85/99] cleanup --- .../src/core/inferFullTargetDescriptor.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/cursorless-engine/src/core/inferFullTargetDescriptor.ts b/packages/cursorless-engine/src/core/inferFullTargetDescriptor.ts index 2f593a40275..81e613bdaab 100644 --- a/packages/cursorless-engine/src/core/inferFullTargetDescriptor.ts +++ b/packages/cursorless-engine/src/core/inferFullTargetDescriptor.ts @@ -209,11 +209,9 @@ function getPreviousTargetAttribute( const target = previousTargets[i]; switch (target.type) { case "primitive": { - if (getAttribute != null) { - const attributeValue = getAttribute(target); - if (attributeValue != null) { - return attributeValue; - } + const attributeValue = getAttribute(target); + if (attributeValue != null) { + return attributeValue; } break; } From 53ecf4caba38770229477f907cb82638d00a998c Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:05:00 +0100 Subject: [PATCH 86/99] cleanup --- .../processTargets/ModifierStageFactoryImpl.ts | 4 ++-- .../processTargets/modifiers/PositionStage.ts | 18 +++++------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts index e172a5d9e91..a6566884734 100644 --- a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts @@ -50,9 +50,9 @@ export class ModifierStageFactoryImpl implements ModifierStageFactory { create(modifier: Modifier): ModifierStage { switch (modifier.type) { case "startOf": - return new StartOfStage(modifier); + return new StartOfStage(); case "endOf": - return new EndOfStage(modifier); + return new EndOfStage(); case "extendThroughStartOf": return new HeadStage(this, modifier); case "extendThroughEndOf": diff --git a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts index 46bb6929b8b..29b4a87b168 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts @@ -1,4 +1,4 @@ -import { EndOfModifier, Range, StartOfModifier } from "@cursorless/common"; +import { Range } from "@cursorless/common"; import { Target } from "../../typings/target.types"; import { ModifierStage } from "../PipelineStages.types"; import { @@ -12,7 +12,7 @@ abstract class StartEndOfStage implements ModifierStage { const parameters: CommonTargetParameters = { editor: target.editor, isReversed: target.isReversed, - contentRange: this.constructContentRange(target.contentRange), + contentRange: this.getContentRange(target.contentRange), }; return [ @@ -22,25 +22,17 @@ abstract class StartEndOfStage implements ModifierStage { ]; } - protected abstract constructContentRange(contentRange: Range): Range; + protected abstract getContentRange(contentRange: Range): Range; } export class StartOfStage extends StartEndOfStage { - constructor(_modifier: StartOfModifier) { - super(); - } - - protected constructContentRange(contentRange: Range): Range { + protected getContentRange(contentRange: Range): Range { return contentRange.start.toEmptyRange(); } } export class EndOfStage extends StartEndOfStage { - constructor(_modifier: EndOfModifier) { - super(); - } - - protected constructContentRange(contentRange: Range): Range { + protected getContentRange(contentRange: Range): Range { return contentRange.end.toEmptyRange(); } } From 5acd5774fc048b5a438a3b7f886b4b470bd6c183 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:05:42 +0100 Subject: [PATCH 87/99] rename --- .../src/processTargets/modifiers/PositionStage.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts index 29b4a87b168..a76dc163f83 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/PositionStage.ts @@ -7,7 +7,7 @@ import { RawSelectionTarget, } from "../targets"; -abstract class StartEndOfStage implements ModifierStage { +abstract class PositionStage implements ModifierStage { run(target: Target): Target[] { const parameters: CommonTargetParameters = { editor: target.editor, @@ -25,13 +25,13 @@ abstract class StartEndOfStage implements ModifierStage { protected abstract getContentRange(contentRange: Range): Range; } -export class StartOfStage extends StartEndOfStage { +export class StartOfStage extends PositionStage { protected getContentRange(contentRange: Range): Range { return contentRange.start.toEmptyRange(); } } -export class EndOfStage extends StartEndOfStage { +export class EndOfStage extends PositionStage { protected getContentRange(contentRange: Range): Range { return contentRange.end.toEmptyRange(); } From a9ac2da7ec08421e6a03ad59da5666d4033b1b98 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:17:02 +0100 Subject: [PATCH 88/99] cleanup --- .../processTargets/targets/DestinationImpl.ts | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts index 3c39595bdd9..6eab1aaffa1 100644 --- a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts +++ b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts @@ -3,7 +3,6 @@ import { Range, Selection, TextEditor, - UnsupportedError, } from "@cursorless/common"; import { EditWithRangeUpdater } from "../../typings/Types"; import { @@ -19,8 +18,8 @@ export default class DestinationImpl implements Destination { private readonly indentationString: string; constructor( - public target: Target, - public insertionMode: InsertionMode, + public readonly target: Target, + public readonly insertionMode: InsertionMode, indentationString?: string, ) { this.contentRange = getContentRange(target.contentRange, insertionMode); @@ -161,22 +160,6 @@ export default class DestinationImpl implements Destination { } } -export function removalUnsupportedForPosition( - insertionMode: InsertionMode, -): Range { - if (insertionMode === "to") { - throw new UnsupportedError( - `Removal is not supported for "${insertionMode}"`, - ); - } - - const preferredModifier = insertionMode === "after" ? "trailing" : "leading"; - - throw new UnsupportedError( - `Please use "${preferredModifier}" modifier; removal is not supported for "${insertionMode}"`, - ); -} - /** Calculate the minimum indentation/padding for a range */ function getIndentationString(editor: TextEditor, range: Range) { let length = Number.MAX_SAFE_INTEGER; From 794c164936d331390c1661ab4b9064e646214e92 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:17:57 +0100 Subject: [PATCH 89/99] cleanup --- .../src/processTargets/targets/NotebookCellDestination.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts b/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts index fe909f44e0e..da2b7074d8c 100644 --- a/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts +++ b/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts @@ -34,7 +34,7 @@ export default class NotebookCellDestination implements Destination { return this.target.isRaw; } - withTarget(target: NotebookCellTarget): Destination { + withTarget(target: NotebookCellTarget): NotebookCellDestination { return new NotebookCellDestination(target, this.insertionMode); } From eb327e4293b21c93650e9c997bd5dbf547d07961 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:29:21 +0100 Subject: [PATCH 90/99] cleanup --- .../src/processTargets/TargetPipelineRunner.ts | 10 +++++----- .../src/processTargets/targets/VerticalRangeTarget.ts | 4 ---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts index 272e33ea101..442e8ec6993 100644 --- a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts @@ -151,7 +151,7 @@ class TargetPipeline { return [ targetsToContinuousTarget( excludeAnchor - ? excludeScope( + ? getExcludedScope( this.modifierStageFactory, anchorTarget, exclusionScopeType, @@ -159,7 +159,7 @@ class TargetPipeline { ) : anchorTarget, excludeActive - ? excludeScope( + ? getExcludedScope( this.modifierStageFactory, activeTarget, exclusionScopeType, @@ -254,17 +254,17 @@ export function processModifierStages( return targets; } -function excludeScope( +function getExcludedScope( modifierStageFactory: ModifierStageFactory, target: Target, - exclusionScopeType: ScopeType, + scopeType: ScopeType, direction: Direction, ): Target { return ( modifierStageFactory .create({ type: "relativeScope", - scopeType: exclusionScopeType, + scopeType, direction, length: 1, offset: 1, diff --git a/packages/cursorless-engine/src/processTargets/targets/VerticalRangeTarget.ts b/packages/cursorless-engine/src/processTargets/targets/VerticalRangeTarget.ts index 98e0eccc5cf..38ce7f7aa62 100644 --- a/packages/cursorless-engine/src/processTargets/targets/VerticalRangeTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/VerticalRangeTarget.ts @@ -4,10 +4,6 @@ interface VerticalRangeTargetParameters extends CommonTargetParameters { readonly insertionDelimiter: string; } -/** - * A target that has no leading or trailing delimiters so it's removal range - * just consists of the content itself. Its insertion delimiter is given by argument. - */ export default class VerticalRangeTarget extends BaseTarget { type = "VerticalRangeTarget"; isToken = false; From 5c4debfe0066106e0a229fe1655bf4304d4510e8 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:35:41 +0100 Subject: [PATCH 91/99] cleanup --- .../scripts/transformRecordedTests/transformations/upgrade.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/scripts/transformRecordedTests/transformations/upgrade.ts b/packages/cursorless-engine/src/scripts/transformRecordedTests/transformations/upgrade.ts index ec303ddbc16..1926bedc9cd 100644 --- a/packages/cursorless-engine/src/scripts/transformRecordedTests/transformations/upgrade.ts +++ b/packages/cursorless-engine/src/scripts/transformRecordedTests/transformations/upgrade.ts @@ -8,6 +8,6 @@ export const upgrade = flow(upgradeCommand, reorderFields); function upgradeCommand(fixture: TestCaseFixtureLegacy): TestCaseFixture { return { ...fixture, - command: flow(canonicalizeAndValidateCommand)(fixture.command), + command: canonicalizeAndValidateCommand(fixture.command), }; } From 822c07bd73cc2fbdaa61d59137a060f214cf6b9f Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:36:38 +0100 Subject: [PATCH 92/99] more --- packages/cursorless-engine/src/testCaseRecorder/TestCase.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts index e84bd905739..00ab9e6cb7c 100644 --- a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts +++ b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts @@ -21,7 +21,7 @@ import { StoredTargetMap } from ".."; import { ide } from "../singletons/ide.singleton"; import { extractTargetKeys } from "../testUtil/extractTargetKeys"; import { takeSnapshot } from "../testUtil/takeSnapshot"; -import { getPartialTargetDescriptors as getPartialTargetDescriptors } from "../util/getPartialTargetDescriptors"; +import { getPartialTargetDescriptors } from "../util/getPartialTargetDescriptors"; export class TestCase { private languageId: string; From a26aac441e0a0b8da032b86f5edce1699342f5f4 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 15:16:12 +0100 Subject: [PATCH 93/99] Cleanup --- .../src/util/getPartialTargetDescriptors.ts | 33 +++++-------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts index 4f7f3f64b0b..0b088c3aa55 100644 --- a/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts +++ b/packages/cursorless-engine/src/util/getPartialTargetDescriptors.ts @@ -1,8 +1,6 @@ import { ActionDescriptor, DestinationDescriptor, - PartialPrimitiveTargetDescriptor, - PartialRangeTargetDescriptor, PartialTargetDescriptor, } from "@cursorless/common"; @@ -16,7 +14,7 @@ export function getPartialTargetDescriptors( case "moveToTarget": return [ action.source, - getPartialTargetDescriptorFromDestination(action.destination), + ...getPartialTargetDescriptorsFromDestination(action.destination), ]; case "swapTargets": return [action.target1, action.target2]; @@ -24,36 +22,21 @@ export function getPartialTargetDescriptors( case "insertSnippet": case "replace": case "editNew": - return [getPartialTargetDescriptorFromDestination(action.destination)]; + return getPartialTargetDescriptorsFromDestination(action.destination); default: return [action.target]; } } -export function getPartialTargetDescriptorFromDestination( +function getPartialTargetDescriptorsFromDestination( destination: DestinationDescriptor, -): PartialTargetDescriptor { +): PartialTargetDescriptor[] { switch (destination.type) { - case "list": { - const elements: ( - | PartialPrimitiveTargetDescriptor - | PartialRangeTargetDescriptor - )[] = []; - destination.destinations.forEach((destination) => { - if (destination.target.type === "list") { - elements.push(...destination.target.elements); - } else { - elements.push(destination.target); - } - }); - return { - type: "list", - elements, - }; - } + case "list": + return destination.destinations.map(({ target }) => target); case "primitive": - return destination.target; + return [destination.target]; case "implicit": - return destination; + return []; } } From 12044c4b5ddcd7e3a356521e8186e95b2933989f Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 15:22:21 +0100 Subject: [PATCH 94/99] More cleanup --- .../src/keyboard/KeyboardCommandsTargeted.ts | 49 +++++++------------ 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts index a666f8a60b9..80d44036c28 100644 --- a/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts +++ b/packages/cursorless-vscode/src/keyboard/KeyboardCommandsTargeted.ts @@ -1,8 +1,6 @@ import { ActionDescriptor, ActionType, - DestinationDescriptor, - ImplicitTargetDescriptor, LATEST_VERSION, PartialPrimitiveTargetDescriptor, PartialTargetDescriptor, @@ -165,11 +163,11 @@ export default class KeyboardCommandsTargeted { }); /** - * Performs action {@link action} on the current target - * @param action The action to run + * Performs action {@link name} on the current target + * @param name The action to run * @returns A promise that resolves to the result of the cursorless command */ - performActionOnTarget = async (action: ActionType) => { + performActionOnTarget = async (name: ActionType) => { const target: PartialPrimitiveTargetDescriptor = { type: "primitive", mark: { @@ -179,7 +177,7 @@ export default class KeyboardCommandsTargeted { let returnValue: unknown; - switch (action) { + switch (name) { case "wrapWithPairedDelimiter": case "rewrapWithPairedDelimiter": case "insertSnippet": @@ -188,52 +186,56 @@ export default class KeyboardCommandsTargeted { case "replace": case "editNew": case "getText": - throw Error(`Unsupported keyboard action: ${action}`); + throw Error(`Unsupported keyboard action: ${name}`); case "replaceWithTarget": case "moveToTarget": returnValue = await executeCursorlessCommand({ - name: action, + name, source: target, - destination: toDestination({ type: "implicit" }), + destination: { type: "implicit" }, }); break; case "swapTargets": returnValue = await executeCursorlessCommand({ - name: action, + name, target1: target, target2: { type: "implicit" }, }); break; case "callAsFunction": returnValue = await executeCursorlessCommand({ - name: action, + name, callee: target, argument: { type: "implicit" }, }); break; case "pasteFromClipboard": returnValue = await executeCursorlessCommand({ - name: action, - destination: toDestination(target), + name, + destination: { + type: "primitive", + insertionMode: "to", + target, + }, }); break; case "generateSnippet": case "highlight": returnValue = await executeCursorlessCommand({ - name: action, + name, target, }); break; default: returnValue = await executeCursorlessCommand({ - name: action, + name, target, }); } await this.highlightTarget(); - if (EXIT_CURSORLESS_MODE_ACTIONS.includes(action)) { + if (EXIT_CURSORLESS_MODE_ACTIONS.includes(name)) { // For some Cursorless actions, it is more convenient if we automatically // exit modal mode await this.modal.modeOff(); @@ -282,21 +284,6 @@ function executeCursorlessCommand(action: ActionDescriptor) { }); } -function toDestination( - target: PartialPrimitiveTargetDescriptor | ImplicitTargetDescriptor, -): DestinationDescriptor { - switch (target.type) { - case "primitive": - return { - type: "primitive", - insertionMode: "to", - target, - }; - case "implicit": - return target; - } -} - const EXIT_CURSORLESS_MODE_ACTIONS: ActionType[] = [ "setSelectionBefore", "setSelectionAfter", From 71d6f565b7b3e78d51ef5f26b9a5444f5b9a84b4 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 15:36:34 +0100 Subject: [PATCH 95/99] Remove `VerticalRangeTarget` --- .../processTargets/TargetPipelineRunner.ts | 4 +-- .../src/processTargets/targets/PlainTarget.ts | 8 ++++-- .../targets/VerticalRangeTarget.ts | 27 ------------------- .../src/processTargets/targets/index.ts | 2 -- 4 files changed, 8 insertions(+), 33 deletions(-) delete mode 100644 packages/cursorless-engine/src/processTargets/targets/VerticalRangeTarget.ts diff --git a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts index 442e8ec6993..c5f147d8b02 100644 --- a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts @@ -17,7 +17,7 @@ import { ModifierStageFactory } from "./ModifierStageFactory"; import { MarkStage, ModifierStage } from "./PipelineStages.types"; import ImplicitStage from "./marks/ImplicitStage"; import { ContainingTokenIfUntypedEmptyStage } from "./modifiers/ConditionalModifierStages"; -import { VerticalRangeTarget } from "./targets"; +import { PlainTarget } from "./targets"; export class TargetPipelineRunner { constructor( @@ -348,7 +348,7 @@ function targetsToVerticalTarget( ); results.push( - new VerticalRangeTarget({ + new PlainTarget({ editor: anchorTarget.editor, isReversed: anchorTarget.isReversed, contentRange, diff --git a/packages/cursorless-engine/src/processTargets/targets/PlainTarget.ts b/packages/cursorless-engine/src/processTargets/targets/PlainTarget.ts index 4d826524e01..06d993270b3 100644 --- a/packages/cursorless-engine/src/processTargets/targets/PlainTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/PlainTarget.ts @@ -2,19 +2,22 @@ import { BaseTarget, CommonTargetParameters } from "."; interface PlainTargetParameters extends CommonTargetParameters { readonly isToken?: boolean; + readonly insertionDelimiter?: string; } /** * A target that has no leading or trailing delimiters so it's removal range - * just consists of the content itself. Its insertion delimiter is empty string. + * just consists of the content itself. Its insertion delimiter is empty string, + * unless specified. */ export default class PlainTarget extends BaseTarget { type = "PlainTarget"; - insertionDelimiter = ""; + insertionDelimiter: string; constructor(parameters: PlainTargetParameters) { super(parameters); this.isToken = parameters.isToken ?? true; + this.insertionDelimiter = parameters.insertionDelimiter ?? ""; } getLeadingDelimiterTarget = () => undefined; @@ -25,6 +28,7 @@ export default class PlainTarget extends BaseTarget { return { ...this.state, isToken: this.isToken, + insertionDelimiter: this.insertionDelimiter, }; } } diff --git a/packages/cursorless-engine/src/processTargets/targets/VerticalRangeTarget.ts b/packages/cursorless-engine/src/processTargets/targets/VerticalRangeTarget.ts deleted file mode 100644 index 38ce7f7aa62..00000000000 --- a/packages/cursorless-engine/src/processTargets/targets/VerticalRangeTarget.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { BaseTarget, CommonTargetParameters } from "."; - -interface VerticalRangeTargetParameters extends CommonTargetParameters { - readonly insertionDelimiter: string; -} - -export default class VerticalRangeTarget extends BaseTarget { - type = "VerticalRangeTarget"; - isToken = false; - insertionDelimiter: string; - - constructor(parameters: VerticalRangeTargetParameters) { - super(parameters); - this.insertionDelimiter = parameters.insertionDelimiter; - } - - getLeadingDelimiterTarget = () => undefined; - getTrailingDelimiterTarget = () => undefined; - getRemovalRange = () => this.contentRange; - - protected getCloneParameters() { - return { - ...this.state, - insertionDelimiter: this.insertionDelimiter, - }; - } -} diff --git a/packages/cursorless-engine/src/processTargets/targets/index.ts b/packages/cursorless-engine/src/processTargets/targets/index.ts index 13b4244bc2b..68fec06b05e 100644 --- a/packages/cursorless-engine/src/processTargets/targets/index.ts +++ b/packages/cursorless-engine/src/processTargets/targets/index.ts @@ -11,8 +11,6 @@ export * from "./ParagraphTarget"; export { default as ParagraphTarget } from "./ParagraphTarget"; export * from "./PlainTarget"; export { default as PlainTarget } from "./PlainTarget"; -export * from "./VerticalRangeTarget"; -export { default as VerticalRangeTarget } from "./VerticalRangeTarget"; export * from "./DestinationImpl"; export { default as DestinationImpl } from "./DestinationImpl"; export * from "./RawSelectionTarget"; From 43ee76db0198120fce11ccad49f789622337805e Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 15:37:41 +0100 Subject: [PATCH 96/99] Remove some default imports --- .../src/processTargets/targets/DestinationImpl.ts | 2 +- .../src/processTargets/targets/NotebookCellDestination.ts | 2 +- packages/cursorless-engine/src/processTargets/targets/index.ts | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts index 6eab1aaffa1..1b5d787f03b 100644 --- a/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts +++ b/packages/cursorless-engine/src/processTargets/targets/DestinationImpl.ts @@ -11,7 +11,7 @@ import { Target, } from "../../typings/target.types"; -export default class DestinationImpl implements Destination { +export class DestinationImpl implements Destination { public readonly contentRange: Range; private readonly isLineDelimiter: boolean; private readonly isBefore: boolean; diff --git a/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts b/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts index da2b7074d8c..9f28d148bb0 100644 --- a/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts +++ b/packages/cursorless-engine/src/processTargets/targets/NotebookCellDestination.ts @@ -8,7 +8,7 @@ import { EditWithRangeUpdater } from "../../typings/Types"; import { Destination, EditNewActionType } from "../../typings/target.types"; import NotebookCellTarget from "./NotebookCellTarget"; -export default class NotebookCellDestination implements Destination { +export class NotebookCellDestination implements Destination { constructor( public target: NotebookCellTarget, public insertionMode: InsertionMode, diff --git a/packages/cursorless-engine/src/processTargets/targets/index.ts b/packages/cursorless-engine/src/processTargets/targets/index.ts index 68fec06b05e..0154f82e78f 100644 --- a/packages/cursorless-engine/src/processTargets/targets/index.ts +++ b/packages/cursorless-engine/src/processTargets/targets/index.ts @@ -6,13 +6,12 @@ export * from "./LineTarget"; export { default as LineTarget } from "./LineTarget"; export * from "./NotebookCellTarget"; export { default as NotebookCellTarget } from "./NotebookCellTarget"; -export { default as NotebookCellDestination } from "./NotebookCellDestination"; +export * from "./NotebookCellDestination"; export * from "./ParagraphTarget"; export { default as ParagraphTarget } from "./ParagraphTarget"; export * from "./PlainTarget"; export { default as PlainTarget } from "./PlainTarget"; export * from "./DestinationImpl"; -export { default as DestinationImpl } from "./DestinationImpl"; export * from "./RawSelectionTarget"; export { default as RawSelectionTarget } from "./RawSelectionTarget"; export * from "./ScopeTypeTarget"; From c53d1bdf0bd3e79bfb9714fbb3ee270b294fe446 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 15:45:45 +0100 Subject: [PATCH 97/99] clean up tests --- .../bringFineAfterThis.yml | 0 .../bringWhaleBeforeThis.yml | 0 .../bringFineAfterJustThis.yml | 32 +++++++++++++++++++ .../bringWhaleBeforeJustThis.yml | 32 +++++++++++++++++++ 4 files changed, 64 insertions(+) rename packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/{updateSelections => implicitExpansion}/bringFineAfterThis.yml (100%) rename packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/{updateSelections => implicitExpansion}/bringWhaleBeforeThis.yml (100%) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringFineAfterJustThis.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringWhaleBeforeJustThis.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringFineAfterThis.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/implicitExpansion/bringFineAfterThis.yml similarity index 100% rename from packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringFineAfterThis.yml rename to packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/implicitExpansion/bringFineAfterThis.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringWhaleBeforeThis.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/implicitExpansion/bringWhaleBeforeThis.yml similarity index 100% rename from packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringWhaleBeforeThis.yml rename to packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/implicitExpansion/bringWhaleBeforeThis.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringFineAfterJustThis.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringFineAfterJustThis.yml new file mode 100644 index 00000000000..4c02f80f8d1 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringFineAfterJustThis.yml @@ -0,0 +1,32 @@ +languageId: plaintext +command: + version: 6 + spokenForm: bring fine after just this + action: + name: replaceWithTarget + source: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: f} + destination: + type: primitive + insertionMode: after + target: + type: primitive + modifiers: + - {type: toRawSelection} + mark: {type: cursor} + usePrePhraseSnapshot: true +initialState: + documentContents: foo world + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + marks: + default.f: + start: {line: 0, character: 0} + end: {line: 0, character: 3} +finalState: + documentContents: foo fooworld + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringWhaleBeforeJustThis.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringWhaleBeforeJustThis.yml new file mode 100644 index 00000000000..40ca5dc8504 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/updateSelections/bringWhaleBeforeJustThis.yml @@ -0,0 +1,32 @@ +languageId: plaintext +command: + version: 6 + spokenForm: bring whale before just this + action: + name: replaceWithTarget + source: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: w} + destination: + type: primitive + insertionMode: before + target: + type: primitive + modifiers: + - {type: toRawSelection} + mark: {type: cursor} + usePrePhraseSnapshot: true +initialState: + documentContents: foo world + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} + marks: + default.w: + start: {line: 0, character: 4} + end: {line: 0, character: 9} +finalState: + documentContents: fooworld world + selections: + - anchor: {line: 0, character: 8} + active: {line: 0, character: 8} From db052bd02d047471262a3589dfdafc282419650f Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 15:46:53 +0100 Subject: [PATCH 98/99] whoops --- .../cursorless-engine/src/processTargets/targets/BaseTarget.ts | 2 +- .../src/processTargets/targets/NotebookCellTarget.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts b/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts index ddced4a4438..da49eb96b24 100644 --- a/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/BaseTarget.ts @@ -14,7 +14,7 @@ import { createContinuousRange, createContinuousRangeUntypedTarget, } from "../targetUtil/createContinuousRange"; -import DestinationImpl from "./DestinationImpl"; +import { DestinationImpl } from "./DestinationImpl"; /** Parameters supported by all target classes */ export interface MinimumTargetParameters { diff --git a/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts b/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts index e96cbfab60d..cd71744ee81 100644 --- a/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/NotebookCellTarget.ts @@ -1,7 +1,7 @@ import { InsertionMode } from "@cursorless/common"; import { BaseTarget, CommonTargetParameters } from "."; import { Destination } from "../../typings/target.types"; -import NotebookCellDestination from "./NotebookCellDestination"; +import { NotebookCellDestination } from "./NotebookCellDestination"; export default class NotebookCellTarget extends BaseTarget { type = "NotebookCellTarget"; From 081d5df6ada8c1014b1b8787f173604916fe5466 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Thu, 13 Jul 2023 15:57:04 +0100 Subject: [PATCH 99/99] Re-add removed tests --- .../recorded/actions/giveEndOfDot.yml | 39 +++++++++++++++++++ .../recorded/actions/giveStartOfDot.yml | 39 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveEndOfDot.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveStartOfDot.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveEndOfDot.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveEndOfDot.yml new file mode 100644 index 00000000000..910716c16eb --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveEndOfDot.yml @@ -0,0 +1,39 @@ +languageId: plaintext +command: + version: 6 + spokenForm: give end of dot + action: + name: deselect + target: + type: primitive + modifiers: + - {type: endOf} + mark: {type: decoratedSymbol, symbolColor: default, character: .} + usePrePhraseSnapshot: true +initialState: + documentContents: a b.c + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + marks: + default..: + start: {line: 0, character: 3} + end: {line: 0, character: 4} +finalState: + documentContents: a b.c + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} + thatMark: + - type: PlainTarget + contentRange: + start: {line: 0, character: 4} + end: {line: 0, character: 4} + isReversed: false + hasExplicitRange: true diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveStartOfDot.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveStartOfDot.yml new file mode 100644 index 00000000000..6f88d94bf3b --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/actions/giveStartOfDot.yml @@ -0,0 +1,39 @@ +languageId: plaintext +command: + version: 6 + spokenForm: give start of dot + action: + name: deselect + target: + type: primitive + modifiers: + - {type: startOf} + mark: {type: decoratedSymbol, symbolColor: default, character: .} + usePrePhraseSnapshot: true +initialState: + documentContents: a b.c + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + marks: + default..: + start: {line: 0, character: 3} + end: {line: 0, character: 4} +finalState: + documentContents: a b.c + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + thatMark: + - type: PlainTarget + contentRange: + start: {line: 0, character: 3} + end: {line: 0, character: 3} + isReversed: false + hasExplicitRange: true