Add the compare-interface
This module will behave like the interface.ts module and handle interactions between the compare webview and the extension.
This commit is contained in:
@@ -314,6 +314,10 @@
|
||||
"command": "codeQLQueryHistory.setLabel",
|
||||
"title": "Set Label"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.compareWith",
|
||||
"title": "Compare with..."
|
||||
},
|
||||
{
|
||||
"command": "codeQL.restartQueryServer",
|
||||
"title": "CodeQL: Restart Query Server"
|
||||
@@ -401,6 +405,11 @@
|
||||
"group": "9_qlCommands",
|
||||
"when": "view == codeQLQueryHistory"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.compareWith",
|
||||
"group": "9_qlCommands",
|
||||
"when": "view == codeQLQueryHistory"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.showQueryLog",
|
||||
"group": "9_qlCommands",
|
||||
@@ -523,6 +532,10 @@
|
||||
{
|
||||
"command": "codeQLQueryHistory.setLabel",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.compareWith",
|
||||
"when": "false"
|
||||
}
|
||||
],
|
||||
"editor/context": [
|
||||
|
||||
85
extensions/ql-vscode/src/compare/compare-interface.ts
Normal file
85
extensions/ql-vscode/src/compare/compare-interface.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { DisposableObject } from "semmle-vscode-utils";
|
||||
import { WebviewPanel, ExtensionContext, window as Window, ViewColumn, Uri } from "vscode";
|
||||
import * as path from 'path';
|
||||
|
||||
import { tmpDir } from "../run-queries";
|
||||
import { CompletedQuery } from "../query-results";
|
||||
import { CompareViewMessage } from "../interface-types";
|
||||
import { Logger } from "../logging";
|
||||
import { CodeQLCliServer } from "../cli";
|
||||
import { DatabaseManager } from "../databases";
|
||||
import { getHtmlForWebview, WebviewReveal } from "../webview-utils";
|
||||
import { showAndLogErrorMessage } from "../helpers";
|
||||
|
||||
interface ComparePair {
|
||||
from: CompletedQuery;
|
||||
to: CompletedQuery;
|
||||
}
|
||||
|
||||
export class CompareInterfaceManager extends DisposableObject {
|
||||
private comparePair: ComparePair | undefined;
|
||||
private panel: WebviewPanel | undefined;
|
||||
|
||||
constructor(
|
||||
public ctx: ExtensionContext,
|
||||
public databaseManager: DatabaseManager,
|
||||
public cliServer: CodeQLCliServer,
|
||||
public logger: Logger
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
showResults(from: CompletedQuery, to: CompletedQuery, forceReveal = WebviewReveal.NotForced) {
|
||||
this.comparePair = { from, to };
|
||||
if (forceReveal === WebviewReveal.Forced) {
|
||||
this.getPanel().reveal(undefined, true);
|
||||
}
|
||||
}
|
||||
|
||||
getPanel(): WebviewPanel {
|
||||
if (this.panel == undefined) {
|
||||
const { ctx } = this;
|
||||
const panel = (this.panel = Window.createWebviewPanel(
|
||||
"compareView", // internal name
|
||||
"Compare CodeQL Query Results", // user-visible name
|
||||
{ viewColumn: ViewColumn.Beside, preserveFocus: true },
|
||||
{
|
||||
enableScripts: true,
|
||||
enableFindWidget: true,
|
||||
retainContextWhenHidden: true,
|
||||
localResourceRoots: [
|
||||
Uri.file(tmpDir.name),
|
||||
Uri.file(path.join(this.ctx.extensionPath, "out")),
|
||||
],
|
||||
}
|
||||
));
|
||||
this.panel.onDidDispose(
|
||||
() => this.panel = undefined,
|
||||
null,
|
||||
ctx.subscriptions
|
||||
);
|
||||
|
||||
const scriptPathOnDisk = Uri.file(
|
||||
ctx.asAbsolutePath("out/compareView.js")
|
||||
);
|
||||
|
||||
const stylesheetPathOnDisk = Uri.file(
|
||||
ctx.asAbsolutePath("out/compareView.css")
|
||||
);
|
||||
|
||||
panel.webview.html = getHtmlForWebview(panel.webview, scriptPathOnDisk, stylesheetPathOnDisk);
|
||||
panel.webview.onDidReceiveMessage(
|
||||
async (e) => this.handleMsgFromView(e),
|
||||
undefined,
|
||||
ctx.subscriptions
|
||||
);
|
||||
}
|
||||
return this.panel;
|
||||
}
|
||||
|
||||
private async handleMsgFromView(msg: CompareViewMessage): Promise<void> {
|
||||
/** TODO */
|
||||
showAndLogErrorMessage(JSON.stringify(msg));
|
||||
showAndLogErrorMessage(JSON.stringify(this.comparePair));
|
||||
}
|
||||
}
|
||||
@@ -170,3 +170,9 @@ interface ChangeInterpretedResultsSortMsg {
|
||||
*/
|
||||
sortState?: InterpretedResultsSortState;
|
||||
}
|
||||
|
||||
export interface CompareViewMessage {
|
||||
t: 'change-compare';
|
||||
newResultSetName: string;
|
||||
// TODO do we need to include the ids of the queries
|
||||
}
|
||||
|
||||
@@ -135,10 +135,15 @@ class HistoryTreeDataProvider implements vscode.TreeDataProvider<CompletedQuery>
|
||||
}
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
get allHistory(): CompletedQuery[] {
|
||||
return this.history;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this._onDidChangeTreeData.fire();
|
||||
this._onDidChangeTreeData.fire(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +157,6 @@ export class QueryHistoryManager {
|
||||
treeDataProvider: HistoryTreeDataProvider;
|
||||
ctx: ExtensionContext;
|
||||
treeView: vscode.TreeView<CompletedQuery>;
|
||||
selectedCallback: ((item: CompletedQuery) => void) | undefined;
|
||||
lastItemClick: { time: Date; item: CompletedQuery } | undefined;
|
||||
|
||||
async invokeCallbackOn(queryHistoryItem: CompletedQuery) {
|
||||
@@ -200,6 +204,22 @@ export class QueryHistoryManager {
|
||||
}
|
||||
}
|
||||
|
||||
async handleCompareWith(query: CompletedQuery) {
|
||||
const dbName = query.database.name;
|
||||
const comparableQueryLabels = this.treeDataProvider.allHistory
|
||||
.filter((otherQuery) => otherQuery !== query && otherQuery.didRunSuccessfully && otherQuery.database.name === dbName)
|
||||
.map(otherQuery => ({
|
||||
label: otherQuery.toString(),
|
||||
description: otherQuery.databaseName,
|
||||
detail: otherQuery.statusString,
|
||||
query: otherQuery
|
||||
}));
|
||||
const choice = await vscode.window.showQuickPick(comparableQueryLabels);
|
||||
if (choice) {
|
||||
this.doCompareCallback(query, choice.query);
|
||||
}
|
||||
}
|
||||
|
||||
async handleItemClicked(queryHistoryItem: CompletedQuery) {
|
||||
this.treeDataProvider.setCurrentItem(queryHistoryItem);
|
||||
|
||||
@@ -277,10 +297,10 @@ export class QueryHistoryManager {
|
||||
constructor(
|
||||
ctx: ExtensionContext,
|
||||
private queryHistoryConfigListener: QueryHistoryConfig,
|
||||
selectedCallback?: (item: CompletedQuery) => Promise<void>
|
||||
private selectedCallback: (item: CompletedQuery) => Promise<void>,
|
||||
private doCompareCallback: (from: CompletedQuery, to: CompletedQuery) => Promise<void>,
|
||||
) {
|
||||
this.ctx = ctx;
|
||||
this.selectedCallback = selectedCallback;
|
||||
const treeDataProvider = this.treeDataProvider = new HistoryTreeDataProvider(ctx);
|
||||
this.treeView = Window.createTreeView('codeQLQueryHistory', { treeDataProvider });
|
||||
// Lazily update the tree view selection due to limitations of TreeView API (see
|
||||
@@ -296,6 +316,7 @@ export class QueryHistoryManager {
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.openQuery', this.handleOpenQuery));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.removeHistoryItem', this.handleRemoveHistoryItem.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.setLabel', this.handleSetLabel.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.compareWith', this.handleCompareWith.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryLog', this.handleShowQueryLog.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryText', this.handleShowQueryText.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.viewSarif', this.handleViewSarif.bind(this)));
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { expect } from 'chai';
|
||||
import * as path from 'path';
|
||||
import * as tmp from 'tmp';
|
||||
import { window, ViewColumn, Uri } from 'vscode';
|
||||
import { fileUriToWebviewUri, webviewUriToFileUri } from '../../interface';
|
||||
import { expect } from "chai";
|
||||
import * as path from "path";
|
||||
import * as tmp from "tmp";
|
||||
import { window, ViewColumn, Uri } from "vscode";
|
||||
import { fileUriToWebviewUri, webviewUriToFileUri } from '../../webview-utils';
|
||||
|
||||
describe('webview uri conversion', function() {
|
||||
const fileSuffix = '.bqrs';
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
import * as crypto from "crypto";
|
||||
import { Uri, Location, Range, WebviewPanel, Webview, window as Window, workspace, ViewColumn, Selection, TextEditorRevealType, ThemeColor } from "vscode";
|
||||
import {
|
||||
Uri,
|
||||
Location,
|
||||
Range,
|
||||
WebviewPanel,
|
||||
Webview,
|
||||
workspace,
|
||||
window as Window,
|
||||
ViewColumn,
|
||||
Selection,
|
||||
TextEditorRevealType,
|
||||
ThemeColor,
|
||||
} from "vscode";
|
||||
import {
|
||||
FivePartLocation,
|
||||
LocationStyle,
|
||||
@@ -136,25 +148,6 @@ export function getHtmlForWebview(
|
||||
</html>`;
|
||||
}
|
||||
|
||||
|
||||
const findMatchBackground = new ThemeColor("editor.findMatchBackground");
|
||||
const findRangeHighlightBackground = new ThemeColor(
|
||||
"editor.findRangeHighlightBackground"
|
||||
);
|
||||
|
||||
export const shownLocationDecoration = Window.createTextEditorDecorationType(
|
||||
{
|
||||
backgroundColor: findMatchBackground,
|
||||
}
|
||||
);
|
||||
|
||||
export const shownLocationLineDecoration = Window.createTextEditorDecorationType(
|
||||
{
|
||||
backgroundColor: findRangeHighlightBackground,
|
||||
isWholeLine: true,
|
||||
}
|
||||
);
|
||||
|
||||
export async function showLocation(
|
||||
loc: ResolvableLocationValue,
|
||||
databaseItem: DatabaseItem
|
||||
@@ -188,3 +181,19 @@ export async function showLocation(
|
||||
editor.setDecorations(shownLocationLineDecoration, [range]);
|
||||
}
|
||||
}
|
||||
|
||||
const findMatchBackground = new ThemeColor("editor.findMatchBackground");
|
||||
const findRangeHighlightBackground = new ThemeColor(
|
||||
"editor.findRangeHighlightBackground"
|
||||
);
|
||||
|
||||
export const shownLocationDecoration = Window.createTextEditorDecorationType({
|
||||
backgroundColor: findMatchBackground,
|
||||
});
|
||||
|
||||
export const shownLocationLineDecoration = Window.createTextEditorDecorationType(
|
||||
{
|
||||
backgroundColor: findRangeHighlightBackground,
|
||||
isWholeLine: true,
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user