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
76 changes: 0 additions & 76 deletions extensions/ql-vscode/src/commandRunner.ts

This file was deleted.

75 changes: 70 additions & 5 deletions extensions/ql-vscode/src/common/vscode/commands.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import { commands } from "vscode";
import { commandRunner, NoProgressTask } from "../../commandRunner";
import { commands, Disposable } from "vscode";
import { CommandFunction, CommandManager } from "../../packages/commands";
import { OutputChannelLogger } from "../logging";
import { extLogger, OutputChannelLogger } from "../logging";
import {
asError,
getErrorMessage,
getErrorStack,
} from "../../pure/helpers-pure";
import { redactableError } from "../../pure/errors";
import { UserCancellationException } from "../../progress";
import {
showAndLogExceptionWithTelemetry,
showAndLogWarningMessage,
} from "../../helpers";
import { telemetryListener } from "../../telemetry";

/**
* Create a command manager for VSCode, wrapping the commandRunner
Expand All @@ -10,11 +21,65 @@ import { OutputChannelLogger } from "../logging";
export function createVSCodeCommandManager<
Commands extends Record<string, CommandFunction>,
>(outputLogger?: OutputChannelLogger): CommandManager<Commands> {
return new CommandManager((commandId, task: NoProgressTask) => {
return commandRunner(commandId, task, outputLogger);
return new CommandManager((commandId, task) => {
return registerCommandWithErrorHandling(commandId, task, outputLogger);
}, wrapExecuteCommand);
}

/**
* A wrapper for command registration. This wrapper adds uniform error handling for commands.
*
* @param commandId The ID of the command to register.
* @param task The task to run. It is passed directly to `commands.registerCommand`. Any
* arguments to the command handler are passed on to the task.
*/
export function registerCommandWithErrorHandling(
commandId: string,
task: (...args: any[]) => Promise<any>,
outputLogger = extLogger,
): Disposable {
return commands.registerCommand(commandId, async (...args: any[]) => {
const startTime = Date.now();
let error: Error | undefined;

try {
return await task(...args);
} catch (e) {
error = asError(e);
const errorMessage = redactableError(error)`${
getErrorMessage(e) || e
} (${commandId})`;
const errorStack = getErrorStack(e);
if (e instanceof UserCancellationException) {
// User has cancelled this action manually
if (e.silent) {
void outputLogger.log(errorMessage.fullMessage);
} else {
void showAndLogWarningMessage(errorMessage.fullMessage, {
outputLogger,
});
}
} else {
// Include the full stack in the error log only.
const fullMessage = errorStack
? `${errorMessage.fullMessage}\n${errorStack}`
: errorMessage.fullMessage;
void showAndLogExceptionWithTelemetry(errorMessage, {
outputLogger,
fullMessage,
extraTelemetryProperties: {
command: commandId,
},
});
}
return undefined;
} finally {
const executionTime = Date.now() - startTime;
telemetryListener?.sendCommandUsage(commandId, executionTime, error);
}
});
}

/**
* wrapExecuteCommand wraps commands.executeCommand to satisfy that the
* type is a Promise. Type script does not seem to be smart enough
Expand Down
8 changes: 5 additions & 3 deletions extensions/ql-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ import { QLTestAdapterFactory } from "./test-adapter";
import { TestUIService } from "./test-ui";
import { CompareView } from "./compare/compare-view";
import { initializeTelemetry } from "./telemetry";
import { commandRunner } from "./commandRunner";
import { ProgressCallback, withProgress } from "./progress";
import { CodeQlStatusBarHandler } from "./status-bar";
import { getPackagingCommands } from "./packaging";
Expand Down Expand Up @@ -123,6 +122,7 @@ import {
import { getAstCfgCommands } from "./ast-cfg-commands";
import { getQueryEditorCommands } from "./query-editor";
import { App } from "./common/app";
import { registerCommandWithErrorHandling } from "./common/vscode/commands";

/**
* extension.ts
Expand Down Expand Up @@ -238,7 +238,9 @@ function registerErrorStubs(
if (excludedCommands.indexOf(command) === -1) {
// This is purposefully using `commandRunner` instead of the command manager because these
// commands are untyped and registered pre-activation.
errorStubs.push(commandRunner(command, stubGenerator(command)));
errorStubs.push(
registerCommandWithErrorHandling(command, stubGenerator(command)),
);
}
});
}
Expand Down Expand Up @@ -341,7 +343,7 @@ export async function activate(
ctx.subscriptions.push(
// This is purposefully using `commandRunner` directly instead of the command manager
// because this command is registered pre-activation.
commandRunner(checkForUpdatesCommand, () =>
registerCommandWithErrorHandling(checkForUpdatesCommand, () =>
installOrUpdateThenTryActivate(
ctx,
app,
Expand Down