Path: blob/main/src/vs/base/browser/ui/list/rowCache.ts
5222 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { $ } from '../../dom.js';6import { IDisposable } from '../../../common/lifecycle.js';7import { IListRenderer } from './list.js';89export interface IRow {10domNode: HTMLElement;11templateId: string;12templateData: any;13}1415export class RowCache<T> implements IDisposable {1617private cache = new Map<string, IRow[]>();1819private readonly transactionNodesPendingRemoval = new Set<HTMLElement>();20private inTransaction = false;2122constructor(private renderers: Map<string, IListRenderer<T, any>>) { }2324/**25* Returns a row either by creating a new one or reusing26* a previously released row which shares the same templateId.27*28* @returns A row and `isReusingConnectedDomNode` if the row's node is already in the dom in a stale position.29*/30alloc(templateId: string): { row: IRow; isReusingConnectedDomNode: boolean } {31let result = this.getTemplateCache(templateId).pop();3233let isStale = false;34if (result) {35isStale = this.transactionNodesPendingRemoval.delete(result.domNode);36} else {37const domNode = $('.monaco-list-row');38const renderer = this.getRenderer(templateId);39const templateData = renderer.renderTemplate(domNode);40result = { domNode, templateId, templateData };41}4243return { row: result, isReusingConnectedDomNode: isStale };44}4546/**47* Releases the row for eventual reuse.48*/49release(row: IRow): void {50if (!row) {51return;52}5354this.releaseRow(row);55}5657/**58* Begin a set of changes that use the cache. This lets us skip work when a row is removed and then inserted again.59*/60transact(makeChanges: () => void) {61if (this.inTransaction) {62throw new Error('Already in transaction');63}6465this.inTransaction = true;6667try {68makeChanges();69} finally {70for (const domNode of this.transactionNodesPendingRemoval) {71this.doRemoveNode(domNode);72}7374this.transactionNodesPendingRemoval.clear();75this.inTransaction = false;76}77}7879private releaseRow(row: IRow): void {80const { domNode, templateId } = row;81if (domNode) {82if (this.inTransaction) {83this.transactionNodesPendingRemoval.add(domNode);84} else {85this.doRemoveNode(domNode);86}87}8889const cache = this.getTemplateCache(templateId);90cache.push(row);91}9293private doRemoveNode(domNode: HTMLElement) {94domNode.classList.remove('scrolling');95domNode.remove();96}9798private getTemplateCache(templateId: string): IRow[] {99let result = this.cache.get(templateId);100101if (!result) {102result = [];103this.cache.set(templateId, result);104}105106return result;107}108109dispose(): void {110this.cache.forEach((cachedRows, templateId) => {111for (const cachedRow of cachedRows) {112const renderer = this.getRenderer(templateId);113renderer.disposeTemplate(cachedRow.templateData);114cachedRow.templateData = null;115}116});117118this.cache.clear();119this.transactionNodesPendingRemoval.clear();120}121122private getRenderer(templateId: string): IListRenderer<T, any> {123const renderer = this.renderers.get(templateId);124if (!renderer) {125throw new Error(`No renderer found for ${templateId}`);126}127return renderer;128}129}130131132