Skip to content

Commit 1d42d48

Browse files
authored
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
2 parents d342b06 + 745544c commit 1d42d48

File tree

6 files changed

+118
-12
lines changed

6 files changed

+118
-12
lines changed

extensions/ql-vscode/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- Avoid showing a popup when hovering over source elements in database source files. [#3125](https://github.com/github/vscode-codeql/pull/3125)
77
- 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)
88
- 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)
9+
- Add option to cancel opening the model editor. [#3189](https://github.com/github/vscode-codeql/pull/3189)
910

1011
## 1.11.0 - 13 December 2023
1112

extensions/ql-vscode/src/model-editor/extension-pack-picker.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { join } from "path";
22
import { outputFile, pathExists, readFile } from "fs-extra";
33
import { dump as dumpYaml, load as loadYaml } from "js-yaml";
4-
import { Uri } from "vscode";
4+
import { CancellationToken, Uri } from "vscode";
55
import Ajv from "ajv";
66
import { CodeQLCliServer } from "../codeql-cli/cli";
77
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
8-
import { ProgressCallback } from "../common/vscode/progress";
8+
import {
9+
ProgressCallback,
10+
UserCancellationException,
11+
} from "../common/vscode/progress";
912
import { DatabaseItem } from "../databases/local-databases";
1013
import { getQlPackPath, QLPACK_FILENAMES } from "../common/ql";
1114
import { getErrorMessage } from "../common/helpers-pure";
@@ -31,6 +34,7 @@ export async function pickExtensionPack(
3134
modelConfig: ModelConfig,
3235
logger: NotificationLogger,
3336
progress: ProgressCallback,
37+
token: CancellationToken,
3438
maxStep: number,
3539
): Promise<ExtensionPack | undefined> {
3640
progress({
@@ -50,6 +54,13 @@ export async function pickExtensionPack(
5054
true,
5155
);
5256

57+
if (token.isCancellationRequested) {
58+
throw new UserCancellationException(
59+
"Open Model editor action cancelled.",
60+
true,
61+
);
62+
}
63+
5364
progress({
5465
message: "Creating extension pack...",
5566
step: 2,

extensions/ql-vscode/src/model-editor/model-editor-module.ts

+21-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import { DatabaseItem, DatabaseManager } from "../databases/local-databases";
66
import { ensureDir } from "fs-extra";
77
import { join } from "path";
88
import { App } from "../common/app";
9-
import { withProgress } from "../common/vscode/progress";
9+
import {
10+
UserCancellationException,
11+
withProgress,
12+
} from "../common/vscode/progress";
1013
import { pickExtensionPack } from "./extension-pack-picker";
1114
import { showAndLogErrorMessage } from "../common/logging";
1215
import { dir } from "tmp-promise";
@@ -159,7 +162,7 @@ export class ModelEditorModule extends DisposableObject {
159162
}
160163

161164
return withProgress(
162-
async (progress) => {
165+
async (progress, token) => {
163166
const maxStep = 4;
164167

165168
if (!(await this.cliServer.cliConstraints.supportsQlpacksKind())) {
@@ -176,6 +179,7 @@ export class ModelEditorModule extends DisposableObject {
176179
this.modelConfig,
177180
this.app.logger,
178181
progress,
182+
token,
179183
maxStep,
180184
);
181185
if (!modelFile) {
@@ -188,6 +192,13 @@ export class ModelEditorModule extends DisposableObject {
188192
maxStep,
189193
});
190194

195+
if (token.isCancellationRequested) {
196+
throw new UserCancellationException(
197+
"Open Model editor action cancelled.",
198+
true,
199+
);
200+
}
201+
191202
// Create new temporary directory for query files and pack dependencies
192203
const { path: queryDir, cleanup: cleanupQueryDir } = await dir({
193204
unsafeCleanup: true,
@@ -211,6 +222,13 @@ export class ModelEditorModule extends DisposableObject {
211222
maxStep,
212223
});
213224

225+
if (token.isCancellationRequested) {
226+
throw new UserCancellationException(
227+
"Open Model editor action cancelled.",
228+
true,
229+
);
230+
}
231+
214232
// Check again just before opening the editor to ensure no model editor has been opened between
215233
// our first check and now.
216234
if (this.modelingStore.isDbOpen(db.databaseUri.toString())) {
@@ -253,6 +271,7 @@ export class ModelEditorModule extends DisposableObject {
253271
},
254272
{
255273
title: "Opening CodeQL Model Editor",
274+
cancellable: true,
256275
},
257276
);
258277
}

extensions/ql-vscode/src/model-editor/model-editor-queries.ts

+32-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import {
77
import { CancellationToken } from "vscode";
88
import { CodeQLCliServer } from "../codeql-cli/cli";
99
import { DatabaseItem } from "../databases/local-databases";
10-
import { ProgressCallback } from "../common/vscode/progress";
10+
import {
11+
ProgressCallback,
12+
UserCancellationException,
13+
} from "../common/vscode/progress";
1114
import { redactableError } from "../common/errors";
1215
import { telemetryListener } from "../common/vscode/telemetry";
1316
import { join } from "path";
@@ -89,6 +92,13 @@ export async function runModelEditorQueries(
8992
// For a reference of what this should do in the future, see the previous implementation in
9093
// https://github.com/github/vscode-codeql/blob/089d3566ef0bc67d9b7cc66e8fd6740b31c1c0b0/extensions/ql-vscode/src/data-extensions-editor/external-api-usage-query.ts#L33-L72
9194

95+
if (token.isCancellationRequested) {
96+
throw new UserCancellationException(
97+
"Run model editor queries cancelled.",
98+
true,
99+
);
100+
}
101+
92102
progress({
93103
message: "Resolving QL packs",
94104
step: 1,
@@ -99,6 +109,13 @@ export async function runModelEditorQueries(
99109
await cliServer.resolveQlpacks(additionalPacks, true),
100110
);
101111

112+
if (token.isCancellationRequested) {
113+
throw new UserCancellationException(
114+
"Run model editor queries cancelled.",
115+
true,
116+
);
117+
}
118+
102119
progress({
103120
message: "Resolving query",
104121
step: 2,
@@ -143,6 +160,13 @@ export async function runModelEditorQueries(
143160
return;
144161
}
145162

163+
if (token.isCancellationRequested) {
164+
throw new UserCancellationException(
165+
"Run model editor queries cancelled.",
166+
true,
167+
);
168+
}
169+
146170
// Read the results and covert to internal representation
147171
progress({
148172
message: "Decoding results",
@@ -159,6 +183,13 @@ export async function runModelEditorQueries(
159183
return;
160184
}
161185

186+
if (token.isCancellationRequested) {
187+
throw new UserCancellationException(
188+
"Run model editor queries cancelled.",
189+
true,
190+
);
191+
}
192+
162193
progress({
163194
message: "Finalizing results",
164195
step: 1950,

extensions/ql-vscode/src/model-editor/model-editor-view.ts

+34-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
CancellationToken,
23
CancellationTokenSource,
34
Tab,
45
TabInputWebview,
@@ -14,7 +15,11 @@ import {
1415
FromModelEditorMessage,
1516
ToModelEditorMessage,
1617
} from "../common/interface-types";
17-
import { ProgressCallback, withProgress } from "../common/vscode/progress";
18+
import {
19+
ProgressCallback,
20+
UserCancellationException,
21+
withProgress,
22+
} from "../common/vscode/progress";
1823
import { QueryRunner } from "../query-server";
1924
import {
2025
showAndLogErrorMessage,
@@ -47,6 +52,8 @@ import { ModelingStore } from "./modeling-store";
4752
import { ModelingEvents } from "./modeling-events";
4853
import { getModelsAsDataLanguage, ModelsAsDataLanguage } from "./languages";
4954
import { runGenerateQueries } from "./generate";
55+
import { ResponseError } from "vscode-jsonrpc";
56+
import { LSPErrorCodes } from "vscode-languageclient";
5057

5158
export class ModelEditorView extends AbstractWebview<
5259
ToModelEditorMessage,
@@ -338,8 +345,8 @@ export class ModelEditorView extends AbstractWebview<
338345

339346
await Promise.all([
340347
this.setViewState(),
341-
withProgress((progress) => this.loadMethods(progress), {
342-
cancellable: false,
348+
withProgress((progress, token) => this.loadMethods(progress, token), {
349+
cancellable: true,
343350
}),
344351
this.loadExistingModeledMethods(),
345352
]);
@@ -423,11 +430,16 @@ export class ModelEditorView extends AbstractWebview<
423430
}
424431
}
425432

426-
protected async loadMethods(progress: ProgressCallback): Promise<void> {
433+
protected async loadMethods(
434+
progress: ProgressCallback,
435+
token?: CancellationToken,
436+
): Promise<void> {
427437
const mode = this.modelingStore.getMode(this.databaseItem);
428438

429439
try {
430-
const cancellationTokenSource = new CancellationTokenSource();
440+
if (!token) {
441+
token = new CancellationTokenSource().token;
442+
}
431443
const queryResult = await runModelEditorQueries(mode, {
432444
cliServer: this.cliServer,
433445
queryRunner: this.queryRunner,
@@ -441,14 +453,29 @@ export class ModelEditorView extends AbstractWebview<
441453
...update,
442454
message: `Loading models: ${update.message}`,
443455
}),
444-
token: cancellationTokenSource.token,
456+
token,
445457
});
446458
if (!queryResult) {
447459
return;
448460
}
449461

462+
if (token.isCancellationRequested) {
463+
throw new UserCancellationException(
464+
"Model editor: Load methods cancelled.",
465+
true,
466+
);
467+
}
468+
450469
this.modelingStore.setMethods(this.databaseItem, queryResult);
451470
} catch (err) {
471+
if (
472+
err instanceof ResponseError &&
473+
err.code === LSPErrorCodes.RequestCancelled
474+
) {
475+
this.panel?.dispose();
476+
return;
477+
}
478+
452479
void showAndLogExceptionWithTelemetry(
453480
this.app.logger,
454481
this.app.telemetry,
@@ -578,6 +605,7 @@ export class ModelEditorView extends AbstractWebview<
578605
this.modelConfig,
579606
this.app.logger,
580607
progress,
608+
token,
581609
3,
582610
);
583611
if (!modelFile) {

extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/extension-pack-picker.test.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { Uri, workspace, WorkspaceFolder } from "vscode";
1+
import {
2+
CancellationTokenSource,
3+
Uri,
4+
workspace,
5+
WorkspaceFolder,
6+
} from "vscode";
27
import { dump as dumpYaml, load as loadYaml } from "js-yaml";
38
import { outputFile, readFile } from "fs-extra";
49
import { join } from "path";
@@ -24,6 +29,7 @@ describe("pickExtensionPack", () => {
2429
};
2530

2631
const progress = jest.fn();
32+
const token = new CancellationTokenSource().token;
2733
let workspaceFoldersSpy: jest.SpyInstance;
2834
let additionalPacks: string[];
2935
let workspaceFolder: WorkspaceFolder;
@@ -79,6 +85,7 @@ describe("pickExtensionPack", () => {
7985
modelConfig,
8086
logger,
8187
progress,
88+
token,
8289
maxStep,
8390
),
8491
).toEqual(autoExtensionPack);
@@ -136,6 +143,7 @@ describe("pickExtensionPack", () => {
136143
modelConfig,
137144
logger,
138145
progress,
146+
token,
139147
maxStep,
140148
),
141149
).toEqual({
@@ -190,6 +198,7 @@ describe("pickExtensionPack", () => {
190198
modelConfig,
191199
logger,
192200
progress,
201+
token,
193202
maxStep,
194203
),
195204
).toEqual({
@@ -234,6 +243,7 @@ describe("pickExtensionPack", () => {
234243
modelConfig,
235244
logger,
236245
progress,
246+
token,
237247
maxStep,
238248
),
239249
).toEqual(undefined);
@@ -260,6 +270,7 @@ describe("pickExtensionPack", () => {
260270
modelConfig,
261271
logger,
262272
progress,
273+
token,
263274
maxStep,
264275
),
265276
).toEqual(undefined);
@@ -288,6 +299,7 @@ describe("pickExtensionPack", () => {
288299
modelConfig,
289300
logger,
290301
progress,
302+
token,
291303
maxStep,
292304
),
293305
).toEqual(undefined);
@@ -326,6 +338,7 @@ describe("pickExtensionPack", () => {
326338
modelConfig,
327339
logger,
328340
progress,
341+
token,
329342
maxStep,
330343
),
331344
).toEqual(undefined);
@@ -364,6 +377,7 @@ describe("pickExtensionPack", () => {
364377
modelConfig,
365378
logger,
366379
progress,
380+
token,
367381
maxStep,
368382
),
369383
).toEqual(undefined);
@@ -405,6 +419,7 @@ describe("pickExtensionPack", () => {
405419
modelConfig,
406420
logger,
407421
progress,
422+
token,
408423
maxStep,
409424
),
410425
).toEqual(undefined);
@@ -463,6 +478,7 @@ describe("pickExtensionPack", () => {
463478
modelConfig,
464479
logger,
465480
progress,
481+
token,
466482
maxStep,
467483
),
468484
).toEqual(extensionPack);

0 commit comments

Comments
 (0)