Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 7 additions & 12 deletions packages/common/src/types/TreeSitter.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
import type { Range, TextDocument } from "@cursorless/common";
import type { Node, Query, Tree } from "web-tree-sitter";
import type { TextDocument } from "@cursorless/common";
import type { Query, Tree } from "web-tree-sitter";

export interface TreeSitter {
/**
* Function to access nodes in the tree sitter.
*/
getNodeAtLocation(document: TextDocument, range: Range): Node;

/**
* Function to access the tree sitter tree.
*/
getTree(document: TextDocument): Tree;

/**
* Loads a language, returning true if it was successfully loaded
*
Expand All @@ -20,6 +10,11 @@ export interface TreeSitter {
*/
loadLanguage(languageId: string): Promise<boolean>;

/**
* Function to access the tree sitter tree.
*/
getTree(document: TextDocument): Tree;

/**
* Create a query if the language is loaded.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import type { Range, TextDocument, TreeSitter } from "@cursorless/common";
import type { Node, Query, Tree } from "web-tree-sitter";
import type { TextDocument, TreeSitter } from "@cursorless/common";
import type { Query, Tree } from "web-tree-sitter";

export class DisabledTreeSitter implements TreeSitter {
getTree(_document: TextDocument): Tree {
throw new Error("Tree sitter not provided");
}

loadLanguage(_languageId: string): Promise<boolean> {
return Promise.resolve(false);
}

getNodeAtLocation(_document: TextDocument, _range: Range): Node {
getTree(_document: TextDocument): Tree {
throw new Error("Tree sitter not provided");
}

Expand Down
9 changes: 2 additions & 7 deletions packages/cursorless-engine/src/testUtil/TestTreeSitter.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { Range, TextDocument, TreeSitter } from "@cursorless/common";
import type { TextDocument, TreeSitter } from "@cursorless/common";
import { createRequire } from "node:module";
import * as path from "node:path";
import type {
Node,
Tree,
Parser as TreeSitterParser,
Language as TreeSitterLanguage,
Parser as TreeSitterParser,
Query as TreeSitterQuery,
} from "web-tree-sitter";

Expand Down Expand Up @@ -38,10 +37,6 @@ function initTreeSitter() {
}

export class TestTreeSitter implements TreeSitter {
getNodeAtLocation(_document: TextDocument, _range: Range): Node {
throw new Error("getNodeAtLocation: not implemented.");
}

getTree(document: TextDocument): Tree {
const language = languageCache.get(document.languageId);

Expand Down
64 changes: 64 additions & 0 deletions packages/cursorless-vscode/src/createScopeVisualizer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type {
Disposable,
IDE,
ScopeProvider,
ScopeType,
} from "@cursorless/common";
import { pull } from "lodash-es";
import {
type VscodeScopeVisualizer,
createVscodeScopeVisualizer,
} from "./ide/vscode/VSCodeScopeVisualizer";
import type {
ScopeVisualizer,
ScopeVisualizerListener,
VisualizationType,
} from "./ScopeVisualizerCommandApi";

export function createScopeVisualizer(
ide: IDE,
scopeProvider: ScopeProvider,
): ScopeVisualizer {
let scopeVisualizer: VscodeScopeVisualizer | undefined;
let currentScopeType: ScopeType | undefined;

const listeners: ScopeVisualizerListener[] = [];

return {
start(scopeType: ScopeType, visualizationType: VisualizationType) {
scopeVisualizer?.dispose();
scopeVisualizer = createVscodeScopeVisualizer(
ide,
scopeProvider,
scopeType,
visualizationType,
);
scopeVisualizer.start();
currentScopeType = scopeType;
listeners
.slice()
.forEach((listener) => listener(scopeType, visualizationType));
},

stop() {
scopeVisualizer?.dispose();
scopeVisualizer = undefined;
currentScopeType = undefined;
listeners.slice().forEach((listener) => listener(undefined, undefined));
},

get scopeType() {
return currentScopeType;
},

onDidChangeScopeType(listener: ScopeVisualizerListener): Disposable {
listeners.push(listener);

return {
dispose() {
pull(listeners, listener);
},
};
},
};
}
10 changes: 10 additions & 0 deletions packages/cursorless-vscode/src/createTreeSitter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { TreeSitter } from "@cursorless/common";
import type { ParseTreeApi } from "@cursorless/vscode-common";

export function createTreeSitter(parseTreeApi: ParseTreeApi): TreeSitter {
return {
loadLanguage: parseTreeApi.loadLanguage,
createQuery: parseTreeApi.createQuery,
getTree: (document) => parseTreeApi.getTreeForUri(document.uri),
};
}
44 changes: 44 additions & 0 deletions packages/cursorless-vscode/src/createVscodeIde.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as crypto from "crypto";
import * as os from "node:os";
import * as path from "node:path";
import type { ExtensionContext } from "vscode";
import { FakeFontMeasurements } from "./ide/vscode/hats/FakeFontMeasurements";
import { FontMeasurementsImpl } from "./ide/vscode/hats/FontMeasurementsImpl";
import { VscodeHats } from "./ide/vscode/hats/VscodeHats";
import { VscodeFileSystem } from "./ide/vscode/VscodeFileSystem";
import { VscodeIDE } from "./ide/vscode/VscodeIDE";
import { vscodeApi } from "./vscodeApi";

export async function createVscodeIde(context: ExtensionContext) {
const vscodeIDE = new VscodeIDE(context);

const hats = new VscodeHats(
vscodeIDE,
vscodeApi,
context,
vscodeIDE.runMode === "test"
? new FakeFontMeasurements()
: new FontMeasurementsImpl(context),
);

await hats.init();

// FIXME: Inject this from test harness. Would need to arrange to delay
// extension initialization, probably by returning a function from extension
// init that has parameters consisting of test configuration, and have that
// function do the actual initialization.
const cursorlessDir =
vscodeIDE.runMode === "test"
? path.join(os.tmpdir(), crypto.randomBytes(16).toString("hex"))
: path.join(os.homedir(), ".cursorless");

const fileSystem = new VscodeFileSystem(
context,
vscodeIDE.runMode,
cursorlessDir,
);

await fileSystem.initialize();

return { vscodeIDE, hats, fileSystem };
}
141 changes: 8 additions & 133 deletions packages/cursorless-vscode/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
import type {
Disposable,
EnforceUndefined,
IDE,
Range,
ScopeProvider,
ScopeType,
TextDocument,
TreeSitter,
} from "@cursorless/common";
import type { EnforceUndefined } from "@cursorless/common";
import {
FakeCommandServerApi,
FakeIDE,
Expand All @@ -27,36 +18,22 @@ import {
ScopeTestRecorder,
TestCaseRecorder,
} from "@cursorless/test-case-recorder";
import type { CursorlessApi, ParseTreeApi } from "@cursorless/vscode-common";
import type { CursorlessApi } from "@cursorless/vscode-common";
import {
getCommandServerApi,
getParseTreeApi,
toVscodeRange,
} from "@cursorless/vscode-common";
import * as crypto from "crypto";
import { pull } from "lodash-es";
import * as os from "node:os";
import * as path from "node:path";
import * as vscode from "vscode";
import type { ExtensionContext } from "vscode";
import { InstallationDependencies } from "./InstallationDependencies";
import { ReleaseNotes } from "./ReleaseNotes";
import { ScopeTreeProvider } from "./ScopeTreeProvider";
import type {
ScopeVisualizer,
ScopeVisualizerListener,
VisualizationType,
} from "./ScopeVisualizerCommandApi";
import { StatusBarItem } from "./StatusBarItem";
import { VscodeSnippets } from "./VscodeSnippets";
import { constructTestHelpers } from "./constructTestHelpers";
import { createScopeVisualizer } from "./createScopeVisualizer";
import { createTreeSitter } from "./createTreeSitter";
import { createTutorial } from "./createTutorial";
import type { VscodeScopeVisualizer } from "./ide/vscode/VSCodeScopeVisualizer";
import { createVscodeScopeVisualizer } from "./ide/vscode/VSCodeScopeVisualizer";
import { VscodeFileSystem } from "./ide/vscode/VscodeFileSystem";
import { VscodeIDE } from "./ide/vscode/VscodeIDE";
import { FakeFontMeasurements } from "./ide/vscode/hats/FakeFontMeasurements";
import { FontMeasurementsImpl } from "./ide/vscode/hats/FontMeasurementsImpl";
import { VscodeHats } from "./ide/vscode/hats/VscodeHats";
import { createVscodeIde } from "./createVscodeIde";
import { KeyboardCommands } from "./keyboard/KeyboardCommands";
import { registerCommands } from "./registerCommands";
import { revisualizeOnCustomRegexChange } from "./revisualizeOnCustomRegexChange";
Expand All @@ -72,9 +49,10 @@ import { vscodeApi } from "./vscodeApi";
* - Creates an entrypoint for running commands {@link CommandRunner}.
*/
export async function activate(
context: vscode.ExtensionContext,
context: ExtensionContext,
): Promise<CursorlessApi> {
const parseTreeApi = await getParseTreeApi();
const treeSitter = createTreeSitter(parseTreeApi);

const { vscodeIDE, hats, fileSystem } = await createVscodeIde(context);
const isTesting = vscodeIDE.runMode === "test";
Expand All @@ -89,7 +67,6 @@ export async function activate(
? fakeCommandServerApi
: await getCommandServerApi();

const treeSitter = createTreeSitter(parseTreeApi);
const talonSpokenForms = new FileSystemTalonSpokenForms(fileSystem);

const snippets = new VscodeSnippets(normalizedIde);
Expand Down Expand Up @@ -212,105 +189,3 @@ export async function activate(
: undefined,
};
}

async function createVscodeIde(context: vscode.ExtensionContext) {
const vscodeIDE = new VscodeIDE(context);

const hats = new VscodeHats(
vscodeIDE,
vscodeApi,
context,
vscodeIDE.runMode === "test"
? new FakeFontMeasurements()
: new FontMeasurementsImpl(context),
);
await hats.init();

// FIXME: Inject this from test harness. Would need to arrange to delay
// extension initialization, probably by returning a function from extension
// init that has parameters consisting of test configuration, and have that
// function do the actual initialization.
const cursorlessDir =
vscodeIDE.runMode === "test"
? path.join(os.tmpdir(), crypto.randomBytes(16).toString("hex"))
: path.join(os.homedir(), ".cursorless");

const fileSystem = new VscodeFileSystem(
context,
vscodeIDE.runMode,
cursorlessDir,
);
await fileSystem.initialize();

return { vscodeIDE, hats, fileSystem };
}

function createTreeSitter(parseTreeApi: ParseTreeApi): TreeSitter {
return {
getNodeAtLocation(document: TextDocument, range: Range) {
return parseTreeApi.getNodeAtLocation(
new vscode.Location(document.uri, toVscodeRange(range)),
);
},

getTree(document: TextDocument) {
return parseTreeApi.getTreeForUri(document.uri);
},

loadLanguage: parseTreeApi.loadLanguage,
createQuery: parseTreeApi.createQuery,
};
}

function createScopeVisualizer(
ide: IDE,
scopeProvider: ScopeProvider,
): ScopeVisualizer {
let scopeVisualizer: VscodeScopeVisualizer | undefined;
let currentScopeType: ScopeType | undefined;

const listeners: ScopeVisualizerListener[] = [];

return {
start(scopeType: ScopeType, visualizationType: VisualizationType) {
scopeVisualizer?.dispose();
scopeVisualizer = createVscodeScopeVisualizer(
ide,
scopeProvider,
scopeType,
visualizationType,
);
scopeVisualizer.start();
currentScopeType = scopeType;
listeners
.slice()
.forEach((listener) => listener(scopeType, visualizationType));
},

stop() {
scopeVisualizer?.dispose();
scopeVisualizer = undefined;
currentScopeType = undefined;
listeners.slice().forEach((listener) => listener(undefined, undefined));
},

get scopeType() {
return currentScopeType;
},

onDidChangeScopeType(listener: ScopeVisualizerListener): Disposable {
listeners.push(listener);

return {
dispose() {
pull(listeners, listener);
},
};
},
};
}

// this method is called when your extension is deactivated
export function deactivate() {
// do nothing
}
5 changes: 2 additions & 3 deletions packages/vscode-common/src/getExtensionApi.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import type { CommandServerApi } from "@cursorless/common";
import * as vscode from "vscode";
import type { Node, Query, Tree } from "web-tree-sitter";
import type { Query, Tree } from "web-tree-sitter";
import type { VscodeTestHelpers } from "./TestHelpers";

export interface CursorlessApi {
testHelpers: VscodeTestHelpers | undefined;
}

export interface ParseTreeApi {
getNodeAtLocation(location: vscode.Location): Node;
getTreeForUri(uri: vscode.Uri): Tree;
loadLanguage(languageId: string): Promise<boolean>;
getTreeForUri(uri: vscode.Uri): Tree;
createQuery(languageId: string, source: string): Query | undefined;
}

Expand Down
Loading