Skip to content

Commit 7f472ac

Browse files
committed
Add the commandRunner
The commandRunner wraps all vscode command registrations. It provides uniform error handling and an optional progress monitor. In general, progress monitors should only be created by the commandRunner and passed through to the locations that use it.
1 parent 43d5ee7 commit 7f472ac

13 files changed

Lines changed: 658 additions & 461 deletions

File tree

extensions/ql-vscode/src/astViewer.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
ExtensionContext,
44
TreeDataProvider,
55
EventEmitter,
6-
commands,
76
Event,
87
ProviderResult,
98
TreeItemCollapsibleState,
@@ -19,6 +18,7 @@ import { DatabaseItem } from './databases';
1918
import { UrlValue, BqrsId } from './bqrs-cli-types';
2019
import { showLocation } from './interface-utils';
2120
import { isStringLoc, isWholeFileLoc, isLineColumnLoc } from './bqrs-utils';
21+
import { commandRunner } from './helpers';
2222

2323

2424
export interface AstItem {
@@ -45,10 +45,10 @@ class AstViewerDataProvider implements TreeDataProvider<AstItem> {
4545
this._onDidChangeTreeData.event;
4646

4747
constructor() {
48-
commands.registerCommand('codeQLAstViewer.gotoCode',
49-
async (item: AstItem) => {
50-
await showLocation(item.fileLocation);
51-
});
48+
commandRunner('codeQLAstViewer.gotoCode',
49+
async (item: AstItem) => {
50+
await showLocation(item.fileLocation);
51+
});
5252
}
5353

5454
refresh(): void {
@@ -109,7 +109,7 @@ export class AstViewer {
109109
showCollapseAll: true
110110
});
111111

112-
commands.registerCommand('codeQLAstViewer.clear', () => {
112+
commandRunner('codeQLAstViewer.clear', async () => {
113113
this.clear();
114114
});
115115

extensions/ql-vscode/src/contextual/locationFinder.ts

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import * as vscode from 'vscode';
22

33
import { decodeSourceArchiveUri, zipArchiveScheme } from '../archive-filesystem-provider';
4-
import { ColumnKindCode, EntityValue, getResultSetSchema } from '../bqrs-cli-types';
4+
import { ColumnKindCode, EntityValue, getResultSetSchema, ResultSetSchema } from '../bqrs-cli-types';
55
import { CodeQLCliServer } from '../cli';
66
import { DatabaseManager, DatabaseItem } from '../databases';
77
import fileRangeFromURI from './fileRangeFromURI';
88
import * as messages from '../messages';
99
import { QueryServerClient } from '../queryserver-client';
1010
import { QueryWithResults, compileAndRunQueryAgainstDatabase } from '../run-queries';
11+
import { ProgressCallback } from '../helpers';
1112
import { KeyType } from './keyType';
1213
import { qlpackOfDatabase, resolveQueries } from './queryResolver';
1314

@@ -28,6 +29,8 @@ export interface FullLocationLink extends vscode.LocationLink {
2829
* @param dbm The database manager
2930
* @param uriString The selected source file and location
3031
* @param keyType The contextual query type to run
32+
* @param progress A progress callback
33+
* @param token A CancellationToken
3134
* @param filter A function that will filter extraneous results
3235
*/
3336
export async function getLocationsForUriString(
@@ -36,37 +39,42 @@ export async function getLocationsForUriString(
3639
dbm: DatabaseManager,
3740
uriString: string,
3841
keyType: KeyType,
42+
progress: ProgressCallback,
43+
token: vscode.CancellationToken,
3944
filter: (src: string, dest: string) => boolean
4045
): Promise<FullLocationLink[]> {
4146
const uri = decodeSourceArchiveUri(vscode.Uri.parse(uriString));
4247
const sourceArchiveUri = vscode.Uri.file(uri.sourceArchiveZipPath).with({ scheme: zipArchiveScheme });
4348

4449
const db = dbm.findDatabaseItemBySourceArchive(sourceArchiveUri);
45-
if (db) {
46-
const qlpack = await qlpackOfDatabase(cli, db);
47-
if (qlpack === undefined) {
48-
throw new Error('Can\'t infer qlpack from database source archive');
49-
}
50-
const links: FullLocationLink[] = [];
51-
for (const query of await resolveQueries(cli, qlpack, keyType)) {
52-
const templates: messages.TemplateDefinitions = {
53-
[TEMPLATE_NAME]: {
54-
values: {
55-
tuples: [[{
56-
stringValue: uri.pathWithinSourceArchive
57-
}]]
58-
}
59-
}
60-
};
61-
const results = await compileAndRunQueryAgainstDatabase(cli, qs, db, false, vscode.Uri.file(query), templates);
62-
if (results.result.resultType == messages.QueryResultType.SUCCESS) {
63-
links.push(...await getLinksFromResults(results, cli, db, filter));
64-
}
65-
}
66-
return links;
67-
} else {
50+
if (!db) {
6851
return [];
6952
}
53+
54+
const qlpack = await qlpackOfDatabase(cli, db);
55+
if (qlpack === undefined) {
56+
throw new Error('Can\'t infer qlpack from database source archive');
57+
}
58+
const templates = createTemplates(uri.pathWithinSourceArchive);
59+
60+
const links: FullLocationLink[] = [];
61+
for (const query of await resolveQueries(cli, qlpack, keyType)) {
62+
const results = await compileAndRunQueryAgainstDatabase(
63+
cli,
64+
qs,
65+
db,
66+
false,
67+
vscode.Uri.file(query),
68+
progress,
69+
token,
70+
templates
71+
);
72+
73+
if (results.result.resultType == messages.QueryResultType.SUCCESS) {
74+
links.push(...await getLinksFromResults(results, cli, db, filter));
75+
}
76+
}
77+
return links;
7078
}
7179

7280
async function getLinksFromResults(
@@ -79,10 +87,7 @@ async function getLinksFromResults(
7987
const bqrsPath = results.query.resultsPaths.resultsPath;
8088
const info = await cli.bqrsInfo(bqrsPath);
8189
const selectInfo = getResultSetSchema(SELECT_QUERY_NAME, info);
82-
if (selectInfo && selectInfo.columns.length == 3
83-
&& selectInfo.columns[0].kind == ColumnKindCode.ENTITY
84-
&& selectInfo.columns[1].kind == ColumnKindCode.ENTITY
85-
&& selectInfo.columns[2].kind == ColumnKindCode.STRING) {
90+
if (isValidSelect(selectInfo)) {
8691
// TODO: Page this
8792
const allTuples = await cli.bqrsDecode(bqrsPath, SELECT_QUERY_NAME);
8893
for (const tuple of allTuples.tuples) {
@@ -101,3 +106,22 @@ async function getLinksFromResults(
101106
}
102107
return localLinks;
103108
}
109+
110+
function createTemplates(path: string): messages.TemplateDefinitions {
111+
return {
112+
[TEMPLATE_NAME]: {
113+
values: {
114+
tuples: [[{
115+
stringValue: path
116+
}]]
117+
}
118+
}
119+
};
120+
}
121+
122+
function isValidSelect(selectInfo: ResultSetSchema | undefined) {
123+
return selectInfo && selectInfo.columns.length == 3
124+
&& selectInfo.columns[0].kind == ColumnKindCode.ENTITY
125+
&& selectInfo.columns[1].kind == ColumnKindCode.ENTITY
126+
&& selectInfo.columns[2].kind == ColumnKindCode.STRING;
127+
}

extensions/ql-vscode/src/contextual/templateProvider.ts

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as vscode from 'vscode';
33
import { decodeSourceArchiveUri, zipArchiveScheme } from '../archive-filesystem-provider';
44
import { CodeQLCliServer } from '../cli';
55
import { DatabaseManager } from '../databases';
6-
import { CachedOperation } from '../helpers';
6+
import { CachedOperation, ProgressCallback, withProgress } from '../helpers';
77
import * as messages from '../messages';
88
import { QueryServerClient } from '../queryserver-client';
99
import { compileAndRunQueryAgainstDatabase, QueryWithResults } from '../run-queries';
@@ -44,14 +44,22 @@ export class TemplateQueryDefinitionProvider implements vscode.DefinitionProvide
4444
}
4545

4646
private async getDefinitions(uriString: string): Promise<vscode.LocationLink[]> {
47-
return getLocationsForUriString(
48-
this.cli,
49-
this.qs,
50-
this.dbm,
51-
uriString,
52-
KeyType.DefinitionQuery,
53-
(src, _dest) => src === uriString
54-
);
47+
return await withProgress({
48+
location: vscode.ProgressLocation.Notification,
49+
cancellable: true,
50+
title: 'Finding definitions'
51+
}, async (progress, token) => {
52+
return getLocationsForUriString(
53+
this.cli,
54+
this.qs,
55+
this.dbm,
56+
uriString,
57+
KeyType.DefinitionQuery,
58+
progress,
59+
token,
60+
(src, _dest) => src === uriString
61+
);
62+
});
5563
}
5664
}
5765

@@ -83,14 +91,22 @@ export class TemplateQueryReferenceProvider implements vscode.ReferenceProvider
8391
}
8492

8593
private async getReferences(uriString: string): Promise<FullLocationLink[]> {
86-
return getLocationsForUriString(
87-
this.cli,
88-
this.qs,
89-
this.dbm,
90-
uriString,
91-
KeyType.ReferenceQuery,
92-
(_src, dest) => dest === uriString
93-
);
94+
return await withProgress({
95+
location: vscode.ProgressLocation.Notification,
96+
cancellable: true,
97+
title: 'Finding references'
98+
}, async (progress, token) => {
99+
return getLocationsForUriString(
100+
this.cli,
101+
this.qs,
102+
this.dbm,
103+
uriString,
104+
KeyType.DefinitionQuery,
105+
progress,
106+
token,
107+
(src, _dest) => src === uriString
108+
);
109+
});
94110
}
95111
}
96112

@@ -101,6 +117,10 @@ export class TemplatePrintAstProvider {
101117
private cli: CodeQLCliServer,
102118
private qs: QueryServerClient,
103119
private dbm: DatabaseManager,
120+
121+
// Note: progress and token are only used if a cached value is not available
122+
private progress: ProgressCallback,
123+
private token: vscode.CancellationToken
104124
) {
105125
this.cache = new CachedOperation<QueryWithResults | undefined>(this.getAst.bind(this));
106126
}
@@ -157,12 +177,15 @@ export class TemplatePrintAstProvider {
157177
}
158178
}
159179
};
180+
160181
return await compileAndRunQueryAgainstDatabase(
161182
this.cli,
162183
this.qs,
163184
db,
164185
false,
165186
vscode.Uri.file(query),
187+
this.progress,
188+
this.token,
166189
templates
167190
);
168191
}

0 commit comments

Comments
 (0)