Files
xterm.js/src/common/buffer/BufferLine.test.ts
T

768 lines
39 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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 { CellData } from 'common/buffer/CellData';
import { CharData, IBufferLine } from '../Types';
import { assert } from 'chai';
import { AttributeData } from 'common/buffer/AttributeData';
class TestBufferLine extends BufferLine {
public get combined(): {[index: number]: string} {
return this._combined;
}
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 = -1;
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 = -1;
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);
});
});
});
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, CellData.fromCharData([123, 'a', 456, 'a'.charCodeAt(0)]), 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, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)]));
line.insertCells(1, 3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
assert.deepEqual(line.toArray(), [
[1, 'a', 0, 'a'.charCodeAt(0)],
[4, 'd', 0, 'd'.charCodeAt(0)],
[4, 'd', 0, 'd'.charCodeAt(0)]
]);
});
it('deleteCells', function(): void {
const line = new TestBufferLine(5);
line.setCell(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)]));
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
line.deleteCells(1, 2, CellData.fromCharData([6, 'f', 0, 'f'.charCodeAt(0)]));
assert.deepEqual(line.toArray(), [
[1, 'a', 0, 'a'.charCodeAt(0)],
[4, 'd', 0, 'd'.charCodeAt(0)],
[5, 'e', 0, 'e'.charCodeAt(0)],
[6, 'f', 0, 'f'.charCodeAt(0)],
[6, 'f', 0, 'f'.charCodeAt(0)]
]);
});
it('replaceCells', function(): void {
const line = new TestBufferLine(5);
line.setCell(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)]));
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
line.replaceCells(2, 4, CellData.fromCharData([6, 'f', 0, 'f'.charCodeAt(0)]));
assert.deepEqual(line.toArray(), [
[1, 'a', 0, 'a'.charCodeAt(0)],
[2, 'b', 0, 'b'.charCodeAt(0)],
[6, 'f', 0, 'f'.charCodeAt(0)],
[6, 'f', 0, 'f'.charCodeAt(0)],
[5, 'e', 0, 'e'.charCodeAt(0)]
]);
});
it('fill', function(): void {
const line = new TestBufferLine(5);
line.setCell(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)]));
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
line.fill(CellData.fromCharData([123, 'z', 0, 'z'.charCodeAt(0)]));
assert.deepEqual(line.toArray(), [
[123, 'z', 0, 'z'.charCodeAt(0)],
[123, 'z', 0, 'z'.charCodeAt(0)],
[123, 'z', 0, 'z'.charCodeAt(0)],
[123, 'z', 0, 'z'.charCodeAt(0)],
[123, 'z', 0, 'z'.charCodeAt(0)]
]);
});
it('clone', function(): void {
const line = new TestBufferLine(5, undefined, true);
line.setCell(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)]));
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
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, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)]));
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
const line2 = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), 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, CellData.fromCharData([1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]));
assert.deepEqual(line.toArray(), [[1, 'e\u0301', 0, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]]);
const line2 = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, '\u0301'.charCodeAt(0)]), 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, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
assert.deepEqual(line.toArray(), (Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
});
it('enlarge(true)', function(): void {
const line = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
assert.deepEqual(line.toArray(), (Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
});
it('shrink(true) - should apply new size', function(): void {
const line = new TestBufferLine(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
line.resize(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
assert.deepEqual(line.toArray(), (Array(5) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
});
it('shrink to 0 length', function(): void {
const line = new TestBufferLine(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
line.resize(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
assert.deepEqual(line.toArray(), (Array(0) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
});
it('should remove combining data on replaced cells after shrinking then enlarging', () => {
const line = new TestBufferLine(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), 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, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), 'aa😁aa');
line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
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, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
assert.equal(line.getTrimmedLength(), 0);
});
it('ASCII', function(): void {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.getTrimmedLength(), 3);
});
it('surrogate', function(): void {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)]));
assert.equal(line.getTrimmedLength(), 3);
});
it('combining', function(): void {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]));
assert.equal(line.getTrimmedLength(), 3);
});
it('fullwidth', function(): void {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)]));
line.setCell(3, CellData.fromCharData([0, '', 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, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
assert.equal(line.translateToString(false), ' ');
assert.equal(line.translateToString(true), '');
});
it('ASCII', function(): void {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(4, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(5, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(false), 'a a aa ');
assert.equal(line.translateToString(true), 'a a aa');
assert.equal(line.translateToString(false, 0, 5), 'a a a');
assert.equal(line.translateToString(false, 0, 4), 'a a ');
assert.equal(line.translateToString(false, 0, 3), 'a a');
assert.equal(line.translateToString(true, 0, 5), 'a a a');
assert.equal(line.translateToString(true, 0, 4), 'a a ');
assert.equal(line.translateToString(true, 0, 3), 'a a');
});
it('surrogate', function(): void {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)]));
line.setCell(4, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)]));
line.setCell(5, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)]));
assert.equal(line.translateToString(false), 'a 𝄞 𝄞𝄞 ');
assert.equal(line.translateToString(true), 'a 𝄞 𝄞𝄞');
assert.equal(line.translateToString(false, 0, 5), 'a 𝄞 𝄞');
assert.equal(line.translateToString(false, 0, 4), 'a 𝄞 ');
assert.equal(line.translateToString(false, 0, 3), 'a 𝄞');
assert.equal(line.translateToString(true, 0, 5), 'a 𝄞 𝄞');
assert.equal(line.translateToString(true, 0, 4), 'a 𝄞 ');
assert.equal(line.translateToString(true, 0, 3), 'a 𝄞');
});
it('combining', function(): void {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]));
line.setCell(4, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]));
line.setCell(5, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]));
assert.equal(line.translateToString(false), 'a e\u0301 e\u0301e\u0301 ');
assert.equal(line.translateToString(true), 'a e\u0301 e\u0301e\u0301');
assert.equal(line.translateToString(false, 0, 5), 'a e\u0301 e\u0301');
assert.equal(line.translateToString(false, 0, 4), 'a e\u0301 ');
assert.equal(line.translateToString(false, 0, 3), 'a e\u0301');
assert.equal(line.translateToString(true, 0, 5), 'a e\u0301 e\u0301');
assert.equal(line.translateToString(true, 0, 4), 'a e\u0301 ');
assert.equal(line.translateToString(true, 0, 3), 'a e\u0301');
});
it('fullwidth', function(): void {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)]));
line.setCell(3, CellData.fromCharData([0, '', 0, 0]));
line.setCell(5, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)]));
line.setCell(6, CellData.fromCharData([0, '', 0, 0]));
line.setCell(7, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)]));
line.setCell(8, CellData.fromCharData([0, '', 0, 0]));
assert.equal(line.translateToString(false), 'a 1 11 ');
assert.equal(line.translateToString(true), 'a 1 11');
assert.equal(line.translateToString(false, 0, 7), 'a 1 1');
assert.equal(line.translateToString(false, 0, 6), 'a 1 1');
assert.equal(line.translateToString(false, 0, 5), 'a 1 ');
assert.equal(line.translateToString(false, 0, 4), 'a 1');
assert.equal(line.translateToString(false, 0, 3), 'a 1');
assert.equal(line.translateToString(false, 0, 2), 'a ');
assert.equal(line.translateToString(true, 0, 7), 'a 1 1');
assert.equal(line.translateToString(true, 0, 6), 'a 1 1');
assert.equal(line.translateToString(true, 0, 5), 'a 1 ');
assert.equal(line.translateToString(true, 0, 4), 'a 1');
assert.equal(line.translateToString(true, 0, 3), 'a 1');
assert.equal(line.translateToString(true, 0, 2), 'a ');
});
it('space at end', function(): void {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(4, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(5, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
line.setCell(6, CellData.fromCharData([1, ' ', 1, ' '.charCodeAt(0)]));
assert.equal(line.translateToString(false), 'a a aa ');
assert.equal(line.translateToString(true), 'a a aa ');
});
it('should always return some sane value', function(): void {
// 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, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
assert.equal(line.translateToString(false), ' ');
assert.equal(line.translateToString(true), '');
});
it('should work with endCol=0', () => {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(true, 0, 0), '');
});
});
describe('addCharToCell', () => {
it('should set width to 1 for empty cell', () => {
const line = new TestBufferLine(3, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
line.addCodepointToCell(0, '\u0301'.charCodeAt(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, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), 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));
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, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), 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));
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 = CellData.fromCharData([1, '¥', 2, '¥'.charCodeAt(0)]);
for (let i = 0; i < line.length; i += 2) {
line.setCell(i, cell);
}
}
it('insert - wide char at pos', () => {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.insertCells(9, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), '¥¥¥¥ a');
line.insertCells(8, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), '¥¥¥¥a ');
line.insertCells(1, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), ' a ¥¥¥a');
});
it('insert - wide char at end', () => {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.insertCells(0, 3, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), 'aaa¥¥¥ ');
line.insertCells(4, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), 'aaa a ¥¥');
line.insertCells(4, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), 'aaa aa ¥ ');
});
it('delete', () => {
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.deleteCells(0, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), ' ¥¥¥¥a');
line.deleteCells(5, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), ' ¥¥¥aaa');
line.deleteCells(0, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), ' ¥¥aaaaa');
});
it('replace - start at 0', () => {
let line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.replaceCells(0, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), 'a ¥¥¥¥');
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.replaceCells(0, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), 'aa¥¥¥¥');
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.replaceCells(0, 3, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), 'aaa ¥¥¥');
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.replaceCells(0, 8, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), 'aaaaaaaa¥');
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.replaceCells(0, 9, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), 'aaaaaaaaa ');
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.replaceCells(0, 10, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), 'aaaaaaaaaa');
});
it('replace - start at 1', () => {
let line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.replaceCells(1, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), ' a¥¥¥¥');
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.replaceCells(1, 3, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), ' aa ¥¥¥');
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.replaceCells(1, 4, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), ' aaa¥¥¥');
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.replaceCells(1, 8, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), ' aaaaaaa¥');
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.replaceCells(1, 9, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), ' aaaaaaaa ');
line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
populate(line);
line.replaceCells(1, 10, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
assert.equal(line.translateToString(), ' aaaaaaaaa');
});
});
describe('extended attributes', () => {
it('setCells', function(): void {
const line = new TestBufferLine(5);
const cell = CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]);
// 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);
assert.deepEqual(line.toArray(), [
[1, 'a', 0, 'a'.charCodeAt(0)],
[1, 'a', 0, 'a'.charCodeAt(0)],
[1, 'A', 0, 'A'.charCodeAt(0)],
[1, 'A', 0, 'A'.charCodeAt(0)],
[1, 'A', 0, 'A'.charCodeAt(0)]
]);
assert.equal((line as any)._extendedAttrs[0], undefined);
assert.equal((line as any)._extendedAttrs[1].underlineStyle, UnderlineStyle.CURLY);
assert.equal((line as any)._extendedAttrs[2].underlineStyle, UnderlineStyle.CURLY);
assert.equal((line as any)._extendedAttrs[3].underlineStyle, UnderlineStyle.DOTTED);
assert.equal((line as any)._extendedAttrs[4], undefined);
// should be ref to the same object
assert.equal((line as any)._extendedAttrs[1], (line as any)._extendedAttrs[2]);
// should be a different obj
assert.notEqual((line as any)._extendedAttrs[1], (line as any)._extendedAttrs[3]);
});
it('loadCell', () => {
const line = new TestBufferLine(5);
const cell = CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]);
// 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 = CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]);
cell.extended.underlineStyle = UnderlineStyle.CURLY;
cell.bg |= BgFlags.HAS_EXTENDED;
line.fill(cell);
assert.equal((line as any)._extendedAttrs[0].underlineStyle, UnderlineStyle.CURLY);
assert.equal((line as any)._extendedAttrs[1].underlineStyle, UnderlineStyle.CURLY);
assert.equal((line as any)._extendedAttrs[2].underlineStyle, UnderlineStyle.CURLY);
});
it('insertCells', () => {
const line = new TestBufferLine(5);
const cell = CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]);
cell.extended.underlineStyle = UnderlineStyle.CURLY;
cell.bg |= BgFlags.HAS_EXTENDED;
line.insertCells(1, 3, cell);
assert.equal((line as any)._extendedAttrs[1].underlineStyle, UnderlineStyle.CURLY);
assert.equal((line as any)._extendedAttrs[2].underlineStyle, UnderlineStyle.CURLY);
assert.equal((line as any)._extendedAttrs[3].underlineStyle, UnderlineStyle.CURLY);
assert.equal((line as any)._extendedAttrs[4], undefined);
cell.extended = cell.extended.clone();
cell.extended.underlineStyle = UnderlineStyle.DOTTED;
line.insertCells(2, 2, cell);
assert.equal((line as any)._extendedAttrs[1].underlineStyle, UnderlineStyle.CURLY);
assert.equal((line as any)._extendedAttrs[2].underlineStyle, UnderlineStyle.DOTTED);
assert.equal((line as any)._extendedAttrs[3].underlineStyle, UnderlineStyle.DOTTED);
assert.equal((line as any)._extendedAttrs[4].underlineStyle, UnderlineStyle.CURLY);
});
it('deleteCells', () => {
const line = new TestBufferLine(5);
const fillCell = CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]);
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((line as any)._extendedAttrs[0].underlineStyle, UnderlineStyle.CURLY);
assert.equal((line as any)._extendedAttrs[1].underlineStyle, UnderlineStyle.CURLY);
assert.equal((line as any)._extendedAttrs[2].underlineStyle, UnderlineStyle.DOUBLE);
assert.equal((line as any)._extendedAttrs[3].underlineStyle, UnderlineStyle.DOUBLE);
assert.equal((line as any)._extendedAttrs[4].underlineStyle, UnderlineStyle.DOUBLE);
});
it('replaceCells', () => {
const line = new TestBufferLine(5);
const fillCell = CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]);
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((line as any)._extendedAttrs[0].underlineStyle, UnderlineStyle.CURLY);
assert.equal((line as any)._extendedAttrs[1].underlineStyle, UnderlineStyle.DOUBLE);
assert.equal((line as any)._extendedAttrs[2].underlineStyle, UnderlineStyle.DOUBLE);
assert.equal((line as any)._extendedAttrs[3].underlineStyle, UnderlineStyle.CURLY);
assert.equal((line as any)._extendedAttrs[4].underlineStyle, UnderlineStyle.CURLY);
});
it('clone', () => {
const line = new TestBufferLine(5);
const cell = CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]);
// 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((nLine as any)._extendedAttrs[0], (line as any)._extendedAttrs[0]);
assert.equal((nLine as any)._extendedAttrs[1], (line as any)._extendedAttrs[1]);
assert.equal((nLine as any)._extendedAttrs[2], (line as any)._extendedAttrs[2]);
assert.equal((nLine as any)._extendedAttrs[3], (line as any)._extendedAttrs[3]);
assert.equal((nLine as any)._extendedAttrs[4], (line as any)._extendedAttrs[4]);
});
it('copyFrom', () => {
const initial = new TestBufferLine(5);
const cell = CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]);
// 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(CellData.fromCharData([1, 'b', 0, 'b'.charCodeAt(0)]));
line.copyFrom(initial);
assert.equal((line as any)._extendedAttrs[0], (initial as any)._extendedAttrs[0]);
assert.equal((line as any)._extendedAttrs[1], (initial as any)._extendedAttrs[1]);
assert.equal((line as any)._extendedAttrs[2], (initial as any)._extendedAttrs[2]);
assert.equal((line as any)._extendedAttrs[3], (initial as any)._extendedAttrs[3]);
assert.equal((line as any)._extendedAttrs[4], (initial as any)._extendedAttrs[4]);
});
});
});