add frontground/background color support for SerializeAddon

This commit is contained in:
javacs3
2019-11-21 20:41:46 +08:00
parent f088a9953f
commit d81b4999dc
3 changed files with 492 additions and 19 deletions
@@ -4,8 +4,10 @@
*/
import { Terminal, ITerminalAddon } from 'xterm';
// import { IBufferLine } from 'common/Types';
import { ICellData } from 'common/Types';
import { CellData } from 'common/buffer/CellData';
import { IBuffer } from 'common/buffer/Types';
import { Attributes, FgFlags, BgFlags } from 'common/buffer/Constants';
function crop(value: number | undefined, low: number, high: number, initial: number): number {
if (value === undefined) {
@@ -14,20 +16,184 @@ function crop(value: number | undefined, low: number, high: number, initial: num
return Math.max(low, Math.min(value, high));
}
class SerializeHandler {
class BaseSerializeHandler {
constructor(private _buffer: IBuffer) { }
serialize(start: number, end: number): string {
const rows = end - start;
const lines: string[] = new Array<string>(rows);
serialize(startRow: number, endRow: number): string {
let oldCell = new CellData();
for (let i = start; i < end; i++) {
const line = this._buffer.lines.get(i);
this._serializeStart(endRow - startRow);
lines[i - start] = line ? line.translateToString() : '';
for (let row = startRow; row < endRow; row++) {
const line = this._buffer.lines.get(row);
this._lineStart(row);
if (line) {
for (let col = 0; col < line.length; col++) {
const cell = new CellData();
line.loadCell(col, cell);
if (oldCell.fg !== cell.fg) {
this._fgChanged(cell, oldCell, row, col);
}
if (oldCell.bg !== cell.bg) {
this._bgChanged(cell, oldCell, row, col);
}
this._cellChanged(cell, oldCell, row, col);
oldCell = cell;
}
}
this._lineEnd(row);
}
return lines.join('\r\n');
this._serializeEnd();
return this._serializeFinished();
}
protected _cellChanged(cell: ICellData, oldCell: ICellData, row: number, col: number): void { }
protected _fgChanged(cell: ICellData, oldCell: ICellData, row: number, col: number): void { }
protected _bgChanged(cell: ICellData, oldCell: ICellData, row: number, col: number): void { }
protected _lineStart(row: number): void { }
protected _lineEnd(row: number): void { }
protected _serializeStart(rows: number): void { }
protected _serializeEnd(): void { }
protected _serializeFinished(): string { return ''; }
}
const FG_FM_MASK = FgFlags.FM_MASK;
const BG_FM_MASK = BgFlags.FM_MASK;
const COLOR_MASK = Attributes.CM_MASK | Attributes.RGB_MASK;
class StringSerializeHandler extends BaseSerializeHandler {
private _rowIndex: number = 0;
private _allRows: string[] = new Array<string>();
private _currentRow: string = '';
private _sgrSeq: string[] = [];
constructor(buffer: IBuffer) {
super(buffer);
}
protected _serializeStart(rows: number): void {
this._allRows = new Array<string>(rows);
}
protected _lineEnd(row: number): void {
this._allRows[this._rowIndex++] = this._currentRow;
this._currentRow = '';
}
protected _fgChanged(cell: ICellData, oldCell: ICellData, row: number, col: number): void {
const fgFlagsChanged = (cell.fg ^ oldCell.fg) & FG_FM_MASK;
const fgColorChanged = (cell.fg ^ oldCell.fg) & COLOR_MASK;
const sgrSeq = this._sgrSeq;
if ((cell.fg === 0) && (cell.bg === 0)) {
return;
}
if (fgFlagsChanged) {
if (fgFlagsChanged & FgFlags.INVERSE) {
sgrSeq.push(cell.isInverse() ? '7' : '27');
}
if (fgFlagsChanged & FgFlags.BOLD) {
sgrSeq.push(cell.isBold() ? '1' : '22');
}
if (fgFlagsChanged & FgFlags.UNDERLINE) {
sgrSeq.push(cell.isUnderline() ? '4' : '24');
}
if (fgFlagsChanged & FgFlags.BLINK) {
sgrSeq.push(cell.isBlink() ? '5' : '25');
}
if (fgFlagsChanged & FgFlags.INVISIBLE) {
sgrSeq.push(cell.isInvisible() ? '8' : '28');
}
}
if (fgColorChanged) {
const fgColor = cell.getFgColor();
if (cell.isFgDefault()) {
sgrSeq.push('39');
} else if (cell.isFgPalette()) {
switch (cell.getFgColorMode()) {
case Attributes.CM_P16: sgrSeq.push(`${30 + fgColor}`); break;
case Attributes.CM_P256: sgrSeq.push(`38;5;${fgColor}`); break;
}
} else if (cell.isFgRGB()) {
const [r, g, b] = CellData.toColorRGB(fgColor);
sgrSeq.push(`38;2;${r};${g};${b}`);
}
}
}
protected _bgChanged(cell: ICellData, oldCell: ICellData, row: number, col: number): void {
const bgFlagsChanged = (cell.bg ^ oldCell.bg) & BG_FM_MASK;
const bgColorChanged = (cell.bg ^ oldCell.bg) & COLOR_MASK;
const sgrSeq = this._sgrSeq;
if ((cell.bg === 0) && (cell.fg === 0)) {
return;
}
if (bgFlagsChanged) {
if (bgFlagsChanged & BgFlags.ITALIC) {
sgrSeq.push(cell.isItalic() ? '3' : '23');
}
if (bgFlagsChanged & BgFlags.DIM) {
sgrSeq.push(cell.isDim() ? '2' : '22');
}
}
if (bgColorChanged) {
const bgColor = cell.getBgColor();
if (cell.isBgDefault()) {
sgrSeq.push('49');
} else if (cell.isBgPalette()) {
switch (cell.getBgColorMode()) {
case Attributes.CM_P16: sgrSeq.push(`${40 + bgColor}`); break;
case Attributes.CM_P256: sgrSeq.push(`48;5;${bgColor}`); break;
}
} else if (cell.isFgRGB()) {
const [r, g, b] = CellData.toColorRGB(bgColor);
sgrSeq.push(`48;2;${r};${g};${b}`);
}
}
}
protected _cellChanged(cell: ICellData, oldCell: ICellData, row: number, col: number): void {
const fgChanged = cell.fg !== oldCell.fg;
const bgChanged = cell.bg !== oldCell.bg;
const isfgBgNormal = (cell.fg === 0) && (cell.bg === 0);
if ((fgChanged || bgChanged) && isfgBgNormal) {
this._currentRow += '\x1b[0m';
}
if (this._sgrSeq.length) {
this._currentRow += `\x1b[${this._sgrSeq.join(';')}m`;
this._sgrSeq = [];
}
this._currentRow += cell.getChars();
}
protected _serializeFinished(): string {
return this._allRows.join('\r\n');
}
}
@@ -41,13 +207,15 @@ export class SerializeAddon implements ITerminalAddon {
}
public serialize(rows?: number): string {
// TODO: Add frontground/background color support later
// TODO: Add re-position cursor support
// TODO: Add word wrap mode support
// TODO: Add combinedData support
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
const maxRows = this._terminal.rows;
const handler = new SerializeHandler((<any>this._terminal)._core.buffer);
const handler = new StringSerializeHandler((<any>this._terminal)._core.buffer);
rows = crop(rows, 0, maxRows, maxRows);