From ad7498f334b243731c60d7b2a78924432979e8ef Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 7 Jul 2026 10:09:11 +0200 Subject: [PATCH 1/2] Show proper error message when trying to visualize pseudo scopes --- packages/app-vscode/src/createScopeVisualizer.ts | 9 ++++++++- .../command/PartialTargetDescriptor.types.ts | 16 +++++++++++----- .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 6 +++--- .../src/scopeProviders/ScopeInfoProvider.ts | 11 +++++------ 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/packages/app-vscode/src/createScopeVisualizer.ts b/packages/app-vscode/src/createScopeVisualizer.ts index 59fc7b5696..d5d788aea1 100644 --- a/packages/app-vscode/src/createScopeVisualizer.ts +++ b/packages/app-vscode/src/createScopeVisualizer.ts @@ -5,8 +5,9 @@ import type { ScopeProvider, ScopeType, } from "@cursorless/lib-common"; -import { createVscodeScopeVisualizer } from "./ide/vscode/VSCodeScopeVisualizer"; +import { isPseudoScope } from "@cursorless/lib-common"; import type { VscodeScopeVisualizer } from "./ide/vscode/VSCodeScopeVisualizer"; +import { createVscodeScopeVisualizer } from "./ide/vscode/VSCodeScopeVisualizer"; import type { ScopeVisualizer, ScopeVisualizerListener, @@ -24,6 +25,12 @@ export function createScopeVisualizer( return { start(scopeType: ScopeType, visualizationType: VisualizationType) { + if (isPseudoScope(scopeType)) { + throw new Error( + `Can't visualize pseudo scopes like '${scopeType.type}'`, + ); + } + scopeVisualizer?.dispose(); scopeVisualizer = createVscodeScopeVisualizer( ide, diff --git a/packages/lib-common/src/types/command/PartialTargetDescriptor.types.ts b/packages/lib-common/src/types/command/PartialTargetDescriptor.types.ts index 191430f7b8..da47dab585 100644 --- a/packages/lib-common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/lib-common/src/types/command/PartialTargetDescriptor.types.ts @@ -211,11 +211,7 @@ export const simpleScopeTypeTypes = [ "interior", ] as const; -export function isSimpleScopeType( - scopeType: ScopeType, -): scopeType is SimpleScopeType { - return (simpleScopeTypeTypes as readonly string[]).includes(scopeType.type); -} +const simpleScopeTypeTypesSet = new Set(simpleScopeTypeTypes); export type SimpleScopeTypeType = (typeof simpleScopeTypeTypes)[number]; @@ -226,6 +222,16 @@ export const pseudoScopes = new Set([ "functionName", ]); +export function isSimpleScopeType( + scopeType: ScopeType, +): scopeType is SimpleScopeType { + return (simpleScopeTypeTypesSet as Set).has(scopeType.type); +} + +export function isPseudoScope(scopeType: ScopeType): boolean { + return isSimpleScopeType(scopeType) && pseudoScopes.has(scopeType.type); +} + export interface SimpleScopeType { type: SimpleScopeTypeType; } diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index 99ddf6dc63..7fd0eaebf0 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -1,5 +1,5 @@ -import { pseudoScopes, UnsupportedScopeError } from "@cursorless/lib-common"; import type { IDE, ScopeType } from "@cursorless/lib-common"; +import { isPseudoScope, UnsupportedScopeError } from "@cursorless/lib-common"; import type { LanguageDefinitions } from "../../../languages/LanguageDefinitions"; import { BoundedNonWhitespaceSequenceScopeHandler, @@ -139,8 +139,8 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { return ConditionalScopeHandler.maybeCreate(this, scopeType, languageId); default: // Pseudoscopes are handled separately in their own modifiers. - if (pseudoScopes.has(scopeType.type)) { - throw new Error(`Unexpected scope type '${scopeType.type}'`); + if (isPseudoScope(scopeType)) { + throw new Error(`Unexpected pseudo scope type '${scopeType.type}'`); } return this.languageDefinitions .get(languageId) diff --git a/packages/lib-engine/src/scopeProviders/ScopeInfoProvider.ts b/packages/lib-engine/src/scopeProviders/ScopeInfoProvider.ts index 05ae88b805..a10fd436e4 100644 --- a/packages/lib-engine/src/scopeProviders/ScopeInfoProvider.ts +++ b/packages/lib-engine/src/scopeProviders/ScopeInfoProvider.ts @@ -7,7 +7,7 @@ import type { SurroundingPairScopeType, } from "@cursorless/lib-common"; import { - pseudoScopes, + isPseudoScope, simpleScopeTypeTypes, surroundingPairNames, } from "@cursorless/lib-common"; @@ -67,11 +67,10 @@ export class ScopeInfoProvider { private updateScopeTypeInfos(): void { const scopeTypes: ScopeType[] = [ ...simpleScopeTypeTypes - // Ignore instance pseudo-scope because it's not really a scope - .filter((scopeTypeType) => !pseudoScopes.has(scopeTypeType)) - .map((scopeTypeType) => ({ - type: scopeTypeType, - })), + // Create simple scope types from simple scope type types + .map((scopeTypeType) => ({ type: scopeTypeType })) + // Ignore pseudo-scope because it's not really a scope + .filter((scopeType) => !isPseudoScope(scopeType)), ...surroundingPairNames.map( (surroundingPairName): SurroundingPairScopeType => ({ From 5d508aadad23ad7606ec45034bce8fa21843c7d2 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 7 Jul 2026 10:19:00 +0200 Subject: [PATCH 2/2] Better error handling of tutorial steps --- packages/lib-tutorial/src/setupStep.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/lib-tutorial/src/setupStep.ts b/packages/lib-tutorial/src/setupStep.ts index e07944aa94..0379d84607 100644 --- a/packages/lib-tutorial/src/setupStep.ts +++ b/packages/lib-tutorial/src/setupStep.ts @@ -49,8 +49,15 @@ export async function setupStep( throw new Error("No current tutorial found"); } - const { initialState: snapshot, languageId = "plaintext" } = - currentTutorial.steps[state.stepNumber]; + const step = currentTutorial.steps[state.stepNumber]; + + if (step == null) { + throw new Error( + `No tutorial step found for step number ${state.stepNumber}`, + ); + } + + const { initialState: snapshot, languageId = "plaintext" } = step; if (snapshot == null) { return { editor, highlightRanges: [] };