From 785b4f37d34d038f6678b73efe5fb12436ca7043 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 3 Feb 2016 11:22:11 +0100 Subject: [PATCH] list: improve row cache --- src/vs/base/browser/ui/list/listImpl.ts | 34 ++++++++------ src/vs/base/browser/ui/list/rowCache.ts | 62 +++++++++++++------------ 2 files changed, 53 insertions(+), 43 deletions(-) diff --git a/src/vs/base/browser/ui/list/listImpl.ts b/src/vs/base/browser/ui/list/listImpl.ts index 7458390d050..9cea31a7d90 100644 --- a/src/vs/base/browser/ui/list/listImpl.ts +++ b/src/vs/base/browser/ui/list/listImpl.ts @@ -12,17 +12,19 @@ import { IScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEleme import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/impl/scrollableElement'; import { RangeMap } from './rangeMap'; import { IScrollEvent, IDelegate, IRendererMap } from './list'; -import { RowCache } from './rowCache'; +import { RowCache, IRow } from './rowCache'; interface IItem { - domNode: HTMLElement; + height: number; + templateId: string; + row: IRow; } export class List implements IScrollable { private items: IItem[]; private rangeMap: RangeMap; - private rowCache: RowCache; + private cache: RowCache; private _scrollTop: number; private _viewHeight: number; @@ -41,7 +43,7 @@ export class List implements IScrollable { constructor(container: HTMLElement, delegate: IDelegate, renderers: IRendererMap) { this.items = []; this.rangeMap = new RangeMap(); - this.rowCache = new RowCache(renderers); + this.cache = new RowCache(renderers); this.domNode = document.createElement('div'); this.domNode.className = 'monaco-list'; @@ -176,32 +178,38 @@ export class List implements IScrollable { private insertItemInDOM(index: number): void { const item = this.items[index]; - if (!item.domNode) { - // item.domNode = this.cache.alloc(this.templateId); - item.domNode = document.createElement('div'); - + if (!item.row) { + item.row = this.cache.alloc(item.templateId); // used in reverse lookup from HTMLElement to Item // ( this.element)[TreeView.BINDING] = this; } - if (item.domNode.parentElement) { + if (item.row.domNode.parentElement) { return; } const nextItem = this.items[index + 1]; - if (nextItem && nextItem.domNode) { - this.rowsContainer.insertBefore(item.domNode, nextItem.domNode); + if (nextItem && nextItem.row) { + this.rowsContainer.insertBefore(item.row.domNode, nextItem.row.domNode); } else { - this.rowsContainer.appendChild(item.domNode); + this.rowsContainer.appendChild(item.row.domNode); } this.renderItem(index); } private removeItemFromDOM(index: number): void { - // TODO + const item = this.items[index]; + + if (!item.row) { + return; + } + + // ( this.element)[TreeView.BINDING] = null; + this.cache.release(item.row); + item.row = null; } private renderItem(index: number): void { diff --git a/src/vs/base/browser/ui/list/rowCache.ts b/src/vs/base/browser/ui/list/rowCache.ts index 064a4472e04..d0ea9292542 100644 --- a/src/vs/base/browser/ui/list/rowCache.ts +++ b/src/vs/base/browser/ui/list/rowCache.ts @@ -5,10 +5,10 @@ import { IRendererMap } from './list'; import { IDisposable } from 'vs/base/common/lifecycle'; -import * as DOM from 'vs/base/browser/dom'; +import { append, emmet as $, addClass, removeClass } from 'vs/base/browser/dom'; export interface IRow { - element: HTMLElement; + domNode: HTMLElement; templateId: string; templateData: any; } @@ -32,72 +32,74 @@ export class RowCache implements IDisposable { private scrollingRow: IRow; constructor(private renderers: IRendererMap) { - this.cache = { '': [] }; + this.cache = Object.create(null); this.scrollingRow = null; } - public alloc(templateId: string): IRow { + /** + * Returns a row either by creating a new one or reusing + * a previously released row which shares the same templateId. + */ + alloc(templateId: string): IRow { let result = this.getTemplateCache(templateId).pop(); if (!result) { - const content = document.createElement('div'); - content.className = 'content'; - - const row = document.createElement('div'); - row.appendChild(content); - + const domNode = $('div'); + const content = append(domNode, $('.content')); const renderer = this.renderers[templateId]; - - result = { - element: row, - templateId: templateId, - templateData: renderer.renderTemplate(content) - }; + const templateData = renderer.renderTemplate(content); + result = { domNode, templateId, templateData }; } return result; } - public release(templateId: string, row: IRow): void { - var lastScrollTime = getLastScrollTime(row.element); + /** + * Releases the row for eventual reuse. The row's domNode + * will eventually be removed from its parent, given that + * it is not the currently scrolling row (for OS X ballistic + * scrolling). + */ + release(row: IRow): void { + var lastScrollTime = getLastScrollTime(row.domNode); if (!lastScrollTime) { - removeFromParent(row.element); - this.getTemplateCache(templateId).push(row); + removeFromParent(row.domNode); + this.getTemplateCache(row.templateId).push(row); return; } if (this.scrollingRow) { - var lastKnownScrollTime = getLastScrollTime(this.scrollingRow.element); + var lastKnownScrollTime = getLastScrollTime(this.scrollingRow.domNode); if (lastKnownScrollTime > lastScrollTime) { - removeFromParent(row.element); - this.getTemplateCache(templateId).push(row); + removeFromParent(row.domNode); + this.getTemplateCache(row.templateId).push(row); return; } - if (this.scrollingRow.element.parentElement) { - removeFromParent(this.scrollingRow.element); - DOM.removeClass(this.scrollingRow.element, 'scrolling'); + if (this.scrollingRow.domNode.parentElement) { + removeFromParent(this.scrollingRow.domNode); + removeClass(this.scrollingRow.domNode, 'scrolling'); this.getTemplateCache(this.scrollingRow.templateId).push(this.scrollingRow); } } this.scrollingRow = row; - DOM.addClass(this.scrollingRow.element, 'scrolling'); + addClass(this.scrollingRow.domNode, 'scrolling'); } private getTemplateCache(templateId: string): IRow[] { return this.cache[templateId] || (this.cache[templateId] = []); } - public garbageCollect(): void { + garbageCollect(): void { if (this.cache) { Object.keys(this.cache).forEach(templateId => { this.cache[templateId].forEach(cachedRow => { const renderer = this.renderers[templateId]; renderer.disposeTemplate(cachedRow.templateData); - cachedRow.element = null; + cachedRow.domNode = null; cachedRow.templateData = null; }); @@ -112,7 +114,7 @@ export class RowCache implements IDisposable { } } - public dispose(): void { + dispose(): void { this.garbageCollect(); this.cache = null; this.renderers = null; -- GitLab