From 37153018b95714ff1ad793fa90e77d08297f1b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 24 Jul 2023 16:06:19 +0200 Subject: [PATCH] fix BCE --- demo/client.ts | 18 +++++- demo/index.html | 1 + src/browser/renderer/dom/DomRenderer.ts | 3 +- .../dom/DomRendererRowFactory.test.ts | 57 ++++++++----------- .../renderer/dom/DomRendererRowFactory.ts | 45 +++++++-------- src/common/Types.d.ts | 1 + src/common/buffer/BufferLine.ts | 11 +++- 7 files changed, 76 insertions(+), 60 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index 11837c5cd..9df171303 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -228,6 +228,7 @@ if (document.location.pathname === '/test') { document.getElementById('add-decoration').addEventListener('click', addDecoration); document.getElementById('add-overview-ruler').addEventListener('click', addOverviewRuler); document.getElementById('weblinks-test').addEventListener('click', testWeblinks); + document.getElementById('bce').addEventListener('click', coloredErase); addVtButtons(); } @@ -1186,6 +1187,21 @@ ipv6 https://[::1]/with/some?vars=and&a#hash aaa stop at final '.': This is a sentence with an url to http://example.com. stop at final '?': Is this the right url http://example.com/? stop at final '?': Maybe this one http://example.com/with?arguments=false? - `; +`; term.write(linkExamples.split('\n').join('\r\n')); } + + +function coloredErase(): void { + const data = ` +Test BG-colored Erase (BCE): + The color block in the following lines should look identical. + For newly created rows at the bottom the last color should be applied + for all cells to the right. + + def 41 42 43 44 45 46 47\x1b[47m +\x1b[m \x1b[41m \x1b[42m \x1b[43m \x1b[44m \x1b[45m \x1b[46m \x1b[47m +\x1b[m\x1b[5X\x1b[41m\x1b[5C\x1b[5X\x1b[42m\x1b[5C\x1b[5X\x1b[43m\x1b[5C\x1b[5X\x1b[44m\x1b[5C\x1b[5X\x1b[45m\x1b[5C\x1b[5X\x1b[46m\x1b[5C\x1b[5X\x1b[47m\x1b[5C\x1b[5X\x1b[m +`; +term.write(data.split('\n').join('\r\n')); +} diff --git a/demo/index.html b/demo/index.html index 42f8462ef..526fe5bc6 100644 --- a/demo/index.html +++ b/demo/index.html @@ -83,6 +83,7 @@
+
Decorations
diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index c744251c3..08a545289 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -444,7 +444,6 @@ export class DomRenderer extends Disposable implements IRenderer { cursorX, cursorBlink, this.dimensions.css.cell.width, - this._bufferService.cols, this._fontMetrics, this._linkState ) @@ -476,6 +475,7 @@ export class DomRenderer extends Disposable implements IRenderer { const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1); const cursorBlink = this._optionsService.rawOptions.cursorBlink; const cursorStyle = this._optionsService.rawOptions.cursorStyle; + cols = Math.min(cols, this._bufferService.cols); // refresh rows within link range this._linkState[0] = +enabled; @@ -499,7 +499,6 @@ export class DomRenderer extends Disposable implements IRenderer { cursorX, cursorBlink, this.dimensions.css.cell.width, - this._bufferService.cols, this._fontMetrics, this._linkState ) diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index 0692a5bae..ef83722e2 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -39,7 +39,7 @@ describe('DomRendererRowFactory', () => { describe('createRow', () => { it('should not create anything for an empty row', () => { - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), '' ); @@ -49,7 +49,7 @@ describe('DomRendererRowFactory', () => { lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, '語', 2, '語'.charCodeAt(0)])); // There should be no element for the following "empty" cell lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, '', 0, 0])); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), '語' ); @@ -57,7 +57,7 @@ describe('DomRendererRowFactory', () => { it('should add class for cursor and cursor style', () => { for (const style of ['block', 'bar', 'underline']) { - const fragment = rowFactory.createRow(lineData, 0, true, style, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, true, style, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), ` ` ); @@ -65,27 +65,18 @@ describe('DomRendererRowFactory', () => { }); it('should add class for cursor blink', () => { - const fragment = rowFactory.createRow(lineData, 0, true, 'block', 0, true, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, true, 'block', 0, true, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), ` ` ); }); - it('should not render cells that go beyond the terminal\'s columns', () => { - lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); - lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)])); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 1, EMPTY_METRICS, EMPTY_LINKSTATE); - assert.equal(getFragmentHtml(fragment), - 'a' - ); - }); - describe('attributes', () => { it('should add class for bold', () => { const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]); cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.BOLD; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -95,7 +86,7 @@ describe('DomRendererRowFactory', () => { const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]); cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.ITALIC; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -105,7 +96,7 @@ describe('DomRendererRowFactory', () => { const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]); cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.DIM; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -118,7 +109,7 @@ describe('DomRendererRowFactory', () => { cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED; cell.extended.underlineStyle = UnderlineStyle.SINGLE; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -129,7 +120,7 @@ describe('DomRendererRowFactory', () => { cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED; cell.extended.underlineStyle = UnderlineStyle.DOUBLE; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -140,7 +131,7 @@ describe('DomRendererRowFactory', () => { cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED; cell.extended.underlineStyle = UnderlineStyle.CURLY; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -151,7 +142,7 @@ describe('DomRendererRowFactory', () => { cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED; cell.extended.underlineStyle = UnderlineStyle.DOTTED; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -162,7 +153,7 @@ describe('DomRendererRowFactory', () => { cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED; cell.extended.underlineStyle = UnderlineStyle.DASHED; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -173,7 +164,7 @@ describe('DomRendererRowFactory', () => { const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]); cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.OVERLINE; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -183,7 +174,7 @@ describe('DomRendererRowFactory', () => { const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]); cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.STRIKETHROUGH; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -196,7 +187,7 @@ describe('DomRendererRowFactory', () => { cell.fg &= ~Attributes.PCOLOR_MASK; cell.fg |= i; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), `a` ); @@ -210,7 +201,7 @@ describe('DomRendererRowFactory', () => { cell.bg &= ~Attributes.PCOLOR_MASK; cell.bg |= i; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), `a` ); @@ -222,7 +213,7 @@ describe('DomRendererRowFactory', () => { cell.fg |= Attributes.CM_P16 | 2 | FgFlags.INVERSE; cell.bg |= Attributes.CM_P16 | 1; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -233,7 +224,7 @@ describe('DomRendererRowFactory', () => { cell.fg |= FgFlags.INVERSE; cell.bg |= Attributes.CM_P16 | 1; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -243,7 +234,7 @@ describe('DomRendererRowFactory', () => { const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]); cell.fg |= Attributes.CM_P16 | 1 | FgFlags.INVERSE; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -256,7 +247,7 @@ describe('DomRendererRowFactory', () => { cell.fg &= ~Attributes.PCOLOR_MASK; cell.fg |= i; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), `a` ); @@ -268,7 +259,7 @@ describe('DomRendererRowFactory', () => { cell.fg |= Attributes.CM_RGB | 1 << 16 | 2 << 8 | 3; cell.bg |= Attributes.CM_RGB | 4 << 16 | 5 << 8 | 6; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -279,7 +270,7 @@ describe('DomRendererRowFactory', () => { cell.fg |= Attributes.CM_RGB | 1 << 16 | 2 << 8 | 3 | FgFlags.INVERSE; cell.bg |= Attributes.CM_RGB | 4 << 16 | 5 << 8 | 6; lineData.setCell(0, cell); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'a' ); @@ -291,7 +282,7 @@ describe('DomRendererRowFactory', () => { lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)])); rowFactory.handleSelectionChanged([1, 0], [2, 0], false); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), 'ab' ); @@ -299,7 +290,7 @@ describe('DomRendererRowFactory', () => { it('should force whitespace cells to be rendered above the background', () => { lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); rowFactory.handleSelectionChanged([0, 0], [2, 0], false); - const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20, EMPTY_METRICS, EMPTY_LINKSTATE); + const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_METRICS, EMPTY_LINKSTATE); assert.equal(getFragmentHtml(fragment), ' a' ); diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index 639a44337..4bb974a20 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -5,7 +5,7 @@ import { IBufferLine, ICellData, IColor } from 'common/Types'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants'; -import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Attributes } from 'common/buffer/Constants'; +import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Attributes, NULL_CELL_WIDTH } from 'common/buffer/Constants'; import { CellData } from 'common/buffer/CellData'; import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; import { color, rgba } from 'common/Color'; @@ -57,28 +57,19 @@ export class DomRendererRowFactory { cursorX: number, cursorBlink: boolean, cellWidth: number, - cols: number, metrics: Uint8Array, linkState: Uint8Array ): DocumentFragment { + const fragment = this._document.createDocumentFragment(); - const joinedRanges = this._characterJoinerService.getJoinedCharacters(row); - // Find the line length first, this prevents the need to output a bunch of - // empty cells at the end. This cannot easily be integrated into the main - // loop below because of the colCount feature (which can be removed after we - // properly support reflow and disallow data to go beyond the right-side of - // the viewport). - let lineLength = 0; - for (let x = Math.min(lineData.length, cols) - 1; x >= 0; x--) { - if (lineData.loadCell(x, this._workCell).getCode() !== NULL_CELL_CODE || (isCursorRow && x === cursorX)) { - lineLength = x + 1; - break; - } - } - const colors = this._themeService.colors; + let lineLength = lineData.getNoBgTrimmedLength(); + if (isCursorRow && lineLength < cursorX + 1) { + lineLength = cursorX + 1; + } + let charElement: HTMLSpanElement | undefined; let cellAmount = 0; let text = ''; @@ -89,8 +80,7 @@ export class DomRendererRowFactory { const isHover = linkState[0]; - let x = 0; - for (; x < lineLength; x++) { + for (let x = 0; x < lineLength; x++) { lineData.loadCell(x, this._workCell); let width = this._workCell.getWidth(); @@ -129,6 +119,7 @@ export class DomRendererRowFactory { const isInSelection = this._isCellInSelection(x, row); const isCursorCell = isCursorRow && x === cursorX; const cc = cell.getCode(); + const isNull = cc === NULL_CELL_CODE && width === NULL_CELL_WIDTH; const isCombined = cell.isCombined(); const isLinkHover = isHover && x >= linkState[1] && x <= linkState[2]; @@ -145,9 +136,9 @@ export class DomRendererRowFactory { * - underline from hover state did not change */ if ( - cellAmount && width === 1 && !isCombined + cellAmount + && (isNull || (width === 1 && !isCombined && cc < 1424 && !metrics[cc])) && cell.bg === oldBg && cell.fg === oldFg && cell.extended.ext === oldExt - && cc < 1424 && !metrics[cc] && !isInSelection && !isCursorCell && isLinkHover === oldLinkHover @@ -166,7 +157,9 @@ export class DomRendererRowFactory { } else { if (cellAmount) { charElement.textContent = text; - charElement.style.width = `${cellWidth * cellAmount}px`; + if (cellAmount > 1) { + charElement.style.width = `${cellWidth * cellAmount}px`; + } } charElement = this._document.createElement('span'); cellAmount = 0; @@ -385,7 +378,7 @@ export class DomRendererRowFactory { // account first char for later merge if it meets the start conditions - if (width === 1 && !isCombined && cc < 1424 && !metrics[cc] && !isInSelection && !isCursorCell) { + if ((isNull || (width === 1 && !isCombined && cc < 1424 && !metrics[cc])) && !isInSelection && !isCursorCell) { cellAmount++; } else { // every non-mergeable char gets directly written to its own span @@ -399,7 +392,13 @@ export class DomRendererRowFactory { // postfix width and text of last merged span if (charElement && cellAmount) { charElement.textContent = text; - charElement.style.width = `${cellWidth * cellAmount}px`; + /* + * optimization: if the last merged span has no BG color set, use faster "width: auto", + * else use correct px value for aligned BG coloring and BCE + */ + if (cellAmount > 1) { + charElement.style.width = (oldBg & Attributes.CM_MASK) ? `${cellWidth * cellAmount}px`: 'auto'; + } } return fragment; diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index 70143525f..056353d8e 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -242,6 +242,7 @@ export interface IBufferLine { copyFrom(line: IBufferLine): void; clone(): IBufferLine; getTrimmedLength(): number; + getNoBgTrimmedLength(): number; translateToString(trimRight?: boolean, startCol?: number, endCol?: number): string; /* direct access to cell attrs */ diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index d5f438445..c3d127cdc 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -5,7 +5,7 @@ import { CharData, IBufferLine, ICellData, IAttributeData, IExtendedAttrs } from 'common/Types'; import { stringFromCodePoint } from 'common/input/TextDecoder'; -import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content, BgFlags, FgFlags } from 'common/buffer/Constants'; +import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content, BgFlags, FgFlags, Attributes } from 'common/buffer/Constants'; import { CellData } from 'common/buffer/CellData'; import { AttributeData, ExtendedAttrs } from 'common/buffer/AttributeData'; @@ -463,6 +463,15 @@ export class BufferLine implements IBufferLine { return 0; } + public getNoBgTrimmedLength(): number { + for (let i = this.length - 1; i >= 0; --i) { + if ((this._data[i * CELL_SIZE + Cell.CONTENT] & Content.HAS_CONTENT_MASK) || (this._data[i * CELL_SIZE + Cell.BG] & Attributes.CM_MASK)) { + return i + (this._data[i * CELL_SIZE + Cell.CONTENT] >> Content.WIDTH_SHIFT); + } + } + return 0; + } + public copyCellsFrom(src: BufferLine, srcCol: number, destCol: number, length: number, applyInReverse: boolean): void { const srcData = src._data; if (applyInReverse) {