Skip to content

Commit aa268cf

Browse files
committed
Rename and move commandRunner
The `commandRunner` name doesn't really make sense since it doesn't "run" a command, but rather registers a command. This renames it to `registerCommandWithErrorHandling` and moves it to the `common/vscode` directory.
1 parent aca0489 commit aa268cf

3 files changed

Lines changed: 75 additions & 84 deletions

File tree

extensions/ql-vscode/src/commandRunner.ts

Lines changed: 0 additions & 76 deletions
This file was deleted.

extensions/ql-vscode/src/common/vscode/commands.ts

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1-
import { commands } from "vscode";
2-
import { commandRunner, NoProgressTask } from "../../commandRunner";
1+
import { commands, Disposable } from "vscode";
32
import { CommandFunction, CommandManager } from "../../packages/commands";
4-
import { OutputChannelLogger } from "../logging";
3+
import { extLogger, OutputChannelLogger } from "../logging";
4+
import {
5+
asError,
6+
getErrorMessage,
7+
getErrorStack,
8+
} from "../../pure/helpers-pure";
9+
import { redactableError } from "../../pure/errors";
10+
import { UserCancellationException } from "../../progress";
11+
import {
12+
showAndLogExceptionWithTelemetry,
13+
showAndLogWarningMessage,
14+
} from "../../helpers";
15+
import { telemetryListener } from "../../telemetry";
516

617
/**
718
* Create a command manager for VSCode, wrapping the commandRunner
@@ -10,11 +21,65 @@ import { OutputChannelLogger } from "../logging";
1021
export function createVSCodeCommandManager<
1122
Commands extends Record<string, CommandFunction>,
1223
>(outputLogger?: OutputChannelLogger): CommandManager<Commands> {
13-
return new CommandManager((commandId, task: NoProgressTask) => {
14-
return commandRunner(commandId, task, outputLogger);
24+
return new CommandManager((commandId, task) => {
25+
return registerCommandWithErrorHandling(commandId, task, outputLogger);
1526
}, wrapExecuteCommand);
1627
}
1728

29+
/**
30+
* A wrapper for command registration. This wrapper adds uniform error handling for commands.
31+
*
32+
* @param commandId The ID of the command to register.
33+
* @param task The task to run. It is passed directly to `commands.registerCommand`. Any
34+
* arguments to the command handler are passed on to the task.
35+
*/
36+
export function registerCommandWithErrorHandling(
37+
commandId: string,
38+
task: (...args: any[]) => Promise<any>,
39+
outputLogger = extLogger,
40+
): Disposable {
41+
return commands.registerCommand(commandId, async (...args: any[]) => {
42+
const startTime = Date.now();
43+
let error: Error | undefined;
44+
45+
try {
46+
return await task(...args);
47+
} catch (e) {
48+
error = asError(e);
49+
const errorMessage = redactableError(error)`${
50+
getErrorMessage(e) || e
51+
} (${commandId})`;
52+
const errorStack = getErrorStack(e);
53+
if (e instanceof UserCancellationException) {
54+
// User has cancelled this action manually
55+
if (e.silent) {
56+
void outputLogger.log(errorMessage.fullMessage);
57+
} else {
58+
void showAndLogWarningMessage(errorMessage.fullMessage, {
59+
outputLogger,
60+
});
61+
}
62+
} else {
63+
// Include the full stack in the error log only.
64+
const fullMessage = errorStack
65+
? `${errorMessage.fullMessage}\n${errorStack}`
66+
: errorMessage.fullMessage;
67+
void showAndLogExceptionWithTelemetry(errorMessage, {
68+
outputLogger,
69+
fullMessage,
70+
extraTelemetryProperties: {
71+
command: commandId,
72+
},
73+
});
74+
}
75+
return undefined;
76+
} finally {
77+
const executionTime = Date.now() - startTime;
78+
telemetryListener?.sendCommandUsage(commandId, executionTime, error);
79+
}
80+
});
81+
}
82+
1883
/**
1984
* wrapExecuteCommand wraps commands.executeCommand to satisfy that the
2085
* type is a Promise. Type script does not seem to be smart enough

extensions/ql-vscode/src/extension.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ import { QLTestAdapterFactory } from "./test-adapter";
8787
import { TestUIService } from "./test-ui";
8888
import { CompareView } from "./compare/compare-view";
8989
import { initializeTelemetry } from "./telemetry";
90-
import { commandRunner } from "./commandRunner";
9190
import { ProgressCallback, withProgress } from "./progress";
9291
import { CodeQlStatusBarHandler } from "./status-bar";
9392
import { getPackagingCommands } from "./packaging";
@@ -123,6 +122,7 @@ import {
123122
import { getAstCfgCommands } from "./ast-cfg-commands";
124123
import { getQueryEditorCommands } from "./query-editor";
125124
import { App } from "./common/app";
125+
import { registerCommandWithErrorHandling } from "./common/vscode/commands";
126126

127127
/**
128128
* extension.ts
@@ -238,7 +238,9 @@ function registerErrorStubs(
238238
if (excludedCommands.indexOf(command) === -1) {
239239
// This is purposefully using `commandRunner` instead of the command manager because these
240240
// commands are untyped and registered pre-activation.
241-
errorStubs.push(commandRunner(command, stubGenerator(command)));
241+
errorStubs.push(
242+
registerCommandWithErrorHandling(command, stubGenerator(command)),
243+
);
242244
}
243245
});
244246
}
@@ -341,7 +343,7 @@ export async function activate(
341343
ctx.subscriptions.push(
342344
// This is purposefully using `commandRunner` directly instead of the command manager
343345
// because this command is registered pre-activation.
344-
commandRunner(checkForUpdatesCommand, () =>
346+
registerCommandWithErrorHandling(checkForUpdatesCommand, () =>
345347
installOrUpdateThenTryActivate(
346348
ctx,
347349
app,

0 commit comments

Comments
 (0)