RGB support in buffer and attributes
This commit is contained in:
+4
-4
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { assert, expect } from 'chai';
|
import { assert, expect } from 'chai';
|
||||||
import { ITerminal } from './Types';
|
import { ITerminal } from './Types';
|
||||||
import { Buffer, DEFAULT_ATTR } from './Buffer';
|
import { Buffer, DEFAULT_ATTR_DATA } from './Buffer';
|
||||||
import { CircularList } from './common/CircularList';
|
import { CircularList } from './common/CircularList';
|
||||||
import { MockTerminal, TestTerminal } from './ui/TestUtils.test';
|
import { MockTerminal, TestTerminal } from './ui/TestUtils.test';
|
||||||
import { BufferLine, CellData } from './BufferLine';
|
import { BufferLine, CellData } from './BufferLine';
|
||||||
@@ -37,7 +37,7 @@ describe('Buffer', () => {
|
|||||||
|
|
||||||
describe('fillViewportRows', () => {
|
describe('fillViewportRows', () => {
|
||||||
it('should fill the buffer with blank lines based on the size of the viewport', () => {
|
it('should fill the buffer with blank lines based on the size of the viewport', () => {
|
||||||
const blankLineChar = buffer.getBlankLine(DEFAULT_ATTR).loadCell(0, new CellData()).asCharData;
|
const blankLineChar = buffer.getBlankLine(DEFAULT_ATTR_DATA).loadCell(0, new CellData()).asCharData;
|
||||||
buffer.fillViewportRows();
|
buffer.fillViewportRows();
|
||||||
assert.equal(buffer.lines.length, INIT_ROWS);
|
assert.equal(buffer.lines.length, INIT_ROWS);
|
||||||
for (let y = 0; y < INIT_ROWS; y++) {
|
for (let y = 0; y < INIT_ROWS; y++) {
|
||||||
@@ -184,7 +184,7 @@ describe('Buffer', () => {
|
|||||||
buffer.fillViewportRows();
|
buffer.fillViewportRows();
|
||||||
// Create 10 extra blank lines
|
// Create 10 extra blank lines
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR));
|
buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||||
}
|
}
|
||||||
// Set cursor to the bottom of the buffer
|
// Set cursor to the bottom of the buffer
|
||||||
buffer.y = INIT_ROWS - 1;
|
buffer.y = INIT_ROWS - 1;
|
||||||
@@ -204,7 +204,7 @@ describe('Buffer', () => {
|
|||||||
buffer.fillViewportRows();
|
buffer.fillViewportRows();
|
||||||
// Create 10 extra blank lines
|
// Create 10 extra blank lines
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR));
|
buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||||
}
|
}
|
||||||
// Set cursor to the bottom of the buffer
|
// Set cursor to the bottom of the buffer
|
||||||
buffer.y = INIT_ROWS - 1;
|
buffer.y = INIT_ROWS - 1;
|
||||||
|
|||||||
+27
-14
@@ -4,13 +4,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CircularList } from './common/CircularList';
|
import { CircularList } from './common/CircularList';
|
||||||
import { ITerminal, IBuffer, IBufferLine, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult, ICellData } from './Types';
|
import { ITerminal, IBuffer, IBufferLine, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult, ICellData, IAttributeData } from './Types';
|
||||||
import { EventEmitter } from './common/EventEmitter';
|
import { EventEmitter } from './common/EventEmitter';
|
||||||
import { IMarker } from 'xterm';
|
import { IMarker } from 'xterm';
|
||||||
import { BufferLine, CellData } from './BufferLine';
|
import { BufferLine, CellData, AttributeData } from './BufferLine';
|
||||||
import { DEFAULT_COLOR } from './renderer/atlas/Types';
|
import { DEFAULT_COLOR } from './renderer/atlas/Types';
|
||||||
|
|
||||||
export const DEFAULT_ATTR = (0 << 18) | (DEFAULT_COLOR << 9) | (256 << 0);
|
export const DEFAULT_ATTR = (0 << 18) | (DEFAULT_COLOR << 9) | (256 << 0);
|
||||||
|
|
||||||
|
export const DEFAULT_ATTR_DATA = new AttributeData();
|
||||||
|
|
||||||
export const CHAR_DATA_ATTR_INDEX = 0;
|
export const CHAR_DATA_ATTR_INDEX = 0;
|
||||||
export const CHAR_DATA_CHAR_INDEX = 1;
|
export const CHAR_DATA_CHAR_INDEX = 1;
|
||||||
export const CHAR_DATA_WIDTH_INDEX = 2;
|
export const CHAR_DATA_WIDTH_INDEX = 2;
|
||||||
@@ -43,7 +46,7 @@ export class Buffer implements IBuffer {
|
|||||||
public tabs: any;
|
public tabs: any;
|
||||||
public savedY: number;
|
public savedY: number;
|
||||||
public savedX: number;
|
public savedX: number;
|
||||||
public savedCurAttr: number;
|
public savedCurAttrData = DEFAULT_ATTR_DATA.clone();
|
||||||
public markers: Marker[] = [];
|
public markers: Marker[] = [];
|
||||||
private _nullCell: ICellData = CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
private _nullCell: ICellData = CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||||
private _whitespaceCell: ICellData = CellData.fromCharData([0, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE]);
|
private _whitespaceCell: ICellData = CellData.fromCharData([0, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE]);
|
||||||
@@ -61,19 +64,29 @@ export class Buffer implements IBuffer {
|
|||||||
this.clear();
|
this.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getNullCell(fg: number = 0, bg: number = 0): ICellData {
|
public getNullCell(attr?: IAttributeData): ICellData {
|
||||||
this._nullCell.fg = fg;
|
if (attr) {
|
||||||
this._nullCell.bg = bg;
|
this._nullCell.fg = attr.fg;
|
||||||
|
this._nullCell.bg = attr.bg;
|
||||||
|
} else {
|
||||||
|
this._nullCell.fg = 0;
|
||||||
|
this._nullCell.bg = 0;
|
||||||
|
}
|
||||||
return this._nullCell;
|
return this._nullCell;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getWhitespaceCell(fg: number = 0, bg: number = 0): ICellData {
|
public getWhitespaceCell(attr?: IAttributeData): ICellData {
|
||||||
this._whitespaceCell.fg = fg;
|
if (attr) {
|
||||||
this._whitespaceCell.bg = bg;
|
this._whitespaceCell.fg = attr.fg;
|
||||||
|
this._whitespaceCell.bg = attr.bg;
|
||||||
|
} else {
|
||||||
|
this._whitespaceCell.fg = 0;
|
||||||
|
this._whitespaceCell.bg = 0;
|
||||||
|
}
|
||||||
return this._whitespaceCell;
|
return this._whitespaceCell;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getBlankLine(attr: number, isWrapped?: boolean): IBufferLine {
|
public getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine {
|
||||||
return new BufferLine(this._terminal.cols, this.getNullCell(attr), isWrapped);
|
return new BufferLine(this._terminal.cols, this.getNullCell(attr), isWrapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,10 +118,10 @@ export class Buffer implements IBuffer {
|
|||||||
/**
|
/**
|
||||||
* Fills the buffer's viewport with blank lines.
|
* Fills the buffer's viewport with blank lines.
|
||||||
*/
|
*/
|
||||||
public fillViewportRows(fillAttr?: number): void {
|
public fillViewportRows(fillAttr?: IAttributeData): void {
|
||||||
if (this.lines.length === 0) {
|
if (this.lines.length === 0) {
|
||||||
if (fillAttr === undefined) {
|
if (fillAttr === undefined) {
|
||||||
fillAttr = DEFAULT_ATTR;
|
fillAttr = DEFAULT_ATTR_DATA;
|
||||||
}
|
}
|
||||||
let i = this._terminal.rows;
|
let i = this._terminal.rows;
|
||||||
while (i--) {
|
while (i--) {
|
||||||
@@ -149,7 +162,7 @@ export class Buffer implements IBuffer {
|
|||||||
if (this.lines.length > 0) {
|
if (this.lines.length > 0) {
|
||||||
// Deal with columns increasing (we don't do anything when columns reduce)
|
// Deal with columns increasing (we don't do anything when columns reduce)
|
||||||
if (this._terminal.cols < newCols) {
|
if (this._terminal.cols < newCols) {
|
||||||
const cell = this.getNullCell(DEFAULT_ATTR); // does xterm use the default attr?
|
const cell = this.getNullCell(DEFAULT_ATTR_DATA); // does xterm use the default attr?
|
||||||
for (let i = 0; i < this.lines.length; i++) {
|
for (let i = 0; i < this.lines.length; i++) {
|
||||||
this.lines.get(i).resize(newCols, cell);
|
this.lines.get(i).resize(newCols, cell);
|
||||||
}
|
}
|
||||||
@@ -172,7 +185,7 @@ export class Buffer implements IBuffer {
|
|||||||
} else {
|
} else {
|
||||||
// Add a blank line if there is no buffer left at the top to scroll to, or if there
|
// Add a blank line if there is no buffer left at the top to scroll to, or if there
|
||||||
// are blank lines after the cursor
|
// are blank lines after the cursor
|
||||||
this.lines.push(new BufferLine(newCols, this.getNullCell(DEFAULT_ATTR)));
|
this.lines.push(new BufferLine(newCols, this.getNullCell(DEFAULT_ATTR_DATA)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,23 +24,23 @@ describe('CellData', () => {
|
|||||||
// ASCII
|
// ASCII
|
||||||
cell.setFromCharData([123, 'a', 1, 'a'.charCodeAt(0)]);
|
cell.setFromCharData([123, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
chai.assert.deepEqual(cell.asCharData, [123, 'a', 1, 'a'.charCodeAt(0)]);
|
chai.assert.deepEqual(cell.asCharData, [123, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
chai.assert.equal(cell.combined, 0);
|
chai.assert.equal(cell.isCombined, 0);
|
||||||
// combining
|
// combining
|
||||||
cell.setFromCharData([123, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
|
cell.setFromCharData([123, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
|
||||||
chai.assert.deepEqual(cell.asCharData, [123, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
|
chai.assert.deepEqual(cell.asCharData, [123, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
|
||||||
chai.assert.equal(cell.combined, Content.IS_COMBINED);
|
chai.assert.equal(cell.isCombined, Content.IS_COMBINED);
|
||||||
// surrogate
|
// surrogate
|
||||||
cell.setFromCharData([123, '𝄞', 1, 0x1D11E]);
|
cell.setFromCharData([123, '𝄞', 1, 0x1D11E]);
|
||||||
chai.assert.deepEqual(cell.asCharData, [123, '𝄞', 1, 0x1D11E]);
|
chai.assert.deepEqual(cell.asCharData, [123, '𝄞', 1, 0x1D11E]);
|
||||||
chai.assert.equal(cell.combined, 0);
|
chai.assert.equal(cell.isCombined, 0);
|
||||||
// surrogate + combining
|
// surrogate + combining
|
||||||
cell.setFromCharData([123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]);
|
cell.setFromCharData([123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]);
|
||||||
chai.assert.deepEqual(cell.asCharData, [123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]);
|
chai.assert.deepEqual(cell.asCharData, [123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]);
|
||||||
chai.assert.equal(cell.combined, Content.IS_COMBINED);
|
chai.assert.equal(cell.isCombined, Content.IS_COMBINED);
|
||||||
// wide char
|
// wide char
|
||||||
cell.setFromCharData([123, '1', 2, '1'.charCodeAt(0)]);
|
cell.setFromCharData([123, '1', 2, '1'.charCodeAt(0)]);
|
||||||
chai.assert.deepEqual(cell.asCharData, [123, '1', 2, '1'.charCodeAt(0)]);
|
chai.assert.deepEqual(cell.asCharData, [123, '1', 2, '1'.charCodeAt(0)]);
|
||||||
chai.assert.equal(cell.combined, 0);
|
chai.assert.equal(cell.isCombined, 0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -366,7 +366,7 @@ describe('BufferLine', function(): void {
|
|||||||
// width is set to 1
|
// width is set to 1
|
||||||
chai.assert.deepEqual(cell.asCharData, [DEFAULT_ATTR, '\u0301', 1, 0x0301]);
|
chai.assert.deepEqual(cell.asCharData, [DEFAULT_ATTR, '\u0301', 1, 0x0301]);
|
||||||
// do not account a single combining char as combined
|
// do not account a single combining char as combined
|
||||||
chai.assert.equal(cell.combined, 0);
|
chai.assert.equal(cell.isCombined, 0);
|
||||||
});
|
});
|
||||||
it('should add char to combining string in cell', () => {
|
it('should add char to combining string in cell', () => {
|
||||||
const line = new TestBufferLine(3, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
const line = new TestBufferLine(3, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||||
@@ -379,7 +379,7 @@ describe('BufferLine', function(): void {
|
|||||||
// width is set to 1
|
// width is set to 1
|
||||||
chai.assert.deepEqual(cell.asCharData, [123, 'e\u0301\u0301', 1, 0x0301]);
|
chai.assert.deepEqual(cell.asCharData, [123, 'e\u0301\u0301', 1, 0x0301]);
|
||||||
// do not account a single combining char as combined
|
// do not account a single combining char as combined
|
||||||
chai.assert.equal(cell.combined, Content.IS_COMBINED);
|
chai.assert.equal(cell.isCombined, Content.IS_COMBINED);
|
||||||
});
|
});
|
||||||
it('should create combining string on taken cell', () => {
|
it('should create combining string on taken cell', () => {
|
||||||
const line = new TestBufferLine(3, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
const line = new TestBufferLine(3, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||||
@@ -392,7 +392,7 @@ describe('BufferLine', function(): void {
|
|||||||
// width is set to 1
|
// width is set to 1
|
||||||
chai.assert.deepEqual(cell.asCharData, [123, 'e\u0301', 1, 0x0301]);
|
chai.assert.deepEqual(cell.asCharData, [123, 'e\u0301', 1, 0x0301]);
|
||||||
// do not account a single combining char as combined
|
// do not account a single combining char as combined
|
||||||
chai.assert.equal(cell.combined, Content.IS_COMBINED);
|
chai.assert.equal(cell.isCombined, Content.IS_COMBINED);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+236
-4
@@ -2,11 +2,67 @@
|
|||||||
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
import { CharData, IBufferLine, ICellData } from './Types';
|
import { CharData, IBufferLine, ICellData, IColorRGB, IAttributeData } from './Types';
|
||||||
import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, WHITESPACE_CELL_CHAR, CHAR_DATA_ATTR_INDEX } from './Buffer';
|
import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, WHITESPACE_CELL_CHAR, CHAR_DATA_ATTR_INDEX } from './Buffer';
|
||||||
import { stringFromCodePoint } from './core/input/TextDecoder';
|
import { stringFromCodePoint } from './core/input/TextDecoder';
|
||||||
|
import { FLAGS } from './renderer/Types';
|
||||||
|
import { DEFAULT_ANSI_COLORS } from './renderer/ColorManager';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO:
|
||||||
|
* The below color-related code can be removed when true color is implemented.
|
||||||
|
* It's only purpose is to match true color requests with the closest matching
|
||||||
|
* ANSI color code.
|
||||||
|
*/
|
||||||
|
const matchColorCache: {[colorRGBHash: number]: number} = {};
|
||||||
|
|
||||||
|
// http://stackoverflow.com/questions/1633828
|
||||||
|
function matchColorDistance(r1: number, g1: number, b1: number, r2: number, g2: number, b2: number): number {
|
||||||
|
return Math.pow(30 * (r1 - r2), 2)
|
||||||
|
+ Math.pow(59 * (g1 - g2), 2)
|
||||||
|
+ Math.pow(11 * (b1 - b2), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchColor(r1: number, g1: number, b1: number): number {
|
||||||
|
const hash = (r1 << 16) | (g1 << 8) | b1;
|
||||||
|
|
||||||
|
if (matchColorCache[hash] !== null && matchColorCache[hash] !== undefined) {
|
||||||
|
return matchColorCache[hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
let ldiff = Infinity;
|
||||||
|
let li = -1;
|
||||||
|
let i = 0;
|
||||||
|
let c: number;
|
||||||
|
let r2: number;
|
||||||
|
let g2: number;
|
||||||
|
let b2: number;
|
||||||
|
let diff: number;
|
||||||
|
|
||||||
|
for (; i < DEFAULT_ANSI_COLORS.length; i++) {
|
||||||
|
c = DEFAULT_ANSI_COLORS[i].rgba;
|
||||||
|
r2 = c >>> 24;
|
||||||
|
g2 = c >>> 16 & 0xFF;
|
||||||
|
b2 = c >>> 8 & 0xFF;
|
||||||
|
// assume that alpha is 0xFF
|
||||||
|
|
||||||
|
diff = matchColorDistance(r1, g1, b1, r2, g2, b2);
|
||||||
|
|
||||||
|
if (diff === 0) {
|
||||||
|
li = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diff < ldiff) {
|
||||||
|
ldiff = diff;
|
||||||
|
li = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchColorCache[hash] = li;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* buffer memory layout:
|
* buffer memory layout:
|
||||||
*
|
*
|
||||||
@@ -76,12 +132,188 @@ export const enum Content {
|
|||||||
WIDTH_SHIFT = 22
|
WIDTH_SHIFT = 22
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum Attributes {
|
||||||
|
/**
|
||||||
|
* bit 1..8 blue in RGB, color in P256 and P16
|
||||||
|
*/
|
||||||
|
BLUE_MASK = 0xFF,
|
||||||
|
BLUE_SHIFT = 0,
|
||||||
|
PCOLOR_MASK = 0xFF,
|
||||||
|
PCOLOR_SHIFT = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bit 9..16 green in RGB
|
||||||
|
*/
|
||||||
|
GREEN_MASK = 0xFF00,
|
||||||
|
GREEN_SHIFT = 8,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bit 17..24 red in RGB
|
||||||
|
*/
|
||||||
|
RED_MASK = 0xFF0000,
|
||||||
|
RED_SHIFT = 16,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3)
|
||||||
|
*/
|
||||||
|
CM_MASK = 0x3000000,
|
||||||
|
CM_DEFAULT = 0,
|
||||||
|
CM_P16 = 0x1000000,
|
||||||
|
CM_P256 = 0x2000000,
|
||||||
|
CM_RGB = 0x3000000,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bit 1..24 RGB room
|
||||||
|
*/
|
||||||
|
RGB_MASK = 0xFFFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FgFlags {
|
||||||
|
/**
|
||||||
|
* bit 27..31 (32th bit unused)
|
||||||
|
*/
|
||||||
|
INVERSE = 0x4000000,
|
||||||
|
BOLD = 0x8000000,
|
||||||
|
UNDERLINE = 0x10000000,
|
||||||
|
BLINK = 0x20000000,
|
||||||
|
INVISIBLE = 0x40000000
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum BgFlags {
|
||||||
|
/**
|
||||||
|
* bit 27..32 (upper 4 unused)
|
||||||
|
*/
|
||||||
|
ITALIC = 0x4000000,
|
||||||
|
DIM = 0x8000000
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AttributeData implements IAttributeData {
|
||||||
|
static toRGB(value: number): IColorRGB {
|
||||||
|
return [
|
||||||
|
value >>> Attributes.RED_SHIFT & 255,
|
||||||
|
value >>> Attributes.GREEN_SHIFT & 255,
|
||||||
|
value & 255
|
||||||
|
];
|
||||||
|
}
|
||||||
|
static fromRGB(value: IColorRGB): number {
|
||||||
|
return (value[0] & 255) << Attributes.RED_SHIFT | (value[1] & 255) << Attributes.GREEN_SHIFT | value[2] & 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
public clone(): IAttributeData {
|
||||||
|
const newObj = new AttributeData();
|
||||||
|
newObj.fg = this.fg;
|
||||||
|
newObj.bg = this.bg;
|
||||||
|
return newObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// data
|
||||||
|
public fg: number = 0;
|
||||||
|
public bg: number = 0;
|
||||||
|
|
||||||
|
// flags
|
||||||
|
public isInverse(): number { return this.fg & FgFlags.INVERSE; }
|
||||||
|
public isBold(): number { return this.fg & FgFlags.BOLD; }
|
||||||
|
public isUnderline(): number { return this.fg & FgFlags.UNDERLINE; }
|
||||||
|
public isBlink(): number { return this.fg & FgFlags.BLINK; }
|
||||||
|
public isInvisible(): number { return this.fg & FgFlags.INVISIBLE; }
|
||||||
|
public isItalic(): number { return this.bg & BgFlags.ITALIC; }
|
||||||
|
public isDim(): number { return this.bg & BgFlags.DIM; }
|
||||||
|
|
||||||
|
// color modes
|
||||||
|
public getColormodeFg(): number { return this.fg & Attributes.CM_MASK; }
|
||||||
|
public getColormodeBg(): number { return this.bg & Attributes.CM_MASK; }
|
||||||
|
public isFgRGB(): boolean { return (this.fg & Attributes.CM_MASK) === Attributes.CM_RGB; }
|
||||||
|
public isBgRGB(): boolean { return (this.bg & Attributes.CM_MASK) === Attributes.CM_RGB; }
|
||||||
|
public isFgPalette(): boolean { return (this.fg & Attributes.CM_MASK) === Attributes.CM_P16 || (this.fg & Attributes.CM_MASK) === Attributes.CM_P256; }
|
||||||
|
public isBgPalette(): boolean { return (this.bg & Attributes.CM_MASK) === Attributes.CM_P16 || (this.bg & Attributes.CM_MASK) === Attributes.CM_P256; }
|
||||||
|
public isFgDefault(): boolean { return (this.fg & Attributes.CM_MASK) === 0; }
|
||||||
|
public isBgDefault(): boolean { return (this.bg & Attributes.CM_MASK) === 0; }
|
||||||
|
|
||||||
|
// colors
|
||||||
|
public getFgColor(channels: boolean = false): number | IColorRGB {
|
||||||
|
switch (this.fg & Attributes.CM_MASK) {
|
||||||
|
case Attributes.CM_P16:
|
||||||
|
case Attributes.CM_P256: return this.fg & Attributes.PCOLOR_MASK;
|
||||||
|
case Attributes.CM_RGB: return (channels) ? AttributeData.toRGB(this.fg & Attributes.RGB_MASK) : this.fg & Attributes.RGB_MASK;
|
||||||
|
default: return -1; // CM_DEFAULT defaults to -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public getBgColor(channels: boolean = false): number | IColorRGB {
|
||||||
|
switch (this.bg & Attributes.CM_MASK) {
|
||||||
|
case Attributes.CM_P16:
|
||||||
|
case Attributes.CM_P256: return this.bg & Attributes.PCOLOR_MASK;
|
||||||
|
case Attributes.CM_RGB: return (channels) ? AttributeData.toRGB(this.bg & Attributes.RGB_MASK) : this.bg & Attributes.RGB_MASK;
|
||||||
|
default: return -1; // CM_DEFAULT defaults to -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getOldFlags(): number {
|
||||||
|
let flags = 0;
|
||||||
|
if (this.isBold()) {
|
||||||
|
flags |= FLAGS.BOLD;
|
||||||
|
}
|
||||||
|
if (this.isUnderline()) {
|
||||||
|
flags |= FLAGS.UNDERLINE;
|
||||||
|
}
|
||||||
|
if (this.isBlink()) {
|
||||||
|
flags |= FLAGS.BLINK;
|
||||||
|
}
|
||||||
|
if (this.isDim()) {
|
||||||
|
flags |= FLAGS.DIM;
|
||||||
|
}
|
||||||
|
if (this.isInvisible()) {
|
||||||
|
flags |= FLAGS.INVISIBLE;
|
||||||
|
}
|
||||||
|
if (this.isInverse()) {
|
||||||
|
flags |= FLAGS.INVERSE;
|
||||||
|
}
|
||||||
|
if (this.isItalic()) {
|
||||||
|
flags |= FLAGS.ITALIC;
|
||||||
|
}
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
public getOldFgColor(): number {
|
||||||
|
let color = this.getFgColor() as number;
|
||||||
|
if (color === -1) {
|
||||||
|
return 256;
|
||||||
|
}
|
||||||
|
if (this.isFgRGB()) {
|
||||||
|
color = matchColor(
|
||||||
|
(this.fg & Attributes.RED_MASK) >> Attributes.RED_SHIFT,
|
||||||
|
(this.fg & Attributes.GREEN_MASK) >> Attributes.GREEN_SHIFT,
|
||||||
|
(this.fg & Attributes.BLUE_MASK) >> Attributes.BLUE_SHIFT
|
||||||
|
);
|
||||||
|
if (color === -1) {
|
||||||
|
color = 256;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
public getOldBgColor(): number {
|
||||||
|
let color = this.getBgColor() as number;
|
||||||
|
if (color === -1) {
|
||||||
|
return 256;
|
||||||
|
}
|
||||||
|
if (this.isBgRGB()) {
|
||||||
|
color = matchColor(
|
||||||
|
(this.bg & Attributes.RED_MASK) >> Attributes.RED_SHIFT,
|
||||||
|
(this.bg & Attributes.GREEN_MASK) >> Attributes.GREEN_SHIFT,
|
||||||
|
(this.bg & Attributes.BLUE_MASK) >> Attributes.BLUE_SHIFT
|
||||||
|
);
|
||||||
|
if (color === -1) {
|
||||||
|
color = 256;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CellData - represents a single Cell in the terminal buffer.
|
* CellData - represents a single Cell in the terminal buffer.
|
||||||
*
|
*
|
||||||
* TODO: attr getter
|
* TODO: attr getter
|
||||||
*/
|
*/
|
||||||
export class CellData implements ICellData {
|
export class CellData extends AttributeData implements ICellData {
|
||||||
|
|
||||||
/** Helper to create CellData from CharData. */
|
/** Helper to create CellData from CharData. */
|
||||||
public static fromCharData(value: CharData): CellData {
|
public static fromCharData(value: CharData): CellData {
|
||||||
@@ -97,7 +329,7 @@ export class CellData implements ICellData {
|
|||||||
public combinedData: string = '';
|
public combinedData: string = '';
|
||||||
|
|
||||||
/** Whether cell contains a combined string. */
|
/** Whether cell contains a combined string. */
|
||||||
public get combined(): number {
|
public get isCombined(): number {
|
||||||
return this.content & Content.IS_COMBINED;
|
return this.content & Content.IS_COMBINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +351,7 @@ export class CellData implements ICellData {
|
|||||||
|
|
||||||
/** Codepoint of cell (or last charCode of combined string) */
|
/** Codepoint of cell (or last charCode of combined string) */
|
||||||
public get code(): number {
|
public get code(): number {
|
||||||
return ((this.combined) ? this.combinedData.charCodeAt(this.combinedData.length - 1) : this.content & Content.CODEPOINT_MASK);
|
return ((this.isCombined) ? this.combinedData.charCodeAt(this.combinedData.length - 1) : this.content & Content.CODEPOINT_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set data from CharData */
|
/** Set data from CharData */
|
||||||
|
|||||||
+2
-2
@@ -3,7 +3,7 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ITerminal, IBufferSet } from './Types';
|
import { ITerminal, IBufferSet, IAttributeData } from './Types';
|
||||||
import { Buffer } from './Buffer';
|
import { Buffer } from './Buffer';
|
||||||
import { EventEmitter } from './common/EventEmitter';
|
import { EventEmitter } from './common/EventEmitter';
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ export class BufferSet extends EventEmitter implements IBufferSet {
|
|||||||
/**
|
/**
|
||||||
* Sets the alt Buffer of the BufferSet as its currently active Buffer
|
* Sets the alt Buffer of the BufferSet as its currently active Buffer
|
||||||
*/
|
*/
|
||||||
public activateAltBuffer(fillAttr?: number): void {
|
public activateAltBuffer(fillAttr?: IAttributeData): void {
|
||||||
if (this._activeBuffer === this._alt) {
|
if (this._activeBuffer === this._alt) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+119
-15
@@ -5,33 +5,33 @@
|
|||||||
|
|
||||||
import { assert, expect } from 'chai';
|
import { assert, expect } from 'chai';
|
||||||
import { InputHandler } from './InputHandler';
|
import { InputHandler } from './InputHandler';
|
||||||
import { MockInputHandlingTerminal } from './ui/TestUtils.test';
|
import { MockInputHandlingTerminal, TestTerminal } from './ui/TestUtils.test';
|
||||||
import { DEFAULT_ATTR } from './Buffer';
|
import { DEFAULT_ATTR_DATA } from './Buffer';
|
||||||
import { Terminal } from './Terminal';
|
import { Terminal } from './Terminal';
|
||||||
import { IBufferLine } from './Types';
|
import { IBufferLine } from './Types';
|
||||||
import { CellData } from './BufferLine';
|
import { CellData, Attributes } from './BufferLine';
|
||||||
|
|
||||||
describe('InputHandler', () => {
|
describe('InputHandler', () => {
|
||||||
describe('save and restore cursor', () => {
|
describe('save and restore cursor', () => {
|
||||||
const terminal = new MockInputHandlingTerminal();
|
const terminal = new MockInputHandlingTerminal();
|
||||||
terminal.buffer.x = 1;
|
terminal.buffer.x = 1;
|
||||||
terminal.buffer.y = 2;
|
terminal.buffer.y = 2;
|
||||||
terminal.curAttr = 3;
|
terminal.curAttrData.fg = 3;
|
||||||
const inputHandler = new InputHandler(terminal);
|
const inputHandler = new InputHandler(terminal);
|
||||||
// Save cursor position
|
// Save cursor position
|
||||||
inputHandler.saveCursor([]);
|
inputHandler.saveCursor([]);
|
||||||
assert.equal(terminal.buffer.x, 1);
|
assert.equal(terminal.buffer.x, 1);
|
||||||
assert.equal(terminal.buffer.y, 2);
|
assert.equal(terminal.buffer.y, 2);
|
||||||
assert.equal(terminal.curAttr, 3);
|
assert.equal(terminal.curAttrData.fg, 3);
|
||||||
// Change cursor position
|
// Change cursor position
|
||||||
terminal.buffer.x = 10;
|
terminal.buffer.x = 10;
|
||||||
terminal.buffer.y = 20;
|
terminal.buffer.y = 20;
|
||||||
terminal.curAttr = 30;
|
terminal.curAttrData.fg = 30;
|
||||||
// Restore cursor position
|
// Restore cursor position
|
||||||
inputHandler.restoreCursor([]);
|
inputHandler.restoreCursor([]);
|
||||||
assert.equal(terminal.buffer.x, 1);
|
assert.equal(terminal.buffer.x, 1);
|
||||||
assert.equal(terminal.buffer.y, 2);
|
assert.equal(terminal.buffer.y, 2);
|
||||||
assert.equal(terminal.curAttr, 3);
|
assert.equal(terminal.curAttrData.fg, 3);
|
||||||
});
|
});
|
||||||
describe('setCursorStyle', () => {
|
describe('setCursorStyle', () => {
|
||||||
it('should call Terminal.setOption with correct params', () => {
|
it('should call Terminal.setOption with correct params', () => {
|
||||||
@@ -357,45 +357,149 @@ describe('InputHandler', () => {
|
|||||||
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('');
|
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('');
|
||||||
expect(term.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
|
expect(term.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
|
||||||
// Text color of 'TEST' should be red
|
// Text color of 'TEST' should be red
|
||||||
expect((term.buffer.lines.get(1).loadCell(4, new CellData()).fg >> 9) & 0x1ff).to.equal(1);
|
expect((term.buffer.lines.get(1).loadCell(4, new CellData()).getOldFgColor())).to.equal(1);
|
||||||
});
|
});
|
||||||
it('should handle DECSET/DECRST 1047 (alt screen buffer)', () => {
|
it('should handle DECSET/DECRST 1047 (alt screen buffer)', () => {
|
||||||
handler.parse('\x1b[?1047h\r\n\x1b[31mJUNK\x1b[?1047lTEST');
|
handler.parse('\x1b[?1047h\r\n\x1b[31mJUNK\x1b[?1047lTEST');
|
||||||
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('');
|
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('');
|
||||||
expect(term.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
|
expect(term.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
|
||||||
// Text color of 'TEST' should be red
|
// Text color of 'TEST' should be red
|
||||||
expect((term.buffer.lines.get(1).loadCell(4, new CellData()).fg >> 9) & 0x1ff).to.equal(1);
|
expect((term.buffer.lines.get(1).loadCell(4, new CellData()).getOldFgColor())).to.equal(1);
|
||||||
});
|
});
|
||||||
it('should handle DECSET/DECRST 1048 (alt screen cursor)', () => {
|
it('should handle DECSET/DECRST 1048 (alt screen cursor)', () => {
|
||||||
handler.parse('\x1b[?1048h\r\n\x1b[31mJUNK\x1b[?1048lTEST');
|
handler.parse('\x1b[?1048h\r\n\x1b[31mJUNK\x1b[?1048lTEST');
|
||||||
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
||||||
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('JUNK');
|
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('JUNK');
|
||||||
// Text color of 'TEST' should be default
|
// Text color of 'TEST' should be default
|
||||||
expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR);
|
expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
|
||||||
// Text color of 'JUNK' should be red
|
// Text color of 'JUNK' should be red
|
||||||
expect((term.buffer.lines.get(1).loadCell(0, new CellData()).fg >> 9) & 0x1ff).to.equal(1);
|
expect((term.buffer.lines.get(1).loadCell(0, new CellData()).getOldFgColor())).to.equal(1);
|
||||||
});
|
});
|
||||||
it('should handle DECSET/DECRST 1049 (alt screen buffer+cursor)', () => {
|
it('should handle DECSET/DECRST 1049 (alt screen buffer+cursor)', () => {
|
||||||
handler.parse('\x1b[?1049h\r\n\x1b[31mJUNK\x1b[?1049lTEST');
|
handler.parse('\x1b[?1049h\r\n\x1b[31mJUNK\x1b[?1049lTEST');
|
||||||
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
||||||
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('');
|
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('');
|
||||||
// Text color of 'TEST' should be default
|
// Text color of 'TEST' should be default
|
||||||
expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR);
|
expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
|
||||||
});
|
});
|
||||||
it('should handle DECSET/DECRST 1049 - maintains saved cursor for alt buffer', () => {
|
it('should handle DECSET/DECRST 1049 - maintains saved cursor for alt buffer', () => {
|
||||||
handler.parse('\x1b[?1049h\r\n\x1b[31m\x1b[s\x1b[?1049lTEST');
|
handler.parse('\x1b[?1049h\r\n\x1b[31m\x1b[s\x1b[?1049lTEST');
|
||||||
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
||||||
// Text color of 'TEST' should be default
|
// Text color of 'TEST' should be default
|
||||||
expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR);
|
expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
|
||||||
handler.parse('\x1b[?1049h\x1b[uTEST');
|
handler.parse('\x1b[?1049h\x1b[uTEST');
|
||||||
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('TEST');
|
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('TEST');
|
||||||
// Text color of 'TEST' should be red
|
// Text color of 'TEST' should be red
|
||||||
expect((term.buffer.lines.get(1).loadCell(0, new CellData()).fg >> 9) & 0x1ff).to.equal(1);
|
expect((term.buffer.lines.get(1).loadCell(0, new CellData()).getOldFgColor())).to.equal(1);
|
||||||
});
|
});
|
||||||
it('should handle DECSET/DECRST 1049 - clears alt buffer with erase attributes', () => {
|
it('should handle DECSET/DECRST 1049 - clears alt buffer with erase attributes', () => {
|
||||||
handler.parse('\x1b[42m\x1b[?1049h');
|
handler.parse('\x1b[42m\x1b[?1049h');
|
||||||
// Buffer should be filled with green background
|
// Buffer should be filled with green background
|
||||||
expect(term.buffer.lines.get(20).loadCell(10, new CellData()).fg & 0x1ff).to.equal(2);
|
expect(term.buffer.lines.get(20).loadCell(10, new CellData()).getOldBgColor()).to.equal(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('text attributes', () => {
|
||||||
|
let term: TestTerminal;
|
||||||
|
beforeEach(() => {
|
||||||
|
term = new TestTerminal();
|
||||||
|
});
|
||||||
|
it('bold', () => {
|
||||||
|
term.writeSync('\x1b[1m');
|
||||||
|
assert.equal(!!term.curAttrData.isBold(), true);
|
||||||
|
term.writeSync('\x1b[22m');
|
||||||
|
assert.equal(!!term.curAttrData.isBold(), false);
|
||||||
|
});
|
||||||
|
it('dim', () => {
|
||||||
|
term.writeSync('\x1b[2m');
|
||||||
|
assert.equal(!!term.curAttrData.isDim(), true);
|
||||||
|
term.writeSync('\x1b[22m');
|
||||||
|
assert.equal(!!term.curAttrData.isDim(), false);
|
||||||
|
});
|
||||||
|
it('italic', () => {
|
||||||
|
term.writeSync('\x1b[3m');
|
||||||
|
assert.equal(!!term.curAttrData.isItalic(), true);
|
||||||
|
term.writeSync('\x1b[23m');
|
||||||
|
assert.equal(!!term.curAttrData.isItalic(), false);
|
||||||
|
});
|
||||||
|
it('underline', () => {
|
||||||
|
term.writeSync('\x1b[4m');
|
||||||
|
assert.equal(!!term.curAttrData.isUnderline(), true);
|
||||||
|
term.writeSync('\x1b[24m');
|
||||||
|
assert.equal(!!term.curAttrData.isUnderline(), false);
|
||||||
|
});
|
||||||
|
it('blink', () => {
|
||||||
|
term.writeSync('\x1b[5m');
|
||||||
|
assert.equal(!!term.curAttrData.isBlink(), true);
|
||||||
|
term.writeSync('\x1b[25m');
|
||||||
|
assert.equal(!!term.curAttrData.isBlink(), false);
|
||||||
|
});
|
||||||
|
it('inverse', () => {
|
||||||
|
term.writeSync('\x1b[7m');
|
||||||
|
assert.equal(!!term.curAttrData.isInverse(), true);
|
||||||
|
term.writeSync('\x1b[27m');
|
||||||
|
assert.equal(!!term.curAttrData.isInverse(), false);
|
||||||
|
});
|
||||||
|
it('invisible', () => {
|
||||||
|
term.writeSync('\x1b[8m');
|
||||||
|
assert.equal(!!term.curAttrData.isInvisible(), true);
|
||||||
|
term.writeSync('\x1b[28m');
|
||||||
|
assert.equal(!!term.curAttrData.isInvisible(), false);
|
||||||
|
});
|
||||||
|
it('colormode palette 16', () => {
|
||||||
|
assert.equal(term.curAttrData.getColormodeFg(), 0); // DEFAULT
|
||||||
|
assert.equal(term.curAttrData.getColormodeBg(), 0); // DEFAULT
|
||||||
|
// lower 8 colors
|
||||||
|
for (let i = 0; i < 8; ++i) {
|
||||||
|
term.writeSync(`\x1b[${i + 30};${i + 40}m`);
|
||||||
|
assert.equal(term.curAttrData.getColormodeFg(), Attributes.CM_P16);
|
||||||
|
assert.equal(term.curAttrData.getFgColor(), i);
|
||||||
|
assert.equal(term.curAttrData.getColormodeBg(), Attributes.CM_P16);
|
||||||
|
assert.equal(term.curAttrData.getBgColor(), i);
|
||||||
|
}
|
||||||
|
// reset to DEFAULT
|
||||||
|
term.writeSync(`\x1b[39;49m`);
|
||||||
|
assert.equal(term.curAttrData.getColormodeFg(), 0);
|
||||||
|
assert.equal(term.curAttrData.getColormodeBg(), 0);
|
||||||
|
});
|
||||||
|
it('colormode palette 256', () => {
|
||||||
|
assert.equal(term.curAttrData.getColormodeFg(), 0); // DEFAULT
|
||||||
|
assert.equal(term.curAttrData.getColormodeBg(), 0); // DEFAULT
|
||||||
|
// lower 8 colors
|
||||||
|
for (let i = 0; i < 256; ++i) {
|
||||||
|
term.writeSync(`\x1b[38;5;${i};48;5;${i}m`);
|
||||||
|
assert.equal(term.curAttrData.getColormodeFg(), Attributes.CM_P256);
|
||||||
|
assert.equal(term.curAttrData.getFgColor(), i);
|
||||||
|
assert.equal(term.curAttrData.getColormodeBg(), Attributes.CM_P256);
|
||||||
|
assert.equal(term.curAttrData.getBgColor(), i);
|
||||||
|
}
|
||||||
|
// reset to DEFAULT
|
||||||
|
term.writeSync(`\x1b[39;49m`);
|
||||||
|
assert.equal(term.curAttrData.getColormodeFg(), 0);
|
||||||
|
assert.equal(term.curAttrData.getFgColor(), -1);
|
||||||
|
assert.equal(term.curAttrData.getColormodeBg(), 0);
|
||||||
|
assert.equal(term.curAttrData.getBgColor(), -1);
|
||||||
|
});
|
||||||
|
it('colormode RGB', () => {
|
||||||
|
assert.equal(term.curAttrData.getColormodeFg(), 0); // DEFAULT
|
||||||
|
assert.equal(term.curAttrData.getColormodeBg(), 0); // DEFAULT
|
||||||
|
term.writeSync(`\x1b[38;2;1;2;3;48;2;4;5;6m`);
|
||||||
|
assert.equal(term.curAttrData.getColormodeFg(), Attributes.CM_RGB);
|
||||||
|
assert.equal(term.curAttrData.getFgColor(), 1 << 16 | 2 << 8 | 3);
|
||||||
|
assert.deepEqual(term.curAttrData.getFgColor(true), [1, 2, 3]);
|
||||||
|
assert.equal(term.curAttrData.getColormodeBg(), Attributes.CM_RGB);
|
||||||
|
assert.deepEqual(term.curAttrData.getBgColor(true), [4, 5, 6]);
|
||||||
|
// reset to DEFAULT
|
||||||
|
term.writeSync(`\x1b[39;49m`);
|
||||||
|
assert.equal(term.curAttrData.getColormodeFg(), 0);
|
||||||
|
assert.equal(term.curAttrData.getFgColor(), -1);
|
||||||
|
assert.equal(term.curAttrData.getColormodeBg(), 0);
|
||||||
|
assert.equal(term.curAttrData.getBgColor(), -1);
|
||||||
|
});
|
||||||
|
it('should zero missing RGB values', () => {
|
||||||
|
term.writeSync(`\x1b[38;2;1;2;3m`);
|
||||||
|
term.writeSync(`\x1b[38;2;5m`);
|
||||||
|
assert.deepEqual(term.curAttrData.getFgColor(true), [5, 0, 0]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+77
-76
@@ -4,19 +4,17 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IInputHandler, IDcsHandler, IEscapeSequenceParser, IBuffer, IInputHandlingTerminal } from './Types';
|
import { IInputHandler, IDcsHandler, IEscapeSequenceParser, IInputHandlingTerminal } from './Types';
|
||||||
import { C0, C1 } from './common/data/EscapeSequences';
|
import { C0, C1 } from './common/data/EscapeSequences';
|
||||||
import { CHARSETS, DEFAULT_CHARSET } from './core/data/Charsets';
|
import { CHARSETS, DEFAULT_CHARSET } from './core/data/Charsets';
|
||||||
import { DEFAULT_ATTR, NULL_CELL_WIDTH, NULL_CELL_CODE } from './Buffer';
|
import { NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR_DATA } from './Buffer';
|
||||||
import { FLAGS } from './renderer/Types';
|
|
||||||
import { wcwidth } from './CharWidth';
|
import { wcwidth } from './CharWidth';
|
||||||
import { EscapeSequenceParser } from './EscapeSequenceParser';
|
import { EscapeSequenceParser } from './EscapeSequenceParser';
|
||||||
import { ICharset } from './core/Types';
|
|
||||||
import { IDisposable } from 'xterm';
|
import { IDisposable } from 'xterm';
|
||||||
import { Disposable } from './common/Lifecycle';
|
import { Disposable } from './common/Lifecycle';
|
||||||
import { concat, utf32ToString } from './common/TypedArrayUtils';
|
import { concat, utf32ToString } from './common/TypedArrayUtils';
|
||||||
import { StringToUtf32, stringFromCodePoint } from './core/input/TextDecoder';
|
import { StringToUtf32, stringFromCodePoint } from './core/input/TextDecoder';
|
||||||
import { CellData } from './BufferLine';
|
import { CellData, Attributes, FgFlags, BgFlags } from './BufferLine';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map collect to glevel. Used in `selectCharset`.
|
* Map collect to glevel. Used in `selectCharset`.
|
||||||
@@ -314,13 +312,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
public print(data: Uint32Array, start: number, end: number): void {
|
public print(data: Uint32Array, start: number, end: number): void {
|
||||||
let code: number;
|
let code: number;
|
||||||
let chWidth: number;
|
let chWidth: number;
|
||||||
const buffer: IBuffer = this._terminal.buffer;
|
const buffer = this._terminal.buffer;
|
||||||
const charset: ICharset = this._terminal.charset;
|
const charset = this._terminal.charset;
|
||||||
const screenReaderMode: boolean = this._terminal.options.screenReaderMode;
|
const screenReaderMode = this._terminal.options.screenReaderMode;
|
||||||
const cols: number = this._terminal.cols;
|
const cols = this._terminal.cols;
|
||||||
const wraparoundMode: boolean = this._terminal.wraparoundMode;
|
const wraparoundMode = this._terminal.wraparoundMode;
|
||||||
const insertMode: boolean = this._terminal.insertMode;
|
const insertMode = this._terminal.insertMode;
|
||||||
const curAttr: number = this._terminal.curAttr;
|
const curAttr = this._terminal.curAttrData;
|
||||||
let bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
|
let bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
|
||||||
|
|
||||||
this._terminal.updateRange(buffer.y);
|
this._terminal.updateRange(buffer.y);
|
||||||
@@ -401,12 +399,12 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
// a halfwidth char any fullwidth shifted there is lost
|
// a halfwidth char any fullwidth shifted there is lost
|
||||||
// and will be set to empty cell
|
// and will be set to empty cell
|
||||||
if (bufferRow.loadCell(cols - 1, this._cell).width === 2) {
|
if (bufferRow.loadCell(cols - 1, this._cell).width === 2) {
|
||||||
bufferRow.setDataFromCodePoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr, 0);
|
bufferRow.setDataFromCodePoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr.fg, curAttr.bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// write current char to buffer and advance cursor
|
// write current char to buffer and advance cursor
|
||||||
bufferRow.setDataFromCodePoint(buffer.x++, code, chWidth, curAttr, 0);
|
bufferRow.setDataFromCodePoint(buffer.x++, code, chWidth, curAttr.fg, curAttr.bg);
|
||||||
|
|
||||||
// fullwidth char - also set next cell to placeholder stub and advance cursor
|
// fullwidth char - also set next cell to placeholder stub and advance cursor
|
||||||
// for graphemes bigger than fullwidth we can simply loop to zero
|
// for graphemes bigger than fullwidth we can simply loop to zero
|
||||||
@@ -414,7 +412,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
if (chWidth > 0) {
|
if (chWidth > 0) {
|
||||||
while (--chWidth) {
|
while (--chWidth) {
|
||||||
// other than a regular empty cell a cell following a wide char has no width
|
// other than a regular empty cell a cell following a wide char has no width
|
||||||
bufferRow.setDataFromCodePoint(buffer.x++, 0, 0, curAttr, 0);
|
bufferRow.setDataFromCodePoint(buffer.x++, 0, 0, curAttr.fg, curAttr.bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -520,7 +518,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).insertCells(
|
this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).insertCells(
|
||||||
this._terminal.buffer.x,
|
this._terminal.buffer.x,
|
||||||
params[0] || 1,
|
params[0] || 1,
|
||||||
this._terminal.buffer.getNullCell(this._terminal.eraseAttr())
|
this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())
|
||||||
);
|
);
|
||||||
this._terminal.updateRange(this._terminal.buffer.y);
|
this._terminal.updateRange(this._terminal.buffer.y);
|
||||||
}
|
}
|
||||||
@@ -694,7 +692,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
line.replaceCells(
|
line.replaceCells(
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
this._terminal.buffer.getNullCell(this._terminal.eraseAttr())
|
this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())
|
||||||
);
|
);
|
||||||
if (clearWrap) {
|
if (clearWrap) {
|
||||||
line.isWrapped = false;
|
line.isWrapped = false;
|
||||||
@@ -817,7 +815,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
// test: echo -e '\e[44m\e[1L\e[0m'
|
// test: echo -e '\e[44m\e[1L\e[0m'
|
||||||
// blankLine(true) - xterm/linux behavior
|
// blankLine(true) - xterm/linux behavior
|
||||||
buffer.lines.splice(scrollBottomAbsolute - 1, 1);
|
buffer.lines.splice(scrollBottomAbsolute - 1, 1);
|
||||||
buffer.lines.splice(row, 0, buffer.getBlankLine(this._terminal.eraseAttr()));
|
buffer.lines.splice(row, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.maxRange();
|
// this.maxRange();
|
||||||
@@ -847,7 +845,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
// test: echo -e '\e[44m\e[1M\e[0m'
|
// test: echo -e '\e[44m\e[1M\e[0m'
|
||||||
// blankLine(true) - xterm/linux behavior
|
// blankLine(true) - xterm/linux behavior
|
||||||
buffer.lines.splice(row, 1);
|
buffer.lines.splice(row, 1);
|
||||||
buffer.lines.splice(j, 0, buffer.getBlankLine(this._terminal.eraseAttr()));
|
buffer.lines.splice(j, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.maxRange();
|
// this.maxRange();
|
||||||
@@ -863,7 +861,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).deleteCells(
|
this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).deleteCells(
|
||||||
this._terminal.buffer.x,
|
this._terminal.buffer.x,
|
||||||
params[0] || 1,
|
params[0] || 1,
|
||||||
this._terminal.buffer.getNullCell(this._terminal.eraseAttr())
|
this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())
|
||||||
);
|
);
|
||||||
this._terminal.updateRange(this._terminal.buffer.y);
|
this._terminal.updateRange(this._terminal.buffer.y);
|
||||||
}
|
}
|
||||||
@@ -879,7 +877,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
|
|
||||||
while (param--) {
|
while (param--) {
|
||||||
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1);
|
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1);
|
||||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR));
|
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||||
}
|
}
|
||||||
// this.maxRange();
|
// this.maxRange();
|
||||||
this._terminal.updateRange(buffer.scrollTop);
|
this._terminal.updateRange(buffer.scrollTop);
|
||||||
@@ -898,7 +896,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
|
|
||||||
while (param--) {
|
while (param--) {
|
||||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
|
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
|
||||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR));
|
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||||
}
|
}
|
||||||
// this.maxRange();
|
// this.maxRange();
|
||||||
this._terminal.updateRange(buffer.scrollTop);
|
this._terminal.updateRange(buffer.scrollTop);
|
||||||
@@ -914,7 +912,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).replaceCells(
|
this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).replaceCells(
|
||||||
this._terminal.buffer.x,
|
this._terminal.buffer.x,
|
||||||
this._terminal.buffer.x + (params[0] || 1),
|
this._terminal.buffer.x + (params[0] || 1),
|
||||||
this._terminal.buffer.getNullCell(this._terminal.eraseAttr())
|
this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -973,7 +971,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
line.loadCell(buffer.x - 1, this._cell);
|
line.loadCell(buffer.x - 1, this._cell);
|
||||||
line.replaceCells(buffer.x,
|
line.replaceCells(buffer.x,
|
||||||
buffer.x + (params[0] || 1),
|
buffer.x + (params[0] || 1),
|
||||||
(this._cell.content !== undefined) ? this._cell : buffer.getNullCell(DEFAULT_ATTR)
|
(this._cell.content !== undefined) ? this._cell : buffer.getNullCell(DEFAULT_ATTR_DATA)
|
||||||
);
|
);
|
||||||
// FIXME: no updateRange here?
|
// FIXME: no updateRange here?
|
||||||
}
|
}
|
||||||
@@ -1307,7 +1305,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
// FALL-THROUGH
|
// FALL-THROUGH
|
||||||
case 47: // alt screen buffer
|
case 47: // alt screen buffer
|
||||||
case 1047: // alt screen buffer
|
case 1047: // alt screen buffer
|
||||||
this._terminal.buffers.activateAltBuffer(this._terminal.eraseAttr());
|
this._terminal.buffers.activateAltBuffer(this._terminal.eraseAttrData());
|
||||||
this._terminal.refresh(0, this._terminal.rows - 1);
|
this._terminal.refresh(0, this._terminal.rows - 1);
|
||||||
if (this._terminal.viewport) {
|
if (this._terminal.viewport) {
|
||||||
this._terminal.viewport.syncScrollArea();
|
this._terminal.viewport.syncScrollArea();
|
||||||
@@ -1567,127 +1565,128 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
public charAttributes(params: number[]): void {
|
public charAttributes(params: number[]): void {
|
||||||
// Optimize a single SGR0.
|
// Optimize a single SGR0.
|
||||||
if (params.length === 1 && params[0] === 0) {
|
if (params.length === 1 && params[0] === 0) {
|
||||||
this._terminal.curAttr = DEFAULT_ATTR;
|
this._terminal.curAttrData.fg = DEFAULT_ATTR_DATA.fg;
|
||||||
|
this._terminal.curAttrData.bg = DEFAULT_ATTR_DATA.bg;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const l = params.length;
|
const l = params.length;
|
||||||
let flags = this._terminal.curAttr >> 18;
|
|
||||||
let fg = (this._terminal.curAttr >> 9) & 0x1ff;
|
|
||||||
let bg = this._terminal.curAttr & 0x1ff;
|
|
||||||
let p;
|
let p;
|
||||||
|
const attr = this._terminal.curAttrData;
|
||||||
|
|
||||||
for (let i = 0; i < l; i++) {
|
for (let i = 0; i < l; i++) {
|
||||||
p = params[i];
|
p = params[i];
|
||||||
if (p >= 30 && p <= 37) {
|
if (p >= 30 && p <= 37) {
|
||||||
// fg color 8
|
// fg color 8
|
||||||
fg = p - 30;
|
attr.fg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
|
||||||
|
attr.fg |= Attributes.CM_P16 | (p - 30);
|
||||||
} else if (p >= 40 && p <= 47) {
|
} else if (p >= 40 && p <= 47) {
|
||||||
// bg color 8
|
// bg color 8
|
||||||
bg = p - 40;
|
attr.bg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
|
||||||
|
attr.bg |= Attributes.CM_P16 | (p - 40);
|
||||||
} else if (p >= 90 && p <= 97) {
|
} else if (p >= 90 && p <= 97) {
|
||||||
// fg color 16
|
// fg color 16
|
||||||
p += 8;
|
attr.fg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
|
||||||
fg = p - 90;
|
attr.fg |= Attributes.CM_P16 | (p - 90) | 8;
|
||||||
} else if (p >= 100 && p <= 107) {
|
} else if (p >= 100 && p <= 107) {
|
||||||
// bg color 16
|
// bg color 16
|
||||||
p += 8;
|
attr.bg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
|
||||||
bg = p - 100;
|
attr.bg |= Attributes.CM_P16 | (p - 100) | 8;
|
||||||
} else if (p === 0) {
|
} else if (p === 0) {
|
||||||
// default
|
// default
|
||||||
flags = DEFAULT_ATTR >> 18;
|
attr.fg = DEFAULT_ATTR_DATA.fg;
|
||||||
fg = (DEFAULT_ATTR >> 9) & 0x1ff;
|
attr.bg = DEFAULT_ATTR_DATA.bg;
|
||||||
bg = DEFAULT_ATTR & 0x1ff;
|
|
||||||
// flags = 0;
|
|
||||||
// fg = 0x1ff;
|
|
||||||
// bg = 0x1ff;
|
|
||||||
} else if (p === 1) {
|
} else if (p === 1) {
|
||||||
// bold text
|
// bold text
|
||||||
flags |= FLAGS.BOLD;
|
attr.fg |= FgFlags.BOLD;
|
||||||
} else if (p === 3) {
|
} else if (p === 3) {
|
||||||
// italic text
|
// italic text
|
||||||
flags |= FLAGS.ITALIC;
|
attr.bg |= BgFlags.ITALIC;
|
||||||
} else if (p === 4) {
|
} else if (p === 4) {
|
||||||
// underlined text
|
// underlined text
|
||||||
flags |= FLAGS.UNDERLINE;
|
attr.fg |= FgFlags.UNDERLINE;
|
||||||
} else if (p === 5) {
|
} else if (p === 5) {
|
||||||
// blink
|
// blink
|
||||||
flags |= FLAGS.BLINK;
|
attr.fg |= FgFlags.BLINK;
|
||||||
} else if (p === 7) {
|
} else if (p === 7) {
|
||||||
// inverse and positive
|
// inverse and positive
|
||||||
// test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
|
// test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
|
||||||
flags |= FLAGS.INVERSE;
|
attr.fg |= FgFlags.INVERSE;
|
||||||
} else if (p === 8) {
|
} else if (p === 8) {
|
||||||
// invisible
|
// invisible
|
||||||
flags |= FLAGS.INVISIBLE;
|
attr.fg |= FgFlags.INVISIBLE;
|
||||||
} else if (p === 2) {
|
} else if (p === 2) {
|
||||||
// dimmed text
|
// dimmed text
|
||||||
flags |= FLAGS.DIM;
|
attr.bg |= BgFlags.DIM;
|
||||||
} else if (p === 22) {
|
} else if (p === 22) {
|
||||||
// not bold nor faint
|
// not bold nor faint
|
||||||
flags &= ~FLAGS.BOLD;
|
attr.fg &= ~FgFlags.BOLD;
|
||||||
flags &= ~FLAGS.DIM;
|
attr.bg &= ~BgFlags.DIM;
|
||||||
} else if (p === 23) {
|
} else if (p === 23) {
|
||||||
// not italic
|
// not italic
|
||||||
flags &= ~FLAGS.ITALIC;
|
attr.bg &= ~BgFlags.ITALIC;
|
||||||
} else if (p === 24) {
|
} else if (p === 24) {
|
||||||
// not underlined
|
// not underlined
|
||||||
flags &= ~FLAGS.UNDERLINE;
|
attr.fg &= ~FgFlags.UNDERLINE;
|
||||||
} else if (p === 25) {
|
} else if (p === 25) {
|
||||||
// not blink
|
// not blink
|
||||||
flags &= ~FLAGS.BLINK;
|
attr.fg &= ~FgFlags.BLINK;
|
||||||
} else if (p === 27) {
|
} else if (p === 27) {
|
||||||
// not inverse
|
// not inverse
|
||||||
flags &= ~FLAGS.INVERSE;
|
attr.fg &= ~FgFlags.INVERSE;
|
||||||
} else if (p === 28) {
|
} else if (p === 28) {
|
||||||
// not invisible
|
// not invisible
|
||||||
flags &= ~FLAGS.INVISIBLE;
|
attr.fg &= ~FgFlags.INVISIBLE;
|
||||||
} else if (p === 39) {
|
} else if (p === 39) {
|
||||||
// reset fg
|
// reset fg
|
||||||
fg = (DEFAULT_ATTR >> 9) & 0x1ff;
|
attr.fg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
|
||||||
|
attr.fg |= DEFAULT_ATTR_DATA.fg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
|
||||||
} else if (p === 49) {
|
} else if (p === 49) {
|
||||||
// reset bg
|
// reset bg
|
||||||
bg = DEFAULT_ATTR & 0x1ff;
|
attr.bg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
|
||||||
|
attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
|
||||||
} else if (p === 38) {
|
} else if (p === 38) {
|
||||||
// fg color 256
|
// fg color 256
|
||||||
if (params[i + 1] === 2) {
|
if (params[i + 1] === 2) {
|
||||||
i += 2;
|
i += 2;
|
||||||
fg = this._terminal.matchColor(
|
attr.fg |= Attributes.CM_RGB;
|
||||||
params[i] & 0xff,
|
attr.fg &= ~Attributes.RGB_MASK;
|
||||||
params[i + 1] & 0xff,
|
attr.fg |= (params[i] & 0xFF) << Attributes.RED_SHIFT;
|
||||||
params[i + 2] & 0xff);
|
attr.fg |= (params[i + 1] & 0xFF) << Attributes.GREEN_SHIFT;
|
||||||
if (fg === -1) fg = 0x1ff;
|
attr.fg |= (params[i + 2] & 0xFF) << Attributes.BLUE_SHIFT;
|
||||||
i += 2;
|
i += 2;
|
||||||
} else if (params[i + 1] === 5) {
|
} else if (params[i + 1] === 5) {
|
||||||
i += 2;
|
i += 2;
|
||||||
p = params[i] & 0xff;
|
p = params[i] & 0xff;
|
||||||
fg = p;
|
attr.fg &= ~Attributes.PCOLOR_MASK;
|
||||||
|
attr.fg |= Attributes.CM_P256 | p;
|
||||||
}
|
}
|
||||||
} else if (p === 48) {
|
} else if (p === 48) {
|
||||||
// bg color 256
|
// bg color 256
|
||||||
if (params[i + 1] === 2) {
|
if (params[i + 1] === 2) {
|
||||||
i += 2;
|
i += 2;
|
||||||
bg = this._terminal.matchColor(
|
attr.bg |= Attributes.CM_RGB;
|
||||||
params[i] & 0xff,
|
attr.bg &= ~Attributes.RGB_MASK;
|
||||||
params[i + 1] & 0xff,
|
attr.bg |= (params[i] & 0xFF) << Attributes.RED_SHIFT;
|
||||||
params[i + 2] & 0xff);
|
attr.bg |= (params[i + 1] & 0xFF) << Attributes.GREEN_SHIFT;
|
||||||
if (bg === -1) bg = 0x1ff;
|
attr.bg |= (params[i + 2] & 0xFF) << Attributes.BLUE_SHIFT;
|
||||||
i += 2;
|
i += 2;
|
||||||
} else if (params[i + 1] === 5) {
|
} else if (params[i + 1] === 5) {
|
||||||
i += 2;
|
i += 2;
|
||||||
p = params[i] & 0xff;
|
p = params[i] & 0xff;
|
||||||
bg = p;
|
attr.bg &= ~Attributes.PCOLOR_MASK;
|
||||||
|
attr.bg |= Attributes.CM_P256 | p;
|
||||||
}
|
}
|
||||||
} else if (p === 100) {
|
} else if (p === 100) {
|
||||||
// reset fg/bg
|
// reset fg/bg
|
||||||
fg = (DEFAULT_ATTR >> 9) & 0x1ff;
|
attr.fg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
|
||||||
bg = DEFAULT_ATTR & 0x1ff;
|
attr.fg |= DEFAULT_ATTR_DATA.fg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
|
||||||
|
attr.bg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
|
||||||
|
attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
|
||||||
} else {
|
} else {
|
||||||
this._terminal.error('Unknown SGR attribute: %d.', p);
|
this._terminal.error('Unknown SGR attribute: %d.', p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._terminal.curAttr = (flags << 18) | (fg << 9) | bg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1774,7 +1773,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
this._terminal.applicationCursor = false;
|
this._terminal.applicationCursor = false;
|
||||||
this._terminal.buffer.scrollTop = 0;
|
this._terminal.buffer.scrollTop = 0;
|
||||||
this._terminal.buffer.scrollBottom = this._terminal.rows - 1;
|
this._terminal.buffer.scrollBottom = this._terminal.rows - 1;
|
||||||
this._terminal.curAttr = DEFAULT_ATTR;
|
this._terminal.curAttrData = DEFAULT_ATTR_DATA;
|
||||||
this._terminal.buffer.x = this._terminal.buffer.y = 0; // ?
|
this._terminal.buffer.x = this._terminal.buffer.y = 0; // ?
|
||||||
this._terminal.charset = null;
|
this._terminal.charset = null;
|
||||||
this._terminal.glevel = 0; // ??
|
this._terminal.glevel = 0; // ??
|
||||||
@@ -1837,7 +1836,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
public saveCursor(params: number[]): void {
|
public saveCursor(params: number[]): void {
|
||||||
this._terminal.buffer.savedX = this._terminal.buffer.x;
|
this._terminal.buffer.savedX = this._terminal.buffer.x;
|
||||||
this._terminal.buffer.savedY = this._terminal.buffer.y;
|
this._terminal.buffer.savedY = this._terminal.buffer.y;
|
||||||
this._terminal.buffer.savedCurAttr = this._terminal.curAttr;
|
this._terminal.buffer.savedCurAttrData.fg = this._terminal.curAttrData.fg;
|
||||||
|
this._terminal.buffer.savedCurAttrData.bg = this._terminal.curAttrData.bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1849,7 +1849,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
public restoreCursor(params: number[]): void {
|
public restoreCursor(params: number[]): void {
|
||||||
this._terminal.buffer.x = this._terminal.buffer.savedX || 0;
|
this._terminal.buffer.x = this._terminal.buffer.savedX || 0;
|
||||||
this._terminal.buffer.y = this._terminal.buffer.savedY || 0;
|
this._terminal.buffer.y = this._terminal.buffer.savedY || 0;
|
||||||
this._terminal.curAttr = this._terminal.buffer.savedCurAttr || DEFAULT_ATTR;
|
this._terminal.curAttrData.fg = this._terminal.buffer.savedCurAttrData.fg;
|
||||||
|
this._terminal.curAttrData.bg = this._terminal.buffer.savedCurAttrData.bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import { assert, expect } from 'chai';
|
import { assert, expect } from 'chai';
|
||||||
import { Terminal } from './Terminal';
|
import { Terminal } from './Terminal';
|
||||||
import { MockViewport, MockCompositionHelper, MockRenderer } from './ui/TestUtils.test';
|
import { MockViewport, MockCompositionHelper, MockRenderer } from './ui/TestUtils.test';
|
||||||
import { DEFAULT_ATTR } from './Buffer';
|
import { DEFAULT_ATTR_DATA } from './Buffer';
|
||||||
import { CellData } from './BufferLine';
|
import { CellData } from './BufferLine';
|
||||||
|
|
||||||
const INIT_COLS = 80;
|
const INIT_COLS = 80;
|
||||||
@@ -260,7 +260,7 @@ describe('term.js addons', () => {
|
|||||||
assert.equal(term.buffer.lines.length, term.rows);
|
assert.equal(term.buffer.lines.length, term.rows);
|
||||||
assert.deepEqual(term.buffer.lines.get(0), promptLine);
|
assert.deepEqual(term.buffer.lines.get(0), promptLine);
|
||||||
for (let i = 1; i < term.rows; i++) {
|
for (let i = 1; i < term.rows; i++) {
|
||||||
assert.deepEqual(term.buffer.lines.get(i), term.buffer.getBlankLine(DEFAULT_ATTR));
|
assert.deepEqual(term.buffer.lines.get(i), term.buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
it('should clear a buffer larger than rows', () => {
|
it('should clear a buffer larger than rows', () => {
|
||||||
@@ -277,7 +277,7 @@ describe('term.js addons', () => {
|
|||||||
assert.equal(term.buffer.lines.length, term.rows);
|
assert.equal(term.buffer.lines.length, term.rows);
|
||||||
assert.deepEqual(term.buffer.lines.get(0), promptLine);
|
assert.deepEqual(term.buffer.lines.get(0), promptLine);
|
||||||
for (let i = 1; i < term.rows; i++) {
|
for (let i = 1; i < term.rows; i++) {
|
||||||
assert.deepEqual(term.buffer.lines.get(i), term.buffer.getBlankLine(DEFAULT_ATTR));
|
assert.deepEqual(term.buffer.lines.get(i), term.buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
it('should not break the prompt when cleared twice', () => {
|
it('should not break the prompt when cleared twice', () => {
|
||||||
@@ -290,7 +290,7 @@ describe('term.js addons', () => {
|
|||||||
assert.equal(term.buffer.lines.length, term.rows);
|
assert.equal(term.buffer.lines.length, term.rows);
|
||||||
assert.deepEqual(term.buffer.lines.get(0), promptLine);
|
assert.deepEqual(term.buffer.lines.get(0), promptLine);
|
||||||
for (let i = 1; i < term.rows; i++) {
|
for (let i = 1; i < term.rows; i++) {
|
||||||
assert.deepEqual(term.buffer.lines.get(i), term.buffer.getBlankLine(DEFAULT_ATTR));
|
assert.deepEqual(term.buffer.lines.get(i), term.buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+16
-79
@@ -21,11 +21,11 @@
|
|||||||
* http://linux.die.net/man/7/urxvt
|
* http://linux.die.net/man/7/urxvt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharData, CharacterJoinerHandler, IBufferLine } from './Types';
|
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharacterJoinerHandler, IBufferLine, IAttributeData } from './Types';
|
||||||
import { IMouseZoneManager } from './ui/Types';
|
import { IMouseZoneManager } from './ui/Types';
|
||||||
import { IRenderer } from './renderer/Types';
|
import { IRenderer } from './renderer/Types';
|
||||||
import { BufferSet } from './BufferSet';
|
import { BufferSet } from './BufferSet';
|
||||||
import { Buffer, MAX_BUFFER_SIZE, DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR } from './Buffer';
|
import { Buffer, MAX_BUFFER_SIZE, DEFAULT_ATTR_DATA } from './Buffer';
|
||||||
import { CompositionHelper } from './CompositionHelper';
|
import { CompositionHelper } from './CompositionHelper';
|
||||||
import { EventEmitter } from './common/EventEmitter';
|
import { EventEmitter } from './common/EventEmitter';
|
||||||
import { Viewport } from './Viewport';
|
import { Viewport } from './Viewport';
|
||||||
@@ -41,7 +41,6 @@ import { addDisposableDomListener } from './ui/Lifecycle';
|
|||||||
import * as Strings from './Strings';
|
import * as Strings from './Strings';
|
||||||
import { MouseHelper } from './ui/MouseHelper';
|
import { MouseHelper } from './ui/MouseHelper';
|
||||||
import { DEFAULT_BELL_SOUND, SoundManager } from './SoundManager';
|
import { DEFAULT_BELL_SOUND, SoundManager } from './SoundManager';
|
||||||
import { DEFAULT_ANSI_COLORS } from './renderer/ColorManager';
|
|
||||||
import { MouseZoneManager } from './ui/MouseZoneManager';
|
import { MouseZoneManager } from './ui/MouseZoneManager';
|
||||||
import { AccessibilityManager } from './AccessibilityManager';
|
import { AccessibilityManager } from './AccessibilityManager';
|
||||||
import { ScreenDprMonitor } from './ui/ScreenDprMonitor';
|
import { ScreenDprMonitor } from './ui/ScreenDprMonitor';
|
||||||
@@ -52,6 +51,7 @@ import { IKeyboardEvent } from './common/Types';
|
|||||||
import { evaluateKeyboardEvent } from './core/input/Keyboard';
|
import { evaluateKeyboardEvent } from './core/input/Keyboard';
|
||||||
import { KeyboardResultType, ICharset } from './core/Types';
|
import { KeyboardResultType, ICharset } from './core/Types';
|
||||||
import { clone } from './common/Clone';
|
import { clone } from './common/Clone';
|
||||||
|
import { Attributes } from './BufferLine';
|
||||||
|
|
||||||
// Let it work inside Node.js for automated testing purposes.
|
// Let it work inside Node.js for automated testing purposes.
|
||||||
const document = (typeof window !== 'undefined') ? window.document : null;
|
const document = (typeof window !== 'undefined') ? window.document : null;
|
||||||
@@ -168,7 +168,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
private _refreshEnd: number;
|
private _refreshEnd: number;
|
||||||
public savedCols: number;
|
public savedCols: number;
|
||||||
|
|
||||||
public curAttr: number;
|
public curAttrData: IAttributeData;
|
||||||
|
private _eraseAttrData: IAttributeData;
|
||||||
|
|
||||||
public params: (string | number)[];
|
public params: (string | number)[];
|
||||||
public currentParam: string | number;
|
public currentParam: string | number;
|
||||||
@@ -288,7 +289,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
// TODO: Can this be just []?
|
// TODO: Can this be just []?
|
||||||
this.charsets = [null];
|
this.charsets = [null];
|
||||||
|
|
||||||
this.curAttr = DEFAULT_ATTR;
|
this.curAttrData = DEFAULT_ATTR_DATA.clone();
|
||||||
|
this._eraseAttrData = DEFAULT_ATTR_DATA.clone();
|
||||||
|
|
||||||
this.params = [];
|
this.params = [];
|
||||||
this.currentParam = 0;
|
this.currentParam = 0;
|
||||||
@@ -328,9 +330,10 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
/**
|
/**
|
||||||
* back_color_erase feature for xterm.
|
* back_color_erase feature for xterm.
|
||||||
*/
|
*/
|
||||||
public eraseAttr(): number {
|
public eraseAttrData(): IAttributeData {
|
||||||
// if (this.is('screen')) return DEFAULT_ATTR;
|
this._eraseAttrData.bg &= ~(Attributes.CM_MASK | 0xFFFFFF);
|
||||||
return (DEFAULT_ATTR & ~0x1ff) | (this.curAttr & 0x1ff);
|
this._eraseAttrData.bg |= this.curAttrData.bg & ~0xFC000000;
|
||||||
|
return this._eraseAttrData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1175,8 +1178,9 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
public scroll(isWrapped: boolean = false): void {
|
public scroll(isWrapped: boolean = false): void {
|
||||||
let newLine: IBufferLine;
|
let newLine: IBufferLine;
|
||||||
newLine = this._blankLine;
|
newLine = this._blankLine;
|
||||||
if (!newLine || newLine.length !== this.cols || newLine.getFG(0) !== this.eraseAttr()) {
|
const eraseAttr = this.eraseAttrData();
|
||||||
newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped);
|
if (!newLine || newLine.length !== this.cols || newLine.getFG(0) !== eraseAttr.fg || newLine.getBG(0) !== eraseAttr.bg) {
|
||||||
|
newLine = this.buffer.getBlankLine(eraseAttr, isWrapped);
|
||||||
this._blankLine = newLine;
|
this._blankLine = newLine;
|
||||||
}
|
}
|
||||||
newLine.isWrapped = isWrapped;
|
newLine.isWrapped = isWrapped;
|
||||||
@@ -1745,23 +1749,12 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
this.buffer.ybase = 0;
|
this.buffer.ybase = 0;
|
||||||
this.buffer.y = 0;
|
this.buffer.y = 0;
|
||||||
for (let i = 1; i < this.rows; i++) {
|
for (let i = 1; i < this.rows; i++) {
|
||||||
this.buffer.lines.push(this.buffer.getBlankLine(DEFAULT_ATTR));
|
this.buffer.lines.push(this.buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||||
}
|
}
|
||||||
this.refresh(0, this.rows - 1);
|
this.refresh(0, this.rows - 1);
|
||||||
this.emit('scroll', this.buffer.ydisp);
|
this.emit('scroll', this.buffer.ydisp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If cur return the back color xterm feature attribute. Else return default attribute.
|
|
||||||
* @param cur
|
|
||||||
*/
|
|
||||||
public ch(cur?: boolean): CharData {
|
|
||||||
if (cur) {
|
|
||||||
return [this.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE];
|
|
||||||
}
|
|
||||||
return [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluate if the current terminal is the given argument.
|
* Evaluate if the current terminal is the given argument.
|
||||||
* @param term The terminal name to evaluate
|
* @param term The terminal name to evaluate
|
||||||
@@ -1837,7 +1830,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
// blankLine(true) is xterm/linux behavior
|
// blankLine(true) is xterm/linux behavior
|
||||||
const scrollRegionHeight = this.buffer.scrollBottom - this.buffer.scrollTop;
|
const scrollRegionHeight = this.buffer.scrollBottom - this.buffer.scrollTop;
|
||||||
this.buffer.lines.shiftElements(this.buffer.y + this.buffer.ybase, scrollRegionHeight, 1);
|
this.buffer.lines.shiftElements(this.buffer.y + this.buffer.ybase, scrollRegionHeight, 1);
|
||||||
this.buffer.lines.set(this.buffer.y + this.buffer.ybase, this.buffer.getBlankLine(this.eraseAttr()));
|
this.buffer.lines.set(this.buffer.y + this.buffer.ybase, this.buffer.getBlankLine(this.eraseAttrData()));
|
||||||
this.updateRange(this.buffer.scrollTop);
|
this.updateRange(this.buffer.scrollTop);
|
||||||
this.updateRange(this.buffer.scrollBottom);
|
this.updateRange(this.buffer.scrollBottom);
|
||||||
} else {
|
} else {
|
||||||
@@ -1882,46 +1875,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove when true color is implemented
|
|
||||||
public matchColor(r1: number, g1: number, b1: number): number {
|
|
||||||
const hash = (r1 << 16) | (g1 << 8) | b1;
|
|
||||||
|
|
||||||
if (matchColorCache[hash] !== null && matchColorCache[hash] !== undefined) {
|
|
||||||
return matchColorCache[hash];
|
|
||||||
}
|
|
||||||
|
|
||||||
let ldiff = Infinity;
|
|
||||||
let li = -1;
|
|
||||||
let i = 0;
|
|
||||||
let c: number;
|
|
||||||
let r2: number;
|
|
||||||
let g2: number;
|
|
||||||
let b2: number;
|
|
||||||
let diff: number;
|
|
||||||
|
|
||||||
for (; i < DEFAULT_ANSI_COLORS.length; i++) {
|
|
||||||
c = DEFAULT_ANSI_COLORS[i].rgba;
|
|
||||||
r2 = c >>> 24;
|
|
||||||
g2 = c >>> 16 & 0xFF;
|
|
||||||
b2 = c >>> 8 & 0xFF;
|
|
||||||
// assume that alpha is 0xFF
|
|
||||||
|
|
||||||
diff = matchColorDistance(r1, g1, b1, r2, g2, b2);
|
|
||||||
|
|
||||||
if (diff === 0) {
|
|
||||||
li = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diff < ldiff) {
|
|
||||||
ldiff = diff;
|
|
||||||
li = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchColorCache[hash] = li;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _visualBell(): boolean {
|
private _visualBell(): boolean {
|
||||||
return false;
|
return false;
|
||||||
// return this.options.bellStyle === 'visual' ||
|
// return this.options.bellStyle === 'visual' ||
|
||||||
@@ -1944,19 +1897,3 @@ function wasModifierKeyOnlyEvent(ev: KeyboardEvent): boolean {
|
|||||||
ev.keyCode === 17 || // Ctrl
|
ev.keyCode === 17 || // Ctrl
|
||||||
ev.keyCode === 18; // Alt
|
ev.keyCode === 18; // Alt
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO:
|
|
||||||
* The below color-related code can be removed when true color is implemented.
|
|
||||||
* It's only purpose is to match true color requests with the closest matching
|
|
||||||
* ANSI color code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const matchColorCache: {[colorRGBHash: number]: number} = {};
|
|
||||||
|
|
||||||
// http://stackoverflow.com/questions/1633828
|
|
||||||
function matchColorDistance(r1: number, g1: number, b1: number, r2: number, g2: number, b2: number): number {
|
|
||||||
return Math.pow(30 * (r1 - r2), 2)
|
|
||||||
+ Math.pow(59 * (g1 - g2), 2)
|
|
||||||
+ Math.pow(11 * (b1 - b2), 2);
|
|
||||||
}
|
|
||||||
|
|||||||
+48
-12
@@ -48,7 +48,7 @@ export interface IInputHandlingTerminal extends IEventEmitter {
|
|||||||
insertMode: boolean;
|
insertMode: boolean;
|
||||||
wraparoundMode: boolean;
|
wraparoundMode: boolean;
|
||||||
bracketedPasteMode: boolean;
|
bracketedPasteMode: boolean;
|
||||||
curAttr: number;
|
curAttrData: IAttributeData;
|
||||||
savedCols: number;
|
savedCols: number;
|
||||||
x10Mouse: boolean;
|
x10Mouse: boolean;
|
||||||
vt200Mouse: boolean;
|
vt200Mouse: boolean;
|
||||||
@@ -70,7 +70,7 @@ export interface IInputHandlingTerminal extends IEventEmitter {
|
|||||||
updateRange(y: number): void;
|
updateRange(y: number): void;
|
||||||
scroll(isWrapped?: boolean): void;
|
scroll(isWrapped?: boolean): void;
|
||||||
setgLevel(g: number): void;
|
setgLevel(g: number): void;
|
||||||
eraseAttr(): number;
|
eraseAttrData(): IAttributeData;
|
||||||
is(term: string): boolean;
|
is(term: string): boolean;
|
||||||
setgCharset(g: number, charset: ICharset): void;
|
setgCharset(g: number, charset: ICharset): void;
|
||||||
resize(x: number, y: number): void;
|
resize(x: number, y: number): void;
|
||||||
@@ -78,7 +78,6 @@ export interface IInputHandlingTerminal extends IEventEmitter {
|
|||||||
reset(): void;
|
reset(): void;
|
||||||
showCursor(): void;
|
showCursor(): void;
|
||||||
refresh(start: number, end: number): void;
|
refresh(start: number, end: number): void;
|
||||||
matchColor(r1: number, g1: number, b1: number): number;
|
|
||||||
error(text: string, data?: any): void;
|
error(text: string, data?: any): void;
|
||||||
setOption(key: string, value: any): void;
|
setOption(key: string, value: any): void;
|
||||||
tabSet(): void;
|
tabSet(): void;
|
||||||
@@ -289,17 +288,17 @@ export interface IBuffer {
|
|||||||
hasScrollback: boolean;
|
hasScrollback: boolean;
|
||||||
savedY: number;
|
savedY: number;
|
||||||
savedX: number;
|
savedX: number;
|
||||||
savedCurAttr: number;
|
savedCurAttrData: IAttributeData;
|
||||||
isCursorInViewport: boolean;
|
isCursorInViewport: boolean;
|
||||||
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string;
|
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string;
|
||||||
getWrappedRangeForLine(y: number): { first: number, last: number };
|
getWrappedRangeForLine(y: number): { first: number, last: number };
|
||||||
nextStop(x?: number): number;
|
nextStop(x?: number): number;
|
||||||
prevStop(x?: number): number;
|
prevStop(x?: number): number;
|
||||||
getBlankLine(attr: number, isWrapped?: boolean): IBufferLine;
|
getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine;
|
||||||
stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[];
|
stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[];
|
||||||
iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator;
|
iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator;
|
||||||
getNullCell(fg?: number, bg?: number): ICellData;
|
getNullCell(attr?: IAttributeData): ICellData;
|
||||||
getWhitespaceCell(fg?: number, bg?: number): ICellData;
|
getWhitespaceCell(attr?: IAttributeData): ICellData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IBufferSet extends IEventEmitter {
|
export interface IBufferSet extends IEventEmitter {
|
||||||
@@ -308,7 +307,7 @@ export interface IBufferSet extends IEventEmitter {
|
|||||||
active: IBuffer;
|
active: IBuffer;
|
||||||
|
|
||||||
activateNormalBuffer(): void;
|
activateNormalBuffer(): void;
|
||||||
activateAltBuffer(fillAttr?: number): void;
|
activateAltBuffer(fillAttr?: IAttributeData): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISelectionManager {
|
export interface ISelectionManager {
|
||||||
@@ -513,13 +512,50 @@ export interface IEscapeSequenceParser extends IDisposable {
|
|||||||
clearErrorHandler(): void;
|
clearErrorHandler(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cell data */
|
/** RGB color type */
|
||||||
export interface ICellData {
|
export type IColorRGB = [number, number, number];
|
||||||
content: number;
|
|
||||||
|
/** Attribute data */
|
||||||
|
export interface IAttributeData {
|
||||||
fg: number;
|
fg: number;
|
||||||
bg: number;
|
bg: number;
|
||||||
|
|
||||||
|
clone(): IAttributeData;
|
||||||
|
|
||||||
|
// flags
|
||||||
|
isInverse(): number;
|
||||||
|
isBold(): number;
|
||||||
|
isUnderline(): number;
|
||||||
|
isBlink(): number;
|
||||||
|
isInvisible(): number;
|
||||||
|
isItalic(): number;
|
||||||
|
isDim(): number;
|
||||||
|
|
||||||
|
// color modes
|
||||||
|
getColormodeFg(): number;
|
||||||
|
getColormodeBg(): number;
|
||||||
|
isFgRGB(): boolean;
|
||||||
|
isBgRGB(): boolean;
|
||||||
|
isFgPalette(): boolean;
|
||||||
|
isBgPalette(): boolean;
|
||||||
|
isFgDefault(): boolean;
|
||||||
|
isBgDefault(): boolean;
|
||||||
|
|
||||||
|
// colors
|
||||||
|
getFgColor(channels?: boolean): number | IColorRGB;
|
||||||
|
getBgColor(channels?: boolean): number | IColorRGB;
|
||||||
|
|
||||||
|
// shim for old API
|
||||||
|
getOldFlags(): number;
|
||||||
|
getOldFgColor(): number;
|
||||||
|
getOldBgColor(): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cell data */
|
||||||
|
export interface ICellData extends IAttributeData {
|
||||||
|
content: number;
|
||||||
combinedData: string;
|
combinedData: string;
|
||||||
combined: number;
|
isCombined: number;
|
||||||
width: number;
|
width: number;
|
||||||
chars: string;
|
chars: string;
|
||||||
code: number;
|
code: number;
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ export class TextRenderLayer extends BaseRenderLayer {
|
|||||||
// Can either represent character(s) for a single cell or multiple cells
|
// Can either represent character(s) for a single cell or multiple cells
|
||||||
// if indicated by a character joiner.
|
// if indicated by a character joiner.
|
||||||
let chars = this._cell.chars || WHITESPACE_CELL_CHAR;
|
let chars = this._cell.chars || WHITESPACE_CELL_CHAR;
|
||||||
const attr = this._cell.fg;
|
|
||||||
let width = this._cell.width;
|
let width = this._cell.width;
|
||||||
|
|
||||||
// If true, indicates that the current character(s) to draw were joined.
|
// If true, indicates that the current character(s) to draw were joined.
|
||||||
@@ -137,9 +136,9 @@ export class TextRenderLayer extends BaseRenderLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const flags = attr >> 18;
|
const flags = this._cell.getOldFlags();
|
||||||
let bg = attr & 0x1ff;
|
let bg = this._cell.getOldBgColor();
|
||||||
let fg = (attr >> 9) & 0x1ff;
|
let fg = this._cell.getOldFgColor();
|
||||||
|
|
||||||
// If inverse flag is on, the foreground should become the background.
|
// If inverse flag is on, the foreground should become the background.
|
||||||
if (flags & FLAGS.INVERSE) {
|
if (flags & FLAGS.INVERSE) {
|
||||||
|
|||||||
@@ -6,11 +6,9 @@
|
|||||||
import jsdom = require('jsdom');
|
import jsdom = require('jsdom');
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { DomRendererRowFactory } from './DomRendererRowFactory';
|
import { DomRendererRowFactory } from './DomRendererRowFactory';
|
||||||
import { DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR } from '../../Buffer';
|
import { DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR_DATA } from '../../Buffer';
|
||||||
import { FLAGS } from '../Types';
|
import { BufferLine, CellData, FgFlags, BgFlags, Attributes } from '../../BufferLine';
|
||||||
import { BufferLine, CellData } from '../../BufferLine';
|
|
||||||
import { IBufferLine } from '../../Types';
|
import { IBufferLine } from '../../Types';
|
||||||
import { DEFAULT_COLOR } from '../atlas/Types';
|
|
||||||
|
|
||||||
describe('DomRendererRowFactory', () => {
|
describe('DomRendererRowFactory', () => {
|
||||||
let dom: jsdom.JSDOM;
|
let dom: jsdom.JSDOM;
|
||||||
@@ -61,7 +59,9 @@ describe('DomRendererRowFactory', () => {
|
|||||||
|
|
||||||
describe('attributes', () => {
|
describe('attributes', () => {
|
||||||
it('should add class for bold', () => {
|
it('should add class for bold', () => {
|
||||||
lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR | (FLAGS.BOLD << 18), 'a', 1, 'a'.charCodeAt(0)]));
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
|
cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.BOLD;
|
||||||
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span class="xterm-bold">a</span>'
|
'<span class="xterm-bold">a</span>'
|
||||||
@@ -69,7 +69,9 @@ describe('DomRendererRowFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should add class for italic', () => {
|
it('should add class for italic', () => {
|
||||||
lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR | (FLAGS.ITALIC << 18), 'a', 1, 'a'.charCodeAt(0)]));
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
|
cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.ITALIC;
|
||||||
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span class="xterm-italic">a</span>'
|
'<span class="xterm-italic">a</span>'
|
||||||
@@ -77,9 +79,12 @@ describe('DomRendererRowFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should add classes for 256 foreground colors', () => {
|
it('should add classes for 256 foreground colors', () => {
|
||||||
const defaultAttrNoFgColor = (0 << 9) | (DEFAULT_COLOR << 0);
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
|
cell.fg |= Attributes.CM_P256;
|
||||||
for (let i = 0; i < 256; i++) {
|
for (let i = 0; i < 256; i++) {
|
||||||
lineData.setCell(0, CellData.fromCharData([defaultAttrNoFgColor | (i << 9), 'a', 1, 'a'.charCodeAt(0)]));
|
cell.fg &= ~Attributes.PCOLOR_MASK;
|
||||||
|
cell.fg |= i;
|
||||||
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
`<span class="xterm-fg-${i}">a</span>`
|
`<span class="xterm-fg-${i}">a</span>`
|
||||||
@@ -88,9 +93,12 @@ describe('DomRendererRowFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should add classes for 256 background colors', () => {
|
it('should add classes for 256 background colors', () => {
|
||||||
const defaultAttrNoBgColor = (DEFAULT_ATTR << 9) | (0 << 0);
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
|
cell.bg |= Attributes.CM_P256;
|
||||||
for (let i = 0; i < 256; i++) {
|
for (let i = 0; i < 256; i++) {
|
||||||
lineData.setCell(0, CellData.fromCharData([defaultAttrNoBgColor | (i << 0), 'a', 1, 'a'.charCodeAt(0)]));
|
cell.bg &= ~Attributes.PCOLOR_MASK;
|
||||||
|
cell.bg |= i;
|
||||||
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
`<span class="xterm-bg-${i}">a</span>`
|
`<span class="xterm-bg-${i}">a</span>`
|
||||||
@@ -99,7 +107,10 @@ describe('DomRendererRowFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly invert colors', () => {
|
it('should correctly invert colors', () => {
|
||||||
lineData.setCell(0, CellData.fromCharData([(FLAGS.INVERSE << 18) | (2 << 9) | (1 << 0), 'a', 1, 'a'.charCodeAt(0)]));
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
|
cell.fg |= Attributes.CM_P16 | 2 | FgFlags.INVERSE;
|
||||||
|
cell.bg |= Attributes.CM_P16 | 1;
|
||||||
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span class="xterm-fg-1 xterm-bg-2">a</span>'
|
'<span class="xterm-fg-1 xterm-bg-2">a</span>'
|
||||||
@@ -107,7 +118,10 @@ describe('DomRendererRowFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly invert default fg color', () => {
|
it('should correctly invert default fg color', () => {
|
||||||
lineData.setCell(0, CellData.fromCharData([(FLAGS.INVERSE << 18) | (DEFAULT_ATTR << 9) | (1 << 0), 'a', 1, 'a'.charCodeAt(0)]));
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
|
cell.fg |= FgFlags.INVERSE;
|
||||||
|
cell.bg |= Attributes.CM_P16 | 1;
|
||||||
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span class="xterm-fg-1 xterm-bg-257">a</span>'
|
'<span class="xterm-fg-1 xterm-bg-257">a</span>'
|
||||||
@@ -115,7 +129,9 @@ describe('DomRendererRowFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly invert default bg color', () => {
|
it('should correctly invert default bg color', () => {
|
||||||
lineData.setCell(0, CellData.fromCharData([(FLAGS.INVERSE << 18) | (1 << 9) | (DEFAULT_COLOR << 0), 'a', 1, 'a'.charCodeAt(0)]));
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
|
cell.fg |= Attributes.CM_P16 | 1 | FgFlags.INVERSE;
|
||||||
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
'<span class="xterm-fg-257 xterm-bg-1">a</span>'
|
'<span class="xterm-fg-257 xterm-bg-1">a</span>'
|
||||||
@@ -123,8 +139,12 @@ describe('DomRendererRowFactory', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should turn bold fg text bright', () => {
|
it('should turn bold fg text bright', () => {
|
||||||
|
const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
|
||||||
|
cell.fg |= FgFlags.BOLD | Attributes.CM_P16;
|
||||||
for (let i = 0; i < 8; i++) {
|
for (let i = 0; i < 8; i++) {
|
||||||
lineData.setCell(0, CellData.fromCharData([(FLAGS.BOLD << 18) | (i << 9) | (DEFAULT_COLOR << 0), 'a', 1, 'a'.charCodeAt(0)]));
|
cell.fg &= ~Attributes.PCOLOR_MASK;
|
||||||
|
cell.fg |= i;
|
||||||
|
lineData.setCell(0, cell);
|
||||||
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20);
|
||||||
assert.equal(getFragmentHtml(fragment),
|
assert.equal(getFragmentHtml(fragment),
|
||||||
`<span class="xterm-bold xterm-fg-${i + 8}">a</span>`
|
`<span class="xterm-bold xterm-fg-${i + 8}">a</span>`
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ export class DomRendererRowFactory {
|
|||||||
|
|
||||||
for (let x = 0; x < lineLength; x++) {
|
for (let x = 0; x < lineLength; x++) {
|
||||||
lineData.loadCell(x, this._cell);
|
lineData.loadCell(x, this._cell);
|
||||||
const attr = this._cell.fg;
|
|
||||||
const width = this._cell.width;
|
const width = this._cell.width;
|
||||||
|
|
||||||
// The character to the left is a wide character, drawing is owned by the char at x-1
|
// The character to the left is a wide character, drawing is owned by the char at x-1
|
||||||
@@ -54,9 +53,9 @@ export class DomRendererRowFactory {
|
|||||||
charElement.style.width = `${cellWidth * width}px`;
|
charElement.style.width = `${cellWidth * width}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const flags = attr >> 18;
|
const flags = this._cell.getOldFlags();
|
||||||
let bg = attr & 0x1ff;
|
let bg = this._cell.getOldBgColor();
|
||||||
let fg = (attr >> 9) & 0x1ff;
|
let fg = this._cell.getOldFgColor();
|
||||||
|
|
||||||
if (isCursorRow && x === cursorX) {
|
if (isCursorRow && x === cursorX) {
|
||||||
charElement.classList.add(CURSOR_CLASS);
|
charElement.classList.add(CURSOR_CLASS);
|
||||||
|
|||||||
@@ -4,12 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { IColorSet, IRenderer, IRenderDimensions, IColorManager } from '../renderer/Types';
|
import { IColorSet, IRenderer, IRenderDimensions, IColorManager } from '../renderer/Types';
|
||||||
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminal, IBuffer, IBufferSet, IBrowser, ICharMeasure, ISelectionManager, ITerminalOptions, ILinkifier, IMouseHelper, ILinkMatcherOptions, CharacterJoinerHandler, IBufferLine, IBufferStringIterator, ICellData } from '../Types';
|
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminal, IBuffer, IBufferSet, IBrowser, ICharMeasure, ISelectionManager, ITerminalOptions, ILinkifier, IMouseHelper, ILinkMatcherOptions, CharacterJoinerHandler, IBufferLine, IBufferStringIterator, ICellData, IAttributeData } from '../Types';
|
||||||
import { ICircularList, XtermListener } from '../common/Types';
|
import { ICircularList, XtermListener } from '../common/Types';
|
||||||
import { Buffer } from '../Buffer';
|
import { Buffer } from '../Buffer';
|
||||||
import * as Browser from '../core/Platform';
|
import * as Browser from '../core/Platform';
|
||||||
import { ITheme, IDisposable, IMarker } from 'xterm';
|
import { ITheme, IDisposable, IMarker } from 'xterm';
|
||||||
import { Terminal } from '../Terminal';
|
import { Terminal } from '../Terminal';
|
||||||
|
import { AttributeData } from '../BufferLine';
|
||||||
|
|
||||||
export class TestTerminal extends Terminal {
|
export class TestTerminal extends Terminal {
|
||||||
writeSync(data: string): void {
|
writeSync(data: string): void {
|
||||||
@@ -187,7 +188,7 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal {
|
|||||||
insertMode: boolean;
|
insertMode: boolean;
|
||||||
wraparoundMode: boolean;
|
wraparoundMode: boolean;
|
||||||
bracketedPasteMode: boolean;
|
bracketedPasteMode: boolean;
|
||||||
curAttr: number;
|
curAttrData = new AttributeData();
|
||||||
savedCols: number;
|
savedCols: number;
|
||||||
x10Mouse: boolean;
|
x10Mouse: boolean;
|
||||||
vt200Mouse: boolean;
|
vt200Mouse: boolean;
|
||||||
@@ -222,7 +223,7 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal {
|
|||||||
setgLevel(g: number): void {
|
setgLevel(g: number): void {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
eraseAttr(): number {
|
eraseAttrData(): IAttributeData {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
eraseRight(x: number, y: number): void {
|
eraseRight(x: number, y: number): void {
|
||||||
@@ -309,7 +310,7 @@ export class MockBuffer implements IBuffer {
|
|||||||
scrollTop: number;
|
scrollTop: number;
|
||||||
savedY: number;
|
savedY: number;
|
||||||
savedX: number;
|
savedX: number;
|
||||||
savedCurAttr: number;
|
savedCurAttrData = new AttributeData();
|
||||||
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string {
|
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string {
|
||||||
return Buffer.prototype.translateBufferLineToString.apply(this, arguments);
|
return Buffer.prototype.translateBufferLineToString.apply(this, arguments);
|
||||||
}
|
}
|
||||||
@@ -325,7 +326,7 @@ export class MockBuffer implements IBuffer {
|
|||||||
setLines(lines: ICircularList<IBufferLine>): void {
|
setLines(lines: ICircularList<IBufferLine>): void {
|
||||||
this.lines = lines;
|
this.lines = lines;
|
||||||
}
|
}
|
||||||
getBlankLine(attr: number, isWrapped?: boolean): IBufferLine {
|
getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine {
|
||||||
return Buffer.prototype.getBlankLine.apply(this, arguments);
|
return Buffer.prototype.getBlankLine.apply(this, arguments);
|
||||||
}
|
}
|
||||||
stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[] {
|
stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[] {
|
||||||
@@ -334,10 +335,10 @@ export class MockBuffer implements IBuffer {
|
|||||||
iterator(trimRight: boolean, startIndex?: number, endIndex?: number): IBufferStringIterator {
|
iterator(trimRight: boolean, startIndex?: number, endIndex?: number): IBufferStringIterator {
|
||||||
return Buffer.prototype.iterator.apply(this, arguments);
|
return Buffer.prototype.iterator.apply(this, arguments);
|
||||||
}
|
}
|
||||||
getNullCell(fg: number = 0, bg: number = 0): ICellData {
|
getNullCell(attr?: IAttributeData): ICellData {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
getWhitespaceCell(fg: number = 0, bg: number = 0): ICellData {
|
getWhitespaceCell(attr?: IAttributeData): ICellData {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user