diff --git a/data/fixtures/recorded/actions/bringName.yml b/data/fixtures/recorded/actions/bringName.yml new file mode 100644 index 0000000000..87d9e4dcff --- /dev/null +++ b/data/fixtures/recorded/actions/bringName.yml @@ -0,0 +1,86 @@ +languageId: typescript +command: + version: 7 + spokenForm: bring name + action: + name: replaceWithTarget + source: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + destination: {type: implicit} + usePrePhraseSnapshot: false +initialState: + documentContents: |- + function aaa() { + ; + ; + } + + function bbb() { + ; + } + selections: + - anchor: {line: 1, character: 2} + active: {line: 1, character: 2} + - anchor: {line: 2, character: 2} + active: {line: 2, character: 2} + - anchor: {line: 6, character: 2} + active: {line: 6, character: 2} + marks: {} +finalState: + documentContents: |- + function aaa() { + aaa; + aaa; + } + + function bbb() { + bbb; + } + selections: + - anchor: {line: 1, character: 5} + active: {line: 1, character: 5} + - anchor: {line: 2, character: 5} + active: {line: 2, character: 5} + - anchor: {line: 6, character: 5} + active: {line: 6, character: 5} + thatMark: + - type: UntypedTarget + contentRange: + start: {line: 1, character: 2} + end: {line: 1, character: 5} + isReversed: false + hasExplicitRange: true + - type: UntypedTarget + contentRange: + start: {line: 2, character: 2} + end: {line: 2, character: 5} + isReversed: false + hasExplicitRange: true + - type: UntypedTarget + contentRange: + start: {line: 6, character: 2} + end: {line: 6, character: 5} + isReversed: false + hasExplicitRange: true + sourceMark: + - type: UntypedTarget + contentRange: + start: {line: 0, character: 9} + end: {line: 0, character: 12} + isReversed: false + hasExplicitRange: true + - type: UntypedTarget + contentRange: + start: {line: 0, character: 9} + end: {line: 0, character: 12} + isReversed: false + hasExplicitRange: true + - type: UntypedTarget + contentRange: + start: {line: 5, character: 9} + end: {line: 5, character: 12} + isReversed: false + hasExplicitRange: true diff --git a/data/fixtures/recorded/hatTokenMap/bringPointAndHarpToEndOfThisAndEndOfWhaleTakeWhale.yml b/data/fixtures/recorded/hatTokenMap/bringPointAndHarpToEndOfThisAndEndOfWhaleTakeWhale.yml index 339965dd3b..b28aaf97be 100644 --- a/data/fixtures/recorded/hatTokenMap/bringPointAndHarpToEndOfThisAndEndOfWhaleTakeWhale.yml +++ b/data/fixtures/recorded/hatTokenMap/bringPointAndHarpToEndOfThisAndEndOfWhaleTakeWhale.yml @@ -56,4 +56,4 @@ finalState: end: {line: 0, character: 5} default.w: start: {line: 0, character: 7} - end: {line: 0, character: 12} + end: {line: 0, character: 18} diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 812359dcbf..d4c8ebb90f 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -27,6 +27,7 @@ export class CommandRunnerImpl implements CommandRunner { private inferenceContext: InferenceContext; private finalStages: ModifierStage[] = []; private noAutomaticTokenExpansion: boolean | undefined; + private allowDuplicateTargets: boolean | undefined; constructor( private commandServerApi: CommandServerApi, @@ -103,6 +104,7 @@ export class CommandRunnerImpl implements CommandRunner { switch (actionDescriptor.name) { case "replaceWithTarget": + this.allowDuplicateTargets = true; return this.actions.replaceWithTarget.run( this.getTargets(actionDescriptor.source), this.getDestinations(actionDescriptor.destination), @@ -239,6 +241,7 @@ export class CommandRunnerImpl implements CommandRunner { return this.pipelineRunner.run(targetDescriptor, { actionFinalStages: this.finalStages, noAutomaticTokenExpansion: this.noAutomaticTokenExpansion, + allowDuplicateTargets: this.allowDuplicateTargets, }); } diff --git a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts index 8f146c5940..ba1359a4a2 100644 --- a/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/cursorless-engine/src/processTargets/TargetPipelineRunner.ts @@ -27,6 +27,7 @@ import { PlainTarget } from "./targets"; interface TargetPipelineRunnerOpts { actionFinalStages?: ModifierStage[]; noAutomaticTokenExpansion?: boolean; + allowDuplicateTargets?: boolean; } export class TargetPipelineRunner { @@ -53,13 +54,14 @@ export class TargetPipelineRunner { { actionFinalStages = [], noAutomaticTokenExpansion = false, + allowDuplicateTargets = false, }: TargetPipelineRunnerOpts = {}, ): Target[] { return new TargetPipeline( this.modifierStageFactory, this.markStageFactory, target, - { actionFinalStages, noAutomaticTokenExpansion }, + { actionFinalStages, noAutomaticTokenExpansion, allowDuplicateTargets }, ).run(); } } @@ -87,7 +89,8 @@ class TargetPipeline { * the target */ run(): Target[] { - return uniqTargets(this.processTarget(this.target)); + const targets = this.processTarget(this.target); + return this.opts.allowDuplicateTargets ? targets : uniqTargets(targets); } processTarget(target: TargetDescriptor): Target[] {