Skip to content

Commit e9fbd6d

Browse files
committed
Add the compare-interface
This module will behave like the interface.ts module and handle interactions between the compare webview and the extension.
1 parent 2ab4c1a commit e9fbd6d

File tree

6 files changed

+163
-29
lines changed

6 files changed

+163
-29
lines changed

extensions/ql-vscode/package.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,10 @@
314314
"command": "codeQLQueryHistory.setLabel",
315315
"title": "Set Label"
316316
},
317+
{
318+
"command": "codeQLQueryHistory.compareWith",
319+
"title": "Compare with..."
320+
},
317321
{
318322
"command": "codeQL.restartQueryServer",
319323
"title": "CodeQL: Restart Query Server"
@@ -401,6 +405,11 @@
401405
"group": "9_qlCommands",
402406
"when": "view == codeQLQueryHistory"
403407
},
408+
{
409+
"command": "codeQLQueryHistory.compareWith",
410+
"group": "9_qlCommands",
411+
"when": "view == codeQLQueryHistory"
412+
},
404413
{
405414
"command": "codeQLQueryHistory.showQueryLog",
406415
"group": "9_qlCommands",
@@ -523,6 +532,10 @@
523532
{
524533
"command": "codeQLQueryHistory.setLabel",
525534
"when": "false"
535+
},
536+
{
537+
"command": "codeQLQueryHistory.compareWith",
538+
"when": "false"
526539
}
527540
],
528541
"editor/context": [
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { DisposableObject } from "semmle-vscode-utils";
2+
import { WebviewPanel, ExtensionContext, window as Window, ViewColumn, Uri } from "vscode";
3+
import * as path from 'path';
4+
5+
import { tmpDir } from "../run-queries";
6+
import { CompletedQuery } from "../query-results";
7+
import { CompareViewMessage } from "../interface-types";
8+
import { Logger } from "../logging";
9+
import { CodeQLCliServer } from "../cli";
10+
import { DatabaseManager } from "../databases";
11+
import { getHtmlForWebview, WebviewReveal } from "../webview-utils";
12+
import { showAndLogErrorMessage } from "../helpers";
13+
14+
interface ComparePair {
15+
from: CompletedQuery;
16+
to: CompletedQuery;
17+
}
18+
19+
export class CompareInterfaceManager extends DisposableObject {
20+
private comparePair: ComparePair | undefined;
21+
private panel: WebviewPanel | undefined;
22+
23+
constructor(
24+
public ctx: ExtensionContext,
25+
public databaseManager: DatabaseManager,
26+
public cliServer: CodeQLCliServer,
27+
public logger: Logger
28+
) {
29+
super();
30+
}
31+
32+
showResults(from: CompletedQuery, to: CompletedQuery, forceReveal = WebviewReveal.NotForced) {
33+
this.comparePair = { from, to };
34+
if (forceReveal === WebviewReveal.Forced) {
35+
this.getPanel().reveal(undefined, true);
36+
}
37+
}
38+
39+
getPanel(): WebviewPanel {
40+
if (this.panel == undefined) {
41+
const { ctx } = this;
42+
const panel = (this.panel = Window.createWebviewPanel(
43+
"compareView", // internal name
44+
"Compare CodeQL Query Results", // user-visible name
45+
{ viewColumn: ViewColumn.Beside, preserveFocus: true },
46+
{
47+
enableScripts: true,
48+
enableFindWidget: true,
49+
retainContextWhenHidden: true,
50+
localResourceRoots: [
51+
Uri.file(tmpDir.name),
52+
Uri.file(path.join(this.ctx.extensionPath, "out")),
53+
],
54+
}
55+
));
56+
this.panel.onDidDispose(
57+
() => this.panel = undefined,
58+
null,
59+
ctx.subscriptions
60+
);
61+
62+
const scriptPathOnDisk = Uri.file(
63+
ctx.asAbsolutePath("out/compareView.js")
64+
);
65+
66+
const stylesheetPathOnDisk = Uri.file(
67+
ctx.asAbsolutePath("out/compareView.css")
68+
);
69+
70+
panel.webview.html = getHtmlForWebview(panel.webview, scriptPathOnDisk, stylesheetPathOnDisk);
71+
panel.webview.onDidReceiveMessage(
72+
async (e) => this.handleMsgFromView(e),
73+
undefined,
74+
ctx.subscriptions
75+
);
76+
}
77+
return this.panel;
78+
}
79+
80+
private async handleMsgFromView(msg: CompareViewMessage): Promise<void> {
81+
/** TODO */
82+
showAndLogErrorMessage(JSON.stringify(msg));
83+
showAndLogErrorMessage(JSON.stringify(this.comparePair));
84+
}
85+
}

extensions/ql-vscode/src/interface-types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,9 @@ interface ChangeInterpretedResultsSortMsg {
170170
*/
171171
sortState?: InterpretedResultsSortState;
172172
}
173+
174+
export interface CompareViewMessage {
175+
t: 'change-compare';
176+
newResultSetName: string;
177+
// TODO do we need to include the ids of the queries
178+
}

extensions/ql-vscode/src/query-history.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,15 @@ class HistoryTreeDataProvider implements vscode.TreeDataProvider<CompletedQuery>
135135
}
136136
this.refresh();
137137
}
138+
139+
}
140+
141+
get allHistory(): CompletedQuery[] {
142+
return this.history;
138143
}
139144

140145
refresh() {
141-
this._onDidChangeTreeData.fire();
146+
this._onDidChangeTreeData.fire(undefined);
142147
}
143148
}
144149

