Merge pull request #3189 from github/nora/make-model-editor-open-cancellable
CodeQL model editor: user should be able to stop modeling before the main editor view is open
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
- Avoid showing a popup when hovering over source elements in database source files. [#3125](https://github.com/github/vscode-codeql/pull/3125)
|
||||
- Add comparison of alerts when comparing query results. This allows viewing path explanations for differences in alerts. [#3113](https://github.com/github/vscode-codeql/pull/3113)
|
||||
- Fix a bug where the CodeQL CLI and variant analysis results were corrupted after extraction in VS Code Insiders. [#3151](https://github.com/github/vscode-codeql/pull/3151) & [#3152](https://github.com/github/vscode-codeql/pull/3152)
|
||||
- Add option to cancel opening the model editor. [#3189](https://github.com/github/vscode-codeql/pull/3189)
|
||||
|
||||
## 1.11.0 - 13 December 2023
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { join } from "path";
|
||||
import { outputFile, pathExists, readFile } from "fs-extra";
|
||||
import { dump as dumpYaml, load as loadYaml } from "js-yaml";
|
||||
import { Uri } from "vscode";
|
||||
import { CancellationToken, Uri } from "vscode";
|
||||
import Ajv from "ajv";
|
||||
import { CodeQLCliServer } from "../codeql-cli/cli";
|
||||
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
|
||||
import { ProgressCallback } from "../common/vscode/progress";
|
||||
import {
|
||||
ProgressCallback,
|
||||
UserCancellationException,
|
||||
} from "../common/vscode/progress";
|
||||
import { DatabaseItem } from "../databases/local-databases";
|
||||
import { getQlPackPath, QLPACK_FILENAMES } from "../common/ql";
|
||||
import { getErrorMessage } from "../common/helpers-pure";
|
||||
@@ -31,6 +34,7 @@ export async function pickExtensionPack(
|
||||
modelConfig: ModelConfig,
|
||||
logger: NotificationLogger,
|
||||
progress: ProgressCallback,
|
||||
token: CancellationToken,
|
||||
maxStep: number,
|
||||
): Promise<ExtensionPack | undefined> {
|
||||
progress({
|
||||
@@ -50,6 +54,13 @@ export async function pickExtensionPack(
|
||||
true,
|
||||
);
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
throw new UserCancellationException(
|
||||
"Open Model editor action cancelled.",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
progress({
|
||||
message: "Creating extension pack...",
|
||||
step: 2,
|
||||
|
||||
@@ -6,7 +6,10 @@ import { DatabaseItem, DatabaseManager } from "../databases/local-databases";
|
||||
import { ensureDir } from "fs-extra";
|
||||
import { join } from "path";
|
||||
import { App } from "../common/app";
|
||||
import { withProgress } from "../common/vscode/progress";
|
||||
import {
|
||||
UserCancellationException,
|
||||
withProgress,
|
||||
} from "../common/vscode/progress";
|
||||
import { pickExtensionPack } from "./extension-pack-picker";
|
||||
import { showAndLogErrorMessage } from "../common/logging";
|
||||
import { dir } from "tmp-promise";
|
||||
@@ -159,7 +162,7 @@ export class ModelEditorModule extends DisposableObject {
|
||||
}
|
||||
|
||||
return withProgress(
|
||||
async (progress) => {
|
||||
async (progress, token) => {
|
||||
const maxStep = 4;
|
||||
|
||||
if (!(await this.cliServer.cliConstraints.supportsQlpacksKind())) {
|
||||
@@ -176,6 +179,7 @@ export class ModelEditorModule extends DisposableObject {
|
||||
this.modelConfig,
|
||||
this.app.logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
);
|
||||
if (!modelFile) {
|
||||
@@ -188,6 +192,13 @@ export class ModelEditorModule extends DisposableObject {
|
||||
maxStep,
|
||||
});
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
throw new UserCancellationException(
|
||||
"Open Model editor action cancelled.",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
// Create new temporary directory for query files and pack dependencies
|
||||
const { path: queryDir, cleanup: cleanupQueryDir } = await dir({
|
||||
unsafeCleanup: true,
|
||||
@@ -211,6 +222,13 @@ export class ModelEditorModule extends DisposableObject {
|
||||
maxStep,
|
||||
});
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
throw new UserCancellationException(
|
||||
"Open Model editor action cancelled.",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
// Check again just before opening the editor to ensure no model editor has been opened between
|
||||
// our first check and now.
|
||||
if (this.modelingStore.isDbOpen(db.databaseUri.toString())) {
|
||||
@@ -253,6 +271,7 @@ export class ModelEditorModule extends DisposableObject {
|
||||
},
|
||||
{
|
||||
title: "Opening CodeQL Model Editor",
|
||||
cancellable: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ import {
|
||||
import { CancellationToken } from "vscode";
|
||||
import { CodeQLCliServer } from "../codeql-cli/cli";
|
||||
import { DatabaseItem } from "../databases/local-databases";
|
||||
import { ProgressCallback } from "../common/vscode/progress";
|
||||
import {
|
||||
ProgressCallback,
|
||||
UserCancellationException,
|
||||
} from "../common/vscode/progress";
|
||||
import { redactableError } from "../common/errors";
|
||||
import { telemetryListener } from "../common/vscode/telemetry";
|
||||
import { join } from "path";
|
||||
@@ -89,6 +92,13 @@ export async function runModelEditorQueries(
|
||||
// For a reference of what this should do in the future, see the previous implementation in
|
||||
// https://github.com/github/vscode-codeql/blob/089d3566ef0bc67d9b7cc66e8fd6740b31c1c0b0/extensions/ql-vscode/src/data-extensions-editor/external-api-usage-query.ts#L33-L72
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
throw new UserCancellationException(
|
||||
"Run model editor queries cancelled.",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
progress({
|
||||
message: "Resolving QL packs",
|
||||
step: 1,
|
||||
@@ -99,6 +109,13 @@ export async function runModelEditorQueries(
|
||||
await cliServer.resolveQlpacks(additionalPacks, true),
|
||||
);
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
throw new UserCancellationException(
|
||||
"Run model editor queries cancelled.",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
progress({
|
||||
message: "Resolving query",
|
||||
step: 2,
|
||||
@@ -143,6 +160,13 @@ export async function runModelEditorQueries(
|
||||
return;
|
||||
}
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
throw new UserCancellationException(
|
||||
"Run model editor queries cancelled.",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
// Read the results and covert to internal representation
|
||||
progress({
|
||||
message: "Decoding results",
|
||||
@@ -159,6 +183,13 @@ export async function runModelEditorQueries(
|
||||
return;
|
||||
}
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
throw new UserCancellationException(
|
||||
"Run model editor queries cancelled.",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
progress({
|
||||
message: "Finalizing results",
|
||||
step: 1950,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
CancellationToken,
|
||||
CancellationTokenSource,
|
||||
Tab,
|
||||
TabInputWebview,
|
||||
@@ -14,7 +15,11 @@ import {
|
||||
FromModelEditorMessage,
|
||||
ToModelEditorMessage,
|
||||
} from "../common/interface-types";
|
||||
import { ProgressCallback, withProgress } from "../common/vscode/progress";
|
||||
import {
|
||||
ProgressCallback,
|
||||
UserCancellationException,
|
||||
withProgress,
|
||||
} from "../common/vscode/progress";
|
||||
import { QueryRunner } from "../query-server";
|
||||
import {
|
||||
showAndLogErrorMessage,
|
||||
@@ -47,6 +52,8 @@ import { ModelingStore } from "./modeling-store";
|
||||
import { ModelingEvents } from "./modeling-events";
|
||||
import { getModelsAsDataLanguage, ModelsAsDataLanguage } from "./languages";
|
||||
import { runGenerateQueries } from "./generate";
|
||||
import { ResponseError } from "vscode-jsonrpc";
|
||||
import { LSPErrorCodes } from "vscode-languageclient";
|
||||
|
||||
export class ModelEditorView extends AbstractWebview<
|
||||
ToModelEditorMessage,
|
||||
@@ -338,8 +345,8 @@ export class ModelEditorView extends AbstractWebview<
|
||||
|
||||
await Promise.all([
|
||||
this.setViewState(),
|
||||
withProgress((progress) => this.loadMethods(progress), {
|
||||
cancellable: false,
|
||||
withProgress((progress, token) => this.loadMethods(progress, token), {
|
||||
cancellable: true,
|
||||
}),
|
||||
this.loadExistingModeledMethods(),
|
||||
]);
|
||||
@@ -423,11 +430,16 @@ export class ModelEditorView extends AbstractWebview<
|
||||
}
|
||||
}
|
||||
|
||||
protected async loadMethods(progress: ProgressCallback): Promise<void> {
|
||||
protected async loadMethods(
|
||||
progress: ProgressCallback,
|
||||
token?: CancellationToken,
|
||||
): Promise<void> {
|
||||
const mode = this.modelingStore.getMode(this.databaseItem);
|
||||
|
||||
try {
|
||||
const cancellationTokenSource = new CancellationTokenSource();
|
||||
if (!token) {
|
||||
token = new CancellationTokenSource().token;
|
||||
}
|
||||
const queryResult = await runModelEditorQueries(mode, {
|
||||
cliServer: this.cliServer,
|
||||
queryRunner: this.queryRunner,
|
||||
@@ -441,14 +453,29 @@ export class ModelEditorView extends AbstractWebview<
|
||||
...update,
|
||||
message: `Loading models: ${update.message}`,
|
||||
}),
|
||||
token: cancellationTokenSource.token,
|
||||
token,
|
||||
});
|
||||
if (!queryResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
throw new UserCancellationException(
|
||||
"Model editor: Load methods cancelled.",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
this.modelingStore.setMethods(this.databaseItem, queryResult);
|
||||
} catch (err) {
|
||||
if (
|
||||
err instanceof ResponseError &&
|
||||
err.code === LSPErrorCodes.RequestCancelled
|
||||
) {
|
||||
this.panel?.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
void showAndLogExceptionWithTelemetry(
|
||||
this.app.logger,
|
||||
this.app.telemetry,
|
||||
@@ -578,6 +605,7 @@ export class ModelEditorView extends AbstractWebview<
|
||||
this.modelConfig,
|
||||
this.app.logger,
|
||||
progress,
|
||||
token,
|
||||
3,
|
||||
);
|
||||
if (!modelFile) {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { Uri, workspace, WorkspaceFolder } from "vscode";
|
||||
import {
|
||||
CancellationTokenSource,
|
||||
Uri,
|
||||
workspace,
|
||||
WorkspaceFolder,
|
||||
} from "vscode";
|
||||
import { dump as dumpYaml, load as loadYaml } from "js-yaml";
|
||||
import { outputFile, readFile } from "fs-extra";
|
||||
import { join } from "path";
|
||||
@@ -24,6 +29,7 @@ describe("pickExtensionPack", () => {
|
||||
};
|
||||
|
||||
const progress = jest.fn();
|
||||
const token = new CancellationTokenSource().token;
|
||||
let workspaceFoldersSpy: jest.SpyInstance;
|
||||
let additionalPacks: string[];
|
||||
let workspaceFolder: WorkspaceFolder;
|
||||
@@ -79,6 +85,7 @@ describe("pickExtensionPack", () => {
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual(autoExtensionPack);
|
||||
@@ -136,6 +143,7 @@ describe("pickExtensionPack", () => {
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual({
|
||||
@@ -190,6 +198,7 @@ describe("pickExtensionPack", () => {
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual({
|
||||
@@ -234,6 +243,7 @@ describe("pickExtensionPack", () => {
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
@@ -260,6 +270,7 @@ describe("pickExtensionPack", () => {
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
@@ -288,6 +299,7 @@ describe("pickExtensionPack", () => {
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
@@ -326,6 +338,7 @@ describe("pickExtensionPack", () => {
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
@@ -364,6 +377,7 @@ describe("pickExtensionPack", () => {
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
@@ -405,6 +419,7 @@ describe("pickExtensionPack", () => {
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
@@ -463,6 +478,7 @@ describe("pickExtensionPack", () => {
|
||||
modelConfig,
|
||||
logger,
|
||||
progress,
|
||||
token,
|
||||
maxStep,
|
||||
),
|
||||
).toEqual(extensionPack);
|
||||
|
||||
Reference in New Issue
Block a user