diff --git a/src/Buffer.ts b/src/Buffer.ts index 12b2137d..bd3645e7 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 e86c098f..4a65dbfe 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 f19a7f28..d463fcb7 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 5aa069e7..92fc0df8 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.buffers.resize(x, y); + + // Adjust rows in the DOM to accurately reflect the new dimensions + while (this.children.length < y) { + this.insertRow(); + } + while (this.children.length > y) { + el = this.children.shift(); + if (!el) continue; + el.parentNode.removeChild(el); } this.cols = x; - this.setupStops(this.cols); - - // 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; - } - - if (this.buffer.x >= x) { - this.buffer.x = x - 1; - } - - this.buffer.scrollTop = 0; - this.buffer.scrollBottom = y - 1; + this.setupStops(this.cols); this.charMeasure.measure();