Merge pull request #2751 from jerch/underline_core
Underline styles and colors - core part
This commit is contained in:
@@ -8,7 +8,7 @@ import { InputHandler } from 'common/InputHandler';
|
||||
import { IBufferLine, IAttributeData } from 'common/Types';
|
||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { Attributes } from 'common/buffer/Constants';
|
||||
import { Attributes, UnderlineStyle } from 'common/buffer/Constants';
|
||||
import { AttributeData } from 'common/buffer/AttributeData';
|
||||
import { Params } from 'common/parser/Params';
|
||||
import { MockCoreService, MockBufferService, MockDirtyRowService, MockOptionsService, MockLogService, MockCoreMouseService, MockCharsetService, MockUnicodeService } from 'common/TestUtils.test';
|
||||
@@ -1468,6 +1468,181 @@ describe('InputHandler', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('extended underline style support (SGR 4)', () => {
|
||||
beforeEach(() => {
|
||||
bufferService.resize(10, 5);
|
||||
});
|
||||
it('4 | 24', () => {
|
||||
inputHandler.parse('\x1b[4m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.SINGLE);
|
||||
inputHandler.parse('\x1b[24m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.NONE);
|
||||
});
|
||||
it('21 | 24', () => {
|
||||
inputHandler.parse('\x1b[21m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.DOUBLE);
|
||||
inputHandler.parse('\x1b[24m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.NONE);
|
||||
});
|
||||
it('4:1 | 4:0', () => {
|
||||
inputHandler.parse('\x1b[4:1m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.SINGLE);
|
||||
inputHandler.parse('\x1b[4:0m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.NONE);
|
||||
inputHandler.parse('\x1b[4:1m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.SINGLE);
|
||||
inputHandler.parse('\x1b[24m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.NONE);
|
||||
});
|
||||
it('4:2 | 4:0', () => {
|
||||
inputHandler.parse('\x1b[4:2m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.DOUBLE);
|
||||
inputHandler.parse('\x1b[4:0m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.NONE);
|
||||
inputHandler.parse('\x1b[4:2m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.DOUBLE);
|
||||
inputHandler.parse('\x1b[24m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.NONE);
|
||||
});
|
||||
it('4:3 | 4:0', () => {
|
||||
inputHandler.parse('\x1b[4:3m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.CURLY);
|
||||
inputHandler.parse('\x1b[4:0m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.NONE);
|
||||
inputHandler.parse('\x1b[4:3m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.CURLY);
|
||||
inputHandler.parse('\x1b[24m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.NONE);
|
||||
});
|
||||
it('4:4 | 4:0', () => {
|
||||
inputHandler.parse('\x1b[4:4m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.DOTTED);
|
||||
inputHandler.parse('\x1b[4:0m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.NONE);
|
||||
inputHandler.parse('\x1b[4:4m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.DOTTED);
|
||||
inputHandler.parse('\x1b[24m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.NONE);
|
||||
});
|
||||
it('4:5 | 4:0', () => {
|
||||
inputHandler.parse('\x1b[4:5m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.DASHED);
|
||||
inputHandler.parse('\x1b[4:0m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.NONE);
|
||||
inputHandler.parse('\x1b[4:5m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.DASHED);
|
||||
inputHandler.parse('\x1b[24m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.NONE);
|
||||
});
|
||||
it('4:x --> 4 should revert to single underline', () => {
|
||||
inputHandler.parse('\x1b[4:5m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.DASHED);
|
||||
inputHandler.parse('\x1b[4m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineStyle(), UnderlineStyle.SINGLE);
|
||||
});
|
||||
});
|
||||
describe('underline colors (SGR 58 & SGR 59)', () => {
|
||||
beforeEach(() => {
|
||||
bufferService.resize(10, 5);
|
||||
});
|
||||
it('defaults to FG color', () => {
|
||||
for (const s of ['', '\x1b[30m', '\x1b[38;510m', '\x1b[38;2;1;2;3m']) {
|
||||
inputHandler.parse(s);
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineColor(), inputHandler.curAttrData.getFgColor());
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineColorMode(), inputHandler.curAttrData.getFgColorMode());
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorRGB(), inputHandler.curAttrData.isFgRGB());
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorPalette(), inputHandler.curAttrData.isFgPalette());
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorDefault(), inputHandler.curAttrData.isFgDefault());
|
||||
}
|
||||
});
|
||||
it('correctly sets P256/RGB colors', () => {
|
||||
inputHandler.parse('\x1b[4m');
|
||||
inputHandler.parse('\x1b[58;5;123m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineColor(), 123);
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineColorMode(), Attributes.CM_P256);
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorRGB(), false);
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorPalette(), true);
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorDefault(), false);
|
||||
inputHandler.parse('\x1b[58;2::1:2:3m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineColor(), (1 << 16) | (2 << 8) | 3);
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineColorMode(), Attributes.CM_RGB);
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorRGB(), true);
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorPalette(), false);
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorDefault(), false);
|
||||
});
|
||||
it('P256/RGB persistence', () => {
|
||||
const cell = new CellData();
|
||||
inputHandler.parse('\x1b[4m');
|
||||
inputHandler.parse('\x1b[58;5;123m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineColor(), 123);
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineColorMode(), Attributes.CM_P256);
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorRGB(), false);
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorPalette(), true);
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorDefault(), false);
|
||||
inputHandler.parse('ab');
|
||||
bufferService.buffer!.lines.get(0)!.loadCell(1, cell);
|
||||
assert.equal(cell.getUnderlineColor(), 123);
|
||||
assert.equal(cell.getUnderlineColorMode(), Attributes.CM_P256);
|
||||
assert.equal(cell.isUnderlineColorRGB(), false);
|
||||
assert.equal(cell.isUnderlineColorPalette(), true);
|
||||
assert.equal(cell.isUnderlineColorDefault(), false);
|
||||
|
||||
inputHandler.parse('\x1b[4:0m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineColor(), inputHandler.curAttrData.getFgColor());
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineColorMode(), inputHandler.curAttrData.getFgColorMode());
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorRGB(), inputHandler.curAttrData.isFgRGB());
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorPalette(), inputHandler.curAttrData.isFgPalette());
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorDefault(), inputHandler.curAttrData.isFgDefault());
|
||||
inputHandler.parse('a');
|
||||
bufferService.buffer!.lines.get(0)!.loadCell(1, cell);
|
||||
assert.equal(cell.getUnderlineColor(), 123);
|
||||
assert.equal(cell.getUnderlineColorMode(), Attributes.CM_P256);
|
||||
assert.equal(cell.isUnderlineColorRGB(), false);
|
||||
assert.equal(cell.isUnderlineColorPalette(), true);
|
||||
assert.equal(cell.isUnderlineColorDefault(), false);
|
||||
bufferService.buffer!.lines.get(0)!.loadCell(2, cell);
|
||||
assert.equal(cell.getUnderlineColor(), inputHandler.curAttrData.getFgColor());
|
||||
assert.equal(cell.getUnderlineColorMode(), inputHandler.curAttrData.getFgColorMode());
|
||||
assert.equal(cell.isUnderlineColorRGB(), inputHandler.curAttrData.isFgRGB());
|
||||
assert.equal(cell.isUnderlineColorPalette(), inputHandler.curAttrData.isFgPalette());
|
||||
assert.equal(cell.isUnderlineColorDefault(), inputHandler.curAttrData.isFgDefault());
|
||||
|
||||
inputHandler.parse('\x1b[4m');
|
||||
inputHandler.parse('\x1b[58;2::1:2:3m');
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineColor(), (1 << 16) | (2 << 8) | 3);
|
||||
assert.equal(inputHandler.curAttrData.getUnderlineColorMode(), Attributes.CM_RGB);
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorRGB(), true);
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorPalette(), false);
|
||||
assert.equal(inputHandler.curAttrData.isUnderlineColorDefault(), false);
|
||||
inputHandler.parse('a');
|
||||
inputHandler.parse('\x1b[24m');
|
||||
bufferService.buffer!.lines.get(0)!.loadCell(1, cell);
|
||||
assert.equal(cell.getUnderlineColor(), 123);
|
||||
assert.equal(cell.getUnderlineColorMode(), Attributes.CM_P256);
|
||||
assert.equal(cell.isUnderlineColorRGB(), false);
|
||||
assert.equal(cell.isUnderlineColorPalette(), true);
|
||||
assert.equal(cell.isUnderlineColorDefault(), false);
|
||||
bufferService.buffer!.lines.get(0)!.loadCell(3, cell);
|
||||
assert.equal(cell.getUnderlineColor(), (1 << 16) | (2 << 8) | 3);
|
||||
assert.equal(cell.getUnderlineColorMode(), Attributes.CM_RGB);
|
||||
assert.equal(cell.isUnderlineColorRGB(), true);
|
||||
assert.equal(cell.isUnderlineColorPalette(), false);
|
||||
assert.equal(cell.isUnderlineColorDefault(), false);
|
||||
|
||||
// eAttrs in buffer pos 0 and 1 should be the same object
|
||||
assert.equal(
|
||||
(bufferService.buffer!.lines.get(0)! as any)._extendedAttrs[0],
|
||||
(bufferService.buffer!.lines.get(0)! as any)._extendedAttrs[1]
|
||||
);
|
||||
// should not have written eAttr for pos 2 in the buffer
|
||||
assert.equal((bufferService.buffer!.lines.get(0)! as any)._extendedAttrs[2], undefined);
|
||||
// eAttrs in buffer pos 1 and pos 3 must be different objs
|
||||
assert.notEqual(
|
||||
(bufferService.buffer!.lines.get(0)! as any)._extendedAttrs[1],
|
||||
(bufferService.buffer!.lines.get(0)! as any)._extendedAttrs[3]
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('DECSTR', () => {
|
||||
beforeEach(() => {
|
||||
bufferService.resize(10, 5);
|
||||
|
||||
+86
-29
@@ -14,7 +14,7 @@ import { StringToUtf32, stringFromCodePoint, utf32ToString, Utf8ToUtf32 } from '
|
||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
import { EventEmitter, IEvent } from 'common/EventEmitter';
|
||||
import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IFunctionIdentifier } from 'common/parser/Types';
|
||||
import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content } from 'common/buffer/Constants';
|
||||
import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content, UnderlineStyle } from 'common/buffer/Constants';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { AttributeData } from 'common/buffer/AttributeData';
|
||||
import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService, ICharsetService, IUnicodeService } from 'common/services/Services';
|
||||
@@ -509,7 +509,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
// handle wide chars: reset start_cell-1 if we would overwrite the second cell of a wide char
|
||||
if (buffer.x && end - start > 0 && bufferRow.getWidth(buffer.x - 1) === 2) {
|
||||
bufferRow.setCellFromCodePoint(buffer.x - 1, 0, 1, curAttr.fg, curAttr.bg);
|
||||
bufferRow.setCellFromCodePoint(buffer.x - 1, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
}
|
||||
|
||||
for (let pos = start; pos < end; ++pos) {
|
||||
@@ -558,7 +558,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
if (wraparoundMode) {
|
||||
// clear left over cells to the right
|
||||
while (buffer.x < cols) {
|
||||
bufferRow.setCellFromCodePoint(buffer.x++, 0, 1, curAttr.fg, curAttr.bg);
|
||||
bufferRow.setCellFromCodePoint(buffer.x++, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
}
|
||||
buffer.x = 0;
|
||||
buffer.y++;
|
||||
@@ -593,12 +593,12 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// a halfwidth char any fullwidth shifted there is lost
|
||||
// and will be set to empty cell
|
||||
if (bufferRow.getWidth(cols - 1) === 2) {
|
||||
bufferRow.setCellFromCodePoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr.fg, curAttr.bg);
|
||||
bufferRow.setCellFromCodePoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
}
|
||||
}
|
||||
|
||||
// write current char to buffer and advance cursor
|
||||
bufferRow.setCellFromCodePoint(buffer.x++, code, chWidth, curAttr.fg, curAttr.bg);
|
||||
bufferRow.setCellFromCodePoint(buffer.x++, code, chWidth, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
|
||||
// fullwidth char - also set next cell to placeholder stub and advance cursor
|
||||
// for graphemes bigger than fullwidth we can simply loop to zero
|
||||
@@ -606,7 +606,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
if (chWidth > 0) {
|
||||
while (--chWidth) {
|
||||
// other than a regular empty cell a cell following a wide char has no width
|
||||
bufferRow.setCellFromCodePoint(buffer.x++, 0, 0, curAttr.fg, curAttr.bg);
|
||||
bufferRow.setCellFromCodePoint(buffer.x++, 0, 0, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -627,7 +627,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
// handle wide chars: reset cell to the right if it is second cell of a wide char
|
||||
if (buffer.x < cols && end - start > 0 && bufferRow.getWidth(buffer.x) === 0 && !bufferRow.hasContent(buffer.x)) {
|
||||
bufferRow.setCellFromCodePoint(buffer.x, 0, 1, curAttr.fg, curAttr.bg);
|
||||
bufferRow.setCellFromCodePoint(buffer.x, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
|
||||
}
|
||||
|
||||
this._dirtyRowService.markDirty(buffer.y);
|
||||
@@ -2105,6 +2105,21 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to write color information packed with color mode.
|
||||
*/
|
||||
private _updateAttrColor(color: number, mode: number, c1: number, c2: number, c3: number): number {
|
||||
if (mode === 2) {
|
||||
color |= Attributes.CM_RGB;
|
||||
color &= ~Attributes.RGB_MASK;
|
||||
color |= AttributeData.fromColorRGB([c1, c2, c3]);
|
||||
} else if (mode === 5) {
|
||||
color &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
|
||||
color |= Attributes.CM_P256 | (c1 & 0xff);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to extract and apply color params/subparams.
|
||||
* Returns advance for params index.
|
||||
@@ -2154,35 +2169,57 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
|
||||
// apply colors
|
||||
if (accu[0] === 38) {
|
||||
if (accu[1] === 2) {
|
||||
attr.fg |= Attributes.CM_RGB;
|
||||
attr.fg &= ~Attributes.RGB_MASK;
|
||||
attr.fg |= AttributeData.fromColorRGB([accu[3], accu[4], accu[5]]);
|
||||
} else if (accu[1] === 5) {
|
||||
attr.fg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
|
||||
attr.fg |= Attributes.CM_P256 | (accu[3] & 0xff);
|
||||
}
|
||||
} else if (accu[0] === 48) {
|
||||
if (accu[1] === 2) {
|
||||
attr.bg |= Attributes.CM_RGB;
|
||||
attr.bg &= ~Attributes.RGB_MASK;
|
||||
attr.bg |= AttributeData.fromColorRGB([accu[3], accu[4], accu[5]]);
|
||||
} else if (accu[1] === 5) {
|
||||
attr.bg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
|
||||
attr.bg |= Attributes.CM_P256 | (accu[3] & 0xff);
|
||||
}
|
||||
switch (accu[0]) {
|
||||
case 38:
|
||||
attr.fg = this._updateAttrColor(attr.fg, accu[1], accu[3], accu[4], accu[5]);
|
||||
break;
|
||||
case 48:
|
||||
attr.bg = this._updateAttrColor(attr.bg, accu[1], accu[3], accu[4], accu[5]);
|
||||
break;
|
||||
case 58:
|
||||
attr.extended = attr.extended.clone();
|
||||
attr.extended.underlineColor = this._updateAttrColor(attr.extended.underlineColor, accu[1], accu[3], accu[4], accu[5]);
|
||||
}
|
||||
|
||||
return advance;
|
||||
}
|
||||
|
||||
/**
|
||||
* SGR 4 subparams:
|
||||
* 4:0 - equal to SGR 24 (turn off all underline)
|
||||
* 4:1 - equal to SGR 4 (single underline)
|
||||
* 4:2 - equal to SGR 21 (double underline)
|
||||
* 4:3 - curly underline
|
||||
* 4:4 - dotted underline
|
||||
* 4:5 - dashed underline
|
||||
*/
|
||||
private _processUnderline(style: number, attr: IAttributeData): void {
|
||||
// treat extended attrs as immutable, thus always clone from old one
|
||||
// this is needed since the buffer only holds references to it
|
||||
attr.extended = attr.extended.clone();
|
||||
|
||||
// default to 1 == single underline
|
||||
if (!~style || style > 5) {
|
||||
style = 1;
|
||||
}
|
||||
attr.extended.underlineStyle = style;
|
||||
attr.fg |= FgFlags.UNDERLINE;
|
||||
|
||||
// 0 deactivates underline
|
||||
if (style === 0) {
|
||||
attr.fg &= ~FgFlags.UNDERLINE;
|
||||
}
|
||||
|
||||
// update HAS_EXTENDED in BG
|
||||
attr.updateExtended();
|
||||
}
|
||||
|
||||
/**
|
||||
* CSI Pm m Character Attributes (SGR).
|
||||
*
|
||||
* @vt: #P[See below for supported attributes.] CSI SGR "Select Graphic Rendition" "CSI Pm m" "Set/Reset various text attributes."
|
||||
* SGR selects one or more character attributes at the same time. Multiple params (up to 32)
|
||||
* are applied from in order from left to right. The changed attributes are applied to all new
|
||||
* are applied in order from left to right. The changed attributes are applied to all new
|
||||
* characters received. If you move characters in the viewport by scrolling or any other means,
|
||||
* then the attributes move with the characters.
|
||||
*
|
||||
@@ -2194,13 +2231,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* | 1 | Bold. (also see `options.drawBoldTextInBrightColors`) | #Y |
|
||||
* | 2 | Faint, decreased intensity. | #Y |
|
||||
* | 3 | Italic. | #Y |
|
||||
* | 4 | Underlined. (no support for newer underline styles) | #Y |
|
||||
* | 4 | Underlined (see below for style support). | #Y |
|
||||
* | 5 | Slowly blinking. | #N |
|
||||
* | 6 | Rapidly blinking. | #N |
|
||||
* | 7 | Inverse. Flips foreground and background color. | #Y |
|
||||
* | 8 | Invisible (hidden). | #Y |
|
||||
* | 9 | Crossed-out characters. | #N |
|
||||
* | 21 | Doubly underlined. | #N |
|
||||
* | 21 | Doubly underlined. | #P[Currently outputs a single underline.] |
|
||||
* | 22 | Normal (neither bold nor faint). | #Y |
|
||||
* | 23 | No italic. | #Y |
|
||||
* | 24 | Not underlined. | #Y |
|
||||
@@ -2231,6 +2268,18 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* | 90 - 97 | Bright foreground color (analogous to 30 - 37). | #Y |
|
||||
* | 100 - 107 | Bright background color (analogous to 40 - 47). | #Y |
|
||||
*
|
||||
* Underline supports subparams to denote the style in the form `4 : x`:
|
||||
*
|
||||
* | x | Meaning | Support |
|
||||
* | ------ | ------------------------------------------------------------- | ------- |
|
||||
* | 0 | No underline. Same as `SGR 24 m`. | #Y |
|
||||
* | 1 | Single underline. Same as `SGR 4 m`. | #Y |
|
||||
* | 2 | Double underline. | #P[Currently outputs a single underline.] |
|
||||
* | 3 | Curly underline. | #P[Currently outputs a single underline.] |
|
||||
* | 4 | Dotted underline. | #P[Currently outputs a single underline.] |
|
||||
* | 5 | Dashed underline. | #P[Currently outputs a single underline.] |
|
||||
* | other | Single underline. Same as `SGR 4 m`. | #Y |
|
||||
*
|
||||
* Extended colors are supported for foreground (Ps=38) and background (Ps=48) as follows:
|
||||
*
|
||||
* | Ps + 1 | Meaning | Support |
|
||||
@@ -2289,6 +2338,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
} else if (p === 4) {
|
||||
// underlined text
|
||||
attr.fg |= FgFlags.UNDERLINE;
|
||||
this._processUnderline(params.hasSubParams(i) ? params.getSubParams(i)![0] : UnderlineStyle.SINGLE, attr);
|
||||
} else if (p === 5) {
|
||||
// blink
|
||||
attr.fg |= FgFlags.BLINK;
|
||||
@@ -2302,6 +2352,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
} else if (p === 2) {
|
||||
// dimmed text
|
||||
attr.bg |= BgFlags.DIM;
|
||||
} else if (p === 21) {
|
||||
// double underline
|
||||
this._processUnderline(UnderlineStyle.DOUBLE, attr);
|
||||
} else if (p === 22) {
|
||||
// not bold nor faint
|
||||
attr.fg &= ~FgFlags.BOLD;
|
||||
@@ -2329,9 +2382,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// reset bg
|
||||
attr.bg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
|
||||
attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
|
||||
} else if (p === 38 || p === 48) {
|
||||
} else if (p === 38 || p === 48 || p === 58) {
|
||||
// fg color 256 and RGB
|
||||
i += this._extractColor(params, i, attr);
|
||||
} else if (p === 59) {
|
||||
attr.extended = attr.extended.clone();
|
||||
attr.extended.underlineColor = -1;
|
||||
attr.updateExtended();
|
||||
} else if (p === 100) { // FIXME: dead branch, p=100 already handled above!
|
||||
// reset fg/bg
|
||||
attr.fg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
|
||||
|
||||
Vendored
+19
-1
@@ -84,10 +84,18 @@ export interface ICharset {
|
||||
export type CharData = [number, string, number, number];
|
||||
export type IColorRGB = [number, number, number];
|
||||
|
||||
export interface IExtendedAttrs {
|
||||
underlineStyle: number;
|
||||
underlineColor: number;
|
||||
clone(): IExtendedAttrs;
|
||||
isEmpty(): boolean;
|
||||
}
|
||||
|
||||
/** Attribute data */
|
||||
export interface IAttributeData {
|
||||
fg: number;
|
||||
bg: number;
|
||||
extended: IExtendedAttrs;
|
||||
|
||||
clone(): IAttributeData;
|
||||
|
||||
@@ -114,6 +122,16 @@ export interface IAttributeData {
|
||||
// colors
|
||||
getFgColor(): number;
|
||||
getBgColor(): number;
|
||||
|
||||
// extended attrs
|
||||
hasExtendedAttrs(): number;
|
||||
updateExtended(): void;
|
||||
getUnderlineColor(): number;
|
||||
getUnderlineColorMode(): number;
|
||||
isUnderlineColorRGB(): boolean;
|
||||
isUnderlineColorPalette(): boolean;
|
||||
isUnderlineColorDefault(): boolean;
|
||||
getUnderlineStyle(): number;
|
||||
}
|
||||
|
||||
/** Cell data */
|
||||
@@ -138,7 +156,7 @@ export interface IBufferLine {
|
||||
set(index: number, value: CharData): void;
|
||||
loadCell(index: number, cell: ICellData): ICellData;
|
||||
setCell(index: number, cell: ICellData): void;
|
||||
setCellFromCodePoint(index: number, codePoint: number, width: number, fg: number, bg: number): void;
|
||||
setCellFromCodePoint(index: number, codePoint: number, width: number, fg: number, bg: number, eAttrs: IExtendedAttrs): void;
|
||||
addCodepointToCell(index: number, codePoint: number): void;
|
||||
insertCells(pos: number, n: number, ch: ICellData, eraseAttr?: IAttributeData): void;
|
||||
deleteCells(pos: number, n: number, fill: ICellData, eraseAttr?: IAttributeData): void;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IAttributeData, IColorRGB } from 'common/Types';
|
||||
import { Attributes, FgFlags, BgFlags } from 'common/buffer/Constants';
|
||||
import { IAttributeData, IColorRGB, IExtendedAttrs } from 'common/Types';
|
||||
import { Attributes, FgFlags, BgFlags, UnderlineStyle } from 'common/buffer/Constants';
|
||||
|
||||
export class AttributeData implements IAttributeData {
|
||||
public static toColorRGB(value: number): IColorRGB {
|
||||
@@ -23,12 +23,14 @@ export class AttributeData implements IAttributeData {
|
||||
const newObj = new AttributeData();
|
||||
newObj.fg = this.fg;
|
||||
newObj.bg = this.bg;
|
||||
newObj.extended = this.extended.clone();
|
||||
return newObj;
|
||||
}
|
||||
|
||||
// data
|
||||
public fg: number = 0;
|
||||
public bg: number = 0;
|
||||
public fg = 0;
|
||||
public bg = 0;
|
||||
public extended = new ExtendedAttrs();
|
||||
|
||||
// flags
|
||||
public isInverse(): number { return this.fg & FgFlags.INVERSE; }
|
||||
@@ -67,4 +69,79 @@ export class AttributeData implements IAttributeData {
|
||||
default: return -1; // CM_DEFAULT defaults to -1
|
||||
}
|
||||
}
|
||||
|
||||
// extended attrs
|
||||
public hasExtendedAttrs(): number {
|
||||
return this.bg & BgFlags.HAS_EXTENDED;
|
||||
}
|
||||
public updateExtended(): void {
|
||||
if (this.extended.isEmpty()) {
|
||||
this.bg &= ~BgFlags.HAS_EXTENDED;
|
||||
} else {
|
||||
this.bg |= BgFlags.HAS_EXTENDED;
|
||||
}
|
||||
}
|
||||
public getUnderlineColor(): number {
|
||||
if ((this.bg & BgFlags.HAS_EXTENDED) && ~this.extended.underlineColor) {
|
||||
switch (this.extended.underlineColor & Attributes.CM_MASK) {
|
||||
case Attributes.CM_P16:
|
||||
case Attributes.CM_P256: return this.extended.underlineColor & Attributes.PCOLOR_MASK;
|
||||
case Attributes.CM_RGB: return this.extended.underlineColor & Attributes.RGB_MASK;
|
||||
default: return this.getFgColor();
|
||||
}
|
||||
}
|
||||
return this.getFgColor();
|
||||
}
|
||||
public getUnderlineColorMode(): number {
|
||||
return (this.bg & BgFlags.HAS_EXTENDED) && ~this.extended.underlineColor
|
||||
? this.extended.underlineColor & Attributes.CM_MASK
|
||||
: this.getFgColorMode();
|
||||
}
|
||||
public isUnderlineColorRGB(): boolean {
|
||||
return (this.bg & BgFlags.HAS_EXTENDED) && ~this.extended.underlineColor
|
||||
? (this.extended.underlineColor & Attributes.CM_MASK) === Attributes.CM_RGB
|
||||
: this.isFgRGB();
|
||||
}
|
||||
public isUnderlineColorPalette(): boolean {
|
||||
return (this.bg & BgFlags.HAS_EXTENDED) && ~this.extended.underlineColor
|
||||
? (this.extended.underlineColor & Attributes.CM_MASK) === Attributes.CM_P16
|
||||
|| (this.extended.underlineColor & Attributes.CM_MASK) === Attributes.CM_P256
|
||||
: this.isFgPalette();
|
||||
}
|
||||
public isUnderlineColorDefault(): boolean {
|
||||
return (this.bg & BgFlags.HAS_EXTENDED) && ~this.extended.underlineColor
|
||||
? (this.extended.underlineColor & Attributes.CM_MASK) === 0
|
||||
: this.isFgDefault();
|
||||
}
|
||||
public getUnderlineStyle(): UnderlineStyle {
|
||||
return this.fg & FgFlags.UNDERLINE
|
||||
? (this.bg & BgFlags.HAS_EXTENDED ? this.extended.underlineStyle : UnderlineStyle.SINGLE)
|
||||
: UnderlineStyle.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extended attributes for a cell.
|
||||
* Holds information about different underline styles and color.
|
||||
*/
|
||||
export class ExtendedAttrs implements IExtendedAttrs {
|
||||
constructor(
|
||||
// underline style, NONE is empty
|
||||
public underlineStyle: UnderlineStyle = UnderlineStyle.NONE,
|
||||
// underline color, -1 is empty (same as FG)
|
||||
public underlineColor: number = -1
|
||||
) {}
|
||||
|
||||
public clone(): IExtendedAttrs {
|
||||
return new ExtendedAttrs(this.underlineStyle, this.underlineColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient method to indicate whether the object holds no additional information,
|
||||
* that needs to be persistant in the buffer.
|
||||
*/
|
||||
public isEmpty(): boolean {
|
||||
return this.underlineStyle === UnderlineStyle.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { reflowLargerApplyNewLayout, reflowLargerCreateNewLayout, reflowLargerGe
|
||||
import { Marker } from 'common/buffer/Marker';
|
||||
import { IOptionsService, IBufferService } from 'common/services/Services';
|
||||
import { DEFAULT_CHARSET } from 'common/data/Charsets';
|
||||
import { ExtendedAttrs } from 'common/buffer/AttributeData';
|
||||
|
||||
export const MAX_BUFFER_SIZE = 4294967295; // 2^32 - 1
|
||||
|
||||
@@ -60,9 +61,11 @@ export class Buffer implements IBuffer {
|
||||
if (attr) {
|
||||
this._nullCell.fg = attr.fg;
|
||||
this._nullCell.bg = attr.bg;
|
||||
this._nullCell.extended = attr.extended;
|
||||
} else {
|
||||
this._nullCell.fg = 0;
|
||||
this._nullCell.bg = 0;
|
||||
this._nullCell.extended = new ExtendedAttrs();
|
||||
}
|
||||
return this._nullCell;
|
||||
}
|
||||
@@ -71,9 +74,11 @@ export class Buffer implements IBuffer {
|
||||
if (attr) {
|
||||
this._whitespaceCell.fg = attr.fg;
|
||||
this._whitespaceCell.bg = attr.bg;
|
||||
this._whitespaceCell.extended = attr.extended;
|
||||
} else {
|
||||
this._whitespaceCell.fg = 0;
|
||||
this._whitespaceCell.bg = 0;
|
||||
this._whitespaceCell.extended = new ExtendedAttrs();
|
||||
}
|
||||
return this._whitespaceCell;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
import * as chai from 'chai';
|
||||
import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR, Content } from 'common/buffer/Constants';
|
||||
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, expect } from 'chai';
|
||||
import { AttributeData } from 'common/buffer/AttributeData';
|
||||
|
||||
|
||||
class TestBufferLine extends BufferLine {
|
||||
public get combined(): {[index: number]: string} {
|
||||
@@ -22,49 +24,147 @@ class TestBufferLine extends BufferLine {
|
||||
}
|
||||
}
|
||||
|
||||
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)]);
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
chai.assert.equal(cell.isCombined(), 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)]);
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
|
||||
chai.assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||||
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]);
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, '𝄞', 1, 0x1D11E]);
|
||||
chai.assert.equal(cell.isCombined(), 0);
|
||||
assert.deepEqual(cell.getAsCharData(), [123, '𝄞', 1, 0x1D11E]);
|
||||
assert.equal(cell.isCombined(), 0);
|
||||
// surrogate + combining
|
||||
cell.setFromCharData([123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]);
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]);
|
||||
chai.assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||||
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)]);
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, '1', 2, '1'.charCodeAt(0)]);
|
||||
chai.assert.equal(cell.isCombined(), 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);
|
||||
chai.expect(line.length).equals(0);
|
||||
chai.expect(line.isWrapped).equals(false);
|
||||
expect(line.length).equals(0);
|
||||
expect(line.isWrapped).equals(false);
|
||||
line = new TestBufferLine(10);
|
||||
chai.expect(line.length).equals(10);
|
||||
chai.expect(line.loadCell(0, new CellData()).getAsCharData()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
chai.expect(line.isWrapped).equals(false);
|
||||
expect(line.length).equals(10);
|
||||
expect(line.loadCell(0, new CellData()).getAsCharData()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
expect(line.isWrapped).equals(false);
|
||||
line = new TestBufferLine(10, undefined, true);
|
||||
chai.expect(line.length).equals(10);
|
||||
chai.expect(line.loadCell(0, new CellData()).getAsCharData()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
chai.expect(line.isWrapped).equals(true);
|
||||
expect(line.length).equals(10);
|
||||
expect(line.loadCell(0, new CellData()).getAsCharData()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
expect(line.isWrapped).equals(true);
|
||||
line = new TestBufferLine(10, CellData.fromCharData([123, 'a', 456, 'a'.charCodeAt(0)]), true);
|
||||
chai.expect(line.length).equals(10);
|
||||
chai.expect(line.loadCell(0, new CellData()).getAsCharData()).eql([123, 'a', 456, 'a'.charCodeAt(0)]);
|
||||
chai.expect(line.isWrapped).equals(true);
|
||||
expect(line.length).equals(10);
|
||||
expect(line.loadCell(0, new CellData()).getAsCharData()).eql([123, 'a', 456, 'a'.charCodeAt(0)]);
|
||||
expect(line.isWrapped).equals(true);
|
||||
});
|
||||
it('insertCells', function(): void {
|
||||
const line = new TestBufferLine(3);
|
||||
@@ -72,7 +172,7 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
chai.expect(line.toArray()).eql([
|
||||
expect(line.toArray()).eql([
|
||||
[1, 'a', 0, 'a'.charCodeAt(0)],
|
||||
[4, 'd', 0, 'd'.charCodeAt(0)],
|
||||
[4, 'd', 0, 'd'.charCodeAt(0)]
|
||||
@@ -86,7 +186,7 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
chai.expect(line.toArray()).eql([
|
||||
expect(line.toArray()).eql([
|
||||
[1, 'a', 0, 'a'.charCodeAt(0)],
|
||||
[4, 'd', 0, 'd'.charCodeAt(0)],
|
||||
[5, 'e', 0, 'e'.charCodeAt(0)],
|
||||
@@ -102,7 +202,7 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
chai.expect(line.toArray()).eql([
|
||||
expect(line.toArray()).eql([
|
||||
[1, 'a', 0, 'a'.charCodeAt(0)],
|
||||
[2, 'b', 0, 'b'.charCodeAt(0)],
|
||||
[6, 'f', 0, 'f'.charCodeAt(0)],
|
||||
@@ -118,7 +218,7 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
chai.expect(line.toArray()).eql([
|
||||
expect(line.toArray()).eql([
|
||||
[123, 'z', 0, 'z'.charCodeAt(0)],
|
||||
[123, 'z', 0, 'z'.charCodeAt(0)],
|
||||
[123, 'z', 0, 'z'.charCodeAt(0)],
|
||||
@@ -134,9 +234,9 @@ describe('BufferLine', function(): void {
|
||||
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();
|
||||
chai.expect(TestBufferLine.prototype.toArray.apply(line2)).eql(line.toArray());
|
||||
chai.expect(line2.length).equals(line.length);
|
||||
chai.expect(line2.isWrapped).equals(line.isWrapped);
|
||||
expect(TestBufferLine.prototype.toArray.apply(line2)).eql(line.toArray());
|
||||
expect(line2.length).equals(line.length);
|
||||
expect(line2.isWrapped).equals(line.isWrapped);
|
||||
});
|
||||
it('copyFrom', function(): void {
|
||||
const line = new TestBufferLine(5);
|
||||
@@ -147,92 +247,92 @@ describe('BufferLine', function(): void {
|
||||
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);
|
||||
chai.expect(line2.toArray()).eql(line.toArray());
|
||||
chai.expect(line2.length).equals(line.length);
|
||||
chai.expect(line2.isWrapped).equals(line.isWrapped);
|
||||
expect(line2.toArray()).eql(line.toArray());
|
||||
expect(line2.length).equals(line.length);
|
||||
expect(line2.isWrapped).equals(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)]));
|
||||
chai.expect(line.toArray()).eql([[1, 'e\u0301', 0, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]]);
|
||||
expect(line.toArray()).eql([[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);
|
||||
chai.expect(line2.toArray()).eql(line.toArray());
|
||||
expect(line2.toArray()).eql(line.toArray());
|
||||
const line3 = line.clone();
|
||||
chai.expect(TestBufferLine.prototype.toArray.apply(line3)).eql(line.toArray());
|
||||
expect(TestBufferLine.prototype.toArray.apply(line3)).eql(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)]));
|
||||
chai.expect(line.toArray()).eql((Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql((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)]));
|
||||
chai.expect(line.toArray()).eql((Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql((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)]));
|
||||
chai.expect(line.toArray()).eql((Array(5) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql((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)]));
|
||||
chai.expect(line.toArray()).eql((Array(0) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql((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) ]);
|
||||
chai.expect(line.translateToString()).eql('aa😁aaaaaa😁');
|
||||
chai.expect(Object.keys(line.combined).length).eql(2);
|
||||
expect(line.translateToString()).eql('aa😁aaaaaa😁');
|
||||
expect(Object.keys(line.combined).length).eql(2);
|
||||
line.resize(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
chai.expect(line.translateToString()).eql('aa😁aa');
|
||||
expect(line.translateToString()).eql('aa😁aa');
|
||||
line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
chai.expect(line.translateToString()).eql('aa😁aaaaaaa');
|
||||
chai.expect(Object.keys(line.combined).length).eql(1);
|
||||
expect(line.translateToString()).eql('aa😁aaaaaaa');
|
||||
expect(Object.keys(line.combined).length).eql(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);
|
||||
chai.expect(line.getTrimmedLength()).equal(0);
|
||||
expect(line.getTrimmedLength()).equal(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)]));
|
||||
chai.expect(line.getTrimmedLength()).equal(3);
|
||||
expect(line.getTrimmedLength()).equal(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)]));
|
||||
chai.expect(line.getTrimmedLength()).equal(3);
|
||||
expect(line.getTrimmedLength()).equal(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)]));
|
||||
chai.expect(line.getTrimmedLength()).equal(3);
|
||||
expect(line.getTrimmedLength()).equal(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]));
|
||||
chai.expect(line.getTrimmedLength()).equal(4); // also counts null cell after fullwidth
|
||||
expect(line.getTrimmedLength()).equal(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);
|
||||
chai.expect(line.translateToString(false)).equal(' ');
|
||||
chai.expect(line.translateToString(true)).equal('');
|
||||
expect(line.translateToString(false)).equal(' ');
|
||||
expect(line.translateToString(true)).equal('');
|
||||
});
|
||||
it('ASCII', function(): void {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
@@ -240,14 +340,14 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
chai.expect(line.translateToString(false)).equal('a a aa ');
|
||||
chai.expect(line.translateToString(true)).equal('a a aa');
|
||||
chai.expect(line.translateToString(false, 0, 5)).equal('a a a');
|
||||
chai.expect(line.translateToString(false, 0, 4)).equal('a a ');
|
||||
chai.expect(line.translateToString(false, 0, 3)).equal('a a');
|
||||
chai.expect(line.translateToString(true, 0, 5)).equal('a a a');
|
||||
chai.expect(line.translateToString(true, 0, 4)).equal('a a ');
|
||||
chai.expect(line.translateToString(true, 0, 3)).equal('a a');
|
||||
expect(line.translateToString(false)).equal('a a aa ');
|
||||
expect(line.translateToString(true)).equal('a a aa');
|
||||
expect(line.translateToString(false, 0, 5)).equal('a a a');
|
||||
expect(line.translateToString(false, 0, 4)).equal('a a ');
|
||||
expect(line.translateToString(false, 0, 3)).equal('a a');
|
||||
expect(line.translateToString(true, 0, 5)).equal('a a a');
|
||||
expect(line.translateToString(true, 0, 4)).equal('a a ');
|
||||
expect(line.translateToString(true, 0, 3)).equal('a a');
|
||||
|
||||
});
|
||||
it('surrogate', function(): void {
|
||||
@@ -256,14 +356,14 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
chai.expect(line.translateToString(false)).equal('a 𝄞 𝄞𝄞 ');
|
||||
chai.expect(line.translateToString(true)).equal('a 𝄞 𝄞𝄞');
|
||||
chai.expect(line.translateToString(false, 0, 5)).equal('a 𝄞 𝄞');
|
||||
chai.expect(line.translateToString(false, 0, 4)).equal('a 𝄞 ');
|
||||
chai.expect(line.translateToString(false, 0, 3)).equal('a 𝄞');
|
||||
chai.expect(line.translateToString(true, 0, 5)).equal('a 𝄞 𝄞');
|
||||
chai.expect(line.translateToString(true, 0, 4)).equal('a 𝄞 ');
|
||||
chai.expect(line.translateToString(true, 0, 3)).equal('a 𝄞');
|
||||
expect(line.translateToString(false)).equal('a 𝄞 𝄞𝄞 ');
|
||||
expect(line.translateToString(true)).equal('a 𝄞 𝄞𝄞');
|
||||
expect(line.translateToString(false, 0, 5)).equal('a 𝄞 𝄞');
|
||||
expect(line.translateToString(false, 0, 4)).equal('a 𝄞 ');
|
||||
expect(line.translateToString(false, 0, 3)).equal('a 𝄞');
|
||||
expect(line.translateToString(true, 0, 5)).equal('a 𝄞 𝄞');
|
||||
expect(line.translateToString(true, 0, 4)).equal('a 𝄞 ');
|
||||
expect(line.translateToString(true, 0, 3)).equal('a 𝄞');
|
||||
});
|
||||
it('combining', function(): void {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
@@ -271,14 +371,14 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
chai.expect(line.translateToString(false)).equal('a e\u0301 e\u0301e\u0301 ');
|
||||
chai.expect(line.translateToString(true)).equal('a e\u0301 e\u0301e\u0301');
|
||||
chai.expect(line.translateToString(false, 0, 5)).equal('a e\u0301 e\u0301');
|
||||
chai.expect(line.translateToString(false, 0, 4)).equal('a e\u0301 ');
|
||||
chai.expect(line.translateToString(false, 0, 3)).equal('a e\u0301');
|
||||
chai.expect(line.translateToString(true, 0, 5)).equal('a e\u0301 e\u0301');
|
||||
chai.expect(line.translateToString(true, 0, 4)).equal('a e\u0301 ');
|
||||
chai.expect(line.translateToString(true, 0, 3)).equal('a e\u0301');
|
||||
expect(line.translateToString(false)).equal('a e\u0301 e\u0301e\u0301 ');
|
||||
expect(line.translateToString(true)).equal('a e\u0301 e\u0301e\u0301');
|
||||
expect(line.translateToString(false, 0, 5)).equal('a e\u0301 e\u0301');
|
||||
expect(line.translateToString(false, 0, 4)).equal('a e\u0301 ');
|
||||
expect(line.translateToString(false, 0, 3)).equal('a e\u0301');
|
||||
expect(line.translateToString(true, 0, 5)).equal('a e\u0301 e\u0301');
|
||||
expect(line.translateToString(true, 0, 4)).equal('a e\u0301 ');
|
||||
expect(line.translateToString(true, 0, 3)).equal('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);
|
||||
@@ -289,20 +389,20 @@ describe('BufferLine', function(): void {
|
||||
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]));
|
||||
chai.expect(line.translateToString(false)).equal('a 1 11 ');
|
||||
chai.expect(line.translateToString(true)).equal('a 1 11');
|
||||
chai.expect(line.translateToString(false, 0, 7)).equal('a 1 1');
|
||||
chai.expect(line.translateToString(false, 0, 6)).equal('a 1 1');
|
||||
chai.expect(line.translateToString(false, 0, 5)).equal('a 1 ');
|
||||
chai.expect(line.translateToString(false, 0, 4)).equal('a 1');
|
||||
chai.expect(line.translateToString(false, 0, 3)).equal('a 1');
|
||||
chai.expect(line.translateToString(false, 0, 2)).equal('a ');
|
||||
chai.expect(line.translateToString(true, 0, 7)).equal('a 1 1');
|
||||
chai.expect(line.translateToString(true, 0, 6)).equal('a 1 1');
|
||||
chai.expect(line.translateToString(true, 0, 5)).equal('a 1 ');
|
||||
chai.expect(line.translateToString(true, 0, 4)).equal('a 1');
|
||||
chai.expect(line.translateToString(true, 0, 3)).equal('a 1');
|
||||
chai.expect(line.translateToString(true, 0, 2)).equal('a ');
|
||||
expect(line.translateToString(false)).equal('a 1 11 ');
|
||||
expect(line.translateToString(true)).equal('a 1 11');
|
||||
expect(line.translateToString(false, 0, 7)).equal('a 1 1');
|
||||
expect(line.translateToString(false, 0, 6)).equal('a 1 1');
|
||||
expect(line.translateToString(false, 0, 5)).equal('a 1 ');
|
||||
expect(line.translateToString(false, 0, 4)).equal('a 1');
|
||||
expect(line.translateToString(false, 0, 3)).equal('a 1');
|
||||
expect(line.translateToString(false, 0, 2)).equal('a ');
|
||||
expect(line.translateToString(true, 0, 7)).equal('a 1 1');
|
||||
expect(line.translateToString(true, 0, 6)).equal('a 1 1');
|
||||
expect(line.translateToString(true, 0, 5)).equal('a 1 ');
|
||||
expect(line.translateToString(true, 0, 4)).equal('a 1');
|
||||
expect(line.translateToString(true, 0, 3)).equal('a 1');
|
||||
expect(line.translateToString(true, 0, 2)).equal('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);
|
||||
@@ -311,21 +411,21 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
chai.expect(line.translateToString(false)).equal('a a aa ');
|
||||
chai.expect(line.translateToString(true)).equal('a a aa ');
|
||||
expect(line.translateToString(false)).equal('a a aa ');
|
||||
expect(line.translateToString(true)).equal('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);
|
||||
chai.expect(line.translateToString(false)).equal(' ');
|
||||
chai.expect(line.translateToString(true)).equal('');
|
||||
expect(line.translateToString(false)).equal(' ');
|
||||
expect(line.translateToString(true)).equal('');
|
||||
});
|
||||
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)]));
|
||||
chai.expect(line.translateToString(true, 0, 0)).equal('');
|
||||
expect(line.translateToString(true, 0, 0)).equal('');
|
||||
});
|
||||
});
|
||||
describe('addCharToCell', () => {
|
||||
@@ -335,9 +435,9 @@ describe('BufferLine', function(): void {
|
||||
const cell = line.loadCell(0, new CellData());
|
||||
// chars contains single combining char
|
||||
// width is set to 1
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [DEFAULT_ATTR, '\u0301', 1, 0x0301]);
|
||||
assert.deepEqual(cell.getAsCharData(), [DEFAULT_ATTR, '\u0301', 1, 0x0301]);
|
||||
// do not account a single combining char as combined
|
||||
chai.assert.equal(cell.isCombined(), 0);
|
||||
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);
|
||||
@@ -348,9 +448,9 @@ describe('BufferLine', function(): void {
|
||||
line.loadCell(0, cell);
|
||||
// chars contains 3 chars
|
||||
// width is set to 1
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301\u0301', 1, 0x0301]);
|
||||
assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301\u0301', 1, 0x0301]);
|
||||
// do not account a single combining char as combined
|
||||
chai.assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||||
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);
|
||||
@@ -361,9 +461,9 @@ describe('BufferLine', function(): void {
|
||||
line.loadCell(0, cell);
|
||||
// chars contains 2 chars
|
||||
// width is set to 1
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, 0x0301]);
|
||||
assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, 0x0301]);
|
||||
// do not account a single combining char as combined
|
||||
chai.assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||||
assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||||
});
|
||||
});
|
||||
describe('correct fullwidth handling', () => {
|
||||
@@ -377,83 +477,291 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), '¥¥¥¥ a');
|
||||
assert.equal(line.translateToString(), '¥¥¥¥ a');
|
||||
line.insertCells(8, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), '¥¥¥¥a ');
|
||||
assert.equal(line.translateToString(), '¥¥¥¥a ');
|
||||
line.insertCells(1, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), ' a ¥¥¥a');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaa¥¥¥ ');
|
||||
assert.equal(line.translateToString(), 'aaa¥¥¥ ');
|
||||
line.insertCells(4, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaa a ¥¥');
|
||||
assert.equal(line.translateToString(), 'aaa a ¥¥');
|
||||
line.insertCells(4, 1, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaa aa ¥ ');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), ' ¥¥¥¥a');
|
||||
assert.equal(line.translateToString(), ' ¥¥¥¥a');
|
||||
line.deleteCells(5, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), ' ¥¥¥aaa');
|
||||
assert.equal(line.translateToString(), ' ¥¥¥aaa');
|
||||
line.deleteCells(0, 2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.assert.equal(line.translateToString(), ' ¥¥aaaaa');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), 'a ¥¥¥¥');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), 'aa¥¥¥¥');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaa ¥¥¥');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaaaaaaa¥');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaaaaaaaa ');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), 'aaaaaaaaaa');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), ' a¥¥¥¥');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), ' aa ¥¥¥');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), ' aaa¥¥¥');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), ' aaaaaaa¥');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), ' aaaaaaaa ');
|
||||
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)]));
|
||||
chai.assert.equal(line.translateToString(), ' aaaaaaaaa');
|
||||
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]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { CharData, IBufferLine, ICellData, IAttributeData } from 'common/Types';
|
||||
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 } 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 } from 'common/buffer/Constants';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { AttributeData } from 'common/buffer/AttributeData';
|
||||
import { AttributeData, ExtendedAttrs } from 'common/buffer/AttributeData';
|
||||
|
||||
/**
|
||||
* buffer memory layout:
|
||||
@@ -55,6 +55,7 @@ export const DEFAULT_ATTR_DATA = Object.freeze(new AttributeData());
|
||||
export class BufferLine implements IBufferLine {
|
||||
protected _data: Uint32Array;
|
||||
protected _combined: {[index: number]: string} = {};
|
||||
protected _extendedAttrs: {[index: number]: ExtendedAttrs} = {};
|
||||
public length: number;
|
||||
|
||||
constructor(cols: number, fillCellData?: ICellData, public isWrapped: boolean = false) {
|
||||
@@ -174,6 +175,9 @@ export class BufferLine implements IBufferLine {
|
||||
if (cell.content & Content.IS_COMBINED_MASK) {
|
||||
cell.combinedData = this._combined[index];
|
||||
}
|
||||
if (cell.bg & BgFlags.HAS_EXTENDED) {
|
||||
cell.extended = this._extendedAttrs[index];
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
@@ -184,6 +188,9 @@ export class BufferLine implements IBufferLine {
|
||||
if (cell.content & Content.IS_COMBINED_MASK) {
|
||||
this._combined[index] = cell.combinedData;
|
||||
}
|
||||
if (cell.bg & BgFlags.HAS_EXTENDED) {
|
||||
this._extendedAttrs[index] = cell.extended;
|
||||
}
|
||||
this._data[index * CELL_SIZE + Cell.CONTENT] = cell.content;
|
||||
this._data[index * CELL_SIZE + Cell.FG] = cell.fg;
|
||||
this._data[index * CELL_SIZE + Cell.BG] = cell.bg;
|
||||
@@ -194,7 +201,10 @@ export class BufferLine implements IBufferLine {
|
||||
* Since the input handler see the incoming chars as UTF32 codepoints,
|
||||
* it gets an optimized access method.
|
||||
*/
|
||||
public setCellFromCodePoint(index: number, codePoint: number, width: number, fg: number, bg: number): void {
|
||||
public setCellFromCodePoint(index: number, codePoint: number, width: number, fg: number, bg: number, eAttrs: IExtendedAttrs): void {
|
||||
if (bg & BgFlags.HAS_EXTENDED) {
|
||||
this._extendedAttrs[index] = eAttrs;
|
||||
}
|
||||
this._data[index * CELL_SIZE + Cell.CONTENT] = codePoint | (width << Content.WIDTH_SHIFT);
|
||||
this._data[index * CELL_SIZE + Cell.FG] = fg;
|
||||
this._data[index * CELL_SIZE + Cell.BG] = bg;
|
||||
@@ -233,7 +243,7 @@ export class BufferLine implements IBufferLine {
|
||||
|
||||
// handle fullwidth at pos: reset cell one to the left if pos is second cell of a wide char
|
||||
if (pos && this.getWidth(pos - 1) === 2) {
|
||||
this.setCellFromCodePoint(pos - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||
this.setCellFromCodePoint(pos - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
||||
}
|
||||
|
||||
if (n < this.length - pos) {
|
||||
@@ -252,7 +262,7 @@ export class BufferLine implements IBufferLine {
|
||||
|
||||
// handle fullwidth at line end: reset last cell if it is first cell of a wide char
|
||||
if (this.getWidth(this.length - 1) === 2) {
|
||||
this.setCellFromCodePoint(this.length - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||
this.setCellFromCodePoint(this.length - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,21 +286,21 @@ export class BufferLine implements IBufferLine {
|
||||
// - reset pos-1 if wide char
|
||||
// - reset pos if width==0 (previous second cell of a wide char)
|
||||
if (pos && this.getWidth(pos - 1) === 2) {
|
||||
this.setCellFromCodePoint(pos - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||
this.setCellFromCodePoint(pos - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
||||
}
|
||||
if (this.getWidth(pos) === 0 && !this.hasContent(pos)) {
|
||||
this.setCellFromCodePoint(pos, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||
this.setCellFromCodePoint(pos, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
||||
}
|
||||
}
|
||||
|
||||
public replaceCells(start: number, end: number, fillCellData: ICellData, eraseAttr?: IAttributeData): void {
|
||||
// handle fullwidth at start: reset cell one to the left if start is second cell of a wide char
|
||||
if (start && this.getWidth(start - 1) === 2) {
|
||||
this.setCellFromCodePoint(start - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||
this.setCellFromCodePoint(start - 1, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
||||
}
|
||||
// handle fullwidth at last cell + 1: reset to empty cell if it is second part of a wide char
|
||||
if (end < this.length && this.getWidth(end - 1) === 2) {
|
||||
this.setCellFromCodePoint(end, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0);
|
||||
this.setCellFromCodePoint(end, 0, 1, eraseAttr?.fg || 0, eraseAttr?.bg || 0, eraseAttr?.extended || new ExtendedAttrs());
|
||||
}
|
||||
|
||||
while (start < end && start < this.length) {
|
||||
@@ -320,7 +330,7 @@ export class BufferLine implements IBufferLine {
|
||||
const data = new Uint32Array(cols * CELL_SIZE);
|
||||
data.set(this._data.subarray(0, cols * CELL_SIZE));
|
||||
this._data = data;
|
||||
// Remove any cut off combined data
|
||||
// Remove any cut off combined data, FIXME: repeat this for extended attrs
|
||||
const keys = Object.keys(this._combined);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = parseInt(keys[i], 10);
|
||||
@@ -339,6 +349,7 @@ export class BufferLine implements IBufferLine {
|
||||
/** fill a line with fillCharData */
|
||||
public fill(fillCellData: ICellData): void {
|
||||
this._combined = {};
|
||||
this._extendedAttrs = {};
|
||||
for (let i = 0; i < this.length; ++i) {
|
||||
this.setCell(i, fillCellData);
|
||||
}
|
||||
@@ -357,6 +368,10 @@ export class BufferLine implements IBufferLine {
|
||||
for (const el in line._combined) {
|
||||
this._combined[el] = line._combined[el];
|
||||
}
|
||||
this._extendedAttrs = {};
|
||||
for (const el in line._extendedAttrs) {
|
||||
this._extendedAttrs[el] = line._extendedAttrs[el];
|
||||
}
|
||||
this.isWrapped = line.isWrapped;
|
||||
}
|
||||
|
||||
@@ -368,6 +383,9 @@ export class BufferLine implements IBufferLine {
|
||||
for (const el in this._combined) {
|
||||
newLine._combined[el] = this._combined[el];
|
||||
}
|
||||
for (const el in this._extendedAttrs) {
|
||||
newLine._extendedAttrs[el] = this._extendedAttrs[el];
|
||||
}
|
||||
newLine.isWrapped = this.isWrapped;
|
||||
return newLine;
|
||||
}
|
||||
@@ -397,7 +415,7 @@ export class BufferLine implements IBufferLine {
|
||||
}
|
||||
}
|
||||
|
||||
// Move any combined data over as needed
|
||||
// Move any combined data over as needed, FIXME: repeat for extended attrs
|
||||
const srcCombinedKeys = Object.keys(src._combined);
|
||||
for (let i = 0; i < srcCombinedKeys.length; i++) {
|
||||
const key = parseInt(srcCombinedKeys[i], 10);
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { CharData, ICellData } from 'common/Types';
|
||||
import { CharData, ICellData, IExtendedAttrs } from 'common/Types';
|
||||
import { stringFromCodePoint } from 'common/input/TextDecoder';
|
||||
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, Content } from 'common/buffer/Constants';
|
||||
import { AttributeData } from 'common/buffer/AttributeData';
|
||||
import { AttributeData, ExtendedAttrs } from 'common/buffer/AttributeData';
|
||||
|
||||
/**
|
||||
* CellData - represents a single Cell in the terminal buffer.
|
||||
@@ -19,10 +19,11 @@ export class CellData extends AttributeData implements ICellData {
|
||||
return obj;
|
||||
}
|
||||
/** Primitives from terminal buffer. */
|
||||
public content: number = 0;
|
||||
public fg: number = 0;
|
||||
public bg: number = 0;
|
||||
public combinedData: string = '';
|
||||
public content = 0;
|
||||
public fg = 0;
|
||||
public bg = 0;
|
||||
public extended: IExtendedAttrs = new ExtendedAttrs();
|
||||
public combinedData = '';
|
||||
/** Whether cell contains a combined string. */
|
||||
public isCombined(): number {
|
||||
return this.content & Content.IS_COMBINED_MASK;
|
||||
|
||||
@@ -121,8 +121,18 @@ export const enum FgFlags {
|
||||
|
||||
export const enum BgFlags {
|
||||
/**
|
||||
* bit 27..32 (upper 4 unused)
|
||||
* bit 27..32 (upper 3 unused)
|
||||
*/
|
||||
ITALIC = 0x4000000,
|
||||
DIM = 0x8000000
|
||||
DIM = 0x8000000,
|
||||
HAS_EXTENDED = 0x10000000
|
||||
}
|
||||
|
||||
export const enum UnderlineStyle {
|
||||
NONE = 0,
|
||||
SINGLE = 1,
|
||||
DOUBLE = 2,
|
||||
CURLY = 3,
|
||||
DOTTED = 4,
|
||||
DASHED = 5
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user