Add code lens for quick evaluation (#1035)
* Add code lens for quick eval command * Ensure commented out predicates do not have code lens * Improve conditional check for commented out predicate detection * Refactor regex * Move comment check to eliminate evaluating regex more than once Co-authored-by: marcnjaramillo <mnj.webdeveloper@gmail.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
## [UNRELEASED]
|
||||
|
||||
- Add a CodeLens to make the Quick Evaluation command more accessible. Click the `Quick Evaluation` prompt above a predicate definition in the editor to evaluate that predicate on its own. [#1035](https://github.com/github/vscode-codeql/pull/1035)
|
||||
- Fix a bug where the _Alerts_ option would show in the results view even if there is no alerts table available. [#1038](https://github.com/github/vscode-codeql/pull/1038)
|
||||
|
||||
## 1.5.8 - 2 December 2021
|
||||
|
||||
@@ -11,7 +11,8 @@ import {
|
||||
window as Window,
|
||||
env,
|
||||
window,
|
||||
QuickPickItem
|
||||
QuickPickItem,
|
||||
Range
|
||||
} from 'vscode';
|
||||
import { LanguageClient } from 'vscode-languageclient';
|
||||
import * as os from 'os';
|
||||
@@ -21,6 +22,7 @@ import { testExplorerExtensionId, TestHub } from 'vscode-test-adapter-api';
|
||||
|
||||
import { AstViewer } from './astViewer';
|
||||
import * as archiveFilesystemProvider from './archive-filesystem-provider';
|
||||
import QuickEvalCodeLensProvider from './quickEvalCodeLensProvider';
|
||||
import { CodeQLCliServer, CliVersionConstraint } from './cli';
|
||||
import {
|
||||
CliConfigListener,
|
||||
@@ -156,6 +158,7 @@ export interface CodeQLExtensionInterface {
|
||||
* @returns CodeQLExtensionInterface
|
||||
*/
|
||||
export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionInterface | Record<string, never>> {
|
||||
|
||||
void logger.log(`Starting ${extensionId} extension`);
|
||||
if (extension === undefined) {
|
||||
throw new Error(`Can't find extension ${extensionId}`);
|
||||
@@ -166,6 +169,9 @@ export async function activate(ctx: ExtensionContext): Promise<CodeQLExtensionIn
|
||||
await initializeTelemetry(extension, ctx);
|
||||
languageSupport.install();
|
||||
|
||||
const codelensProvider = new QuickEvalCodeLensProvider();
|
||||
languages.registerCodeLensProvider({ scheme: 'file', language: 'ql' }, codelensProvider);
|
||||
|
||||
ctx.subscriptions.push(distributionConfigListener);
|
||||
const codeQlVersionRange = DEFAULT_DISTRIBUTION_VERSION_RANGE;
|
||||
const distributionManager = new DistributionManager(distributionConfigListener, codeQlVersionRange, ctx);
|
||||
@@ -471,6 +477,7 @@ async function activateWithInstalledDistribution(
|
||||
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
|
||||
@@ -485,7 +492,9 @@ async function activateWithInstalledDistribution(
|
||||
quickEval,
|
||||
selectedQuery,
|
||||
progress,
|
||||
token
|
||||
token,
|
||||
undefined,
|
||||
range
|
||||
);
|
||||
const item = qhm.buildCompletedQuery(info);
|
||||
await showResultsForCompletedQuery(item, WebviewReveal.NotForced);
|
||||
@@ -733,6 +742,22 @@ async function activateWithInstalledDistribution(
|
||||
cancellable: true
|
||||
})
|
||||
);
|
||||
|
||||
ctx.subscriptions.push(
|
||||
commandRunnerWithProgress(
|
||||
'codeQL.codeLensQuickEval',
|
||||
async (
|
||||
progress: ProgressCallback,
|
||||
token: CancellationToken,
|
||||
uri: Uri,
|
||||
range: Range
|
||||
) => await compileAndRunQuery(true, uri, progress, token, undefined, range),
|
||||
{
|
||||
title: 'Running query',
|
||||
cancellable: true
|
||||
})
|
||||
);
|
||||
|
||||
ctx.subscriptions.push(
|
||||
commandRunnerWithProgress('codeQL.quickQuery', async (
|
||||
progress: ProgressCallback,
|
||||
|
||||
43
extensions/ql-vscode/src/quickEvalCodeLensProvider.ts
Normal file
43
extensions/ql-vscode/src/quickEvalCodeLensProvider.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import {
|
||||
CodeLensProvider,
|
||||
TextDocument,
|
||||
CodeLens,
|
||||
Command,
|
||||
Range
|
||||
} from 'vscode';
|
||||
|
||||
class QuickEvalCodeLensProvider implements CodeLensProvider {
|
||||
async provideCodeLenses(document: TextDocument): Promise<CodeLens[]> {
|
||||
|
||||
const codeLenses: CodeLens[] = [];
|
||||
|
||||
for (let index = 0; index < document.lineCount; index++) {
|
||||
const textLine = document.lineAt(index);
|
||||
|
||||
// Match a predicate signature, including predicate name, parameter list, and opening brace.
|
||||
// This currently does not match predicates that span multiple lines.
|
||||
const regex = new RegExp(/(\w+)\s*\([^()]*\)\s*\{/);
|
||||
|
||||
const matches = textLine.text.match(regex);
|
||||
|
||||
// Make sure that a code lens is not generated for any predicate that is commented out.
|
||||
if (matches && !(/^\s*\/\//).test(textLine.text)) {
|
||||
const range: Range = new Range(
|
||||
textLine.range.start.line, matches.index!,
|
||||
textLine.range.end.line, matches.index! + 1
|
||||
);
|
||||
|
||||
const command: Command = {
|
||||
command: 'codeQL.codeLensQuickEval',
|
||||
title: `Quick Evaluation: ${matches[1]}`,
|
||||
arguments: [document.uri, range]
|
||||
};
|
||||
const codeLens = new CodeLens(range, command);
|
||||
codeLenses.push(codeLens);
|
||||
}
|
||||
}
|
||||
return codeLenses;
|
||||
}
|
||||
}
|
||||
|
||||
export default QuickEvalCodeLensProvider;
|
||||
@@ -5,6 +5,7 @@ import * as tmp from 'tmp-promise';
|
||||
import {
|
||||
CancellationToken,
|
||||
ConfigurationTarget,
|
||||
Range,
|
||||
TextDocument,
|
||||
TextEditor,
|
||||
Uri,
|
||||
@@ -332,17 +333,18 @@ async function convertToQlPath(filePath: string): Promise<string> {
|
||||
|
||||
|
||||
/** Gets the selected position within the given editor. */
|
||||
async function getSelectedPosition(editor: TextEditor): Promise<messages.Position> {
|
||||
const pos = editor.selection.start;
|
||||
const posEnd = editor.selection.end;
|
||||
// Convert from 0-based to 1-based line and column numbers.
|
||||
return {
|
||||
fileName: await convertToQlPath(editor.document.fileName),
|
||||
line: pos.line + 1,
|
||||
column: pos.character + 1,
|
||||
endLine: posEnd.line + 1,
|
||||
endColumn: posEnd.character + 1
|
||||
};
|
||||
async function getSelectedPosition(editor: TextEditor, range?: Range): Promise<messages.Position> {
|
||||
const selectedRange = range || editor.selection;
|
||||
const pos = selectedRange.start;
|
||||
const posEnd = selectedRange.end;
|
||||
// Convert from 0-based to 1-based line and column numbers.
|
||||
return {
|
||||
fileName: await convertToQlPath(editor.document.fileName),
|
||||
line: pos.line + 1,
|
||||
column: pos.character + 1,
|
||||
endLine: posEnd.line + 1,
|
||||
endColumn: posEnd.character + 1
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -490,7 +492,7 @@ type SelectedQuery = {
|
||||
* @param selectedResourceUri The selected resource when the command was run.
|
||||
* @param quickEval Whether the command being run is `Quick Evaluation`.
|
||||
*/
|
||||
export async function determineSelectedQuery(selectedResourceUri: Uri | undefined, quickEval: boolean): Promise<SelectedQuery> {
|
||||
export async function determineSelectedQuery(selectedResourceUri: Uri | undefined, quickEval: boolean, range?: Range): Promise<SelectedQuery> {
|
||||
const editor = window.activeTextEditor;
|
||||
|
||||
// Choose which QL file to use.
|
||||
@@ -544,7 +546,7 @@ export async function determineSelectedQuery(selectedResourceUri: Uri | undefine
|
||||
// Report an error if we end up in this (hopefully unlikely) situation.
|
||||
throw new Error('The selected resource for quick evaluation should match the active editor.');
|
||||
}
|
||||
quickEvalPosition = await getSelectedPosition(editor);
|
||||
quickEvalPosition = await getSelectedPosition(editor, range);
|
||||
quickEvalText = editor.document.getText(editor.selection);
|
||||
}
|
||||
|
||||
@@ -560,13 +562,14 @@ export async function compileAndRunQueryAgainstDatabase(
|
||||
progress: ProgressCallback,
|
||||
token: CancellationToken,
|
||||
templates?: messages.TemplateDefinitions,
|
||||
range?: Range
|
||||
): Promise<QueryWithResults> {
|
||||
if (!db.contents || !db.contents.dbSchemeUri) {
|
||||
throw new Error(`Database ${db.databaseUri} does not have a CodeQL database scheme.`);
|
||||
}
|
||||
|
||||
// Determine which query to run, based on the selection and the active editor.
|
||||
const { queryPath, quickEvalPosition, quickEvalText } = await determineSelectedQuery(selectedQueryUri, quickEval);
|
||||
const { queryPath, quickEvalPosition, quickEvalText } = await determineSelectedQuery(selectedQueryUri, quickEval, range);
|
||||
|
||||
const historyItemOptions: QueryHistoryItemOptions = {};
|
||||
historyItemOptions.isQuickQuery === isQuickQueryPath(queryPath);
|
||||
|
||||
Reference in New Issue
Block a user