diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index c575f36a1a..1c3d9a6b41 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -129,6 +129,7 @@ export type SimpleScopeTypeType = | "identifier" | "nonWhitespaceSequence" | "boundedNonWhitespaceSequence" + | "instance" | "url"; export interface SimpleScopeType { diff --git a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts index 7bdea8395a..e428649eda 100644 --- a/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts +++ b/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts @@ -119,7 +119,7 @@ function usesScopeType( ); } -export async function checkForOldInference( +export function checkForOldInference( partialTargets: PartialTargetDescriptor[], ) { const hasOldInference = partialTargets.some((target) => { @@ -136,16 +136,16 @@ export async function checkForOldInference( const hideInferenceWarning = globalState.get("hideInferenceWarning"); if (!hideInferenceWarning) { - const pressed = await showWarning( + 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", - ); - - if (pressed) { - globalState.set("hideInferenceWarning", true); - } + ).then((pressed) => { + if (pressed) { + globalState.set("hideInferenceWarning", true); + } + }); } } } diff --git a/packages/cursorless-engine/src/core/inferFullTargets.ts b/packages/cursorless-engine/src/core/inferFullTargets.ts index 61799a8367..e0611436af 100644 --- a/packages/cursorless-engine/src/core/inferFullTargets.ts +++ b/packages/cursorless-engine/src/core/inferFullTargets.ts @@ -9,14 +9,13 @@ import { PartialTargetDescriptor, PositionModifier, } from "@cursorless/common"; +import { findLastIndex } from "lodash"; import { EveryRangeTargetDescriptor, PrimitiveTargetDescriptor, RangeTargetDescriptor, TargetDescriptor, } from "../typings/TargetDescriptor"; -import { findLastIndex } from "lodash"; -import produce from "immer"; /** * Performs inference on the partial targets provided by the user, using @@ -90,13 +89,11 @@ function inferRangeTarget( active: inferPrimitiveTarget( target.active, previousTargets.concat(target.anchor), + true, ), }; - const isAnchorMarkImplicit = - target.anchor.type === "implicit" || target.anchor.mark == null; - - return handleEveryRangeTarget(fullTarget, isAnchorMarkImplicit) ?? fullTarget; + return handleEveryRangeTarget(fullTarget) ?? fullTarget; } /** @@ -127,12 +124,9 @@ function inferRangeTarget( * resulting range. * * @param fullTarget The full range target, post-inference - * @param isAnchorMarkImplicit `true` if the anchor mark was implicit on the - * original partial target */ function handleEveryRangeTarget( fullTarget: RangeTargetDescriptor, - isAnchorMarkImplicit: boolean, ): EveryRangeTargetDescriptor | null { const { anchor, rangeType, active, excludeAnchor, excludeActive } = fullTarget; @@ -154,6 +148,10 @@ function handleEveryRangeTarget( anchor.modifiers[everyScopeModifierIndex] as EveryScopeModifier ).scopeType; + if (scopeType.type === "instance") { + return null; + } + const [beforeEveryModifiers, afterEveryModifiers] = [ anchor.modifiers.slice(0, everyScopeModifierIndex), anchor.modifiers.slice(everyScopeModifierIndex + 1), @@ -167,7 +165,7 @@ function handleEveryRangeTarget( // If they say "every line past bat", the anchor is implicit, even though // it comes across the wire as a primitive target due to the "every line", // which we've now removed - afterEveryModifiers.length === 0 && isAnchorMarkImplicit + afterEveryModifiers.length === 0 && anchor.mark == null ? { type: "implicit" } : { type: "primitive", @@ -175,16 +173,7 @@ function handleEveryRangeTarget( positionModifier: undefined, modifiers: afterEveryModifiers, }, - // Remove the "every" (and everything before it) from the active if it - // ended up there from inference - active: produce(active, (draft) => { - draft.modifiers = draft.modifiers.slice( - findLastIndex( - draft.modifiers, - (modifier) => modifier.type === "everyScope", - ) + 1, - ); - }), + active, modifiers: beforeEveryModifiers, positionModifier: anchor.positionModifier, excludeAnchor, @@ -206,6 +195,7 @@ function inferPossiblyImplicitTarget( function inferPrimitiveTarget( target: PartialPrimitiveTargetDescriptor, previousTargets: PartialTargetDescriptor[], + isActive: boolean = false, ): PrimitiveTargetDescriptor { const ownPositionModifier = getPositionModifier(target); const ownModifiers = getPreservedModifiers(target); @@ -217,9 +207,25 @@ function inferPrimitiveTarget( : null) ?? undefined; - const modifiers = + let modifiers = ownModifiers ?? getPreviousPreservedModifiers(previousTargets) ?? []; + if (isActive) { + // If we're inferring the active end of a range target, we don't want any + // every or instance modifiers, because those will be handled by rewrites, + // because "past" binds more tightly than "every" or "instance" + modifiers = modifiers.slice( + findLastIndex( + modifiers, + (modifier) => + modifier.type === "everyScope" || + ((modifier.type === "relativeScope" || + modifier.type === "ordinalScope") && + modifier.scopeType.type === "instance"), + ) + 1, + ); + } + const positionModifier = ownPositionModifier ?? getPreviousPositionModifier(previousTargets); diff --git a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts index b13e03344f..95eb9d2446 100644 --- a/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts +++ b/packages/cursorless-engine/src/testCaseRecorder/TestCase.ts @@ -120,9 +120,7 @@ export class TestCase { visibleRanges: !visibleRangeActions.includes(this.command.action.name), }; - return Object.keys(excludedFields).filter( - (field) => excludedFields[field], - ); + return Object.keys(excludedFields).filter((field) => excludedFields[field]); } toYaml() {