Try implement cursor restortion

This commit is contained in:
mmis1000
2020-09-15 17:58:56 +08:00
parent edee1a1067
commit 881a4a0dbb
2 changed files with 89 additions and 23 deletions
@@ -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
View File
@@ -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;