Skip to content

Commit cf53645

Browse files
authored
Merge pull request #210 from jcreedcmu/jcreed/quick-query
Teach extension to do Quick Query
2 parents d69d7dc + 27a3efe commit cf53645

File tree

8 files changed

+223
-13
lines changed

8 files changed

+223
-13
lines changed

extensions/ql-vscode/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## 1.0.4
44

55
- Disable word-based autocomplete by default.
6+
- Add command `CodeQL: Quick Query` for easy query creation without
7+
having to choose a place in the filesystem to store the query file.
68

79
## 1.0.3 - 13 January 2020
810

extensions/ql-vscode/package.json

+7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"onCommand:codeQL.setCurrentDatabase",
2828
"onCommand:codeQLDatabases.chooseDatabase",
2929
"onCommand:codeQLDatabases.setCurrentDatabase",
30+
"onCommand:codeQL.quickQuery",
3031
"onWebviewPanel:resultsView",
3132
"onFileSystem:codeql-zip-archive"
3233
],
@@ -142,6 +143,10 @@
142143
"command": "codeQL.quickEval",
143144
"title": "CodeQL: Quick Evaluation"
144145
},
146+
{
147+
"command": "codeQL.quickQuery",
148+
"title": "CodeQL: Quick Query"
149+
},
145150
{
146151
"command": "codeQL.chooseDatabase",
147152
"title": "CodeQL: Choose Database",
@@ -341,6 +346,7 @@
341346
"classnames": "~2.2.6",
342347
"fs-extra": "^8.1.0",
343348
"glob-promise": "^3.4.0",
349+
"js-yaml": "^3.12.0",
344350
"node-fetch": "~2.6.0",
345351
"react": "^16.8.6",
346352
"react-dom": "^16.8.6",
@@ -359,6 +365,7 @@
359365
"@types/glob": "^7.1.1",
360366
"@types/google-protobuf": "^3.2.7",
361367
"@types/gulp": "^4.0.6",
368+
"@types/js-yaml": "~3.12.1",
362369
"@types/jszip": "~3.1.6",
363370
"@types/mocha": "~5.2.7",
364371
"@types/node": "^12.0.8",

extensions/ql-vscode/src/cli.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ export interface UpgradesInfo {
5050
finalDbscheme: string;
5151
}
5252

53+
/**
54+
* The expected output of `codeql resolve qlpacks`.
55+
*/
56+
export type QlpacksInfo = { [name: string]: string[] };
57+
5358
/**
5459
* The expected output of `codeql resolve metadata`.
5560
*/
@@ -396,7 +401,6 @@ export class CodeQLCliServer implements Disposable {
396401
"Resolving database");
397402
}
398403

399-
400404
/**
401405
* Gets information necessary for upgrading a database.
402406
* @param dbScheme the path to the dbscheme of the database to be upgraded.
@@ -412,6 +416,21 @@ export class CodeQLCliServer implements Disposable {
412416
"Resolving database upgrade scripts",
413417
);
414418
}
419+
420+
/**
421+
* Gets information about available qlpacks
422+
* @param searchPath A list of directories to search for qlpacks
423+
* @returns A dictionary mapping qlpack name to the directory it comes from
424+
*/
425+
resolveQlpacks(searchPath: string[]): Promise<QlpacksInfo> {
426+
const args = ['--additional-packs', searchPath.join(path.delimiter)];
427+
428+
return this.runJsonCodeQlCliCommand<QlpacksInfo>(
429+
['resolve', 'qlpacks'],
430+
args,
431+
"Resolving qlpack information",
432+
);
433+
}
415434
}
416435

417436
/**

extensions/ql-vscode/src/databases.ts

+13
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,11 @@ export interface DatabaseItem {
236236
*/
237237
getSourceLocationPrefix(server: cli.CodeQLCliServer): Promise<string>;
238238

