diff --git a/packages/cursorless-engine/src/CommandHistory.ts b/packages/cursorless-engine/src/CommandHistory.ts index e98c9b1a1b..f7a964c3ab 100644 --- a/packages/cursorless-engine/src/CommandHistory.ts +++ b/packages/cursorless-engine/src/CommandHistory.ts @@ -27,7 +27,7 @@ export class CommandHistory implements CommandRunnerDecorator { constructor( private ide: IDE, private storage: CommandHistoryStorage, - private commandServerApi: CommandServerApi | null, + private commandServerApi: CommandServerApi | undefined, ) {} wrapCommandRunner( diff --git a/packages/cursorless-engine/src/core/Debug.ts b/packages/cursorless-engine/src/core/Debug.ts index 255b05cf8d..91f341193c 100644 --- a/packages/cursorless-engine/src/core/Debug.ts +++ b/packages/cursorless-engine/src/core/Debug.ts @@ -1,24 +1,19 @@ -import { Disposable, TextEditorSelectionChangeEvent } from "@cursorless/common"; -import type { SyntaxNode, TreeCursor } from "web-tree-sitter"; -import { ide } from "../singletons/ide.singleton"; -import { TreeSitter } from "../typings/TreeSitter"; +import type { Disposable, IDE } from "@cursorless/common"; /** * Debug logger */ export class Debug { private disposableConfiguration?: Disposable; - private disposableSelection?: Disposable; active: boolean; - constructor(private treeSitter: TreeSitter) { - ide().disposeOnExit(this); + constructor(private ide: IDE) { + ide.disposeOnExit(this); this.evaluateSetting = this.evaluateSetting.bind(this); - this.logBranchTypes = this.logBranchTypes.bind(this); this.active = true; - switch (ide().runMode) { + switch (ide.runMode) { // Development mode. Always enable. case "development": this.enableDebugLog(); @@ -31,7 +26,7 @@ export class Debug { case "production": this.evaluateSetting(); this.disposableConfiguration = - ide().configuration.onDidChangeConfiguration(this.evaluateSetting); + ide.configuration.onDidChangeConfiguration(this.evaluateSetting); break; } } @@ -46,98 +41,22 @@ export class Debug { if (this.disposableConfiguration) { this.disposableConfiguration.dispose(); } - if (this.disposableSelection) { - this.disposableSelection.dispose(); - } } private enableDebugLog() { this.active = true; - this.disposableSelection = ide().onDidChangeTextEditorSelection( - this.logBranchTypes, - ); } private disableDebugLog() { this.active = false; - if (this.disposableSelection) { - this.disposableSelection.dispose(); - this.disposableSelection = undefined; - } } private evaluateSetting() { - const debugEnabled = ide().configuration.getOwnConfiguration("debug"); + const debugEnabled = this.ide.configuration.getOwnConfiguration("debug"); if (debugEnabled) { this.enableDebugLog(); } else { this.disableDebugLog(); } } - - private logBranchTypes(event: TextEditorSelectionChangeEvent) { - let node: SyntaxNode; - try { - node = this.treeSitter.getNodeAtLocation( - ide().activeTextEditor!.document, - event.selections[0], - ); - } catch (error) { - return; - } - - const ancestors: SyntaxNode[] = [node]; - while (node.parent != null) { - ancestors.unshift(node.parent); - node = node.parent; - } - - const cursor = node.tree.walk(); - this.printCursorLocationInfo(ancestors, cursor, 0); - } - - private printCursorLocationInfo( - nodes: SyntaxNode[], - cursor: TreeCursor, - index: number, - ) { - const field = cursor.currentFieldName; - const fieldText = field != null ? `${field}: ` : ""; - const indent = " ".repeat(index); - const nodeIsLast = index === nodes.length - 1; - const { nodeIsNamed } = cursor; - let text = `${indent}${fieldText}`; - - if (nodeIsNamed) { - text += `(${cursor.nodeType}`; - if (nodeIsLast) { - text += ")"; - } - } else { - text += `"${cursor.nodeType}"`; - } - - console.log(text); - - if ( - !nodeIsLast && - this.cursorGoToChildWithId(cursor, nodes[index + 1].id) - ) { - this.printCursorLocationInfo(nodes, cursor, index + 1); - } - - if (nodeIsNamed && !nodeIsLast) { - console.log(`${indent})`); - } - } - - private cursorGoToChildWithId(cursor: TreeCursor, id: number): boolean { - cursor.gotoFirstChild(); - while (cursor.currentNode.id !== id) { - if (!cursor.gotoNextSibling()) { - return false; - } - } - return true; - } } diff --git a/packages/cursorless-engine/src/core/HatTokenMapImpl.ts b/packages/cursorless-engine/src/core/HatTokenMapImpl.ts index 2df45a8859..3be6a51bbf 100644 --- a/packages/cursorless-engine/src/core/HatTokenMapImpl.ts +++ b/packages/cursorless-engine/src/core/HatTokenMapImpl.ts @@ -43,7 +43,7 @@ export class HatTokenMapImpl implements HatTokenMap { rangeUpdater: RangeUpdater, private debug: Debug, hats: Hats, - private commandServerApi: CommandServerApi | null, + private commandServerApi: CommandServerApi, ) { ide().disposeOnExit(this); this.activeMap = new IndividualHatMap(rangeUpdater); @@ -130,18 +130,15 @@ export class HatTokenMapImpl implements HatTokenMap { } private async maybeTakePrePhraseSnapshot() { - const phraseStartSignal = this.commandServerApi?.signals?.prePhrase; + const newSignalVersion = + await this.commandServerApi.signals.prePhrase.getVersion(); - if (phraseStartSignal != null) { - const newSignalVersion = await phraseStartSignal.getVersion(); + if (newSignalVersion !== this.lastSignalVersion) { + this.debug.log("taking snapshot"); + this.lastSignalVersion = newSignalVersion; - if (newSignalVersion !== this.lastSignalVersion) { - this.debug.log("taking snapshot"); - this.lastSignalVersion = newSignalVersion; - - if (newSignalVersion != null) { - this.takePrePhraseSnapshot(); - } + if (newSignalVersion != null) { + this.takePrePhraseSnapshot(); } } } diff --git a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts index 34738cd1e2..903650870c 100644 --- a/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts +++ b/packages/cursorless-engine/src/core/commandRunner/CommandRunnerImpl.ts @@ -26,7 +26,7 @@ export class CommandRunnerImpl implements CommandRunner { private noAutomaticTokenExpansion: boolean | undefined; constructor( - private commandServerApi: CommandServerApi | null, + private commandServerApi: CommandServerApi, private debug: Debug, private storedTargets: StoredTargetMap, private pipelineRunner: TargetPipelineRunner, diff --git a/packages/cursorless-engine/src/core/getCommandFallback.ts b/packages/cursorless-engine/src/core/getCommandFallback.ts index 0474062c13..6e6510bbb8 100644 --- a/packages/cursorless-engine/src/core/getCommandFallback.ts +++ b/packages/cursorless-engine/src/core/getCommandFallback.ts @@ -10,11 +10,11 @@ import { import type { ActionReturnValue } from "../actions/actions.types"; export async function getCommandFallback( - commandServerApi: CommandServerApi | null, + commandServerApi: CommandServerApi, runAction: (actionDescriptor: ActionDescriptor) => Promise, command: CommandComplete, ): Promise { - const focusedElementType = await commandServerApi?.getFocusedElementType(); + const focusedElementType = await commandServerApi.getFocusedElementType(); if (focusedElementType == null || focusedElementType === "textEditor") { return null; diff --git a/packages/cursorless-engine/src/cursorlessEngine.ts b/packages/cursorless-engine/src/cursorlessEngine.ts index 34738b3c5c..984e0bd3e9 100644 --- a/packages/cursorless-engine/src/cursorlessEngine.ts +++ b/packages/cursorless-engine/src/cursorlessEngine.ts @@ -1,11 +1,11 @@ import { Command, CommandServerApi, - FileSystem, Hats, IDE, ScopeProvider, ensureCommandShape, + type FileSystem, } from "@cursorless/common"; import { KeyboardTargetUpdater } from "./KeyboardTargetUpdater"; import { @@ -17,6 +17,8 @@ import { HatTokenMapImpl } from "./core/HatTokenMapImpl"; import type { Snippets } from "./core/Snippets"; import { StoredTargetMap } from "./core/StoredTargets"; import { RangeUpdater } from "./core/updateSelections/RangeUpdater"; +import { DisabledCommandServerApi } from "./disabledComponents/DisabledCommandServerApi"; +import { DisabledHatTokenMap } from "./disabledComponents/DisabledHatTokenMap"; import { DisabledSnippets } from "./disabledComponents/DisabledSnippets"; import { DisabledTalonSpokenForms } from "./disabledComponents/DisabledTalonSpokenForms"; import { CustomSpokenFormGeneratorImpl } from "./generateSpokenForm/CustomSpokenFormGeneratorImpl"; @@ -34,40 +36,44 @@ import { type TalonSpokenForms } from "./scopeProviders/TalonSpokenForms"; import { injectIde } from "./singletons/ide.singleton"; import { TreeSitter } from "./typings/TreeSitter"; -export async function createCursorlessEngine( - treeSitter: TreeSitter, - ide: IDE, - hats: Hats, - commandServerApi: CommandServerApi | null, - fileSystem: FileSystem, - talonSpokenForms: TalonSpokenForms | undefined, - snippets: Snippets = new DisabledSnippets(), -): Promise { - injectIde(ide); +interface Props { + ide: IDE; + hats?: Hats; + treeSitter: TreeSitter; + fileSystem: FileSystem; + commandServerApi?: CommandServerApi; + talonSpokenForms?: TalonSpokenForms; + snippets?: Snippets; +} - const debug = new Debug(treeSitter); +export async function createCursorlessEngine({ + ide, + hats, + treeSitter, + fileSystem, + commandServerApi = new DisabledCommandServerApi(), + talonSpokenForms = new DisabledTalonSpokenForms(), + snippets = new DisabledSnippets(), +}: Props): Promise { + injectIde(ide); + const debug = new Debug(ide); const rangeUpdater = new RangeUpdater(); - - const hatTokenMap = new HatTokenMapImpl( - rangeUpdater, - debug, - hats, - commandServerApi, - ); - hatTokenMap.allocateHats(); - const storedTargets = new StoredTargetMap(); - const keyboardTargetUpdater = new KeyboardTargetUpdater(storedTargets); + const customSpokenFormGenerator = new CustomSpokenFormGeneratorImpl( + talonSpokenForms, + ); + + const hatTokenMap = + hats != null + ? new HatTokenMapImpl(rangeUpdater, debug, hats, commandServerApi) + : new DisabledHatTokenMap(); + void hatTokenMap.allocateHats(); const languageDefinitions = new LanguageDefinitions(fileSystem, treeSitter); await languageDefinitions.init(); - const customSpokenFormGenerator = new CustomSpokenFormGeneratorImpl( - talonSpokenForms ?? new DisabledTalonSpokenForms(), - ); - ide.disposeOnExit( rangeUpdater, languageDefinitions, diff --git a/packages/cursorless-engine/src/disabledComponents/DisabledCommandServerApi.ts b/packages/cursorless-engine/src/disabledComponents/DisabledCommandServerApi.ts new file mode 100644 index 0000000000..63c3bf9946 --- /dev/null +++ b/packages/cursorless-engine/src/disabledComponents/DisabledCommandServerApi.ts @@ -0,0 +1,15 @@ +import type { CommandServerApi } from "@cursorless/common"; + +export class DisabledCommandServerApi implements CommandServerApi { + getFocusedElementType() { + return Promise.resolve(undefined); + } + + readonly signals = { + prePhrase: { + getVersion() { + return Promise.resolve(null); + }, + }, + }; +} diff --git a/packages/cursorless-engine/src/disabledComponents/DisabledHatTokenMap.ts b/packages/cursorless-engine/src/disabledComponents/DisabledHatTokenMap.ts new file mode 100644 index 0000000000..3e9ef947a0 --- /dev/null +++ b/packages/cursorless-engine/src/disabledComponents/DisabledHatTokenMap.ts @@ -0,0 +1,22 @@ +import type { HatTokenMap } from "@cursorless/common"; + +export class DisabledHatTokenMap implements HatTokenMap { + async allocateHats() { + // Do nothing + } + + async getReadableMap() { + return { + getEntries() { + return []; + }, + getToken() { + throw new Error("Hat map is disabled"); + }, + }; + } + + dispose() { + // Do nothing + } +} diff --git a/packages/cursorless-engine/src/runCommand.ts b/packages/cursorless-engine/src/runCommand.ts index 3db9f42f5c..c8a12b3888 100644 --- a/packages/cursorless-engine/src/runCommand.ts +++ b/packages/cursorless-engine/src/runCommand.ts @@ -34,7 +34,7 @@ import { ScopeHandlerFactoryImpl } from "./processTargets/modifiers/scopeHandler */ export async function runCommand( treeSitter: TreeSitter, - commandServerApi: CommandServerApi | null, + commandServerApi: CommandServerApi, debug: Debug, hatTokenMap: HatTokenMap, snippets: Snippets, @@ -90,7 +90,7 @@ async function unwrapLegacyCommandResponse( function createCommandRunner( treeSitter: TreeSitter, - commandServerApi: CommandServerApi | null, + commandServerApi: CommandServerApi, languageDefinitions: LanguageDefinitions, debug: Debug, storedTargets: StoredTargetMap, diff --git a/packages/cursorless-engine/src/spokenForms/CustomSpokenForms.ts b/packages/cursorless-engine/src/spokenForms/CustomSpokenForms.ts index 845edc944e..e94385249a 100644 --- a/packages/cursorless-engine/src/spokenForms/CustomSpokenForms.ts +++ b/packages/cursorless-engine/src/spokenForms/CustomSpokenForms.ts @@ -84,12 +84,11 @@ export class CustomSpokenForms { this.needsInitialTalonUpdate_ = true; } else { console.error("Error loading custom spoken forms", err); + const msg = (err as Error).message.replace(/\.$/, ""); showError( ide().messages, "CustomSpokenForms.updateSpokenFormMaps", - `Error loading custom spoken forms: ${ - (err as Error).message - }}}. Falling back to default spoken forms.`, + `Error loading custom spoken forms: ${msg}. Falling back to default spoken forms.`, ); } diff --git a/packages/cursorless-engine/src/testCaseRecorder/TestCaseRecorder.ts b/packages/cursorless-engine/src/testCaseRecorder/TestCaseRecorder.ts index 596a75e2e6..08a7cea5c3 100644 --- a/packages/cursorless-engine/src/testCaseRecorder/TestCaseRecorder.ts +++ b/packages/cursorless-engine/src/testCaseRecorder/TestCaseRecorder.ts @@ -65,7 +65,7 @@ export class TestCaseRecorder { private spokenFormGenerator = new SpokenFormGenerator(defaultSpokenFormMap); constructor( - private commandServerApi: CommandServerApi | null, + private commandServerApi: CommandServerApi | undefined, private hatTokenMap: HatTokenMap, private storedTargets: StoredTargetMap, ) { diff --git a/packages/cursorless-vscode/src/extension.ts b/packages/cursorless-vscode/src/extension.ts index 21290562fd..1dda8d2528 100644 --- a/packages/cursorless-vscode/src/extension.ts +++ b/packages/cursorless-vscode/src/extension.ts @@ -86,7 +86,7 @@ export async function activate( ? fakeCommandServerApi : await getCommandServerApi(); - const treeSitter: TreeSitter = createTreeSitter(parseTreeApi); + const treeSitter = createTreeSitter(parseTreeApi); const talonSpokenForms = new FileSystemTalonSpokenForms(fileSystem); const snippets = new VscodeSnippets(normalizedIde); @@ -101,15 +101,15 @@ export async function activate( runIntegrationTests, addCommandRunnerDecorator, customSpokenFormGenerator, - } = await createCursorlessEngine( + } = await createCursorlessEngine({ + ide: normalizedIde, treeSitter, - normalizedIde, hats, commandServerApi, fileSystem, talonSpokenForms, snippets, - ); + }); const commandHistoryStorage = new FileSystemCommandHistoryStorage( fileSystem.cursorlessCommandHistoryDirPath, diff --git a/packages/vscode-common/src/getExtensionApi.ts b/packages/vscode-common/src/getExtensionApi.ts index 78138514e8..182bd2f827 100644 --- a/packages/vscode-common/src/getExtensionApi.ts +++ b/packages/vscode-common/src/getExtensionApi.ts @@ -24,7 +24,7 @@ export interface ParseTreeApi { export async function getExtensionApi(extensionId: string) { const extension = vscode.extensions.getExtension(extensionId); - return extension == null ? null : ((await extension.activate()) as T); + return extension == null ? undefined : ((await extension.activate()) as T); } export async function getExtensionApiStrict(extensionId: string) {