Use type-safe VSCode commands

This commit is contained in:
Dave Bartolomeo
2023-04-11 11:22:45 -04:00
parent 7dfa52bbab
commit 19e083e473
6 changed files with 56 additions and 45 deletions

View File

@@ -11,6 +11,7 @@ import type {
VariantAnalysisScannedRepository,
VariantAnalysisScannedRepositoryResult,
} from "../variant-analysis/shared/variant-analysis";
import { QLDebugConfiguration } from "../debugger/debug-configuration";
// A command function matching the signature that VS Code calls when
// a command on a selection is invoked.
@@ -52,6 +53,15 @@ export type BuiltInVsCodeCommands = {
) => Promise<void>;
"vscode.open": (uri: Uri) => Promise<void>;
"vscode.openFolder": (uri: Uri) => Promise<void>;
// We type the `config` property specifically as a CodeQL debug configuration, since that's the
// only kinds we specify anyway.
"workbench.action.debug.start": (options?: {
config?: Partial<QLDebugConfiguration>;
noDebug?: boolean;
}) => Promise<void>;
"workbench.action.debug.stepInto": () => Promise<void>;
"workbench.action.debug.stepOver": () => Promise<void>;
"workbench.action.debug.stepOut": () => Promise<void>;
};
// Commands that are available before the extension is fully activated.

View File

