diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 3a61c5a80be..736fd06cbb1 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,6 +2,9 @@ ## [UNRELEASED] +- Add `CodeQL: Quick Evaluation Count` command to generate the count summary statistics of the results set + without speding the time to compute locations and strings. + ## 1.8.6 - 14 June 2023 - Add repositories to a variant analysis list with GitHub Code Search. [#2439](https://github.com/github/vscode-codeql/pull/2439) and [#2476](https://github.com/github/vscode-codeql/pull/2476) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index b7f9d837993..efc559677f7 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -457,6 +457,10 @@ "command": "codeQL.quickEval", "title": "CodeQL: Quick Evaluation" }, + { + "command": "codeQL.quickEvalCount", + "title": "CodeQL: Quick Evaluation Count" + }, { "command": "codeQL.quickEvalContextEditor", "title": "CodeQL: Quick Evaluation" @@ -1220,6 +1224,10 @@ "command": "codeQL.quickEval", "when": "editorLangId == ql" }, + { + "command": "codeQL.quickEvalCount", + "when": "editorLangId == ql && codeql.supportsQuickEvalCount" + }, { "command": "codeQL.quickEvalContextEditor", "when": "false" diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index edbfe17eb5b..ff7535845d3 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -1483,6 +1483,13 @@ export class CodeQLCliServer implements Disposable { CliVersionConstraint.CLI_VERSION_WITH_PER_QUERY_EVAL_LOG, ) >= 0, ); + await this.app.commands.execute( + "setContext", + "codeql.supportsQuickEvalCount", + newVersion.compare( + CliVersionConstraint.CLI_VERSION_WITH_QUICK_EVAL_COUNT, + ) >= 0, + ); } catch (e) { this._versionChangedListeners.forEach((listener) => listener(undefined), @@ -1845,6 +1852,11 @@ export class CliVersionConstraint { public static CLI_VERSION_GLOBAL_CACHE = new SemVer("2.12.4"); + /** + * CLI version where the query server supports quick-eval count mode. + */ + public static CLI_VERSION_WITH_QUICK_EVAL_COUNT = new SemVer("2.13.3"); + constructor(private readonly cli: CodeQLCliServer) { /**/ } @@ -1918,4 +1930,10 @@ export class CliVersionConstraint { async usesGlobalCompilationCache() { return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_GLOBAL_CACHE); } + + async supportsQuickEvalCount() { + return this.isVersionAtLeast( + CliVersionConstraint.CLI_VERSION_WITH_QUICK_EVAL_COUNT, + ); + } } diff --git a/extensions/ql-vscode/src/common/commands.ts b/extensions/ql-vscode/src/common/commands.ts index 8dc83848da7..bdd24ddd0d8 100644 --- a/extensions/ql-vscode/src/common/commands.ts +++ b/extensions/ql-vscode/src/common/commands.ts @@ -133,6 +133,7 @@ export type LocalQueryCommands = { "codeQLQueries.runLocalQueryFromQueriesPanel": TreeViewContextSingleSelectionCommandFunction; "codeQL.runQueries": ExplorerSelectionCommandFunction; "codeQL.quickEval": (uri: Uri) => Promise; + "codeQL.quickEvalCount": (uri: Uri) => Promise; "codeQL.quickEvalContextEditor": (uri: Uri) => Promise; "codeQL.codeLensQuickEval": (uri: Uri, range: Range) => Promise; "codeQL.quickQuery": () => Promise; diff --git a/extensions/ql-vscode/src/local-queries/local-queries.ts b/extensions/ql-vscode/src/local-queries/local-queries.ts index f02438752f4..cc86f0805f8 100644 --- a/extensions/ql-vscode/src/local-queries/local-queries.ts +++ b/extensions/ql-vscode/src/local-queries/local-queries.ts @@ -40,7 +40,7 @@ import { import { CompletedLocalQueryInfo, LocalQueryInfo } from "../query-results"; import { WebviewReveal } from "./webview"; import { asError, getErrorMessage } from "../common/helpers-pure"; -import { CodeQLCliServer } from "../codeql-cli/cli"; +import { CliVersionConstraint, CodeQLCliServer } from "../codeql-cli/cli"; import { LocalQueryCommands } from "../common/commands"; import { App } from "../common/app"; import { DisposableObject } from "../common/disposable-object"; @@ -107,6 +107,7 @@ export class LocalQueries extends DisposableObject { this.runQueries.bind(this), ), "codeQL.quickEval": this.quickEval.bind(this), + "codeQL.quickEvalCount": this.quickEvalCount.bind(this), "codeQL.quickEvalContextEditor": this.quickEval.bind(this), "codeQL.codeLensQuickEval": this.codeLensQuickEval.bind(this), "codeQL.quickQuery": this.quickQuery.bind(this), @@ -240,6 +241,29 @@ export class LocalQueries extends DisposableObject { ); } + private async quickEvalCount(uri: Uri): Promise { + await withProgress( + async (progress, token) => { + if (!(await this.cliServer.cliConstraints.supportsQuickEvalCount())) { + throw new Error( + `Quick evaluation count is only supported by CodeQL CLI v${CliVersionConstraint.CLI_VERSION_WITH_QUICK_EVAL_COUNT} or later.`, + ); + } + await this.compileAndRunQuery( + QuickEvalType.QuickEvalCount, + uri, + progress, + token, + undefined, + ); + }, + { + title: "Running query", + cancellable: true, + }, + ); + } + private async codeLensQuickEval(uri: Uri, range: Range): Promise { await withProgress( async (progress, token) => diff --git a/extensions/ql-vscode/src/query-results.ts b/extensions/ql-vscode/src/query-results.ts index a8648438def..da8e1c2ea91 100644 --- a/extensions/ql-vscode/src/query-results.ts +++ b/extensions/ql-vscode/src/query-results.ts @@ -41,6 +41,7 @@ export interface InitialQueryInfo { readonly queryText: string; // text of the selected file, or the selected text when doing quick eval readonly isQuickQuery: boolean; readonly isQuickEval: boolean; + readonly isQuickEvalCount?: boolean; // Missing is false for backwards compatibility readonly quickEvalPosition?: messages.Position; readonly queryPath: string; readonly databaseInfo: DatabaseInfo; @@ -270,7 +271,9 @@ export class LocalQueryInfo { * - Otherwise, return the query file name. */ getQueryName() { - if (this.initialInfo.quickEvalPosition) { + if (this.initialInfo.isQuickEvalCount) { + return `Quick evaluation counts of ${this.getQueryFileName()}`; + } else if (this.initialInfo.isQuickEval) { return `Quick evaluation of ${this.getQueryFileName()}`; } else if (this.completedQuery?.query.metadata?.name) { return this.completedQuery?.query.metadata?.name; diff --git a/extensions/ql-vscode/src/run-queries-shared.ts b/extensions/ql-vscode/src/run-queries-shared.ts index d2dbe03a62c..24d119acbe9 100644 --- a/extensions/ql-vscode/src/run-queries-shared.ts +++ b/extensions/ql-vscode/src/run-queries-shared.ts @@ -585,9 +585,12 @@ export async function createInitialQueryInfo( databaseInfo: DatabaseInfo, ): Promise { const isQuickEval = selectedQuery.quickEval !== undefined; + const isQuickEvalCount = + selectedQuery.quickEval?.quickEvalCount !== undefined; return { queryPath: selectedQuery.queryPath, isQuickEval, + isQuickEvalCount, isQuickQuery: isQuickQueryPath(selectedQuery.queryPath), databaseInfo, id: `${basename(selectedQuery.queryPath)}-${nanoid()}`, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts index 2529434733e..9030e063c9b 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts @@ -58,6 +58,7 @@ describe("query-results", () => { endLine: 2, fileName: "/home/users/yz", }; + (fqi.initialInfo as any).isQuickEval = true; expect(fqi.getQueryName()).toBe("Quick evaluation of yz:1-2"); (fqi.initialInfo as any).quickEvalPosition.endLine = 1; expect(fqi.getQueryName()).toBe("Quick evaluation of yz:1");