Skip to content

Commit 3e59859

Browse files
author
Dave Bartolomeo
authored
Merge pull request #2538 from github/dbartol/save-before-start
Save dirty documents before evaluating queries
2 parents aa4df08 + a4cff53 commit 3e59859

File tree

19 files changed

+222
-113
lines changed

19 files changed

+222
-113
lines changed

extensions/ql-vscode/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"contributes": {
7272
"configurationDefaults": {
7373
"[ql]": {
74+
"debug.saveBeforeStart": "nonUntitledEditorsInActiveGroup",
7475
"editor.wordBasedSuggestions": false
7576
},
7677
"[dbscheme]": {
@@ -246,8 +247,8 @@
246247
},
247248
"codeQL.runningQueries.autoSave": {
248249
"type": "boolean",
249-
"default": false,
250-
"description": "Enable automatically saving a modified query file when running a query."
250+
"description": "Enable automatically saving a modified query file when running a query.",
251+
"markdownDeprecationMessage": "This property is deprecated and no longer has any effect. To control automatic saving of documents before running queries, use the `debug.saveBeforeStart` setting."
251252
},
252253
"codeQL.runningQueries.maxQueries": {
253254
"type": "integer",
@@ -1726,7 +1727,7 @@
17261727
"test:vscode-integration:activated-extension": "jest --projects test/vscode-tests/activated-extension",
17271728
"test:vscode-integration:no-workspace": "jest --projects test/vscode-tests/no-workspace",
17281729
"test:vscode-integration:minimal-workspace": "jest --projects test/vscode-tests/minimal-workspace",
1729-
"test:cli-integration": "jest --projects test/vscode-tests/cli-integration",
1730+
"test:cli-integration": "jest --projects test/vscode-tests/cli-integration --verbose",
17301731
"update-vscode": "node ./node_modules/vscode/bin/install",
17311732
"format": "prettier --write **/*.{ts,tsx} && eslint . --ext .ts,.tsx --fix",
17321733
"lint": "eslint . --ext .js,.ts,.tsx --max-warnings=0",

extensions/ql-vscode/src/config.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ export interface InspectionResult<T> {
7070
workspaceFolderValue?: T;
7171
}
7272

73+
const VSCODE_DEBUG_SETTING = new Setting("debug", undefined);
74+
export const VSCODE_SAVE_BEFORE_START_SETTING = new Setting(
75+
"saveBeforeStart",
76+
VSCODE_DEBUG_SETTING,
77+
);
78+
7379
const ROOT_SETTING = new Setting("codeQL");
7480

7581
// Global configuration
@@ -161,10 +167,6 @@ export const NUMBER_OF_TEST_THREADS_SETTING = new Setting(
161167
RUNNING_TESTS_SETTING,
162168
);
163169
export const MAX_QUERIES = new Setting("maxQueries", RUNNING_QUERIES_SETTING);
164-
export const AUTOSAVE_SETTING = new Setting(
165-
"autoSave",
166-
RUNNING_QUERIES_SETTING,
167-
);
168170
export const PAGE_SIZE = new Setting("pageSize", RESULTS_DISPLAY_SETTING);
169171
const CUSTOM_LOG_DIRECTORY_SETTING = new Setting(
170172
"customLogDirectory",

extensions/ql-vscode/src/debugger/debugger-ui.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { CoreQueryResults } from "../query-server";
1414
import {
1515
getQuickEvalContext,
1616
QueryOutputDir,
17+
saveBeforeStart,
1718
validateQueryUri,
1819
} from "../run-queries-shared";
1920
import { QLResolvedDebugConfiguration } from "./debug-configuration";
@@ -73,6 +74,9 @@ class QLDebugAdapterTracker
7374
}
7475

7576
public async quickEval(): Promise<void> {
77+
// Since we're not going through VS Code's launch path, we need to save dirty files ourselves.
78+
await saveBeforeStart();
79+
7680
const args: CodeQLProtocol.QuickEvalRequest["arguments"] = {
7781
quickEvalContext: await getQuickEvalContext(undefined, false),
7882
};

extensions/ql-vscode/src/local-queries/local-queries.ts

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
Range,
1111
Uri,
1212
window,
13-
workspace,
1413
} from "vscode";
1514
import {
1615
TeeLogger,
@@ -32,8 +31,8 @@ import {
3231
createInitialQueryInfo,
3332
createTimestampFile,
3433
getQuickEvalContext,
35-
promptUserToSaveChanges,
3634
QueryOutputDir,
35+
saveBeforeStart,
3736
SelectedQuery,
3837
validateQueryUri,
3938
} from "../run-queries-shared";
@@ -54,25 +53,6 @@ interface DatabaseQuickPickItem extends QuickPickItem {
5453
databaseItem: DatabaseItem;
5554
}
5655

57-
/**
58-
* If either the query file or the quickeval file is dirty, give the user the chance to save them.
59-
*/
60-
async function promptToSaveQueryIfNeeded(query: SelectedQuery): Promise<void> {
61-
// There seems to be no way to ask VS Code to find an existing text document by name, without
62-
// automatically opening the document if it is not found.
63-
const queryUri = Uri.file(query.queryPath).toString();
64-
const quickEvalUri =
65-
query.quickEval !== undefined
66-
? Uri.file(query.quickEval.quickEvalPosition.fileName).toString()
67-
: undefined;
68-
for (const openDocument of workspace.textDocuments) {
69-
const documentUri = openDocument.uri.toString();
70-
if (documentUri === queryUri || documentUri === quickEvalUri) {
71-
await promptUserToSaveChanges(openDocument);
72-
}
73-
}
74-
}
75-
7656
export enum QuickEvalType {
7757
None,
7858
QuickEval,
@@ -432,6 +412,8 @@ export class LocalQueries extends DisposableObject {
432412
range?: Range,
433413
templates?: Record<string, string>,
434414
): Promise<CoreCompletedQuery> {
415+
await saveBeforeStart();
416+
435417
let queryPath: string;
436418
if (queryUri !== undefined) {
437419
// The query URI is provided by the command, most likely because the command was run from an
@@ -462,8 +444,6 @@ export class LocalQueries extends DisposableObject {
462444
const additionalPacks = getOnDiskWorkspaceFolders();
463445
const extensionPacks = await this.getDefaultExtensionPacks(additionalPacks);
464446

465-
await promptToSaveQueryIfNeeded(selectedQuery);
466-
467447
const coreQueryRun = this.queryRunner.createQueryRun(
468448
databaseItem.databaseUri.fsPath,
469449
{

extensions/ql-vscode/src/run-queries-shared.ts

Lines changed: 35 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,8 @@ import * as messages from "./query-server/messages-shared";
22
import * as legacyMessages from "./query-server/legacy-messages";
33
import { DatabaseInfo, QueryMetadata } from "./common/interface-types";
44
import { join, parse, dirname, basename } from "path";
5-
import {
6-
ConfigurationTarget,
7-
Range,
8-
TextDocument,
9-
TextEditor,
10-
Uri,
11-
window,
12-
} from "vscode";
13-
import { isCanary, AUTOSAVE_SETTING } from "./config";
14-
import { UserCancellationException } from "./common/vscode/progress";
5+
import { Range, TextEditor, Uri, window, workspace } from "vscode";
6+
import { isCanary, VSCODE_SAVE_BEFORE_START_SETTING } from "./config";
157
import {
168
pathExists,
179
readFile,
@@ -491,55 +483,41 @@ async function getSelectedPosition(
491483
};
492484
}
493485

486+
type SaveBeforeStartMode =
487+
| "nonUntitledEditorsInActiveGroup"
488+
| "allEditorsInActiveGroup"
489+
| "none";
490+
494491
/**
495-
* Prompts the user to save `document` if it has unsaved changes.
496-
*
497-
* @param document The document to save.
498-
*
499-
* @returns true if we should save changes and false if we should continue without saving changes.
500-
* @throws UserCancellationException if we should abort whatever operation triggered this prompt
492+
* Saves dirty files before running queries, based on the user's settings.
501493
*/
502-
export async function promptUserToSaveChanges(
503-
document: TextDocument,
504-
): Promise<boolean> {
505-
if (document.isDirty) {
506-
if (AUTOSAVE_SETTING.getValue()) {
507-
return true;
508-
} else {
509-
const yesItem = { title: "Yes", isCloseAffordance: false };
510-
const alwaysItem = { title: "Always Save", isCloseAffordance: false };
511-
const noItem = {
512-
title: "No (run version on disk)",
513-
isCloseAffordance: false,
514-
};
515-
const cancelItem = { title: "Cancel", isCloseAffordance: true };
516-
const message = `Query file '${basename(
517-
document.uri.fsPath,
518-
)}' has unsaved changes. Save now?`;
519-
const chosenItem = await window.showInformationMessage(
520-
message,
521-
{ modal: true },
522-
yesItem,
523-
alwaysItem,
524-
noItem,
525-
cancelItem,
526-
);
527-
528-
if (chosenItem === alwaysItem) {
529-
await AUTOSAVE_SETTING.updateValue(true, ConfigurationTarget.Workspace);
530-
return true;
531-
}
532-
533-
if (chosenItem === yesItem) {
534-
return true;
535-
}
536-
537-
if (chosenItem === cancelItem) {
538-
throw new UserCancellationException("Query run cancelled.", true);
539-
}
540-
}
494+
export async function saveBeforeStart(): Promise<void> {
495+
const mode: SaveBeforeStartMode =
496+
(VSCODE_SAVE_BEFORE_START_SETTING.getValue<string>({
497+
languageId: "ql",
498+
}) as SaveBeforeStartMode) ?? "nonUntitledEditorsInActiveGroup";
499+
500+
// Despite the names of the modes, the VS Code implementation doesn't restrict itself to the
501+
// current tab group. It saves all dirty files in all groups. We'll do the same.
502+
switch (mode) {
503+
case "nonUntitledEditorsInActiveGroup":
504+
await workspace.saveAll(false);
505+
break;
506+
507+
case "allEditorsInActiveGroup":
508+
// The VS Code implementation of this mode only saves an untitled file if it is the document
509+
// in the active editor. That's too much work for us, so we'll just live with the inconsistency.
510+
await workspace.saveAll(true);
511+
break;
512+
513+
case "none":
514+
break;
515+
516+
default:
517+
// Unexpected value. Fall back to the default behavior.
518+
await workspace.saveAll(false);
519+
break;
541520
}
542-
return false;
543521
}
544522

545523
/**
@@ -566,7 +544,7 @@ async function convertToQlPath(filePath: string): Promise<string> {
566544
}
567545
}
568546
}
569-
throw new Error(`Can't convert path to form suitable for QL:${filePath}`);
547+
throw new Error(`Can't convert path to form suitable for QL: ${filePath}`);
570548
} else {
571549
return filePath;
572550
}

extensions/ql-vscode/test/vscode-tests/cli-integration/data/QuickEvalLib.qll renamed to extensions/ql-vscode/test/data/debugger/QuickEvalLib.qll

File renamed without changes.

extensions/ql-vscode/test/vscode-tests/cli-integration/data/QuickEvalQuery.ql renamed to extensions/ql-vscode/test/data/debugger/QuickEvalQuery.ql

File renamed without changes.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
lockVersion: 1.0.0
3+
dependencies:
4+
codeql/javascript-all:
5+
version: 0.6.3
6+
codeql/javascript-queries:
7+
version: 0.6.3
8+
codeql/regex:
9+
version: 0.0.14
10+
codeql/suite-helpers:
11+
version: 0.5.3
12+
codeql/tutorial:
13+
version: 0.0.11
14+
codeql/typos:
15+
version: 0.0.18
16+
codeql/util:
17+
version: 0.0.11
18+
codeql/yaml:
19+
version: 0.0.3
20+
compiled: false
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
name: integration-test-debugger-javascript
2+
version: 0.0.0
3+
dependencies:
4+
codeql/javascript-queries: "*"

extensions/ql-vscode/test/vscode-tests/cli-integration/data/simple-query.ql renamed to extensions/ql-vscode/test/data/debugger/simple-query.ql

File renamed without changes.

0 commit comments

Comments
 (0)