@@ -6,7 +6,6 @@ import {
debug,
Uri,
CancellationTokenSource,
commands,
} from "vscode";
import { DebuggerCommands } from "../common/commands";
import { isCanary } from "../config";
@@ -24,6 +23,7 @@ import {
} from "../run-queries-shared";
import { QLResolvedDebugConfiguration } from "./debug-configuration";
import * as CodeQLProtocol from "./debug-protocol";
import { App } from "../common/app";
/**
* Listens to messages passing between VS Code and the debug adapter, so that we can supplement the
@@ -144,6 +144,7 @@ export class DebuggerUI
private readonly sessions = new Map<string, QLDebugAdapterTracker>();
constructor(
private readonly app: App,
private readonly localQueryResultsView: ResultsView,
private readonly localQueries: LocalQueries,
private readonly dbm: DatabaseManager,
@@ -204,7 +205,7 @@ export class DebuggerUI
private async startDebuggingSelection(): Promise<void> {
// Launch the currently selected debug configuration, but specifying QuickEval mode.
await commands.executeCommand("workbench.action.debug.start", {
await this.app.commands.execute("workbench.action.debug.start", {
config: {
quickEval: true,
},

View File

@@ -873,7 +873,12 @@ async function activateWithInstalledDistribution(
);
void extLogger.log("Initializing debugger UI.");
const debuggerUI = new DebuggerUI(localQueryResultsView, localQueries, dbm);
const debuggerUI = new DebuggerUI(
app,
localQueryResultsView,
localQueries,
dbm,
);
ctx.subscriptions.push(debuggerUI);
const dataExtensionsEditorModule =

View File

@@ -4,13 +4,10 @@ import {
DebugSession,
ProviderResult,
Uri,
commands,
debug,
workspace,
} from "vscode";
import * as CodeQLProtocol from "../../../src/debugger/debug-protocol";
import { DebuggerCommands } from "../../../src/common/commands";
import { CommandManager } from "../../../src/packages/commands";
import { DisposableObject } from "../../../src/pure/disposable-object";
import { QueryResultType } from "../../../src/pure/legacy-messages";
import { CoreCompletedQuery } from "../../../src/queryRunner";
@@ -22,6 +19,7 @@ import {
import { join } from "path";
import { writeFile } from "fs-extra";
import { expect } from "@jest/globals";
import { AppCommandManager } from "../../../src/common/commands";
type Resolver<T> = (value: T) => void;
@@ -190,9 +188,7 @@ export class DebugController
*/
private resolver: Resolver<AnyDebugEvent> | undefined = undefined;
public constructor(
private readonly debuggerCommands: CommandManager<DebuggerCommands>,
) {
public constructor(private readonly appCommands: AppCommandManager) {
super();
this.push(debug.registerDebugAdapterTrackerFactory("codeql", this));
this.push(
@@ -232,7 +228,7 @@ export class DebugController
* Starts a debug session via the "codeQL.debugQuery" copmmand.
*/
public debugQuery(uri: Uri): Promise<void> {
return this.debuggerCommands.execute("codeQL.debugQuery", uri);
return this.appCommands.execute("codeQL.debugQuery", uri);
}
public async startDebugging(
@@ -251,7 +247,7 @@ export class DebugController
}
: {};
return await commands.executeCommand("workbench.action.debug.start", {
return await this.appCommands.execute("workbench.action.debug.start", {
config: fullConfig,
...options,
});
@@ -265,21 +261,19 @@ export class DebugController
}
public async continueDebuggingSelection(): Promise<void> {
return await this.debuggerCommands.execute(
"codeQL.continueDebuggingSelection",
);
return await this.appCommands.execute("codeQL.continueDebuggingSelection");
}
public async stepInto(): Promise<void> {
return await commands.executeCommand("workbench.action.debug.stepInto");
return await this.appCommands.execute("workbench.action.debug.stepInto");
}
public async stepOver(): Promise<void> {
return await commands.executeCommand("workbench.action.debug.stepOver");
return await this.appCommands.execute("workbench.action.debug.stepOver");
}
public async stepOut(): Promise<void> {
return await commands.executeCommand("workbench.action.debug.stepOut");
return await this.appCommands.execute("workbench.action.debug.stepOut");
}
public handleEvent(event: AnyDebugEvent): void {
@@ -411,12 +405,12 @@ export class DebugController
* debug controller is cleaned up.
*/
export async function withDebugController<T>(
debuggerCommands: CommandManager<DebuggerCommands>,
appCommands: AppCommandManager,
op: (controller: DebugController) => Promise<T>,
): Promise<T> {
await workspace.getConfiguration().update("codeQL.canary", true);
try {
const controller = new DebugController(debuggerCommands);
const controller = new DebugController(appCommands);
try {
try {
const result = await op(controller);

View File

@@ -8,13 +8,13 @@ import {
getActivatedExtension,
} from "../global.helper";
import { describeWithCodeQL } from "../cli";
import { createVSCodeCommandManager } from "../../../src/common/vscode/commands";
import { DebuggerCommands } from "../../../src/common/commands";
import { withDebugController } from "./debug-controller";
import { CodeQLCliServer } from "../../../src/cli";
import { QueryOutputDir } from "../../../src/run-queries-shared";
import { createVSCodeCommandManager } from "../../../src/common/vscode/commands";
import { AllCommands } from "../../../src/common/commands";
jest.setTimeout(2000_000);
jest.setTimeout(20_000);
async function selectForQuickEval(
path: string,
@@ -43,7 +43,7 @@ async function getResultCount(
describeWithCodeQL()("Debugger", () => {
let databaseManager: DatabaseManager;
let cli: CodeQLCliServer;
const debuggerCommands = createVSCodeCommandManager<DebuggerCommands>();
const appCommands = createVSCodeCommandManager<AllCommands>();
const simpleQueryPath = join(__dirname, "data", "simple-query.ql");
const quickEvalQueryPath = join(__dirname, "data", "QuickEvalQuery.ql");
const quickEvalLibPath = join(__dirname, "data", "QuickEvalLib.qll");
@@ -62,7 +62,7 @@ describeWithCodeQL()("Debugger", () => {
});
it("should debug a query and keep the session active", async () => {
await withDebugController(debuggerCommands, async (controller) => {
await withDebugController(appCommands, async (controller) => {
await controller.debugQuery(Uri.file(simpleQueryPath));
await controller.expectLaunched();
await controller.expectSucceeded();
@@ -71,7 +71,7 @@ describeWithCodeQL()("Debugger", () => {
});
it("should run a query and then stop debugging", async () => {
await withDebugController(debuggerCommands, async (controller) => {
await withDebugController(appCommands, async (controller) => {
await controller.startDebugging(
{
query: simpleQueryPath,
@@ -87,7 +87,7 @@ describeWithCodeQL()("Debugger", () => {
});
it("should run a quick evaluation", async () => {
await withDebugController(debuggerCommands, async (controller) => {
await withDebugController(appCommands, async (controller) => {
await selectForQuickEval(quickEvalQueryPath, 18, 5, 18, 22);
// Don't specify a query path, so we'll default to the active document ("QuickEvalQuery.ql")
@@ -105,7 +105,7 @@ describeWithCodeQL()("Debugger", () => {
});
it("should run a quick evaluation on a library without any query context", async () => {
await withDebugController(debuggerCommands, async (controller) => {
await withDebugController(appCommands, async (controller) => {
await selectForQuickEval(quickEvalLibPath, 4, 15, 4, 32);
// Don't specify a query path, so we'll default to the active document ("QuickEvalLib.qll")
@@ -123,7 +123,7 @@ describeWithCodeQL()("Debugger", () => {
});
it("should run a quick evaluation on a library in the context of a specific query", async () => {
await withDebugController(debuggerCommands, async (controller) => {
await withDebugController(appCommands, async (controller) => {
await selectForQuickEval(quickEvalLibPath, 4, 15, 4, 32);
await controller.startDebuggingSelection({

View File

@@ -1,4 +1,4 @@
import { debug, CancellationToken, ExtensionContext, Range, Uri } from "vscode";
import { CancellationToken, ExtensionContext, Range, Uri } from "vscode";
import { join, dirname } from "path";
import {
pathExistsSync,
@@ -24,19 +24,18 @@ import { QueryResultType } from "../../../src/pure/new-messages";
import { createVSCodeCommandManager } from "../../../src/common/vscode/commands";
import {
AllCommands,
DebuggerCommands,
AppCommandManager,
QueryServerCommands,
} from "../../../src/common/commands";
import { ProgressCallback } from "../../../src/progress";
import { CommandManager } from "../../../src/packages/commands";
import { withDebugController } from "./debug-controller";
type DebugMode = "localQueries" | "launch";
type DebugMode = "localQueries" | "debug";
async function compileAndRunQuery(
mode: DebugMode,
appCommands: AppCommandManager,
localQueries: LocalQueries,
debuggerCommands: CommandManager<DebuggerCommands>,
quickEval: boolean,
queryUri: Uri,
progress: ProgressCallback,
@@ -55,14 +54,17 @@ async function compileAndRunQuery(
range,
);
case "launch":
return await withDebugController(debuggerCommands, async (controller) => {
await controller.debugQuery(queryUri);
case "debug":
return await withDebugController(appCommands, async (controller) => {
await controller.startDebugging(
{
query: queryUri.fsPath,
},
true,
);
await controller.expectLaunched();
const succeeded = await controller.expectSucceeded();
await controller.expectStopped();
expect(debug.activeDebugSession?.name).not.toBeUndefined();
await debug.stopDebugging();
await controller.expectExited();
await controller.expectTerminated();
await controller.expectSessionClosed();
@@ -71,9 +73,9 @@ async function compileAndRunQuery(
}
}
jest.setTimeout(2000_000);
jest.setTimeout(20_000);
const MODES: DebugMode[] = ["localQueries", "launch"];
const MODES: DebugMode[] = ["localQueries", "debug"];
/**
* Integration tests for queries
@@ -90,7 +92,6 @@ describeWithCodeQL()("Queries", () => {
const appCommandManager = createVSCodeCommandManager<AllCommands>();
const queryServerCommandManager =
createVSCodeCommandManager<QueryServerCommands>();
const debuggerCommands = createVSCodeCommandManager<DebuggerCommands>();
let qlpackFile: string;
let qlpackLockFile: string;
@@ -174,8 +175,8 @@ describeWithCodeQL()("Queries", () => {
async function runQueryWithExtensions() {
const result = await compileAndRunQuery(
mode,
appCommandManager,
localQueries,
debuggerCommands,
false,
Uri.file(queryUsingExtensionPath),
progress,
@@ -208,8 +209,8 @@ describeWithCodeQL()("Queries", () => {
const queryPath = join(__dirname, "data", "simple-query.ql");
const result = await compileAndRunQuery(
mode,
appCommandManager,
localQueries,
debuggerCommands,
false,
Uri.file(queryPath),
progress,
@@ -228,8 +229,8 @@ describeWithCodeQL()("Queries", () => {
const queryPath = join(__dirname, "data", "simple-query.ql");
const result = await compileAndRunQuery(
mode,
appCommandManager,
localQueries,
debuggerCommands,
false,
Uri.file(queryPath),
progress,