From 3743895b66f0bf14b4014cba96d0596e9ecb730f Mon Sep 17 00:00:00 2001 From: Musab Guma'a Date: Wed, 27 Oct 2021 21:42:00 +0100 Subject: [PATCH] Add "Preview Query Help" command --- extensions/ql-vscode/CHANGELOG.md | 1 + extensions/ql-vscode/package.json | 23 ++++++++++++++++++ extensions/ql-vscode/src/cli.ts | 14 +++++++++++ extensions/ql-vscode/src/extension.ts | 34 +++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index ad0225ff29e..7595c15118c 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -7,6 +7,7 @@ - Fix a bug with importing large databases. Databases over 4GB can now be imported directly from LGTM or from a zip file. This functionality is only available when using CodeQL CLI version 2.6.0 or later. [#971](https://github.com/github/vscode-codeql/pull/971) - Replace certain control codes (`U+0000` - `U+001F`) with their corresponding control labels (`U+2400` - `U+241F`) in the results view. [#963](https://github.com/github/vscode-codeql/pull/963) - Allow case-insensitive project slugs for GitHub repositories when adding a CodeQL database from LGTM. [#978](https://github.com/github/vscode-codeql/pull/961) +- Add a _CodeQL: Preview Query Help_ command to generate Markdown previews of `.qhelp` query help files. This command should only be run in trusted workspaces. See https://codeql.github.com/docs/codeql-cli/testing-query-help-files for more information about query help. [#988](https://github.com/github/vscode-codeql/pull/988) - Make "Open Referenced File" command accessible from the active editor menu. [#989](https://github.com/github/vscode-codeql/pull/989) - Allow query result locations with 0 as the end column value. These are treated as the first column in the line. [#1002](https://github.com/github/vscode-codeql/pull/1002) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 9028f1e1fe4..5177cd077d8 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -46,6 +46,7 @@ "onCommand:codeQL.setCurrentDatabase", "onCommand:codeQL.viewAst", "onCommand:codeQL.openReferencedFile", + "onCommand:codeQL.previewQueryHelp", "onCommand:codeQL.chooseDatabaseFolder", "onCommand:codeQL.chooseDatabaseArchive", "onCommand:codeQL.chooseDatabaseInternet", @@ -295,6 +296,10 @@ "command": "codeQL.openReferencedFile", "title": "CodeQL: Open Referenced File" }, + { + "command": "codeQL.previewQueryHelp", + "title": "CodeQL: Preview Query Help" + }, { "command": "codeQL.quickQuery", "title": "CodeQL: Quick Query" @@ -681,6 +686,11 @@ "group": "9_qlCommands", "when": "view == codeQLQueryHistory" }, + { + "command": "codeQL.previewQueryHelp", + "group": "9_qlCommands", + "when": "view == codeQLQueryHistory && resourceScheme == .qhelp && isWorkspaceTrusted" + }, { "command": "codeQLTests.showOutputDifferences", "group": "qltest@1", @@ -712,6 +722,11 @@ "command": "codeQL.openReferencedFile", "group": "9_qlCommands", "when": "resourceExtname == .qlref" + }, + { + "command": "codeQL.previewQueryHelp", + "group": "9_qlCommands", + "when": "resourceExtname == .qhelp && isWorkspaceTrusted" } ], "commandPalette": [ @@ -743,6 +758,10 @@ "command": "codeQL.openReferencedFile", "when": "resourceExtname == .qlref" }, + { + "command": "codeQL.previewQueryHelp", + "when": "resourceExtname == .qhelp && isWorkspaceTrusted" + }, { "command": "codeQL.setCurrentDatabase", "when": "false" @@ -900,6 +919,10 @@ { "command": "codeQL.openReferencedFile", "when": "resourceExtname == .qlref" + }, + { + "command": "codeQL.previewQueryHelp", + "when": "resourceExtname == .qhelp && isWorkspaceTrusted" } ] }, diff --git a/extensions/ql-vscode/src/cli.ts b/extensions/ql-vscode/src/cli.ts index 8c1515a28a3..c06d92a6fc8 100644 --- a/extensions/ql-vscode/src/cli.ts +++ b/extensions/ql-vscode/src/cli.ts @@ -621,6 +621,20 @@ export class CodeQLCliServer implements Disposable { return await this.runCodeQlCliCommand(['database', 'unbundle'], subcommandArgs, `Extracting ${archivePath} to directory ${target}`); } + + /** + * Uses a .qhelp file to generate Query Help documentation in a specified format. + * @param pathToQhelp The path to the .qhelp file + * @param format The format in which the query help should be generated {@link https://codeql.github.com/docs/codeql-cli/manual/generate-query-help/#cmdoption-codeql-generate-query-help-format} + * @param outputDirectory The output directory for the generated file + */ + async generateQueryHelp(pathToQhelp:string, outputDirectory?: string): Promise { + const subcommandArgs = ['--format=markdown']; + if(outputDirectory) subcommandArgs.push('--output', outputDirectory); + subcommandArgs.push(pathToQhelp); + + return await this.runCodeQlCliCommand(['generate', 'query-help'], subcommandArgs, `Generating qhelp in markdown format at ${outputDirectory}`); + } /** * Gets the results from a bqrs. diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 8a6b2e2ddfe..704485c9c30 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -16,6 +16,7 @@ import { import { LanguageClient } from 'vscode-languageclient'; import * as os from 'os'; import * as path from 'path'; +import * as tmp from 'tmp-promise'; import { testExplorerExtensionId, TestHub } from 'vscode-test-adapter-api'; import { AstViewer } from './astViewer'; @@ -493,6 +494,32 @@ async function activateWithInstalledDistribution( } } + const qhelpTmpDir = tmp.dirSync({ prefix: 'qhelp_', keep: false, unsafeCleanup: true }); + ctx.subscriptions.push({ dispose: qhelpTmpDir.removeCallback }); + + async function previewQueryHelp( + selectedQuery: Uri + ): Promise { + // selectedQuery is unpopulated when executing through the command palette + const pathToQhelp = selectedQuery ? selectedQuery.fsPath : window.activeTextEditor?.document.uri.fsPath; + if(pathToQhelp) { + // Create temporary directory + const relativePathToMd = path.basename(pathToQhelp, '.qhelp') + '.md'; + const absolutePathToMd = path.join(qhelpTmpDir.name, relativePathToMd); + const uri = Uri.file(absolutePathToMd); + try { + await cliServer.generateQueryHelp(pathToQhelp , absolutePathToMd); + await commands.executeCommand('markdown.showPreviewToSide', uri); + } catch (err) { + const errorMessage = err.message.includes('Generating qhelp in markdown') ? ( + `Could not generate markdown from ${pathToQhelp}: Bad formatting in .qhelp file.` + ) : `Could not open a preview of the generated file (${absolutePathToMd}).`; + void helpers.showAndLogErrorMessage(errorMessage, { fullMessage: `${errorMessage}\n${err}` }); + } + } + + } + async function openReferencedFile( selectedQuery: Uri ): Promise { @@ -746,6 +773,13 @@ async function activateWithInstalledDistribution( ) ); + ctx.subscriptions.push( + commandRunner( + 'codeQL.previewQueryHelp', + previewQueryHelp + ) + ); + ctx.subscriptions.push( commandRunnerWithProgress('codeQL.restartQueryServer', async ( progress: ProgressCallback,