- common/tsconfig.json is a solution root referencing layer tsconfigs - Each layer has composite tsconfig with correct references and outDir - Break buffer/services cycle via buffer-local service interfaces - Move cell types, keyboard/unicode types, TaskQueue, and TerminalOptions - Exclude *.test.ts from composite builds; fix parser IDisposable imports Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
911 lines
41 KiB
TypeScript
911 lines
41 KiB
TypeScript
/**
|
||
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||
* @license MIT
|
||
*/
|
||
import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR, Content, UnderlineStyle, BgFlags, Attributes, FgFlags } from 'common/buffer/Constants';
|
||
import { BufferLine } from 'common/buffer/BufferLine';
|
||
import { BufferLineStringCache } from 'common/buffer/BufferLineStringCache';
|
||
import { CellData } from 'common/buffer/CellData';
|
||
import { CharData, IBufferLine, ICellData } from 'common/buffer/CellTypes';
|
||
import { assert } from 'chai';
|
||
import { AttributeData } from 'common/buffer/AttributeData';
|
||
import { createCellData, NULL_CELL_DATA, extendedAttributes } from 'common/terminal/TestUtils.test';
|
||
|
||
const TEST_STRING_CACHE = new BufferLineStringCache();
|
||
|
||
|
||
class TestBufferLine extends BufferLine {
|
||
constructor(cols: number, fillCellData?: ICellData, isWrapped: boolean = false) {
|
||
super(TEST_STRING_CACHE, cols, fillCellData, isWrapped);
|
||
}
|
||
|
||
public get combined(): {[index: number]: string} {
|
||
return this._combined;
|
||
}
|
||
|
||
public get cachedString(): string | undefined {
|
||
return this._getStringCacheEntry(false)?.value;
|
||
}
|
||
|
||
public set cachedString(value: string | undefined) {
|
||
this._getStringCacheEntry(true)!.value = value;
|
||
}
|
||
|
||
public get isCachedStringTrimmed(): boolean {
|
||
return this._getStringCacheEntry(false)?.isTrimmed ?? false;
|
||
}
|
||
|
||
public set isCachedStringTrimmed(value: boolean) {
|
||
this._getStringCacheEntry(true)!.isTrimmed = value;
|
||
}
|
||
|
||
public toArray(): CharData[] {
|
||
const result = [];
|
||
for (let i = 0; i < this.length; ++i) {
|
||
result.push(this.loadCell(i, new CellData()).getAsCharData());
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
|
||
describe('AttributeData', () => {
|
||
describe('extended attributes', () => {
|
||
it('hasExtendedAttrs', () => {
|
||
const attrs = new AttributeData();
|
||
assert.equal(!!attrs.hasExtendedAttrs(), false);
|
||
attrs.bg |= BgFlags.HAS_EXTENDED;
|
||
assert.equal(!!attrs.hasExtendedAttrs(), true);
|
||
});
|
||
it('getUnderlineColor - P256', () => {
|
||
const attrs = new AttributeData();
|
||
// set a P256 color
|
||
attrs.extended.underlineColor = Attributes.CM_P256 | 45;
|
||
|
||
// should use FG color if BgFlags.HAS_EXTENDED is not set
|
||
assert.equal(attrs.getUnderlineColor(), -1);
|
||
|
||
// should use underlineColor if BgFlags.HAS_EXTENDED is set and underlineColor holds a value
|
||
attrs.bg |= BgFlags.HAS_EXTENDED;
|
||
assert.equal(attrs.getUnderlineColor(), 45);
|
||
|
||
// should use FG color if underlineColor holds no value
|
||
attrs.extended.underlineColor = 0;
|
||
attrs.fg |= Attributes.CM_P256 | 123;
|
||
assert.equal(attrs.getUnderlineColor(), 123);
|
||
});
|
||
it('getUnderlineColor - RGB', () => {
|
||
const attrs = new AttributeData();
|
||
// set a P256 color
|
||
attrs.extended.underlineColor = Attributes.CM_RGB | (1 << 16) | (2 << 8) | 3;
|
||
|
||
// should use FG color if BgFlags.HAS_EXTENDED is not set
|
||
assert.equal(attrs.getUnderlineColor(), -1);
|
||
|
||
// should use underlineColor if BgFlags.HAS_EXTENDED is set and underlineColor holds a value
|
||
attrs.bg |= BgFlags.HAS_EXTENDED;
|
||
assert.equal(attrs.getUnderlineColor(), (1 << 16) | (2 << 8) | 3);
|
||
|
||
// should use FG color if underlineColor holds no value
|
||
attrs.extended.underlineColor = 0;
|
||
attrs.fg |= Attributes.CM_P256 | 123;
|
||
assert.equal(attrs.getUnderlineColor(), 123);
|
||
});
|
||
it('getUnderlineColorMode / isUnderlineColorRGB / isUnderlineColorPalette / isUnderlineColorDefault', () => {
|
||
const attrs = new AttributeData();
|
||
|
||
// should always return color mode of fg
|
||
for (const mode of [Attributes.CM_DEFAULT, Attributes.CM_P16, Attributes.CM_P256, Attributes.CM_RGB]) {
|
||
attrs.extended.underlineColor = mode;
|
||
assert.equal(attrs.getUnderlineColorMode(), attrs.getFgColorMode());
|
||
assert.equal(attrs.isUnderlineColorDefault(), true);
|
||
}
|
||
attrs.fg = Attributes.CM_RGB;
|
||
for (const mode of [Attributes.CM_DEFAULT, Attributes.CM_P16, Attributes.CM_P256, Attributes.CM_RGB]) {
|
||
attrs.extended.underlineColor = mode;
|
||
assert.equal(attrs.getUnderlineColorMode(), attrs.getFgColorMode());
|
||
assert.equal(attrs.isUnderlineColorDefault(), false);
|
||
assert.equal(attrs.isUnderlineColorRGB(), true);
|
||
}
|
||
|
||
// should return own mode
|
||
attrs.bg |= BgFlags.HAS_EXTENDED;
|
||
attrs.extended.underlineColor = Attributes.CM_DEFAULT;
|
||
assert.equal(attrs.getUnderlineColorMode(), Attributes.CM_DEFAULT);
|
||
attrs.extended.underlineColor = Attributes.CM_P16;
|
||
assert.equal(attrs.getUnderlineColorMode(), Attributes.CM_P16);
|
||
assert.equal(attrs.isUnderlineColorPalette(), true);
|
||
attrs.extended.underlineColor = Attributes.CM_P256;
|
||
assert.equal(attrs.getUnderlineColorMode(), Attributes.CM_P256);
|
||
assert.equal(attrs.isUnderlineColorPalette(), true);
|
||
attrs.extended.underlineColor = Attributes.CM_RGB;
|
||
assert.equal(attrs.getUnderlineColorMode(), Attributes.CM_RGB);
|
||
assert.equal(attrs.isUnderlineColorRGB(), true);
|
||
});
|
||
it('getUnderlineStyle', () => {
|
||
const attrs = new AttributeData();
|
||
|
||
// defaults to no underline style
|
||
assert.equal(attrs.getUnderlineStyle(), UnderlineStyle.NONE);
|
||
|
||
// should return NONE if UNDERLINE is not set
|
||
attrs.extended.underlineStyle = UnderlineStyle.CURLY;
|
||
assert.equal(attrs.getUnderlineStyle(), UnderlineStyle.NONE);
|
||
|
||
// should return SINGLE style if UNDERLINE is set and HAS_EXTENDED is false
|
||
attrs.fg |= FgFlags.UNDERLINE;
|
||
assert.equal(attrs.getUnderlineStyle(), UnderlineStyle.SINGLE);
|
||
|
||
// should return correct style if both is set
|
||
attrs.bg |= BgFlags.HAS_EXTENDED;
|
||
assert.equal(attrs.getUnderlineStyle(), UnderlineStyle.CURLY);
|
||
|
||
// should return NONE if UNDERLINE is not set, but HAS_EXTENDED is true
|
||
attrs.fg &= ~FgFlags.UNDERLINE;
|
||
assert.equal(attrs.getUnderlineStyle(), UnderlineStyle.NONE);
|
||
});
|
||
it('getUnderlineVariantOffset', () => {
|
||
const attrs = new AttributeData();
|
||
|
||
// defaults to no offset
|
||
assert.equal(attrs.getUnderlineVariantOffset(), 0);
|
||
|
||
// should return 0 - 7
|
||
for (let i = 0; i < 8; ++i) {
|
||
attrs.extended.underlineVariantOffset = i;
|
||
assert.equal(attrs.getUnderlineVariantOffset(), i);
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('CellData', () => {
|
||
it('CharData <--> CellData equality', () => {
|
||
const cell = new CellData();
|
||
// ASCII
|
||
cell.setFromCharData([123, 'a', 1, 'a'.charCodeAt(0)]);
|
||
assert.deepEqual(cell.getAsCharData(), [123, 'a', 1, 'a'.charCodeAt(0)]);
|
||
assert.equal(cell.isCombined(), 0);
|
||
// combining
|
||
cell.setFromCharData([123, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
|
||
assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
|
||
assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||
// surrogate
|
||
cell.setFromCharData([123, '𝄞', 1, 0x1D11E]);
|
||
assert.deepEqual(cell.getAsCharData(), [123, '𝄞', 1, 0x1D11E]);
|
||
assert.equal(cell.isCombined(), 0);
|
||
// surrogate + combining
|
||
cell.setFromCharData([123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]);
|
||
assert.deepEqual(cell.getAsCharData(), [123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]);
|
||
assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||
// wide char
|
||
cell.setFromCharData([123, '1', 2, '1'.charCodeAt(0)]);
|
||
assert.deepEqual(cell.getAsCharData(), [123, '1', 2, '1'.charCodeAt(0)]);
|
||
assert.equal(cell.isCombined(), 0);
|
||
});
|
||
});
|
||
|
||
describe('BufferLine', function(): void {
|
||
it('ctor', function(): void {
|
||
let line: IBufferLine = new TestBufferLine(0);
|
||
assert.equal(line.length, 0);
|
||
assert.equal(line.isWrapped, false);
|
||
line = new TestBufferLine(10);
|
||
assert.equal(line.length, 10);
|
||
assert.deepEqual(line.loadCell(0, new CellData()).getAsCharData(), [0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||
assert.equal(line.isWrapped, false);
|
||
line = new TestBufferLine(10, undefined, true);
|
||
assert.equal(line.length, 10);
|
||
assert.deepEqual(line.loadCell(0, new CellData()).getAsCharData(), [0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||
assert.equal(line.isWrapped, true);
|
||
line = new TestBufferLine(10, createCellData(123, 'a', 456), true);
|
||
assert.equal(line.length, 10);
|
||
assert.deepEqual(line.loadCell(0, new CellData()).getAsCharData(), [123, 'a', 456, 'a'.charCodeAt(0)]);
|
||
assert.equal(line.isWrapped, true);
|
||
});
|
||
it('insertCells', function(): void {
|
||
const line = new TestBufferLine(3);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(1, createCellData(2, 'b', 1));
|
||
line.setCell(2, createCellData(3, 'c', 1));
|
||
line.insertCells(1, 3, createCellData(4, 'd', 1));
|
||
assert.deepEqual(line.toArray(), [
|
||
[1, 'a', 1, 'a'.charCodeAt(0)],
|
||
[4, 'd', 1, 'd'.charCodeAt(0)],
|
||
[4, 'd', 1, 'd'.charCodeAt(0)]
|
||
]);
|
||
});
|
||
it('deleteCells', function(): void {
|
||
const line = new TestBufferLine(5);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(1, createCellData(2, 'b', 1));
|
||
line.setCell(2, createCellData(3, 'c', 1));
|
||
line.setCell(3, createCellData(4, 'd', 1));
|
||
line.setCell(4, createCellData(5, 'e', 1));
|
||
line.deleteCells(1, 2, createCellData(6, 'f', 1));
|
||
assert.deepEqual(line.toArray(), [
|
||
[1, 'a', 1, 'a'.charCodeAt(0)],
|
||
[4, 'd', 1, 'd'.charCodeAt(0)],
|
||
[5, 'e', 1, 'e'.charCodeAt(0)],
|
||
[6, 'f', 1, 'f'.charCodeAt(0)],
|
||
[6, 'f', 1, 'f'.charCodeAt(0)]
|
||
]);
|
||
});
|
||
it('replaceCells', function(): void {
|
||
const line = new TestBufferLine(5);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(1, createCellData(2, 'b', 1));
|
||
line.setCell(2, createCellData(3, 'c', 1));
|
||
line.setCell(3, createCellData(4, 'd', 1));
|
||
line.setCell(4, createCellData(5, 'e', 1));
|
||
line.replaceCells(2, 4, createCellData(6, 'f', 1));
|
||
assert.deepEqual(line.toArray(), [
|
||
[1, 'a', 1, 'a'.charCodeAt(0)],
|
||
[2, 'b', 1, 'b'.charCodeAt(0)],
|
||
[6, 'f', 1, 'f'.charCodeAt(0)],
|
||
[6, 'f', 1, 'f'.charCodeAt(0)],
|
||
[5, 'e', 1, 'e'.charCodeAt(0)]
|
||
]);
|
||
});
|
||
it('fill', function(): void {
|
||
const line = new TestBufferLine(5);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(1, createCellData(2, 'b', 1));
|
||
line.setCell(2, createCellData(3, 'c', 1));
|
||
line.setCell(3, createCellData(4, 'd', 1));
|
||
line.setCell(4, createCellData(5, 'e', 1));
|
||
line.fill(createCellData(123, 'z', 1));
|
||
assert.deepEqual(line.toArray(), [
|
||
[123, 'z', 1, 'z'.charCodeAt(0)],
|
||
[123, 'z', 1, 'z'.charCodeAt(0)],
|
||
[123, 'z', 1, 'z'.charCodeAt(0)],
|
||
[123, 'z', 1, 'z'.charCodeAt(0)],
|
||
[123, 'z', 1, 'z'.charCodeAt(0)]
|
||
]);
|
||
});
|
||
it('clone', function(): void {
|
||
const line = new TestBufferLine(5, undefined, true);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(1, createCellData(2, 'b', 1));
|
||
line.setCell(2, createCellData(3, 'c', 1));
|
||
line.setCell(3, createCellData(4, 'd', 1));
|
||
line.setCell(4, createCellData(5, 'e', 1));
|
||
const line2 = line.clone();
|
||
assert.deepEqual(TestBufferLine.prototype.toArray.apply(line2), line.toArray());
|
||
assert.equal(line2.length, line.length);
|
||
assert.equal(line2.isWrapped, line.isWrapped);
|
||
});
|
||
it('copyFrom', function(): void {
|
||
const line = new TestBufferLine(5);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(1, createCellData(2, 'b', 1));
|
||
line.setCell(2, createCellData(3, 'c', 1));
|
||
line.setCell(3, createCellData(4, 'd', 1));
|
||
line.setCell(4, createCellData(5, 'e', 1));
|
||
const line2 = new TestBufferLine(5, createCellData(1, 'a', 1), true);
|
||
line2.copyFrom(line);
|
||
assert.deepEqual(line2.toArray(), line.toArray());
|
||
assert.equal(line2.length, line.length);
|
||
assert.equal(line2.isWrapped, line.isWrapped);
|
||
});
|
||
it('should support combining chars', function(): void {
|
||
// CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print
|
||
// --> set code to the last charCodeAt value of the string
|
||
// Note: needs to be fixed once the string pointer is in place
|
||
const line = new TestBufferLine(2, createCellData(1, 'e\u0301', 1));
|
||
assert.deepEqual(line.toArray(), [[1, 'e\u0301', 1, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]]);
|
||
const line2 = new TestBufferLine(5, createCellData(1, 'a', 1), true);
|
||
line2.copyFrom(line);
|
||
assert.deepEqual(line2.toArray(), line.toArray());
|
||
const line3 = line.clone();
|
||
assert.deepEqual(TestBufferLine.prototype.toArray.apply(line3), line.toArray());
|
||
});
|
||
describe('resize', function(): void {
|
||
it('enlarge(false)', function(): void {
|
||
const line = new TestBufferLine(5, createCellData(1, 'a', 1), false);
|
||
line.resize(10, createCellData(1, 'a', 1));
|
||
assert.deepEqual(line.toArray(), (Array(10) as any).fill([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||
});
|
||
it('enlarge(true)', function(): void {
|
||
const line = new TestBufferLine(5, createCellData(1, 'a', 1), false);
|
||
line.resize(10, createCellData(1, 'a', 1));
|
||
assert.deepEqual(line.toArray(), (Array(10) as any).fill([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||
});
|
||
it('shrink(true) - should apply new size', function(): void {
|
||
const line = new TestBufferLine(10, createCellData(1, 'a', 1), false);
|
||
line.resize(5, createCellData(1, 'a', 1));
|
||
assert.deepEqual(line.toArray(), (Array(5) as any).fill([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||
});
|
||
it('shrink to 0 length', function(): void {
|
||
const line = new TestBufferLine(10, createCellData(1, 'a', 1), false);
|
||
line.resize(0, createCellData(1, 'a', 1));
|
||
assert.deepEqual(line.toArray(), (Array(0) as any).fill([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||
});
|
||
it('should remove combining data on replaced cells after shrinking then enlarging', () => {
|
||
const line = new TestBufferLine(10, createCellData(1, 'a', 1), false);
|
||
line.set(2, [ 0, '😁', 1, '😁'.charCodeAt(0) ]);
|
||
line.set(9, [ 0, '😁', 1, '😁'.charCodeAt(0) ]);
|
||
assert.equal(line.translateToString(), 'aa😁aaaaaa😁');
|
||
assert.equal(Object.keys(line.combined).length, 2);
|
||
line.resize(5, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), 'aa😁aa');
|
||
line.resize(10, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), 'aa😁aaaaaaa');
|
||
assert.equal(Object.keys(line.combined).length, 1);
|
||
});
|
||
});
|
||
describe('getTrimLength', function(): void {
|
||
it('empty line', function(): void {
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
assert.equal(line.getTrimmedLength(), 0);
|
||
});
|
||
it('ASCII', function(): void {
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(2, createCellData(1, 'a', 1));
|
||
assert.equal(line.getTrimmedLength(), 3);
|
||
});
|
||
it('surrogate', function(): void {
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(2, createCellData(1, '𝄞', 1));
|
||
assert.equal(line.getTrimmedLength(), 3);
|
||
});
|
||
it('combining', function(): void {
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(2, createCellData(1, 'e\u0301', 1));
|
||
assert.equal(line.getTrimmedLength(), 3);
|
||
});
|
||
it('fullwidth', function(): void {
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(2, createCellData(1, '1', 2));
|
||
line.setCell(3, createCellData(0, '', 0));
|
||
assert.equal(line.getTrimmedLength(), 4); // also counts null cell after fullwidth
|
||
});
|
||
});
|
||
describe('translateToString with and w\'o trimming', function(): void {
|
||
it('empty line', function(): void {
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
const columns: number[] = [];
|
||
assert.equal(line.translateToString(false, undefined, undefined, columns), ' ');
|
||
assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||
assert.equal(line.translateToString(true, undefined, undefined, columns), '');
|
||
assert.deepEqual(columns, [0]);
|
||
});
|
||
it('ASCII', function(): void {
|
||
const columns: number[] = [];
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(2, createCellData(1, 'a', 1));
|
||
line.setCell(4, createCellData(1, 'a', 1));
|
||
line.setCell(5, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(false, undefined, undefined, columns), 'a a aa ');
|
||
assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||
assert.equal(line.translateToString(true, undefined, undefined, columns), 'a a aa');
|
||
assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6]);
|
||
for (const trimRight of [true, false]) {
|
||
assert.equal(line.translateToString(trimRight, 0, 5, columns), 'a a a');
|
||
assert.deepEqual(columns, [0, 1, 2, 3, 4, 5]);
|
||
assert.equal(line.translateToString(trimRight, 0, 4, columns), 'a a ');
|
||
assert.deepEqual(columns, [0, 1, 2, 3, 4]);
|
||
assert.equal(line.translateToString(trimRight, 0, 3, columns), 'a a');
|
||
assert.deepEqual(columns, [0, 1, 2, 3]);
|
||
}
|
||
|
||
});
|
||
it('surrogate', function(): void {
|
||
const columns: number[] = [];
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(2, createCellData(1, '𝄞', 1));
|
||
line.setCell(4, createCellData(1, '𝄞', 1));
|
||
line.setCell(5, createCellData(1, '𝄞', 1));
|
||
assert.equal(line.translateToString(false, undefined, undefined, columns), 'a 𝄞 𝄞𝄞 ');
|
||
assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10]);
|
||
assert.equal(line.translateToString(true, undefined, undefined, columns), 'a 𝄞 𝄞𝄞');
|
||
assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5, 5, 6]);
|
||
for (const trimRight of [true, false]) {
|
||
assert.equal(line.translateToString(trimRight, 0, 5, columns), 'a 𝄞 𝄞');
|
||
assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5]);
|
||
assert.equal(line.translateToString(trimRight, 0, 4, columns), 'a 𝄞 ');
|
||
assert.deepEqual(columns, [0, 1, 2, 2, 3, 4]);
|
||
assert.equal(line.translateToString(trimRight, 0, 3, columns), 'a 𝄞');
|
||
assert.deepEqual(columns, [0, 1, 2, 2, 3]);
|
||
}
|
||
});
|
||
it('combining', function(): void {
|
||
const columns: number[] = [];
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(2, createCellData(1, 'e\u0301', 1));
|
||
line.setCell(4, createCellData(1, 'e\u0301', 1));
|
||
line.setCell(5, createCellData(1, 'e\u0301', 1));
|
||
assert.equal(line.translateToString(false, undefined, undefined, columns), 'a e\u0301 e\u0301e\u0301 ');
|
||
assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10]);
|
||
assert.equal(line.translateToString(true, undefined, undefined, columns), 'a e\u0301 e\u0301e\u0301');
|
||
assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5, 5, 6]);
|
||
for (const trimRight of [true, false]) {
|
||
assert.equal(line.translateToString(trimRight, 0, 5, columns), 'a e\u0301 e\u0301');
|
||
assert.deepEqual(columns, [0, 1, 2, 2, 3, 4, 4, 5]);
|
||
assert.equal(line.translateToString(trimRight, 0, 4, columns), 'a e\u0301 ');
|
||
assert.deepEqual(columns, [0, 1, 2, 2, 3, 4]);
|
||
assert.equal(line.translateToString(trimRight, 0, 3, columns), 'a e\u0301');
|
||
assert.deepEqual(columns, [0, 1, 2, 2, 3]);
|
||
}
|
||
});
|
||
it('fullwidth', function(): void {
|
||
const columns: number[] = [];
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(2, createCellData(1, '1', 2));
|
||
line.setCell(3, createCellData(0, '', 0));
|
||
line.setCell(5, createCellData(1, '1', 2));
|
||
line.setCell(6, createCellData(0, '', 0));
|
||
line.setCell(7, createCellData(1, '1', 2));
|
||
line.setCell(8, createCellData(0, '', 0));
|
||
assert.equal(line.translateToString(false, undefined, undefined, columns), 'a 1 11 ');
|
||
assert.deepEqual(columns, [0, 1, 2, 4, 5, 7, 9, 10]);
|
||
assert.equal(line.translateToString(true, undefined, undefined, columns), 'a 1 11');
|
||
assert.deepEqual(columns, [0, 1, 2, 4, 5, 7, 9]);
|
||
for (const trimRight of [true, false]) {
|
||
assert.equal(line.translateToString(trimRight, 0, 7, columns), 'a 1 1');
|
||
assert.deepEqual(columns, [0, 1, 2, 4, 5, 7]);
|
||
assert.equal(line.translateToString(trimRight, 0, 6, columns), 'a 1 1');
|
||
assert.deepEqual(columns, [0, 1, 2, 4, 5, 7]);
|
||
assert.equal(line.translateToString(trimRight, 0, 5, columns), 'a 1 ');
|
||
assert.deepEqual(columns, [0, 1, 2, 4, 5]);
|
||
assert.equal(line.translateToString(trimRight, 0, 4, columns), 'a 1');
|
||
assert.deepEqual(columns, [0, 1, 2, 4]);
|
||
assert.equal(line.translateToString(trimRight, 0, 3, columns), 'a 1');
|
||
assert.deepEqual(columns, [0, 1, 2, 4]);
|
||
assert.equal(line.translateToString(trimRight, 0, 2, columns), 'a ');
|
||
assert.deepEqual(columns, [0, 1, 2]);
|
||
}
|
||
});
|
||
it('space at end', function(): void {
|
||
const columns: number[] = [];
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(2, createCellData(1, 'a', 1));
|
||
line.setCell(4, createCellData(1, 'a', 1));
|
||
line.setCell(5, createCellData(1, 'a', 1));
|
||
line.setCell(6, createCellData(1, ' ', 1));
|
||
assert.equal(line.translateToString(false, undefined, undefined, columns), 'a a aa ');
|
||
assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||
assert.equal(line.translateToString(true, undefined, undefined, columns), 'a a aa ');
|
||
assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7]);
|
||
});
|
||
it('should always return some sane value', function(): void {
|
||
const columns: number[] = [];
|
||
// sanity check - broken line with invalid out of bound null width cells
|
||
// this can atm happen with deleting/inserting chars in inputhandler by "breaking"
|
||
// fullwidth pairs --> needs to be fixed after settling BufferLine impl
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
assert.equal(line.translateToString(false, undefined, undefined, columns), ' ');
|
||
assert.deepEqual(columns, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||
assert.equal(line.translateToString(true, undefined, undefined, columns), '');
|
||
assert.deepEqual(columns, [0]);
|
||
});
|
||
it('should work with endCol=0', () => {
|
||
const columns: number[] = [];
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(true, 0, 0, columns), '');
|
||
assert.deepEqual(columns, [0]);
|
||
});
|
||
});
|
||
describe('addCharToCell', () => {
|
||
it('should set width to 1 for empty cell', () => {
|
||
const line = new TestBufferLine(3, NULL_CELL_DATA, false);
|
||
line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0);
|
||
const cell = line.loadCell(0, new CellData());
|
||
// chars contains single combining char
|
||
// width is set to 1
|
||
assert.deepEqual(cell.getAsCharData(), [DEFAULT_ATTR, '\u0301', 1, 0x0301]);
|
||
// do not account a single combining char as combined
|
||
assert.equal(cell.isCombined(), 0);
|
||
});
|
||
it('should add char to combining string in cell', () => {
|
||
const line = new TestBufferLine(3, NULL_CELL_DATA, false);
|
||
const cell = line .loadCell(0, new CellData());
|
||
cell.setFromCharData([123, 'e\u0301', 1, 'e\u0301'.charCodeAt(1)]);
|
||
line.setCell(0, cell);
|
||
line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0);
|
||
line.loadCell(0, cell);
|
||
// chars contains 3 chars
|
||
// width is set to 1
|
||
assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301\u0301', 1, 0x0301]);
|
||
// do not account a single combining char as combined
|
||
assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||
});
|
||
it('should create combining string on taken cell', () => {
|
||
const line = new TestBufferLine(3, NULL_CELL_DATA, false);
|
||
const cell = line .loadCell(0, new CellData());
|
||
cell.setFromCharData([123, 'e', 1, 'e'.charCodeAt(1)]);
|
||
line.setCell(0, cell);
|
||
line.addCodepointToCell(0, '\u0301'.charCodeAt(0), 0);
|
||
line.loadCell(0, cell);
|
||
// chars contains 2 chars
|
||
// width is set to 1
|
||
assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, 0x0301]);
|
||
// do not account a single combining char as combined
|
||
assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||
});
|
||
});
|
||
describe('correct fullwidth handling', () => {
|
||
function populate(line: BufferLine): void {
|
||
const cell = createCellData(1, '¥', 2);
|
||
for (let i = 0; i < line.length; i += 2) {
|
||
line.setCell(i, cell);
|
||
}
|
||
}
|
||
it('insert - wide char at pos', () => {
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.insertCells(9, 1, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), '¥¥¥¥ a');
|
||
line.insertCells(8, 1, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), '¥¥¥¥a ');
|
||
line.insertCells(1, 1, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), ' a ¥¥¥a');
|
||
});
|
||
it('insert - wide char at end', () => {
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.insertCells(0, 3, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), 'aaa¥¥¥ ');
|
||
line.insertCells(4, 1, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), 'aaa a ¥¥');
|
||
line.insertCells(4, 1, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), 'aaa aa ¥ ');
|
||
});
|
||
it('delete', () => {
|
||
const line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.deleteCells(0, 1, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), ' ¥¥¥¥a');
|
||
line.deleteCells(5, 2, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), ' ¥¥¥aaa');
|
||
line.deleteCells(0, 2, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), ' ¥¥aaaaa');
|
||
});
|
||
it('replace - start at 0', () => {
|
||
let line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.replaceCells(0, 1, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), 'a ¥¥¥¥');
|
||
line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.replaceCells(0, 2, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), 'aa¥¥¥¥');
|
||
line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.replaceCells(0, 3, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), 'aaa ¥¥¥');
|
||
line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.replaceCells(0, 8, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), 'aaaaaaaa¥');
|
||
line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.replaceCells(0, 9, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), 'aaaaaaaaa ');
|
||
line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.replaceCells(0, 10, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), 'aaaaaaaaaa');
|
||
});
|
||
it('replace - start at 1', () => {
|
||
let line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.replaceCells(1, 2, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), ' a¥¥¥¥');
|
||
line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.replaceCells(1, 3, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), ' aa ¥¥¥');
|
||
line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.replaceCells(1, 4, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), ' aaa¥¥¥');
|
||
line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.replaceCells(1, 8, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), ' aaaaaaa¥');
|
||
line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.replaceCells(1, 9, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), ' aaaaaaaa ');
|
||
line = new TestBufferLine(10, NULL_CELL_DATA, false);
|
||
populate(line);
|
||
line.replaceCells(1, 10, createCellData(1, 'a', 1));
|
||
assert.equal(line.translateToString(), ' aaaaaaaaa');
|
||
});
|
||
});
|
||
describe('extended attributes', () => {
|
||
it('setCells', function(): void {
|
||
const line = new TestBufferLine(5);
|
||
const cell = createCellData(1, 'a', 1);
|
||
// no eAttrs
|
||
line.setCell(0, cell);
|
||
|
||
// some underline style
|
||
cell.extended.underlineStyle = UnderlineStyle.CURLY;
|
||
cell.bg |= BgFlags.HAS_EXTENDED;
|
||
line.setCell(1, cell);
|
||
|
||
// same eAttr, different codepoint
|
||
cell.content = createCellData(1, 'A', 1).content;
|
||
line.setCell(2, cell);
|
||
|
||
// different eAttr
|
||
cell.extended = cell.extended.clone();
|
||
cell.extended.underlineStyle = UnderlineStyle.DOTTED;
|
||
line.setCell(3, cell);
|
||
|
||
// no eAttrs again
|
||
cell.bg &= ~BgFlags.HAS_EXTENDED;
|
||
line.setCell(4, cell);
|
||
|
||
assert.deepEqual(line.toArray(), [
|
||
[1, 'a', 1, 'a'.charCodeAt(0)],
|
||
[1, 'a', 1, 'a'.charCodeAt(0)],
|
||
[1, 'A', 1, 'A'.charCodeAt(0)],
|
||
[1, 'A', 1, 'A'.charCodeAt(0)],
|
||
[1, 'A', 1, 'A'.charCodeAt(0)]
|
||
]);
|
||
assert.equal(extendedAttributes(line, 0), undefined);
|
||
assert.equal(extendedAttributes(line, 1)?.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(extendedAttributes(line, 2)?.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(extendedAttributes(line, 3)?.underlineStyle, UnderlineStyle.DOTTED);
|
||
assert.equal(extendedAttributes(line, 4)?.underlineStyle, undefined);
|
||
// should be ref to the same object
|
||
assert.equal(extendedAttributes(line, 1), extendedAttributes(line, 2));
|
||
// should be a different obj
|
||
assert.notEqual(extendedAttributes(line, 1), extendedAttributes(line, 3));
|
||
});
|
||
it('loadCell', () => {
|
||
const line = new TestBufferLine(5);
|
||
const cell = createCellData(1, 'a', 1);
|
||
// no eAttrs
|
||
line.setCell(0, cell);
|
||
|
||
// some underline style
|
||
cell.extended.underlineStyle = UnderlineStyle.CURLY;
|
||
cell.bg |= BgFlags.HAS_EXTENDED;
|
||
line.setCell(1, cell);
|
||
|
||
// same eAttr, different codepoint
|
||
cell.content = 65; // 'A'
|
||
line.setCell(2, cell);
|
||
|
||
// different eAttr
|
||
cell.extended = cell.extended.clone();
|
||
cell.extended.underlineStyle = UnderlineStyle.DOTTED;
|
||
line.setCell(3, cell);
|
||
|
||
// no eAttrs again
|
||
cell.bg &= ~BgFlags.HAS_EXTENDED;
|
||
line.setCell(4, cell);
|
||
|
||
const cell0 = new CellData();
|
||
line.loadCell(0, cell0);
|
||
const cell1 = new CellData();
|
||
line.loadCell(1, cell1);
|
||
const cell2 = new CellData();
|
||
line.loadCell(2, cell2);
|
||
const cell3 = new CellData();
|
||
line.loadCell(3, cell3);
|
||
const cell4 = new CellData();
|
||
line.loadCell(4, cell4);
|
||
|
||
assert.equal(cell0.extended.underlineStyle, UnderlineStyle.NONE);
|
||
assert.equal(cell1.extended.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(cell2.extended.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(cell3.extended.underlineStyle, UnderlineStyle.DOTTED);
|
||
assert.equal(cell4.extended.underlineStyle, UnderlineStyle.NONE);
|
||
assert.equal(cell1.extended, cell2.extended);
|
||
assert.notEqual(cell2.extended, cell3.extended);
|
||
});
|
||
it('fill', () => {
|
||
const line = new TestBufferLine(3);
|
||
const cell = createCellData(1, 'a', 1);
|
||
cell.extended.underlineStyle = UnderlineStyle.CURLY;
|
||
cell.bg |= BgFlags.HAS_EXTENDED;
|
||
line.fill(cell);
|
||
assert.equal(extendedAttributes(line, 0)?.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(extendedAttributes(line, 1)?.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(extendedAttributes(line, 2)?.underlineStyle, UnderlineStyle.CURLY);
|
||
});
|
||
it('insertCells', () => {
|
||
const line = new TestBufferLine(5);
|
||
const cell = createCellData(1, 'a', 1);
|
||
cell.extended.underlineStyle = UnderlineStyle.CURLY;
|
||
cell.bg |= BgFlags.HAS_EXTENDED;
|
||
line.insertCells(1, 3, cell);
|
||
assert.equal(extendedAttributes(line, 1)?.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(extendedAttributes(line, 2)?.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(extendedAttributes(line, 3)?.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(extendedAttributes(line, 4), undefined);
|
||
cell.extended = cell.extended.clone();
|
||
cell.extended.underlineStyle = UnderlineStyle.DOTTED;
|
||
line.insertCells(2, 2, cell);
|
||
assert.equal(extendedAttributes(line, 1)?.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(extendedAttributes(line, 2)?.underlineStyle, UnderlineStyle.DOTTED);
|
||
assert.equal(extendedAttributes(line, 3)?.underlineStyle, UnderlineStyle.DOTTED);
|
||
assert.equal(extendedAttributes(line, 4)?.underlineStyle, UnderlineStyle.CURLY);
|
||
});
|
||
it('deleteCells', () => {
|
||
const line = new TestBufferLine(5);
|
||
const fillCell = createCellData(1, 'a', 1);
|
||
fillCell.extended.underlineStyle = UnderlineStyle.CURLY;
|
||
fillCell.bg |= BgFlags.HAS_EXTENDED;
|
||
line.fill(fillCell);
|
||
fillCell.extended = fillCell.extended.clone();
|
||
fillCell.extended.underlineStyle = UnderlineStyle.DOUBLE;
|
||
line.deleteCells(1, 3, fillCell);
|
||
assert.equal(extendedAttributes(line, 0)?.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(extendedAttributes(line, 1)?.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(extendedAttributes(line, 2)?.underlineStyle, UnderlineStyle.DOUBLE);
|
||
assert.equal(extendedAttributes(line, 3)?.underlineStyle, UnderlineStyle.DOUBLE);
|
||
assert.equal(extendedAttributes(line, 4)?.underlineStyle, UnderlineStyle.DOUBLE);
|
||
});
|
||
it('replaceCells', () => {
|
||
const line = new TestBufferLine(5);
|
||
const fillCell = createCellData(1, 'a', 1);
|
||
fillCell.extended.underlineStyle = UnderlineStyle.CURLY;
|
||
fillCell.bg |= BgFlags.HAS_EXTENDED;
|
||
line.fill(fillCell);
|
||
fillCell.extended = fillCell.extended.clone();
|
||
fillCell.extended.underlineStyle = UnderlineStyle.DOUBLE;
|
||
line.replaceCells(1, 3, fillCell);
|
||
assert.equal(extendedAttributes(line, 0)?.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(extendedAttributes(line, 1)?.underlineStyle, UnderlineStyle.DOUBLE);
|
||
assert.equal(extendedAttributes(line, 2)?.underlineStyle, UnderlineStyle.DOUBLE);
|
||
assert.equal(extendedAttributes(line, 3)?.underlineStyle, UnderlineStyle.CURLY);
|
||
assert.equal(extendedAttributes(line, 4)?.underlineStyle, UnderlineStyle.CURLY);
|
||
});
|
||
it('clone', () => {
|
||
const line = new TestBufferLine(5);
|
||
const cell = createCellData(1, 'a', 1);
|
||
// no eAttrs
|
||
line.setCell(0, cell);
|
||
|
||
// some underline style
|
||
cell.extended.underlineStyle = UnderlineStyle.CURLY;
|
||
cell.bg |= BgFlags.HAS_EXTENDED;
|
||
line.setCell(1, cell);
|
||
|
||
// same eAttr, different codepoint
|
||
cell.content = 65; // 'A'
|
||
line.setCell(2, cell);
|
||
|
||
// different eAttr
|
||
cell.extended = cell.extended.clone();
|
||
cell.extended.underlineStyle = UnderlineStyle.DOTTED;
|
||
line.setCell(3, cell);
|
||
|
||
// no eAttrs again
|
||
cell.bg &= ~BgFlags.HAS_EXTENDED;
|
||
line.setCell(4, cell);
|
||
|
||
const nLine = line.clone();
|
||
assert.equal(extendedAttributes(nLine, 0), extendedAttributes(line, 0));
|
||
assert.equal(extendedAttributes(nLine, 1), extendedAttributes(line, 1));
|
||
assert.equal(extendedAttributes(nLine, 2), extendedAttributes(line, 2));
|
||
assert.equal(extendedAttributes(nLine, 3), extendedAttributes(line, 3));
|
||
assert.equal(extendedAttributes(nLine, 4), extendedAttributes(line, 4));
|
||
});
|
||
it('copyFrom', () => {
|
||
const initial = new TestBufferLine(5);
|
||
const cell = createCellData(1, 'a', 1);
|
||
// no eAttrs
|
||
initial.setCell(0, cell);
|
||
|
||
// some underline style
|
||
cell.extended.underlineStyle = UnderlineStyle.CURLY;
|
||
cell.bg |= BgFlags.HAS_EXTENDED;
|
||
initial.setCell(1, cell);
|
||
|
||
// same eAttr, different codepoint
|
||
cell.content = 65; // 'A'
|
||
initial.setCell(2, cell);
|
||
|
||
// different eAttr
|
||
cell.extended = cell.extended.clone();
|
||
cell.extended.underlineStyle = UnderlineStyle.DOTTED;
|
||
initial.setCell(3, cell);
|
||
|
||
// no eAttrs again
|
||
cell.bg &= ~BgFlags.HAS_EXTENDED;
|
||
initial.setCell(4, cell);
|
||
|
||
const line = new TestBufferLine(5);
|
||
line.fill(createCellData(1, 'b', 1));
|
||
line.copyFrom(initial);
|
||
assert.equal(extendedAttributes(line, 0), extendedAttributes(initial, 0));
|
||
assert.equal(extendedAttributes(line, 1), extendedAttributes(initial, 1));
|
||
assert.equal(extendedAttributes(line, 2), extendedAttributes(initial, 2));
|
||
assert.equal(extendedAttributes(line, 3), extendedAttributes(initial, 3));
|
||
assert.equal(extendedAttributes(line, 4), extendedAttributes(initial, 4));
|
||
});
|
||
|
||
it('should cache canonical string translations', () => {
|
||
const line = new TestBufferLine(5);
|
||
line.setCell(0, createCellData(1, 'a', 1));
|
||
line.setCell(1, createCellData(1, 'b', 1));
|
||
line.setCell(2, createCellData(1, 'c', 1));
|
||
|
||
// Trimmed-only canonical request should cache the trimmed value.
|
||
const trimmed = line.translateToString(true, undefined, undefined, undefined);
|
||
assert.equal(trimmed, 'abc');
|
||
assert.equal(line.cachedString, 'abc');
|
||
assert.equal(line.isCachedStringTrimmed, true);
|
||
|
||
// Non-trimmed canonical request should refresh cache with the full value.
|
||
const translated = line.translateToString(false, undefined, undefined, undefined);
|
||
assert.equal(translated, 'abc ');
|
||
assert.equal(line.cachedString, 'abc ');
|
||
assert.equal(line.isCachedStringTrimmed, false);
|
||
|
||
// Once non-trimmed is cached, trimmed should be derived via trimEnd().
|
||
assert.equal(line.translateToString(true, undefined, undefined, undefined), 'abc');
|
||
assert.equal(line.cachedString, 'abc ');
|
||
assert.equal(line.isCachedStringTrimmed, false);
|
||
|
||
line.cachedString = 'cached-non-trimmed ';
|
||
line.isCachedStringTrimmed = false;
|
||
assert.equal(line.translateToString(false, undefined, undefined, undefined), 'cached-non-trimmed ');
|
||
assert.equal(line.translateToString(true, undefined, undefined, undefined), 'cached-non-trimmed');
|
||
|
||
line.cachedString = 'cached-trimmed';
|
||
line.isCachedStringTrimmed = true;
|
||
assert.equal(line.translateToString(true, undefined, undefined, undefined), 'cached-trimmed');
|
||
assert.equal(line.translateToString(false, undefined, undefined, undefined), 'abc ');
|
||
assert.equal(line.cachedString, 'abc ');
|
||
assert.equal(line.isCachedStringTrimmed, false);
|
||
|
||
// Any optional translation argument should bypass cache.
|
||
assert.equal(line.translateToString(false, 0, 2, undefined), 'ab');
|
||
assert.equal(line.translateToString(true, 0, 2, undefined), 'ab');
|
||
});
|
||
|
||
it('should invalidate cached canonical strings on line mutations', () => {
|
||
const assertCacheInvalidated = (mutate: (line: TestBufferLine) => void): void => {
|
||
const line = new TestBufferLine(5);
|
||
line.fill(createCellData(1, 'a', 1));
|
||
line.translateToString(true, undefined, undefined, undefined);
|
||
assert.equal(line.cachedString, 'aaaaa');
|
||
assert.equal(line.isCachedStringTrimmed, true);
|
||
line.translateToString(false, undefined, undefined, undefined);
|
||
assert.equal(line.cachedString, 'aaaaa');
|
||
assert.equal(line.isCachedStringTrimmed, false);
|
||
mutate(line);
|
||
assert.equal(line.cachedString, undefined);
|
||
assert.equal(line.isCachedStringTrimmed, false);
|
||
};
|
||
|
||
assertCacheInvalidated(line => line.set(0, [0, 'b', 1, 'b'.charCodeAt(0)]));
|
||
assertCacheInvalidated(line => line.setCell(0, createCellData(1, 'b', 1)));
|
||
assertCacheInvalidated(line => line.setCellFromCodepoint(0, 'b'.charCodeAt(0), 1, createCellData(1, 'b', 1)));
|
||
assertCacheInvalidated(line => line.addCodepointToCell(0, 0x301, 0));
|
||
assertCacheInvalidated(line => line.insertCells(1, 1, createCellData(1, 'b', 1)));
|
||
assertCacheInvalidated(line => line.deleteCells(1, 1, createCellData(1, 'b', 1)));
|
||
assertCacheInvalidated(line => line.replaceCells(1, 3, createCellData(1, 'b', 1)));
|
||
assertCacheInvalidated(line => line.resize(6, createCellData(1, 'b', 1)));
|
||
assertCacheInvalidated(line => line.fill(createCellData(1, 'b', 1)));
|
||
assertCacheInvalidated(line => {
|
||
const src = new TestBufferLine(5);
|
||
src.fill(createCellData(1, 'x', 1));
|
||
line.copyFrom(src);
|
||
});
|
||
assertCacheInvalidated(line => {
|
||
const src = new TestBufferLine(5);
|
||
src.fill(createCellData(1, 'x', 1));
|
||
line.copyCellsFrom(src, 0, 0, 2, false);
|
||
});
|
||
});
|
||
});
|
||
});
|