Skip to content

Commit 8d0d4bb

Browse files
authored
Merge pull request #176 from jcreedcmu/jcreed/label
Improvements to Query History Labels
2 parents 7fc18d3 + 4bbd5af commit 8d0d4bb

File tree

4 files changed

+114
-12
lines changed

4 files changed

+114
-12
lines changed

extensions/ql-vscode/package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@
111111
"type": "boolean",
112112
"default": false,
113113
"description": "Enable debug logging and tuple counting when running CodeQL queries. This information is useful for debugging query performance."
114+
},
115+
"codeQL.queryHistory.format": {
116+
"type": "string",
117+
"default": "[%t] %q on %d - %s",
118+
"description": "Default string for how to label query history items. %t is the time of the query, %q is the query name, %d is the database name, and %s is a status string."
114119
}
115120
}
116121
},
@@ -178,6 +183,10 @@
178183
{
179184
"command": "codeQLQueryResults.previousPathStep",
180185
"title": "CodeQL: Show Previous Step on Path"
186+
},
187+
{
188+
"command": "codeQLQueryHistory.setLabel",
189+
"title": "Set Label"
181190
}
182191
],
183192
"menus": {
@@ -213,6 +222,11 @@
213222
"command": "codeQLQueryHistory.removeHistoryItem",
214223
"group": "9_qlCommands",
215224
"when": "view == codeQLQueryHistory"
225+
},
226+
{
227+
"command": "codeQLQueryHistory.setLabel",
228+
"group": "9_qlCommands",
229+
"when": "view == codeQLQueryHistory"
216230
}
217231
],
218232
"explorer/context": [
@@ -259,6 +273,10 @@
259273
{
260274
"command": "codeQLQueryHistory.itemClicked",
261275
"when": "false"
276+
},
277+
{
278+
"command": "codeQLQueryHistory.setLabel",
279+
"when": "false"
262280
}
263281
],
264282
"editor/context": [

extensions/ql-vscode/src/config.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ const DISTRIBUTION_SETTING = new Setting('cli', ROOT_SETTING);
3737
const CUSTOM_CODEQL_PATH_SETTING = new Setting('executablePath', DISTRIBUTION_SETTING);
3838
const INCLUDE_PRERELEASE_SETTING = new Setting('includePrerelease', DISTRIBUTION_SETTING);
3939
const PERSONAL_ACCESS_TOKEN_SETTING = new Setting('personalAccessToken', DISTRIBUTION_SETTING);
40+
const QUERY_HISTORY_SETTING = new Setting('queryHistory', ROOT_SETTING);
41+
const QUERY_HISTORY_FORMAT_SETTING = new Setting('format', QUERY_HISTORY_SETTING);
4042

4143
/** When these settings change, the distribution should be updated. */
4244
const DISTRIBUTION_CHANGE_SETTINGS = [CUSTOM_CODEQL_PATH_SETTING, INCLUDE_PRERELEASE_SETTING, PERSONAL_ACCESS_TOKEN_SETTING];
@@ -70,6 +72,14 @@ export interface QueryServerConfig {
7072
onDidChangeQueryServerConfiguration?: Event<void>;
7173
}
7274

75+
/** When these settings change, the query history should be refreshed. */
76+
const QUERY_HISTORY_SETTINGS = [QUERY_HISTORY_FORMAT_SETTING];
77+
78+
export interface QueryHistoryConfig {
79+
format: string,
80+
onDidChangeQueryHistoryConfiguration: Event<void>;
81+
}
82+
7383
abstract class ConfigListener extends DisposableObject {
7484
protected readonly _onDidChangeConfiguration = this.push(new EventEmitter<void>());
7585

@@ -176,3 +186,17 @@ export class QueryServerConfigListener extends ConfigListener implements QuerySe
176186
this.handleDidChangeConfigurationForRelevantSettings(QUERY_SERVER_RESTARTING_SETTINGS, e);
177187
}
178188
}
189+
190+
export class QueryHistoryConfigListener extends ConfigListener implements QueryHistoryConfig {
191+
protected handleDidChangeConfiguration(e: ConfigurationChangeEvent): void {
192+
this.handleDidChangeConfigurationForRelevantSettings(QUERY_HISTORY_SETTINGS, e);
193+
}
194+
195+
public get onDidChangeQueryHistoryConfiguration(): Event<void> {
196+
return this._onDidChangeConfiguration.event;
197+
}
198+
199+
public get format(): string {
200+
return QUERY_HISTORY_FORMAT_SETTING.getValue<string>();
201+
}
202+
}

