Merge branch 'master' into node_addons
This commit is contained in:
@@ -14,7 +14,10 @@ function constrain(value: number, low: number, high: number): number {
|
||||
|
||||
// TODO: Refine this template class later
|
||||
abstract class BaseSerializeHandler {
|
||||
constructor(private _buffer: IBuffer) { }
|
||||
constructor(
|
||||
protected readonly _buffer: IBuffer
|
||||
) {
|
||||
}
|
||||
|
||||
public serialize(startRow: number, endRow: number): string {
|
||||
// we need two of them to flip between old and new cell
|
||||
@@ -72,8 +75,6 @@ function equalFlags(cell1: IBufferCell, cell2: IBufferCell): boolean {
|
||||
&& cell1.isDim() === cell2.isDim();
|
||||
}
|
||||
|
||||
|
||||
|
||||
class StringSerializeHandler extends BaseSerializeHandler {
|
||||
private _rowIndex: number = 0;
|
||||
private _allRows: string[] = new Array<string>();
|
||||
@@ -84,7 +85,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
// we can see a full colored cell and a null cell that only have background the same style
|
||||
// but the information isn't preserved by null cell itself
|
||||
// so wee need to record it when required.
|
||||
private _cursorStyle: IBufferCell = this._buffer1.getNullCell();
|
||||
private _cursorStyle: IBufferCell = this._buffer.getNullCell();
|
||||
|
||||
// where exact the cursor styles comes from
|
||||
// because we can't copy the cell directly
|
||||
@@ -93,7 +94,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
private _cursorStyleCol: number = 0;
|
||||
|
||||
// this is a null cell for reference for checking whether background is empty or not
|
||||
private _backgroundCell: IBufferCell = this._buffer1.getNullCell();
|
||||
private _backgroundCell: IBufferCell = this._buffer.getNullCell();
|
||||
|
||||
private _firstRow: number = 0;
|
||||
private _lastCursorRow: number = 0;
|
||||
@@ -101,8 +102,11 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
private _lastContentCursorRow: number = 0;
|
||||
private _lastContentCursorCol: number = 0;
|
||||
|
||||
constructor(private _buffer1: IBuffer, private _terminal: Terminal) {
|
||||
super(_buffer1);
|
||||
constructor(
|
||||
buffer: IBuffer,
|
||||
private readonly _terminal: Terminal
|
||||
) {
|
||||
super(buffer);
|
||||
}
|
||||
|
||||
protected _beforeSerialize(rows: number, start: number, end: number): void {
|
||||
@@ -112,14 +116,14 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
this._firstRow = start;
|
||||
}
|
||||
|
||||
private _thisRowLastChar: IBufferCell = this._buffer1.getNullCell();
|
||||
private _thisRowLastSecondChar: IBufferCell = this._buffer1.getNullCell();
|
||||
private _nextRowFirstChar: IBufferCell = this._buffer1.getNullCell();
|
||||
private _thisRowLastChar: IBufferCell = this._buffer.getNullCell();
|
||||
private _thisRowLastSecondChar: IBufferCell = this._buffer.getNullCell();
|
||||
private _nextRowFirstChar: IBufferCell = this._buffer.getNullCell();
|
||||
protected _rowEnd(row: number, isLastRow: boolean): void {
|
||||
// if there is colorful empty cell at line end, whe must pad it back, or the the color block will missing
|
||||
if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) {
|
||||
// use clear right to set background.
|
||||
this._currentRow += `\x1b[${this._nullCellCount}X`;
|
||||
this._currentRow += `\u001b[${this._nullCellCount}X`;
|
||||
}
|
||||
|
||||
let rowSeparator = '';
|
||||
@@ -128,13 +132,13 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
if (!isLastRow) {
|
||||
// Enable BCE
|
||||
if (row - this._firstRow >= this._terminal.rows) {
|
||||
this._buffer1.getLine(this._cursorStyleRow)?.getCell(this._cursorStyleCol, this._backgroundCell);
|
||||
this._buffer.getLine(this._cursorStyleRow)?.getCell(this._cursorStyleCol, this._backgroundCell);
|
||||
}
|
||||
|
||||
// Fetch current line
|
||||
const currentLine = this._buffer1.getLine(row)!;
|
||||
const currentLine = this._buffer.getLine(row)!;
|
||||
// Fetch next line
|
||||
const nextLine = this._buffer1.getLine(row + 1)!;
|
||||
const nextLine = this._buffer.getLine(row + 1)!;
|
||||
|
||||
if (!nextLine.isWrapped) {
|
||||
// just insert the line break
|
||||
@@ -188,15 +192,15 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
// insert enough character to force the wrap
|
||||
rowSeparator = '-'.repeat(this._nullCellCount + 1);
|
||||
// move back and erase next line head
|
||||
rowSeparator += '\x1b[1D\x1b[1X';
|
||||
rowSeparator += '\u001b[1D\u001b[1X';
|
||||
|
||||
if (this._nullCellCount > 0) {
|
||||
// do these because we filled the last several null slot, which we shouldn't
|
||||
rowSeparator += '\x1b[A';
|
||||
rowSeparator += `\x1b[${currentLine.length - this._nullCellCount}C`;
|
||||
rowSeparator += `\x1b[${this._nullCellCount}X`;
|
||||
rowSeparator += `\x1b[${currentLine.length - this._nullCellCount}D`;
|
||||
rowSeparator += '\x1b[B';
|
||||
rowSeparator += '\u001b[A';
|
||||
rowSeparator += `\u001b[${currentLine.length - this._nullCellCount}C`;
|
||||
rowSeparator += `\u001b[${this._nullCellCount}X`;
|
||||
rowSeparator += `\u001b[${currentLine.length - this._nullCellCount}D`;
|
||||
rowSeparator += '\u001b[B';
|
||||
}
|
||||
|
||||
// This is content and need the be serialized even it is invisible.
|
||||
@@ -286,20 +290,20 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
if (this._nullCellCount > 0) {
|
||||
// use clear right to set background.
|
||||
if (!equalBg(this._cursorStyle, this._backgroundCell)) {
|
||||
this._currentRow += `\x1b[${this._nullCellCount}X`;
|
||||
this._currentRow += `\u001b[${this._nullCellCount}X`;
|
||||
}
|
||||
// use move right to move cursor.
|
||||
this._currentRow += `\x1b[${this._nullCellCount}C`;
|
||||
this._currentRow += `\u001b[${this._nullCellCount}C`;
|
||||
this._nullCellCount = 0;
|
||||
}
|
||||
|
||||
this._lastContentCursorRow = this._lastCursorRow = row;
|
||||
this._lastContentCursorCol = this._lastCursorCol = col;
|
||||
|
||||
this._currentRow += `\x1b[${sgrSeq.join(';')}m`;
|
||||
this._currentRow += `\u001b[${sgrSeq.join(';')}m`;
|
||||
|
||||
// update the last cursor style
|
||||
const line = this._buffer1.getLine(row);
|
||||
const line = this._buffer.getLine(row);
|
||||
if (line !== undefined) {
|
||||
line.getCell(col, this._cursorStyle);
|
||||
this._cursorStyleRow = row;
|
||||
@@ -318,10 +322,10 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
// because style change is handled by previous stage
|
||||
// use move right when background is empty, use clear right when there is background.
|
||||
if (equalBg(this._cursorStyle, this._backgroundCell)) {
|
||||
this._currentRow += `\x1b[${this._nullCellCount}C`;
|
||||
this._currentRow += `\u001b[${this._nullCellCount}C`;
|
||||
} else {
|
||||
this._currentRow += `\x1b[${this._nullCellCount}X`;
|
||||
this._currentRow += `\x1b[${this._nullCellCount}C`;
|
||||
this._currentRow += `\u001b[${this._nullCellCount}X`;
|
||||
this._currentRow += `\u001b[${this._nullCellCount}C`;
|
||||
}
|
||||
this._nullCellCount = 0;
|
||||
}
|
||||
@@ -339,7 +343,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
|
||||
// the fixup is only required for data without scrollback
|
||||
// because it will always be placed at last line otherwise
|
||||
if (this._buffer1.length - this._firstRow <= this._terminal.rows) {
|
||||
if (this._buffer.length - this._firstRow <= this._terminal.rows) {
|
||||
rowEnd = this._lastContentCursorRow + 1 - this._firstRow;
|
||||
this._lastCursorCol = this._lastContentCursorCol;
|
||||
this._lastCursorRow = this._lastContentCursorRow;
|
||||
@@ -355,8 +359,8 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
}
|
||||
|
||||
// restore the cursor
|
||||
const realCursorRow = this._buffer1.baseY + this._buffer1.cursorY;
|
||||
const realCursorCol = this._buffer1.cursorX;
|
||||
const realCursorRow = this._buffer.baseY + this._buffer.cursorY;
|
||||
const realCursorCol = this._buffer.cursorX;
|
||||
|
||||
const cursorMoved = (realCursorRow !== this._lastCursorRow || realCursorCol !== this._lastCursorCol);
|
||||
|
||||
@@ -380,7 +384,6 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
moveRight(realCursorCol - this._lastCursorCol);
|
||||
}
|
||||
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
@@ -394,14 +397,40 @@ export class SerializeAddon implements ITerminalAddon {
|
||||
this._terminal = terminal;
|
||||
}
|
||||
|
||||
private _getString(buffer: IBuffer, scrollback?: number): string {
|
||||
private _serializeBuffer(terminal: Terminal, buffer: IBuffer, scrollback?: number): string {
|
||||
const maxRows = buffer.length;
|
||||
const handler = new StringSerializeHandler(buffer, this._terminal!);
|
||||
const handler = new StringSerializeHandler(buffer, terminal);
|
||||
const correctRows = (scrollback === undefined) ? maxRows : constrain(scrollback + terminal.rows, 0, maxRows);
|
||||
return handler.serialize(maxRows - correctRows, maxRows);
|
||||
}
|
||||
|
||||
const correctRows = (scrollback === undefined) ? maxRows : constrain(scrollback + this!._terminal!.rows, 0, maxRows);
|
||||
const result = handler.serialize(maxRows - correctRows, maxRows);
|
||||
private _serializeModes(terminal: Terminal): string {
|
||||
let content = '';
|
||||
const modes = terminal.modes;
|
||||
|
||||
return result;
|
||||
// Default: false
|
||||
if (modes.applicationCursorKeysMode) content += '\x1b[?1h';
|
||||
if (modes.applicationKeypadMode) content += '\x1b[?66h';
|
||||
if (modes.bracketedPasteMode) content += '\x1b[?2004h';
|
||||
if (modes.insertMode) content += '\x1b[4h';
|
||||
if (modes.originMode) content += '\x1b[?6h';
|
||||
if (modes.reverseWraparoundMode) content += '\x1b[?45h';
|
||||
if (modes.sendFocusMode) content += '\x1b[?1004h';
|
||||
|
||||
// Default: true
|
||||
if (modes.wraparoundMode === false) content += '\x1b[?7l';
|
||||
|
||||
// Default: 'none'
|
||||
if (modes.mouseTrackingMode !== 'none') {
|
||||
switch (modes.mouseTrackingMode) {
|
||||
case 'x10': content += '\x1b[?9h'; break;
|
||||
case 'vt200': content += '\x1b[?1000h'; break;
|
||||
case 'drag': content += '\x1b[?1002h'; break;
|
||||
case 'any': content += '\x1b[?1003h'; break;
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public serialize(scrollback?: number): string {
|
||||
@@ -410,17 +439,19 @@ export class SerializeAddon implements ITerminalAddon {
|
||||
throw new Error('Cannot use addon until it has been loaded');
|
||||
}
|
||||
|
||||
if (this._terminal.buffer.active.type === 'normal') {
|
||||
return this._getString(this._terminal.buffer.active, scrollback);
|
||||
// Normal buffer
|
||||
let content = this._serializeBuffer(this._terminal, this._terminal.buffer.normal, scrollback);
|
||||
|
||||
// Alternate buffer
|
||||
if (this._terminal.buffer.active.type === 'alternate') {
|
||||
const alternativeScreenContent = this._serializeBuffer(this._terminal, this._terminal.buffer.alternate, undefined);
|
||||
content += `\u001b[?1049h\u001b[H${alternativeScreenContent}`;
|
||||
}
|
||||
|
||||
const normalScreenContent = this._getString(this._terminal.buffer.normal, scrollback);
|
||||
// alt screen don't have scrollback
|
||||
const alternativeScreenContent = this._getString(this._terminal.buffer.alternate, undefined);
|
||||
// Modes
|
||||
content += this._serializeModes(this._terminal);
|
||||
|
||||
return normalScreenContent
|
||||
+ '\u001b[?1049h\u001b[H'
|
||||
+ alternativeScreenContent;
|
||||
return content;
|
||||
}
|
||||
|
||||
public dispose(): void { }
|
||||
|
||||
@@ -30,6 +30,12 @@ const testNormalScreenEqual = async (page: any, str: string): Promise<void> => {
|
||||
assert.equal(JSON.stringify(originalBuffer), JSON.stringify(newBuffer));
|
||||
};
|
||||
|
||||
async function testSerializeEquals(writeContent: string, expectedSerialized: string): Promise<void> {
|
||||
await writeRawSync(page, writeContent);
|
||||
const result = await page.evaluate(`serializeAddon.serialize();`) as string;
|
||||
assert.strictEqual(result, expectedSerialized);
|
||||
}
|
||||
|
||||
describe('SerializeAddon', () => {
|
||||
before(async function(): Promise<any> {
|
||||
const browserType = getBrowserType();
|
||||
@@ -89,8 +95,6 @@ describe('SerializeAddon', () => {
|
||||
});
|
||||
|
||||
it('empty content', async function(): Promise<any> {
|
||||
const rows = 10;
|
||||
const cols = 10;
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), '');
|
||||
});
|
||||
|
||||
@@ -177,17 +181,17 @@ describe('SerializeAddon', () => {
|
||||
const cols = 10;
|
||||
const line = '+'.repeat(cols);
|
||||
const lines: string[] = [
|
||||
mkSGR(FG_P16_GREEN) + line, // Workaround: If we clear all flags a the end, serialize will use \x1b[0m to clear instead of the sepcific disable sequence
|
||||
mkSGR(INVERSE) + line,
|
||||
mkSGR(BOLD) + line,
|
||||
mkSGR(UNDERLINED) + line,
|
||||
mkSGR(BLINK) + line,
|
||||
mkSGR(INVISIBLE) + line,
|
||||
mkSGR(NO_INVERSE) + line,
|
||||
mkSGR(NO_BOLD) + line,
|
||||
mkSGR(NO_UNDERLINED) + line,
|
||||
mkSGR(NO_BLINK) + line,
|
||||
mkSGR(NO_INVISIBLE) + line
|
||||
sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags a the end, serialize will use \x1b[0m to clear instead of the sepcific disable sequence
|
||||
sgr(INVERSE) + line,
|
||||
sgr(BOLD) + line,
|
||||
sgr(UNDERLINED) + line,
|
||||
sgr(BLINK) + line,
|
||||
sgr(INVISIBLE) + line,
|
||||
sgr(NO_INVERSE) + line,
|
||||
sgr(NO_BOLD) + line,
|
||||
sgr(NO_UNDERLINED) + line,
|
||||
sgr(NO_BLINK) + line,
|
||||
sgr(NO_INVISIBLE) + line
|
||||
];
|
||||
const rows = lines.length;
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
@@ -209,16 +213,16 @@ describe('SerializeAddon', () => {
|
||||
const cols = 10;
|
||||
const line = '+'.repeat(cols);
|
||||
const lines: string[] = [
|
||||
mkSGR(FG_P16_RED) + line, // fg Red,
|
||||
mkSGR(UNDERLINED) + line, // fg Red, Underlined
|
||||
mkSGR(FG_P16_GREEN) + line, // fg Green, Underlined
|
||||
mkSGR(INVERSE) + line, // fg Green, Underlined, Inverse
|
||||
mkSGR(NO_INVERSE) + line, // fg Green, Underlined
|
||||
mkSGR(INVERSE) + line, // fg Green, Underlined, Inverse
|
||||
mkSGR(BG_P16_YELLOW) + line, // fg Green, bg Yellow, Underlined, Inverse
|
||||
mkSGR(FG_RESET) + line, // bg Yellow, Underlined, Inverse
|
||||
mkSGR(BG_RESET) + line, // Underlined, Inverse
|
||||
mkSGR(NORMAL) + line // Back to normal
|
||||
sgr(FG_P16_RED) + line, // fg Red,
|
||||
sgr(UNDERLINED) + line, // fg Red, Underlined
|
||||
sgr(FG_P16_GREEN) + line, // fg Green, Underlined
|
||||
sgr(INVERSE) + line, // fg Green, Underlined, Inverse
|
||||
sgr(NO_INVERSE) + line, // fg Green, Underlined
|
||||
sgr(INVERSE) + line, // fg Green, Underlined, Inverse
|
||||
sgr(BG_P16_YELLOW) + line, // fg Green, bg Yellow, Underlined, Inverse
|
||||
sgr(FG_RESET) + line, // bg Yellow, Underlined, Inverse
|
||||
sgr(BG_RESET) + line, // Underlined, Inverse
|
||||
sgr(NORMAL) + line // Back to normal
|
||||
];
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
|
||||
@@ -228,19 +232,19 @@ describe('SerializeAddon', () => {
|
||||
const cols = 10;
|
||||
const line = '+'.repeat(cols);
|
||||
const lines: string[] = [
|
||||
mkSGR(FG_P16_RED) + line, // fg Red
|
||||
mkSGR(FG_P16_GREEN, BG_P16_YELLOW) + line, // fg Green, bg Yellow
|
||||
mkSGR(UNDERLINED, ITALIC) + line, // fg Green, bg Yellow, Underlined, Italic
|
||||
mkSGR(NO_UNDERLINED, NO_ITALIC) + line, // fg Green, bg Yellow
|
||||
mkSGR(FG_RESET, ITALIC) + line, // bg Yellow, Italic
|
||||
mkSGR(BG_RESET) + line, // Italic
|
||||
mkSGR(NORMAL) + line, // Back to normal
|
||||
mkSGR(FG_P16_RED) + line, // fg Red
|
||||
mkSGR(FG_P16_GREEN, BG_P16_YELLOW) + line, // fg Green, bg Yellow
|
||||
mkSGR(UNDERLINED, ITALIC) + line, // fg Green, bg Yellow, Underlined, Italic
|
||||
mkSGR(NO_UNDERLINED, NO_ITALIC) + line, // fg Green, bg Yellow
|
||||
mkSGR(FG_RESET, ITALIC) + line, // bg Yellow, Italic
|
||||
mkSGR(BG_RESET) + line // Italic
|
||||
sgr(FG_P16_RED) + line, // fg Red
|
||||
sgr(FG_P16_GREEN, BG_P16_YELLOW) + line, // fg Green, bg Yellow
|
||||
sgr(UNDERLINED, ITALIC) + line, // fg Green, bg Yellow, Underlined, Italic
|
||||
sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green, bg Yellow
|
||||
sgr(FG_RESET, ITALIC) + line, // bg Yellow, Italic
|
||||
sgr(BG_RESET) + line, // Italic
|
||||
sgr(NORMAL) + line, // Back to normal
|
||||
sgr(FG_P16_RED) + line, // fg Red
|
||||
sgr(FG_P16_GREEN, BG_P16_YELLOW) + line, // fg Green, bg Yellow
|
||||
sgr(UNDERLINED, ITALIC) + line, // fg Green, bg Yellow, Underlined, Italic
|
||||
sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green, bg Yellow
|
||||
sgr(FG_RESET, ITALIC) + line, // bg Yellow, Italic
|
||||
sgr(BG_RESET) + line // Italic
|
||||
];
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
|
||||
@@ -250,16 +254,16 @@ describe('SerializeAddon', () => {
|
||||
const cols = 10;
|
||||
const line = '+'.repeat(cols);
|
||||
const lines: string[] = [
|
||||
mkSGR(FG_P256_RED) + line, // fg Red 256,
|
||||
mkSGR(UNDERLINED) + line, // fg Red 256, Underlined
|
||||
mkSGR(FG_P256_GREEN) + line, // fg Green 256, Underlined
|
||||
mkSGR(INVERSE) + line, // fg Green 256, Underlined, Inverse
|
||||
mkSGR(NO_INVERSE) + line, // fg Green 256, Underlined
|
||||
mkSGR(INVERSE) + line, // fg Green 256, Underlined, Inverse
|
||||
mkSGR(BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256, Underlined, Inverse
|
||||
mkSGR(FG_RESET) + line, // bg Yellow 256, Underlined, Inverse
|
||||
mkSGR(BG_RESET) + line, // Underlined, Inverse
|
||||
mkSGR(NORMAL) + line // Back to normal
|
||||
sgr(FG_P256_RED) + line, // fg Red 256,
|
||||
sgr(UNDERLINED) + line, // fg Red 256, Underlined
|
||||
sgr(FG_P256_GREEN) + line, // fg Green 256, Underlined
|
||||
sgr(INVERSE) + line, // fg Green 256, Underlined, Inverse
|
||||
sgr(NO_INVERSE) + line, // fg Green 256, Underlined
|
||||
sgr(INVERSE) + line, // fg Green 256, Underlined, Inverse
|
||||
sgr(BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256, Underlined, Inverse
|
||||
sgr(FG_RESET) + line, // bg Yellow 256, Underlined, Inverse
|
||||
sgr(BG_RESET) + line, // Underlined, Inverse
|
||||
sgr(NORMAL) + line // Back to normal
|
||||
];
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
|
||||
@@ -269,19 +273,19 @@ describe('SerializeAddon', () => {
|
||||
const cols = 10;
|
||||
const line = '+'.repeat(cols);
|
||||
const lines: string[] = [
|
||||
mkSGR(FG_P256_RED) + line, // fg Red 256
|
||||
mkSGR(FG_P256_GREEN, BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256
|
||||
mkSGR(UNDERLINED, ITALIC) + line, // fg Green 256, bg Yellow 256, Underlined, Italic
|
||||
mkSGR(NO_UNDERLINED, NO_ITALIC) + line, // fg Green 256, bg Yellow 256
|
||||
mkSGR(FG_RESET, ITALIC) + line, // bg Yellow 256, Italic
|
||||
mkSGR(BG_RESET) + line, // Italic
|
||||
mkSGR(NORMAL) + line, // Back to normal
|
||||
mkSGR(FG_P256_RED) + line, // fg Red 256
|
||||
mkSGR(FG_P256_GREEN, BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256
|
||||
mkSGR(UNDERLINED, ITALIC) + line, // fg Green 256, bg Yellow 256, Underlined, Italic
|
||||
mkSGR(NO_UNDERLINED, NO_ITALIC) + line, // fg Green 256, bg Yellow 256
|
||||
mkSGR(FG_RESET, ITALIC) + line, // bg Yellow 256, Italic
|
||||
mkSGR(BG_RESET) + line // Italic
|
||||
sgr(FG_P256_RED) + line, // fg Red 256
|
||||
sgr(FG_P256_GREEN, BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256
|
||||
sgr(UNDERLINED, ITALIC) + line, // fg Green 256, bg Yellow 256, Underlined, Italic
|
||||
sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green 256, bg Yellow 256
|
||||
sgr(FG_RESET, ITALIC) + line, // bg Yellow 256, Italic
|
||||
sgr(BG_RESET) + line, // Italic
|
||||
sgr(NORMAL) + line, // Back to normal
|
||||
sgr(FG_P256_RED) + line, // fg Red 256
|
||||
sgr(FG_P256_GREEN, BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256
|
||||
sgr(UNDERLINED, ITALIC) + line, // fg Green 256, bg Yellow 256, Underlined, Italic
|
||||
sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green 256, bg Yellow 256
|
||||
sgr(FG_RESET, ITALIC) + line, // bg Yellow 256, Italic
|
||||
sgr(BG_RESET) + line // Italic
|
||||
];
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
|
||||
@@ -291,16 +295,16 @@ describe('SerializeAddon', () => {
|
||||
const cols = 10;
|
||||
const line = '+'.repeat(cols);
|
||||
const lines: string[] = [
|
||||
mkSGR(FG_RGB_RED) + line, // fg Red RGB,
|
||||
mkSGR(UNDERLINED) + line, // fg Red RGB, Underlined
|
||||
mkSGR(FG_RGB_GREEN) + line, // fg Green RGB, Underlined
|
||||
mkSGR(INVERSE) + line, // fg Green RGB, Underlined, Inverse
|
||||
mkSGR(NO_INVERSE) + line, // fg Green RGB, Underlined
|
||||
mkSGR(INVERSE) + line, // fg Green RGB, Underlined, Inverse
|
||||
mkSGR(BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB, Underlined, Inverse
|
||||
mkSGR(FG_RESET) + line, // bg Yellow RGB, Underlined, Inverse
|
||||
mkSGR(BG_RESET) + line, // Underlined, Inverse
|
||||
mkSGR(NORMAL) + line // Back to normal
|
||||
sgr(FG_RGB_RED) + line, // fg Red RGB,
|
||||
sgr(UNDERLINED) + line, // fg Red RGB, Underlined
|
||||
sgr(FG_RGB_GREEN) + line, // fg Green RGB, Underlined
|
||||
sgr(INVERSE) + line, // fg Green RGB, Underlined, Inverse
|
||||
sgr(NO_INVERSE) + line, // fg Green RGB, Underlined
|
||||
sgr(INVERSE) + line, // fg Green RGB, Underlined, Inverse
|
||||
sgr(BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB, Underlined, Inverse
|
||||
sgr(FG_RESET) + line, // bg Yellow RGB, Underlined, Inverse
|
||||
sgr(BG_RESET) + line, // Underlined, Inverse
|
||||
sgr(NORMAL) + line // Back to normal
|
||||
];
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
|
||||
@@ -310,19 +314,19 @@ describe('SerializeAddon', () => {
|
||||
const cols = 10;
|
||||
const line = '+'.repeat(cols);
|
||||
const lines: string[] = [
|
||||
mkSGR(FG_RGB_RED) + line, // fg Red RGB
|
||||
mkSGR(FG_RGB_GREEN, BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB
|
||||
mkSGR(UNDERLINED, ITALIC) + line, // fg Green RGB, bg Yellow RGB, Underlined, Italic
|
||||
mkSGR(NO_UNDERLINED, NO_ITALIC) + line, // fg Green RGB, bg Yellow RGB
|
||||
mkSGR(FG_RESET, ITALIC) + line, // bg Yellow RGB, Italic
|
||||
mkSGR(BG_RESET) + line, // Italic
|
||||
mkSGR(NORMAL) + line, // Back to normal
|
||||
mkSGR(FG_RGB_RED) + line, // fg Red RGB
|
||||
mkSGR(FG_RGB_GREEN, BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB
|
||||
mkSGR(UNDERLINED, ITALIC) + line, // fg Green RGB, bg Yellow RGB, Underlined, Italic
|
||||
mkSGR(NO_UNDERLINED, NO_ITALIC) + line, // fg Green RGB, bg Yellow RGB
|
||||
mkSGR(FG_RESET, ITALIC) + line, // bg Yellow RGB, Italic
|
||||
mkSGR(BG_RESET) + line // Italic
|
||||
sgr(FG_RGB_RED) + line, // fg Red RGB
|
||||
sgr(FG_RGB_GREEN, BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB
|
||||
sgr(UNDERLINED, ITALIC) + line, // fg Green RGB, bg Yellow RGB, Underlined, Italic
|
||||
sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green RGB, bg Yellow RGB
|
||||
sgr(FG_RESET, ITALIC) + line, // bg Yellow RGB, Italic
|
||||
sgr(BG_RESET) + line, // Italic
|
||||
sgr(NORMAL) + line, // Back to normal
|
||||
sgr(FG_RGB_RED) + line, // fg Red RGB
|
||||
sgr(FG_RGB_GREEN, BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB
|
||||
sgr(UNDERLINED, ITALIC) + line, // fg Green RGB, bg Yellow RGB, Underlined, Italic
|
||||
sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green RGB, bg Yellow RGB
|
||||
sgr(FG_RESET, ITALIC) + line, // bg Yellow RGB, Italic
|
||||
sgr(BG_RESET) + line // Italic
|
||||
];
|
||||
await writeSync(page, lines.join('\\r\\n'));
|
||||
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
|
||||
@@ -481,6 +485,52 @@ describe('SerializeAddon', () => {
|
||||
|
||||
await testNormalScreenEqual(page, lines.join(''));
|
||||
});
|
||||
|
||||
describe('handle modes', () => {
|
||||
it('applicationCursorKeysMode', async () => {
|
||||
await testSerializeEquals('test\u001b[?1h', 'test\u001b[?1h');
|
||||
await testSerializeEquals('\u001b[?1l', 'test');
|
||||
});
|
||||
it('applicationKeypadMode', async () => {
|
||||
await testSerializeEquals('test\u001b[?66h', 'test\u001b[?66h');
|
||||
await testSerializeEquals('\u001b[?66l', 'test');
|
||||
});
|
||||
it('bracketedPasteMode', async () => {
|
||||
await testSerializeEquals('test\u001b[?2004h', 'test\u001b[?2004h');
|
||||
await testSerializeEquals('\u001b[?2004l', 'test');
|
||||
});
|
||||
it('insertMode', async () => {
|
||||
await testSerializeEquals('test\u001b[4h', 'test\u001b[4h');
|
||||
await testSerializeEquals('\u001b[4l', 'test');
|
||||
});
|
||||
it('mouseTrackingMode', async () => {
|
||||
await testSerializeEquals('test\u001b[?9h', 'test\u001b[?9h');
|
||||
await testSerializeEquals('\u001b[?9l', 'test');
|
||||
await testSerializeEquals('\u001b[?1000h', 'test\u001b[?1000h');
|
||||
await testSerializeEquals('\u001b[?1000l', 'test');
|
||||
await testSerializeEquals('\u001b[?1002h', 'test\u001b[?1002h');
|
||||
await testSerializeEquals('\u001b[?1002l', 'test');
|
||||
await testSerializeEquals('\u001b[?1003h', 'test\u001b[?1003h');
|
||||
await testSerializeEquals('\u001b[?1003l', 'test');
|
||||
});
|
||||
it('originMode', async () => {
|
||||
// origin mode moves cursor to (0,0)
|
||||
await testSerializeEquals('test\u001b[?6h', 'test\u001b[4D\u001b[?6h');
|
||||
await testSerializeEquals('\u001b[?6l', 'test\u001b[4D');
|
||||
});
|
||||
it('reverseWraparoundMode', async () => {
|
||||
await testSerializeEquals('test\u001b[?45h', 'test\u001b[?45h');
|
||||
await testSerializeEquals('\u001b[?45l', 'test');
|
||||
});
|
||||
it('sendFocusMode', async () => {
|
||||
await testSerializeEquals('test\u001b[?1004h', 'test\u001b[?1004h');
|
||||
await testSerializeEquals('\u001b[?1004l', 'test');
|
||||
});
|
||||
it('wraparoundMode', async () => {
|
||||
await testSerializeEquals('test\u001b[?7l', 'test\u001b[?7l');
|
||||
await testSerializeEquals('\u001b[?7h', 'test');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function newArray<T>(initial: T | ((index: number) => T), count: number): T[] {
|
||||
@@ -503,7 +553,7 @@ function digitsString(length: number, from: number = 0, sgr: string = ''): strin
|
||||
return s;
|
||||
}
|
||||
|
||||
function mkSGR(...seq: string[]): string {
|
||||
function sgr(...seq: string[]): string {
|
||||
return `\x1b[${seq.join(';')}m`;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
|
||||
import { Terminal, ITerminalAddon } from 'xterm';
|
||||
|
||||
declare module 'xterm-addon-serialize' {
|
||||
@@ -21,12 +20,13 @@ declare module 'xterm-addon-serialize' {
|
||||
public activate(terminal: Terminal): void;
|
||||
|
||||
/**
|
||||
* Serializes terminal rows into a string that can be written back to the terminal
|
||||
* to restore the state. The cursor will also be positioned to the correct cell.
|
||||
* When restoring a terminal it is best to do before `Terminal.open` is called
|
||||
* to avoid wasting CPU cycles rendering incomplete frames.
|
||||
* @param scrollback The number of rows in scrollback buffer to serialize, starting from the bottom of the
|
||||
* scrollback buffer. This defaults to the all available rows in the scrollback buffer.
|
||||
* Serializes terminal rows into a string that can be written back to the terminal to restore
|
||||
* the state. The cursor will also be positioned to the correct cell. When restoring a terminal
|
||||
* it is best to do before `Terminal.open` is called to avoid wasting CPU cycles rendering
|
||||
* incomplete frames.
|
||||
* @param scrollback The number of rows in scrollback buffer to serialize, starting from the
|
||||
* bottom of the scrollback buffer. This defaults to the all available rows in the scrollback
|
||||
* buffer.
|
||||
*/
|
||||
public serialize(scrollback?: number): string;
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
|
||||
this._renderLayers = [
|
||||
new LinkRenderLayer(this._core.screenElement!, 2, this._colors, this._core),
|
||||
new CursorRenderLayer(this._core.screenElement!, 3, this._colors, this._onRequestRedraw)
|
||||
new CursorRenderLayer(this._core.screenElement!, 3, this._colors, this._core, this._onRequestRedraw)
|
||||
];
|
||||
this.dimensions = {
|
||||
scaledCharWidth: 0,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Terminal } from 'xterm';
|
||||
import { BaseRenderLayer } from './BaseRenderLayer';
|
||||
import { ICellData } from 'common/Types';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { IColorSet } from 'browser/Types';
|
||||
import { IColorSet, ITerminal } from 'browser/Types';
|
||||
import { IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/Types';
|
||||
import { IEventEmitter } from 'common/EventEmitter';
|
||||
|
||||
@@ -34,6 +34,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
container: HTMLElement,
|
||||
zIndex: number,
|
||||
colors: IColorSet,
|
||||
private readonly _terminal: ITerminal,
|
||||
private _onRequestRefreshRowsEvent: IEventEmitter<IRequestRedrawEvent>
|
||||
) {
|
||||
super(container, 'cursor', zIndex, true, colors);
|
||||
@@ -120,7 +121,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
private _render(terminal: Terminal, triggeredByAnimationFrame: boolean): void {
|
||||
// Don't draw the cursor if it's hidden
|
||||
// TODO: Need to expose API for this
|
||||
if (!(terminal as any)._core._coreService.isCursorInitialized || (terminal as any)._core._coreService.isCursorHidden) {
|
||||
if (!this._terminal.coreService.isCursorInitialized || this._terminal.coreService.isCursorHidden) {
|
||||
this._clearCursor();
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user