underline color support, test cases

This commit is contained in:
Jörg Breitbart
2020-03-08 22:56:58 +01:00
parent 058f33cd7b
commit 120527cb41
5 changed files with 228 additions and 32 deletions
+104 -1
View File
@@ -1445,7 +1445,7 @@ describe('InputHandler', () => {
assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥ ¥', '¥¥¥¥¥', '']);
});
});
describe('extended SGR 4 support', () => {
describe('extended underline style support (SGR 4)', () => {
let term: TestTerminal;
beforeEach(() => {
term = new TestTerminal({cols: 10, rows: 5});
@@ -1519,4 +1519,107 @@ describe('InputHandler', () => {
assert.equal(term.curAttrData.getUnderlineStyle(), UnderlineStyle.SINGLE);
});
});
describe('underline colors (SGR 58 & SGR 59)', () => {
let term: TestTerminal;
beforeEach(() => {
term = new TestTerminal({cols: 10, rows: 5});
});
it('defaults to FG color', () => {
for (const s of ['', '\x1b[30m', '\x1b[38;510m', '\x1b[38;2;1;2;3m']) {
term.writeSync(s);
assert.equal(term.curAttrData.getUnderlineColor(), term.curAttrData.getFgColor());
assert.equal(term.curAttrData.getUnderlineColorMode(), term.curAttrData.getFgColorMode());
assert.equal(term.curAttrData.isUnderlineColorRGB(), term.curAttrData.isFgRGB());
assert.equal(term.curAttrData.isUnderlineColorPalette(), term.curAttrData.isFgPalette());
assert.equal(term.curAttrData.isUnderlineColorDefault(), term.curAttrData.isFgDefault());
}
});
it('correctly sets P256/RGB colors', () => {
term.writeSync('\x1b[4m');
term.writeSync('\x1b[58;5;123m');
assert.equal(term.curAttrData.getUnderlineColor(), 123);
assert.equal(term.curAttrData.getUnderlineColorMode(), Attributes.CM_P256);
assert.equal(term.curAttrData.isUnderlineColorRGB(), false);
assert.equal(term.curAttrData.isUnderlineColorPalette(), true);
assert.equal(term.curAttrData.isUnderlineColorDefault(), false);
term.writeSync('\x1b[58;2::1:2:3m');
assert.equal(term.curAttrData.getUnderlineColor(), (1 << 16) | (2 << 8) | 3);
assert.equal(term.curAttrData.getUnderlineColorMode(), Attributes.CM_RGB);
assert.equal(term.curAttrData.isUnderlineColorRGB(), true);
assert.equal(term.curAttrData.isUnderlineColorPalette(), false);
assert.equal(term.curAttrData.isUnderlineColorDefault(), false);
});
it('P256/RGB persistence', () => {
const cell = new CellData();
term.writeSync('\x1b[4m');
term.writeSync('\x1b[58;5;123m');
assert.equal(term.curAttrData.getUnderlineColor(), 123);
assert.equal(term.curAttrData.getUnderlineColorMode(), Attributes.CM_P256);
assert.equal(term.curAttrData.isUnderlineColorRGB(), false);
assert.equal(term.curAttrData.isUnderlineColorPalette(), true);
assert.equal(term.curAttrData.isUnderlineColorDefault(), false);
term.writeSync('ab');
(term as any)._bufferService.buffers.active.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);
term.writeSync('\x1b[4:0m');
assert.equal(term.curAttrData.getUnderlineColor(), term.curAttrData.getFgColor());
assert.equal(term.curAttrData.getUnderlineColorMode(), term.curAttrData.getFgColorMode());
assert.equal(term.curAttrData.isUnderlineColorRGB(), term.curAttrData.isFgRGB());
assert.equal(term.curAttrData.isUnderlineColorPalette(), term.curAttrData.isFgPalette());
assert.equal(term.curAttrData.isUnderlineColorDefault(), term.curAttrData.isFgDefault());
term.writeSync('a');
(term as any)._bufferService.buffers.active.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);
(term as any)._bufferService.buffers.active.lines.get(0).loadCell(2, cell);
assert.equal(cell.getUnderlineColor(), term.curAttrData.getFgColor());
assert.equal(cell.getUnderlineColorMode(), term.curAttrData.getFgColorMode());
assert.equal(cell.isUnderlineColorRGB(), term.curAttrData.isFgRGB());
assert.equal(cell.isUnderlineColorPalette(), term.curAttrData.isFgPalette());
assert.equal(cell.isUnderlineColorDefault(), term.curAttrData.isFgDefault());
term.writeSync('\x1b[4m');
term.writeSync('\x1b[58;2::1:2:3m');
assert.equal(term.curAttrData.getUnderlineColor(), (1 << 16) | (2 << 8) | 3);
assert.equal(term.curAttrData.getUnderlineColorMode(), Attributes.CM_RGB);
assert.equal(term.curAttrData.isUnderlineColorRGB(), true);
assert.equal(term.curAttrData.isUnderlineColorPalette(), false);
assert.equal(term.curAttrData.isUnderlineColorDefault(), false);
term.writeSync('a');
term.writeSync('\x1b[24m');
(term as any)._bufferService.buffers.active.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);
(term as any)._bufferService.buffers.active.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(
(term as any)._bufferService.buffers.active.lines.get(0)._extendedAttrs[0],
(term as any)._bufferService.buffers.active.lines.get(0)._extendedAttrs[1]
);
// should not have written eAttr for pos 2 in the buffer
assert.equal((term as any)._bufferService.buffers.active.lines.get(0)._extendedAttrs[2], undefined);
// eAttrs in buffer pos 1 and pos 3 must be different objs
assert.notEqual(
(term as any)._bufferService.buffers.active.lines.get(0)._extendedAttrs[1],
(term as any)._bufferService.buffers.active.lines.get(0)._extendedAttrs[3]
);
});
});
});
+31 -24
View File
@@ -2026,6 +2026,21 @@ export class InputHandler extends Disposable implements IInputHandler {
}
}
/**
* Helper to write color information packed with color mode.
*/
private _updateColor(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.
@@ -2075,24 +2090,16 @@ 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._updateColor(attr.fg, accu[1], accu[3], accu[4], accu[5]);
break;
case 48:
attr.bg = this._updateColor(attr.bg, accu[1], accu[3], accu[4], accu[5]);
break;
case 58:
attr.extended = attr.extended.clone();
attr.extended.underlineColor = this._updateColor(attr.extended.underlineColor, accu[1], accu[3], accu[4], accu[5]);
}
return advance;
@@ -2125,11 +2132,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
// update HAS_EXTENDED in BG
if (attr.extended.isEmpty()) {
attr.bg &= ~BgFlags.HAS_EXTENDED;
} else {
attr.bg |= BgFlags.HAS_EXTENDED;
}
attr.updateExtended();
}
/**
@@ -2300,9 +2303,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);
+5
View File
@@ -109,7 +109,12 @@ export interface IAttributeData {
// extended attrs
hasExtendedAttrs(): number;
updateExtended(): void;
getUnderlineColor(): number;
getUnderlineColorMode(): number;
isUnderlineColorRGB(): boolean;
isUnderlineColorPalette(): boolean;
isUnderlineColorDefault(): boolean;
getUnderlineStyle(): number;
}
+37 -5
View File
@@ -73,12 +73,45 @@ export class AttributeData implements IAttributeData {
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) {
return 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)
@@ -104,11 +137,10 @@ export class ExtendedAttrs implements IExtendedAttrs {
}
/**
* Convenient method to indicate whether the object holds no additional information
* and can be removed from the global attr object.
* Convenient method to indicate whether the object holds no additional information,
* that needs to be persistant in the buffer.
*/
public isEmpty(): boolean {
// needs to test for every single attribute stored
return !(this.underlineStyle !== UnderlineStyle.NONE || ~this.underlineColor);
return this.underlineStyle === UnderlineStyle.NONE;
}
}
+51 -2
View File
@@ -32,9 +32,27 @@ describe('AttributeData', () => {
attrs.bg |= BgFlags.HAS_EXTENDED;
assert.equal(!!attrs.hasExtendedAttrs(), true);
});
it('getUnderlineColor', () => {
it('getUnderlineColor - P256', () => {
const attrs = new AttributeData();
attrs.extended.underlineColor = (1 << 16) | (2 << 8) | 3;
// 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);
@@ -48,6 +66,37 @@ describe('AttributeData', () => {
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();