From 2a0da173f200b006f292b0592d2d47334e78bbc9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 28 Dec 2018 14:55:15 -0800 Subject: [PATCH] Add a bunch of reflow tests --- src/Buffer.test.ts | 115 +++++++++++++++++++++++++++++++++++++++++++++ src/Buffer.ts | 18 +++++-- 2 files changed, 128 insertions(+), 5 deletions(-) diff --git a/src/Buffer.test.ts b/src/Buffer.test.ts index 0546dfe8..189b3ad6 100644 --- a/src/Buffer.test.ts +++ b/src/Buffer.test.ts @@ -233,6 +233,121 @@ describe('Buffer', () => { } }); }); + + describe('reflow', () => { + beforeEach(() => { + terminal.eraseAttr = () => DEFAULT_ATTR; + // Needed until the setting is removed + terminal.options.experimentalBufferLineImpl = 'TypedArray'; + }); + it('should not wrap empty lines', () => { + buffer.fillViewportRows(); + assert.equal(buffer.lines.length, INIT_ROWS); + buffer.resize(INIT_COLS - 5, INIT_ROWS); + assert.equal(buffer.lines.length, INIT_ROWS); + }); + it('should shrink row length', () => { + buffer.fillViewportRows(); + buffer.resize(5, 10); + assert.equal(buffer.lines.length, 10); + assert.equal(buffer.lines.get(0).length, 5); + assert.equal(buffer.lines.get(1).length, 5); + assert.equal(buffer.lines.get(2).length, 5); + assert.equal(buffer.lines.get(3).length, 5); + assert.equal(buffer.lines.get(4).length, 5); + assert.equal(buffer.lines.get(5).length, 5); + assert.equal(buffer.lines.get(6).length, 5); + assert.equal(buffer.lines.get(7).length, 5); + assert.equal(buffer.lines.get(8).length, 5); + assert.equal(buffer.lines.get(9).length, 5); + }); + it('should wrap and unwrap lines', () => { + buffer.fillViewportRows(); + buffer.resize(5, 10); + terminal.cols = 5; + const firstLine = buffer.lines.get(0); + for (let i = 0; i < 5; i++) { + const code = 'a'.charCodeAt(0) + i; + const char = String.fromCharCode(code); + firstLine.set(i, [null, char, 1, code]); + } + assert.equal(buffer.lines.get(0).length, 5); + assert.equal(buffer.lines.get(0).translateToString(), 'abcde'); + buffer.resize(1, 10); + terminal.cols = 1; + assert.equal(buffer.lines.length, 10); + assert.equal(buffer.lines.get(0).translateToString(), 'a'); + assert.equal(buffer.lines.get(1).translateToString(), 'b'); + assert.equal(buffer.lines.get(2).translateToString(), 'c'); + assert.equal(buffer.lines.get(3).translateToString(), 'd'); + assert.equal(buffer.lines.get(4).translateToString(), 'e'); + assert.equal(buffer.lines.get(5).translateToString(), ' '); + assert.equal(buffer.lines.get(6).translateToString(), ' '); + assert.equal(buffer.lines.get(7).translateToString(), ' '); + assert.equal(buffer.lines.get(8).translateToString(), ' '); + assert.equal(buffer.lines.get(9).translateToString(), ' '); + buffer.resize(5, 10); + terminal.cols = 5; + assert.equal(buffer.lines.length, 10); + assert.equal(buffer.lines.get(0).translateToString(), 'abcde'); + assert.equal(buffer.lines.get(1).translateToString(), ' '); + assert.equal(buffer.lines.get(2).translateToString(), ' '); + assert.equal(buffer.lines.get(3).translateToString(), ' '); + assert.equal(buffer.lines.get(4).translateToString(), ' '); + assert.equal(buffer.lines.get(5).translateToString(), ' '); + assert.equal(buffer.lines.get(6).translateToString(), ' '); + assert.equal(buffer.lines.get(7).translateToString(), ' '); + assert.equal(buffer.lines.get(8).translateToString(), ' '); + assert.equal(buffer.lines.get(9).translateToString(), ' '); + }); + it('should discard parts of wrapped lines that go out of the scrollback', () => { + buffer.fillViewportRows(); + terminal.options.scrollback = 1; + buffer.resize(10, 5); + terminal.cols = 10; + terminal.rows = 5; + const lastLine = buffer.lines.get(4); + for (let i = 0; i < 10; i++) { + const code = 'a'.charCodeAt(0) + i; + const char = String.fromCharCode(code); + lastLine.set(i, [null, char, 1, code]); + } + assert.equal(buffer.lines.length, 5); + buffer.y = 4; + buffer.resize(2, 5); + terminal.cols = 2; + assert.equal(buffer.y, 4); + assert.equal(buffer.ybase, 1); + assert.equal(buffer.lines.length, 6); + assert.equal(buffer.lines.get(0).translateToString(), ' '); + assert.equal(buffer.lines.get(1).translateToString(), 'ab'); + assert.equal(buffer.lines.get(2).translateToString(), 'cd'); + assert.equal(buffer.lines.get(3).translateToString(), 'ef'); + assert.equal(buffer.lines.get(4).translateToString(), 'gh'); + assert.equal(buffer.lines.get(5).translateToString(), 'ij'); + buffer.resize(1, 5); + terminal.cols = 1; + assert.equal(buffer.y, 4); + assert.equal(buffer.ybase, 1); + assert.equal(buffer.lines.length, 6); + assert.equal(buffer.lines.get(0).translateToString(), 'e'); + assert.equal(buffer.lines.get(1).translateToString(), 'f'); + assert.equal(buffer.lines.get(2).translateToString(), 'g'); + assert.equal(buffer.lines.get(3).translateToString(), 'h'); + assert.equal(buffer.lines.get(4).translateToString(), 'i'); + assert.equal(buffer.lines.get(5).translateToString(), 'j'); + buffer.resize(10, 5); + terminal.cols = 10; + assert.equal(buffer.y, 0); + assert.equal(buffer.ybase, 0); + assert.equal(buffer.lines.length, 5); + assert.equal(buffer.lines.get(0).translateToString(), 'efghij '); + assert.equal(buffer.lines.get(1).translateToString(), ' '); + assert.equal(buffer.lines.get(2).translateToString(), ' '); + assert.equal(buffer.lines.get(3).translateToString(), ' '); + assert.equal(buffer.lines.get(4).translateToString(), ' '); + }); + }); }); describe('buffer marked to have no scrollback', () => { diff --git a/src/Buffer.ts b/src/Buffer.ts index 41a76ccc..5f6791d8 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -164,9 +164,9 @@ export class Buffer implements IBuffer { if (this.lines.length > 0) { // Deal with columns increasing (reducing needs to happen after reflow) if (this._terminal.cols < newCols) { - const ch: CharData = [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; // does xterm use the default attr? + const fillCharData: CharData = [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; for (let i = 0; i < this.lines.length; i++) { - this.lines.get(i).resize(newCols, ch); + this.lines.get(i).resize(newCols, fillCharData); } } @@ -237,9 +237,11 @@ export class Buffer implements IBuffer { if (this._terminal.options.experimentalBufferLineImpl === 'TypedArray') { this._reflow(newCols); + // Trim the end of the line off if cols shrunk if (this._terminal.cols > newCols) { + const fillCharData: CharData = [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; for (let i = 0; i < this.lines.length; i++) { - this.lines.get(i).resize(newCols, null); + this.lines.get(i).resize(newCols, fillCharData); } } } @@ -273,7 +275,7 @@ export class Buffer implements IBuffer { // Check how many lines it's wrapped for const wrappedLines: BufferLine[] = [this.lines.get(y) as BufferLine]; - while (nextLine.isWrapped) { + while (nextLine.isWrapped && i < this.lines.length) { wrappedLines.push(nextLine); nextLine = this.lines.get(++i) as BufferLine; } @@ -361,7 +363,13 @@ export class Buffer implements IBuffer { const cellsNeeded = (wrappedLines.length - 1) * this._terminal.cols + lastLineLength; const linesNeeded = Math.ceil(cellsNeeded / newCols); const linesToAdd = linesNeeded - wrappedLines.length; - const trimmedLines = Math.max(0, this.lines.length - this.lines.maxLength + linesToAdd); + let trimmedLines: number; + if (this.ybase === 0 && this.y !== this.lines.length - 1) { + // If the top section of the buffer is not yet filled + trimmedLines = Math.max(0, this.y - this.lines.maxLength + linesToAdd); + } else { + trimmedLines = Math.max(0, this.lines.length - this.lines.maxLength + linesToAdd); + } // Add the new lines const newLines: BufferLine[] = [];