Merge remote-tracking branch 'origin/main' into koesie10/restart-typed-command
This commit is contained in:
@@ -89,7 +89,7 @@ export type ProgressTaskWithArgs<R> = (
|
|||||||
* @param args arguments passed to this task passed on from
|
* @param args arguments passed to this task passed on from
|
||||||
* `commands.registerCommand`.
|
* `commands.registerCommand`.
|
||||||
*/
|
*/
|
||||||
type NoProgressTask = (...args: any[]) => Promise<any>;
|
export type NoProgressTask = (...args: any[]) => Promise<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This mediates between the kind of progress callbacks we want to
|
* This mediates between the kind of progress callbacks we want to
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { CommandManager } from "../packages/commands";
|
import type { CommandManager } from "../packages/commands";
|
||||||
import type { Uri } from "vscode";
|
import type { Uri, Range } from "vscode";
|
||||||
import type { DbTreeViewItem } from "../databases/ui/db-tree-view-item";
|
import type { DbTreeViewItem } from "../databases/ui/db-tree-view-item";
|
||||||
import type { DatabaseItem } from "../local-databases";
|
import type { DatabaseItem } from "../local-databases";
|
||||||
import type { QueryHistoryInfo } from "../query-history/query-history-info";
|
import type { QueryHistoryInfo } from "../query-history/query-history-info";
|
||||||
@@ -31,6 +31,21 @@ export type BaseCommands = {
|
|||||||
"codeQL.restartQueryServer": () => Promise<void>;
|
"codeQL.restartQueryServer": () => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Commands used for running local queries
|
||||||
|
export type LocalQueryCommands = {
|
||||||
|
"codeQL.runQuery": (uri?: Uri) => Promise<void>;
|
||||||
|
"codeQL.runQueryContextEditor": (uri?: Uri) => Promise<void>;
|
||||||
|
"codeQL.runQueryOnMultipleDatabases": (uri?: Uri) => Promise<void>;
|
||||||
|
"codeQL.runQueryOnMultipleDatabasesContextEditor": (
|
||||||
|
uri?: Uri,
|
||||||
|
) => Promise<void>;
|
||||||
|
"codeQL.runQueries": SelectionCommandFunction<Uri>;
|
||||||
|
"codeQL.quickEval": (uri: Uri) => Promise<void>;
|
||||||
|
"codeQL.quickEvalContextEditor": (uri: Uri) => Promise<void>;
|
||||||
|
"codeQL.codeLensQuickEval": (uri: Uri, range: Range) => Promise<void>;
|
||||||
|
"codeQL.quickQuery": () => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
// Commands used for the query history panel
|
// Commands used for the query history panel
|
||||||
export type QueryHistoryCommands = {
|
export type QueryHistoryCommands = {
|
||||||
// Commands in the "navigation" group
|
// Commands in the "navigation" group
|
||||||
@@ -65,27 +80,42 @@ export type QueryHistoryCommands = {
|
|||||||
|
|
||||||
// Commands used for the local databases panel
|
// Commands used for the local databases panel
|
||||||
export type LocalDatabasesCommands = {
|
export type LocalDatabasesCommands = {
|
||||||
"codeQL.setCurrentDatabase": (uri: Uri) => Promise<void>;
|
// Command palette commands
|
||||||
"codeQL.setDefaultTourDatabase": () => Promise<void>;
|
"codeQL.chooseDatabaseFolder": () => Promise<void>;
|
||||||
|
"codeQL.chooseDatabaseArchive": () => Promise<void>;
|
||||||
|
"codeQL.chooseDatabaseInternet": () => Promise<void>;
|
||||||
|
"codeQL.chooseDatabaseGithub": () => Promise<void>;
|
||||||
"codeQL.upgradeCurrentDatabase": () => Promise<void>;
|
"codeQL.upgradeCurrentDatabase": () => Promise<void>;
|
||||||
"codeQL.clearCache": () => Promise<void>;
|
"codeQL.clearCache": () => Promise<void>;
|
||||||
|
|
||||||
|
// Explorer context menu
|
||||||
|
"codeQL.setCurrentDatabase": (uri: Uri) => Promise<void>;
|
||||||
|
|
||||||
|
// Database panel view title commands
|
||||||
"codeQLDatabases.chooseDatabaseFolder": () => Promise<void>;
|
"codeQLDatabases.chooseDatabaseFolder": () => Promise<void>;
|
||||||
"codeQLDatabases.chooseDatabaseArchive": () => Promise<void>;
|
"codeQLDatabases.chooseDatabaseArchive": () => Promise<void>;
|
||||||
"codeQLDatabases.chooseDatabaseInternet": () => Promise<void>;
|
"codeQLDatabases.chooseDatabaseInternet": () => Promise<void>;
|
||||||
"codeQLDatabases.chooseDatabaseGithub": () => Promise<void>;
|
"codeQLDatabases.chooseDatabaseGithub": () => Promise<void>;
|
||||||
|
"codeQLDatabases.sortByName": () => Promise<void>;
|
||||||
|
"codeQLDatabases.sortByDateAdded": () => Promise<void>;
|
||||||
|
|
||||||
|
// Database panel context menu
|
||||||
"codeQLDatabases.setCurrentDatabase": (
|
"codeQLDatabases.setCurrentDatabase": (
|
||||||
databaseItem: DatabaseItem,
|
databaseItem: DatabaseItem,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
"codeQLDatabases.sortByName": () => Promise<void>;
|
|
||||||
"codeQLDatabases.sortByDateAdded": () => Promise<void>;
|
|
||||||
"codeQLDatabases.removeOrphanedDatabases": () => Promise<void>;
|
|
||||||
|
|
||||||
|
// Database panel selection commands
|
||||||
"codeQLDatabases.removeDatabase": SelectionCommandFunction<DatabaseItem>;
|
"codeQLDatabases.removeDatabase": SelectionCommandFunction<DatabaseItem>;
|
||||||
"codeQLDatabases.upgradeDatabase": SelectionCommandFunction<DatabaseItem>;
|
"codeQLDatabases.upgradeDatabase": SelectionCommandFunction<DatabaseItem>;
|
||||||
"codeQLDatabases.renameDatabase": SelectionCommandFunction<DatabaseItem>;
|
"codeQLDatabases.renameDatabase": SelectionCommandFunction<DatabaseItem>;
|
||||||
"codeQLDatabases.openDatabaseFolder": SelectionCommandFunction<DatabaseItem>;
|
"codeQLDatabases.openDatabaseFolder": SelectionCommandFunction<DatabaseItem>;
|
||||||
"codeQLDatabases.addDatabaseSource": SelectionCommandFunction<DatabaseItem>;
|
"codeQLDatabases.addDatabaseSource": SelectionCommandFunction<DatabaseItem>;
|
||||||
|
|
||||||
|
// Codespace template commands
|
||||||
|
"codeQL.setDefaultTourDatabase": () => Promise<void>;
|
||||||
|
|
||||||
|
// Internal commands
|
||||||
|
"codeQLDatabases.removeOrphanedDatabases": () => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Commands tied to variant analysis
|
// Commands tied to variant analysis
|
||||||
@@ -110,10 +140,19 @@ export type DatabasePanelCommands = {
|
|||||||
"codeQLVariantAnalysisRepositories.removeItemContextMenu": SingleSelectionCommandFunction<DbTreeViewItem>;
|
"codeQLVariantAnalysisRepositories.removeItemContextMenu": SingleSelectionCommandFunction<DbTreeViewItem>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EvalLogViewerCommands = {
|
||||||
|
"codeQLEvalLogViewer.clear": () => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
export type AllCommands = BaseCommands &
|
export type AllCommands = BaseCommands &
|
||||||
QueryHistoryCommands &
|
QueryHistoryCommands &
|
||||||
LocalDatabasesCommands &
|
LocalDatabasesCommands &
|
||||||
VariantAnalysisCommands &
|
VariantAnalysisCommands &
|
||||||
DatabasePanelCommands;
|
DatabasePanelCommands &
|
||||||
|
EvalLogViewerCommands;
|
||||||
|
|
||||||
export type AppCommandManager = CommandManager<AllCommands>;
|
export type AppCommandManager = CommandManager<AllCommands>;
|
||||||
|
|
||||||
|
// Separate command manager because it uses a different logger
|
||||||
|
export type QueryServerCommands = LocalQueryCommands;
|
||||||
|
export type QueryServerCommandManager = CommandManager<QueryServerCommands>;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { commands } from "vscode";
|
import { commands } from "vscode";
|
||||||
import { commandRunner } from "../../commandRunner";
|
import { commandRunner, NoProgressTask } from "../../commandRunner";
|
||||||
import { CommandFunction, CommandManager } from "../../packages/commands";
|
import { CommandFunction, CommandManager } from "../../packages/commands";
|
||||||
|
import { OutputChannelLogger } from "../logging";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a command manager for VSCode, wrapping the commandRunner
|
* Create a command manager for VSCode, wrapping the commandRunner
|
||||||
@@ -8,8 +9,10 @@ import { CommandFunction, CommandManager } from "../../packages/commands";
|
|||||||
*/
|
*/
|
||||||
export function createVSCodeCommandManager<
|
export function createVSCodeCommandManager<
|
||||||
Commands extends Record<string, CommandFunction>,
|
Commands extends Record<string, CommandFunction>,
|
||||||
>(): CommandManager<Commands> {
|
>(outputLogger?: OutputChannelLogger): CommandManager<Commands> {
|
||||||
return new CommandManager(commandRunner, wrapExecuteCommand);
|
return new CommandManager((commandId, task: NoProgressTask) => {
|
||||||
|
return commandRunner(commandId, task, outputLogger);
|
||||||
|
}, wrapExecuteCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,21 +3,23 @@ import { VSCodeCredentials } from "../../authentication";
|
|||||||
import { Disposable } from "../../pure/disposable-object";
|
import { Disposable } from "../../pure/disposable-object";
|
||||||
import { App, AppMode } from "../app";
|
import { App, AppMode } from "../app";
|
||||||
import { AppEventEmitter } from "../events";
|
import { AppEventEmitter } from "../events";
|
||||||
import { extLogger, Logger } from "../logging";
|
import { extLogger, Logger, queryServerLogger } from "../logging";
|
||||||
import { Memento } from "../memento";
|
import { Memento } from "../memento";
|
||||||
import { VSCodeAppEventEmitter } from "./events";
|
import { VSCodeAppEventEmitter } from "./events";
|
||||||
import { AppCommandManager } from "../commands";
|
import { AppCommandManager, QueryServerCommandManager } from "../commands";
|
||||||
import { createVSCodeCommandManager } from "./commands";
|
import { createVSCodeCommandManager } from "./commands";
|
||||||
|
|
||||||
export class ExtensionApp implements App {
|
export class ExtensionApp implements App {
|
||||||
public readonly credentials: VSCodeCredentials;
|
public readonly credentials: VSCodeCredentials;
|
||||||
public readonly commands: AppCommandManager;
|
public readonly commands: AppCommandManager;
|
||||||
|
public readonly queryServerCommands: QueryServerCommandManager;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
public readonly extensionContext: vscode.ExtensionContext,
|
public readonly extensionContext: vscode.ExtensionContext,
|
||||||
) {
|
) {
|
||||||
this.credentials = new VSCodeCredentials();
|
this.credentials = new VSCodeCredentials();
|
||||||
this.commands = createVSCodeCommandManager();
|
this.commands = createVSCodeCommandManager();
|
||||||
|
this.queryServerCommands = createVSCodeCommandManager(queryServerLogger);
|
||||||
extensionContext.subscriptions.push(this.commands);
|
extensionContext.subscriptions.push(this.commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import {
|
|||||||
EventEmitter,
|
EventEmitter,
|
||||||
TreeItemCollapsibleState,
|
TreeItemCollapsibleState,
|
||||||
} from "vscode";
|
} from "vscode";
|
||||||
import { commandRunner } from "./commandRunner";
|
|
||||||
import { DisposableObject } from "./pure/disposable-object";
|
import { DisposableObject } from "./pure/disposable-object";
|
||||||
import { showAndLogExceptionWithTelemetry } from "./helpers";
|
import { showAndLogExceptionWithTelemetry } from "./helpers";
|
||||||
import { asError, getErrorMessage } from "./pure/helpers-pure";
|
import { asError, getErrorMessage } from "./pure/helpers-pure";
|
||||||
import { redactableError } from "./pure/errors";
|
import { redactableError } from "./pure/errors";
|
||||||
|
import { EvalLogViewerCommands } from "./common/commands";
|
||||||
|
|
||||||
export interface EvalLogTreeItem {
|
export interface EvalLogTreeItem {
|
||||||
label?: string;
|
label?: string;
|
||||||
@@ -80,11 +80,12 @@ export class EvalLogViewer extends DisposableObject {
|
|||||||
|
|
||||||
this.push(this.treeView);
|
this.push(this.treeView);
|
||||||
this.push(this.treeDataProvider);
|
this.push(this.treeDataProvider);
|
||||||
this.push(
|
}
|
||||||
commandRunner("codeQLEvalLogViewer.clear", async () => {
|
|
||||||
this.clear();
|
public getCommands(): EvalLogViewerCommands {
|
||||||
}),
|
return {
|
||||||
);
|
"codeQLEvalLogViewer.clear": async () => this.clear(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private clear(): void {
|
private clear(): void {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import "source-map-support/register";
|
import "source-map-support/register";
|
||||||
import {
|
import {
|
||||||
CancellationToken,
|
CancellationToken,
|
||||||
CancellationTokenSource,
|
|
||||||
commands,
|
commands,
|
||||||
Disposable,
|
Disposable,
|
||||||
env,
|
env,
|
||||||
@@ -9,8 +8,6 @@ import {
|
|||||||
extensions,
|
extensions,
|
||||||
languages,
|
languages,
|
||||||
ProgressLocation,
|
ProgressLocation,
|
||||||
QuickPickItem,
|
|
||||||
Range,
|
|
||||||
Uri,
|
Uri,
|
||||||
version as vscodeVersion,
|
version as vscodeVersion,
|
||||||
window as Window,
|
window as Window,
|
||||||
@@ -36,12 +33,11 @@ import {
|
|||||||
CliConfigListener,
|
CliConfigListener,
|
||||||
DistributionConfigListener,
|
DistributionConfigListener,
|
||||||
joinOrderWarningThreshold,
|
joinOrderWarningThreshold,
|
||||||
MAX_QUERIES,
|
|
||||||
QueryHistoryConfigListener,
|
QueryHistoryConfigListener,
|
||||||
QueryServerConfigListener,
|
QueryServerConfigListener,
|
||||||
} from "./config";
|
} from "./config";
|
||||||
import { install } from "./languageSupport";
|
import { install } from "./languageSupport";
|
||||||
import { DatabaseItem, DatabaseManager } from "./local-databases";
|
import { DatabaseManager } from "./local-databases";
|
||||||
import { DatabaseUI } from "./local-databases-ui";
|
import { DatabaseUI } from "./local-databases-ui";
|
||||||
import {
|
import {
|
||||||
TemplatePrintAstProvider,
|
TemplatePrintAstProvider,
|
||||||
@@ -60,7 +56,6 @@ import {
|
|||||||
GithubRateLimitedError,
|
GithubRateLimitedError,
|
||||||
} from "./distribution";
|
} from "./distribution";
|
||||||
import {
|
import {
|
||||||
findLanguage,
|
|
||||||
showAndLogErrorMessage,
|
showAndLogErrorMessage,
|
||||||
showAndLogExceptionWithTelemetry,
|
showAndLogExceptionWithTelemetry,
|
||||||
showAndLogInformationMessage,
|
showAndLogInformationMessage,
|
||||||
@@ -87,20 +82,17 @@ import {
|
|||||||
queryServerLogger,
|
queryServerLogger,
|
||||||
} from "./common";
|
} from "./common";
|
||||||
import { QueryHistoryManager } from "./query-history/query-history-manager";
|
import { QueryHistoryManager } from "./query-history/query-history-manager";
|
||||||
import { CompletedLocalQueryInfo, LocalQueryInfo } from "./query-results";
|
import { CompletedLocalQueryInfo } from "./query-results";
|
||||||
import { QueryServerClient as LegacyQueryServerClient } from "./legacy-query-server/queryserver-client";
|
import { QueryServerClient as LegacyQueryServerClient } from "./legacy-query-server/queryserver-client";
|
||||||
import { QueryServerClient } from "./query-server/queryserver-client";
|
import { QueryServerClient } from "./query-server/queryserver-client";
|
||||||
import { displayQuickQuery } from "./quick-query";
|
|
||||||
import { QLTestAdapterFactory } from "./test-adapter";
|
import { QLTestAdapterFactory } from "./test-adapter";
|
||||||
import { TestUIService } from "./test-ui";
|
import { TestUIService } from "./test-ui";
|
||||||
import { CompareView } from "./compare/compare-view";
|
import { CompareView } from "./compare/compare-view";
|
||||||
import { gatherQlFiles } from "./pure/files";
|
|
||||||
import { initializeTelemetry } from "./telemetry";
|
import { initializeTelemetry } from "./telemetry";
|
||||||
import {
|
import {
|
||||||
commandRunner,
|
commandRunner,
|
||||||
commandRunnerWithProgress,
|
commandRunnerWithProgress,
|
||||||
ProgressCallback,
|
ProgressCallback,
|
||||||
ProgressUpdate,
|
|
||||||
withProgress,
|
withProgress,
|
||||||
} from "./commandRunner";
|
} from "./commandRunner";
|
||||||
import { CodeQlStatusBarHandler } from "./status-bar";
|
import { CodeQlStatusBarHandler } from "./status-bar";
|
||||||
@@ -114,7 +106,6 @@ import { EvalLogViewer } from "./eval-log-viewer";
|
|||||||
import { SummaryLanguageSupport } from "./log-insights/summary-language-support";
|
import { SummaryLanguageSupport } from "./log-insights/summary-language-support";
|
||||||
import { JoinOrderScannerProvider } from "./log-insights/join-order";
|
import { JoinOrderScannerProvider } from "./log-insights/join-order";
|
||||||
import { LogScannerService } from "./log-insights/log-scanner-service";
|
import { LogScannerService } from "./log-insights/log-scanner-service";
|
||||||
import { createInitialQueryInfo } from "./run-queries-shared";
|
|
||||||
import { LegacyQueryRunner } from "./legacy-query-server/legacyRunner";
|
import { LegacyQueryRunner } from "./legacy-query-server/legacyRunner";
|
||||||
import { NewQueryRunner } from "./query-server/query-runner";
|
import { NewQueryRunner } from "./query-server/query-runner";
|
||||||
import { QueryRunner } from "./queryRunner";
|
import { QueryRunner } from "./queryRunner";
|
||||||
@@ -134,7 +125,16 @@ import { DbModule } from "./databases/db-module";
|
|||||||
import { redactableError } from "./pure/errors";
|
import { redactableError } from "./pure/errors";
|
||||||
import { QueryHistoryDirs } from "./query-history/query-history-dirs";
|
import { QueryHistoryDirs } from "./query-history/query-history-dirs";
|
||||||
import { DirResult } from "tmp";
|
import { DirResult } from "tmp";
|
||||||
import { AllCommands, BaseCommands } from "./common/commands";
|
import {
|
||||||
|
AllCommands,
|
||||||
|
BaseCommands,
|
||||||
|
QueryServerCommands,
|
||||||
|
} from "./common/commands";
|
||||||
|
import {
|
||||||
|
compileAndRunQuery,
|
||||||
|
getLocalQueryCommands,
|
||||||
|
showResultsForCompletedQuery,
|
||||||
|
} from "./local-queries";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* extension.ts
|
* extension.ts
|
||||||
@@ -244,10 +244,6 @@ interface DistributionUpdateConfig {
|
|||||||
allowAutoUpdating: boolean;
|
allowAutoUpdating: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DatabaseQuickPickItem extends QuickPickItem {
|
|
||||||
databaseItem: DatabaseItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
const shouldUpdateOnNextActivationKey = "shouldUpdateOnNextActivation";
|
const shouldUpdateOnNextActivationKey = "shouldUpdateOnNextActivation";
|
||||||
|
|
||||||
const codeQlVersionRange = DEFAULT_DISTRIBUTION_VERSION_RANGE;
|
const codeQlVersionRange = DEFAULT_DISTRIBUTION_VERSION_RANGE;
|
||||||
@@ -818,300 +814,6 @@ async function activateWithInstalledDistribution(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void extLogger.log("Registering top-level command palette commands.");
|
void extLogger.log("Registering top-level command palette commands.");
|
||||||
ctx.subscriptions.push(
|
|
||||||
commandRunnerWithProgress(
|
|
||||||
"codeQL.runQuery",
|
|
||||||
async (
|
|
||||||
progress: ProgressCallback,
|
|
||||||
token: CancellationToken,
|
|
||||||
uri: Uri | undefined,
|
|
||||||
) =>
|
|
||||||
await compileAndRunQuery(
|
|
||||||
qs,
|
|
||||||
qhm,
|
|
||||||
databaseUI,
|
|
||||||
localQueryResultsView,
|
|
||||||
queryStorageDir,
|
|
||||||
false,
|
|
||||||
uri,
|
|
||||||
progress,
|
|
||||||
token,
|
|
||||||
undefined,
|
|
||||||
),
|
|
||||||
{
|
|
||||||
title: "Running query",
|
|
||||||
cancellable: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Open the query server logger on error since that's usually where the interesting errors appear.
|
|
||||||
queryServerLogger,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Since we are tracking extension usage through commands, this command mirrors the runQuery command
|
|
||||||
ctx.subscriptions.push(
|
|
||||||
commandRunnerWithProgress(
|
|
||||||
"codeQL.runQueryContextEditor",
|
|
||||||
async (
|
|
||||||
progress: ProgressCallback,
|
|
||||||
token: CancellationToken,
|
|
||||||
uri: Uri | undefined,
|
|
||||||
) =>
|
|
||||||
await compileAndRunQuery(
|
|
||||||
qs,
|
|
||||||
qhm,
|
|
||||||
databaseUI,
|
|
||||||
localQueryResultsView,
|
|
||||||
queryStorageDir,
|
|
||||||
false,
|
|
||||||
uri,
|
|
||||||
progress,
|
|
||||||
token,
|
|
||||||
undefined,
|
|
||||||
),
|
|
||||||
{
|
|
||||||
title: "Running query",
|
|
||||||
cancellable: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Open the query server logger on error since that's usually where the interesting errors appear.
|
|
||||||
queryServerLogger,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
ctx.subscriptions.push(
|
|
||||||
commandRunnerWithProgress(
|
|
||||||
"codeQL.runQueryOnMultipleDatabases",
|
|
||||||
async (
|
|
||||||
progress: ProgressCallback,
|
|
||||||
token: CancellationToken,
|
|
||||||
uri: Uri | undefined,
|
|
||||||
) =>
|
|
||||||
await compileAndRunQueryOnMultipleDatabases(
|
|
||||||
cliServer,
|
|
||||||
qs,
|
|
||||||
qhm,
|
|
||||||
dbm,
|
|
||||||
databaseUI,
|
|
||||||
localQueryResultsView,
|
|
||||||
queryStorageDir,
|
|
||||||
progress,
|
|
||||||
token,
|
|
||||||
uri,
|
|
||||||
),
|
|
||||||
{
|
|
||||||
title: "Running query on selected databases",
|
|
||||||
cancellable: true,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
// Since we are tracking extension usage through commands, this command mirrors the runQueryOnMultipleDatabases command
|
|
||||||
ctx.subscriptions.push(
|
|
||||||
commandRunnerWithProgress(
|
|
||||||
"codeQL.runQueryOnMultipleDatabasesContextEditor",
|
|
||||||
async (
|
|
||||||
progress: ProgressCallback,
|
|
||||||
token: CancellationToken,
|
|
||||||
uri: Uri | undefined,
|
|
||||||
) =>
|
|
||||||
await compileAndRunQueryOnMultipleDatabases(
|
|
||||||
cliServer,
|
|
||||||
qs,
|
|
||||||
qhm,
|
|
||||||
dbm,
|
|
||||||
databaseUI,
|
|
||||||
localQueryResultsView,
|
|
||||||
queryStorageDir,
|
|
||||||
progress,
|
|
||||||
token,
|
|
||||||
uri,
|
|
||||||
),
|
|
||||||
{
|
|
||||||
title: "Running query on selected databases",
|
|
||||||
cancellable: true,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
ctx.subscriptions.push(
|
|
||||||
commandRunnerWithProgress(
|
|
||||||
"codeQL.runQueries",
|
|
||||||
async (
|
|
||||||
progress: ProgressCallback,
|
|
||||||
token: CancellationToken,
|
|
||||||
_: Uri | undefined,
|
|
||||||
multi: Uri[],
|
|
||||||
) => {
|
|
||||||
const maxQueryCount = MAX_QUERIES.getValue() as number;
|
|
||||||
const [files, dirFound] = await gatherQlFiles(
|
|
||||||
multi.map((uri) => uri.fsPath),
|
|
||||||
);
|
|
||||||
if (files.length > maxQueryCount) {
|
|
||||||
throw new Error(
|
|
||||||
`You tried to run ${files.length} queries, but the maximum is ${maxQueryCount}. Try selecting fewer queries or changing the 'codeQL.runningQueries.maxQueries' setting.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// warn user and display selected files when a directory is selected because some ql
|
|
||||||
// files may be hidden from the user.
|
|
||||||
if (dirFound) {
|
|
||||||
const fileString = files.map((file) => basename(file)).join(", ");
|
|
||||||
const res = await showBinaryChoiceDialog(
|
|
||||||
`You are about to run ${files.length} queries: ${fileString} Do you want to continue?`,
|
|
||||||
);
|
|
||||||
if (!res) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const queryUris = files.map((path) => Uri.parse(`file:${path}`, true));
|
|
||||||
|
|
||||||
// Use a wrapped progress so that messages appear with the queries remaining in it.
|
|
||||||
let queriesRemaining = queryUris.length;
|
|
||||||
function wrappedProgress(update: ProgressUpdate) {
|
|
||||||
const message =
|
|
||||||
queriesRemaining > 1
|
|
||||||
? `${queriesRemaining} remaining. ${update.message}`
|
|
||||||
: update.message;
|
|
||||||
progress({
|
|
||||||
...update,
|
|
||||||
message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
wrappedProgress({
|
|
||||||
maxStep: queryUris.length,
|
|
||||||
step: queryUris.length - queriesRemaining,
|
|
||||||
message: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
queryUris.map(async (uri) =>
|
|
||||||
compileAndRunQuery(
|
|
||||||
qs,
|
|
||||||
qhm,
|
|
||||||
databaseUI,
|
|
||||||
localQueryResultsView,
|
|
||||||
queryStorageDir,
|
|
||||||
false,
|
|
||||||
uri,
|
|
||||||
wrappedProgress,
|
|
||||||
token,
|
|
||||||
undefined,
|
|
||||||
).then(() => queriesRemaining--),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Running queries",
|
|
||||||
cancellable: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Open the query server logger on error since that's usually where the interesting errors appear.
|
|
||||||
queryServerLogger,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.subscriptions.push(
|
|
||||||
commandRunnerWithProgress(
|
|
||||||
"codeQL.quickEval",
|
|
||||||
async (
|
|
||||||
progress: ProgressCallback,
|
|
||||||
token: CancellationToken,
|
|
||||||
uri: Uri | undefined,
|
|
||||||
) =>
|
|
||||||
await compileAndRunQuery(
|
|
||||||
qs,
|
|
||||||
qhm,
|
|
||||||
databaseUI,
|
|
||||||
localQueryResultsView,
|
|
||||||
queryStorageDir,
|
|
||||||
true,
|
|
||||||
uri,
|
|
||||||
progress,
|
|
||||||
token,
|
|
||||||
undefined,
|
|
||||||
),
|
|
||||||
{
|
|
||||||
title: "Running query",
|
|
||||||
cancellable: true,
|
|
||||||
},
|
|
||||||
// Open the query server logger on error since that's usually where the interesting errors appear.
|
|
||||||
queryServerLogger,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Since we are tracking extension usage through commands, this command mirrors the "codeQL.quickEval" command
|
|
||||||
ctx.subscriptions.push(
|
|
||||||
commandRunnerWithProgress(
|
|
||||||
"codeQL.quickEvalContextEditor",
|
|
||||||
async (
|
|
||||||
progress: ProgressCallback,
|
|
||||||
token: CancellationToken,
|
|
||||||
uri: Uri | undefined,
|
|
||||||
) =>
|
|
||||||
await compileAndRunQuery(
|
|
||||||
qs,
|
|
||||||
qhm,
|
|
||||||
databaseUI,
|
|
||||||
localQueryResultsView,
|
|
||||||
queryStorageDir,
|
|
||||||
true,
|
|
||||||
uri,
|
|
||||||
progress,
|
|
||||||
token,
|
|
||||||
undefined,
|
|
||||||
),
|
|
||||||
{
|
|
||||||
title: "Running query",
|
|
||||||
cancellable: true,
|
|
||||||
},
|
|
||||||
// Open the query server logger on error since that's usually where the interesting errors appear.
|
|
||||||
queryServerLogger,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.subscriptions.push(
|
|
||||||
commandRunnerWithProgress(
|
|
||||||
"codeQL.codeLensQuickEval",
|
|
||||||
async (
|
|
||||||
progress: ProgressCallback,
|
|
||||||
token: CancellationToken,
|
|
||||||
uri: Uri,
|
|
||||||
range: Range,
|
|
||||||
) =>
|
|
||||||
await compileAndRunQuery(
|
|
||||||
qs,
|
|
||||||
qhm,
|
|
||||||
databaseUI,
|
|
||||||
localQueryResultsView,
|
|
||||||
queryStorageDir,
|
|
||||||
true,
|
|
||||||
uri,
|
|
||||||
progress,
|
|
||||||
token,
|
|
||||||
undefined,
|
|
||||||
range,
|
|
||||||
),
|
|
||||||
{
|
|
||||||
title: "Running query",
|
|
||||||
cancellable: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Open the query server logger on error since that's usually where the interesting errors appear.
|
|
||||||
queryServerLogger,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.subscriptions.push(
|
|
||||||
commandRunnerWithProgress(
|
|
||||||
"codeQL.quickQuery",
|
|
||||||
async (progress: ProgressCallback, token: CancellationToken) =>
|
|
||||||
displayQuickQuery(ctx, cliServer, databaseUI, progress, token),
|
|
||||||
{
|
|
||||||
title: "Run Quick Query",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Open the query server logger on error since that's usually where the interesting errors appear.
|
|
||||||
queryServerLogger,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
const allCommands: AllCommands = {
|
const allCommands: AllCommands = {
|
||||||
...getCommands(cliServer, qs),
|
...getCommands(cliServer, qs),
|
||||||
@@ -1119,12 +821,33 @@ async function activateWithInstalledDistribution(
|
|||||||
...variantAnalysisManager.getCommands(),
|
...variantAnalysisManager.getCommands(),
|
||||||
...databaseUI.getCommands(),
|
...databaseUI.getCommands(),
|
||||||
...dbModule.getCommands(),
|
...dbModule.getCommands(),
|
||||||
|
...evalLogViewer.getCommands(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const [commandName, command] of Object.entries(allCommands)) {
|
for (const [commandName, command] of Object.entries(allCommands)) {
|
||||||
app.commands.register(commandName as keyof AllCommands, command);
|
app.commands.register(commandName as keyof AllCommands, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const queryServerCommands: QueryServerCommands = {
|
||||||
|
...getLocalQueryCommands({
|
||||||
|
app,
|
||||||
|
queryRunner: qs,
|
||||||
|
queryHistoryManager: qhm,
|
||||||
|
databaseManager: dbm,
|
||||||
|
cliServer,
|
||||||
|
databaseUI,
|
||||||
|
localQueryResultsView,
|
||||||
|
queryStorageDir,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [commandName, command] of Object.entries(queryServerCommands)) {
|
||||||
|
app.queryServerCommands.register(
|
||||||
|
commandName as keyof QueryServerCommands,
|
||||||
|
command,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ctx.subscriptions.push(
|
ctx.subscriptions.push(
|
||||||
commandRunner(
|
commandRunner(
|
||||||
"codeQL.copyVariantAnalysisRepoList",
|
"codeQL.copyVariantAnalysisRepoList",
|
||||||
@@ -1229,49 +952,6 @@ async function activateWithInstalledDistribution(
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.subscriptions.push(
|
|
||||||
commandRunnerWithProgress(
|
|
||||||
"codeQL.chooseDatabaseFolder",
|
|
||||||
(progress: ProgressCallback, token: CancellationToken) =>
|
|
||||||
databaseUI.chooseDatabaseFolder(progress, token),
|
|
||||||
{
|
|
||||||
title: "Choose a Database from a Folder",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
ctx.subscriptions.push(
|
|
||||||
commandRunnerWithProgress(
|
|
||||||
"codeQL.chooseDatabaseArchive",
|
|
||||||
(progress: ProgressCallback, token: CancellationToken) =>
|
|
||||||
databaseUI.chooseDatabaseArchive(progress, token),
|
|
||||||
{
|
|
||||||
title: "Choose a Database from an Archive",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
ctx.subscriptions.push(
|
|
||||||
commandRunnerWithProgress(
|
|
||||||
"codeQL.chooseDatabaseGithub",
|
|
||||||
async (progress: ProgressCallback, token: CancellationToken) => {
|
|
||||||
await databaseUI.chooseDatabaseGithub(progress, token);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Adding database from GitHub",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
ctx.subscriptions.push(
|
|
||||||
commandRunnerWithProgress(
|
|
||||||
"codeQL.chooseDatabaseInternet",
|
|
||||||
(progress: ProgressCallback, token: CancellationToken) =>
|
|
||||||
databaseUI.chooseDatabaseInternet(progress, token),
|
|
||||||
|
|
||||||
{
|
|
||||||
title: "Adding database from URL",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.subscriptions.push(
|
ctx.subscriptions.push(
|
||||||
commandRunner("codeQL.copyVersion", async () => {
|
commandRunner("codeQL.copyVersion", async () => {
|
||||||
const text = `CodeQL extension version: ${
|
const text = `CodeQL extension version: ${
|
||||||
@@ -1609,160 +1289,6 @@ async function showResultsForComparison(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showResultsForCompletedQuery(
|
|
||||||
localQueryResultsView: ResultsView,
|
|
||||||
query: CompletedLocalQueryInfo,
|
|
||||||
forceReveal: WebviewReveal,
|
|
||||||
): Promise<void> {
|
|
||||||
await localQueryResultsView.showResults(query, forceReveal, false);
|
|
||||||
}
|
|
||||||
async function compileAndRunQuery(
|
|
||||||
qs: QueryRunner,
|
|
||||||
qhm: QueryHistoryManager,
|
|
||||||
databaseUI: DatabaseUI,
|
|
||||||
localQueryResultsView: ResultsView,
|
|
||||||
queryStorageDir: string,
|
|
||||||
quickEval: boolean,
|
|
||||||
selectedQuery: Uri | undefined,
|
|
||||||
progress: ProgressCallback,
|
|
||||||
token: CancellationToken,
|
|
||||||
databaseItem: DatabaseItem | undefined,
|
|
||||||
range?: Range,
|
|
||||||
): Promise<void> {
|
|
||||||
if (qs !== undefined) {
|
|
||||||
// If no databaseItem is specified, use the database currently selected in the Databases UI
|
|
||||||
databaseItem =
|
|
||||||
databaseItem || (await databaseUI.getDatabaseItem(progress, token));
|
|
||||||
if (databaseItem === undefined) {
|
|
||||||
throw new Error("Can't run query without a selected database");
|
|
||||||
}
|
|
||||||
const databaseInfo = {
|
|
||||||
name: databaseItem.name,
|
|
||||||
databaseUri: databaseItem.databaseUri.toString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// handle cancellation from the history view.
|
|
||||||
const source = new CancellationTokenSource();
|
|
||||||
token.onCancellationRequested(() => source.cancel());
|
|
||||||
|
|
||||||
const initialInfo = await createInitialQueryInfo(
|
|
||||||
selectedQuery,
|
|
||||||
databaseInfo,
|
|
||||||
quickEval,
|
|
||||||
range,
|
|
||||||
);
|
|
||||||
const item = new LocalQueryInfo(initialInfo, source);
|
|
||||||
qhm.addQuery(item);
|
|
||||||
try {
|
|
||||||
const completedQueryInfo = await qs.compileAndRunQueryAgainstDatabase(
|
|
||||||
databaseItem,
|
|
||||||
initialInfo,
|
|
||||||
queryStorageDir,
|
|
||||||
progress,
|
|
||||||
source.token,
|
|
||||||
undefined,
|
|
||||||
item,
|
|
||||||
);
|
|
||||||
qhm.completeQuery(item, completedQueryInfo);
|
|
||||||
await showResultsForCompletedQuery(
|
|
||||||
localQueryResultsView,
|
|
||||||
item as CompletedLocalQueryInfo,
|
|
||||||
WebviewReveal.Forced,
|
|
||||||
);
|
|
||||||
// Note we must update the query history view after showing results as the
|
|
||||||
// display and sorting might depend on the number of results
|
|
||||||
} catch (e) {
|
|
||||||
const err = asError(e);
|
|
||||||
err.message = `Error running query: ${err.message}`;
|
|
||||||
item.failureReason = err.message;
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
await qhm.refreshTreeView();
|
|
||||||
source.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function compileAndRunQueryOnMultipleDatabases(
|
|
||||||
cliServer: CodeQLCliServer,
|
|
||||||
qs: QueryRunner,
|
|
||||||
qhm: QueryHistoryManager,
|
|
||||||
dbm: DatabaseManager,
|
|
||||||
databaseUI: DatabaseUI,
|
|
||||||
localQueryResultsView: ResultsView,
|
|
||||||
queryStorageDir: string,
|
|
||||||
progress: ProgressCallback,
|
|
||||||
token: CancellationToken,
|
|
||||||
uri: Uri | undefined,
|
|
||||||
): Promise<void> {
|
|
||||||
let filteredDBs = dbm.databaseItems;
|
|
||||||
if (filteredDBs.length === 0) {
|
|
||||||
void showAndLogErrorMessage(
|
|
||||||
"No databases found. Please add a suitable database to your workspace.",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If possible, only show databases with the right language (otherwise show all databases).
|
|
||||||
const queryLanguage = await findLanguage(cliServer, uri);
|
|
||||||
if (queryLanguage) {
|
|
||||||
filteredDBs = dbm.databaseItems.filter(
|
|
||||||
(db) => db.language === queryLanguage,
|
|
||||||
);
|
|
||||||
if (filteredDBs.length === 0) {
|
|
||||||
void showAndLogErrorMessage(
|
|
||||||
`No databases found for language ${queryLanguage}. Please add a suitable database to your workspace.`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const quickPickItems = filteredDBs.map<DatabaseQuickPickItem>((dbItem) => ({
|
|
||||||
databaseItem: dbItem,
|
|
||||||
label: dbItem.name,
|
|
||||||
description: dbItem.language,
|
|
||||||
}));
|
|
||||||
/**
|
|
||||||
* Databases that were selected in the quick pick menu.
|
|
||||||
*/
|
|
||||||
const quickpick = await window.showQuickPick<DatabaseQuickPickItem>(
|
|
||||||
quickPickItems,
|
|
||||||
{ canPickMany: true, ignoreFocusOut: true },
|
|
||||||
);
|
|
||||||
if (quickpick !== undefined) {
|
|
||||||
// Collect all skipped databases and display them at the end (instead of popping up individual errors)
|
|
||||||
const skippedDatabases = [];
|
|
||||||
const errors = [];
|
|
||||||
for (const item of quickpick) {
|
|
||||||
try {
|
|
||||||
await compileAndRunQuery(
|
|
||||||
qs,
|
|
||||||
qhm,
|
|
||||||
databaseUI,
|
|
||||||
localQueryResultsView,
|
|
||||||
queryStorageDir,
|
|
||||||
false,
|
|
||||||
uri,
|
|
||||||
progress,
|
|
||||||
token,
|
|
||||||
item.databaseItem,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
skippedDatabases.push(item.label);
|
|
||||||
errors.push(getErrorMessage(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (skippedDatabases.length > 0) {
|
|
||||||
void extLogger.log(`Errors:\n${errors.join("\n")}`);
|
|
||||||
void showAndLogWarningMessage(
|
|
||||||
`The following databases were skipped:\n${skippedDatabases.join(
|
|
||||||
"\n",
|
|
||||||
)}.\nFor details about the errors, see the logs.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
void showAndLogErrorMessage("No databases selected.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function previewQueryHelp(
|
async function previewQueryHelp(
|
||||||
cliServer: CodeQLCliServer,
|
cliServer: CodeQLCliServer,
|
||||||
qhelpTmpDir: DirResult,
|
qhelpTmpDir: DirResult,
|
||||||
|
|||||||
@@ -208,6 +208,13 @@ export class DatabaseUI extends DisposableObject {
|
|||||||
|
|
||||||
public getCommands(): LocalDatabasesCommands {
|
public getCommands(): LocalDatabasesCommands {
|
||||||
return {
|
return {
|
||||||
|
"codeQL.chooseDatabaseFolder":
|
||||||
|
this.handleChooseDatabaseFolderFromPalette.bind(this),
|
||||||
|
"codeQL.chooseDatabaseArchive":
|
||||||
|
this.handleChooseDatabaseArchiveFromPalette.bind(this),
|
||||||
|
"codeQL.chooseDatabaseInternet":
|
||||||
|
this.handleChooseDatabaseInternet.bind(this),
|
||||||
|
"codeQL.chooseDatabaseGithub": this.handleChooseDatabaseGithub.bind(this),
|
||||||
"codeQL.setCurrentDatabase": this.handleSetCurrentDatabase.bind(this),
|
"codeQL.setCurrentDatabase": this.handleSetCurrentDatabase.bind(this),
|
||||||
"codeQL.setDefaultTourDatabase":
|
"codeQL.setDefaultTourDatabase":
|
||||||
this.handleSetDefaultTourDatabase.bind(this),
|
this.handleSetDefaultTourDatabase.bind(this),
|
||||||
@@ -242,7 +249,7 @@ export class DatabaseUI extends DisposableObject {
|
|||||||
await this.databaseManager.setCurrentDatabaseItem(databaseItem);
|
await this.databaseManager.setCurrentDatabaseItem(databaseItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async chooseDatabaseFolder(
|
private async chooseDatabaseFolder(
|
||||||
progress: ProgressCallback,
|
progress: ProgressCallback,
|
||||||
token: CancellationToken,
|
token: CancellationToken,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@@ -268,6 +275,17 @@ export class DatabaseUI extends DisposableObject {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async handleChooseDatabaseFolderFromPalette(): Promise<void> {
|
||||||
|
return withProgress(
|
||||||
|
async (progress, token) => {
|
||||||
|
await this.chooseDatabaseFolder(progress, token);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Choose a Database from a Folder",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private async handleSetDefaultTourDatabase(): Promise<void> {
|
private async handleSetDefaultTourDatabase(): Promise<void> {
|
||||||
return withProgress(
|
return withProgress(
|
||||||
async (progress, token) => {
|
async (progress, token) => {
|
||||||
@@ -392,7 +410,7 @@ export class DatabaseUI extends DisposableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async chooseDatabaseArchive(
|
private async chooseDatabaseArchive(
|
||||||
progress: ProgressCallback,
|
progress: ProgressCallback,
|
||||||
token: CancellationToken,
|
token: CancellationToken,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@@ -418,23 +436,27 @@ export class DatabaseUI extends DisposableObject {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async chooseDatabaseInternet(
|
private async handleChooseDatabaseArchiveFromPalette(): Promise<void> {
|
||||||
progress: ProgressCallback,
|
return withProgress(
|
||||||
token: CancellationToken,
|
async (progress, token) => {
|
||||||
): Promise<DatabaseItem | undefined> {
|
await this.chooseDatabaseArchive(progress, token);
|
||||||
return await promptImportInternetDatabase(
|
},
|
||||||
this.databaseManager,
|
{
|
||||||
this.storagePath,
|
title: "Choose a Database from an Archive",
|
||||||
progress,
|
},
|
||||||
token,
|
|
||||||
this.queryServer?.cliServer,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleChooseDatabaseInternet(): Promise<void> {
|
private async handleChooseDatabaseInternet(): Promise<void> {
|
||||||
return withProgress(
|
return withProgress(
|
||||||
async (progress, token) => {
|
async (progress, token) => {
|
||||||
await this.chooseDatabaseInternet(progress, token);
|
await promptImportInternetDatabase(
|
||||||
|
this.databaseManager,
|
||||||
|
this.storagePath,
|
||||||
|
progress,
|
||||||
|
token,
|
||||||
|
this.queryServer?.cliServer,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Adding database from URL",
|
title: "Adding database from URL",
|
||||||
@@ -442,26 +464,19 @@ export class DatabaseUI extends DisposableObject {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async chooseDatabaseGithub(
|
|
||||||
progress: ProgressCallback,
|
|
||||||
token: CancellationToken,
|
|
||||||
): Promise<DatabaseItem | undefined> {
|
|
||||||
const credentials = isCanary() ? this.app.credentials : undefined;
|
|
||||||
|
|
||||||
return await promptImportGithubDatabase(
|
|
||||||
this.databaseManager,
|
|
||||||
this.storagePath,
|
|
||||||
credentials,
|
|
||||||
progress,
|
|
||||||
token,
|
|
||||||
this.queryServer?.cliServer,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleChooseDatabaseGithub(): Promise<void> {
|
private async handleChooseDatabaseGithub(): Promise<void> {
|
||||||
return withProgress(
|
return withProgress(
|
||||||
async (progress, token) => {
|
async (progress, token) => {
|
||||||
await this.chooseDatabaseGithub(progress, token);
|
const credentials = isCanary() ? this.app.credentials : undefined;
|
||||||
|
|
||||||
|
await promptImportGithubDatabase(
|
||||||
|
this.databaseManager,
|
||||||
|
this.storagePath,
|
||||||
|
credentials,
|
||||||
|
progress,
|
||||||
|
token,
|
||||||
|
this.queryServer?.cliServer,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Adding database from GitHub",
|
title: "Adding database from GitHub",
|
||||||
|
|||||||
394
extensions/ql-vscode/src/local-queries.ts
Normal file
394
extensions/ql-vscode/src/local-queries.ts
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
import {
|
||||||
|
ProgressCallback,
|
||||||
|
ProgressUpdate,
|
||||||
|
withProgress,
|
||||||
|
} from "./commandRunner";
|
||||||
|
import {
|
||||||
|
CancellationToken,
|
||||||
|
CancellationTokenSource,
|
||||||
|
QuickPickItem,
|
||||||
|
Range,
|
||||||
|
Uri,
|
||||||
|
window,
|
||||||
|
} from "vscode";
|
||||||
|
import { extLogger } from "./common";
|
||||||
|
import { MAX_QUERIES } from "./config";
|
||||||
|
import { gatherQlFiles } from "./pure/files";
|
||||||
|
import { basename } from "path";
|
||||||
|
import {
|
||||||
|
findLanguage,
|
||||||
|
showAndLogErrorMessage,
|
||||||
|
showAndLogWarningMessage,
|
||||||
|
showBinaryChoiceDialog,
|
||||||
|
} from "./helpers";
|
||||||
|
import { displayQuickQuery } from "./quick-query";
|
||||||
|
import { QueryRunner } from "./queryRunner";
|
||||||
|
import { QueryHistoryManager } from "./query-history/query-history-manager";
|
||||||
|
import { DatabaseUI } from "./local-databases-ui";
|
||||||
|
import { ResultsView } from "./interface";
|
||||||
|
import { DatabaseItem, DatabaseManager } from "./local-databases";
|
||||||
|
import { createInitialQueryInfo } from "./run-queries-shared";
|
||||||
|
import { CompletedLocalQueryInfo, LocalQueryInfo } from "./query-results";
|
||||||
|
import { WebviewReveal } from "./interface-utils";
|
||||||
|
import { asError, getErrorMessage } from "./pure/helpers-pure";
|
||||||
|
import { CodeQLCliServer } from "./cli";
|
||||||
|
import { LocalQueryCommands } from "./common/commands";
|
||||||
|
import { App } from "./common/app";
|
||||||
|
|
||||||
|
type LocalQueryOptions = {
|
||||||
|
app: App;
|
||||||
|
queryRunner: QueryRunner;
|
||||||
|
queryHistoryManager: QueryHistoryManager;
|
||||||
|
databaseManager: DatabaseManager;
|
||||||
|
cliServer: CodeQLCliServer;
|
||||||
|
databaseUI: DatabaseUI;
|
||||||
|
localQueryResultsView: ResultsView;
|
||||||
|
queryStorageDir: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getLocalQueryCommands({
|
||||||
|
app,
|
||||||
|
queryRunner,
|
||||||
|
queryHistoryManager,
|
||||||
|
databaseManager,
|
||||||
|
cliServer,
|
||||||
|
databaseUI,
|
||||||
|
localQueryResultsView,
|
||||||
|
queryStorageDir,
|
||||||
|
}: LocalQueryOptions): LocalQueryCommands {
|
||||||
|
const runQuery = async (uri: Uri | undefined) =>
|
||||||
|
withProgress(
|
||||||
|
async (progress, token) => {
|
||||||
|
await compileAndRunQuery(
|
||||||
|
queryRunner,
|
||||||
|
queryHistoryManager,
|
||||||
|
databaseUI,
|
||||||
|
localQueryResultsView,
|
||||||
|
queryStorageDir,
|
||||||
|
false,
|
||||||
|
uri,
|
||||||
|
progress,
|
||||||
|
token,
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Running query",
|
||||||
|
cancellable: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const runQueryOnMultipleDatabases = async (uri: Uri | undefined) =>
|
||||||
|
withProgress(
|
||||||
|
async (progress, token) =>
|
||||||
|
await compileAndRunQueryOnMultipleDatabases(
|
||||||
|
cliServer,
|
||||||
|
queryRunner,
|
||||||
|
queryHistoryManager,
|
||||||
|
databaseManager,
|
||||||
|
databaseUI,
|
||||||
|
localQueryResultsView,
|
||||||
|
queryStorageDir,
|
||||||
|
progress,
|
||||||
|
token,
|
||||||
|
uri,
|
||||||
|
),
|
||||||
|
{
|
||||||
|
title: "Running query on selected databases",
|
||||||
|
cancellable: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const runQueries = async (_: Uri | undefined, multi: Uri[]) =>
|
||||||
|
withProgress(
|
||||||
|
async (progress, token) => {
|
||||||
|
const maxQueryCount = MAX_QUERIES.getValue() as number;
|
||||||
|
const [files, dirFound] = await gatherQlFiles(
|
||||||
|
multi.map((uri) => uri.fsPath),
|
||||||
|
);
|
||||||
|
if (files.length > maxQueryCount) {
|
||||||
|
throw new Error(
|
||||||
|
`You tried to run ${files.length} queries, but the maximum is ${maxQueryCount}. Try selecting fewer queries or changing the 'codeQL.runningQueries.maxQueries' setting.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// warn user and display selected files when a directory is selected because some ql
|
||||||
|
// files may be hidden from the user.
|
||||||
|
if (dirFound) {
|
||||||
|
const fileString = files.map((file) => basename(file)).join(", ");
|
||||||
|
const res = await showBinaryChoiceDialog(
|
||||||
|
`You are about to run ${files.length} queries: ${fileString} Do you want to continue?`,
|
||||||
|
);
|
||||||
|
if (!res) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const queryUris = files.map((path) => Uri.parse(`file:${path}`, true));
|
||||||
|
|
||||||
|
// Use a wrapped progress so that messages appear with the queries remaining in it.
|
||||||
|
let queriesRemaining = queryUris.length;
|
||||||
|
|
||||||
|
function wrappedProgress(update: ProgressUpdate) {
|
||||||
|
const message =
|
||||||
|
queriesRemaining > 1
|
||||||
|
? `${queriesRemaining} remaining. ${update.message}`
|
||||||
|
: update.message;
|
||||||
|
progress({
|
||||||
|
...update,
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedProgress({
|
||||||
|
maxStep: queryUris.length,
|
||||||
|
step: queryUris.length - queriesRemaining,
|
||||||
|
message: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
queryUris.map(async (uri) =>
|
||||||
|
compileAndRunQuery(
|
||||||
|
queryRunner,
|
||||||
|
queryHistoryManager,
|
||||||
|
databaseUI,
|
||||||
|
localQueryResultsView,
|
||||||
|
queryStorageDir,
|
||||||
|
false,
|
||||||
|
uri,
|
||||||
|
wrappedProgress,
|
||||||
|
token,
|
||||||
|
undefined,
|
||||||
|
).then(() => queriesRemaining--),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Running queries",
|
||||||
|
cancellable: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const quickEval = async (uri: Uri) =>
|
||||||
|
withProgress(
|
||||||
|
async (progress, token) => {
|
||||||
|
await compileAndRunQuery(
|
||||||
|
queryRunner,
|
||||||
|
queryHistoryManager,
|
||||||
|
databaseUI,
|
||||||
|
localQueryResultsView,
|
||||||
|
queryStorageDir,
|
||||||
|
true,
|
||||||
|
uri,
|
||||||
|
progress,
|
||||||
|
token,
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Running query",
|
||||||
|
cancellable: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const codeLensQuickEval = async (uri: Uri, range: Range) =>
|
||||||
|
withProgress(
|
||||||
|
async (progress, token) =>
|
||||||
|
await compileAndRunQuery(
|
||||||
|
queryRunner,
|
||||||
|
queryHistoryManager,
|
||||||
|
databaseUI,
|
||||||
|
localQueryResultsView,
|
||||||
|
queryStorageDir,
|
||||||
|
true,
|
||||||
|
uri,
|
||||||
|
progress,
|
||||||
|
token,
|
||||||
|
undefined,
|
||||||
|
range,
|
||||||
|
),
|
||||||
|
{
|
||||||
|
title: "Running query",
|
||||||
|
cancellable: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const quickQuery = async () =>
|
||||||
|
withProgress(
|
||||||
|
async (progress, token) =>
|
||||||
|
displayQuickQuery(app, cliServer, databaseUI, progress, token),
|
||||||
|
{
|
||||||
|
title: "Run Quick Query",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
"codeQL.runQuery": runQuery,
|
||||||
|
"codeQL.runQueryContextEditor": runQuery,
|
||||||
|
"codeQL.runQueryOnMultipleDatabases": runQueryOnMultipleDatabases,
|
||||||
|
"codeQL.runQueryOnMultipleDatabasesContextEditor":
|
||||||
|
runQueryOnMultipleDatabases,
|
||||||
|
"codeQL.runQueries": runQueries,
|
||||||
|
"codeQL.quickEval": quickEval,
|
||||||
|
"codeQL.quickEvalContextEditor": quickEval,
|
||||||
|
"codeQL.codeLensQuickEval": codeLensQuickEval,
|
||||||
|
"codeQL.quickQuery": quickQuery,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function compileAndRunQuery(
|
||||||
|
qs: QueryRunner,
|
||||||
|
qhm: QueryHistoryManager,
|
||||||
|
databaseUI: DatabaseUI,
|
||||||
|
localQueryResultsView: ResultsView,
|
||||||
|
queryStorageDir: string,
|
||||||
|
quickEval: boolean,
|
||||||
|
selectedQuery: Uri | undefined,
|
||||||
|
progress: ProgressCallback,
|
||||||
|
token: CancellationToken,
|
||||||
|
databaseItem: DatabaseItem | undefined,
|
||||||
|
range?: Range,
|
||||||
|
): Promise<void> {
|
||||||
|
if (qs !== undefined) {
|
||||||
|
// If no databaseItem is specified, use the database currently selected in the Databases UI
|
||||||
|
databaseItem =
|
||||||
|
databaseItem || (await databaseUI.getDatabaseItem(progress, token));
|
||||||
|
if (databaseItem === undefined) {
|
||||||
|
throw new Error("Can't run query without a selected database");
|
||||||
|
}
|
||||||
|
const databaseInfo = {
|
||||||
|
name: databaseItem.name,
|
||||||
|
databaseUri: databaseItem.databaseUri.toString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// handle cancellation from the history view.
|
||||||
|
const source = new CancellationTokenSource();
|
||||||
|
token.onCancellationRequested(() => source.cancel());
|
||||||
|
|
||||||
|
const initialInfo = await createInitialQueryInfo(
|
||||||
|
selectedQuery,
|
||||||
|
databaseInfo,
|
||||||
|
quickEval,
|
||||||
|
range,
|
||||||
|
);
|
||||||
|
const item = new LocalQueryInfo(initialInfo, source);
|
||||||
|
qhm.addQuery(item);
|
||||||
|
try {
|
||||||
|
const completedQueryInfo = await qs.compileAndRunQueryAgainstDatabase(
|
||||||
|
databaseItem,
|
||||||
|
initialInfo,
|
||||||
|
queryStorageDir,
|
||||||
|
progress,
|
||||||
|
source.token,
|
||||||
|
undefined,
|
||||||
|
item,
|
||||||
|
);
|
||||||
|
qhm.completeQuery(item, completedQueryInfo);
|
||||||
|
await showResultsForCompletedQuery(
|
||||||
|
localQueryResultsView,
|
||||||
|
item as CompletedLocalQueryInfo,
|
||||||
|
WebviewReveal.Forced,
|
||||||
|
);
|
||||||
|
// Note we must update the query history view after showing results as the
|
||||||
|
// display and sorting might depend on the number of results
|
||||||
|
} catch (e) {
|
||||||
|
const err = asError(e);
|
||||||
|
err.message = `Error running query: ${err.message}`;
|
||||||
|
item.failureReason = err.message;
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
await qhm.refreshTreeView();
|
||||||
|
source.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DatabaseQuickPickItem extends QuickPickItem {
|
||||||
|
databaseItem: DatabaseItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function compileAndRunQueryOnMultipleDatabases(
|
||||||
|
cliServer: CodeQLCliServer,
|
||||||
|
qs: QueryRunner,
|
||||||
|
qhm: QueryHistoryManager,
|
||||||
|
dbm: DatabaseManager,
|
||||||
|
databaseUI: DatabaseUI,
|
||||||
|
localQueryResultsView: ResultsView,
|
||||||
|
queryStorageDir: string,
|
||||||
|
progress: ProgressCallback,
|
||||||
|
token: CancellationToken,
|
||||||
|
uri: Uri | undefined,
|
||||||
|
): Promise<void> {
|
||||||
|
let filteredDBs = dbm.databaseItems;
|
||||||
|
if (filteredDBs.length === 0) {
|
||||||
|
void showAndLogErrorMessage(
|
||||||
|
"No databases found. Please add a suitable database to your workspace.",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If possible, only show databases with the right language (otherwise show all databases).
|
||||||
|
const queryLanguage = await findLanguage(cliServer, uri);
|
||||||
|
if (queryLanguage) {
|
||||||
|
filteredDBs = dbm.databaseItems.filter(
|
||||||
|
(db) => db.language === queryLanguage,
|
||||||
|
);
|
||||||
|
if (filteredDBs.length === 0) {
|
||||||
|
void showAndLogErrorMessage(
|
||||||
|
`No databases found for language ${queryLanguage}. Please add a suitable database to your workspace.`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const quickPickItems = filteredDBs.map<DatabaseQuickPickItem>((dbItem) => ({
|
||||||
|
databaseItem: dbItem,
|
||||||
|
label: dbItem.name,
|
||||||
|
description: dbItem.language,
|
||||||
|
}));
|
||||||
|
/**
|
||||||
|
* Databases that were selected in the quick pick menu.
|
||||||
|
*/
|
||||||
|
const quickpick = await window.showQuickPick<DatabaseQuickPickItem>(
|
||||||
|
quickPickItems,
|
||||||
|
{ canPickMany: true, ignoreFocusOut: true },
|
||||||
|
);
|
||||||
|
if (quickpick !== undefined) {
|
||||||
|
// Collect all skipped databases and display them at the end (instead of popping up individual errors)
|
||||||
|
const skippedDatabases = [];
|
||||||
|
const errors = [];
|
||||||
|
for (const item of quickpick) {
|
||||||
|
try {
|
||||||
|
await compileAndRunQuery(
|
||||||
|
qs,
|
||||||
|
qhm,
|
||||||
|
databaseUI,
|
||||||
|
localQueryResultsView,
|
||||||
|
queryStorageDir,
|
||||||
|
false,
|
||||||
|
uri,
|
||||||
|
progress,
|
||||||
|
token,
|
||||||
|
item.databaseItem,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
skippedDatabases.push(item.label);
|
||||||
|
errors.push(getErrorMessage(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skippedDatabases.length > 0) {
|
||||||
|
void extLogger.log(`Errors:\n${errors.join("\n")}`);
|
||||||
|
void showAndLogWarningMessage(
|
||||||
|
`The following databases were skipped:\n${skippedDatabases.join(
|
||||||
|
"\n",
|
||||||
|
)}.\nFor details about the errors, see the logs.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
void showAndLogErrorMessage("No databases selected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function showResultsForCompletedQuery(
|
||||||
|
localQueryResultsView: ResultsView,
|
||||||
|
query: CompletedLocalQueryInfo,
|
||||||
|
forceReveal: WebviewReveal,
|
||||||
|
): Promise<void> {
|
||||||
|
await localQueryResultsView.showResults(query, forceReveal, false);
|
||||||
|
}
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
import { ensureDir, writeFile, pathExists, readFile } from "fs-extra";
|
import { ensureDir, writeFile, pathExists, readFile } from "fs-extra";
|
||||||
import { dump, load } from "js-yaml";
|
import { dump, load } from "js-yaml";
|
||||||
import { basename, join } from "path";
|
import { basename, join } from "path";
|
||||||
import {
|
import { CancellationToken, window as Window, workspace, Uri } from "vscode";
|
||||||
CancellationToken,
|
|
||||||
ExtensionContext,
|
|
||||||
window as Window,
|
|
||||||
workspace,
|
|
||||||
Uri,
|
|
||||||
} from "vscode";
|
|
||||||
import { LSPErrorCodes, ResponseError } from "vscode-languageclient";
|
import { LSPErrorCodes, ResponseError } from "vscode-languageclient";
|
||||||
import { CodeQLCliServer } from "./cli";
|
import { CodeQLCliServer } from "./cli";
|
||||||
import { DatabaseUI } from "./local-databases-ui";
|
import { DatabaseUI } from "./local-databases-ui";
|
||||||
@@ -20,6 +14,7 @@ import {
|
|||||||
import { ProgressCallback, UserCancellationException } from "./commandRunner";
|
import { ProgressCallback, UserCancellationException } from "./commandRunner";
|
||||||
import { getErrorMessage } from "./pure/helpers-pure";
|
import { getErrorMessage } from "./pure/helpers-pure";
|
||||||
import { FALLBACK_QLPACK_FILENAME, getQlPackPath } from "./pure/ql";
|
import { FALLBACK_QLPACK_FILENAME, getQlPackPath } from "./pure/ql";
|
||||||
|
import { App } from "./common/app";
|
||||||
|
|
||||||
const QUICK_QUERIES_DIR_NAME = "quick-queries";
|
const QUICK_QUERIES_DIR_NAME = "quick-queries";
|
||||||
const QUICK_QUERY_QUERY_NAME = "quick-query.ql";
|
const QUICK_QUERY_QUERY_NAME = "quick-query.ql";
|
||||||
@@ -30,8 +25,8 @@ export function isQuickQueryPath(queryPath: string): boolean {
|
|||||||
return basename(queryPath) === QUICK_QUERY_QUERY_NAME;
|
return basename(queryPath) === QUICK_QUERY_QUERY_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getQuickQueriesDir(ctx: ExtensionContext): Promise<string> {
|
async function getQuickQueriesDir(app: App): Promise<string> {
|
||||||
const storagePath = ctx.storagePath;
|
const storagePath = app.workspaceStoragePath;
|
||||||
if (storagePath === undefined) {
|
if (storagePath === undefined) {
|
||||||
throw new Error("Workspace storage path is undefined");
|
throw new Error("Workspace storage path is undefined");
|
||||||
}
|
}
|
||||||
@@ -57,7 +52,7 @@ function findExistingQuickQueryEditor() {
|
|||||||
* Show a buffer the user can enter a simple query into.
|
* Show a buffer the user can enter a simple query into.
|
||||||
*/
|
*/
|
||||||
export async function displayQuickQuery(
|
export async function displayQuickQuery(
|
||||||
ctx: ExtensionContext,
|
app: App,
|
||||||
cliServer: CodeQLCliServer,
|
cliServer: CodeQLCliServer,
|
||||||
databaseUI: DatabaseUI,
|
databaseUI: DatabaseUI,
|
||||||
progress: ProgressCallback,
|
progress: ProgressCallback,
|
||||||
@@ -73,7 +68,7 @@ export async function displayQuickQuery(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const workspaceFolders = workspace.workspaceFolders || [];
|
const workspaceFolders = workspace.workspaceFolders || [];
|
||||||
const queriesDir = await getQuickQueriesDir(ctx);
|
const queriesDir = await getQuickQueriesDir(app);
|
||||||
|
|
||||||
// We need to have a multi-root workspace to make quick query work
|
// We need to have a multi-root workspace to make quick query work
|
||||||
// at all. Changing the workspace from single-root to multi-root
|
// at all. Changing the workspace from single-root to multi-root
|
||||||
|
|||||||
Reference in New Issue
Block a user