extensions/ql-vscode/src/extension.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { commands, Disposable, ExtensionContext, extensions, ProgressLocation, ProgressOptions, window as Window, Uri } from 'vscode';
22
import { ErrorCodes, LanguageClient, ResponseError } from 'vscode-languageclient';
33
import * as archiveFilesystemProvider from './archive-filesystem-provider';
4-
import { DistributionConfigListener, QueryServerConfigListener } from './config';
4+
import { DistributionConfigListener, QueryServerConfigListener, QueryHistoryConfigListener } from './config';
55
import { DatabaseManager } from './databases';
66
import { DatabaseUI } from './databases-ui';
77
import { DistributionUpdateCheckResultKind, DistributionManager, FindDistributionResult, FindDistributionResultKind, GithubApiError,
@@ -11,7 +11,7 @@ import { spawnIdeServer } from './ide-server';
1111
import { InterfaceManager, WebviewReveal } from './interface';
1212
import { ideServerLogger, logger, queryServerLogger } from './logging';
1313
import { compileAndRunQueryAgainstDatabase, EvaluationInfo, tmpDirDisposal, UserCancellationException } from './queries';
14-
import { QueryHistoryItem, QueryHistoryManager } from './query-history';
14+
import { QueryHistoryManager } from './query-history';
1515
import * as qsClient from './queryserver-client';
1616
import { CodeQLCliServer } from './cli';
1717
import { assertNever } from './helpers-pure';
@@ -78,7 +78,7 @@ export async function activate(ctx: ExtensionContext): Promise<void> {
7878
const distributionManager = new DistributionManager(ctx, distributionConfigListener, DEFAULT_DISTRIBUTION_VERSION_CONSTRAINT);
7979

8080
const shouldUpdateOnNextActivationKey = "shouldUpdateOnNextActivation";
81-
81+
8282
registerErrorStubs(ctx, [checkForUpdatesCommand], command => () => {
8383
helpers.showAndLogErrorMessage(`Can't execute ${command}: waiting to finish loading CodeQL CLI.`);
8484
});
@@ -244,7 +244,12 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
244244
const databaseUI = new DatabaseUI(ctx, cliServer, dbm, qs);
245245
ctx.subscriptions.push(databaseUI);
246246

247-
const qhm = new QueryHistoryManager(ctx, async item => showResultsForInfo(item.info, WebviewReveal.Forced));
247+
const queryHistoryConfigurationListener = new QueryHistoryConfigListener();
248+
const qhm = new QueryHistoryManager(
249+
ctx,
250+
queryHistoryConfigurationListener,
251+
async item => showResultsForInfo(item.info, WebviewReveal.Forced)
252+
);
248253
const intm = new InterfaceManager(ctx, dbm, cliServer, queryServerLogger);
249254
ctx.subscriptions.push(intm);
250255
archiveFilesystemProvider.activate(ctx);
@@ -262,7 +267,7 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
262267
}
263268
const info = await compileAndRunQueryAgainstDatabase(cliServer, qs, dbItem, quickEval, selectedQuery);
264269
await showResultsForInfo(info, WebviewReveal.NotForced);
265-
qhm.push(new QueryHistoryItem(info));
270+
qhm.push(info);
266271
}
267272
catch (e) {
268273
if (e instanceof UserCancellationException) {

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

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ExtensionContext, window as Window } from 'vscode';
33
import { EvaluationInfo } from './queries';
44
import * as helpers from './helpers';
55
import * as messages from './messages';
6+
import { QueryHistoryConfig } from './config';
67
/**
78
* query-history.ts
89
* ------------
@@ -21,7 +22,11 @@ export class QueryHistoryItem {
2122
databaseName: string;
2223
info: EvaluationInfo;
2324

24-
constructor(info: EvaluationInfo) {
25+
constructor(
26+
info: EvaluationInfo,
27+
public config: QueryHistoryConfig,
28+
public label?: string, // user-settable label
29+
) {
2530
this.queryName = helpers.getQueryName(info);
2631
this.databaseName = info.database.name;
2732
this.info = info;
@@ -44,9 +49,29 @@ export class QueryHistoryItem {
4449
}
4550
}
4651

52+
interpolate(template: string): string {
53+
const { databaseName, queryName, time, statusString } = this;
54+
const replacements: { [k: string]: string } = {
55+
t: time,
56+
q: queryName,
57+
d: databaseName,
58+
s: statusString,
59+
'%': '%',
60+
};
61+
return template.replace(/%(.)/g, (match, key) => {
62+
const replacement = replacements[key];
63+
return replacement !== undefined ? replacement : match;
64+
});
65+
}
66+
67+
getLabel(): string {
68+
if (this.label !== undefined)
69+
return this.label;
70+
return this.config.format;
71+
}
72+
4773
toString(): string {
48-
const { databaseName, queryName, time } = this;
49-
return `[${time}] ${queryName} on ${databaseName} - ${this.statusString}`;
74+
return this.interpolate(this.getLabel());
5075
}
5176
}
5277

@@ -109,7 +134,7 @@ class HistoryTreeDataProvider implements vscode.TreeDataProvider<QueryHistoryIte
109134
push(item: QueryHistoryItem): void {
110135
this.current = item;
111136
this.history.push(item);
112-
this._onDidChangeTreeData.fire();
137+
this.refresh();
113138
}
114139

115140
setCurrentItem(item: QueryHistoryItem) {
@@ -127,9 +152,13 @@ class HistoryTreeDataProvider implements vscode.TreeDataProvider<QueryHistoryIte
127152
// are any available.
128153
this.current = this.history[Math.min(index, this.history.length - 1)];
129154
}
130-
this._onDidChangeTreeData.fire();
155+
this.refresh();
131156
}
132157
}
158+
159+
refresh() {
160+
this._onDidChangeTreeData.fire();
161+
}
133162
}
134163

135164
/**
@@ -166,6 +195,23 @@ export class QueryHistoryManager {
166195
}
167196
}
168197

198+
async handleSetLabel(queryHistoryItem: QueryHistoryItem) {
199+
const response = await vscode.window.showInputBox({
200+
prompt: 'Label:',
201+
placeHolder: '(use default)',
202+
value: queryHistoryItem.getLabel(),
203+
});
204+
// undefined response means the user cancelled the dialog; don't change anything
205+
if (response !== undefined) {
206+
if (response === '')
207+
// Interpret empty string response as "go back to using default"
208+
queryHistoryItem.label = undefined;
209+
else
210+
queryHistoryItem.label = response;
211+
this.treeDataProvider.refresh();
212+
}
213+
}
214+
169215
async handleItemClicked(queryHistoryItem: QueryHistoryItem) {
170216
this.treeDataProvider.setCurrentItem(queryHistoryItem);
171217

@@ -185,7 +231,11 @@ export class QueryHistoryManager {
185231
}
186232
}
187233

188-
constructor(ctx: ExtensionContext, selectedCallback?: (item: QueryHistoryItem) => Promise<void>) {
234+
constructor(
235+
ctx: ExtensionContext,
236+
private queryHistoryConfigListener: QueryHistoryConfig,
237+
selectedCallback?: (item: QueryHistoryItem) => Promise<void>
238+
) {
189239
this.ctx = ctx;
190240
this.selectedCallback = selectedCallback;
191241
const treeDataProvider = this.treeDataProvider = new HistoryTreeDataProvider(ctx);
@@ -199,12 +249,17 @@ export class QueryHistoryManager {
199249
});
200250
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.openQuery', this.handleOpenQuery));
201251
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.removeHistoryItem', this.handleRemoveHistoryItem.bind(this)));
252+
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.setLabel', this.handleSetLabel.bind(this)));
202253
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.itemClicked', async (item) => {
203254
return this.handleItemClicked(item);
204255
}));
256+
queryHistoryConfigListener.onDidChangeQueryHistoryConfiguration(() => {
257+
this.treeDataProvider.refresh();
258+
});
205259
}
206260

207-
push(item: QueryHistoryItem) {
261+
push(evaluationInfo: EvaluationInfo) {
262+
const item = new QueryHistoryItem(evaluationInfo, this.queryHistoryConfigListener);
208263
this.treeDataProvider.push(item);
209264
this.treeView.reveal(item, { select: true });
210265
}

0 commit comments

Comments
 (0)