Merge pull request #2287 from github/robertbrignull/selection
Introduce helpers for commands that operate on selections
This commit is contained in:
51
extensions/ql-vscode/src/common/selection-commands.ts
Normal file
51
extensions/ql-vscode/src/common/selection-commands.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { showAndLogErrorMessage } from "../helpers";
|
||||
import {
|
||||
ExplorerSelectionCommandFunction,
|
||||
TreeViewContextMultiSelectionCommandFunction,
|
||||
TreeViewContextSingleSelectionCommandFunction,
|
||||
} from "./commands";
|
||||
|
||||
// A hack to match types that are not an array, which is useful to help avoid
|
||||
// misusing createSingleSelectionCommand, e.g. where T accidentally gets instantiated
|
||||
// as DatabaseItem[] instead of DatabaseItem.
|
||||
type NotArray = object & { length?: never };
|
||||
|
||||
// A way to get the type system to help assert that one type is a supertype of another.
|
||||
type CreateSupertypeOf<Super, Sub extends Super> = Sub;
|
||||
|
||||
// This asserts that SelectionCommand is assignable to all of the different types of
|
||||
// SelectionCommand defined in commands.ts. The intention is the output from the helpers
|
||||
// in this file can be used with any of the select command types and can handle any of
|
||||
// the inputs.
|
||||
type SelectionCommand<T extends NotArray> = CreateSupertypeOf<
|
||||
TreeViewContextMultiSelectionCommandFunction<T> &
|
||||
TreeViewContextSingleSelectionCommandFunction<T> &
|
||||
ExplorerSelectionCommandFunction<T>,
|
||||
(singleItem: T, multiSelect?: T[] | undefined) => Promise<void>
|
||||
>;
|
||||
|
||||
export function createSingleSelectionCommand<T extends NotArray>(
|
||||
f: (argument: T) => Promise<void>,
|
||||
itemName: string,
|
||||
): SelectionCommand<T> {
|
||||
return async (singleItem, multiSelect) => {
|
||||
if (multiSelect === undefined || multiSelect.length === 1) {
|
||||
return f(singleItem);
|
||||
} else {
|
||||
void showAndLogErrorMessage(`Please select a single ${itemName}.`);
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function createMultiSelectionCommand<T extends NotArray>(
|
||||
f: (argument: T[]) => Promise<void>,
|
||||
): SelectionCommand<T> {
|
||||
return async (singleItem, multiSelect) => {
|
||||
if (multiSelect !== undefined && multiSelect.length > 0) {
|
||||
return f(multiSelect);
|
||||
} else {
|
||||
return f([singleItem]);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -46,6 +46,10 @@ import { isCanary } from "../config";
|
||||
import { App } from "../common/app";
|
||||
import { redactableError } from "../pure/errors";
|
||||
import { LocalDatabasesCommands } from "../common/commands";
|
||||
import {
|
||||
createMultiSelectionCommand,
|
||||
createSingleSelectionCommand,
|
||||
} from "../common/selection-commands";
|
||||
|
||||
enum SortOrder {
|
||||
NameAsc = "NameAsc",
|
||||
@@ -240,11 +244,22 @@ export class DatabaseUI extends DisposableObject {
|
||||
this.handleMakeCurrentDatabase.bind(this),
|
||||
"codeQLDatabases.sortByName": this.handleSortByName.bind(this),
|
||||
"codeQLDatabases.sortByDateAdded": this.handleSortByDateAdded.bind(this),
|
||||
"codeQLDatabases.removeDatabase": this.handleRemoveDatabase.bind(this),
|
||||
"codeQLDatabases.upgradeDatabase": this.handleUpgradeDatabase.bind(this),
|
||||
"codeQLDatabases.renameDatabase": this.handleRenameDatabase.bind(this),
|
||||
"codeQLDatabases.openDatabaseFolder": this.handleOpenFolder.bind(this),
|
||||
"codeQLDatabases.addDatabaseSource": this.handleAddSource.bind(this),
|
||||
"codeQLDatabases.removeDatabase": createMultiSelectionCommand(
|
||||
this.handleRemoveDatabase.bind(this),
|
||||
),
|
||||
"codeQLDatabases.upgradeDatabase": createMultiSelectionCommand(
|
||||
this.handleUpgradeDatabase.bind(this),
|
||||
),
|
||||
"codeQLDatabases.renameDatabase": createSingleSelectionCommand(
|
||||
this.handleRenameDatabase.bind(this),
|
||||
"database",
|
||||
),
|
||||
"codeQLDatabases.openDatabaseFolder": createMultiSelectionCommand(
|
||||
this.handleOpenFolder.bind(this),
|
||||
),
|
||||
"codeQLDatabases.addDatabaseSource": createMultiSelectionCommand(
|
||||
this.handleAddSource.bind(this),
|
||||
),
|
||||
"codeQLDatabases.removeOrphanedDatabases":
|
||||
this.handleRemoveOrphanedDatabases.bind(this),
|
||||
};
|
||||
@@ -515,12 +530,11 @@ export class DatabaseUI extends DisposableObject {
|
||||
private async handleUpgradeCurrentDatabase(): Promise<void> {
|
||||
return withProgress(
|
||||
async (progress, token) => {
|
||||
await this.handleUpgradeDatabaseInternal(
|
||||
progress,
|
||||
token,
|
||||
this.databaseManager.currentDatabaseItem,
|
||||
[],
|
||||
);
|
||||
if (this.databaseManager.currentDatabaseItem !== undefined) {
|
||||
await this.handleUpgradeDatabasesInternal(progress, token, [
|
||||
this.databaseManager.currentDatabaseItem,
|
||||
]);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Upgrading current database",
|
||||
@@ -530,16 +544,14 @@ export class DatabaseUI extends DisposableObject {
|
||||
}
|
||||
|
||||
private async handleUpgradeDatabase(
|
||||
databaseItem: DatabaseItem | undefined,
|
||||
multiSelect: DatabaseItem[] | undefined,
|
||||
databaseItems: DatabaseItem[],
|
||||
): Promise<void> {
|
||||
return withProgress(
|
||||
async (progress, token) => {
|
||||
return await this.handleUpgradeDatabaseInternal(
|
||||
return await this.handleUpgradeDatabasesInternal(
|
||||
progress,
|
||||
token,
|
||||
databaseItem,
|
||||
multiSelect,
|
||||
databaseItems,
|
||||
);
|
||||
},
|
||||
{
|
||||
@@ -549,46 +561,37 @@ export class DatabaseUI extends DisposableObject {
|
||||
);
|
||||
}
|
||||
|
||||
private async handleUpgradeDatabaseInternal(
|
||||
private async handleUpgradeDatabasesInternal(
|
||||
progress: ProgressCallback,
|
||||
token: CancellationToken,
|
||||
databaseItem: DatabaseItem | undefined,
|
||||
multiSelect: DatabaseItem[] | undefined,
|
||||
databaseItems: DatabaseItem[],
|
||||
): Promise<void> {
|
||||
if (multiSelect?.length) {
|
||||
await Promise.all(
|
||||
multiSelect.map((dbItem) =>
|
||||
this.handleUpgradeDatabaseInternal(progress, token, dbItem, []),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (this.queryServer === undefined) {
|
||||
throw new Error(
|
||||
"Received request to upgrade database, but there is no running query server.",
|
||||
);
|
||||
}
|
||||
if (databaseItem === undefined) {
|
||||
throw new Error(
|
||||
"Received request to upgrade database, but no database was provided.",
|
||||
);
|
||||
}
|
||||
if (databaseItem.contents === undefined) {
|
||||
throw new Error(
|
||||
"Received request to upgrade database, but database contents could not be found.",
|
||||
);
|
||||
}
|
||||
if (databaseItem.contents.dbSchemeUri === undefined) {
|
||||
throw new Error(
|
||||
"Received request to upgrade database, but database has no schema.",
|
||||
);
|
||||
}
|
||||
await Promise.all(
|
||||
databaseItems.map(async (databaseItem) => {
|
||||
if (this.queryServer === undefined) {
|
||||
throw new Error(
|
||||
"Received request to upgrade database, but there is no running query server.",
|
||||
);
|
||||
}
|
||||
if (databaseItem.contents === undefined) {
|
||||
throw new Error(
|
||||
"Received request to upgrade database, but database contents could not be found.",
|
||||
);
|
||||
}
|
||||
if (databaseItem.contents.dbSchemeUri === undefined) {
|
||||
throw new Error(
|
||||
"Received request to upgrade database, but database has no schema.",
|
||||
);
|
||||
}
|
||||
|
||||
// Search for upgrade scripts in any workspace folders available
|
||||
// Search for upgrade scripts in any workspace folders available
|
||||
|
||||
await this.queryServer.upgradeDatabaseExplicit(
|
||||
databaseItem,
|
||||
progress,
|
||||
token,
|
||||
await this.queryServer.upgradeDatabaseExplicit(
|
||||
databaseItem,
|
||||
progress,
|
||||
token,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -651,24 +654,15 @@ export class DatabaseUI extends DisposableObject {
|
||||
}
|
||||
|
||||
private async handleRemoveDatabase(
|
||||
databaseItem: DatabaseItem,
|
||||
multiSelect: DatabaseItem[] | undefined,
|
||||
databaseItems: DatabaseItem[],
|
||||
): Promise<void> {
|
||||
return withProgress(
|
||||
async (progress, token) => {
|
||||
if (multiSelect?.length) {
|
||||
await Promise.all(
|
||||
multiSelect.map((dbItem) =>
|
||||
this.databaseManager.removeDatabaseItem(progress, token, dbItem),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
await this.databaseManager.removeDatabaseItem(
|
||||
progress,
|
||||
token,
|
||||
databaseItem,
|
||||
);
|
||||
}
|
||||
await Promise.all(
|
||||
databaseItems.map((dbItem) =>
|
||||
this.databaseManager.removeDatabaseItem(progress, token, dbItem),
|
||||
),
|
||||
);
|
||||
},
|
||||
{
|
||||
title: "Removing database",
|
||||
@@ -679,10 +673,7 @@ export class DatabaseUI extends DisposableObject {
|
||||
|
||||
private async handleRenameDatabase(
|
||||
databaseItem: DatabaseItem,
|
||||
multiSelect: DatabaseItem[] | undefined,
|
||||
): Promise<void> {
|
||||
this.assertSingleDatabase(multiSelect);
|
||||
|
||||
const newName = await window.showInputBox({
|
||||
prompt: "Choose new database name",
|
||||
value: databaseItem.name,
|
||||
@@ -693,17 +684,10 @@ export class DatabaseUI extends DisposableObject {
|
||||
}
|
||||
}
|
||||
|
||||
private async handleOpenFolder(
|
||||
databaseItem: DatabaseItem,
|
||||
multiSelect: DatabaseItem[] | undefined,
|
||||
): Promise<void> {
|
||||
if (multiSelect?.length) {
|
||||
await Promise.all(
|
||||
multiSelect.map((dbItem) => env.openExternal(dbItem.databaseUri)),
|
||||
);
|
||||
} else {
|
||||
await env.openExternal(databaseItem.databaseUri);
|
||||
}
|
||||
private async handleOpenFolder(databaseItems: DatabaseItem[]): Promise<void> {
|
||||
await Promise.all(
|
||||
databaseItems.map((dbItem) => env.openExternal(dbItem.databaseUri)),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -711,16 +695,9 @@ export class DatabaseUI extends DisposableObject {
|
||||
* When a database is first added in the "Databases" view, its source folder is added to the workspace.
|
||||
* If the source folder is removed from the workspace for some reason, we want to be able to re-add it if need be.
|
||||
*/
|
||||
private async handleAddSource(
|
||||
databaseItem: DatabaseItem,
|
||||
multiSelect: DatabaseItem[] | undefined,
|
||||
): Promise<void> {
|
||||
if (multiSelect?.length) {
|
||||
for (const dbItem of multiSelect) {
|
||||
await this.databaseManager.addDatabaseSourceArchiveFolder(dbItem);
|
||||
}
|
||||
} else {
|
||||
await this.databaseManager.addDatabaseSourceArchiveFolder(databaseItem);
|
||||
private async handleAddSource(databaseItems: DatabaseItem[]): Promise<void> {
|
||||
for (const dbItem of databaseItems) {
|
||||
await this.databaseManager.addDatabaseSourceArchiveFolder(dbItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -823,13 +800,4 @@ export class DatabaseUI extends DisposableObject {
|
||||
}
|
||||
return Uri.file(dbPath);
|
||||
}
|
||||
|
||||
private assertSingleDatabase(
|
||||
multiSelect: DatabaseItem[] = [],
|
||||
message = "Please select a single database.",
|
||||
) {
|
||||
if (multiSelect.length > 1) {
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import { App } from "../common/app";
|
||||
import { DisposableObject } from "../pure/disposable-object";
|
||||
import { SkeletonQueryWizard } from "../skeleton-query-wizard";
|
||||
import { LocalQueryRun } from "./local-query-run";
|
||||
import { createMultiSelectionCommand } from "../common/selection-commands";
|
||||
|
||||
interface DatabaseQuickPickItem extends QuickPickItem {
|
||||
databaseItem: DatabaseItem;
|
||||
@@ -89,7 +90,9 @@ export class LocalQueries extends DisposableObject {
|
||||
this.runQueryOnMultipleDatabases.bind(this),
|
||||
"codeQL.runQueryOnMultipleDatabasesContextEditor":
|
||||
this.runQueryOnMultipleDatabases.bind(this),
|
||||
"codeQL.runQueries": this.runQueries.bind(this),
|
||||
"codeQL.runQueries": createMultiSelectionCommand(
|
||||
this.runQueries.bind(this),
|
||||
),
|
||||
"codeQL.quickEval": this.quickEval.bind(this),
|
||||
"codeQL.quickEvalContextEditor": this.quickEval.bind(this),
|
||||
"codeQL.codeLensQuickEval": this.codeLensQuickEval.bind(this),
|
||||
@@ -130,12 +133,12 @@ export class LocalQueries extends DisposableObject {
|
||||
);
|
||||
}
|
||||
|
||||
private async runQueries(_: unknown, multi: Uri[]): Promise<void> {
|
||||
private async runQueries(fileURIs: Uri[]): Promise<void> {
|
||||
await withProgress(
|
||||
async (progress, token) => {
|
||||
const maxQueryCount = MAX_QUERIES.getValue() as number;
|
||||
const [files, dirFound] = await gatherQlFiles(
|
||||
multi.map((uri) => uri.fsPath),
|
||||
fileURIs.map((uri) => uri.fsPath),
|
||||
);
|
||||
if (files.length > maxQueryCount) {
|
||||
throw new Error(
|
||||
|
||||
@@ -56,6 +56,10 @@ import { QueryHistoryDirs } from "./query-history-dirs";
|
||||
import { QueryHistoryCommands } from "../common/commands";
|
||||
import { App } from "../common/app";
|
||||
import { tryOpenExternalFile } from "../vscode-utils/external-files";
|
||||
import {
|
||||
createMultiSelectionCommand,
|
||||
createSingleSelectionCommand,
|
||||
} from "../common/selection-commands";
|
||||
|
||||
/**
|
||||
* query-history-manager.ts
|
||||
@@ -232,33 +236,78 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
"codeQLQueryHistory.sortByDate": this.handleSortByDate.bind(this),
|
||||
"codeQLQueryHistory.sortByCount": this.handleSortByCount.bind(this),
|
||||
|
||||
"codeQLQueryHistory.openQueryContextMenu":
|
||||
"codeQLQueryHistory.openQueryContextMenu": createSingleSelectionCommand(
|
||||
this.handleOpenQuery.bind(this),
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.removeHistoryItemContextMenu":
|
||||
this.handleRemoveHistoryItem.bind(this),
|
||||
createMultiSelectionCommand(this.handleRemoveHistoryItem.bind(this)),
|
||||
"codeQLQueryHistory.removeHistoryItemContextInline":
|
||||
this.handleRemoveHistoryItem.bind(this),
|
||||
"codeQLQueryHistory.renameItem": this.handleRenameItem.bind(this),
|
||||
createMultiSelectionCommand(this.handleRemoveHistoryItem.bind(this)),
|
||||
"codeQLQueryHistory.renameItem": createSingleSelectionCommand(
|
||||
this.handleRenameItem.bind(this),
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.compareWith": this.handleCompareWith.bind(this),
|
||||
"codeQLQueryHistory.showEvalLog": this.handleShowEvalLog.bind(this),
|
||||
"codeQLQueryHistory.showEvalLogSummary":
|
||||
"codeQLQueryHistory.showEvalLog": createSingleSelectionCommand(
|
||||
this.handleShowEvalLog.bind(this),
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.showEvalLogSummary": createSingleSelectionCommand(
|
||||
this.handleShowEvalLogSummary.bind(this),
|
||||
"codeQLQueryHistory.showEvalLogViewer":
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.showEvalLogViewer": createSingleSelectionCommand(
|
||||
this.handleShowEvalLogViewer.bind(this),
|
||||
"codeQLQueryHistory.showQueryLog": this.handleShowQueryLog.bind(this),
|
||||
"codeQLQueryHistory.showQueryText": this.handleShowQueryText.bind(this),
|
||||
"codeQLQueryHistory.openQueryDirectory":
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.showQueryLog": createSingleSelectionCommand(
|
||||
this.handleShowQueryLog.bind(this),
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.showQueryText": createSingleSelectionCommand(
|
||||
this.handleShowQueryText.bind(this),
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.openQueryDirectory": createSingleSelectionCommand(
|
||||
this.handleOpenQueryDirectory.bind(this),
|
||||
"codeQLQueryHistory.cancel": this.handleCancel.bind(this),
|
||||
"codeQLQueryHistory.exportResults": this.handleExportResults.bind(this),
|
||||
"codeQLQueryHistory.viewCsvResults": this.handleViewCsvResults.bind(this),
|
||||
"codeQLQueryHistory.viewCsvAlerts": this.handleViewCsvAlerts.bind(this),
|
||||
"codeQLQueryHistory.viewSarifAlerts":
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.cancel": createMultiSelectionCommand(
|
||||
this.handleCancel.bind(this),
|
||||
),
|
||||
"codeQLQueryHistory.exportResults": createSingleSelectionCommand(
|
||||
this.handleExportResults.bind(this),
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.viewCsvResults": createSingleSelectionCommand(
|
||||
this.handleViewCsvResults.bind(this),
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.viewCsvAlerts": createSingleSelectionCommand(
|
||||
this.handleViewCsvAlerts.bind(this),
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.viewSarifAlerts": createSingleSelectionCommand(
|
||||
this.handleViewSarifAlerts.bind(this),
|
||||
"codeQLQueryHistory.viewDil": this.handleViewDil.bind(this),
|
||||
"codeQLQueryHistory.itemClicked": this.handleItemClicked.bind(this),
|
||||
"codeQLQueryHistory.openOnGithub": this.handleOpenOnGithub.bind(this),
|
||||
"codeQLQueryHistory.copyRepoList": this.handleCopyRepoList.bind(this),
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.viewDil": createSingleSelectionCommand(
|
||||
this.handleViewDil.bind(this),
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.itemClicked": createSingleSelectionCommand(
|
||||
this.handleItemClicked.bind(this),
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.openOnGithub": createSingleSelectionCommand(
|
||||
this.handleOpenOnGithub.bind(this),
|
||||
"query",
|
||||
),
|
||||
"codeQLQueryHistory.copyRepoList": createSingleSelectionCommand(
|
||||
this.handleCopyRepoList.bind(this),
|
||||
"query",
|
||||
),
|
||||
|
||||
"codeQL.exportSelectedVariantAnalysisResults":
|
||||
this.exportSelectedVariantAnalysisResults.bind(this),
|
||||
@@ -392,35 +441,26 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
);
|
||||
}
|
||||
|
||||
async handleOpenQuery(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
): Promise<void> {
|
||||
if (!this.assertSingleQuery(multiSelect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (singleItem.t === "variant-analysis") {
|
||||
await this.variantAnalysisManager.openQueryFile(
|
||||
singleItem.variantAnalysis.id,
|
||||
);
|
||||
async handleOpenQuery(item: QueryHistoryInfo): Promise<void> {
|
||||
if (item.t === "variant-analysis") {
|
||||
await this.variantAnalysisManager.openQueryFile(item.variantAnalysis.id);
|
||||
return;
|
||||
}
|
||||
|
||||
let queryPath: string;
|
||||
switch (singleItem.t) {
|
||||
switch (item.t) {
|
||||
case "local":
|
||||
queryPath = singleItem.initialInfo.queryPath;
|
||||
queryPath = item.initialInfo.queryPath;
|
||||
break;
|
||||
default:
|
||||
assertNever(singleItem);
|
||||
assertNever(item);
|
||||
}
|
||||
const textDocument = await workspace.openTextDocument(Uri.file(queryPath));
|
||||
const editor = await window.showTextDocument(textDocument, ViewColumn.One);
|
||||
|
||||
if (singleItem.t === "local") {
|
||||
const queryText = singleItem.initialInfo.queryText;
|
||||
if (queryText !== undefined && singleItem.initialInfo.isQuickQuery) {
|
||||
if (item.t === "local") {
|
||||
const queryText = item.initialInfo.queryText;
|
||||
if (queryText !== undefined && item.initialInfo.isQuickQuery) {
|
||||
await editor.edit((edit) =>
|
||||
edit.replace(
|
||||
textDocument.validateRange(
|
||||
@@ -451,13 +491,9 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
);
|
||||
}
|
||||
|
||||
async handleRemoveHistoryItem(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
multiSelect ||= [singleItem];
|
||||
async handleRemoveHistoryItem(items: QueryHistoryInfo[]) {
|
||||
await Promise.all(
|
||||
multiSelect.map(async (item) => {
|
||||
items.map(async (item) => {
|
||||
if (item.t === "local") {
|
||||
// Removing in progress local queries is not supported. They must be cancelled first.
|
||||
if (item.status !== QueryStatus.InProgress) {
|
||||
@@ -547,17 +583,10 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
}
|
||||
}
|
||||
|
||||
async handleRenameItem(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
): Promise<void> {
|
||||
if (!this.assertSingleQuery(multiSelect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
async handleRenameItem(item: QueryHistoryInfo): Promise<void> {
|
||||
const response = await window.showInputBox({
|
||||
placeHolder: `(use default: ${this.queryHistoryConfigListener.format})`,
|
||||
value: singleItem.userSpecifiedLabel ?? "",
|
||||
value: item.userSpecifiedLabel ?? "",
|
||||
title: "Set query label",
|
||||
prompt:
|
||||
"Set the query history item label. See the description of the codeQL.queryHistory.format setting for more information.",
|
||||
@@ -565,7 +594,7 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
// undefined response means the user cancelled the dialog; don't change anything
|
||||
if (response !== undefined) {
|
||||
// Interpret empty string response as 'go back to using default'
|
||||
singleItem.userSpecifiedLabel = response === "" ? undefined : response;
|
||||
item.userSpecifiedLabel = response === "" ? undefined : response;
|
||||
await this.refreshTreeView();
|
||||
}
|
||||
}
|
||||
@@ -607,53 +636,39 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
}
|
||||
}
|
||||
|
||||
async handleItemClicked(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
if (!this.assertSingleQuery(multiSelect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.treeDataProvider.setCurrentItem(singleItem);
|
||||
async handleItemClicked(item: QueryHistoryInfo) {
|
||||
this.treeDataProvider.setCurrentItem(item);
|
||||
|
||||
const now = new Date();
|
||||
const prevItemClick = this.lastItemClick;
|
||||
this.lastItemClick = { time: now, item: singleItem };
|
||||
this.lastItemClick = { time: now, item };
|
||||
|
||||
if (
|
||||
prevItemClick !== undefined &&
|
||||
now.valueOf() - prevItemClick.time.valueOf() < DOUBLE_CLICK_TIME &&
|
||||
singleItem === prevItemClick.item
|
||||
item === prevItemClick.item
|
||||
) {
|
||||
// show original query file on double click
|
||||
await this.handleOpenQuery(singleItem, [singleItem]);
|
||||
await this.handleOpenQuery(item);
|
||||
} else if (
|
||||
singleItem.t === "variant-analysis" ||
|
||||
singleItem.status === QueryStatus.Completed
|
||||
item.t === "variant-analysis" ||
|
||||
item.status === QueryStatus.Completed
|
||||
) {
|
||||
// show results on single click (if results view is available)
|
||||
await this.openQueryResults(singleItem);
|
||||
await this.openQueryResults(item);
|
||||
}
|
||||
}
|
||||
|
||||
async handleShowQueryLog(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
async handleShowQueryLog(item: QueryHistoryInfo) {
|
||||
// Local queries only
|
||||
if (!this.assertSingleQuery(multiSelect) || singleItem?.t !== "local") {
|
||||
if (item?.t !== "local" || !item.completedQuery) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!singleItem.completedQuery) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (singleItem.completedQuery.logFileLocation) {
|
||||
if (item.completedQuery.logFileLocation) {
|
||||
await tryOpenExternalFile(
|
||||
this.app.commands,
|
||||
singleItem.completedQuery.logFileLocation,
|
||||
item.completedQuery.logFileLocation,
|
||||
);
|
||||
} else {
|
||||
void showAndLogWarningMessage("No log file available");
|
||||
@@ -678,31 +693,24 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
throw new Error("Unable to get query directory");
|
||||
}
|
||||
|
||||
async handleOpenQueryDirectory(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
if (!this.assertSingleQuery(multiSelect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
async handleOpenQueryDirectory(item: QueryHistoryInfo) {
|
||||
let externalFilePath: string | undefined;
|
||||
if (singleItem.t === "local") {
|
||||
if (singleItem.completedQuery) {
|
||||
if (item.t === "local") {
|
||||
if (item.completedQuery) {
|
||||
externalFilePath = join(
|
||||
singleItem.completedQuery.query.querySaveDir,
|
||||
item.completedQuery.query.querySaveDir,
|
||||
"timestamp",
|
||||
);
|
||||
}
|
||||
} else if (singleItem.t === "variant-analysis") {
|
||||
} else if (item.t === "variant-analysis") {
|
||||
externalFilePath = join(
|
||||
this.variantAnalysisManager.getVariantAnalysisStorageLocation(
|
||||
singleItem.variantAnalysis.id,
|
||||
item.variantAnalysis.id,
|
||||
),
|
||||
"timestamp",
|
||||
);
|
||||
} else {
|
||||
assertNever(singleItem);
|
||||
assertNever(item);
|
||||
}
|
||||
|
||||
if (externalFilePath) {
|
||||
@@ -747,44 +755,30 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
);
|
||||
}
|
||||
|
||||
async handleShowEvalLog(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
// Only applicable to an individual local query
|
||||
if (!this.assertSingleQuery(multiSelect) || singleItem.t !== "local") {
|
||||
async handleShowEvalLog(item: QueryHistoryInfo) {
|
||||
if (item.t !== "local") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (singleItem.evalLogLocation) {
|
||||
await tryOpenExternalFile(this.app.commands, singleItem.evalLogLocation);
|
||||
if (item.evalLogLocation) {
|
||||
await tryOpenExternalFile(this.app.commands, item.evalLogLocation);
|
||||
} else {
|
||||
this.warnNoEvalLogs();
|
||||
}
|
||||
}
|
||||
|
||||
async handleShowEvalLogSummary(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
// Only applicable to an individual local query
|
||||
if (!this.assertSingleQuery(multiSelect) || singleItem.t !== "local") {
|
||||
async handleShowEvalLogSummary(item: QueryHistoryInfo) {
|
||||
if (item.t !== "local") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (singleItem.evalLogSummaryLocation) {
|
||||
await tryOpenExternalFile(
|
||||
this.app.commands,
|
||||
singleItem.evalLogSummaryLocation,
|
||||
);
|
||||
if (item.evalLogSummaryLocation) {
|
||||
await tryOpenExternalFile(this.app.commands, item.evalLogSummaryLocation);
|
||||
return;
|
||||
}
|
||||
|
||||
// Summary log file doesn't exist.
|
||||
if (
|
||||
singleItem.evalLogLocation &&
|
||||
(await pathExists(singleItem.evalLogLocation))
|
||||
) {
|
||||
if (item.evalLogLocation && (await pathExists(item.evalLogLocation))) {
|
||||
// If raw log does exist, then the summary log is still being generated.
|
||||
this.warnInProgressEvalLogSummary();
|
||||
} else {
|
||||
@@ -792,17 +786,13 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
}
|
||||
}
|
||||
|
||||
async handleShowEvalLogViewer(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
// Only applicable to an individual local query
|
||||
if (!this.assertSingleQuery(multiSelect) || singleItem.t !== "local") {
|
||||
async handleShowEvalLogViewer(item: QueryHistoryInfo) {
|
||||
if (item.t !== "local") {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the JSON summary file location wasn't saved, display error
|
||||
if (singleItem.jsonEvalLogSummaryLocation === undefined) {
|
||||
if (item.jsonEvalLogSummaryLocation === undefined) {
|
||||
this.warnInProgressEvalLogViewer();
|
||||
return;
|
||||
}
|
||||
@@ -810,27 +800,22 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
// TODO(angelapwen): Stream the file in.
|
||||
try {
|
||||
const evalLogData: EvalLogData[] = await parseViewerData(
|
||||
singleItem.jsonEvalLogSummaryLocation,
|
||||
item.jsonEvalLogSummaryLocation,
|
||||
);
|
||||
const evalLogTreeBuilder = new EvalLogTreeBuilder(
|
||||
singleItem.getQueryName(),
|
||||
item.getQueryName(),
|
||||
evalLogData,
|
||||
);
|
||||
this.evalLogViewer.updateRoots(await evalLogTreeBuilder.getRoots());
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Could not read evaluator log summary JSON file to generate viewer data at ${singleItem.jsonEvalLogSummaryLocation}.`,
|
||||
`Could not read evaluator log summary JSON file to generate viewer data at ${item.jsonEvalLogSummaryLocation}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async handleCancel(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
multiSelect ||= [singleItem];
|
||||
|
||||
const results = multiSelect.map(async (item) => {
|
||||
async handleCancel(items: QueryHistoryInfo[]) {
|
||||
const results = items.map(async (item) => {
|
||||
if (item.status === QueryStatus.InProgress) {
|
||||
if (item.t === "local") {
|
||||
item.cancel();
|
||||
@@ -847,51 +832,32 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
await Promise.all(results);
|
||||
}
|
||||
|
||||
async handleShowQueryText(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] = [],
|
||||
) {
|
||||
if (!this.assertSingleQuery(multiSelect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (singleItem.t === "variant-analysis") {
|
||||
await this.variantAnalysisManager.openQueryText(
|
||||
singleItem.variantAnalysis.id,
|
||||
);
|
||||
async handleShowQueryText(item: QueryHistoryInfo) {
|
||||
if (item.t === "variant-analysis") {
|
||||
await this.variantAnalysisManager.openQueryText(item.variantAnalysis.id);
|
||||
return;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
isQuickEval: String(
|
||||
!!(
|
||||
singleItem.t === "local" && singleItem.initialInfo.quickEvalPosition
|
||||
),
|
||||
!!(item.t === "local" && item.initialInfo.quickEvalPosition),
|
||||
),
|
||||
queryText: encodeURIComponent(getQueryText(singleItem)),
|
||||
queryText: encodeURIComponent(getQueryText(item)),
|
||||
});
|
||||
|
||||
const queryId = getQueryId(singleItem);
|
||||
const queryId = getQueryId(item);
|
||||
|
||||
const uri = Uri.parse(`codeql:${queryId}.ql?${params.toString()}`, true);
|
||||
const doc = await workspace.openTextDocument(uri);
|
||||
await window.showTextDocument(doc, { preview: false });
|
||||
}
|
||||
|
||||
async handleViewSarifAlerts(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
// Local queries only
|
||||
if (
|
||||
!this.assertSingleQuery(multiSelect) ||
|
||||
singleItem.t !== "local" ||
|
||||
!singleItem.completedQuery
|
||||
) {
|
||||
async handleViewSarifAlerts(item: QueryHistoryInfo) {
|
||||
if (item.t !== "local" || !item.completedQuery) {
|
||||
return;
|
||||
}
|
||||
|
||||
const query = singleItem.completedQuery.query;
|
||||
const query = item.completedQuery.query;
|
||||
const hasInterpretedResults = query.canHaveInterpretedResults();
|
||||
if (hasInterpretedResults) {
|
||||
await tryOpenExternalFile(
|
||||
@@ -899,26 +865,18 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
query.resultsPaths.interpretedResultsPath,
|
||||
);
|
||||
} else {
|
||||
const label = this.labelProvider.getLabel(singleItem);
|
||||
const label = this.labelProvider.getLabel(item);
|
||||
void showAndLogInformationMessage(
|
||||
`Query ${label} has no interpreted results.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async handleViewCsvResults(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
// Local queries only
|
||||
if (
|
||||
!this.assertSingleQuery(multiSelect) ||
|
||||
singleItem.t !== "local" ||
|
||||
!singleItem.completedQuery
|
||||
) {
|
||||
async handleViewCsvResults(item: QueryHistoryInfo) {
|
||||
if (item.t !== "local" || !item.completedQuery) {
|
||||
return;
|
||||
}
|
||||
const query = singleItem.completedQuery.query;
|
||||
const query = item.completedQuery.query;
|
||||
if (await query.hasCsv()) {
|
||||
void tryOpenExternalFile(this.app.commands, query.csvPath);
|
||||
return;
|
||||
@@ -928,59 +886,37 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
}
|
||||
}
|
||||
|
||||
async handleViewCsvAlerts(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
// Local queries only
|
||||
if (
|
||||
!this.assertSingleQuery(multiSelect) ||
|
||||
singleItem.t !== "local" ||
|
||||
!singleItem.completedQuery
|
||||
) {
|
||||
async handleViewCsvAlerts(item: QueryHistoryInfo) {
|
||||
if (item.t !== "local" || !item.completedQuery) {
|
||||
return;
|
||||
}
|
||||
|
||||
await tryOpenExternalFile(
|
||||
this.app.commands,
|
||||
await singleItem.completedQuery.query.ensureCsvAlerts(
|
||||
await item.completedQuery.query.ensureCsvAlerts(
|
||||
this.qs.cliServer,
|
||||
this.dbm,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async handleViewDil(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
// Local queries only
|
||||
if (
|
||||
!this.assertSingleQuery(multiSelect) ||
|
||||
singleItem.t !== "local" ||
|
||||
!singleItem.completedQuery
|
||||
) {
|
||||
async handleViewDil(item: QueryHistoryInfo) {
|
||||
if (item.t !== "local" || !item.completedQuery) {
|
||||
return;
|
||||
}
|
||||
|
||||
await tryOpenExternalFile(
|
||||
this.app.commands,
|
||||
await singleItem.completedQuery.query.ensureDilPath(this.qs.cliServer),
|
||||
await item.completedQuery.query.ensureDilPath(this.qs.cliServer),
|
||||
);
|
||||
}
|
||||
|
||||
async handleOpenOnGithub(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
if (
|
||||
!this.assertSingleQuery(multiSelect) ||
|
||||
singleItem.t !== "variant-analysis"
|
||||
) {
|
||||
async handleOpenOnGithub(item: QueryHistoryInfo) {
|
||||
if (item.t !== "variant-analysis") {
|
||||
return;
|
||||
}
|
||||
|
||||
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(singleItem);
|
||||
const actionsWorkflowRunUrl = getActionsWorkflowRunUrl(item);
|
||||
|
||||
await this.app.commands.execute(
|
||||
"vscode.open",
|
||||
@@ -988,39 +924,23 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
);
|
||||
}
|
||||
|
||||
async handleCopyRepoList(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
) {
|
||||
// Variant analyses only
|
||||
if (
|
||||
!this.assertSingleQuery(multiSelect) ||
|
||||
singleItem.t !== "variant-analysis"
|
||||
) {
|
||||
async handleCopyRepoList(item: QueryHistoryInfo) {
|
||||
if (item.t !== "variant-analysis") {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.app.commands.execute(
|
||||
"codeQL.copyVariantAnalysisRepoList",
|
||||
singleItem.variantAnalysis.id,
|
||||
item.variantAnalysis.id,
|
||||
);
|
||||
}
|
||||
|
||||
async handleExportResults(
|
||||
singleItem: QueryHistoryInfo,
|
||||
multiSelect: QueryHistoryInfo[] | undefined,
|
||||
): Promise<void> {
|
||||
// Variant analysis only
|
||||
if (
|
||||
!this.assertSingleQuery(multiSelect) ||
|
||||
singleItem.t !== "variant-analysis"
|
||||
) {
|
||||
async handleExportResults(item: QueryHistoryInfo): Promise<void> {
|
||||
if (item.t !== "variant-analysis") {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.variantAnalysisManager.exportResults(
|
||||
singleItem.variantAnalysis.id,
|
||||
);
|
||||
await this.variantAnalysisManager.exportResults(item.variantAnalysis.id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1123,17 +1043,6 @@ export class QueryHistoryManager extends DisposableObject {
|
||||
return choice?.query;
|
||||
}
|
||||
|
||||
private assertSingleQuery(
|
||||
multiSelect: QueryHistoryInfo[] = [],
|
||||
message = "Please select a single query.",
|
||||
) {
|
||||
if (multiSelect.length > 1) {
|
||||
void showAndLogErrorMessage(message);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the compare with source query. This ensures that all compare command invocations
|
||||
* when exactly 2 queries are selected always have the proper _from_ query. Always use
|
||||
|
||||
@@ -155,9 +155,7 @@ describe("QueryHistoryManager", () => {
|
||||
it("should show results", async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
const itemClicked = localQueryHistory[0];
|
||||
await queryHistoryManager.handleItemClicked(itemClicked, [
|
||||
itemClicked,
|
||||
]);
|
||||
await queryHistoryManager.handleItemClicked(itemClicked);
|
||||
|
||||
expect(
|
||||
localQueriesResultsViewStub.showResults,
|
||||
@@ -175,9 +173,7 @@ describe("QueryHistoryManager", () => {
|
||||
it("should do nothing", async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
const itemClicked = localQueryHistory[2];
|
||||
await queryHistoryManager.handleItemClicked(itemClicked, [
|
||||
itemClicked,
|
||||
]);
|
||||
await queryHistoryManager.handleItemClicked(itemClicked);
|
||||
|
||||
expect(
|
||||
localQueriesResultsViewStub.showResults,
|
||||
@@ -191,9 +187,7 @@ describe("QueryHistoryManager", () => {
|
||||
it("should show results", async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
const itemClicked = variantAnalysisHistory[0];
|
||||
await queryHistoryManager.handleItemClicked(itemClicked, [
|
||||
itemClicked,
|
||||
]);
|
||||
await queryHistoryManager.handleItemClicked(itemClicked);
|
||||
|
||||
expect(variantAnalysisManagerStub.showView).toHaveBeenCalledTimes(
|
||||
1,
|
||||
@@ -211,9 +205,7 @@ describe("QueryHistoryManager", () => {
|
||||
it("should show results", async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
const itemClicked = variantAnalysisHistory[1];
|
||||
await queryHistoryManager.handleItemClicked(itemClicked, [
|
||||
itemClicked,
|
||||
]);
|
||||
await queryHistoryManager.handleItemClicked(itemClicked);
|
||||
|
||||
expect(variantAnalysisManagerStub.showView).toHaveBeenCalledTimes(
|
||||
1,
|
||||
@@ -228,25 +220,6 @@ describe("QueryHistoryManager", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("double click", () => {
|
||||
it("should do nothing", async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
const itemClicked = allHistory[0];
|
||||
const secondItemClicked = allHistory[1];
|
||||
|
||||
await queryHistoryManager.handleItemClicked(itemClicked, [
|
||||
itemClicked,
|
||||
secondItemClicked,
|
||||
]);
|
||||
|
||||
expect(localQueriesResultsViewStub.showResults).not.toHaveBeenCalled();
|
||||
expect(variantAnalysisManagerStub.showView).not.toBeCalled();
|
||||
expect(
|
||||
queryHistoryManager.treeDataProvider.getCurrent(),
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("handleRemoveHistoryItem", () => {
|
||||
@@ -278,9 +251,7 @@ describe("QueryHistoryManager", () => {
|
||||
);
|
||||
|
||||
// remove an item
|
||||
await queryHistoryManager.handleRemoveHistoryItem(toDelete, [
|
||||
toDelete,
|
||||
]);
|
||||
await queryHistoryManager.handleRemoveHistoryItem([toDelete]);
|
||||
});
|
||||
|
||||
it("should remove the item", () => {
|
||||
@@ -320,9 +291,7 @@ describe("QueryHistoryManager", () => {
|
||||
await queryHistoryManager.treeView.reveal(toDelete, {
|
||||
select: true,
|
||||
});
|
||||
await queryHistoryManager.handleRemoveHistoryItem(toDelete, [
|
||||
toDelete,
|
||||
]);
|
||||
await queryHistoryManager.handleRemoveHistoryItem([toDelete]);
|
||||
});
|
||||
|
||||
it("should remove the item", () => {
|
||||
@@ -401,9 +370,7 @@ describe("QueryHistoryManager", () => {
|
||||
|
||||
it("should remove the item", async () => {
|
||||
// remove an item
|
||||
await queryHistoryManager.handleRemoveHistoryItem(toDelete, [
|
||||
toDelete,
|
||||
]);
|
||||
await queryHistoryManager.handleRemoveHistoryItem([toDelete]);
|
||||
|
||||
expect(
|
||||
variantAnalysisManagerStub.removeVariantAnalysis,
|
||||
@@ -415,9 +382,7 @@ describe("QueryHistoryManager", () => {
|
||||
|
||||
it("should not change the selection", async () => {
|
||||
// remove an item
|
||||
await queryHistoryManager.handleRemoveHistoryItem(toDelete, [
|
||||
toDelete,
|
||||
]);
|
||||
await queryHistoryManager.handleRemoveHistoryItem([toDelete]);
|
||||
|
||||
expect(queryHistoryManager.treeDataProvider.getCurrent()).toEqual(
|
||||
selected,
|
||||
@@ -429,9 +394,7 @@ describe("QueryHistoryManager", () => {
|
||||
|
||||
it("should show a modal asking 'Are you sure?'", async () => {
|
||||
// remove an item
|
||||
await queryHistoryManager.handleRemoveHistoryItem(toDelete, [
|
||||
toDelete,
|
||||
]);
|
||||
await queryHistoryManager.handleRemoveHistoryItem([toDelete]);
|
||||
|
||||
expect(showBinaryChoiceDialogSpy).toHaveBeenCalledWith(
|
||||
"You are about to delete this query: a-query-name (javascript). Are you sure?",
|
||||
@@ -440,9 +403,7 @@ describe("QueryHistoryManager", () => {
|
||||
|
||||
it("should show a toast notification with a link to GitHub Actions", async () => {
|
||||
// remove an item
|
||||
await queryHistoryManager.handleRemoveHistoryItem(toDelete, [
|
||||
toDelete,
|
||||
]);
|
||||
await queryHistoryManager.handleRemoveHistoryItem([toDelete]);
|
||||
|
||||
expect(showInformationMessageWithActionSpy).toHaveBeenCalled();
|
||||
});
|
||||
@@ -454,9 +415,7 @@ describe("QueryHistoryManager", () => {
|
||||
|
||||
it("should not delete the item", async () => {
|
||||
// remove an item
|
||||
await queryHistoryManager.handleRemoveHistoryItem(toDelete, [
|
||||
toDelete,
|
||||
]);
|
||||
await queryHistoryManager.handleRemoveHistoryItem([toDelete]);
|
||||
|
||||
expect(queryHistoryManager.treeDataProvider.allHistory).toContain(
|
||||
toDelete,
|
||||
@@ -465,9 +424,7 @@ describe("QueryHistoryManager", () => {
|
||||
|
||||
it("should not show a toast notification", async () => {
|
||||
// remove an item
|
||||
await queryHistoryManager.handleRemoveHistoryItem(toDelete, [
|
||||
toDelete,
|
||||
]);
|
||||
await queryHistoryManager.handleRemoveHistoryItem([toDelete]);
|
||||
|
||||
expect(
|
||||
showInformationMessageWithActionSpy,
|
||||
@@ -493,9 +450,7 @@ describe("QueryHistoryManager", () => {
|
||||
await queryHistoryManager.treeView.reveal(toDelete, {
|
||||
select: true,
|
||||
});
|
||||
await queryHistoryManager.handleRemoveHistoryItem(toDelete, [
|
||||
toDelete,
|
||||
]);
|
||||
await queryHistoryManager.handleRemoveHistoryItem([toDelete]);
|
||||
});
|
||||
|
||||
it("should remove the item", () => {
|
||||
@@ -555,9 +510,7 @@ describe("QueryHistoryManager", () => {
|
||||
);
|
||||
|
||||
// remove an item
|
||||
await queryHistoryManager.handleRemoveHistoryItem(toDelete, [
|
||||
toDelete,
|
||||
]);
|
||||
await queryHistoryManager.handleRemoveHistoryItem([toDelete]);
|
||||
});
|
||||
|
||||
it("should remove the item", () => {
|
||||
@@ -600,9 +553,7 @@ describe("QueryHistoryManager", () => {
|
||||
await queryHistoryManager.treeView.reveal(toDelete, {
|
||||
select: true,
|
||||
});
|
||||
await queryHistoryManager.handleRemoveHistoryItem(toDelete, [
|
||||
toDelete,
|
||||
]);
|
||||
await queryHistoryManager.handleRemoveHistoryItem([toDelete]);
|
||||
});
|
||||
|
||||
it("should remove the item", () => {
|
||||
@@ -640,7 +591,7 @@ describe("QueryHistoryManager", () => {
|
||||
const inProgress1 = localQueryHistory[4];
|
||||
const cancelSpy = jest.spyOn(inProgress1, "cancel");
|
||||
|
||||
await queryHistoryManager.handleCancel(inProgress1, [inProgress1]);
|
||||
await queryHistoryManager.handleCancel([inProgress1]);
|
||||
expect(cancelSpy).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -654,10 +605,7 @@ describe("QueryHistoryManager", () => {
|
||||
const cancelSpy1 = jest.spyOn(inProgress1, "cancel");
|
||||
const cancelSpy2 = jest.spyOn(inProgress2, "cancel");
|
||||
|
||||
await queryHistoryManager.handleCancel(inProgress1, [
|
||||
inProgress1,
|
||||
inProgress2,
|
||||
]);
|
||||
await queryHistoryManager.handleCancel([inProgress1, inProgress2]);
|
||||
expect(cancelSpy1).toBeCalled();
|
||||
expect(cancelSpy2).toBeCalled();
|
||||
});
|
||||
@@ -668,7 +616,7 @@ describe("QueryHistoryManager", () => {
|
||||
// cancelling the selected item
|
||||
const inProgress1 = variantAnalysisHistory[1];
|
||||
|
||||
await queryHistoryManager.handleCancel(inProgress1, [inProgress1]);
|
||||
await queryHistoryManager.handleCancel([inProgress1]);
|
||||
expect(cancelVariantAnalysisSpy).toBeCalledWith(
|
||||
inProgress1.variantAnalysis.id,
|
||||
);
|
||||
@@ -681,10 +629,7 @@ describe("QueryHistoryManager", () => {
|
||||
const inProgress1 = variantAnalysisHistory[1];
|
||||
const inProgress2 = variantAnalysisHistory[3];
|
||||
|
||||
await queryHistoryManager.handleCancel(inProgress1, [
|
||||
inProgress1,
|
||||
inProgress2,
|
||||
]);
|
||||
await queryHistoryManager.handleCancel([inProgress1, inProgress2]);
|
||||
expect(cancelVariantAnalysisSpy).toBeCalledWith(
|
||||
inProgress1.variantAnalysis.id,
|
||||
);
|
||||
@@ -702,7 +647,7 @@ describe("QueryHistoryManager", () => {
|
||||
const completed = localQueryHistory[0];
|
||||
const cancelSpy = jest.spyOn(completed, "cancel");
|
||||
|
||||
await queryHistoryManager.handleCancel(completed, [completed]);
|
||||
await queryHistoryManager.handleCancel([completed]);
|
||||
expect(cancelSpy).not.toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -716,7 +661,7 @@ describe("QueryHistoryManager", () => {
|
||||
const cancelSpy = jest.spyOn(completed, "cancel");
|
||||
const cancelSpy2 = jest.spyOn(failed, "cancel");
|
||||
|
||||
await queryHistoryManager.handleCancel(completed, [completed, failed]);
|
||||
await queryHistoryManager.handleCancel([completed, failed]);
|
||||
expect(cancelSpy).not.toBeCalledTimes(1);
|
||||
expect(cancelSpy2).not.toBeCalledTimes(1);
|
||||
});
|
||||
@@ -727,9 +672,7 @@ describe("QueryHistoryManager", () => {
|
||||
// cancelling the selected item
|
||||
const completedVariantAnalysis = variantAnalysisHistory[0];
|
||||
|
||||
await queryHistoryManager.handleCancel(completedVariantAnalysis, [
|
||||
completedVariantAnalysis,
|
||||
]);
|
||||
await queryHistoryManager.handleCancel([completedVariantAnalysis]);
|
||||
expect(cancelVariantAnalysisSpy).not.toBeCalledWith(
|
||||
completedVariantAnalysis.variantAnalysis,
|
||||
);
|
||||
@@ -742,7 +685,7 @@ describe("QueryHistoryManager", () => {
|
||||
const completedVariantAnalysis = variantAnalysisHistory[0];
|
||||
const failedVariantAnalysis = variantAnalysisHistory[2];
|
||||
|
||||
await queryHistoryManager.handleCancel(completedVariantAnalysis, [
|
||||
await queryHistoryManager.handleCancel([
|
||||
completedVariantAnalysis,
|
||||
failedVariantAnalysis,
|
||||
]);
|
||||
@@ -761,7 +704,7 @@ describe("QueryHistoryManager", () => {
|
||||
queryHistoryManager = await createMockQueryHistory(localQueryHistory);
|
||||
|
||||
const item = localQueryHistory[4];
|
||||
await queryHistoryManager.handleCopyRepoList(item, [item]);
|
||||
await queryHistoryManager.handleCopyRepoList(item);
|
||||
|
||||
expect(executeCommand).not.toBeCalled();
|
||||
});
|
||||
@@ -770,21 +713,12 @@ describe("QueryHistoryManager", () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
|
||||
const item = variantAnalysisHistory[1];
|
||||
await queryHistoryManager.handleCopyRepoList(item, [item]);
|
||||
await queryHistoryManager.handleCopyRepoList(item);
|
||||
expect(executeCommand).toBeCalledWith(
|
||||
"codeQL.copyVariantAnalysisRepoList",
|
||||
item.variantAnalysis.id,
|
||||
);
|
||||
});
|
||||
|
||||
it("should not copy repo list for multiple variant analyses", async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
|
||||
const item1 = variantAnalysisHistory[1];
|
||||
const item2 = variantAnalysisHistory[3];
|
||||
await queryHistoryManager.handleCopyRepoList(item1, [item1, item2]);
|
||||
expect(executeCommand).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("handleExportResults", () => {
|
||||
@@ -792,7 +726,7 @@ describe("QueryHistoryManager", () => {
|
||||
queryHistoryManager = await createMockQueryHistory(localQueryHistory);
|
||||
|
||||
const item = localQueryHistory[4];
|
||||
await queryHistoryManager.handleExportResults(item, [item]);
|
||||
await queryHistoryManager.handleExportResults(item);
|
||||
|
||||
expect(variantAnalysisManagerStub.exportResults).not.toBeCalled();
|
||||
});
|
||||
@@ -801,20 +735,11 @@ describe("QueryHistoryManager", () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
|
||||
const item = variantAnalysisHistory[1];
|
||||
await queryHistoryManager.handleExportResults(item, [item]);
|
||||
await queryHistoryManager.handleExportResults(item);
|
||||
expect(variantAnalysisManagerStub.exportResults).toBeCalledWith(
|
||||
item.variantAnalysis.id,
|
||||
);
|
||||
});
|
||||
|
||||
it("should not export results for multiple variant analyses", async () => {
|
||||
queryHistoryManager = await createMockQueryHistory(allHistory);
|
||||
|
||||
const item1 = variantAnalysisHistory[1];
|
||||
const item2 = variantAnalysisHistory[3];
|
||||
await queryHistoryManager.handleExportResults(item1, [item1, item2]);
|
||||
expect(variantAnalysisManagerStub.exportResults).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Local Queries", () => {
|
||||
|
||||
@@ -132,10 +132,7 @@ describe("Variant Analyses and QueryHistoryManager", () => {
|
||||
await qhm.readQueryHistory();
|
||||
|
||||
// Remove the first variant analysis
|
||||
await qhm.handleRemoveHistoryItem(
|
||||
qhm.treeDataProvider.allHistory[0],
|
||||
undefined,
|
||||
);
|
||||
await qhm.handleRemoveHistoryItem([qhm.treeDataProvider.allHistory[0]]);
|
||||
|
||||
// Add it back to the history
|
||||
qhm.addQuery(rawQueryHistory[0]);
|
||||
@@ -152,7 +149,7 @@ describe("Variant Analyses and QueryHistoryManager", () => {
|
||||
|
||||
// Remove both queries
|
||||
// Just for fun, let's do it in reverse order
|
||||
await qhm.handleRemoveHistoryItem(undefined!, [
|
||||
await qhm.handleRemoveHistoryItem([
|
||||
qhm.treeDataProvider.allHistory[1],
|
||||
qhm.treeDataProvider.allHistory[0],
|
||||
]);
|
||||
@@ -180,13 +177,13 @@ describe("Variant Analyses and QueryHistoryManager", () => {
|
||||
it("should handle a click", async () => {
|
||||
await qhm.readQueryHistory();
|
||||
|
||||
await qhm.handleItemClicked(qhm.treeDataProvider.allHistory[0], []);
|
||||
await qhm.handleItemClicked(qhm.treeDataProvider.allHistory[0]);
|
||||
expect(showViewStub).toBeCalledWith(rawQueryHistory[0].variantAnalysis.id);
|
||||
});
|
||||
|
||||
it("should get the query text", async () => {
|
||||
await qhm.readQueryHistory();
|
||||
await qhm.handleShowQueryText(qhm.treeDataProvider.allHistory[0], []);
|
||||
await qhm.handleShowQueryText(qhm.treeDataProvider.allHistory[0]);
|
||||
|
||||
expect(openQueryTextSpy).toHaveBeenCalledWith(
|
||||
rawQueryHistory[0].variantAnalysis.id,
|
||||
|
||||
Reference in New Issue
Block a user