Skip to content

Commit 3baa0a3

Browse files
Implement sorting of query history by name, date, and result count
1 parent 1d03702 commit 3baa0a3

8 files changed

Lines changed: 178 additions & 14 deletions

File tree

extensions/ql-vscode/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Respect the `codeQL.runningQueries.numberOfThreads` setting when creating SARIF files during result interpretation. [#771](https://github.com/github/vscode-codeql/pull/771)
66
- Allow using raw LGTM project slugs for fetching LGTM databases. [#769](https://github.com/github/vscode-codeql/pull/769)
77
- Better error messages when BQRS interpretation fails to produce SARIF. [#770](https://github.com/github/vscode-codeql/pull/770)
8+
- Implement sorting of the query history view by name, date, and results count. [#767](https://github.com/github/vscode-codeql/pull/767)
89

910
## 1.4.3 - 22 February 2021
1011

Lines changed: 15 additions & 0 deletions
Loading
Lines changed: 15 additions & 0 deletions
Loading

extensions/ql-vscode/package.json

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@
164164
},
165165
"codeQL.queryHistory.format": {
166166
"type": "string",
167-
"default": "[%t] %q on %d - %s",
168-
"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."
167+
"default": "%q on %d - %s, %r results [%t]",
168+
"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, %r is the number of results, and %s is a status string."
169169
},
170170
"codeQL.runningTests.numberOfThreads": {
171171
"scope": "window",
@@ -342,6 +342,30 @@
342342
"dark": "media/dark/trash.svg"
343343
}
344344
},
345+
{
346+
"command": "codeQLQueryHistory.sortByName",
347+
"title": "Sort by Name",
348+
"icon": {
349+
"light": "media/light/sort-alpha.svg",
350+
"dark": "media/dark/sort-alpha.svg"
351+
}
352+
},
353+
{
354+
"command": "codeQLQueryHistory.sortByDate",
355+
"title": "Sort by Query Date",
356+
"icon": {
357+
"light": "media/light/sort-date.svg",
358+
"dark": "media/dark/sort-date.svg"
359+
}
360+
},
361+
{
362+
"command": "codeQLQueryHistory.sortByCount",
363+
"title": "Sort by Results Count",
364+
"icon": {
365+
"light": "media/light/sort-num.svg",
366+
"dark": "media/dark/sort-num.svg"
367+
}
368+
},
345369
{
346370
"command": "codeQLQueryHistory.showQueryLog",
347371
"title": "Show Query Log"
@@ -446,6 +470,21 @@
446470
"when": "view == codeQLQueryHistory",
447471
"group": "navigation"
448472
},
473+
{
474+
"command": "codeQLQueryHistory.sortByName",
475+
"when": "view == codeQLQueryHistory",
476+
"group": "navigation"
477+
},
478+
{
479+
"command": "codeQLQueryHistory.sortByDate",
480+
"when": "view == codeQLQueryHistory",
481+
"group": "navigation"
482+
},
483+
{
484+
"command": "codeQLQueryHistory.sortByCount",
485+
"when": "view == codeQLQueryHistory",
486+
"group": "navigation"
487+
},
449488
{
450489
"command": "codeQLAstViewer.clear",
451490
"when": "view == codeQLAstViewer",

extensions/ql-vscode/src/extension.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -472,12 +472,11 @@ async function activateWithInstalledDistribution(
472472
progress,
473473
token
474474
);
475-
const item = qhm.addQuery(info);
475+
const item = qhm.buildCompletedQuery(info);
476476
await showResultsForCompletedQuery(item, WebviewReveal.NotForced);
477-
// The call to showResults potentially creates SARIF file;
478-
// Update the tree item context value to allow viewing that
479-
// SARIF file from context menu.
480-
await qhm.refreshTreeView(item);
477+
// Note we must update the query history view after showing results as the
478+
// display and sorting might depend on the number of results
479+
await qhm.addCompletedQuery(item);
481480
}
482481
}
483482

extensions/ql-vscode/src/interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ export class InterfaceManager extends DisposableObject {
400400
}
401401
);
402402
const resultSet = transformBqrsResultSet(schema, chunk);
403+
results.setResultCount(resultSet.schema.rows);
403404
const parsedResultSets: ParsedResultSets = {
404405
pageNumber: 0,
405406
pageSize,

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

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as path from 'path';
22
import * as vscode from 'vscode';
3-
import { window as Window } from 'vscode';
3+
import { window as Window, env } from 'vscode';
44
import { CompletedQuery } from './query-results';
55
import { QueryHistoryConfig } from './config';
66
import { QueryWithResults } from './run-queries';
@@ -58,10 +58,21 @@ const SHOW_QUERY_TEXT_QUICK_EVAL_MSG = `\
5858
*/
5959
const FAILED_QUERY_HISTORY_ITEM_ICON = 'media/red-x.svg';
6060

61+
enum SortOrder {
62+
NameAsc = 'NameAsc',
63+
NameDesc = 'NameDesc',
64+
DateAsc = 'DateAsc',
65+
DateDesc = 'DateDesc',
66+
CountAsc = 'CountAsc',
67+
CountDesc = 'CountDesc',
68+
}
69+
6170
/**
6271
* Tree data provider for the query history view.
6372
*/
6473
export class HistoryTreeDataProvider extends DisposableObject {
74+
private _sortOrder = SortOrder.DateAsc;
75+
6576
private _onDidChangeTreeData = super.push(new vscode.EventEmitter<CompletedQuery | undefined>());
6677

6778
readonly onDidChangeTreeData: vscode.Event<CompletedQuery | undefined> = this
@@ -111,7 +122,22 @@ export class HistoryTreeDataProvider extends DisposableObject {
111122
getChildren(
112123
element?: CompletedQuery
113124
): vscode.ProviderResult<CompletedQuery[]> {
114-
return element ? [] : this.history;
125+
return element ? [] : this.history.sort((q1, q2) => {
126+
switch (this.sortOrder) {
127+
case SortOrder.NameAsc:
128+
return q1.toString().localeCompare(q2.toString(), env.language);
129+
case SortOrder.NameDesc:
130+
return q2.toString().localeCompare(q1.toString(), env.language);
131+
case SortOrder.DateAsc:
132+
return q1.date.getTime() - q2.date.getTime();
133+
case SortOrder.DateDesc:
134+
return q2.date.getTime() - q1.date.getTime();
135+
case SortOrder.CountAsc:
136+
return q1.resultCount - q2.resultCount;
137+
case SortOrder.CountDesc:
138+
return q2.resultCount - q1.resultCount;
139+
}
140+
});
115141
}
116142

117143
getParent(_element: CompletedQuery): vscode.ProviderResult<CompletedQuery> {
@@ -157,6 +183,15 @@ export class HistoryTreeDataProvider extends DisposableObject {
157183
find(queryId: number): CompletedQuery | undefined {
158184
return this.allHistory.find((query) => query.query.queryID === queryId);
159185
}
186+
187+
public get sortOrder() {
188+
return this._sortOrder;
189+
}
190+
191+
public set sortOrder(newSortOrder: SortOrder) {
192+
this._sortOrder = newSortOrder;
193+
this._onDidChangeTreeData.fire();
194+
}
160195
}
161196

162197
/**
@@ -224,6 +259,24 @@ export class QueryHistoryManager extends DisposableObject {
224259
this.handleRemoveHistoryItem.bind(this)
225260
)
226261
);
262+
this.push(
263+
commandRunner(
264+
'codeQLQueryHistory.sortByName',
265+
this.handleSortByName.bind(this)
266+
)
267+
);
268+
this.push(
269+
commandRunner(
270+
'codeQLQueryHistory.sortByDate',
271+
this.handleSortByDate.bind(this)
272+
)
273+
);
274+
this.push(
275+
commandRunner(
276+
'codeQLQueryHistory.sortByCount',
277+
this.handleSortByCount.bind(this)
278+
)
279+
);
227280
this.push(
228281
commandRunner(
229282
'codeQLQueryHistory.setLabel',
@@ -345,6 +398,30 @@ export class QueryHistoryManager extends DisposableObject {
345398
}
346399
}
347400

401+
async handleSortByName() {
402+
if (this.treeDataProvider.sortOrder === SortOrder.NameAsc) {
403+
this.treeDataProvider.sortOrder = SortOrder.NameDesc;
404+
} else {
405+
this.treeDataProvider.sortOrder = SortOrder.NameAsc;
406+
}
407+
}
408+
409+
async handleSortByDate() {
410+
if (this.treeDataProvider.sortOrder === SortOrder.DateAsc) {
411+
this.treeDataProvider.sortOrder = SortOrder.DateDesc;
412+
} else {
413+
this.treeDataProvider.sortOrder = SortOrder.DateAsc;
414+
}
415+
}
416+
417+
async handleSortByCount() {
418+
if (this.treeDataProvider.sortOrder === SortOrder.CountAsc) {
419+
this.treeDataProvider.sortOrder = SortOrder.CountDesc;
420+
} else {
421+
this.treeDataProvider.sortOrder = SortOrder.CountAsc;
422+
}
423+
}
424+
348425
async handleSetLabel(
349426
singleItem: CompletedQuery,
350427
multiSelect: CompletedQuery[]
@@ -362,7 +439,12 @@ export class QueryHistoryManager extends DisposableObject {
362439
if (response !== undefined) {
363440
// Interpret empty string response as 'go back to using default'
364441
singleItem.options.label = response === '' ? undefined : response;
365-
this.treeDataProvider.refresh(singleItem);
442+
if (this.treeDataProvider.sortOrder === SortOrder.NameAsc ||
443+
this.treeDataProvider.sortOrder === SortOrder.NameDesc) {
444+
this.treeDataProvider.refresh();
445+
} else {
446+
this.treeDataProvider.refresh(singleItem);
447+
}
366448
}
367449
}
368450

@@ -511,11 +593,14 @@ export class QueryHistoryManager extends DisposableObject {
511593
}
512594
}
513595

514-
addQuery(info: QueryWithResults): CompletedQuery {
596+
buildCompletedQuery(info: QueryWithResults): CompletedQuery {
515597
const item = new CompletedQuery(info, this.queryHistoryConfigListener);
598+
return item;
599+
}
600+
601+
addCompletedQuery(item: CompletedQuery) {
516602
this.treeDataProvider.pushQuery(item);
517603
this.updateTreeViewSelectionIfVisible();
518-
return item;
519604
}
520605

521606
find(queryId: number): CompletedQuery | undefined {

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import { QueryHistoryConfig } from './config';
1111
import { QueryHistoryItemOptions } from './query-history';
1212

1313
export class CompletedQuery implements QueryWithResults {
14+
readonly date: Date;
1415
readonly time: string;
1516
readonly query: QueryInfo;
1617
readonly result: messages.EvaluationResult;
1718
readonly database: DatabaseInfo;
1819
readonly logFileLocation?: string;
1920
options: QueryHistoryItemOptions;
21+
resultCount: number;
2022
dispose: () => void;
2123

2224
/**
@@ -44,8 +46,14 @@ export class CompletedQuery implements QueryWithResults {
4446
this.options = evaluation.options;
4547
this.dispose = evaluation.dispose;
4648

47-
this.time = new Date().toLocaleString(env.language);
49+
this.date = new Date();
50+
this.time = this.date.toLocaleString(env.language);
4851
this.sortedResultsInfo = new Map();
52+
this.resultCount = -1;
53+
}
54+
55+
setResultCount(value: number) {
56+
this.resultCount = value;
4957
}
5058

5159
get databaseName(): string {
@@ -80,11 +88,12 @@ export class CompletedQuery implements QueryWithResults {
8088
}
8189

8290
interpolate(template: string): string {
83-
const { databaseName, queryName, time, statusString } = this;
91+
const { databaseName, queryName, time, resultCount, statusString } = this;
8492
const replacements: { [k: string]: string } = {
8593
t: time,
8694
q: queryName,
8795
d: databaseName,
96+
r: resultCount.toString(),
8897
s: statusString,
8998
'%': '%',
9099
};

0 commit comments

Comments
 (0)