Skip to content

Commit 9948b93

Browse files
Merge branch 'main' into robertbrignull/extension_commands
2 parents 5d6a2e6 + 7059f46 commit 9948b93

File tree

6 files changed

+124
-125
lines changed

6 files changed

+124
-125
lines changed

extensions/ql-vscode/src/common/commands.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { DbTreeViewItem } from "../databases/ui/db-tree-view-item";
55
import type { DatabaseItem } from "../local-databases";
66
import type { QueryHistoryInfo } from "../query-history/query-history-info";
77
import type { RepositoriesFilterSortStateWithIds } from "../pure/variant-analysis-filter-sort";
8+
import type { TestTreeNode } from "../test-tree-node";
89
import type {
910
VariantAnalysis,
1011
VariantAnalysisScannedRepository,
@@ -45,6 +46,18 @@ export type BaseCommands = {
4546
"codeQL.restartQueryServer": () => Promise<void>;
4647
};
4748

49+
// Commands used when working with queries in the editor
50+
export type QueryEditorCommands = {
51+
"codeQL.openReferencedFile": (selectedQuery: Uri) => Promise<void>;
52+
"codeQL.openReferencedFileContextEditor": (
53+
selectedQuery: Uri,
54+
) => Promise<void>;
55+
"codeQL.openReferencedFileContextExplorer": (
56+
selectedQuery: Uri,
57+
) => Promise<void>;
58+
"codeQL.previewQueryHelp": (selectedQuery: Uri) => Promise<void>;
59+
};
60+
4861
// Commands used for running local queries
4962
export type LocalQueryCommands = {
5063
"codeQL.runQuery": (uri?: Uri) => Promise<void>;
@@ -211,6 +224,11 @@ export type SummaryLanguageSupportCommands = {
211224
"codeQL.gotoQL": () => Promise<void>;
212225
};
213226

227+
export type TestUICommands = {
228+
"codeQLTests.showOutputDifferences": (node: TestTreeNode) => Promise<void>;
229+
"codeQLTests.acceptOutput": (node: TestTreeNode) => Promise<void>;
230+
};
231+
214232
export type MockGitHubApiServerCommands = {
215233
"codeQL.mockGitHubApiServer.startRecording": () => Promise<void>;
216234
"codeQL.mockGitHubApiServer.saveScenario": () => Promise<void>;
@@ -221,6 +239,8 @@ export type MockGitHubApiServerCommands = {
221239

222240
// All commands where the implementation is provided by this extension.
223241
export type AllExtensionCommands = BaseCommands &
242+
QueryEditorCommands &
243+
ResultsViewCommands &
224244
QueryHistoryCommands &
225245
LocalDatabasesCommands &
226246
VariantAnalysisCommands &
@@ -230,6 +250,7 @@ export type AllExtensionCommands = BaseCommands &
230250
PackagingCommands &
231251
EvalLogViewerCommands &
232252
SummaryLanguageSupportCommands &
253+
Partial<TestUICommands> &
233254
MockGitHubApiServerCommands;
234255

235256
export type AllCommands = AllExtensionCommands & BuiltInVsCodeCommands;

extensions/ql-vscode/src/extension.ts

Lines changed: 12 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@ import {
1010
Uri,
1111
version as vscodeVersion,
1212
window as Window,
13-
window,
1413
workspace,
1514
} from "vscode";
1615
import { LanguageClient } from "vscode-languageclient/node";
1716
import { arch, platform } from "os";
1817
import { ensureDir } from "fs-extra";
19-
import { basename, join } from "path";
18+
import { join } from "path";
2019
import { dirSync } from "tmp-promise";
2120
import { testExplorerExtensionId, TestHub } from "vscode-test-adapter-api";
2221
import { lt, parse } from "semver";
@@ -110,17 +109,18 @@ import { ExtensionApp } from "./common/vscode/vscode-app";
110109
import { DbModule } from "./databases/db-module";
111110
import { redactableError } from "./pure/errors";
112111
import { QueryHistoryDirs } from "./query-history/query-history-dirs";
113-
import { DirResult } from "tmp";
114112
import {
115113
AllExtensionCommands,
116114
BaseCommands,
117115
QueryServerCommands,
116+
TestUICommands,
118117
} from "./common/commands";
119118
import {
120119
getLocalQueryCommands,
121120
showResultsForCompletedQuery,
122121
} from "./local-queries";
123122
import { getAstCfgCommands } from "./ast-cfg-commands";
123+
import { getQueryEditorCommands } from "./query-editor";
124124

125125
/**
126126
* extension.ts
@@ -805,6 +805,7 @@ async function activateWithInstalledDistribution(
805805
const testExplorerExtension = extensions.getExtension<TestHub>(
806806
testExplorerExtensionId,
807807
);
808+
let testUiCommands: Partial<TestUICommands> = {};
808809
if (testExplorerExtension) {
809810
const testHub = testExplorerExtension.exports;
810811
const testAdapterFactory = new QLTestAdapterFactory(
@@ -816,6 +817,8 @@ async function activateWithInstalledDistribution(
816817

817818
const testUIService = new TestUIService(testHub);
818819
ctx.subscriptions.push(testUIService);
820+
821+
testUiCommands = testUIService.getCommands();
819822
}
820823

821824
const astViewer = new AstViewer();
@@ -839,6 +842,11 @@ async function activateWithInstalledDistribution(
839842

840843
const allCommands: AllExtensionCommands = {
841844
...getCommands(cliServer, qs),
845+
...getQueryEditorCommands({
846+
queryRunner: qs,
847+
cliServer,
848+
qhelpTmpDir: qhelpTmpDir.name,
849+
}),
842850
...localQueryResultsView.getCommands(),
843851
...qhm.getCommands(),
844852
...variantAnalysisManager.getCommands(),
@@ -860,6 +868,7 @@ async function activateWithInstalledDistribution(
860868
}),
861869
...evalLogViewer.getCommands(),
862870
...summaryLanguageSupport.getCommands(),
871+
...testUiCommands,
863872
...mockServer.getCommands(),
864873
};
865874

@@ -887,38 +896,6 @@ async function activateWithInstalledDistribution(
887896
);
888897
}
889898

890-
ctx.subscriptions.push(
891-
commandRunner("codeQL.openReferencedFile", async (selectedQuery: Uri) => {
892-
await openReferencedFile(qs, cliServer, selectedQuery);
893-
}),
894-
);
895-
896-
// Since we are tracking extension usage through commands, this command mirrors the "codeQL.openReferencedFile" command
897-
ctx.subscriptions.push(
898-
commandRunner(
899-
"codeQL.openReferencedFileContextEditor",
900-
async (selectedQuery: Uri) => {
901-
await openReferencedFile(qs, cliServer, selectedQuery);
902-
},
903-
),
904-
);
905-
906-
// Since we are tracking extension usage through commands, this command mirrors the "codeQL.openReferencedFile" command
907-
ctx.subscriptions.push(
908-
commandRunner(
909-
"codeQL.openReferencedFileContextExplorer",
910-
async (selectedQuery: Uri) => {
911-
await openReferencedFile(qs, cliServer, selectedQuery);
912-
},
913-
),
914-
);
915-
916-
ctx.subscriptions.push(
917-
commandRunner("codeQL.previewQueryHelp", async (selectedQuery: Uri) => {
918-
await previewQueryHelp(app, cliServer, qhelpTmpDir, selectedQuery);
919-
}),
920-
);
921-
922899
ctx.subscriptions.push(
923900
commandRunner("codeQL.copyVersion", async () => {
924901
const text = `CodeQL extension version: ${
@@ -1024,52 +1001,6 @@ async function showResultsForComparison(
10241001
}
10251002
}
10261003

1027-
async function previewQueryHelp(
1028-
app: ExtensionApp,
1029-
cliServer: CodeQLCliServer,
1030-
qhelpTmpDir: DirResult,
1031-
selectedQuery: Uri,
1032-
): Promise<void> {
1033-
// selectedQuery is unpopulated when executing through the command palette
1034-
const pathToQhelp = selectedQuery
1035-
? selectedQuery.fsPath
1036-
: window.activeTextEditor?.document.uri.fsPath;
1037-
if (pathToQhelp) {
1038-
// Create temporary directory
1039-
const relativePathToMd = `${basename(pathToQhelp, ".qhelp")}.md`;
1040-
const absolutePathToMd = join(qhelpTmpDir.name, relativePathToMd);
1041-
const uri = Uri.file(absolutePathToMd);
1042-
try {
1043-
await cliServer.generateQueryHelp(pathToQhelp, absolutePathToMd);
1044-
await app.commands.execute("markdown.showPreviewToSide", uri);
1045-
} catch (e) {
1046-
const errorMessage = getErrorMessage(e).includes(
1047-
"Generating qhelp in markdown",
1048-
)
1049-
? redactableError`Could not generate markdown from ${pathToQhelp}: Bad formatting in .qhelp file.`
1050-
: redactableError`Could not open a preview of the generated file (${absolutePathToMd}).`;
1051-
void showAndLogExceptionWithTelemetry(errorMessage, {
1052-
fullMessage: `${errorMessage}\n${getErrorMessage(e)}`,
1053-
});
1054-
}
1055-
}
1056-
}
1057-
1058-
async function openReferencedFile(
1059-
qs: QueryRunner,
1060-
cliServer: CodeQLCliServer,
1061-
selectedQuery: Uri,
1062-
): Promise<void> {
1063-
// If no file is selected, the path of the file in the editor is selected
1064-
const path =
1065-
selectedQuery?.fsPath || window.activeTextEditor?.document.uri.fsPath;
1066-
if (qs !== undefined && path) {
1067-
const resolved = await cliServer.resolveQlref(path);
1068-
const uri = Uri.file(resolved.resolvedPath);
1069-
await window.showTextDocument(uri, { preview: false });
1070-
}
1071-
}
1072-
10731004
function addUnhandledRejectionListener() {
10741005
const handler = (error: unknown) => {
10751006
// This listener will be triggered for errors from other extensions as

extensions/ql-vscode/src/packages/commands/CommandManager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class CommandManager<
3030
constructor(
3131
private readonly commandRegister: <T extends CommandName>(
3232
commandName: T,
33-
fn: Commands[T],
33+
fn: NonNullable<Commands[T]>,
3434
) => Disposable,
3535
private readonly commandExecute: <T extends CommandName>(
3636
commandName: T,
@@ -43,7 +43,7 @@ export class CommandManager<
4343
*/
4444
register<T extends CommandName>(
4545
commandName: T,
46-
definition: Commands[T],
46+
definition: NonNullable<Commands[T]>,
4747
): void {
4848
this.commands.push(this.commandRegister(commandName, definition));
4949
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { commands, Uri, window } from "vscode";
2+
import { CodeQLCliServer } from "./cli";
3+
import { QueryRunner } from "./queryRunner";
4+
import { basename, join } from "path";
5+
import { getErrorMessage } from "./pure/helpers-pure";
6+
import { redactableError } from "./pure/errors";
7+
import { showAndLogExceptionWithTelemetry } from "./helpers";
8+
import { QueryEditorCommands } from "./common/commands";
9+
10+
type QueryEditorOptions = {
11+
queryRunner: QueryRunner;
12+
cliServer: CodeQLCliServer;
13+
14+
qhelpTmpDir: string;
15+
};
16+
17+
export function getQueryEditorCommands({
18+
queryRunner,
19+
cliServer,
20+
qhelpTmpDir,
21+
}: QueryEditorOptions): QueryEditorCommands {
22+
const openReferencedFileCommand = async (selectedQuery: Uri) =>
23+
await openReferencedFile(queryRunner, cliServer, selectedQuery);
24+
25+
return {
26+
"codeQL.openReferencedFile": openReferencedFileCommand,
27+
// Since we are tracking extension usage through commands, this command mirrors the "codeQL.openReferencedFile" command
28+
"codeQL.openReferencedFileContextEditor": openReferencedFileCommand,
29+
// Since we are tracking extension usage through commands, this command mirrors the "codeQL.openReferencedFile" command
30+
"codeQL.openReferencedFileContextExplorer": openReferencedFileCommand,
31+
"codeQL.previewQueryHelp": async (selectedQuery: Uri) =>
32+
await previewQueryHelp(cliServer, qhelpTmpDir, selectedQuery),
33+
};
34+
}
35+
36+
async function previewQueryHelp(
37+
cliServer: CodeQLCliServer,
38+
qhelpTmpDir: string,
39+
selectedQuery: Uri,
40+
): Promise<void> {
41+
// selectedQuery is unpopulated when executing through the command palette
42+
const pathToQhelp = selectedQuery
43+
? selectedQuery.fsPath
44+
: window.activeTextEditor?.document.uri.fsPath;
45+
if (pathToQhelp) {
46+
// Create temporary directory
47+
const relativePathToMd = `${basename(pathToQhelp, ".qhelp")}.md`;
48+
const absolutePathToMd = join(qhelpTmpDir, relativePathToMd);
49+
const uri = Uri.file(absolutePathToMd);
50+
try {
51+
await cliServer.generateQueryHelp(pathToQhelp, absolutePathToMd);
52+
await commands.executeCommand("markdown.showPreviewToSide", uri);
53+
} catch (e) {
54+
const errorMessage = getErrorMessage(e).includes(
55+
"Generating qhelp in markdown",
56+
)
57+
? redactableError`Could not generate markdown from ${pathToQhelp}: Bad formatting in .qhelp file.`
58+
: redactableError`Could not open a preview of the generated file (${absolutePathToMd}).`;
59+
void showAndLogExceptionWithTelemetry(errorMessage, {
60+
fullMessage: `${errorMessage}\n${getErrorMessage(e)}`,
61+
});
62+
}
63+
}
64+
}
65+
66+
async function openReferencedFile(
67+
qs: QueryRunner,
68+
cliServer: CodeQLCliServer,
69+
selectedQuery: Uri,
70+
): Promise<void> {
71+
// If no file is selected, the path of the file in the editor is selected
72+
const path =
73+
selectedQuery?.fsPath || window.activeTextEditor?.document.uri.fsPath;
74+
if (qs !== undefined && path) {
75+
const resolved = await cliServer.resolveQlref(path);
76+
const uri = Uri.file(resolved.resolvedPath);
77+
await window.showTextDocument(uri, { preview: false });
78+
}
79+
}

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ import {
1414
import { showAndLogWarningMessage } from "./helpers";
1515
import { TestTreeNode } from "./test-tree-node";
1616
import { DisposableObject } from "./pure/disposable-object";
17-
import { UIService } from "./vscode-utils/ui-service";
1817
import { QLTestAdapter, getExpectedFile, getActualFile } from "./test-adapter";
19-
import { extLogger } from "./common";
18+
import { TestUICommands } from "./common/commands";
2019

2120
type VSCodeTestEvent =
2221
| TestRunStartedEvent
@@ -42,22 +41,23 @@ class QLTestListener extends DisposableObject {
4241
/**
4342
* Service that implements all UI and commands for QL tests.
4443
*/
45-
export class TestUIService extends UIService implements TestController {
44+
export class TestUIService extends DisposableObject implements TestController {
4645
private readonly listeners: Map<TestAdapter, QLTestListener> = new Map();
4746

4847
constructor(private readonly testHub: TestHub) {
4948
super();
5049

51-
void extLogger.log("Registering CodeQL test panel commands.");
52-
this.registerCommand(
53-
"codeQLTests.showOutputDifferences",
54-
this.showOutputDifferences,
55-
);
56-
this.registerCommand("codeQLTests.acceptOutput", this.acceptOutput);
57-
5850
testHub.registerTestController(this);
5951
}
6052

53+
public getCommands(): TestUICommands {
54+
return {
55+
"codeQLTests.showOutputDifferences":
56+
this.showOutputDifferences.bind(this),
57+
"codeQLTests.acceptOutput": this.acceptOutput.bind(this),
58+
};
59+
}
60+
6161
public dispose(): void {
6262
this.testHub.unregisterTestController(this);
6363

extensions/ql-vscode/src/vscode-utils/ui-service.ts

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)