diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 5bce6cbce43..f5402044dcf 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -8,6 +8,7 @@ - Avoid showing an error popup when user runs a query without `@kind` metadata. [#801](https://github.com/github/vscode-codeql/pull/801) - Fix running of tests when the `ms-python` extension is installed. [#803](https://github.com/github/vscode-codeql/pull/803) +- Add an option to jump from a .qlref file to the .ql file it references. ## 1.4.4 - 19 March 2021 diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 776fb2e167e..9b9f4c1d1cc 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -34,6 +34,7 @@ "onCommand:codeQLDatabases.chooseDatabaseLgtm", "onCommand:codeQL.setCurrentDatabase", "onCommand:codeQL.viewAst", + "onCommand:codeQL.openReferencedFile", "onCommand:codeQL.chooseDatabaseFolder", "onCommand:codeQL.chooseDatabaseArchive", "onCommand:codeQL.chooseDatabaseInternet", @@ -229,6 +230,10 @@ "command": "codeQL.quickEval", "title": "CodeQL: Quick Evaluation" }, + { + "command": "codeQL.openReferencedFile", + "title": "CodeQL: Open Referenced File" + }, { "command": "codeQL.quickQuery", "title": "CodeQL: Quick Query" @@ -619,6 +624,11 @@ "command": "codeQL.runQueries", "group": "9_qlCommands", "when": "resourceScheme != codeql-zip-archive" + }, + { + "command": "codeQL.openReferencedFile", + "group": "9_qlCommands", + "when": "resourceExtname == .qlref" } ], "commandPalette": [ @@ -634,6 +644,10 @@ "command": "codeQL.quickEval", "when": "editorLangId == ql" }, + { + "command": "codeQL.openReferencedFile", + "when": "resourceExtname == .qlref" + }, { "command": "codeQL.setCurrentDatabase", "when": "false" @@ -771,6 +785,10 @@ { "command": "codeQL.quickEval", "when": "editorLangId == ql" + }, + { + "command": "codeQL.openReferencedFile", + "when": "resourceExtname == .qlref" } ] }, diff --git a/extensions/ql-vscode/src/cli.ts b/extensions/ql-vscode/src/cli.ts index 4cb062cd1ea..fcb69e03bad 100644 --- a/extensions/ql-vscode/src/cli.ts +++ b/extensions/ql-vscode/src/cli.ts @@ -73,6 +73,11 @@ export interface UpgradesInfo { */ export type QlpacksInfo = { [name: string]: string[] }; +/** + * The expected output of `codeql resolve qlref`. + */ +export type QlrefInfo = { resolvedPath: string }; + // `codeql bqrs interpret` requires both of these to be present or // both absent. export interface SourceInfo { @@ -143,6 +148,11 @@ export class CodeQLCliServer implements Disposable { */ private static CLI_VERSION_WITH_DOWNGRADES = new SemVer('2.4.4'); + /** + * CLI version where the `codeql resolve qlref` command is available. + */ + public static CLI_VERSION_WITH_RESOLVE_QLREF = new SemVer('2.5.1'); + /** The process for the cli server, or undefined if one doesn't exist yet */ process?: child_process.ChildProcessWithoutNullStreams; /** Queue of future commands*/ @@ -461,12 +471,15 @@ export class CodeQLCliServer implements Disposable { * @param command The `codeql` command to be run, provided as an array of command/subcommand names. * @param commandArgs The arguments to pass to the `codeql` command. * @param description Description of the action being run, to be shown in log and error messages. + * @param addFormat Whether or not to add commandline arguments to specify the format as JSON. * @param progressReporter Used to output progress messages, e.g. to the status bar. * @returns The contents of the command's stdout, if the command succeeded. */ - async runJsonCodeQlCliCommand(command: string[], commandArgs: string[], description: string, progressReporter?: ProgressReporter): Promise { - // Add format argument first, in case commandArgs contains positional parameters. - const args = ['--format', 'json'].concat(commandArgs); + async runJsonCodeQlCliCommand(command: string[], commandArgs: string[], description: string, addFormat = true, progressReporter?: ProgressReporter): Promise { + let args: string[] = []; + if (addFormat) // Add format argument first, in case commandArgs contains positional parameters. + args = args.concat(['--format', 'json']); + args = args.concat(commandArgs); const result = await this.runCodeQlCliCommand(command, args, description, progressReporter); try { return JSON.parse(result) as OutputType; @@ -505,6 +518,18 @@ export class CodeQLCliServer implements Disposable { ); } + public async resolveQlref(qlref: string): Promise { + const subcommandArgs = [ + qlref + ]; + return await this.runJsonCodeQlCliCommand( + ['resolve', 'qlref'], + subcommandArgs, + 'Resolving qlref', + false + ); + } + /** * Runs QL tests. * @param testPaths Full paths of the tests to run. @@ -549,7 +574,7 @@ export class CodeQLCliServer implements Disposable { if (queryMemoryMb !== undefined) { args.push('--ram', queryMemoryMb.toString()); } - return await this.runJsonCodeQlCliCommand(['resolve', 'ram'], args, 'Resolving RAM settings', progressReporter); + return await this.runJsonCodeQlCliCommand(['resolve', 'ram'], args, 'Resolving RAM settings', true, progressReporter); } /** * Gets the headers (and optionally pagination info) of a bqrs. @@ -773,6 +798,10 @@ export class CodeQLCliServer implements Disposable { return (await this.getVersion()).compare(CodeQLCliServer.CLI_VERSION_WITH_DOWNGRADES) >= 0; } + public async supportsResolveQlref() { + return (await this.getVersion()).compare(CodeQLCliServer.CLI_VERSION_WITH_RESOLVE_QLREF) >= 0; + } + private async refreshVersion() { const distribution = await this.distributionProvider.getDistribution(); switch (distribution.kind) { @@ -908,11 +937,11 @@ class SplitBuffer { /** * A version of startsWith that isn't overriden by a broken version of ms-python. - * + * * The definition comes from * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith * which is CC0/public domain - * + * * See https://github.com/github/vscode-codeql/issues/802 for more context as to why we need it. */ private static startsWith(s: string, searchString: string, position: number): boolean { diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 09041070edf..65d186e7e83 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -480,6 +480,25 @@ async function activateWithInstalledDistribution( } } + async function openReferencedFile( + selectedQuery: Uri + ): Promise { + if (qs !== undefined) { + if (await cliServer.supportsResolveQlref()) { + const resolved = await cliServer.resolveQlref(selectedQuery.path); + const uri = Uri.file(resolved.resolvedPath); + await window.showTextDocument(uri, { preview: false }); + } else { + helpers.showAndLogErrorMessage( + 'Jumping from a .qlref file to the .ql file it references is not ' + + 'supported with the CLI version you are running.\n' + + `Please upgrade your CLI to version ${ + CodeQLCliServer.CLI_VERSION_WITH_RESOLVE_QLREF + } or later to use this feature.`); + } + } + } + ctx.subscriptions.push(tmpDirDisposal); logger.log('Initializing CodeQL language server.'); @@ -616,6 +635,12 @@ async function activateWithInstalledDistribution( } ) ); + ctx.subscriptions.push( + commandRunner( + 'codeQL.openReferencedFile', + openReferencedFile + ) + ); ctx.subscriptions.push( commandRunnerWithProgress('codeQL.restartQueryServer', async (