From 3d20c2f266d4a64b4ff027145382d9852955a510 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 5 Aug 2017 18:59:42 -0700 Subject: [PATCH] Resize both buffers on resize This brings in proper support to resize both buffers (#510) and fixes an exception that was caused by wrongfully not clearing the normal buffer when resizing while the alt buffer is active. There was an obscure bug in this that could have caused some great confusion later on; When switching to the alt buffer, a hard terminal reset was performed which tried to retain the buffers. However, because buffers was initialized in the Terminal constructor to a new BufferSet, the Terminal.buffer convenience pointer was pointing at a stale alt buffer which was the one actually being used, not Terminal.buffers.alt. Fixes #842 Fixes #510 --- src/Buffer.ts | 81 +++++++++++++++++++++++++++++++++++++++--- src/BufferSet.ts | 5 +++ src/Interfaces.ts | 1 + src/xterm.js | 89 +++++++---------------------------------------- 4 files changed, 94 insertions(+), 82 deletions(-) diff --git a/src/Buffer.ts b/src/Buffer.ts index 12b2137..bd3645e 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -13,21 +13,21 @@ import { CircularList } from './utils/CircularList'; * - scroll position */ export class Buffer { - public lines: CircularList<[number, string, number][]>; + public readonly lines: CircularList<[number, string, number][]>; public savedY: number; public savedX: number; /** * Create a new Buffer. - * @param {Terminal} terminal - The terminal the Buffer will belong to + * @param {Terminal} _terminal - The terminal the Buffer will belong to * @param {number} ydisp - The scroll position of the Buffer in the viewport * @param {number} ybase - The scroll position of the y cursor (ybase + y = the y position within the Buffer) * @param {number} y - The cursor's y position after ybase * @param {number} x - The cursor's x position after ybase */ constructor( - private terminal: ITerminal, + private _terminal: ITerminal, public ydisp: number = 0, public ybase: number = 0, public y: number = 0, @@ -36,7 +36,78 @@ export class Buffer { public scrollTop: number = 0, public tabs: any = {}, ) { - this.lines = new CircularList<[number, string, number][]>(this.terminal.scrollback); - this.scrollBottom = this.terminal.rows - 1; + this.lines = new CircularList<[number, string, number][]>(this._terminal.scrollback); + this.scrollBottom = this._terminal.rows - 1; + } + + public resize(newCols: number, newRows: number): void { + // Don't resize the buffer if it's empty and hasn't been used yet. + if (this.lines.length === 0) { + return; + } + + // Deal with columns increasing (we don't do anything when columns reduce) + if (this._terminal.cols < newCols) { + const ch: [number, string, number] = [this._terminal.defAttr, ' ', 1]; // does xterm use the default attr? + for (let i = 0; i < this.lines.length; i++) { + if (this.lines.get(i) === undefined) { + this.lines.set(i, this._terminal.blankLine()); + } + while (this.lines.get(i).length < newCols) { + this.lines.get(i).push(ch); + } + } + } + + // Resize rows in both directions as needed + let addToY = 0; + if (this._terminal.rows < newRows) { + for (let y = this._terminal.rows; y < newRows; y++) { + if (this.lines.length < newRows + this.ybase) { + if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) { + // There is room above the buffer and there are no empty elements below the line, + // scroll up + this.ybase--; + addToY++; + if (this.ydisp > 0) { + // Viewport is at the top of the buffer, must increase downwards + this.ydisp--; + } + } else { + // Add a blank line if there is no buffer left at the top to scroll to, or if there + // are blank lines after the cursor + this.lines.push(this._terminal.blankLine()); + } + } + } + } else { // (this._terminal.rows >= newRows) + for (let y = this._terminal.rows; y > newRows; y--) { + if (this.lines.length > newRows + this.ybase) { + if (this.lines.length > this.ybase + this.y + 1) { + // The line is a blank line below the cursor, remove it + this.lines.pop(); + } else { + // The line is the cursor, scroll down + this.ybase++; + this.ydisp++; + } + } + } + } + + // Make sure that the cursor stays on screen + if (this.y >= newRows) { + this.y = newRows - 1; + } + if (addToY) { + this.y += addToY; + } + + if (this.x >= newCols) { + this.x = newCols - 1; + } + + this.scrollTop = 0; + this.scrollBottom = newRows - 1; } } diff --git a/src/BufferSet.ts b/src/BufferSet.ts index e86c098..4a65dbf 100644 --- a/src/BufferSet.ts +++ b/src/BufferSet.ts @@ -65,4 +65,9 @@ export class BufferSet extends EventEmitter implements IBufferSet { this._activeBuffer = this._alt; this.emit('activate', this._alt); } + + public resize(newCols: number, newRows: number): void { + this._normal.resize(newCols, newRows); + this._alt.resize(newCols, newRows); + } } diff --git a/src/Interfaces.ts b/src/Interfaces.ts index f19a7f2..d463fcb 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -48,6 +48,7 @@ export interface ITerminal { emit(event: string, data: any); reset(): void; showCursor(): void; + blankLine(cur?: boolean, isWrapped?: boolean); } export interface IBuffer { diff --git a/src/xterm.js b/src/xterm.js index 5aa069e..92fc0df 100644 --- a/src/xterm.js +++ b/src/xterm.js @@ -221,7 +221,7 @@ function Terminal(options) { this.surrogate_high = ''; // Create the terminal's buffers and set the current buffer - this.buffers = new BufferSet(this); + this.buffers = this.buffers || new BufferSet(this); this.buffer = this.buffers.active; // Convenience shortcut; this.buffers.on('activate', function (buffer) { this._terminal.buffer = buffer; @@ -1930,86 +1930,21 @@ Terminal.prototype.resize = function(x, y) { if (x < 1) x = 1; if (y < 1) y = 1; - // resize cols - j = this.cols; - if (j < x) { - ch = [this.defAttr, ' ', 1]; // does xterm use the default attr? - i = this.buffer.lines.length; - while (i--) { - if (this.buffer.lines.get(i) === undefined) { - this.buffer.lines.set(i, this.blankLine()); - } - while (this.buffer.lines.get(i).length < x) { - this.buffer.lines.get(i).push(ch); - } - } - } - - this.cols = x; - this.setupStops(this.cols); + this.buffers.resize(x, y); - // resize rows - j = this.rows; - addToY = 0; - if (j < y) { - el = this.element; - while (j++ < y) { - // y is rows, not this.buffer.y - if (this.buffer.lines.length < y + this.buffer.ybase) { - if (this.buffer.ybase > 0 && this.buffer.lines.length <= this.buffer.ybase + this.buffer.y + addToY + 1) { - // There is room above the buffer and there are no empty elements below the line, - // scroll up - this.buffer.ybase--; - addToY++; - if (this.buffer.ydisp > 0) { - // Viewport is at the top of the buffer, must increase downwards - this.buffer.ydisp--; - } - } else { - // Add a blank line if there is no buffer left at the top to scroll to, or if there - // are blank lines after the cursor - this.buffer.lines.push(this.blankLine()); - } - } - if (this.children.length < y) { - this.insertRow(); - } - } - } else { // (j > y) - while (j-- > y) { - if (this.buffer.lines.length > y + this.buffer.ybase) { - if (this.buffer.lines.length > this.buffer.ybase + this.buffer.y + 1) { - // The line is a blank line below the cursor, remove it - this.buffer.lines.pop(); - } else { - // The line is the cursor, scroll down - this.buffer.ybase++; - this.buffer.ydisp++; - } - } - if (this.children.length > y) { - el = this.children.shift(); - if (!el) continue; - el.parentNode.removeChild(el); - } - } - } - this.rows = y; - - // Make sure that the cursor stays on screen - if (this.buffer.y >= y) { - this.buffer.y = y - 1; - } - if (addToY) { - this.buffer.y += addToY; + // Adjust rows in the DOM to accurately reflect the new dimensions + while (this.children.length < y) { + this.insertRow(); } - - if (this.buffer.x >= x) { - this.buffer.x = x - 1; + while (this.children.length > y) { + el = this.children.shift(); + if (!el) continue; + el.parentNode.removeChild(el); } - this.buffer.scrollTop = 0; - this.buffer.scrollBottom = y - 1; + this.cols = x; + this.rows = y; + this.setupStops(this.cols); this.charMeasure.measure(); -- 2.39.2