mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Try implement cursor restortion
This commit is contained in:
@@ -13,7 +13,7 @@ function constrain(value: number, low: number, high: number): number {
|
||||
|
||||
interface ISerializeOptions {
|
||||
withAlternate?: boolean;
|
||||
|
||||
withCursor?: boolean;
|
||||
}
|
||||
|
||||
// TODO: Refine this template class later
|
||||
@@ -26,7 +26,7 @@ abstract class BaseSerializeHandler {
|
||||
const cell2 = this._buffer.getNullCell();
|
||||
let oldCell = cell1;
|
||||
|
||||
this._beforeSerialize(endRow - startRow);
|
||||
this._beforeSerialize(endRow - startRow, startRow, endRow);
|
||||
|
||||
for (let row = startRow; row < endRow; row++) {
|
||||
const line = this._buffer.getLine(row);
|
||||
@@ -51,7 +51,7 @@ abstract class BaseSerializeHandler {
|
||||
|
||||
protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { }
|
||||
protected _rowEnd(row: number): void { }
|
||||
protected _beforeSerialize(rows: number): void { }
|
||||
protected _beforeSerialize(rows: number, startRow: number, endRow: number): void { }
|
||||
protected _afterSerialize(): void { }
|
||||
protected _serializeString(): string { return ''; }
|
||||
}
|
||||
@@ -82,12 +82,16 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
private _currentRow: string = '';
|
||||
private _nullCellCount: number = 0;
|
||||
|
||||
constructor(buffer: IBuffer) {
|
||||
super(buffer);
|
||||
private _lastContentCellRow: number = 0;
|
||||
private _lastContentCellCol: number = 0;
|
||||
|
||||
constructor(private _buffer1: IBuffer,private _terminal: Terminal, private _option: ISerializeOptions = {}) {
|
||||
super(_buffer1);
|
||||
}
|
||||
|
||||
protected _beforeSerialize(rows: number): void {
|
||||
protected _beforeSerialize(rows: number, start: number, end: number): void {
|
||||
this._allRows = new Array<string>(rows);
|
||||
this._lastContentCellRow = start;
|
||||
}
|
||||
|
||||
protected _rowEnd(row: number): void {
|
||||
@@ -147,6 +151,9 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
} else if (this._nullCellCount > 0) {
|
||||
this._currentRow += `\x1b[${this._nullCellCount}C`;
|
||||
this._nullCellCount = 0;
|
||||
} else {
|
||||
this._lastContentCellRow = row;
|
||||
this._lastContentCellCol = col + cell.getWidth();
|
||||
}
|
||||
|
||||
this._currentRow += cell.getChars();
|
||||
@@ -154,12 +161,71 @@ class StringSerializeHandler extends BaseSerializeHandler {
|
||||
|
||||
protected _serializeString(): string {
|
||||
let rowEnd = this._allRows.length;
|
||||
|
||||
for (; rowEnd > 0; rowEnd--) {
|
||||
if (this._allRows[rowEnd - 1]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this._allRows.slice(0, rowEnd).join('\r\n');
|
||||
|
||||
let content = this._allRows.slice(0, rowEnd).join('\r\n');
|
||||
|
||||
if (this._option.withCursor) {
|
||||
const realCursorRow = this._buffer1.baseY + this._buffer1.cursorY;
|
||||
const realCursorCol = this._buffer1.cursorX;
|
||||
|
||||
const hasScroll = this._buffer1.length > this._terminal.rows!;
|
||||
const hasEmptyLine = hasScroll ? (this._buffer1.length - 1 > this._lastContentCellRow) : (realCursorRow > this._lastContentCellRow);
|
||||
const cursorMoved =
|
||||
hasScroll
|
||||
? hasEmptyLine
|
||||
? (realCursorCol !== 0 || realCursorRow !== this._buffer1.length - 1)
|
||||
: (realCursorRow !== this._lastContentCellRow || realCursorCol !== this._lastContentCellCol)
|
||||
: hasEmptyLine
|
||||
// we don't need to check the row because empty row count are based on cursor
|
||||
? realCursorCol !== 0
|
||||
: (realCursorRow !== this._lastContentCellRow || realCursorCol !== this._lastContentCellCol);
|
||||
|
||||
const moveRight = (offset: number): void => {
|
||||
if (offset > 0) {
|
||||
content += `\u001b[${offset}C`;
|
||||
} else if (offset < 0) {
|
||||
content += `\u001b[${-offset}D`;
|
||||
}
|
||||
};
|
||||
const moveDown = (offset: number): void => {
|
||||
if (offset > 0) {
|
||||
content += `\u001b[${offset}B`;
|
||||
} else if (offset < 0) {
|
||||
content += `\u001b[${-offset}A`;
|
||||
}
|
||||
};
|
||||
|
||||
// Fix empty lines
|
||||
if (hasEmptyLine) {
|
||||
if (hasScroll) {
|
||||
content += '\r\n'.repeat(this._buffer1.length - 1 - this._lastContentCellRow);
|
||||
} else {
|
||||
content += '\r\n'.repeat(realCursorRow - this._lastContentCellRow);
|
||||
}
|
||||
}
|
||||
|
||||
if (cursorMoved) {
|
||||
if (hasEmptyLine) {
|
||||
if (hasScroll) {
|
||||
moveRight(realCursorCol);
|
||||
moveDown(realCursorRow - (this._buffer1.length - 1));
|
||||
} else {
|
||||
moveRight(realCursorCol);
|
||||
}
|
||||
} else {
|
||||
moveDown(realCursorRow - this._lastContentCellRow);
|
||||
moveRight(realCursorCol - this._lastContentCellCol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +238,15 @@ export class SerializeAddon implements ITerminalAddon {
|
||||
this._terminal = terminal;
|
||||
}
|
||||
|
||||
private _getString(buffer: IBuffer, rows?: number, option?: ISerializeOptions): string {
|
||||
const maxRows = buffer.length;
|
||||
const handler = new StringSerializeHandler(buffer, this._terminal!, option);
|
||||
const correctRows = (rows === undefined) ? maxRows : constrain(rows, 0, maxRows);
|
||||
const result = handler.serialize(maxRows - correctRows, maxRows);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public serialize(rows?: number, options: ISerializeOptions = {}): string {
|
||||
// TODO: Add re-position cursor support
|
||||
// TODO: Add word wrap mode support
|
||||
@@ -180,25 +255,16 @@ export class SerializeAddon implements ITerminalAddon {
|
||||
throw new Error('Cannot use addon until it has been loaded');
|
||||
}
|
||||
|
||||
if (this._terminal.buffer.active.type === 'normal' || !(options?.withAlternate ?? false)) {
|
||||
const maxRows = this._terminal.buffer.active.length;
|
||||
const handler = new StringSerializeHandler(this._terminal.buffer.active);
|
||||
|
||||
rows = (rows === undefined) ? maxRows : constrain(rows, 0, maxRows);
|
||||
|
||||
return handler.serialize(maxRows - rows, maxRows);
|
||||
if (this._terminal.buffer.active.type === 'normal' || !(options.withAlternate ?? false)) {
|
||||
return this._getString(this._terminal.buffer.active, rows, options);
|
||||
}
|
||||
|
||||
const maxNormalRows = this._terminal.buffer.normal.length;
|
||||
const maxAltRows = this._terminal.buffer.alternate.length;
|
||||
const normalHandler = new StringSerializeHandler(this._terminal.buffer.normal);
|
||||
const altHandler = new StringSerializeHandler(this._terminal.buffer.alternate);
|
||||
const normalRows = (rows === undefined) ? maxNormalRows : constrain(rows, 0, maxNormalRows);
|
||||
const altRows = (rows === undefined) ? maxAltRows : constrain(rows, 0, maxAltRows);
|
||||
const normalScreenContent = this._getString(this._terminal.buffer.normal, rows, options);
|
||||
const alternativeScreenContent = this._getString(this._terminal.buffer.alternate, rows, options);
|
||||
|
||||
return normalHandler.serialize(maxNormalRows - normalRows, maxNormalRows)
|
||||
return normalScreenContent
|
||||
+ '\u001b[?1049h\u001b[H'
|
||||
+ altHandler.serialize(maxAltRows - altRows, maxAltRows);
|
||||
+ alternativeScreenContent;
|
||||
}
|
||||
|
||||
public dispose(): void { }
|
||||
|
||||
+1
-1
@@ -413,7 +413,7 @@ function updateTerminalSize(): void {
|
||||
}
|
||||
|
||||
function serializeButtonHandler(): void {
|
||||
const output = addons.serialize.instance.serialize();
|
||||
const output = addons.serialize.instance.serialize(undefined, { withAlternate: true, withCursor: true });
|
||||
const outputString = JSON.stringify(output);
|
||||
|
||||
document.getElementById('serialize-output').innerText = outputString;
|
||||
|
||||
Reference in New Issue
Block a user