Skip to content

Commit a0ee7a6

Browse files
authored
Remove VSCode from CommandRunner and Cheatsheet (#1246)
1 parent 2e5ba05 commit a0ee7a6

File tree

6 files changed

+127
-141
lines changed

6 files changed

+127
-141
lines changed

src/core/Cheatsheet.ts

Lines changed: 57 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { readFile, writeFile } from "fs/promises";
22
import parse from "node-html-parser";
3-
import * as vscode from "vscode";
4-
import { Graph } from "../typings/Types";
53
import path = require("path");
64
import produce from "immer";
75
import { sortBy } from "lodash";
@@ -28,104 +26,76 @@ interface CheatSheetCommandArg {
2826
outputPath: string;
2927
}
3028

31-
export default class Cheatsheet {
32-
private disposables: vscode.Disposable[] = [];
29+
export async function showCheatsheet({
30+
version,
31+
spokenFormInfo,
32+
outputPath,
33+
}: CheatSheetCommandArg) {
34+
if (version !== 0) {
35+
throw new Error(`Unsupported cheatsheet api version: ${version}`);
36+
}
3337

34-
constructor(private graph: Graph) {
35-
ide().disposeOnExit(this);
38+
const cheatsheetPath = path.join(
39+
ide().assetsRoot,
40+
"cursorless-nx",
41+
"dist",
42+
"apps",
43+
"cheatsheet-local",
44+
"index.html",
45+
);
3646

37-
this.showCheatsheet = this.showCheatsheet.bind(this);
38-
this.updateDefaults = this.updateDefaults.bind(this);
39-
}
47+
const cheatsheetContent = (await readFile(cheatsheetPath)).toString();
4048

41-
init() {
42-
this.disposables.push(
43-
vscode.commands.registerCommand(
44-
"cursorless.showCheatsheet",
45-
this.showCheatsheet,
46-
),
47-
vscode.commands.registerCommand(
48-
"cursorless.internal.updateCheatsheetDefaults",
49-
this.updateDefaults,
50-
),
51-
);
52-
}
49+
const root = parse(cheatsheetContent);
5350

54-
private async showCheatsheet({
55-
version,
51+
root.getElementById(
52+
"cheatsheet-data",
53+
).textContent = `document.cheatsheetData = ${JSON.stringify(
5654
spokenFormInfo,
57-
outputPath,
58-
}: CheatSheetCommandArg) {
59-
if (version !== 0) {
60-
throw new Error(`Unsupported cheatsheet api version: ${version}`);
61-
}
62-
63-
const cheatsheetPath = path.join(
64-
ide().assetsRoot,
65-
"cursorless-nx",
66-
"dist",
67-
"apps",
68-
"cheatsheet-local",
69-
"index.html",
70-
);
71-
72-
const cheatsheetContent = (await readFile(cheatsheetPath)).toString();
55+
)};`;
7356

74-
const root = parse(cheatsheetContent);
57+
await writeFile(outputPath, root.toString());
58+
}
7559

76-
root.getElementById(
77-
"cheatsheet-data",
78-
).textContent = `document.cheatsheetData = ${JSON.stringify(
79-
spokenFormInfo,
80-
)};`;
60+
/**
61+
* Updates the default spoken forms stored in `defaults.json` for
62+
* development.
63+
* @param spokenFormInfo The new value to use for default spoken forms.
64+
*/
65+
export async function updateDefaults(spokenFormInfo: CheatsheetInfo) {
66+
const { runMode, assetsRoot, workspaceFolders } = ide();
8167

82-
await writeFile(outputPath, root.toString());
83-
}
68+
const workspacePath =
69+
runMode === "development"
70+
? assetsRoot
71+
: workspaceFolders?.[0].uri.path ?? null;
8472

85-
/**
86-
* Updates the default spoken forms stored in `defaults.json` for
87-
* development.
88-
* @param spokenFormInfo The new value to use for default spoken forms.
89-
*/
90-
private async updateDefaults(spokenFormInfo: CheatsheetInfo) {
91-
const { runMode, assetsRoot, workspaceFolders } = ide();
92-
93-
const workspacePath =
94-
runMode === "development"
95-
? assetsRoot
96-
: workspaceFolders?.[0].uri.path ?? null;
97-
98-
if (workspacePath == null) {
99-
throw new Error(
100-
"Please update defaults from Cursorless workspace or running in debug",
101-
);
102-
}
103-
104-
const defaultsPath = path.join(
105-
workspacePath,
106-
"cursorless-nx",
107-
"libs",
108-
"cheatsheet",
109-
"src",
110-
"lib",
111-
"data",
112-
"sampleSpokenFormInfos",
113-
"defaults.json",
73+
if (workspacePath == null) {
74+
throw new Error(
75+
"Please update defaults from Cursorless workspace or running in debug",
11476
);
77+
}
11578

116-
const outputObject = produce(spokenFormInfo, (draft) => {
117-
draft.sections = sortBy(draft.sections, "id");
118-
draft.sections.forEach((section) => {
119-
section.items = sortBy(section.items, "id");
120-
});
79+
const defaultsPath = path.join(
80+
workspacePath,
81+
"cursorless-nx",
82+
"libs",
83+
"cheatsheet",
84+
"src",
85+
"lib",
86+
"data",
87+
"sampleSpokenFormInfos",
88+
"defaults.json",
89+
);
90+
91+
const outputObject = produce(spokenFormInfo, (draft) => {
92+
draft.sections = sortBy(draft.sections, "id");
93+
draft.sections.forEach((section) => {
94+
section.items = sortBy(section.items, "id");
12195
});
96+
});
12297

123-
await writeFile(defaultsPath, JSON.stringify(outputObject, null, "\t"));
124-
}
125-
126-
dispose() {
127-
this.disposables.forEach(({ dispose }) => dispose());
128-
}
98+
await writeFile(defaultsPath, JSON.stringify(outputObject, null, "\t"));
12999
}
130100

131101
// FIXME: Stop duplicating these types once we have #945

src/core/commandRunner/CommandRunner.ts

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
import * as vscode from "vscode";
21
import { ActionType } from "./typings/ActionCommand";
3-
import { OutdatedExtensionError } from "../../errors";
4-
import { CURSORLESS_COMMAND_ID } from "../../libs/common/commandIds";
52
import ide from "../../libs/cursorless-engine/singletons/ide.singleton";
63
import processTargets from "../../processTargets";
7-
import isTesting from "../../testUtil/isTesting";
84
import { Target } from "../../typings/target.types";
95
import {
106
Graph,
@@ -24,24 +20,13 @@ import { selectionToThatTarget } from "./selectionToThatTarget";
2420

2521
// TODO: Do this using the graph once we migrate its dependencies onto the graph
2622
export default class CommandRunner {
27-
private disposables: vscode.Disposable[] = [];
28-
2923
constructor(
3024
private graph: Graph,
3125
private thatMark: ThatMark,
3226
private sourceMark: ThatMark,
3327
) {
34-
ide().disposeOnExit(this);
35-
3628
this.runCommandBackwardCompatible =
3729
this.runCommandBackwardCompatible.bind(this);
38-
39-
this.disposables.push(
40-
vscode.commands.registerCommand(
41-
CURSORLESS_COMMAND_ID,
42-
this.runCommandBackwardCompatible,
43-
),
44-
);
4530
}
4631

4732
/**
@@ -166,39 +151,17 @@ export default class CommandRunner {
166151

167152
return returnValue;
168153
} catch (e) {
169-
await this.graph.testCaseRecorder.commandErrorHook(e as Error);
170154
const err = e as Error;
171-
if (err instanceof OutdatedExtensionError) {
172-
this.showUpdateExtensionErrorMessage(err);
173-
} else if (!isTesting()) {
174-
vscode.window.showErrorMessage(err.message);
175-
}
176-
console.error(err.message);
177155
console.error(err.stack);
178-
throw err;
156+
throw e;
179157
} finally {
180158
if (this.graph.testCaseRecorder.isActive()) {
181159
this.graph.testCaseRecorder.finallyHook();
182160
}
183161
}
184162
}
185163

186-
async showUpdateExtensionErrorMessage(err: OutdatedExtensionError) {
187-
const item = await vscode.window.showErrorMessage(
188-
err.message,
189-
"Check for updates",
190-
);
191-
192-
if (item == null) {
193-
return;
194-
}
195-
196-
await vscode.commands.executeCommand(
197-
"workbench.extensions.action.checkForUpdates",
198-
);
199-
}
200-
201-
private runCommandBackwardCompatible(
164+
runCommandBackwardCompatible(
202165
spokenFormOrCommand: string | Command,
203166
...rest: unknown[]
204167
) {
@@ -226,10 +189,6 @@ export default class CommandRunner {
226189

227190
return this.runCommand(command);
228191
}
229-
230-
dispose() {
231-
this.disposables.forEach(({ dispose }) => dispose());
232-
}
233192
}
234193

235194
function constructThatTarget(

src/extension.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { Range, TextDocument } from "@cursorless/common";
1+
import { CURSORLESS_COMMAND_ID, Range, TextDocument } from "@cursorless/common";
22
import { toVscodeRange } from "@cursorless/vscode-common";
33
import * as vscode from "vscode";
4+
import { showCheatsheet, updateDefaults } from "./core/Cheatsheet";
45
import CommandRunner from "./core/commandRunner/CommandRunner";
6+
import { Command } from "./core/commandRunner/typings/command.types";
57
import { ThatMark } from "./core/ThatMark";
68
import VscodeIDE from "./ide/vscode/VscodeIDE";
79
import FakeIDE from "./libs/common/ide/fake/FakeIDE";
@@ -17,6 +19,7 @@ import {
1719
import { TargetPlainObject } from "./libs/vscode-common/testUtil/toPlainObject";
1820
import { plainObjectToTarget } from "./testUtil/fromPlainObject";
1921
import isTesting from "./testUtil/isTesting";
22+
import { TestCaseRecorder } from "./testUtil/TestCaseRecorder";
2023
import { Graph } from "./typings/Types";
2124
import graphFactories from "./util/graphFactories";
2225
import makeGraph, { FactoryMap } from "./util/makeGraph";
@@ -62,15 +65,16 @@ export async function activate(
6265
graph.snippets.init();
6366
graph.hatTokenMap.init();
6467
graph.testCaseRecorder.init();
65-
graph.cheatsheet.init();
6668
graph.statusBarItem.init();
6769
graph.keyboardCommands.init();
6870

6971
const thatMark = new ThatMark();
7072
const sourceMark = new ThatMark();
7173

7274
// TODO: Do this using the graph once we migrate its dependencies onto the graph
73-
new CommandRunner(graph, thatMark, sourceMark);
75+
const commandRunner = new CommandRunner(graph, thatMark, sourceMark);
76+
77+
registerCommands(context, vscodeIDE, commandRunner, graph.testCaseRecorder);
7478

7579
return {
7680
thatMark,
@@ -105,3 +109,40 @@ export async function activate(
105109
export function deactivate() {
106110
// do nothing
107111
}
112+
113+
function registerCommands(
114+
extensionContext: vscode.ExtensionContext,
115+
vscodeIde: VscodeIDE,
116+
commandRunner: CommandRunner,
117+
testCaseRecorder: TestCaseRecorder,
118+
): void {
119+
extensionContext.subscriptions.push(
120+
vscode.commands.registerCommand(
121+
CURSORLESS_COMMAND_ID,
122+
async (spokenFormOrCommand: string | Command, ...rest: unknown[]) => {
123+
try {
124+
return await commandRunner.runCommandBackwardCompatible(
125+
spokenFormOrCommand,
126+
...rest,
127+
);
128+
} catch (e) {
129+
const error = e as Error;
130+
if (!isTesting()) {
131+
vscodeIde.handleCommandError(error);
132+
}
133+
await testCaseRecorder.commandErrorHook(error);
134+
throw error;
135+
}
136+
},
137+
),
138+
139+
vscode.commands.registerCommand(
140+
"cursorless.showCheatsheet",
141+
showCheatsheet,
142+
),
143+
vscode.commands.registerCommand(
144+
"cursorless.internal.updateCheatsheetDefaults",
145+
updateDefaults,
146+
),
147+
);
148+
}

src/ide/vscode/VscodeIDE.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { pull } from "lodash";
88
import { v4 as uuid } from "uuid";
99
import * as vscode from "vscode";
1010
import { ExtensionContext, window, workspace, WorkspaceFolder } from "vscode";
11+
import { OutdatedExtensionError } from "../../errors";
1112
import type { TextDocumentChangeEvent } from "../../libs/common/ide/types/Events";
1213
import { FlashDescriptor } from "../../libs/common/ide/types/FlashDescriptor";
1314
import type {
@@ -185,6 +186,29 @@ export default class VscodeIDE implements IDE {
185186
return this.editorMap.get(editor)!;
186187
}
187188

189+
handleCommandError(err: Error) {
190+
if (err instanceof OutdatedExtensionError) {
191+
this.showUpdateExtensionErrorMessage(err);
192+
} else {
193+
vscode.window.showErrorMessage(err.message);
194+
}
195+
}
196+
197+
private async showUpdateExtensionErrorMessage(err: OutdatedExtensionError) {
198+
const item = await vscode.window.showErrorMessage(
199+
err.message,
200+
"Check for updates",
201+
);
202+
203+
if (item == null) {
204+
return;
205+
}
206+
207+
await vscode.commands.executeCommand(
208+
"workbench.extensions.action.checkForUpdates",
209+
);
210+
}
211+
188212
disposeOnExit(...disposables: Disposable[]): () => void {
189213
this.extensionContext.subscriptions.push(...disposables);
190214

src/typings/Types.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import type {
66
} from "@cursorless/common";
77
import { SyntaxNode } from "web-tree-sitter";
88
import { ActionRecord } from "../actions/actions.types";
9-
import Cheatsheet from "../core/Cheatsheet";
109
import Debug from "../core/Debug";
1110
import HatTokenMap from "../core/HatTokenMap";
1211
import { ReadOnlyHatMap } from "../core/IndividualHatMap";
@@ -137,11 +136,6 @@ export interface Graph {
137136
*/
138137
readonly testCaseRecorder: TestCaseRecorder;
139138

140-
/**
141-
* Used to display cheatsheet
142-
*/
143-
readonly cheatsheet: Cheatsheet;
144-
145139
/**
146140
* Creates a VSCode status bar item
147141
*/

0 commit comments

Comments
 (0)