239+
/**
240+
* Returns dataset folder of exported database.
241+
*/
242+
getDatasetFolder(server: cli.CodeQLCliServer): Promise<string>;
243+
239244
/**
240245
* Returns the root uri of the virtual filesystem for this database's source archive,
241246
* as displayed in the filesystem explorer.
@@ -385,6 +390,14 @@ class DatabaseItemImpl implements DatabaseItem {
385390
return dbInfo.sourceLocationPrefix;
386391
}
387392

393+
/**
394+
* Returns path to dataset folder of database.
395+
*/
396+
public async getDatasetFolder(server: cli.CodeQLCliServer): Promise<string> {
397+
const dbInfo = await this.getDbInfo(server);
398+
return dbInfo.datasetFolder;
399+
}
400+
388401
/**
389402
* Returns the root uri of the virtual filesystem for this database's source archive.
390403
*/

extensions/ql-vscode/src/extension.ts

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { QueryHistoryManager } from './query-history';
1717
import * as qsClient from './queryserver-client';
1818
import { CodeQLCliServer } from './cli';
1919
import { assertNever } from './helpers-pure';
20+
import { displayQuickQuery } from './quick-query';
2021

2122
/**
2223
* extension.ts
@@ -305,6 +306,7 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
305306

306307
ctx.subscriptions.push(commands.registerCommand('codeQL.runQuery', async (uri: Uri | undefined) => await compileAndRunQuery(false, uri)));
307308
ctx.subscriptions.push(commands.registerCommand('codeQL.quickEval', async (uri: Uri | undefined) => await compileAndRunQuery(true, uri)));
309+
ctx.subscriptions.push(commands.registerCommand('codeQL.quickQuery', async () => displayQuickQuery(ctx, cliServer, databaseUI)));
308310

309311
ctx.subscriptions.push(client.start());
310312
}

extensions/ql-vscode/src/queries.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { logger } from './logging';
1212
import * as messages from './messages';
1313
import * as qsClient from './queryserver-client';
1414
import { promisify } from 'util';
15+
import { QueryHistoryItemOptions } from './query-history';
16+
import { isQuickQueryPath } from './quick-query';
1517

1618
/**
1719
* queries.ts
@@ -205,6 +207,7 @@ export interface EvaluationInfo {
205207
query: QueryInfo;
206208
result: messages.EvaluationResult;
207209
database: DatabaseInfo;
210+
historyItemOptions: QueryHistoryItemOptions;
208211
}
209212

210213
/**
@@ -393,7 +396,7 @@ export async function clearCacheInDatabase(qs: qsClient.QueryServerClient, dbIte
393396
title: "Clearing Cache",
394397
cancellable: false,
395398
}, (progress, token) =>
396-
qs.sendRequest(messages.clearCache, params, token, progress)
399+
qs.sendRequest(messages.clearCache, params, token, progress)
397400
);
398401
}
399402

@@ -574,6 +577,12 @@ export async function compileAndRunQueryAgainstDatabase(
574577
// Determine which query to run, based on the selection and the active editor.
575578
const { queryPath, quickEvalPosition } = await determineSelectedQuery(selectedQueryUri, quickEval);
576579

580+
// If this is quick query, store the query text
581+
const historyItemOptions: QueryHistoryItemOptions = {};
582+
if (isQuickQueryPath(queryPath)) {
583+
historyItemOptions.queryText = await fs.readFile(queryPath, 'utf8');
584+
}
585+
577586
// Get the workspace folder paths.
578587
const diskWorkspaceFolders = helpers.getOnDiskWorkspaceFolders();
579588
// Figure out the library path for the query.
@@ -616,7 +625,6 @@ export async function compileAndRunQueryAgainstDatabase(
616625

617626
const errors = await query.compile(qs);
618627

619-
620628
if (errors.length == 0) {
621629
const result = await query.run(qs);
622630
return {
@@ -625,7 +633,8 @@ export async function compileAndRunQueryAgainstDatabase(
625633
database: {
626634
name: db.name,
627635
databaseUri: db.databaseUri.toString(true)
628-
}
636+
},
637+
historyItemOptions
629638
};
630639
} else {
631640
// Error dialogs are limited in size and scrollability,
@@ -650,6 +659,7 @@ export async function compileAndRunQueryAgainstDatabase(
650659
" and the query and database use the same target language. For more details on the error, go to View > Output," +
651660
" and choose CodeQL Query Server from the dropdown.");
652661
}
662+
653663
return {
654664
query,
655665
result: {
@@ -662,7 +672,8 @@ export async function compileAndRunQueryAgainstDatabase(
662672
database: {
663673
name: db.name,
664674
databaseUri: db.databaseUri.toString(true)
665-
}
675+
},
676+
historyItemOptions,
666677
};
667678
}
668679
}

extensions/ql-vscode/src/query-history.ts

+19-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ import { QueryHistoryConfig } from './config';
1313
* `TreeDataProvider` subclass below.
1414
*/
1515

