From 2b4c019f13d52cdc1ff9e43d6eac134aa42d7f63 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 4 Jan 2017 09:32:23 -0800 Subject: [PATCH] Use object pool in refresh --- src/utils/DomElementObjectPool.ts | 10 ++- src/xterm.js | 114 +++++++++++++++++++++++------- 2 files changed, 96 insertions(+), 28 deletions(-) diff --git a/src/utils/DomElementObjectPool.ts b/src/utils/DomElementObjectPool.ts index 8b84fd6b..3e8a6bad 100644 --- a/src/utils/DomElementObjectPool.ts +++ b/src/utils/DomElementObjectPool.ts @@ -28,7 +28,7 @@ export class DomElementObjectPool { public acquire(): HTMLElement { let element: HTMLElement; if (this._pool.length === 0) { - element = this.createNew(); + element = this._createNew(); } else { element = this._pool.pop(); } @@ -41,13 +41,19 @@ export class DomElementObjectPool { throw new Error('Could not release an element not yet acquired'); } delete this._inUse[element.getAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE)]; + this._cleanElement(element); this._pool.push(element); } - private createNew(): HTMLElement { + private _createNew(): HTMLElement { const element = document.createElement(this._type); const id = DomElementObjectPool._objectCount++; element.setAttribute(DomElementObjectPool.OBJECT_ID_ATTRIBUTE, id.toString(10)); return element; } + + private _cleanElement(element: HTMLElement): void { + element.className = ''; + element.innerHTML = ''; + } } diff --git a/src/xterm.js b/src/xterm.js index 1d220652..ebfd5f20 100644 --- a/src/xterm.js +++ b/src/xterm.js @@ -15,6 +15,7 @@ import { EventEmitter } from './EventEmitter.js'; import { Viewport } from './Viewport.js'; import { rightClickHandler, pasteHandler, copyHandler } from './handlers/Clipboard.js'; import { CircularList } from './utils/CircularList.js'; +import { DomElementObjectPool } from './utils/DomElementObjectPool.js'; import * as Browser from './utils/Browser'; import * as Keyboard from './utils/Keyboard'; @@ -189,6 +190,7 @@ function Terminal(options) { this.savedX; this.savedY; this.savedCols; + this.spanElementObjectPool = new DomElementObjectPool('span'); // stream this.readable = true; @@ -1076,6 +1078,8 @@ Terminal.prototype.refresh = function(start, end, queue) { end = this.rows.length - 1; } + var currentElement; + for (; y <= end; y++) { row = y + this.ydisp; @@ -1093,6 +1097,9 @@ Terminal.prototype.refresh = function(start, end, queue) { attr = this.defAttr; i = 0; + var documentFragment = document.createDocumentFragment(); + var innerHTML = ''; + for (; i < width; i++) { data = line[i][0]; ch = line[i][1]; @@ -1104,17 +1111,38 @@ Terminal.prototype.refresh = function(start, end, queue) { if (data !== attr) { if (attr !== this.defAttr) { - out += ''; + if (innerHTML) { + currentElement.innerHTML = innerHTML; + innerHTML = ''; + } + documentFragment.appendChild(currentElement); + currentElement = null; + //out += ''; } if (data !== this.defAttr) { - if (data === -1) { - out += ''; + documentFragment.appendChild(currentElement); + } + currentElement = this.spanElementObjectPool.acquire(); + if (data === -1) { + currentElement.classList.add('reverse-video', 'terminal-cursor'); + //out += ''; } else { - var classNames = []; + //var classNames = []; bg = data & 0x1ff; fg = (data >> 9) & 0x1ff; @@ -1122,18 +1150,21 @@ Terminal.prototype.refresh = function(start, end, queue) { if (flags & Terminal.flags.BOLD) { if (!Terminal.brokenBold) { - classNames.push('xterm-bold'); + currentElement.classList.add('xterm-bold'); + //classNames.push('xterm-bold'); } // See: XTerm*boldColors if (fg < 8) fg += 8; } if (flags & Terminal.flags.UNDERLINE) { - classNames.push('xterm-underline'); + currentElement.classList.add('xterm-underline'); + //classNames.push('xterm-underline'); } if (flags & Terminal.flags.BLINK) { - classNames.push('xterm-blink'); + currentElement.classList.add('xterm-blink'); + //classNames.push('xterm-blink'); } // If inverse flag is on, then swap the foreground and background variables. @@ -1146,7 +1177,8 @@ Terminal.prototype.refresh = function(start, end, queue) { } if (flags & Terminal.flags.INVISIBLE) { - classNames.push('xterm-hidden'); + currentElement.classList.add('xterm-hidden'); + //classNames.push('xterm-hidden'); } /** @@ -1166,37 +1198,44 @@ Terminal.prototype.refresh = function(start, end, queue) { } if (bg < 256) { - classNames.push('xterm-bg-color-' + bg); + currentElement.classList.add('xterm-bg-color-' + bg); + //classNames.push('xterm-bg-color-' + bg); } if (fg < 256) { - classNames.push('xterm-color-' + fg); + currentElement.classList.add('xterm-color-' + fg); + //classNames.push('xterm-color-' + fg); } - out += '': - out += '>'; + innerHTML += '>'; + //out += '>'; break; default: if (ch <= ' ') { - out += ' '; + innerHTML += ' '; + //out += ' '; } else { - out += ch; + innerHTML += ch; + // out += ch; } break; } @@ -1204,11 +1243,34 @@ Terminal.prototype.refresh = function(start, end, queue) { attr = data; } - if (attr !== this.defAttr) { - out += ''; + if (innerHTML && !currentElement) { + currentElement = this.spanElementObjectPool.acquire(); + // For some reason the text nodes only containing   don't get added to the DOM + //currentElement = document.createTextNode(''); + } + if (currentElement) { + if (innerHTML) { + currentElement.innerHTML = innerHTML; + innerHTML = ''; + } + documentFragment.appendChild(currentElement); + currentElement = null; + } + // if (attr !== this.defAttr) { + // out += ''; + // } + + //this.children[y].innerHTML = out; + //this.children[y].innerHTML = ''; + + // Return spans to the pool + while (this.children[y].children.length) { + var child = this.children[y].children[0]; + this.children[y].removeChild(child); + this.spanElementObjectPool.release(child); } - this.children[y].innerHTML = out; + this.children[y].appendChild(documentFragment) } if (parent) {