Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/browser/ui/list/rowCache.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { $ } from '../../dom.js';
7
import { IDisposable } from '../../../common/lifecycle.js';
8
import { IListRenderer } from './list.js';
9
10
export interface IRow {
11
domNode: HTMLElement;
12
templateId: string;
13
templateData: any;
14
}
15
16
export class RowCache<T> implements IDisposable {
17
18
private cache = new Map<string, IRow[]>();
19
20
private readonly transactionNodesPendingRemoval = new Set<HTMLElement>();
21
private inTransaction = false;
22
23
constructor(private renderers: Map<string, IListRenderer<T, any>>) { }
24
25
/**
26
* Returns a row either by creating a new one or reusing
27
* a previously released row which shares the same templateId.
28
*
29
* @returns A row and `isReusingConnectedDomNode` if the row's node is already in the dom in a stale position.
30
*/
31
alloc(templateId: string): { row: IRow; isReusingConnectedDomNode: boolean } {
32
let result = this.getTemplateCache(templateId).pop();
33
34
let isStale = false;
35
if (result) {
36
isStale = this.transactionNodesPendingRemoval.has(result.domNode);
37
if (isStale) {
38
this.transactionNodesPendingRemoval.delete(result.domNode);
39
}
40
} else {
41
const domNode = $('.monaco-list-row');
42
const renderer = this.getRenderer(templateId);
43
const templateData = renderer.renderTemplate(domNode);
44
result = { domNode, templateId, templateData };
45
}
46
47
return { row: result, isReusingConnectedDomNode: isStale };
48
}
49
50
/**
51
* Releases the row for eventual reuse.
52
*/
53
release(row: IRow): void {
54
if (!row) {
55
return;
56
}
57
58
this.releaseRow(row);
59
}
60
61
/**
62
* Begin a set of changes that use the cache. This lets us skip work when a row is removed and then inserted again.
63
*/
64
transact(makeChanges: () => void) {
65
if (this.inTransaction) {
66
throw new Error('Already in transaction');
67
}
68
69
this.inTransaction = true;
70
71
try {
72
makeChanges();
73
} finally {
74
for (const domNode of this.transactionNodesPendingRemoval) {
75
this.doRemoveNode(domNode);
76
}
77
78
this.transactionNodesPendingRemoval.clear();
79
this.inTransaction = false;
80
}
81
}
82
83
private releaseRow(row: IRow): void {
84
const { domNode, templateId } = row;
85
if (domNode) {
86
if (this.inTransaction) {
87
this.transactionNodesPendingRemoval.add(domNode);
88
} else {
89
this.doRemoveNode(domNode);
90
}
91
}
92
93
const cache = this.getTemplateCache(templateId);
94
cache.push(row);
95
}
96
97
private doRemoveNode(domNode: HTMLElement) {
98
domNode.classList.remove('scrolling');
99
domNode.remove();
100
}
101
102
private getTemplateCache(templateId: string): IRow[] {
103
let result = this.cache.get(templateId);
104
105
if (!result) {
106
result = [];
107
this.cache.set(templateId, result);
108
}
109
110
return result;
111
}
112
113
dispose(): void {
114
this.cache.forEach((cachedRows, templateId) => {
115
for (const cachedRow of cachedRows) {
116
const renderer = this.getRenderer(templateId);
117
renderer.disposeTemplate(cachedRow.templateData);
118
cachedRow.templateData = null;
119
}
120
});
121
122
this.cache.clear();
123
this.transactionNodesPendingRemoval.clear();
124
}
125
126
private getRenderer(templateId: string): IListRenderer<T, any> {
127
const renderer = this.renderers.get(templateId);
128
if (!renderer) {
129
throw new Error(`No renderer found for ${templateId}`);
130
}
131
return renderer;
132
}
133
}
134
135