diff --git a/src/Buffer.ts b/src/Buffer.ts index 52d9572d..bc2d5f20 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -3,13 +3,13 @@ * @license MIT */ -import { CircularList, IInsertEvent, IDeleteEvent } from './common/CircularList'; -import { CharData, ITerminal, IBuffer, IBufferLine, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult } from './Types'; -import { EventEmitter } from './common/EventEmitter'; import { IMarker } from 'xterm'; import { BufferLine } from './BufferLine'; +import { reflowLargerApplyNewLayout, reflowLargerCreateNewLayout, reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths } from './BufferReflow'; +import { CircularList, IDeleteEvent, IInsertEvent } from './common/CircularList'; +import { EventEmitter } from './common/EventEmitter'; import { DEFAULT_COLOR } from './renderer/atlas/Types'; -import { reflowSmallerGetNewLineLengths, reflowLargerGetLinesToRemove, reflowLargerCreateNewLayout, reflowLargerApplyNewLayout } from './BufferReflow'; +import { BufferIndex, CharData, IBuffer, IBufferLine, IBufferStringIterator, IBufferStringIteratorResult, ITerminal } from './Types'; export const DEFAULT_ATTR = (0 << 18) | (DEFAULT_COLOR << 9) | (256 << 0); export const CHAR_DATA_ATTR_INDEX = 0; @@ -212,7 +212,7 @@ export class Buffer implements IBuffer { this.scrollBottom = newRows - 1; if (this._hasScrollback) { - this._reflow(newCols); + this._reflow(newCols, newRows); // Trim the end of the line off if cols shrunk if (this._cols > newCols) { @@ -226,7 +226,7 @@ export class Buffer implements IBuffer { this._rows = newRows; } - private _reflow(newCols: number): void { + private _reflow(newCols: number, newRows: number): void { if (this._cols === newCols) { return; } @@ -235,12 +235,12 @@ export class Buffer implements IBuffer { if (newCols > this._cols) { this._reflowLarger(newCols); } else { - this._reflowSmaller(newCols); + this._reflowSmaller(newCols, newRows); } } private _reflowLarger(newCols: number): void { - const toRemove: number[] = reflowLargerGetLinesToRemove(this.lines, newCols); + const toRemove: number[] = reflowLargerGetLinesToRemove(this.lines, newCols, this.ybase + this.y); if (toRemove.length > 0) { const newLayoutResult = reflowLargerCreateNewLayout(this.lines, toRemove); reflowLargerApplyNewLayout(this.lines, newLayoutResult.layout); @@ -253,9 +253,13 @@ export class Buffer implements IBuffer { let viewportAdjustments = countRemoved; while (viewportAdjustments-- > 0) { if (this.ybase === 0) { - this.y--; - // Add an extra row at the bottom of the viewport - this.lines.push(new BufferLine(newCols, FILL_CHAR_DATA)); + if (this.y > 0) { + this.y--; + } + if (this.lines.length < this._rows) { + // Add an extra row at the bottom of the viewport + this.lines.push(new BufferLine(newCols, FILL_CHAR_DATA)); + } } else { if (this.ydisp === this.ybase) { this.ydisp--; @@ -265,7 +269,7 @@ export class Buffer implements IBuffer { } } - private _reflowSmaller(newCols: number): void { + private _reflowSmaller(newCols: number, newRows: number): void { // Gather all BufferLines that need to be inserted into the Buffer here so that they can be // batched up and only committed once const toInsert = []; @@ -285,6 +289,13 @@ export class Buffer implements IBuffer { wrappedLines.unshift(nextLine); } + // If these lines contain the cursor don't touch them, the program will handle fixing up + // wrapped lines with the cursor + const absoluteY = this.ybase + this.y; + if (absoluteY >= y && absoluteY < y + wrappedLines.length) { + continue; + } + const lastLineLength = wrappedLines[wrappedLines.length - 1].getTrimmedLength(); const destLineLengths = reflowSmallerGetNewLineLengths(wrappedLines, this._cols, newCols); const linesToAdd = destLineLengths.length - wrappedLines.length; @@ -357,10 +368,13 @@ export class Buffer implements IBuffer { this.ydisp++; } } else { - if (this.ybase === this.ydisp) { - this.ydisp++; + // Ensure ybase does not exceed its maximum value + if (this.ybase < Math.min(this.lines.maxLength, this.lines.length + countToInsert) - newRows) { + if (this.ybase === this.ydisp) { + this.ydisp++; + } + this.ybase++; } - this.ybase++; } } } diff --git a/src/BufferReflow.ts b/src/BufferReflow.ts index 59934e46..24ab69e6 100644 --- a/src/BufferReflow.ts +++ b/src/BufferReflow.ts @@ -3,10 +3,10 @@ * @license MIT */ +import { FILL_CHAR_DATA } from './Buffer'; import { BufferLine } from './BufferLine'; import { CircularList, IDeleteEvent } from './common/CircularList'; import { IBufferLine } from './Types'; -import { FILL_CHAR_DATA } from './Buffer'; export interface INewLayoutResult { layout: number[]; @@ -19,7 +19,7 @@ export interface INewLayoutResult { * @param lines The buffer lines. * @param newCols The columns after resize. */ -export function reflowLargerGetLinesToRemove(lines: CircularList, newCols: number): number[] { +export function reflowLargerGetLinesToRemove(lines: CircularList, newCols: number, bufferAbsoluteY: number): number[] { // Gather all BufferLines that need to be removed from the Buffer here so that they can be // batched up and only committed once const toRemove: number[] = []; @@ -39,6 +39,13 @@ export function reflowLargerGetLinesToRemove(lines: CircularList, n nextLine = lines.get(++i) as BufferLine; } + // If these lines contain the cursor don't touch them, the program will handle fixing up wrapped + // lines with the cursor + if (bufferAbsoluteY >= y && bufferAbsoluteY < i) { + y += wrappedLines.length - 1; + continue; + } + // Copy buffer data to new locations let destLineIndex = 0; let destCol = wrappedLines[destLineIndex].getTrimmedLength(); @@ -64,7 +71,7 @@ export function reflowLargerGetLinesToRemove(lines: CircularList, n } // Make sure the last cell isn't wide, if it is copy it to the current dest - if (destCol === 0) { + if (destCol === 0 && destLineIndex !== 0) { if (wrappedLines[destLineIndex - 1].getWidth(newCols - 1) === 2) { wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[destLineIndex - 1], newCols - 1, destCol++, 1, false); // Null out the end of the last row @@ -166,7 +173,6 @@ export function reflowLargerApplyNewLayout(lines: CircularList, new */ export function reflowSmallerGetNewLineLengths(wrappedLines: BufferLine[], oldCols: number, newCols: number): number[] { const newLineLengths: number[] = []; - const cellsNeeded = wrappedLines.map(l => l.getTrimmedLength()).reduce((p, c) => p + c); // Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and