16+
export type QueryHistoryItemOptions = {
17+
label?: string, // user-settable label
18+
queryText?: string, // stored query for quick query
19+
}
20+
1621
/**
1722
* One item in the user-displayed list of queries that have been run.
1823
*/
@@ -25,7 +30,7 @@ export class QueryHistoryItem {
2530
constructor(
2631
info: EvaluationInfo,
2732
public config: QueryHistoryConfig,
28-
public label?: string, // user-settable label
33+
public options: QueryHistoryItemOptions = info.historyItemOptions,
2934
) {
3035
this.queryName = helpers.getQueryName(info);
3136
this.databaseName = info.database.name;
@@ -65,8 +70,8 @@ export class QueryHistoryItem {
6570
}
6671

6772
getLabel(): string {
68-
if (this.label !== undefined)
69-
return this.label;
73+
if (this.options.label !== undefined)
74+
return this.options.label;
7075
return this.config.format;
7176
}
7277

@@ -179,9 +184,15 @@ export class QueryHistoryManager {
179184
}
180185
}
181186

182-
async handleOpenQuery(queryHistoryItem: QueryHistoryItem) {
187+
async handleOpenQuery(queryHistoryItem: QueryHistoryItem): Promise<void> {
183188
const textDocument = await vscode.workspace.openTextDocument(vscode.Uri.file(queryHistoryItem.info.query.program.queryPath));
184-
await vscode.window.showTextDocument(textDocument, vscode.ViewColumn.One);
189+
const editor = await vscode.window.showTextDocument(textDocument, vscode.ViewColumn.One);
190+
const queryText = queryHistoryItem.options.queryText;
191+
if (queryText !== undefined) {
192+
await editor.edit(edit => edit.replace(textDocument.validateRange(
193+
new vscode.Range(0, 0, textDocument.lineCount, 0)), queryText)
194+
);
195+
}
185196
}
186197

187198
async handleRemoveHistoryItem(queryHistoryItem: QueryHistoryItem) {
@@ -203,9 +214,9 @@ export class QueryHistoryManager {
203214
if (response !== undefined) {
204215
if (response === '')
205216
// Interpret empty string response as "go back to using default"
206-
queryHistoryItem.label = undefined;
217+
queryHistoryItem.options.label = undefined;
207218
else
208-
queryHistoryItem.label = response;
219+
queryHistoryItem.options.label = response;
209220
this.treeDataProvider.refresh();
210221
}
211222
}
@@ -277,7 +288,7 @@ export class QueryHistoryManager {
277288
const current = this.treeDataProvider.getCurrent();
278289
if (current != undefined) {
279290
// We must fire the onDidChangeTreeData event to ensure the current element can be selected
280-
// using `reveal` if the tree view was not visible when the current element was added.
291+
// using `reveal` if the tree view was not visible when the current element was added.
281292
this.treeDataProvider.refresh();
282293
this.treeView.reveal(current);
283294
}

0 commit comments

Comments
 (0)