Add cancellation from query history view

And tweak the commands visible from the view.
This commit is contained in:
Andrew Eisenberg
2022-01-27 18:20:53 -08:00
parent 5a9b49b9bb
commit 9046844f0c
7 changed files with 65 additions and 23 deletions

View File

@@ -496,6 +496,10 @@
"command": "codeQLQueryHistory.showQueryLog",
"title": "Show Query Log"
},
{
"command": "codeQLQueryHistory.cancel",
"title": "Cancel"
},
{
"command": "codeQLQueryHistory.showQueryText",
"title": "Show Query Text"
@@ -664,7 +668,7 @@
{
"command": "codeQLQueryHistory.removeHistoryItem",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory"
"when": "view == codeQLQueryHistory && viewItem != inProgressResultsItem"
},
{
"command": "codeQLQueryHistory.setLabel",
@@ -674,12 +678,12 @@
{
"command": "codeQLQueryHistory.compareWith",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory"
"when": "view == codeQLQueryHistory && (viewItem == rawResultsItem || viewItem == interpretedResultsItem)"
},
{
"command": "codeQLQueryHistory.showQueryLog",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory"
"when": "view == codeQLQueryHistory && (viewItem == rawResultsItem || viewItem == interpretedResultsItem)"
},
{
"command": "codeQLQueryHistory.showQueryText",
@@ -689,7 +693,7 @@
{
"command": "codeQLQueryHistory.viewCsvResults",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory && viewItem != interpretedResultsItem"
"when": "view == codeQLQueryHistory && viewItem == rawResultsItem"
},
{
"command": "codeQLQueryHistory.viewCsvAlerts",
@@ -704,12 +708,12 @@
{
"command": "codeQLQueryHistory.viewDil",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory"
"when": "view == codeQLQueryHistory && (viewItem == rawResultsItem || viewItem == interpretedResultsItem)"
},
{
"command": "codeQL.previewQueryHelp",
"command": "codeQLQueryHistory.cancel",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory && resourceScheme == .qhelp && isWorkspaceTrusted"
"when": "view == codeQLQueryHistory && viewItem == inProgressResultsItem"
},
{
"command": "codeQLTests.showOutputDifferences",
@@ -862,6 +866,10 @@
"command": "codeQLQueryHistory.showQueryLog",
"when": "false"
},
{
"command": "codeQLQueryHistory.cancel",
"when": "false"
},
{
"command": "codeQLQueryHistory.showQueryText",
"when": "false"

View File

@@ -1,5 +1,6 @@
import {
CancellationToken,
CancellationTokenSource,
commands,
Disposable,
ExtensionContext,
@@ -498,8 +499,12 @@ async function activateWithInstalledDistribution(
databaseUri: databaseItem.databaseUri.toString(),
};
// handle cancellation from the history view.
const source = new CancellationTokenSource();
token.onCancellationRequested(() => source.cancel());
const initialInfo = await createInitialQueryInfo(selectedQuery, databaseInfo, quickEval, range);
const item = new FullQueryInfo(initialInfo, queryHistoryConfigurationListener);
const item = new FullQueryInfo(initialInfo, queryHistoryConfigurationListener, source);
qhm.addQuery(item);
try {
const completedQueryInfo = await compileAndRunQueryAgainstDatabase(
@@ -508,7 +513,7 @@ async function activateWithInstalledDistribution(
databaseItem,
initialInfo,
progress,
token,
source.token,
);
item.completeThisQuery(completedQueryInfo);
await showResultsForCompletedQuery(item as FullCompletedQueryInfo, WebviewReveal.NotForced);
@@ -519,6 +524,7 @@ async function activateWithInstalledDistribution(
throw e;
} finally {
qhm.refreshTreeView();
source.dispose();
}
}
}

View File

@@ -119,24 +119,24 @@ export class HistoryTreeDataProvider extends DisposableObject {
arguments: [element],
};
// Mark this query history item according to whether it has a
// SARIF file so that we can make context menu items conditionally
// available.
const hasResults = await element.completedQuery?.query.hasInterpretedResults();
treeItem.contextValue = hasResults
? 'interpretedResultsItem'
: 'rawResultsItem';
// Populate the icon and the context value. We use the context value to
// control which commands are visible in the context menu.
let hasResults;
switch (element.status) {
case QueryStatus.InProgress:
// TODO this is not a good icon.
treeItem.iconPath = new ThemeIcon('sync~spin');
treeItem.contextValue = 'inProgressResultsItem';
break;
case QueryStatus.Completed:
hasResults = await element.completedQuery?.query.hasInterpretedResults();
treeItem.iconPath = this.localSuccessIconPath;
treeItem.contextValue = hasResults
? 'interpretedResultsItem'
: 'rawResultsItem';
break;
case QueryStatus.Failed:
treeItem.iconPath = this.failedIconPath;
treeItem.contextValue = 'cancelledResultsItem';
break;
default:
assertNever(element.status);
@@ -332,6 +332,12 @@ export class QueryHistoryManager extends DisposableObject {
this.handleShowQueryLog.bind(this)
)
);
this.push(
commandRunner(
'codeQLQueryHistory.cancel',
this.handleCancel.bind(this)
)
);
this.push(
commandRunner(
'codeQLQueryHistory.showQueryText',
@@ -439,7 +445,7 @@ export class QueryHistoryManager extends DisposableObject {
const { finalSingleItem, finalMultiSelect } = this.determineSelection(singleItem, multiSelect);
(finalMultiSelect || [finalSingleItem]).forEach((item) => {
// TODO: Removing in progress queries is not supported yet
// Removing in progress queries is not supported yet
if (item.status !== QueryStatus.InProgress) {
this.treeDataProvider.remove(item);
item.completedQuery?.dispose();
@@ -568,6 +574,19 @@ export class QueryHistoryManager extends DisposableObject {
}
}
async handleCancel(
singleItem: FullQueryInfo,
multiSelect: FullQueryInfo[]
) {
const { finalSingleItem, finalMultiSelect } = this.determineSelection(singleItem, multiSelect);
(finalMultiSelect || [finalSingleItem]).forEach((item) => {
if (item.status === QueryStatus.InProgress) {
item.cancel();
}
});
}
async handleShowQueryText(
singleItem: FullQueryInfo,
multiSelect: FullQueryInfo[]

View File

@@ -1,4 +1,4 @@
import { env } from 'vscode';
import { CancellationTokenSource, env } from 'vscode';
import { QueryWithResults, tmpDir, QueryEvaluationInfo } from './run-queries';
import * as messages from './pure/messages';
@@ -180,10 +180,15 @@ export class FullQueryInfo {
constructor(
public readonly initialInfo: InitialQueryInfo,
private readonly config: QueryHistoryConfig,
private readonly source: CancellationTokenSource
) {
/**/
}
cancel() {
this.source.cancel();
}
get startTime() {
return this.initialInfo.start.toLocaleString(env.language);
}

View File

@@ -695,7 +695,8 @@ let queryId = 0;
export async function createInitialQueryInfo(
selectedQueryUri: Uri | undefined,
databaseInfo: DatabaseInfo,
isQuickEval: boolean, range?: Range
isQuickEval: boolean,
range?: Range
): Promise<InitialQueryInfo> {
// Determine which query to run, based on the selection and the active editor.
const { queryPath, quickEvalPosition, quickEvalText } = await determineSelectedQuery(selectedQueryUri, isQuickEval, range);

View File

@@ -515,7 +515,8 @@ describe('query-history', () => {
start: new Date(),
queryPath: 'hucairz'
} as InitialQueryInfo,
configListener
configListener,
{} as vscode.CancellationTokenSource
);
if (queryWitbResults) {

View File

@@ -12,6 +12,7 @@ import { EvaluationResult, QueryResultType } from '../../pure/messages';
import { SortDirection, SortedResultSetInfo } from '../../pure/interface-types';
import { CodeQLCliServer, SourceInfo } from '../../cli';
import { env } from 'process';
import { CancellationTokenSource } from 'vscode';
chai.use(chaiAsPromised);
const expect = chai.expect;
@@ -266,7 +267,8 @@ describe('query-results', () => {
start: new Date(),
queryPath: 'path/to/hucairz'
} as InitialQueryInfo,
mockQueryHistoryConfig()
mockQueryHistoryConfig(),
{} as CancellationTokenSource
);
if (queryWitbResults) {