From f4a966a45af781db60d5496c0eeaa28fa6b5aeaa Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 22 Mar 2026 10:10:58 +0100 Subject: [PATCH 1/6] Made sorted scope handler a custom scope handler --- .../command/PartialTargetDescriptor.types.ts | 6 --- .../scopeHandlers/FallbackScopeHandler.ts | 25 +++++---- .../scopeHandlers/SortedScopeHandler.ts | 54 ++++--------------- .../scopeHandlers/scopeHandler.types.ts | 9 ++++ 4 files changed, 35 insertions(+), 59 deletions(-) diff --git a/packages/lib-common/src/types/command/PartialTargetDescriptor.types.ts b/packages/lib-common/src/types/command/PartialTargetDescriptor.types.ts index 13a9239a9d..191430f7b8 100644 --- a/packages/lib-common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/lib-common/src/types/command/PartialTargetDescriptor.types.ts @@ -260,11 +260,6 @@ export interface SurroundingPairInteriorScopeType { delimiter: SurroundingPairName; } -export interface OneOfScopeType { - type: "oneOf"; - scopeTypes: ScopeType[]; -} - export interface GlyphScopeType { type: "glyph"; character: string; @@ -275,7 +270,6 @@ export type ScopeType = | SurroundingPairScopeType | SurroundingPairInteriorScopeType | CustomRegexScopeType - | OneOfScopeType | GlyphScopeType; export interface ContainingSurroundingPairModifier extends ContainingScopeModifier { diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts index d9bc38fa4f..9a3782a36d 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts @@ -1,8 +1,6 @@ import { - NoContainingScopeError, type Direction, type Position, - type ScopeType, type TextEditor, } from "@cursorless/lib-common"; import { BaseScopeHandler } from "./BaseScopeHandler"; @@ -23,9 +21,17 @@ export class FallbackScopeHandler extends BaseScopeHandler { scopeType: FallbackScopeType, languageId: string, ): ScopeHandler { - const scopeHandlers = scopeType.scopeTypes.map((scopeType) => - scopeHandlerFactory.create(scopeType, languageId), - ); + const scopeHandlers = scopeType.scopeTypes + .map((scopeType) => + scopeHandlerFactory.maybeCreate(scopeType, languageId), + ) + .filter( + (scopeHandler): scopeHandler is ScopeHandler => scopeHandler != null, + ); + + if (scopeHandlers.length === 1) { + return scopeHandlers[0]; + } return this.createFromScopeHandlers(scopeHandlers); } @@ -38,10 +44,11 @@ export class FallbackScopeHandler extends BaseScopeHandler { super(); } - get iterationScopeType(): ScopeType { - throw new NoContainingScopeError( - "Iteration scope for FallbackScopeHandler", - ); + get iterationScopeType(): FallbackScopeType { + return { + type: "fallback", + scopeTypes: this.scopeHandlers.map((s) => s.iterationScopeType), + }; } *generateScopeCandidates( diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SortedScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SortedScopeHandler.ts index e798279326..15d0fc8635 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SortedScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SortedScopeHandler.ts @@ -1,18 +1,13 @@ -import type { - Direction, - OneOfScopeType, - Position, - TextEditor, -} from "@cursorless/lib-common"; +import type { Direction, Position, TextEditor } from "@cursorless/lib-common"; import { BaseScopeHandler } from "./BaseScopeHandler"; import { advanceIteratorsUntil, getInitialIteratorInfos } from "./IteratorInfo"; import type { ScopeHandlerFactory } from "./ScopeHandlerFactory"; import { compareTargetScopes } from "./compareTargetScopes"; import type { TargetScope } from "./scope.types"; import type { - CustomScopeType, ScopeHandler, ScopeIteratorRequirements, + SortedScopeType, } from "./scopeHandler.types"; export class SortedScopeHandler extends BaseScopeHandler { @@ -23,7 +18,7 @@ export class SortedScopeHandler extends BaseScopeHandler { static create( scopeHandlerFactory: ScopeHandlerFactory, - scopeType: OneOfScopeType, + scopeType: SortedScopeType, languageId: string, ): ScopeHandler { const scopeHandlers = scopeType.scopeTypes @@ -38,50 +33,21 @@ export class SortedScopeHandler extends BaseScopeHandler { return scopeHandlers[0]; } - return this.createFromScopeHandlers( - scopeHandlerFactory, - languageId, - scopeHandlers, - ); + return this.createFromScopeHandlers(scopeHandlers); } - static createFromScopeHandlers( - scopeHandlerFactory: ScopeHandlerFactory, - languageId: string, - scopeHandlers: ScopeHandler[], - ): ScopeHandler { - const getIterationScopeHandler = () => - new SortedScopeHandler( - scopeHandlers.map((scopeHandler) => - scopeHandlerFactory.create( - scopeHandler.iterationScopeType, - languageId, - ), - ), - () => { - throw new Error( - "SortedScopeHandler: Iteration scope is not implemented", - ); - }, - ); - - return new SortedScopeHandler(scopeHandlers, getIterationScopeHandler); + static createFromScopeHandlers(scopeHandlers: ScopeHandler[]): ScopeHandler { + return new SortedScopeHandler(scopeHandlers); } - private constructor( - private scopeHandlers: ScopeHandler[], - private getIterationScopeHandler: () => SortedScopeHandler, - ) { + private constructor(private scopeHandlers: ScopeHandler[]) { super(); } - get iterationScopeType(): CustomScopeType { - if (this.iterationScopeHandler == null) { - this.iterationScopeHandler = this.getIterationScopeHandler(); - } + get iterationScopeType(): SortedScopeType { return { - type: "custom", - scopeHandler: this.iterationScopeHandler, + type: "sorted", + scopeTypes: this.scopeHandlers.map((s) => s.iterationScopeType), }; } diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/scopeHandler.types.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/scopeHandler.types.ts index fa975cabae..3ea134cce0 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/scopeHandler.types.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/scopeHandler.types.ts @@ -25,6 +25,14 @@ export interface FallbackScopeType { scopeTypes: (ScopeType | ComplexScopeType)[]; } +/** + * Used to handle sorted scope types. The scope types are yielded in sorted order. + */ +export interface SortedScopeType { + type: "sorted"; + scopeTypes: (ScopeType | ComplexScopeType)[]; +} + /** * Used to handle conditional scope types. The predicate determines if the * scope should be yielded or not. @@ -38,6 +46,7 @@ export interface ConditionalScopeType { export type ComplexScopeType = | CustomScopeType | FallbackScopeType + | SortedScopeType | ConditionalScopeType; /** From 0a591a699279a34e2f9df3cb7b9a2f716f5ae1d3 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 22 Mar 2026 10:56:00 +0100 Subject: [PATCH 2/6] Added complex modifier type --- .../primitiveTargetToSpokenForm.ts | 1 - .../src/processTargets/ModifierStageFactory.ts | 3 ++- .../src/processTargets/ModifierStageFactoryImpl.ts | 12 ++++++------ .../modifiers/ContainingScopeStage.ts | 5 ++--- .../src/processTargets/modifiers/HeadTailStage.ts | 2 +- .../src/processTargets/modifiers/InteriorStage.ts | 9 +++++---- .../processTargets/modifiers/PreferredScopeStage.ts | 1 - .../src/processTargets/modifiers/modifier.types.ts | 10 ++++++++++ .../modifiers/scopeHandlers/BoundedScopeHandler.ts | 13 +++---------- .../CollectionItemScopeHandler.ts | 9 ++++----- .../scopeHandlers/NotebookCellScopeHandler.ts | 9 ++++----- .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 2 +- .../modifiers/scopeHandlers/SortedScopeHandler.ts | 7 ++++++- .../modifiers/scopeHandlers/scopeHandler.types.ts | 3 ++- .../src/scopeProviders/ScopeInfoProvider.ts | 9 --------- 15 files changed, 46 insertions(+), 49 deletions(-) create mode 100644 packages/lib-engine/src/processTargets/modifiers/modifier.types.ts diff --git a/packages/lib-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts b/packages/lib-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts index 6a9ab25a29..3fde97e050 100644 --- a/packages/lib-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts +++ b/packages/lib-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts @@ -226,7 +226,6 @@ export class PrimitiveTargetSpokenFormGenerator { handleScopeType(scopeType: ScopeType): SpokenFormComponent { switch (scopeType.type) { - case "oneOf": case "surroundingPairInterior": throw new NoSpokenFormError(`Scope type '${scopeType.type}'`); case "glyph": diff --git a/packages/lib-engine/src/processTargets/ModifierStageFactory.ts b/packages/lib-engine/src/processTargets/ModifierStageFactory.ts index e0ea848494..b0a3c22d65 100644 --- a/packages/lib-engine/src/processTargets/ModifierStageFactory.ts +++ b/packages/lib-engine/src/processTargets/ModifierStageFactory.ts @@ -1,6 +1,7 @@ import type { Modifier } from "@cursorless/lib-common"; import type { ModifierStage } from "./PipelineStages.types"; +import type { ComplexModifier } from "./modifiers/modifier.types"; export interface ModifierStageFactory { - create(modifier: Modifier): ModifierStage; + create(modifier: Modifier | ComplexModifier): ModifierStage; } diff --git a/packages/lib-engine/src/processTargets/ModifierStageFactoryImpl.ts b/packages/lib-engine/src/processTargets/ModifierStageFactoryImpl.ts index ab6ade2553..cb784472a0 100644 --- a/packages/lib-engine/src/processTargets/ModifierStageFactoryImpl.ts +++ b/packages/lib-engine/src/processTargets/ModifierStageFactoryImpl.ts @@ -24,6 +24,7 @@ import { RangeModifierStage } from "./modifiers/RangeModifierStage"; import { RawSelectionStage } from "./modifiers/RawSelectionStage"; import { RelativeScopeStage } from "./modifiers/RelativeScopeStage"; import { VisibleStage } from "./modifiers/VisibleStage"; +import type { ComplexModifier } from "./modifiers/modifier.types"; import type { ScopeHandlerFactory } from "./modifiers/scopeHandlers/ScopeHandlerFactory"; export class ModifierStageFactoryImpl implements ModifierStageFactory { @@ -35,7 +36,7 @@ export class ModifierStageFactoryImpl implements ModifierStageFactory { this.create = this.create.bind(this); } - create(modifier: Modifier): ModifierStage { + create(modifier: Modifier | ComplexModifier): ModifierStage { switch (modifier.type) { case "startOf": return new StartOfStage(); @@ -62,11 +63,10 @@ export class ModifierStageFactoryImpl implements ModifierStageFactory { if (ClassFunctionNameStage.use(modifier.scopeType)) { return new ClassFunctionNameStage(this, modifier); } - return new ContainingScopeStage( - this, - this.scopeHandlerFactory, - modifier, - ); + return new ContainingScopeStage(this.scopeHandlerFactory, modifier); + + case "complexContainingScope": + return new ContainingScopeStage(this.scopeHandlerFactory, modifier); case "preferredScope": if (ClassFunctionNameStage.use(modifier.scopeType)) { diff --git a/packages/lib-engine/src/processTargets/modifiers/ContainingScopeStage.ts b/packages/lib-engine/src/processTargets/modifiers/ContainingScopeStage.ts index fd3b7a3f46..35359688f5 100644 --- a/packages/lib-engine/src/processTargets/modifiers/ContainingScopeStage.ts +++ b/packages/lib-engine/src/processTargets/modifiers/ContainingScopeStage.ts @@ -1,9 +1,9 @@ import type { ContainingScopeModifier } from "@cursorless/lib-common"; import { NoContainingScopeError } from "@cursorless/lib-common"; import type { Target } from "../../typings/target.types"; -import type { ModifierStageFactory } from "../ModifierStageFactory"; import type { ModifierStage } from "../PipelineStages.types"; import { getContainingScopeTarget } from "./getContainingScopeTarget"; +import type { ComplexContainingScopeModifier } from "./modifier.types"; import type { ScopeHandlerFactory } from "./scopeHandlers/ScopeHandlerFactory"; /** @@ -26,9 +26,8 @@ import type { ScopeHandlerFactory } from "./scopeHandlers/ScopeHandlerFactory"; */ export class ContainingScopeStage implements ModifierStage { constructor( - private modifierStageFactory: ModifierStageFactory, private scopeHandlerFactory: ScopeHandlerFactory, - private modifier: ContainingScopeModifier, + private modifier: ContainingScopeModifier | ComplexContainingScopeModifier, ) {} run(target: Target): Target[] { diff --git a/packages/lib-engine/src/processTargets/modifiers/HeadTailStage.ts b/packages/lib-engine/src/processTargets/modifiers/HeadTailStage.ts index f1a0b72308..81747bbd19 100644 --- a/packages/lib-engine/src/processTargets/modifiers/HeadTailStage.ts +++ b/packages/lib-engine/src/processTargets/modifiers/HeadTailStage.ts @@ -101,7 +101,7 @@ class BoundedLineStage implements ModifierStage { try { return this.modifierStageFactory .create({ - type: "containingScope", + type: "complexContainingScope", scopeType: compoundInteriorScopeType, }) .run(target, options)[0]; diff --git a/packages/lib-engine/src/processTargets/modifiers/InteriorStage.ts b/packages/lib-engine/src/processTargets/modifiers/InteriorStage.ts index 4fef6dcc48..340907d499 100644 --- a/packages/lib-engine/src/processTargets/modifiers/InteriorStage.ts +++ b/packages/lib-engine/src/processTargets/modifiers/InteriorStage.ts @@ -1,4 +1,4 @@ -import type { InteriorOnlyModifier, ScopeType } from "@cursorless/lib-common"; +import type { InteriorOnlyModifier } from "@cursorless/lib-common"; import { NoContainingScopeError, UnsupportedScopeError, @@ -9,6 +9,7 @@ import type { ModifierStage, ModifierStateOptions, } from "../PipelineStages.types"; +import type { SortedScopeType } from "./scopeHandlers/scopeHandler.types"; export class InteriorOnlyStage implements ModifierStage { constructor( @@ -56,7 +57,7 @@ export class InteriorOnlyStage implements ModifierStage { try { return this.modifierStageFactory .create({ - type: "containingScope", + type: "complexContainingScope", scopeType: compoundInteriorScopeType, }) .run(target, options); @@ -69,8 +70,8 @@ export class InteriorOnlyStage implements ModifierStage { } } -export const compoundInteriorScopeType: ScopeType = { - type: "oneOf", +export const compoundInteriorScopeType: SortedScopeType = { + type: "sorted", scopeTypes: [ { type: "interior", diff --git a/packages/lib-engine/src/processTargets/modifiers/PreferredScopeStage.ts b/packages/lib-engine/src/processTargets/modifiers/PreferredScopeStage.ts index 9e1296e8db..c7b75c5cd2 100644 --- a/packages/lib-engine/src/processTargets/modifiers/PreferredScopeStage.ts +++ b/packages/lib-engine/src/processTargets/modifiers/PreferredScopeStage.ts @@ -24,7 +24,6 @@ export class PreferredScopeStage implements ModifierStage { const { scopeType } = this.modifier; const containingScopeStage = new ContainingScopeStage( - this.modifierStageFactory, this.scopeHandlerFactory, { type: "containingScope", scopeType }, ); diff --git a/packages/lib-engine/src/processTargets/modifiers/modifier.types.ts b/packages/lib-engine/src/processTargets/modifiers/modifier.types.ts new file mode 100644 index 0000000000..0a7f50e17c --- /dev/null +++ b/packages/lib-engine/src/processTargets/modifiers/modifier.types.ts @@ -0,0 +1,10 @@ +import type { ScopeType } from "packages/lib-common/src"; +import type { ComplexScopeType } from "./scopeHandlers/scopeHandler.types"; + +export interface ComplexContainingScopeModifier { + type: "complexContainingScope"; + scopeType: ScopeType | ComplexScopeType; + ancestorIndex?: number; +} + +export type ComplexModifier = ComplexContainingScopeModifier; diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/BoundedScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/BoundedScopeHandler.ts index 3e3ed5d407..b789b24405 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/BoundedScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/BoundedScopeHandler.ts @@ -15,6 +15,7 @@ import { BaseScopeHandler } from "./BaseScopeHandler"; import { compareTargetScopes } from "./compareTargetScopes"; import type { TargetScope } from "./scope.types"; import type { + ComplexScopeType, ScopeHandler, ScopeIteratorRequirements, } from "./scopeHandler.types"; @@ -46,17 +47,9 @@ abstract class BoundedBaseScopeHandler extends BaseScopeHandler { ); } - get iterationScopeType(): ScopeType { - switch (this.targetScopeHandler.iterationScopeType.type) { - case "custom": - case "fallback": - case "conditional": - throw Error( - `Iteration scope type can't be '${this.targetScopeHandler.iterationScopeType.type}' for BoundedBaseScopeHandler`, - ); - } + get iterationScopeType(): ComplexScopeType { return { - type: "oneOf", + type: "sorted", scopeTypes: [ this.targetScopeHandler.iterationScopeType, { diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/CollectionItemScopeHandler/CollectionItemScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/CollectionItemScopeHandler/CollectionItemScopeHandler.ts index b3bfa3bc2f..55b0d80b25 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/CollectionItemScopeHandler/CollectionItemScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/CollectionItemScopeHandler/CollectionItemScopeHandler.ts @@ -46,11 +46,10 @@ export class CollectionItemScopeHandler extends BaseScopeHandler { return textualScopeHandler; } - return SortedScopeHandler.createFromScopeHandlers( - scopeHandlerFactory, - languageId, - [languageScopeHandler, textualScopeHandler], - ); + return SortedScopeHandler.createFromScopeHandlers([ + languageScopeHandler, + textualScopeHandler, + ]); })(); } diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/NotebookCellScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/NotebookCellScopeHandler.ts index ce13253aa3..4c1fbbaabc 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/NotebookCellScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/NotebookCellScopeHandler.ts @@ -46,11 +46,10 @@ export class NotebookCellScopeHandler extends BaseScopeHandler { return apiScopeHandler; } - return SortedScopeHandler.createFromScopeHandlers( - scopeHandlerFactory, - languageId, - [languageScopeHandler, apiScopeHandler], - ); + return SortedScopeHandler.createFromScopeHandlers([ + languageScopeHandler, + apiScopeHandler, + ]); })(); } diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index dd1a20ee29..3348bf00fd 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -135,7 +135,7 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { ); case "custom": return scopeType.scopeHandler; - case "oneOf": + case "sorted": return SortedScopeHandler.create(this, scopeType, languageId); case "fallback": return FallbackScopeHandler.create(this, scopeType, languageId); diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SortedScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SortedScopeHandler.ts index 15d0fc8635..c20f04ac5c 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SortedScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SortedScopeHandler.ts @@ -1,4 +1,9 @@ -import type { Direction, Position, TextEditor } from "@cursorless/lib-common"; +import type { + Direction, + Position, + ScopeType, + TextEditor, +} from "@cursorless/lib-common"; import { BaseScopeHandler } from "./BaseScopeHandler"; import { advanceIteratorsUntil, getInitialIteratorInfos } from "./IteratorInfo"; import type { ScopeHandlerFactory } from "./ScopeHandlerFactory"; diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/scopeHandler.types.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/scopeHandler.types.ts index 3ea134cce0..b1bbb335cb 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/scopeHandler.types.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/scopeHandler.types.ts @@ -26,7 +26,8 @@ export interface FallbackScopeType { } /** - * Used to handle sorted scope types. The scope types are yielded in sorted order. + * Used to handle sorted scope types. The scope types are yielded in sorted + * order. */ export interface SortedScopeType { type: "sorted"; diff --git a/packages/lib-engine/src/scopeProviders/ScopeInfoProvider.ts b/packages/lib-engine/src/scopeProviders/ScopeInfoProvider.ts index 78b815140a..d447cefa95 100644 --- a/packages/lib-engine/src/scopeProviders/ScopeInfoProvider.ts +++ b/packages/lib-engine/src/scopeProviders/ScopeInfoProvider.ts @@ -185,14 +185,5 @@ function isLanguageSpecific(scopeType: ScopeType): boolean { case "customRegex": case "glyph": return false; - - case "oneOf": - throw Error( - `Can't decide whether scope type ${JSON.stringify( - scopeType, - undefined, - 3, - )} is language-specific`, - ); } } From 54ee838164fb58d50af03c4a730244fb3d8976de Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 22 Mar 2026 11:28:29 +0100 Subject: [PATCH 3/6] Change complex cope handlers to return undefined if the internal scopes cannot be created --- .../CollectionItemScopeHandler.ts | 2 +- .../scopeHandlers/ConditionalScopeHandler.ts | 30 ++++++++---- .../scopeHandlers/FallbackScopeHandler.ts | 14 +++--- .../scopeHandlers/NotebookCellScopeHandler.ts | 2 +- .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 6 +-- .../scopeHandlers/SortedScopeHandler.ts | 48 ++++++++++++------- 6 files changed, 64 insertions(+), 38 deletions(-) diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/CollectionItemScopeHandler/CollectionItemScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/CollectionItemScopeHandler/CollectionItemScopeHandler.ts index 55b0d80b25..4b502f0379 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/CollectionItemScopeHandler/CollectionItemScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/CollectionItemScopeHandler/CollectionItemScopeHandler.ts @@ -46,7 +46,7 @@ export class CollectionItemScopeHandler extends BaseScopeHandler { return textualScopeHandler; } - return SortedScopeHandler.createFromScopeHandlers([ + return new SortedScopeHandler(scopeHandlerFactory, languageId, [ languageScopeHandler, textualScopeHandler, ]); diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ConditionalScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ConditionalScopeHandler.ts index d50b0312f5..46f505e844 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ConditionalScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ConditionalScopeHandler.ts @@ -10,6 +10,7 @@ import { BaseScopeHandler } from "./BaseScopeHandler"; import type { TargetScope } from "./scope.types"; import type { ConditionalScopeType, + ScopeHandler, ScopeIteratorRequirements, } from "./scopeHandler.types"; import type { ScopeHandlerFactory } from "./ScopeHandlerFactory"; @@ -18,10 +19,26 @@ export class ConditionalScopeHandler extends BaseScopeHandler { public scopeType = undefined; protected isHierarchical = true; - constructor( - public scopeHandlerFactory: ScopeHandlerFactory, + static maybeCreate( + scopeHandlerFactory: ScopeHandlerFactory, + conditionalScopeType: ConditionalScopeType, + languageId: string, + ): ConditionalScopeHandler | undefined { + const scopeHandler = scopeHandlerFactory.maybeCreate( + conditionalScopeType.scopeType, + languageId, + ); + + if (scopeHandler == null) { + return undefined; + } + + return new ConditionalScopeHandler(scopeHandler, conditionalScopeType); + } + + private constructor( + private scopeHandler: ScopeHandler, private conditionalScopeType: ConditionalScopeType, - private languageId: string, ) { super(); } @@ -38,13 +55,8 @@ export class ConditionalScopeHandler extends BaseScopeHandler { direction: Direction, hints: ScopeIteratorRequirements, ): Iterable { - const scopeHandler = this.scopeHandlerFactory.create( - this.conditionalScopeType.scopeType, - this.languageId, - ); - return ifilter( - scopeHandler.generateScopes(editor, position, direction, hints), + this.scopeHandler.generateScopes(editor, position, direction, hints), this.conditionalScopeType.predicate, ); } diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts index 9a3782a36d..445812f533 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/FallbackScopeHandler.ts @@ -16,11 +16,11 @@ export class FallbackScopeHandler extends BaseScopeHandler { public scopeType = undefined; protected isHierarchical = true; - static create( + static maybeCreate( scopeHandlerFactory: ScopeHandlerFactory, scopeType: FallbackScopeType, languageId: string, - ): ScopeHandler { + ): ScopeHandler | undefined { const scopeHandlers = scopeType.scopeTypes .map((scopeType) => scopeHandlerFactory.maybeCreate(scopeType, languageId), @@ -29,18 +29,18 @@ export class FallbackScopeHandler extends BaseScopeHandler { (scopeHandler): scopeHandler is ScopeHandler => scopeHandler != null, ); + if (scopeHandlers.length === 0) { + return undefined; + } + if (scopeHandlers.length === 1) { return scopeHandlers[0]; } - return this.createFromScopeHandlers(scopeHandlers); - } - - static createFromScopeHandlers(scopeHandlers: ScopeHandler[]): ScopeHandler { return new FallbackScopeHandler(scopeHandlers); } - private constructor(private scopeHandlers: ScopeHandler[]) { + constructor(private scopeHandlers: ScopeHandler[]) { super(); } diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/NotebookCellScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/NotebookCellScopeHandler.ts index 4c1fbbaabc..602cbbdffb 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/NotebookCellScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/NotebookCellScopeHandler.ts @@ -46,7 +46,7 @@ export class NotebookCellScopeHandler extends BaseScopeHandler { return apiScopeHandler; } - return SortedScopeHandler.createFromScopeHandlers([ + return new SortedScopeHandler(scopeHandlerFactory, languageId, [ languageScopeHandler, apiScopeHandler, ]); diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index 3348bf00fd..6225b8b53a 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -136,11 +136,11 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { case "custom": return scopeType.scopeHandler; case "sorted": - return SortedScopeHandler.create(this, scopeType, languageId); + return SortedScopeHandler.maybeCreate(this, scopeType, languageId); case "fallback": - return FallbackScopeHandler.create(this, scopeType, languageId); + return FallbackScopeHandler.maybeCreate(this, scopeType, languageId); case "conditional": - return new ConditionalScopeHandler(this, scopeType, languageId); + return ConditionalScopeHandler.maybeCreate(this, scopeType, languageId); default: // Pseudoscopes are handled separately in their own modifiers. if (pseudoScopes.has(scopeType.type)) { diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SortedScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SortedScopeHandler.ts index c20f04ac5c..10dc28fb2c 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SortedScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SortedScopeHandler.ts @@ -1,15 +1,11 @@ -import type { - Direction, - Position, - ScopeType, - TextEditor, -} from "@cursorless/lib-common"; +import type { Direction, Position, TextEditor } from "@cursorless/lib-common"; import { BaseScopeHandler } from "./BaseScopeHandler"; import { advanceIteratorsUntil, getInitialIteratorInfos } from "./IteratorInfo"; import type { ScopeHandlerFactory } from "./ScopeHandlerFactory"; import { compareTargetScopes } from "./compareTargetScopes"; import type { TargetScope } from "./scope.types"; import type { + CustomScopeType, ScopeHandler, ScopeIteratorRequirements, SortedScopeType, @@ -21,11 +17,11 @@ export class SortedScopeHandler extends BaseScopeHandler { private iterationScopeHandler: SortedScopeHandler | undefined; private lastYieldedIndex: number | undefined; - static create( + static maybeCreate( scopeHandlerFactory: ScopeHandlerFactory, scopeType: SortedScopeType, languageId: string, - ): ScopeHandler { + ): ScopeHandler | undefined { const scopeHandlers = scopeType.scopeTypes .map((scopeType) => scopeHandlerFactory.maybeCreate(scopeType, languageId), @@ -34,25 +30,43 @@ export class SortedScopeHandler extends BaseScopeHandler { (scopeHandler): scopeHandler is ScopeHandler => scopeHandler != null, ); + if (scopeHandlers.length === 0) { + return undefined; + } + if (scopeHandlers.length === 1) { return scopeHandlers[0]; } - return this.createFromScopeHandlers(scopeHandlers); - } - - static createFromScopeHandlers(scopeHandlers: ScopeHandler[]): ScopeHandler { - return new SortedScopeHandler(scopeHandlers); + return new SortedScopeHandler( + scopeHandlerFactory, + languageId, + scopeHandlers, + ); } - private constructor(private scopeHandlers: ScopeHandler[]) { + constructor( + private scopeHandlerFactory: ScopeHandlerFactory, + private languageId: string, + private scopeHandlers: ScopeHandler[], + ) { super(); } - get iterationScopeType(): SortedScopeType { + get iterationScopeType(): CustomScopeType { + if (this.iterationScopeHandler == null) { + const iterationScopeHandlers = this.scopeHandlers.map((s) => + this.scopeHandlerFactory.create(s.iterationScopeType, this.languageId), + ); + this.iterationScopeHandler = new SortedScopeHandler( + this.scopeHandlerFactory, + this.languageId, + iterationScopeHandlers, + ); + } return { - type: "sorted", - scopeTypes: this.scopeHandlers.map((s) => s.iterationScopeType), + type: "custom", + scopeHandler: this.iterationScopeHandler, }; } From cf0c0cd3c5d1b7740c8991d75a2119f15216153a Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 22 Mar 2026 11:33:23 +0100 Subject: [PATCH 4/6] Update return type --- .../modifiers/scopeHandlers/BoundedScopeHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/BoundedScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/BoundedScopeHandler.ts index b789b24405..e2007cf49c 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/BoundedScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/BoundedScopeHandler.ts @@ -15,9 +15,9 @@ import { BaseScopeHandler } from "./BaseScopeHandler"; import { compareTargetScopes } from "./compareTargetScopes"; import type { TargetScope } from "./scope.types"; import type { - ComplexScopeType, ScopeHandler, ScopeIteratorRequirements, + SortedScopeType, } from "./scopeHandler.types"; import type { ScopeHandlerFactory } from "./ScopeHandlerFactory"; import { isEveryScopeModifier } from "./util/isHintsEveryScope"; @@ -47,7 +47,7 @@ abstract class BoundedBaseScopeHandler extends BaseScopeHandler { ); } - get iterationScopeType(): ComplexScopeType { + get iterationScopeType(): SortedScopeType { return { type: "sorted", scopeTypes: [ From 4b743b7ffe4168bc89dc9935515e040864dff36b Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 22 Mar 2026 11:45:55 +0100 Subject: [PATCH 5/6] Update import --- .../lib-engine/src/processTargets/modifiers/modifier.types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib-engine/src/processTargets/modifiers/modifier.types.ts b/packages/lib-engine/src/processTargets/modifiers/modifier.types.ts index 0a7f50e17c..ac61c319e5 100644 --- a/packages/lib-engine/src/processTargets/modifiers/modifier.types.ts +++ b/packages/lib-engine/src/processTargets/modifiers/modifier.types.ts @@ -1,4 +1,4 @@ -import type { ScopeType } from "packages/lib-common/src"; +import type { ScopeType } from "@cursorless/lib-common"; import type { ComplexScopeType } from "./scopeHandlers/scopeHandler.types"; export interface ComplexContainingScopeModifier { From eed46d40f555618ea81c97448cf8975407208337 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Sun, 22 Mar 2026 11:46:53 +0100 Subject: [PATCH 6/6] Remove baseUrl from tsconfig.base.json --- tsconfig.base.json | 1 - 1 file changed, 1 deletion(-) diff --git a/tsconfig.base.json b/tsconfig.base.json index c5963ed8a1..d1a4dfbfa4 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "baseUrl": ".", "module": "ESNext", "moduleResolution": "Bundler", "moduleDetection": "force",