@@ -152,7 +157,6 @@ export class QueryHistoryManager {
152157
treeDataProvider: HistoryTreeDataProvider;
153158
ctx: ExtensionContext;
154159
treeView: vscode.TreeView<CompletedQuery>;
155-
selectedCallback: ((item: CompletedQuery) => void) | undefined;
156160
lastItemClick: { time: Date; item: CompletedQuery } | undefined;
157161

158162
async invokeCallbackOn(queryHistoryItem: CompletedQuery) {
@@ -200,6 +204,22 @@ export class QueryHistoryManager {
200204
}
201205
}
202206

207+
async handleCompareWith(query: CompletedQuery) {
208+
const dbName = query.database.name;
209+
const comparableQueryLabels = this.treeDataProvider.allHistory
210+
.filter((otherQuery) => otherQuery !== query && otherQuery.didRunSuccessfully && otherQuery.database.name === dbName)
211+
.map(otherQuery => ({
212+
label: otherQuery.toString(),
213+
description: otherQuery.databaseName,
214+
detail: otherQuery.statusString,
215+
query: otherQuery
216+
}));
217+
const choice = await vscode.window.showQuickPick(comparableQueryLabels);
218+
if (choice) {
219+
this.doCompareCallback(query, choice.query);
220+
}
221+
}
222+
203223
async handleItemClicked(queryHistoryItem: CompletedQuery) {
204224
this.treeDataProvider.setCurrentItem(queryHistoryItem);
205225

@@ -277,10 +297,10 @@ export class QueryHistoryManager {
277297
constructor(
278298
ctx: ExtensionContext,
279299
private queryHistoryConfigListener: QueryHistoryConfig,
280-
selectedCallback?: (item: CompletedQuery) => Promise<void>
300+
private selectedCallback: (item: CompletedQuery) => Promise<void>,
301+
private doCompareCallback: (from: CompletedQuery, to: CompletedQuery) => Promise<void>,
281302
) {
282303
this.ctx = ctx;
283-
this.selectedCallback = selectedCallback;
284304
const treeDataProvider = this.treeDataProvider = new HistoryTreeDataProvider(ctx);
285305
this.treeView = Window.createTreeView('codeQLQueryHistory', { treeDataProvider });
286306
// Lazily update the tree view selection due to limitations of TreeView API (see
@@ -296,6 +316,7 @@ export class QueryHistoryManager {
296316
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.openQuery', this.handleOpenQuery));
297317
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.removeHistoryItem', this.handleRemoveHistoryItem.bind(this)));
298318
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.setLabel', this.handleSetLabel.bind(this)));
319+
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.compareWith', this.handleCompareWith.bind(this)));
299320
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryLog', this.handleShowQueryLog.bind(this)));
300321
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryText', this.handleShowQueryText.bind(this)));
301322
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.viewSarif', this.handleViewSarif.bind(this)));

extensions/ql-vscode/src/vscode-tests/no-workspace/webview-uri.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { expect } from 'chai';
2-
import * as path from 'path';
3-
import * as tmp from 'tmp';
4-
import { window, ViewColumn, Uri } from 'vscode';
5-
import { fileUriToWebviewUri, webviewUriToFileUri } from '../../interface';
1+
import { expect } from "chai";
2+
import * as path from "path";
3+
import * as tmp from "tmp";
4+
import { window, ViewColumn, Uri } from "vscode";
5+
import { fileUriToWebviewUri, webviewUriToFileUri } from '../../webview-utils';
66

77
describe('webview uri conversion', function() {
88
const fileSuffix = '.bqrs';

extensions/ql-vscode/src/webview-utils.ts

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
import * as crypto from "crypto";
2-
import { Uri, Location, Range, WebviewPanel, Webview, window as Window, workspace, ViewColumn, Selection, TextEditorRevealType, ThemeColor } from "vscode";
2+
import {
3+
Uri,
4+
Location,
5+
Range,
6+
WebviewPanel,
7+
Webview,
8+
workspace,
9+
window as Window,
10+
ViewColumn,
11+
Selection,
12+
TextEditorRevealType,
13+
ThemeColor,
14+
} from "vscode";
315
import {
416
FivePartLocation,
517
LocationStyle,
@@ -136,25 +148,6 @@ export function getHtmlForWebview(
136148
</html>`;
137149
}
138150

139-
140-
const findMatchBackground = new ThemeColor("editor.findMatchBackground");
141-
const findRangeHighlightBackground = new ThemeColor(
142-
"editor.findRangeHighlightBackground"
143-
);
144-
145-
export const shownLocationDecoration = Window.createTextEditorDecorationType(
146-
{
147-
backgroundColor: findMatchBackground,
148-
}
149-
);
150-
151-
export const shownLocationLineDecoration = Window.createTextEditorDecorationType(
152-
{
153-
backgroundColor: findRangeHighlightBackground,
154-
isWholeLine: true,
155-
}
156-
);
157-
158151
export async function showLocation(
159152
loc: ResolvableLocationValue,
160153
databaseItem: DatabaseItem
@@ -188,3 +181,19 @@ export async function showLocation(
188181
editor.setDecorations(shownLocationLineDecoration, [range]);
189182
}
190183
}
184+
185+
const findMatchBackground = new ThemeColor("editor.findMatchBackground");
186+
const findRangeHighlightBackground = new ThemeColor(
187+
"editor.findRangeHighlightBackground"
188+
);
189+
190+
export const shownLocationDecoration = Window.createTextEditorDecorationType({
191+
backgroundColor: findMatchBackground,
192+
});
193+
194+
export const shownLocationLineDecoration = Window.createTextEditorDecorationType(
195+
{
196+
backgroundColor: findRangeHighlightBackground,
197+
isWholeLine: true,
198+
}
199+
);

0 commit comments

Comments
 (0)