From 314f98f2a63cd100f1084df46aab0bdbe5c82624 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 27 Dec 2018 23:35:52 -0800 Subject: [PATCH] Mostly working for reflowing to smaller --- src/Buffer.ts | 231 +++++++++++++++++++++++++++++++++------------- src/BufferLine.ts | 17 +++- 2 files changed, 179 insertions(+), 69 deletions(-) diff --git a/src/Buffer.ts b/src/Buffer.ts index 6b247afe..fc8e008e 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -235,83 +235,187 @@ export class Buffer implements IBuffer { this.scrollBottom = newRows - 1; if (this._terminal.options.experimentalBufferLineImpl === 'TypedArray') { - this._reflow(newCols, newRows); + this._reflow(newCols); } } - private _reflow(newCols: number, newRows: number): void { + private _reflow(newCols: number): void { if (this._terminal.cols === newCols) { return; } // Iterate through rows, ignore the last one as it cannot be wrapped for (let y = 0; y < this.lines.length - 1; y++) { - // Check if this row is wrapped - let i = y; - let nextLine = this.lines.get(++i) as BufferLine; - if (!nextLine.isWrapped) { - continue; - } - - // Check how many lines it's wrapped for - const wrappedLines: BufferLine[] = [this.lines.get(y) as BufferLine]; - while (nextLine.isWrapped) { - wrappedLines.push(nextLine); - nextLine = this.lines.get(++i) as BufferLine; - } - if (newCols > this._terminal.cols) { - let destLineIndex = 0; - let destCol = this._terminal.cols; - let srcLineIndex = 1; - let srcCol = 0; - while (srcLineIndex < wrappedLines.length) { - const srcRemainingCells = this._terminal.cols - srcCol; - const destRemainingCells = newCols - destCol; - const cellsToCopy = Math.min(srcRemainingCells, destRemainingCells); - wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[srcLineIndex], srcCol, destCol, cellsToCopy); - destCol += cellsToCopy; - if (destCol === newCols) { - destLineIndex++; - destCol = 0; - } - srcCol += cellsToCopy; - if (srcCol === this._terminal.cols) { - srcLineIndex++; - srcCol = 0; - } - } - - // Work backwards and remove any rows at the end that only contain null cells - let countToRemove = 0; - for (let i = wrappedLines.length - 1; i > 0; i--) { - if (wrappedLines[i].getTrimmedLength() === 0) { - countToRemove++; - } else { - break; - } - } - - // Remove rows and adjust cursor - if (countToRemove > 0) { - this.lines.splice(y + wrappedLines.length - countToRemove, countToRemove); - while (countToRemove-- > 0) { - if (this.ybase === 0) { - this.y--; - } else { - if (this.ydisp === this.ybase) { - this.ydisp--; - } - this.ybase--; - } - } - } + y += this._reflowLarger(y, newCols); } else { - + y += this._reflowSmaller(y, newCols); } } } + private _reflowLarger(y: number, newCols: number): number { + // Check if this row is wrapped + let i = y; + let nextLine = this.lines.get(++i) as BufferLine; + if (!nextLine.isWrapped) { + return 0; + } + + // Check how many lines it's wrapped for + const wrappedLines: BufferLine[] = [this.lines.get(y) as BufferLine]; + while (nextLine.isWrapped) { + wrappedLines.push(nextLine); + nextLine = this.lines.get(++i) as BufferLine; + } + + // Copy buffer data to new locations + let destLineIndex = 0; + let destCol = this._terminal.cols; + let srcLineIndex = 1; + let srcCol = 0; + while (srcLineIndex < wrappedLines.length) { + const srcRemainingCells = this._terminal.cols - srcCol; + const destRemainingCells = newCols - destCol; + const cellsToCopy = Math.min(srcRemainingCells, destRemainingCells); + wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[srcLineIndex], srcCol, destCol, cellsToCopy, false); + destCol += cellsToCopy; + if (destCol === newCols) { + destLineIndex++; + destCol = 0; + } + srcCol += cellsToCopy; + if (srcCol === this._terminal.cols) { + srcLineIndex++; + srcCol = 0; + } + } + + // Clear out remaining cells or fragments could remain + // TODO: @jerch can this be a const? + const fillCharData: CharData = [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; + wrappedLines[destLineIndex].replaceCells(destCol, newCols, fillCharData); + + // Work backwards and remove any rows at the end that only contain null cells + let countToRemove = 0; + for (let i = wrappedLines.length - 1; i > 0; i--) { + if (wrappedLines[i].getTrimmedLength() === 0) { + countToRemove++; + } else { + break; + } + } + + // Remove rows and adjust cursor + if (countToRemove > 0) { + this.lines.splice(y + wrappedLines.length - countToRemove, countToRemove); + let removing = countToRemove; + while (removing-- > 0) { + if (this.ybase === 0) { + this.y--; + // Add an extra row at the bottom of the viewport + this.lines.push(new this._bufferLineConstructor(newCols, fillCharData)); + } else { + if (this.ydisp === this.ybase) { + this.ydisp--; + } + this.ybase--; + } + } + } + // TODO: Handle list trimming + + return wrappedLines.length - countToRemove - 1; + } + + private _reflowSmaller(y: number, newCols: number): number { + // Check whether this line is a problem + const line = this.lines.get(y) as BufferLine; + if (line.getTrimmedLength() <= newCols) { + return 0; + } + + // TODO: How is the cursor x handled if it's wrapped? Do something special when the cursor is this line? + + + // Gather wrapped lines if it's wrapped + let lineIndex = y; + let nextLine = this.lines.get(++lineIndex) as BufferLine; + const wrappedLines: BufferLine[] = [line]; + while (nextLine.isWrapped) { + wrappedLines.push(nextLine); + nextLine = this.lines.get(++lineIndex) as BufferLine; + } + + // Determine how many lines need to be inserted at the end, based on the trimmed length of + // the last wrapped line + if (wrappedLines[wrappedLines.length - 1].getTrimmedLength() === undefined) { + debugger; + } + const lastLineLength = wrappedLines[wrappedLines.length - 1].getTrimmedLength(); + const cellsNeeded = (wrappedLines.length - 1) * this._terminal.cols + lastLineLength; + const linesNeeded = Math.ceil(cellsNeeded / newCols); + const linesToAdd = linesNeeded - wrappedLines.length; + + // Add the new lines + const newLines: BufferLine[] = []; + for (let i = 0; i < linesToAdd; i++) { + // TODO: Remove any! + const newLine = this.getBlankLine((this._terminal as any).eraseAttr(), true) as BufferLine; + newLines.push(newLine); + } + this.lines.splice(y + wrappedLines.length, 0, ...newLines); + wrappedLines.push(...newLines); + + // Copy buffer data to new locations, this needs to happen backwards to do in-place + let destLineIndex = Math.floor(cellsNeeded / newCols); + let destCol = cellsNeeded % newCols; + if (destCol === 0) { + destLineIndex--; + destCol = newCols; + } + let srcLineIndex = wrappedLines.length - linesToAdd - 1; + let srcCol = lastLineLength; + while (srcLineIndex >= 0) { // Don't need to copy any from the first line + const cellsToCopy = Math.min(srcCol, destCol); + wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[srcLineIndex], srcCol - cellsToCopy, destCol - cellsToCopy, cellsToCopy, true); + destCol -= cellsToCopy; + if (destCol === 0) { + destLineIndex--; + destCol = newCols; + } + srcCol -= cellsToCopy; + if (srcCol === 0) { + srcLineIndex--; + srcCol = this._terminal.cols; + } + } + + // Adjust viewport as needed + let viewportAdjustments = linesToAdd; + while (viewportAdjustments-- > 0) { + if (this.ybase === 0) { + if (this.y < this._terminal.rows) { + this.y++; + this.lines.pop(); + } else { + this.ybase++; + this.ydisp++; + } + } else { + if (this.ybase === this.ydisp) { + this.ybase++; + this.ydisp++; + } + } + } + + // TODO: Adjust viewport if needed (remove rows on end if ybase === 0? etc. + // TODO: Handle list trimming + + return wrappedLines.length - 1; + } + /** * Translates a string index back to a BufferIndex. * To get the correct buffer position the string must start at `startCol` 0 @@ -339,10 +443,9 @@ export class Buffer implements IBuffer { } lineIndex++; } - return [lineIndex, 0]; } - /** + /** // TODO: Handle list trimming * Translates a buffer line to a string, with optional start and end columns. * Wide characters will count as two columns in the resulting string. This * function is useful for getting the actual text underneath the raw selection diff --git a/src/BufferLine.ts b/src/BufferLine.ts index aafc9051..ea44de87 100644 --- a/src/BufferLine.ts +++ b/src/BufferLine.ts @@ -304,12 +304,19 @@ export class BufferLine implements IBufferLine { return 0; } - public copyCellsFrom(src: BufferLine, srcCol: number, destCol: number, length: number): void { - console.log(' copyCellsFrom', srcCol, destCol, length); + public copyCellsFrom(src: BufferLine, srcCol: number, destCol: number, length: number, applyInReverse: boolean): void { const srcData = src._data; - for (let cell = 0; cell < length; cell++) { - for (let i = 0; i < CELL_SIZE; i++) { - this._data[(destCol + cell) * CELL_SIZE + i] = srcData[(srcCol + cell) * CELL_SIZE + i]; + if (applyInReverse) { + for (let cell = length - 1; cell >= 0; cell--) { + for (let i = 0; i < CELL_SIZE; i++) { + this._data[(destCol + cell) * CELL_SIZE + i] = srcData[(srcCol + cell) * CELL_SIZE + i]; + } + } + } else { + for (let cell = 0; cell < length; cell++) { + for (let i = 0; i < CELL_SIZE; i++) { + this._data[(destCol + cell) * CELL_SIZE + i] = srcData[(srcCol + cell) * CELL_SIZE + i]; + } } } }