Skip to content

Instantly share code, notes, and snippets.

@jerch
Created August 27, 2019 22:14
Show Gist options
  • Save jerch/2dfaea9b52f8573456894dad70fa0f1c to your computer and use it in GitHub Desktop.
Save jerch/2dfaea9b52f8573456894dad70fa0f1c to your computer and use it in GitHub Desktop.
import { Terminal, ITerminalAddon } from 'xterm';
export const 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 const enum FgFlags {
/**
* bit 27..31 (32th bit unused)
*/
INVERSE = 0x4000000,
BOLD = 0x8000000,
UNDERLINE = 0x10000000,
BLINK = 0x20000000,
INVISIBLE = 0x40000000
}
export const enum BgFlags {
/**
* bit 27..32 (upper 4 unused)
*/
ITALIC = 0x4000000,
DIM = 0x8000000
}
export class SerializeAddon implements ITerminalAddon {
private _terminal: Terminal|undefined = undefined;
public dispose(): void {}
public activate(terminal: Terminal): void {
this._terminal = terminal;
}
private _extractAttributes(oldCell: any, newCell: any): string {
const switches: number[] = [];
if (oldCell.fg !== newCell.fg) {
// flags
if ((oldCell.fg & FgFlags.INVERSE) !== (newCell.fg & FgFlags.INVERSE)) {
switches.push(newCell.fg & FgFlags.INVERSE ? 7 : 27);
}
if ((oldCell.fg & FgFlags.BOLD) !== (newCell.fg & FgFlags.BOLD)) {
switches.push(newCell.fg & FgFlags.BOLD ? 1 : 22);
}
if ((oldCell.fg & FgFlags.UNDERLINE) !== (newCell.fg & FgFlags.UNDERLINE)) {
switches.push(newCell.fg & FgFlags.UNDERLINE ? 4 : 24);
}
if ((oldCell.fg & FgFlags.BLINK) !== (newCell.fg & FgFlags.BLINK)) {
switches.push(newCell.fg & FgFlags.BLINK ? 5 : 25);
}
if ((oldCell.fg & FgFlags.INVISIBLE) !== (newCell.fg & FgFlags.INVISIBLE)) {
switches.push(newCell.fg & FgFlags.INVISIBLE ? 8 : 28);
}
// colors
if ((oldCell.fg & (Attributes.CM_MASK | Attributes.RGB_MASK)) !== (newCell.fg & (Attributes.CM_MASK | Attributes.RGB_MASK))) {
switch (newCell.fg & Attributes.CM_MASK) {
case Attributes.CM_P16:
switches.push(newCell.fg & 8 ? 90 + (newCell.fg & 7) : 30 + (newCell.fg & 7));
break;
case Attributes.CM_P256:
switches.push(38);
switches.push(5);
switches.push(newCell.fg & 0xFF);
break;
case Attributes.CM_RGB:
switches.push(38);
switches.push(2);
switches.push((newCell.fg >> 16) & 0xFF);
switches.push((newCell.fg >> 8) & 0xFF);
switches.push(newCell.fg & 0xFF);
break;
default:
switches.push(39);
}
}
}
if (oldCell.bg !== newCell.bg) {
// flags
if ((oldCell.bg & BgFlags.ITALIC) !== (newCell.bg & BgFlags.ITALIC)) {
switches.push(newCell.bg & BgFlags.ITALIC ? 3 : 23);
}
if ((oldCell.bg & BgFlags.DIM) !== (newCell.bg & BgFlags.DIM)) {
switches.push(newCell.bg & BgFlags.DIM ? 2 : 22);
}
// colors
if ((oldCell.bg & (Attributes.CM_MASK | Attributes.RGB_MASK)) !== (newCell.bg & (Attributes.CM_MASK | Attributes.RGB_MASK))) {
switch (newCell.bg & Attributes.CM_MASK) {
case Attributes.CM_P16:
switches.push(newCell.bg & 8 ? 100 + (newCell.bg & 7) : 40 + (newCell.bg & 7));
break;
case Attributes.CM_P256:
switches.push(48);
switches.push(5);
switches.push(newCell.bg & 0xFF);
break;
case Attributes.CM_RGB:
switches.push(48);
switches.push(2);
switches.push((newCell.bg >> 16) & 0xFF);
switches.push((newCell.bg >> 8) & 0xFF);
switches.push(newCell.bg & 0xFF);
break;
default:
switches.push(49);
}
}
}
return `\x1b[${switches.join(';')}m`;
}
public serialize(): string {
if (!this._terminal) {
return '';
}
// grab cell ctor from private
const ctor = (this._terminal as any)._core._inputHandler._workCell.constructor;
const cell1 = new ctor();
const cell2 = new ctor();
let oldCell = cell1;
const result: string[] = [];
// peek into circularlist and grab all lines for now
const end = (this._terminal as any)._core.buffer.lines._length;
for (let y = 0; y < end; ++y) {
let sLine = '';
const line = (this._terminal as any)._core.buffer.lines.get(y);
const length = line.getTrimmedLength();
for (let x = 0; x < length;) {
const newCell = line.loadCell(x, oldCell === cell1 ? cell2 : cell1);
if (oldCell.fg !== newCell.fg || oldCell.bg !== newCell.bg) {
sLine += this._extractAttributes(oldCell, newCell);
oldCell = newCell;
}
sLine += newCell.getChars();
x += newCell.getWidth();
}
result.push(sLine);
}
return result.join('\r\n');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment