feat: User can see query text at time of execution
Add new command to view the query text in a synthetic, read-only document. Quick eval queries will show the text selected when initially running the query. Quick eval queries where the user has a single caret selection will show the entire line of text.
This commit is contained in:
Родитель
6f935ae6e4
Коммит
00026a7727
|
@ -226,6 +226,10 @@
|
|||
"command": "codeQLQueryHistory.showQueryLog",
|
||||
"title": "Show Query Log"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.showQueryText",
|
||||
"title": "Show Query Text"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryResults.nextPathStep",
|
||||
"title": "CodeQL: Show Next Step on Path"
|
||||
|
@ -295,6 +299,11 @@
|
|||
"group": "9_qlCommands",
|
||||
"when": "view == codeQLQueryHistory"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.showQueryText",
|
||||
"group": "9_qlCommands",
|
||||
"when": "view == codeQLQueryHistory"
|
||||
},
|
||||
{
|
||||
"command": "codeQLTests.showOutputDifferences",
|
||||
"group": "qltest@1",
|
||||
|
@ -355,6 +364,10 @@
|
|||
"command": "codeQLQueryHistory.showQueryLog",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.showQueryText",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "codeQLQueryHistory.setLabel",
|
||||
"when": "false"
|
||||
|
|
|
@ -6,6 +6,7 @@ import { QueryHistoryConfig } from './config';
|
|||
import { QueryWithResults } from './run-queries';
|
||||
import * as helpers from './helpers';
|
||||
import { logger } from './logging';
|
||||
import { URLSearchParams } from 'url';
|
||||
|
||||
/**
|
||||
* query-history.ts
|
||||
|
@ -18,9 +19,32 @@ import { logger } from './logging';
|
|||
|
||||
export type QueryHistoryItemOptions = {
|
||||
label?: string; // user-settable label
|
||||
queryText?: string; // stored query for quick query
|
||||
queryText?: string; // text of the selected file
|
||||
isQuickQuery?: boolean;
|
||||
}
|
||||
|
||||
const SHOW_QUERY_TEXT_MSG = `\
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// This is the text of the entire query file when it was executed for this query //
|
||||
// run. The text or dependent libraries may have changed since then. //
|
||||
// //
|
||||
// This buffer is readonly. To re-execute this query, you must open the original //
|
||||
// query file. //
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
`;
|
||||
|
||||
const SHOW_QUERY_TEXT_QUICK_EVAL_MSG = `\
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// This is the Quick Eval selection of the query file when it was executed for //
|
||||
// this query run. The text or dependent libraries may have changed since then. //
|
||||
// //
|
||||
// This buffer is readonly. To re-execute this query, you must open the original //
|
||||
// query file. //
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
`;
|
||||
|
||||
/**
|
||||
* Path to icon to display next to a failed query history item.
|
||||
*/
|
||||
|
@ -137,7 +161,7 @@ export class QueryHistoryManager {
|
|||
const textDocument = await vscode.workspace.openTextDocument(vscode.Uri.file(queryHistoryItem.query.program.queryPath));
|
||||
const editor = await vscode.window.showTextDocument(textDocument, vscode.ViewColumn.One);
|
||||
const queryText = queryHistoryItem.options.queryText;
|
||||
if (queryText !== undefined) {
|
||||
if (queryText !== undefined && queryHistoryItem.options.isQuickQuery) {
|
||||
await editor.edit(edit => edit.replace(textDocument.validateRange(
|
||||
new vscode.Range(0, 0, textDocument.lineCount, 0)), queryText)
|
||||
);
|
||||
|
@ -218,6 +242,36 @@ export class QueryHistoryManager {
|
|||
}
|
||||
}
|
||||
|
||||
async handleShowQueryText(queryHistoryItem: CompletedQuery) {
|
||||
try {
|
||||
const queryName = queryHistoryItem.queryName.endsWith('.ql') ? queryHistoryItem.queryName : queryHistoryItem.queryName + '.ql';
|
||||
const params = new URLSearchParams({
|
||||
isQuickEval: String(!!queryHistoryItem.query.quickEvalPosition),
|
||||
queryText: await this.getQueryText(queryHistoryItem)
|
||||
});
|
||||
const uri = vscode.Uri.parse(`codeql:${queryHistoryItem.query.queryID}-${queryName}?${params.toString()}`);
|
||||
const doc = await vscode.workspace.openTextDocument(uri);
|
||||
await vscode.window.showTextDocument(doc, { preview: false });
|
||||
} catch (e) {
|
||||
helpers.showAndLogErrorMessage(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async getQueryText(queryHistoryItem: CompletedQuery): Promise<string> {
|
||||
if (queryHistoryItem.options.queryText) {
|
||||
return queryHistoryItem.options.queryText;
|
||||
} else if (queryHistoryItem.query.quickEvalPosition) {
|
||||
// capture all selected lines
|
||||
const startLine = queryHistoryItem.query.quickEvalPosition.line;
|
||||
const endLine = queryHistoryItem.query.quickEvalPosition.endLine;
|
||||
const textDocument =
|
||||
await vscode.workspace.openTextDocument(queryHistoryItem.query.quickEvalPosition.fileName);
|
||||
return textDocument.getText(new vscode.Range(startLine - 1, 0, endLine, 0));
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
ctx: ExtensionContext,
|
||||
private queryHistoryConfigListener: QueryHistoryConfig,
|
||||
|
@ -240,12 +294,24 @@ export class QueryHistoryManager {
|
|||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.removeHistoryItem', this.handleRemoveHistoryItem.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.setLabel', this.handleSetLabel.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryLog', this.handleShowQueryLog.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryText', this.handleShowQueryText.bind(this)));
|
||||
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.itemClicked', async (item) => {
|
||||
return this.handleItemClicked(item);
|
||||
}));
|
||||
queryHistoryConfigListener.onDidChangeQueryHistoryConfiguration(() => {
|
||||
this.treeDataProvider.refresh();
|
||||
});
|
||||
|
||||
// displays query text in a read-only document
|
||||
vscode.workspace.registerTextDocumentContentProvider('codeql', {
|
||||
provideTextDocumentContent(uri: vscode.Uri): vscode.ProviderResult<string> {
|
||||
const params = new URLSearchParams(uri.query)
|
||||
|
||||
return (
|
||||
JSON.parse(params.get('isQuickEval') || '') ? SHOW_QUERY_TEXT_QUICK_EVAL_MSG : SHOW_QUERY_TEXT_MSG
|
||||
) + params.get('queryText');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addQuery(info: QueryWithResults): CompletedQuery {
|
||||
|
|
|
@ -14,7 +14,7 @@ export class CompletedQuery implements QueryWithResults {
|
|||
readonly query: QueryInfo;
|
||||
readonly result: messages.EvaluationResult;
|
||||
readonly database: DatabaseInfo;
|
||||
readonly logFileLocation?: string
|
||||
readonly logFileLocation?: string;
|
||||
options: QueryHistoryItemOptions;
|
||||
dispose: () => void;
|
||||
|
||||
|
|
|
@ -239,8 +239,10 @@ async function getSelectedPosition(editor: vscode.TextEditor): Promise<messages.
|
|||
// 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
|
||||
line: pos.line + 1,
|
||||
column: pos.character + 1,
|
||||
endLine: posEnd.line + 1,
|
||||
endColumn: posEnd.character + 1
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -326,6 +328,7 @@ async function promptUserToSaveChanges(document: vscode.TextDocument): Promise<b
|
|||
type SelectedQuery = {
|
||||
queryPath: string;
|
||||
quickEvalPosition?: messages.Position;
|
||||
quickEvalText?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -382,6 +385,7 @@ export async function determineSelectedQuery(selectedResourceUri: vscode.Uri | u
|
|||
}
|
||||
|
||||
let quickEvalPosition: messages.Position | undefined = undefined;
|
||||
let quickEvalText: string | undefined = undefined;
|
||||
if (quickEval) {
|
||||
if (editor == undefined) {
|
||||
throw new Error('Can\'t run quick evaluation without an active editor.');
|
||||
|
@ -392,9 +396,10 @@ export async function determineSelectedQuery(selectedResourceUri: vscode.Uri | u
|
|||
throw new Error('The selected resource for quick evaluation should match the active editor.');
|
||||
}
|
||||
quickEvalPosition = await getSelectedPosition(editor);
|
||||
quickEvalText = editor.document.getText(editor.selection);
|
||||
}
|
||||
|
||||
return { queryPath, quickEvalPosition };
|
||||
return { queryPath, quickEvalPosition, quickEvalText };
|
||||
}
|
||||
|
||||
export async function compileAndRunQueryAgainstDatabase(
|
||||
|
@ -411,12 +416,14 @@ export async function compileAndRunQueryAgainstDatabase(
|
|||
}
|
||||
|
||||
// Determine which query to run, based on the selection and the active editor.
|
||||
const { queryPath, quickEvalPosition } = await determineSelectedQuery(selectedQueryUri, quickEval);
|
||||
const { queryPath, quickEvalPosition, quickEvalText } = await determineSelectedQuery(selectedQueryUri, quickEval);
|
||||
|
||||
// If this is quick query, store the query text
|
||||
const historyItemOptions: QueryHistoryItemOptions = {};
|
||||
if (isQuickQueryPath(queryPath)) {
|
||||
historyItemOptions.queryText = await fs.readFile(queryPath, 'utf8');
|
||||
historyItemOptions.queryText = await fs.readFile(queryPath, 'utf8');
|
||||
historyItemOptions.isQuickQuery === isQuickQueryPath(queryPath);
|
||||
if (quickEval) {
|
||||
historyItemOptions.queryText = quickEvalText;
|
||||
}
|
||||
|
||||
// Get the workspace folder paths.
|
||||
|
|
Загрузка…
Ссылка в новой задаче