Skip to content

Commit 2262873

Browse files
committed
Merge branch 'main' of github.com:halcyon-tech/vscode-db2i
2 parents 661cdeb + 6678ba2 commit 2262873

File tree

6 files changed

+147
-45
lines changed

6 files changed

+147
-45
lines changed

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@
215215
},
216216
{
217217
"id": "queryHistory",
218-
"name": "Query History",
218+
"name": "Statement History",
219219
"visibility": "visible",
220220
"when": "code-for-ibmi:connected == true"
221221
},
@@ -240,7 +240,7 @@
240240
"viewsWelcome": [
241241
{
242242
"view": "queryHistory",
243-
"contents": "Query history will appear here."
243+
"contents": "Statement history will appear here."
244244
},
245245
{
246246
"view": "jobManager",
@@ -413,13 +413,13 @@
413413
},
414414
{
415415
"command": "vscode-db2i.queryHistory.remove",
416-
"title": "Remove query from history",
416+
"title": "Remove statement from history",
417417
"category": "Db2 for i",
418418
"icon": "$(trash)"
419419
},
420420
{
421421
"command": "vscode-db2i.queryHistory.clear",
422-
"title": "Clear query history",
422+
"title": "Clear statement history",
423423
"category": "Db2 for i",
424424
"icon": "$(trash)"
425425
},

src/Storage.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ import vscode from 'vscode';
33
const QUERIES_KEY = `queries`;
44
const SERVERCOMPONENT_KEY = `serverVersion`
55

6-
export type QueryList = string[];
6+
export interface QueryHistoryItem {
7+
query: string;
8+
unix: number;
9+
}
10+
11+
export type QueryList = QueryHistoryItem[];
712

813
abstract class Storage {
914
protected readonly globalState;
@@ -54,6 +59,24 @@ export class ConnectionStorage extends Storage {
5459
return this.set(SERVERCOMPONENT_KEY, name);
5560
}
5661

62+
/**
63+
* Eventually we will want to remove this function, but for now we need to fix the past queries
64+
*/
65+
fixPastQueries() {
66+
const currentList = this.getPastQueries() as (string|QueryHistoryItem)[];
67+
const hasOldFormat = currentList.some(item => typeof item === `string`);
68+
if (hasOldFormat) {
69+
const newList = currentList.map(item => {
70+
if (typeof item === `string`) {
71+
return { query: item, unix: Math.floor(Date.now() / 1000) - 86400 };
72+
} else {
73+
return item;
74+
}
75+
});
76+
return this.setPastQueries(newList);
77+
}
78+
}
79+
5780
getPastQueries() {
5881
return this.get<QueryList>(QUERIES_KEY) || [];
5982
}

src/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export async function onConnectOrServerInstall(): Promise<boolean> {
3333
Config.setConnectionName(instance.getConnection().currentConnectionName);
3434
determineFeatures();
3535

36+
await Config.fixPastQueries();
37+
3638
await ServerComponent.initialise().then(installed => {
3739
if (installed) {
3840
JobManagerView.setVisible(true);

src/views/html.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ export function getHeader(options: {withCollapsed?: boolean} = {}): string {
2020
top: 0; /* Don't forget this, required for the stickiness */
2121
}
2222
23+
tfoot tr {
24+
background-color: var(--vscode-multiDiffEditor-headerBackground);
25+
text-align: left;
26+
position: sticky; /* Lock the footer row to the bottom so it's always visible as rows are scrolled */
27+
bottom: 0; /* Don't forget this, required for the stickiness */
28+
}
29+
2330
#resultset th,
2431
#resultset td {
2532
padding: 5px 15px;

src/views/queryHistoryView.ts

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import vscode, { MarkdownString, ThemeIcon, TreeItem, window, workspace } from "vscode";
22
import { TreeDataProvider } from "vscode";
33
import { Config } from "../config";
4+
import { QueryHistoryItem } from "../Storage";
45

56
const openSqlDocumentCommand = `vscode-db2i.openSqlDocument`;
67

@@ -22,7 +23,7 @@ export class queryHistory implements TreeDataProvider<any> {
2223
vscode.commands.registerCommand(`vscode-db2i.queryHistory.prepend`, async (newQuery?: string) => {
2324
if (newQuery && Config.ready) {
2425
let currentList = Config.getPastQueries();
25-
const existingQuery = currentList.findIndex(query => query.trim() === newQuery.trim());
26+
const existingQuery = currentList.findIndex(queryItem => queryItem.query.trim() === newQuery.trim());
2627

2728
// If it exists, remove it
2829
if (existingQuery > 0) {
@@ -31,7 +32,10 @@ export class queryHistory implements TreeDataProvider<any> {
3132

3233
// If it's at the top, don't add it, it's already at the top
3334
if (existingQuery !== 0) {
34-
currentList.splice(0, 0, newQuery);
35+
currentList.splice(0, 0, {
36+
query: newQuery,
37+
unix: Math.floor(Date.now() / 1000)
38+
});
3539
}
3640

3741
await Config.setPastQueries(currentList);
@@ -40,11 +44,13 @@ export class queryHistory implements TreeDataProvider<any> {
4044
}
4145
}),
4246

43-
vscode.commands.registerCommand(`vscode-db2i.queryHistory.remove`, async (node: PastQuery) => {
47+
vscode.commands.registerCommand(`vscode-db2i.queryHistory.remove`, async (node: PastQueryNode) => {
4448
if (node && Config.ready) {
4549
let currentList = Config.getPastQueries();
4650
const chosenQuery = node.query;
47-
const existingQuery = currentList.findIndex(query => query.trim() === chosenQuery.trim());
51+
const existingQuery = currentList.findIndex(queryItem =>
52+
queryItem.query.trim() === chosenQuery.trim()
53+
);
4854

4955
// If it exists, remove it
5056
if (existingQuery >= 0) {
@@ -72,17 +78,80 @@ export class queryHistory implements TreeDataProvider<any> {
7278
return element;
7379
}
7480

75-
async getChildren(): Promise<vscode.TreeItem[]> {
81+
async getChildren(timePeriod?: TimePeriodNode): Promise<vscode.TreeItem[]> {
7682
if (Config.ready) {
77-
return Config.getPastQueries().map(query => new PastQuery(query));
83+
if (timePeriod) {
84+
return timePeriod.getChildren();
85+
86+
} else {
87+
const currentList = Config.getPastQueries();
88+
89+
const day = 60 * 60 * 24;
90+
const week = day * 7;
91+
const month = day * 30;
92+
93+
const now = Math.floor(Date.now() / 1000);
94+
const dayAgo = now - day;
95+
const weekAgo = now - week;
96+
const monthAgo = now - month;
97+
98+
let pastDayQueries: PastQueryNode[] = [];
99+
let pastWeekQueries: PastQueryNode[] = [];
100+
let pastMonthQueries: PastQueryNode[] = [];
101+
let olderQueries: PastQueryNode[] = [];
102+
103+
currentList.forEach(queryItem => {
104+
// The smaller the unix value, the older it is
105+
if (queryItem.unix < monthAgo) {
106+
olderQueries.push(new PastQueryNode(queryItem.query));
107+
} else if (queryItem.unix < weekAgo) {
108+
pastMonthQueries.push(new PastQueryNode(queryItem.query));
109+
} else if (queryItem.unix < dayAgo) {
110+
pastWeekQueries.push(new PastQueryNode(queryItem.query));
111+
} else {
112+
pastDayQueries.push(new PastQueryNode(queryItem.query));
113+
}
114+
});
115+
116+
let nodes: TimePeriodNode[] = [];
117+
118+
if (pastDayQueries.length > 0) {
119+
nodes.push(new TimePeriodNode(`Past day`, pastDayQueries, true));
120+
}
121+
if (pastWeekQueries.length > 0) {
122+
nodes.push(new TimePeriodNode(`Past week`, pastWeekQueries));
123+
}
124+
if (pastMonthQueries.length > 0) {
125+
nodes.push(new TimePeriodNode(`Past month`, pastMonthQueries));
126+
}
127+
if (olderQueries.length > 0) {
128+
nodes.push(new TimePeriodNode(`Older`, olderQueries));
129+
}
130+
131+
return nodes;
132+
}
78133

79134
} else {
80135
return [new TreeItem(`A connection is required for query history`)];
81136
}
82137
}
83138
}
84139

85-
class PastQuery extends vscode.TreeItem {
140+
class TimePeriodNode extends vscode.TreeItem {
141+
constructor(public period: string, private nodes: PastQueryNode[], expanded = false) {
142+
super(period, expanded ? vscode.TreeItemCollapsibleState.Expanded : vscode.TreeItemCollapsibleState.Collapsed);
143+
144+
this.contextValue = `timePeriod`;
145+
146+
this.iconPath = new ThemeIcon(`calendar`);
147+
}
148+
149+
getChildren() {
150+
return this.nodes;
151+
}
152+
}
153+
154+
class PastQueryNode extends vscode.TreeItem {
86155
constructor(public query: string) {
87156
super(query.length > 63 ? query.substring(0, 60) + `...` : query);
88157

src/views/results/html.ts

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -52,26 +52,27 @@ export function generateScroller(basicSelect: string, isCL: boolean): string {
5252
*/
5353
const vscode = acquireVsCodeApi();
5454
const basicSelect = ${JSON.stringify(basicSelect)};
55+
const htmlTableId = 'resultset';
56+
const statusId = 'status';
57+
const messageSpanId = 'messageSpan';
58+
5559
let myQueryId = '';
56-
57-
let mustLoadHeaders = true;
60+
let needToInitializeTable = true;
5861
let totalRows = 0;
5962
let noMoreRows = false;
6063
let isFetching = false;
6164
6265
window.addEventListener("load", main);
6366
function main() {
64-
let Observer = new IntersectionObserver(function(entries) {
67+
new IntersectionObserver(function(entries) {
6568
// isIntersecting is true when element and viewport are overlapping
6669
// isIntersecting is false when element and viewport don't overlap
6770
if(entries[0].isIntersecting === true) {
6871
if (isFetching === false && noMoreRows === false) {
6972
fetchNextPage();
7073
}
7174
}
72-
}, { threshold: [0] });
73-
74-
Observer.observe(document.getElementById("nextButton"));
75+
}, { threshold: [0] }).observe(document.getElementById(messageSpanId));
7576
7677
window.addEventListener('message', event => {
7778
const data = event.data;
@@ -85,21 +86,21 @@ export function generateScroller(basicSelect: string, isCL: boolean): string {
8586
isFetching = false;
8687
noMoreRows = data.isDone;
8788
88-
if (mustLoadHeaders && event.data.columnList) {
89-
setHeaders('resultset', event.data.columnList);
90-
mustLoadHeaders = false;
89+
if (needToInitializeTable && event.data.columnList) {
90+
initializeTable(event.data.columnList);
91+
needToInitializeTable = false;
9192
}
9293
9394
if (data.rows && data.rows.length > 0) {
9495
totalRows += data.rows.length;
95-
appendRows('resultset', data.rows);
96+
appendRows(data.rows);
9697
}
9798
98-
const nextButton = document.getElementById("nextButton");
9999
if (data.rows === undefined && totalRows === 0) {
100-
nextButton.innerText = 'Query executed with no result set returned. Rows affected: ' + data.update_count;
100+
document.getElementById(messageSpanId).innerText = 'Statement executed with no result set returned. Rows affected: ' + data.update_count;
101101
} else {
102-
nextButton.innerText = noMoreRows ? ('Loaded ' + totalRows + '. End of data') : ('Loaded ' + totalRows + '. Fetching more...');
102+
document.getElementById(statusId).innerText = noMoreRows ? ('Loaded ' + totalRows + '. End of data') : ('Loaded ' + totalRows + '. More available.');
103+
document.getElementById(messageSpanId).style.visibility = "hidden";
103104
}
104105
break;
105106
@@ -124,27 +125,26 @@ export function generateScroller(basicSelect: string, isCL: boolean): string {
124125
document.getElementById("spinnerContent").style.display = 'none';
125126
}
126127
127-
function setHeaders(tableId, columns) {
128-
var tHeadRef = document.getElementById(tableId).getElementsByTagName('thead')[0];
129-
tHeadRef.innerHTML = '';
130-
131-
// Insert a row at the end of table
132-
var newRow = tHeadRef.insertRow();
133-
134-
columns.forEach(colName => {
135-
// Insert a cell at the end of the row
136-
var newCell = newRow.insertCell();
137-
138-
// Append a text node to the cell
139-
var newText = document.createTextNode(colName);
140-
newCell.appendChild(newText);
141-
});
128+
function initializeTable(columns) {
129+
// Initialize the header
130+
var header = document.getElementById(htmlTableId).getElementsByTagName('thead')[0];
131+
header.innerHTML = '';
132+
var headerRow = header.insertRow();
133+
columns.forEach(colName => headerRow.insertCell().appendChild(document.createTextNode(colName)));
134+
135+
// Initialize the footer
136+
var footer = document.getElementById(htmlTableId).getElementsByTagName('tfoot')[0];
137+
footer.innerHTML = '';
138+
var newCell = footer.insertRow().insertCell();
139+
newCell.colSpan = columns.length;
140+
newCell.id = statusId;
141+
newCell.appendChild(document.createTextNode(' '));
142142
}
143143
144-
function appendRows(tableId, arrayOfObjects) {
145-
var tBodyRef = document.getElementById(tableId).getElementsByTagName('tbody')[0];
144+
function appendRows(rows) {
145+
var tBodyRef = document.getElementById(htmlTableId).getElementsByTagName('tbody')[0];
146146
147-
for (const row of arrayOfObjects) {
147+
for (const row of rows) {
148148
// Insert a row at the end of table
149149
var newRow = tBodyRef.insertRow()
150150
@@ -170,8 +170,9 @@ export function generateScroller(basicSelect: string, isCL: boolean): string {
170170
<table id="resultset">
171171
<thead></thead>
172172
<tbody></tbody>
173-
</table>
174-
<p id="nextButton"></p>
173+
<tfoot></tfoot>
174+
</table>
175+
<p id="messageSpan"></p>
175176
<div id="spinnerContent" class="center-screen">
176177
<p id="loadingText">Running statement</p>
177178
<span class="loader"></span>

0 commit comments

Comments
 (0)