Skip to content

Commit 9a572a3

Browse files
authored
Merge pull request #2008 from github/nora/split-query-history-file
Query History: split file
2 parents ca1d63a + 1f52642 commit 9a572a3

File tree

13 files changed

+2439
-2279
lines changed

13 files changed

+2439
-2279
lines changed

extensions/ql-vscode/src/extension.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ import {
8383
ProgressReporter,
8484
queryServerLogger,
8585
} from "./common";
86-
import { QueryHistoryManager } from "./query-history/query-history";
86+
import { QueryHistoryManager } from "./query-history/query-history-manager";
8787
import { CompletedLocalQueryInfo, LocalQueryInfo } from "./query-results";
8888
import { QueryServerClient as LegacyQueryServerClient } from "./legacy-query-server/queryserver-client";
8989
import { QueryServerClient } from "./query-server/queryserver-client";
@@ -1639,7 +1639,7 @@ const checkForUpdatesCommand = "codeQL.checkForUpdatesToCLI";
16391639
/**
16401640
* This text provider lets us open readonly files in the editor.
16411641
*
1642-
* TODO: Consolidate this with the 'codeql' text provider in query-history.ts.
1642+
* TODO: Consolidate this with the 'codeql' text provider in query-history-manager.ts.
16431643
*/
16441644
function registerRemoteQueryTextProvider() {
16451645
workspace.registerTextDocumentContentProvider("remote-query", {

extensions/ql-vscode/src/log-insights/log-scanner-service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Diagnostic, DiagnosticSeverity, languages, Range, Uri } from "vscode";
22
import { DisposableObject } from "../pure/disposable-object";
3-
import { QueryHistoryManager } from "../query-history/query-history";
43
import { QueryHistoryInfo } from "../query-history/query-history-info";
54
import {
65
EvaluationLogProblemReporter,
@@ -9,6 +8,7 @@ import {
98
import { PipelineInfo, SummarySymbols } from "./summary-parser";
109
import { readFile } from "fs-extra";
1110
import { extLogger } from "../common";
11+
import { QueryHistoryManager } from "../query-history/query-history-manager";
1212

1313
/**
1414
* Compute the key used to find a predicate in the summary symbols.
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
import {
2+
env,
3+
Event,
4+
EventEmitter,
5+
ProviderResult,
6+
ThemeColor,
7+
ThemeIcon,
8+
TreeDataProvider,
9+
TreeItem,
10+
} from "vscode";
11+
import { DisposableObject } from "../pure/disposable-object";
12+
import { assertNever } from "../pure/helpers-pure";
13+
import { QueryHistoryInfo } from "./query-history-info";
14+
import { QueryStatus } from "../query-status";
15+
import { HistoryItemLabelProvider } from "./history-item-label-provider";
16+
17+
export enum SortOrder {
18+
NameAsc = "NameAsc",
19+
NameDesc = "NameDesc",
20+
DateAsc = "DateAsc",
21+
DateDesc = "DateDesc",
22+
CountAsc = "CountAsc",
23+
CountDesc = "CountDesc",
24+
}
25+
26+
/**
27+
* Tree data provider for the query history view.
28+
*/
29+
export class HistoryTreeDataProvider
30+
extends DisposableObject
31+
implements TreeDataProvider<QueryHistoryInfo>
32+
{
33+
private _sortOrder = SortOrder.DateAsc;
34+
35+
private _onDidChangeTreeData = super.push(
36+
new EventEmitter<QueryHistoryInfo | undefined>(),
37+
);
38+
39+
readonly onDidChangeTreeData: Event<QueryHistoryInfo | undefined> =
40+
this._onDidChangeTreeData.event;
41+
42+
private _onDidChangeCurrentQueryItem = super.push(
43+
new EventEmitter<QueryHistoryInfo | undefined>(),
44+
);
45+
46+
public readonly onDidChangeCurrentQueryItem =
47+
this._onDidChangeCurrentQueryItem.event;
48+
49+
private history: QueryHistoryInfo[] = [];
50+
51+
private current: QueryHistoryInfo | undefined;
52+
53+
constructor(private readonly labelProvider: HistoryItemLabelProvider) {
54+
super();
55+
}
56+
57+
async getTreeItem(element: QueryHistoryInfo): Promise<TreeItem> {
58+
const treeItem = new TreeItem(this.labelProvider.getLabel(element));
59+
60+
treeItem.command = {
61+
title: "Query History Item",
62+
command: "codeQLQueryHistory.itemClicked",
63+
arguments: [element],
64+
tooltip: element.failureReason || this.labelProvider.getLabel(element),
65+
};
66+
67+
// Populate the icon and the context value. We use the context value to
68+
// control which commands are visible in the context menu.
69+
treeItem.iconPath = this.getIconPath(element);
70+
treeItem.contextValue = await this.getContextValue(element);
71+
72+
return treeItem;
73+
}
74+
75+
private getIconPath(element: QueryHistoryInfo): ThemeIcon | string {
76+
switch (element.status) {
77+
case QueryStatus.InProgress:
78+
return new ThemeIcon("sync~spin");
79+
case QueryStatus.Completed:
80+
if (element.t === "local") {
81+
return new ThemeIcon("database");
82+
} else {
83+
return new ThemeIcon("cloud");
84+
}
85+
case QueryStatus.Failed:
86+
return new ThemeIcon("error", new ThemeColor("errorForeground"));
87+
default:
88+
assertNever(element.status);
89+
}
90+
}
91+
92+
private async getContextValue(element: QueryHistoryInfo): Promise<string> {
93+
switch (element.status) {
94+
case QueryStatus.InProgress:
95+
if (element.t === "local") {
96+
return "inProgressResultsItem";
97+
} else if (
98+
element.t === "variant-analysis" &&
99+
element.variantAnalysis.actionsWorkflowRunId === undefined
100+
) {
101+
return "pendingRemoteResultsItem";
102+
} else {
103+
return "inProgressRemoteResultsItem";
104+
}
105+
case QueryStatus.Completed:
106+
if (element.t === "local") {
107+
const hasResults =
108+
await element.completedQuery?.query.hasInterpretedResults();
109+
return hasResults ? "interpretedResultsItem" : "rawResultsItem";
110+
} else {
111+
return "remoteResultsItem";
112+
}
113+
case QueryStatus.Failed:
114+
return element.t === "local"
115+
? "cancelledResultsItem"
116+
: "cancelledRemoteResultsItem";
117+
default:
118+
assertNever(element.status);
119+
}
120+
}
121+
122+
getChildren(element?: QueryHistoryInfo): ProviderResult<QueryHistoryInfo[]> {
123+
return element
124+
? []
125+
: this.history.sort((h1, h2) => {
126+
const h1Label = this.labelProvider.getLabel(h1).toLowerCase();
127+
const h2Label = this.labelProvider.getLabel(h2).toLowerCase();
128+
129+
const h1Date = this.getItemDate(h1);
130+
131+
const h2Date = this.getItemDate(h2);
132+
133+
const resultCount1 =
134+
h1.t === "local"
135+
? h1.completedQuery?.resultCount ?? -1
136+
: h1.resultCount ?? -1;
137+
const resultCount2 =
138+
h2.t === "local"
139+
? h2.completedQuery?.resultCount ?? -1
140+
: h2.resultCount ?? -1;
141+
142+
switch (this.sortOrder) {
143+
case SortOrder.NameAsc:
144+
return h1Label.localeCompare(h2Label, env.language);
145+
146+
case SortOrder.NameDesc:
147+
return h2Label.localeCompare(h1Label, env.language);
148+
149+
case SortOrder.DateAsc:
150+
return h1Date - h2Date;
151+
152+
case SortOrder.DateDesc:
153+
return h2Date - h1Date;
154+
155+
case SortOrder.CountAsc:
156+
// If the result counts are equal, sort by name.
157+
return resultCount1 - resultCount2 === 0
158+
? h1Label.localeCompare(h2Label, env.language)
159+
: resultCount1 - resultCount2;
160+
161+
case SortOrder.CountDesc:
162+
// If the result counts are equal, sort by name.
163+
return resultCount2 - resultCount1 === 0
164+
? h2Label.localeCompare(h1Label, env.language)
165+
: resultCount2 - resultCount1;
166+
default:
167+
assertNever(this.sortOrder);
168+
}
169+
});
170+
}
171+
172+
getParent(_element: QueryHistoryInfo): ProviderResult<QueryHistoryInfo> {
173+
return null;
174+
}
175+
176+
getCurrent(): QueryHistoryInfo | undefined {
177+
return this.current;
178+
}
179+
180+
pushQuery(item: QueryHistoryInfo): void {
181+
this.history.push(item);
182+
this.setCurrentItem(item);
183+
this.refresh();
184+
}
185+
186+
setCurrentItem(item?: QueryHistoryInfo) {
187+
if (item !== this.current) {
188+
this.current = item;
189+
this._onDidChangeCurrentQueryItem.fire(item);
190+
}
191+
}
192+
193+
remove(item: QueryHistoryInfo) {
194+
const isCurrent = this.current === item;
195+
if (isCurrent) {
196+
this.setCurrentItem();
197+
}
198+
const index = this.history.findIndex((i) => i === item);
199+
if (index >= 0) {
200+
this.history.splice(index, 1);
201+
if (isCurrent && this.history.length > 0) {
202+
// Try to keep a current item, near the deleted item if there
203+
// are any available.
204+
this.setCurrentItem(
205+
this.history[Math.min(index, this.history.length - 1)],
206+
);
207+
}
208+
this.refresh();
209+
}
210+
}
211+
212+
get allHistory(): QueryHistoryInfo[] {
213+
return this.history;
214+
}
215+
216+
set allHistory(history: QueryHistoryInfo[]) {
217+
this.history = history;
218+
this.setCurrentItem(history[0]);
219+
this.refresh();
220+
}
221+
222+
refresh() {
223+
this._onDidChangeTreeData.fire(undefined);
224+
}
225+
226+
public get sortOrder() {
227+
return this._sortOrder;
228+
}
229+
230+
public set sortOrder(newSortOrder: SortOrder) {
231+
this._sortOrder = newSortOrder;
232+
this._onDidChangeTreeData.fire(undefined);
233+
}
234+
235+
private getItemDate(item: QueryHistoryInfo) {
236+
switch (item.t) {
237+
case "local":
238+
return item.initialInfo.start.getTime();
239+
case "remote":
240+
return item.remoteQuery.executionStartTime;
241+
case "variant-analysis":
242+
return item.variantAnalysis.executionStartTime;
243+
default:
244+
assertNever(item);
245+
}
246+
}
247+
}

0 commit comments

Comments
 (0)