Skip to content

Commit 719d5c9

Browse files
Implement sorting of query history by name, date, and result count
1 parent a86c1ce commit 719d5c9

8 files changed

Lines changed: 193 additions & 14 deletions

File tree

extensions/ql-vscode/CHANGELOG.md

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

1011
## 1.4.3 - 22 February 2021
1112

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

extensions/ql-vscode/package.json

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,8 @@
179179
},
180180
"codeQL.queryHistory.format": {
181181
"type": "string",
182-
"default": "[%t] %q on %d - %s",
183-
"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."
182+
"default": "%q on %d - %s, %r result count [%t]",
183+
"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."
184184
},
185185
"codeQL.runningTests.numberOfThreads": {
186186
"scope": "window",
@@ -357,6 +357,30 @@
357357
"dark": "media/dark/trash.svg"
358358
}
359359
},
360+
{
361+
"command": "codeQLQueryHistory.sortByName",
362+
"title": "Sort by Name",
363+
"icon": {
364+
"light": "media/light/sort-alpha.svg",
365+
"dark": "media/dark/sort-alpha.svg"
366+
}
367+
},
368+
{
369+
"command": "codeQLQueryHistory.sortByDate",
370+
"title": "Sort by Query Date",
371+
"icon": {
372+
"light": "media/light/sort-date.svg",
373+
"dark": "media/dark/sort-date.svg"
374+
}
375+
},
376+
{
377+
"command": "codeQLQueryHistory.sortByCount",
378+
"title": "Sort by Results Count",
379+
"icon": {
380+
"light": "media/light/sort-num.svg",
381+
"dark": "media/dark/sort-num.svg"
382+
}
383+
},
360384
{
361385
"command": "codeQLQueryHistory.showQueryLog",
362386
"title": "Show Query Log"
@@ -461,6 +485,21 @@
461485
"when": "view == codeQLQueryHistory",
462486
"group": "navigation"
463487
},
488+
{
489+
"command": "codeQLQueryHistory.sortByName",
490+
"when": "view == codeQLQueryHistory",
491+
"group": "navigation"
492+
},
493+
{
494+
"command": "codeQLQueryHistory.sortByDate",
495+
"when": "view == codeQLQueryHistory",
496+
"group": "navigation"
497+
},
498+
{
499+
"command": "codeQLQueryHistory.sortByCount",
500+
"when": "view == codeQLQueryHistory",
501+
"group": "navigation"
502+
},
464503
{
465504
"command": "codeQLAstViewer.clear",
466505
"when": "view == codeQLAstViewer",
@@ -666,6 +705,18 @@
666705
"command": "codeQLQueryHistory.compareWith",
667706
"when": "false"
668707
},
708+
{
709+
"command": "codeQLQueryHistory.sortByName",
710+
"when": "false"
711+
},
712+
{
713+
"command": "codeQLQueryHistory.sortByDate",
714+
"when": "false"
715+
},
716+
{
717+
"command": "codeQLQueryHistory.sortByCount",
718+
"when": "false"
719+
},
669720
{
670721
"command": "codeQLAstViewer.gotoCode",
671722
"when": "false"

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(interpretationPage?.numTotalResults || resultSet.schema.rows);
403404
const parsedResultSets: ParsedResultSets = {
404405
pageNumber: 0,
405406
pageSize,

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

Lines changed: 93 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';
@@ -15,6 +15,7 @@ import { URLSearchParams } from 'url';
1515
import { QueryServerClient } from './queryserver-client';
1616
import { DisposableObject } from './pure/disposable-object';
1717
import { commandRunner } from './commandRunner';
18+
import { assertNever } from './pure/helpers-pure';
1819

1920
/**
2021
* query-history.ts
@@ -58,10 +59,21 @@ const SHOW_QUERY_TEXT_QUICK_EVAL_MSG = `\
5859
*/
5960
const FAILED_QUERY_HISTORY_ITEM_ICON = 'media/red-x.svg';
6061

62+
enum SortOrder {
63+
NameAsc = 'NameAsc',
64+
NameDesc = 'NameDesc',
65+
DateAsc = 'DateAsc',
66+
DateDesc = 'DateDesc',
67+
CountAsc = 'CountAsc',
68+
CountDesc = 'CountDesc',
69+
}
70+
6171
/**
6272
* Tree data provider for the query history view.
6373
*/
6474
export class HistoryTreeDataProvider extends DisposableObject {
75+
private _sortOrder = SortOrder.DateAsc;
76+
6577
private _onDidChangeTreeData = super.push(new vscode.EventEmitter<CompletedQuery | undefined>());
6678

6779
readonly onDidChangeTreeData: vscode.Event<CompletedQuery | undefined> = this
@@ -111,7 +123,24 @@ export class HistoryTreeDataProvider extends DisposableObject {
111123
getChildren(
112124
element?: CompletedQuery
113125
): vscode.ProviderResult<CompletedQuery[]> {
114-
return element ? [] : this.history;
126+
return element ? [] : this.history.sort((q1, q2) => {
127+
switch (this.sortOrder) {
128+
case SortOrder.NameAsc:
129+
return q1.toString().localeCompare(q2.toString(), env.language);
130+
case SortOrder.NameDesc:
131+
return q2.toString().localeCompare(q1.toString(), env.language);
132+
case SortOrder.DateAsc:
133+
return q1.date.getTime() - q2.date.getTime();
134+
case SortOrder.DateDesc:
135+
return q2.date.getTime() - q1.date.getTime();
136+
case SortOrder.CountAsc:
137+
return q1.resultCount - q2.resultCount;
138+
case SortOrder.CountDesc:
139+
return q2.resultCount - q1.resultCount;
140+
default:
141+
assertNever(this.sortOrder);
142+
}
143+
});
115144
}
116145

117146
getParent(_element: CompletedQuery): vscode.ProviderResult<CompletedQuery> {
@@ -157,6 +186,15 @@ export class HistoryTreeDataProvider extends DisposableObject {
157186
find(queryId: number): CompletedQuery | undefined {
158187
return this.allHistory.find((query) => query.query.queryID === queryId);
159188
}
189+
190+
public get sortOrder() {
191+
return this._sortOrder;
192+
}
193+
194+
public set sortOrder(newSortOrder: SortOrder) {
195+
this._sortOrder = newSortOrder;
196+
this._onDidChangeTreeData.fire();
197+
}
160198
}
161199

162200
/**
@@ -224,6 +262,24 @@ export class QueryHistoryManager extends DisposableObject {
224262
this.handleRemoveHistoryItem.bind(this)
225263
)
226264
);
265+
this.push(
266+
commandRunner(
267+
'codeQLQueryHistory.sortByName',
268+
this.handleSortByName.bind(this)
269+
)
270+
);
271+
this.push(
272+
commandRunner(
273+
'codeQLQueryHistory.sortByDate',
274+
this.handleSortByDate.bind(this)
275+
)
276+
);
277+
this.push(
278+
commandRunner(
279+
'codeQLQueryHistory.sortByCount',
280+
this.handleSortByCount.bind(this)
281+
)
282+
);
227283
this.push(
228284
commandRunner(
229285
'codeQLQueryHistory.setLabel',
@@ -345,6 +401,30 @@ export class QueryHistoryManager extends DisposableObject {
345401
}
346402
}
347403

404+
async handleSortByName() {
405+
if (this.treeDataProvider.sortOrder === SortOrder.NameAsc) {
406+
this.treeDataProvider.sortOrder = SortOrder.NameDesc;
407+
} else {
408+
this.treeDataProvider.sortOrder = SortOrder.NameAsc;
409+
}
410+
}
411+
412+
async handleSortByDate() {
413+
if (this.treeDataProvider.sortOrder === SortOrder.DateAsc) {
414+
this.treeDataProvider.sortOrder = SortOrder.DateDesc;
415+
} else {
416+
this.treeDataProvider.sortOrder = SortOrder.DateAsc;
417+
}
418+
}
419+
420+
async handleSortByCount() {
421+
if (this.treeDataProvider.sortOrder === SortOrder.CountAsc) {
422+
this.treeDataProvider.sortOrder = SortOrder.CountDesc;
423+
} else {
424+
this.treeDataProvider.sortOrder = SortOrder.CountAsc;
425+
}
426+
}
427+
348428
async handleSetLabel(
349429
singleItem: CompletedQuery,
350430
multiSelect: CompletedQuery[]
@@ -362,7 +442,12 @@ export class QueryHistoryManager extends DisposableObject {
362442
if (response !== undefined) {
363443
// Interpret empty string response as 'go back to using default'
364444
singleItem.options.label = response === '' ? undefined : response;
365-
this.treeDataProvider.refresh(singleItem);
445+
if (this.treeDataProvider.sortOrder === SortOrder.NameAsc ||
446+
this.treeDataProvider.sortOrder === SortOrder.NameDesc) {
447+
this.treeDataProvider.refresh();
448+
} else {
449+
this.treeDataProvider.refresh(singleItem);
450+
}
366451
}
367452
}
368453

@@ -511,11 +596,14 @@ export class QueryHistoryManager extends DisposableObject {
511596
}
512597
}
513598

514-
addQuery(info: QueryWithResults): CompletedQuery {
599+
buildCompletedQuery(info: QueryWithResults): CompletedQuery {
515600
const item = new CompletedQuery(info, this.queryHistoryConfigListener);
601+
return item;
602+
}
603+
604+
addCompletedQuery(item: CompletedQuery) {
516605
this.treeDataProvider.pushQuery(item);
517606
this.updateTreeViewSelectionIfVisible();
518-
return item;
519607
}
520608

521609
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 = 0;
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)