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 @@
SGR test
Ansi colors test
Ansi hyperlinks test
+ Colored Erase (BCE)
Decorations
Decoration
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),
'a b '
);
@@ -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) {