From b3c0c2bd3c9e1c7c009129ebd43d47eca279f280 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Wed, 1 Sep 2021 05:45:00 -0700
Subject: [PATCH 1/8] Add loadtest button to demo
---
demo/client.ts | 34 ++++++++++++++++++++++++++++++++++
demo/index.html | 1 +
2 files changed, 35 insertions(+)
diff --git a/demo/client.ts b/demo/client.ts
index 48b6d177..a02b155c 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -148,6 +148,7 @@ if (document.location.pathname === '/test') {
document.getElementById('dispose').addEventListener('click', disposeRecreateButtonHandler);
document.getElementById('serialize').addEventListener('click', serializeButtonHandler);
document.getElementById('custom-glyph').addEventListener('click', writeCustomGlyphHandler);
+ document.getElementById('load-test').addEventListener('click', loadTest);
}
function createTerminal(): void {
@@ -481,3 +482,36 @@ function writeCustomGlyphHandler() {
term.write(' ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█\n\r');
window.scrollTo(0, 0);
}
+
+function loadTest() {
+ const isWebglEnabled = !!addons.webgl.instance;
+ const testData = [];
+ let byteCount = 0;
+ for (let i = 0; i < 50; i++) {
+ const count = 1 + Math.floor(Math.random() * 79);
+ byteCount += count + 2;
+ const data = new Uint8Array(count + 2);
+ data[0] = 0x0A; // \n
+ for (let i = 1; i < count + 1; i++) {
+ data[i] = 0x61 + Math.floor(Math.random() * (0x7A - 0x61));
+ }
+ // End each line with \r so the cursor remains constant, this is what ls/tree do and improves
+ // performance significantly due to the cursor DOM element not needing to change
+ data[data.length - 1] = 0x0D; // \r
+ testData.push(data);
+ }
+ const start = performance.now();
+ for (let i = 0; i < 1024; i++) {
+ for (const d of testData) {
+ term.write(d);
+ }
+ }
+ // Wait for all data to be parsed before evaluating time
+ term.write('', () => {
+ const time = Math.round(performance.now() - start);
+ const mbs = ((byteCount / 1024) * (1 / (time / 1000))).toFixed(2);
+ term.write(`\n\r\nWrote ${byteCount}kB in ${time}ms (${mbs}MB/s) using the (${isWebglEnabled ? 'webgl' : 'canvas'} renderer)`);
+ // Send ^C to get a new prompt
+ term._core._onData.fire('\x03');
+ });
+}
diff --git a/demo/index.html b/demo/index.html
index 33389ae9..9c86783b 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -63,6 +63,7 @@
+
From cece3db0cb01bfb403d468010b89fb3161fe0b5d Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Wed, 1 Sep 2021 06:15:59 -0700
Subject: [PATCH 2/8] Cache a copy of the active buffer as a private prop
This reduces GC from the const buffer workaround that avoids excessive getter access
with far less getter access
Part of #3450
---
src/common/InputHandler.ts | 435 ++++++++++++++++++-------------------
1 file changed, 206 insertions(+), 229 deletions(-)
diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts
index d4354e90..f2f0d0ce 100644
--- a/src/common/InputHandler.ts
+++ b/src/common/InputHandler.ts
@@ -20,6 +20,7 @@ import { AttributeData } from 'common/buffer/AttributeData';
import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService, ICharsetService, IUnicodeService, LogLevelEnum } from 'common/services/Services';
import { OscHandler } from 'common/parser/OscParser';
import { DcsHandler } from 'common/parser/DcsParser';
+import { IBuffer } from 'common/buffer/Types';
/**
* Map collect to glevel. Used in `selectCharset`.
@@ -234,6 +235,8 @@ export class InputHandler extends Disposable implements IInputHandler {
private _curAttrData: IAttributeData = DEFAULT_ATTR_DATA.clone();
private _eraseAttrDataInternal: IAttributeData = DEFAULT_ATTR_DATA.clone();
+ private _activeBuffer: IBuffer;
+
private _onRequestBell = new EventEmitter();
public get onRequestBell(): IEvent { return this._onRequestBell.event; }
private _onRequestRefreshRows = new EventEmitter();
@@ -282,6 +285,10 @@ export class InputHandler extends Disposable implements IInputHandler {
super();
this.register(this._parser);
+ // Track properties used in performance critical code manually to avoid using slow getters
+ this._activeBuffer = this._bufferService.buffer;
+ this.register(this._bufferService.buffers.onBufferActivate(e => this._activeBuffer = e.activeBuffer));
+
/**
* custom fallback handlers
*/
@@ -508,9 +515,8 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public parse(data: string | Uint8Array, promiseResult?: boolean): void | Promise {
let result: void | Promise;
- let buffer = this._bufferService.buffer;
- let cursorStartX = buffer.x;
- let cursorStartY = buffer.y;
+ let cursorStartX = this._activeBuffer.x;
+ let cursorStartY = this._activeBuffer.y;
let start = 0;
const wasPaused = this._parseStack.paused;
@@ -569,8 +575,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
}
- buffer = this._bufferService.buffer;
- if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
+ if (this._activeBuffer.x !== cursorStartX || this._activeBuffer.y !== cursorStartY) {
this._onCursorMove.fire();
}
@@ -581,20 +586,19 @@ export class InputHandler extends Disposable implements IInputHandler {
public print(data: Uint32Array, start: number, end: number): void {
let code: number;
let chWidth: number;
- const buffer = this._bufferService.buffer;
const charset = this._charsetService.charset;
const screenReaderMode = this._optionsService.options.screenReaderMode;
const cols = this._bufferService.cols;
const wraparoundMode = this._coreService.decPrivateModes.wraparound;
const insertMode = this._coreService.modes.insertMode;
const curAttr = this._curAttrData;
- let bufferRow = buffer.lines.get(buffer.ybase + buffer.y)!;
+ let bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!;
- this._dirtyRowService.markDirty(buffer.y);
+ this._dirtyRowService.markDirty(this._activeBuffer.y);
// handle wide chars: reset start_cell-1 if we would overwrite the second cell of a wide char
- if (buffer.x && end - start > 0 && bufferRow.getWidth(buffer.x - 1) === 2) {
- bufferRow.setCellFromCodePoint(buffer.x - 1, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
+ if (this._activeBuffer.x && end - start > 0 && bufferRow.getWidth(this._activeBuffer.x - 1) === 2) {
+ bufferRow.setCellFromCodePoint(this._activeBuffer.x - 1, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
}
for (let pos = start; pos < end; ++pos) {
@@ -619,17 +623,17 @@ export class InputHandler extends Disposable implements IInputHandler {
}
// insert combining char at last cursor position
- // buffer.x should never be 0 for a combining char
+ // this._activeBuffer.x should never be 0 for a combining char
// since they always follow a cell consuming char
- // therefore we can test for buffer.x to avoid overflow left
- if (!chWidth && buffer.x) {
- if (!bufferRow.getWidth(buffer.x - 1)) {
+ // therefore we can test for this._activeBuffer.x to avoid overflow left
+ if (!chWidth && this._activeBuffer.x) {
+ if (!bufferRow.getWidth(this._activeBuffer.x - 1)) {
// found empty cell after fullwidth, need to go 2 cells back
// it is save to step 2 cells back here
// since an empty cell is only set by fullwidth chars
- bufferRow.addCodepointToCell(buffer.x - 2, code);
+ bufferRow.addCodepointToCell(this._activeBuffer.x - 2, code);
} else {
- bufferRow.addCodepointToCell(buffer.x - 1, code);
+ bufferRow.addCodepointToCell(this._activeBuffer.x - 1, code);
}
continue;
}
@@ -637,31 +641,31 @@ export class InputHandler extends Disposable implements IInputHandler {
// goto next line if ch would overflow
// NOTE: To avoid costly width checks here,
// the terminal does not allow a cols < 2.
- if (buffer.x + chWidth - 1 >= cols) {
+ if (this._activeBuffer.x + chWidth - 1 >= cols) {
// autowrap - DECAWM
// automatically wraps to the beginning of the next line
if (wraparoundMode) {
// clear left over cells to the right
- while (buffer.x < cols) {
- bufferRow.setCellFromCodePoint(buffer.x++, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
+ while (this._activeBuffer.x < cols) {
+ bufferRow.setCellFromCodePoint(this._activeBuffer.x++, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
}
- buffer.x = 0;
- buffer.y++;
- if (buffer.y === buffer.scrollBottom + 1) {
- buffer.y--;
+ this._activeBuffer.x = 0;
+ this._activeBuffer.y++;
+ if (this._activeBuffer.y === this._activeBuffer.scrollBottom + 1) {
+ this._activeBuffer.y--;
this._bufferService.scroll(this._eraseAttrData(), true);
} else {
- if (buffer.y >= this._bufferService.rows) {
- buffer.y = this._bufferService.rows - 1;
+ if (this._activeBuffer.y >= this._bufferService.rows) {
+ this._activeBuffer.y = this._bufferService.rows - 1;
}
// The line already exists (eg. the initial viewport), mark it as a
// wrapped line
- buffer.lines.get(buffer.ybase + buffer.y)!.isWrapped = true;
+ this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = true;
}
// row changed, get it again
- bufferRow = buffer.lines.get(buffer.ybase + buffer.y)!;
+ bufferRow = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!;
} else {
- buffer.x = cols - 1;
+ this._activeBuffer.x = cols - 1;
if (chWidth === 2) {
// FIXME: check for xterm behavior
// What to do here? We got a wide char that does not fit into last cell
@@ -673,7 +677,7 @@ export class InputHandler extends Disposable implements IInputHandler {
// insert mode: move characters to right
if (insertMode) {
// right shift cells according to the width
- bufferRow.insertCells(buffer.x, chWidth, buffer.getNullCell(curAttr), curAttr);
+ bufferRow.insertCells(this._activeBuffer.x, chWidth, this._activeBuffer.getNullCell(curAttr), curAttr);
// test last cell - since the last cell has only room for
// a halfwidth char any fullwidth shifted there is lost
// and will be set to empty cell
@@ -683,15 +687,15 @@ export class InputHandler extends Disposable implements IInputHandler {
}
// write current char to buffer and advance cursor
- bufferRow.setCellFromCodePoint(buffer.x++, code, chWidth, curAttr.fg, curAttr.bg, curAttr.extended);
+ bufferRow.setCellFromCodePoint(this._activeBuffer.x++, code, chWidth, curAttr.fg, curAttr.bg, curAttr.extended);
// fullwidth char - also set next cell to placeholder stub and advance cursor
// for graphemes bigger than fullwidth we can simply loop to zero
- // we already made sure above, that buffer.x + chWidth will not overflow right
+ // we already made sure above, that this._activeBuffer.x + chWidth will not overflow right
if (chWidth > 0) {
while (--chWidth) {
// other than a regular empty cell a cell following a wide char has no width
- bufferRow.setCellFromCodePoint(buffer.x++, 0, 0, curAttr.fg, curAttr.bg, curAttr.extended);
+ bufferRow.setCellFromCodePoint(this._activeBuffer.x++, 0, 0, curAttr.fg, curAttr.bg, curAttr.extended);
}
}
}
@@ -700,7 +704,7 @@ export class InputHandler extends Disposable implements IInputHandler {
// - fullwidth + surrogates: reset
// - combining: only base char gets carried on (bug in xterm?)
if (end - start > 0) {
- bufferRow.loadCell(buffer.x - 1, this._workCell);
+ bufferRow.loadCell(this._activeBuffer.x - 1, this._workCell);
if (this._workCell.getWidth() === 2 || this._workCell.getCode() > 0xFFFF) {
this._parser.precedingCodepoint = 0;
} else if (this._workCell.isCombined()) {
@@ -711,11 +715,11 @@ export class InputHandler extends Disposable implements IInputHandler {
}
// handle wide chars: reset cell to the right if it is second cell of a wide char
- if (buffer.x < cols && end - start > 0 && bufferRow.getWidth(buffer.x) === 0 && !bufferRow.hasContent(buffer.x)) {
- bufferRow.setCellFromCodePoint(buffer.x, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
+ if (this._activeBuffer.x < cols && end - start > 0 && bufferRow.getWidth(this._activeBuffer.x) === 0 && !bufferRow.hasContent(this._activeBuffer.x)) {
+ bufferRow.setCellFromCodePoint(this._activeBuffer.x, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
}
- this._dirtyRowService.markDirty(buffer.y);
+ this._dirtyRowService.markDirty(this._activeBuffer.y);
}
/**
@@ -779,25 +783,22 @@ export class InputHandler extends Disposable implements IInputHandler {
* @vt: #Y C0 FF "Form Feed" "\f, \x0C" "Treated as LF."
*/
public lineFeed(): boolean {
- // make buffer local for faster access
- const buffer = this._bufferService.buffer;
-
- this._dirtyRowService.markDirty(buffer.y);
+ this._dirtyRowService.markDirty(this._activeBuffer.y);
if (this._optionsService.options.convertEol) {
- buffer.x = 0;
+ this._activeBuffer.x = 0;
}
- buffer.y++;
- if (buffer.y === buffer.scrollBottom + 1) {
- buffer.y--;
+ this._activeBuffer.y++;
+ if (this._activeBuffer.y === this._activeBuffer.scrollBottom + 1) {
+ this._activeBuffer.y--;
this._bufferService.scroll(this._eraseAttrData());
- } else if (buffer.y >= this._bufferService.rows) {
- buffer.y = this._bufferService.rows - 1;
+ } else if (this._activeBuffer.y >= this._bufferService.rows) {
+ this._activeBuffer.y = this._bufferService.rows - 1;
}
// If the end of the line is hit, prevent this action from wrapping around to the next line.
- if (buffer.x >= this._bufferService.cols) {
- buffer.x--;
+ if (this._activeBuffer.x >= this._bufferService.cols) {
+ this._activeBuffer.x--;
}
- this._dirtyRowService.markDirty(buffer.y);
+ this._dirtyRowService.markDirty(this._activeBuffer.y);
this._onLineFeed.fire();
return true;
@@ -810,7 +811,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* @vt: #Y C0 CR "Carriage Return" "\r, \x0D" "Move the cursor to the beginning of the row."
*/
public carriageReturn(): boolean {
- this._bufferService.buffer.x = 0;
+ this._activeBuffer.x = 0;
return true;
}
@@ -826,13 +827,11 @@ export class InputHandler extends Disposable implements IInputHandler {
* with the cursor, thus at the home position (top-leftmost cell) this has no effect.
*/
public backspace(): boolean {
- const buffer = this._bufferService.buffer;
-
// reverse wrap-around is disabled
if (!this._coreService.decPrivateModes.reverseWraparound) {
this._restrictCursor();
- if (buffer.x > 0) {
- buffer.x--;
+ if (this._activeBuffer.x > 0) {
+ this._activeBuffer.x--;
}
return true;
}
@@ -842,8 +841,8 @@ export class InputHandler extends Disposable implements IInputHandler {
// to be at x=cols to be able to address the last cell of a row by BS
this._restrictCursor(this._bufferService.cols);
- if (buffer.x > 0) {
- buffer.x--;
+ if (this._activeBuffer.x > 0) {
+ this._activeBuffer.x--;
} else {
/**
* reverse wrap-around handling:
@@ -853,21 +852,21 @@ export class InputHandler extends Disposable implements IInputHandler {
* - cannot peek into scrollbuffer
* - any cursor movement sequence keeps working as expected
*/
- if (buffer.x === 0
- && buffer.y > buffer.scrollTop
- && buffer.y <= buffer.scrollBottom
- && buffer.lines.get(buffer.ybase + buffer.y)?.isWrapped)
+ if (this._activeBuffer.x === 0
+ && this._activeBuffer.y > this._activeBuffer.scrollTop
+ && this._activeBuffer.y <= this._activeBuffer.scrollBottom
+ && this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)?.isWrapped)
{
- buffer.lines.get(buffer.ybase + buffer.y)!.isWrapped = false;
- buffer.y--;
- buffer.x = this._bufferService.cols - 1;
+ this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!.isWrapped = false;
+ this._activeBuffer.y--;
+ this._activeBuffer.x = this._bufferService.cols - 1;
// find last taken cell - last cell can have 3 different states:
// - hasContent(true) + hasWidth(1): narrow char - we are done
// - hasWidth(0): second part of wide char - we are done
// - hasContent(false) + hasWidth(1): empty cell due to early wrapping wide char, go one cell further back
- const line = buffer.lines.get(buffer.ybase + buffer.y)!;
- if (line.hasWidth(buffer.x) && !line.hasContent(buffer.x)) {
- buffer.x--;
+ const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y)!;
+ if (line.hasWidth(this._activeBuffer.x) && !line.hasContent(this._activeBuffer.x)) {
+ this._activeBuffer.x--;
// We do this only once, since width=1 + hasContent=false currently happens only once before
// early wrapping of a wide char.
// This needs to be fixed once we support graphemes taking more than 2 cells.
@@ -885,13 +884,13 @@ export class InputHandler extends Disposable implements IInputHandler {
* @vt: #Y C0 HT "Horizontal Tabulation" "\t, \x09" "Move the cursor to the next character tab stop."
*/
public tab(): boolean {
- if (this._bufferService.buffer.x >= this._bufferService.cols) {
+ if (this._activeBuffer.x >= this._bufferService.cols) {
return true;
}
- const originalX = this._bufferService.buffer.x;
- this._bufferService.buffer.x = this._bufferService.buffer.nextStop();
+ const originalX = this._activeBuffer.x;
+ this._activeBuffer.x = this._activeBuffer.nextStop();
if (this._optionsService.options.screenReaderMode) {
- this._onA11yTab.fire(this._bufferService.buffer.x - originalX);
+ this._onA11yTab.fire(this._activeBuffer.x - originalX);
}
return true;
}
@@ -924,27 +923,27 @@ export class InputHandler extends Disposable implements IInputHandler {
* Restrict cursor to viewport size / scroll margin (origin mode).
*/
private _restrictCursor(maxCol: number = this._bufferService.cols - 1): void {
- this._bufferService.buffer.x = Math.min(maxCol, Math.max(0, this._bufferService.buffer.x));
- this._bufferService.buffer.y = this._coreService.decPrivateModes.origin
- ? Math.min(this._bufferService.buffer.scrollBottom, Math.max(this._bufferService.buffer.scrollTop, this._bufferService.buffer.y))
- : Math.min(this._bufferService.rows - 1, Math.max(0, this._bufferService.buffer.y));
- this._dirtyRowService.markDirty(this._bufferService.buffer.y);
+ this._activeBuffer.x = Math.min(maxCol, Math.max(0, this._activeBuffer.x));
+ this._activeBuffer.y = this._coreService.decPrivateModes.origin
+ ? Math.min(this._activeBuffer.scrollBottom, Math.max(this._activeBuffer.scrollTop, this._activeBuffer.y))
+ : Math.min(this._bufferService.rows - 1, Math.max(0, this._activeBuffer.y));
+ this._dirtyRowService.markDirty(this._activeBuffer.y);
}
/**
* Set absolute cursor position.
*/
private _setCursor(x: number, y: number): void {
- this._dirtyRowService.markDirty(this._bufferService.buffer.y);
+ this._dirtyRowService.markDirty(this._activeBuffer.y);
if (this._coreService.decPrivateModes.origin) {
- this._bufferService.buffer.x = x;
- this._bufferService.buffer.y = this._bufferService.buffer.scrollTop + y;
+ this._activeBuffer.x = x;
+ this._activeBuffer.y = this._activeBuffer.scrollTop + y;
} else {
- this._bufferService.buffer.x = x;
- this._bufferService.buffer.y = y;
+ this._activeBuffer.x = x;
+ this._activeBuffer.y = y;
}
this._restrictCursor();
- this._dirtyRowService.markDirty(this._bufferService.buffer.y);
+ this._dirtyRowService.markDirty(this._activeBuffer.y);
}
/**
@@ -954,7 +953,7 @@ export class InputHandler extends Disposable implements IInputHandler {
// for relative changes we have to make sure we are within 0 .. cols/rows - 1
// before calculating the new position
this._restrictCursor();
- this._setCursor(this._bufferService.buffer.x + x, this._bufferService.buffer.y + y);
+ this._setCursor(this._activeBuffer.x + x, this._activeBuffer.y + y);
}
/**
@@ -966,7 +965,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public cursorUp(params: IParams): boolean {
// stop at scrollTop
- const diffToTop = this._bufferService.buffer.y - this._bufferService.buffer.scrollTop;
+ const diffToTop = this._activeBuffer.y - this._activeBuffer.scrollTop;
if (diffToTop >= 0) {
this._moveCursor(0, -Math.min(diffToTop, params.params[0] || 1));
} else {
@@ -984,7 +983,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public cursorDown(params: IParams): boolean {
// stop at scrollBottom
- const diffToBottom = this._bufferService.buffer.scrollBottom - this._bufferService.buffer.y;
+ const diffToBottom = this._activeBuffer.scrollBottom - this._activeBuffer.y;
if (diffToBottom >= 0) {
this._moveCursor(0, Math.min(diffToBottom, params.params[0] || 1));
} else {
@@ -1025,7 +1024,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public cursorNextLine(params: IParams): boolean {
this.cursorDown(params);
- this._bufferService.buffer.x = 0;
+ this._activeBuffer.x = 0;
return true;
}
@@ -1039,7 +1038,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public cursorPrecedingLine(params: IParams): boolean {
this.cursorUp(params);
- this._bufferService.buffer.x = 0;
+ this._activeBuffer.x = 0;
return true;
}
@@ -1050,7 +1049,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* @vt: #Y CSI CHA "Cursor Horizontal Absolute" "CSI Ps G" "Move cursor to `Ps`-th column of the active row (default=1)."
*/
public cursorCharAbsolute(params: IParams): boolean {
- this._setCursor((params.params[0] || 1) - 1, this._bufferService.buffer.y);
+ this._setCursor((params.params[0] || 1) - 1, this._activeBuffer.y);
return true;
}
@@ -1081,7 +1080,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* @vt: #Y CSI HPA "Horizontal Position Absolute" "CSI Ps ` " "Same as CHA."
*/
public charPosAbsolute(params: IParams): boolean {
- this._setCursor((params.params[0] || 1) - 1, this._bufferService.buffer.y);
+ this._setCursor((params.params[0] || 1) - 1, this._activeBuffer.y);
return true;
}
@@ -1103,7 +1102,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* @vt: #Y CSI VPA "Vertical Position Absolute" "CSI Ps d" "Move cursor to `Ps`-th row (default=1)."
*/
public linePosAbsolute(params: IParams): boolean {
- this._setCursor(this._bufferService.buffer.x, (params.params[0] || 1) - 1);
+ this._setCursor(this._activeBuffer.x, (params.params[0] || 1) - 1);
return true;
}
@@ -1146,9 +1145,9 @@ export class InputHandler extends Disposable implements IInputHandler {
public tabClear(params: IParams): boolean {
const param = params.params[0];
if (param === 0) {
- delete this._bufferService.buffer.tabs[this._bufferService.buffer.x];
+ delete this._activeBuffer.tabs[this._activeBuffer.x];
} else if (param === 3) {
- this._bufferService.buffer.tabs = {};
+ this._activeBuffer.tabs = {};
}
return true;
}
@@ -1160,12 +1159,12 @@ export class InputHandler extends Disposable implements IInputHandler {
* @vt: #Y CSI CHT "Cursor Horizontal Tabulation" "CSI Ps I" "Move cursor `Ps` times tabs forward (default=1)."
*/
public cursorForwardTab(params: IParams): boolean {
- if (this._bufferService.buffer.x >= this._bufferService.cols) {
+ if (this._activeBuffer.x >= this._bufferService.cols) {
return true;
}
let param = params.params[0] || 1;
while (param--) {
- this._bufferService.buffer.x = this._bufferService.buffer.nextStop();
+ this._activeBuffer.x = this._activeBuffer.nextStop();
}
return true;
}
@@ -1176,16 +1175,13 @@ export class InputHandler extends Disposable implements IInputHandler {
* @vt: #Y CSI CBT "Cursor Backward Tabulation" "CSI Ps Z" "Move cursor `Ps` tabs backward (default=1)."
*/
public cursorBackwardTab(params: IParams): boolean {
- if (this._bufferService.buffer.x >= this._bufferService.cols) {
+ if (this._activeBuffer.x >= this._bufferService.cols) {
return true;
}
let param = params.params[0] || 1;
- // make buffer local for faster access
- const buffer = this._bufferService.buffer;
-
while (param--) {
- buffer.x = buffer.prevStop();
+ this._activeBuffer.x = this._activeBuffer.prevStop();
}
return true;
}
@@ -1199,11 +1195,11 @@ export class InputHandler extends Disposable implements IInputHandler {
* @param end end - 1 is last erased cell
*/
private _eraseInBufferLine(y: number, start: number, end: number, clearWrap: boolean = false): void {
- const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y)!;
+ const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
line.replaceCells(
start,
end,
- this._bufferService.buffer.getNullCell(this._eraseAttrData()),
+ this._activeBuffer.getNullCell(this._eraseAttrData()),
this._eraseAttrData()
);
if (clearWrap) {
@@ -1217,8 +1213,8 @@ export class InputHandler extends Disposable implements IInputHandler {
* @param y row index
*/
private _resetBufferLine(y: number): void {
- const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y)!;
- line.fill(this._bufferService.buffer.getNullCell(this._eraseAttrData()));
+ const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
+ line.fill(this._activeBuffer.getNullCell(this._eraseAttrData()));
line.isWrapped = false;
}
@@ -1251,22 +1247,22 @@ export class InputHandler extends Disposable implements IInputHandler {
let j;
switch (params.params[0]) {
case 0:
- j = this._bufferService.buffer.y;
+ j = this._activeBuffer.y;
this._dirtyRowService.markDirty(j);
- this._eraseInBufferLine(j++, this._bufferService.buffer.x, this._bufferService.cols, this._bufferService.buffer.x === 0);
+ this._eraseInBufferLine(j++, this._activeBuffer.x, this._bufferService.cols, this._activeBuffer.x === 0);
for (; j < this._bufferService.rows; j++) {
this._resetBufferLine(j);
}
this._dirtyRowService.markDirty(j);
break;
case 1:
- j = this._bufferService.buffer.y;
+ j = this._activeBuffer.y;
this._dirtyRowService.markDirty(j);
// Deleted front part of line and everything before. This line will no longer be wrapped.
- this._eraseInBufferLine(j, 0, this._bufferService.buffer.x + 1, true);
- if (this._bufferService.buffer.x + 1 >= this._bufferService.cols) {
+ this._eraseInBufferLine(j, 0, this._activeBuffer.x + 1, true);
+ if (this._activeBuffer.x + 1 >= this._bufferService.cols) {
// Deleted entire previous line. This next line can no longer be wrapped.
- this._bufferService.buffer.lines.get(j + 1)!.isWrapped = false;
+ this._activeBuffer.lines.get(j + 1)!.isWrapped = false;
}
while (j--) {
this._resetBufferLine(j);
@@ -1283,11 +1279,11 @@ export class InputHandler extends Disposable implements IInputHandler {
break;
case 3:
// Clear scrollback (everything not in viewport)
- const scrollBackSize = this._bufferService.buffer.lines.length - this._bufferService.rows;
+ const scrollBackSize = this._activeBuffer.lines.length - this._bufferService.rows;
if (scrollBackSize > 0) {
- this._bufferService.buffer.lines.trimStart(scrollBackSize);
- this._bufferService.buffer.ybase = Math.max(this._bufferService.buffer.ybase - scrollBackSize, 0);
- this._bufferService.buffer.ydisp = Math.max(this._bufferService.buffer.ydisp - scrollBackSize, 0);
+ this._activeBuffer.lines.trimStart(scrollBackSize);
+ this._activeBuffer.ybase = Math.max(this._activeBuffer.ybase - scrollBackSize, 0);
+ this._activeBuffer.ydisp = Math.max(this._activeBuffer.ydisp - scrollBackSize, 0);
// Force a scroll event to refresh viewport
this._onScroll.fire(0);
}
@@ -1322,16 +1318,16 @@ export class InputHandler extends Disposable implements IInputHandler {
this._restrictCursor(this._bufferService.cols);
switch (params.params[0]) {
case 0:
- this._eraseInBufferLine(this._bufferService.buffer.y, this._bufferService.buffer.x, this._bufferService.cols);
+ this._eraseInBufferLine(this._activeBuffer.y, this._activeBuffer.x, this._bufferService.cols);
break;
case 1:
- this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.buffer.x + 1);
+ this._eraseInBufferLine(this._activeBuffer.y, 0, this._activeBuffer.x + 1);
break;
case 2:
- this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.cols);
+ this._eraseInBufferLine(this._activeBuffer.y, 0, this._bufferService.cols);
break;
}
- this._dirtyRowService.markDirty(this._bufferService.buffer.y);
+ this._dirtyRowService.markDirty(this._activeBuffer.y);
return true;
}
@@ -1348,26 +1344,23 @@ export class InputHandler extends Disposable implements IInputHandler {
this._restrictCursor();
let param = params.params[0] || 1;
- // make buffer local for faster access
- const buffer = this._bufferService.buffer;
-
- if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
+ if (this._activeBuffer.y > this._activeBuffer.scrollBottom || this._activeBuffer.y < this._activeBuffer.scrollTop) {
return true;
}
- const row: number = buffer.ybase + buffer.y;
+ const row: number = this._activeBuffer.ybase + this._activeBuffer.y;
- const scrollBottomRowsOffset = this._bufferService.rows - 1 - buffer.scrollBottom;
- const scrollBottomAbsolute = this._bufferService.rows - 1 + buffer.ybase - scrollBottomRowsOffset + 1;
+ const scrollBottomRowsOffset = this._bufferService.rows - 1 - this._activeBuffer.scrollBottom;
+ const scrollBottomAbsolute = this._bufferService.rows - 1 + this._activeBuffer.ybase - scrollBottomRowsOffset + 1;
while (param--) {
// test: echo -e '\e[44m\e[1L\e[0m'
// blankLine(true) - xterm/linux behavior
- buffer.lines.splice(scrollBottomAbsolute - 1, 1);
- buffer.lines.splice(row, 0, buffer.getBlankLine(this._eraseAttrData()));
+ this._activeBuffer.lines.splice(scrollBottomAbsolute - 1, 1);
+ this._activeBuffer.lines.splice(row, 0, this._activeBuffer.getBlankLine(this._eraseAttrData()));
}
- this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom);
- buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
+ this._dirtyRowService.markRangeDirty(this._activeBuffer.y, this._activeBuffer.scrollBottom);
+ this._activeBuffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
return true;
}
@@ -1384,27 +1377,24 @@ export class InputHandler extends Disposable implements IInputHandler {
this._restrictCursor();
let param = params.params[0] || 1;
- // make buffer local for faster access
- const buffer = this._bufferService.buffer;
-
- if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
+ if (this._activeBuffer.y > this._activeBuffer.scrollBottom || this._activeBuffer.y < this._activeBuffer.scrollTop) {
return true;
}
- const row: number = buffer.ybase + buffer.y;
+ const row: number = this._activeBuffer.ybase + this._activeBuffer.y;
let j: number;
- j = this._bufferService.rows - 1 - buffer.scrollBottom;
- j = this._bufferService.rows - 1 + buffer.ybase - j;
+ j = this._bufferService.rows - 1 - this._activeBuffer.scrollBottom;
+ j = this._bufferService.rows - 1 + this._activeBuffer.ybase - j;
while (param--) {
// test: echo -e '\e[44m\e[1M\e[0m'
// blankLine(true) - xterm/linux behavior
- buffer.lines.splice(row, 1);
- buffer.lines.splice(j, 0, buffer.getBlankLine(this._eraseAttrData()));
+ this._activeBuffer.lines.splice(row, 1);
+ this._activeBuffer.lines.splice(j, 0, this._activeBuffer.getBlankLine(this._eraseAttrData()));
}
- this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom);
- buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
+ this._dirtyRowService.markRangeDirty(this._activeBuffer.y, this._activeBuffer.scrollBottom);
+ this._activeBuffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
return true;
}
@@ -1421,15 +1411,15 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public insertChars(params: IParams): boolean {
this._restrictCursor();
- const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + this._bufferService.buffer.y);
+ const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y);
if (line) {
line.insertCells(
- this._bufferService.buffer.x,
+ this._activeBuffer.x,
params.params[0] || 1,
- this._bufferService.buffer.getNullCell(this._eraseAttrData()),
+ this._activeBuffer.getNullCell(this._eraseAttrData()),
this._eraseAttrData()
);
- this._dirtyRowService.markDirty(this._bufferService.buffer.y);
+ this._dirtyRowService.markDirty(this._activeBuffer.y);
}
return true;
}
@@ -1447,15 +1437,15 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public deleteChars(params: IParams): boolean {
this._restrictCursor();
- const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + this._bufferService.buffer.y);
+ const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y);
if (line) {
line.deleteCells(
- this._bufferService.buffer.x,
+ this._activeBuffer.x,
params.params[0] || 1,
- this._bufferService.buffer.getNullCell(this._eraseAttrData()),
+ this._activeBuffer.getNullCell(this._eraseAttrData()),
this._eraseAttrData()
);
- this._dirtyRowService.markDirty(this._bufferService.buffer.y);
+ this._dirtyRowService.markDirty(this._activeBuffer.y);
}
return true;
}
@@ -1471,14 +1461,11 @@ export class InputHandler extends Disposable implements IInputHandler {
public scrollUp(params: IParams): boolean {
let param = params.params[0] || 1;
- // make buffer local for faster access
- const buffer = this._bufferService.buffer;
-
while (param--) {
- buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1);
- buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(this._eraseAttrData()));
+ this._activeBuffer.lines.splice(this._activeBuffer.ybase + this._activeBuffer.scrollTop, 1);
+ this._activeBuffer.lines.splice(this._activeBuffer.ybase + this._activeBuffer.scrollBottom, 0, this._activeBuffer.getBlankLine(this._eraseAttrData()));
}
- this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
+ this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
return true;
}
@@ -1490,14 +1477,11 @@ export class InputHandler extends Disposable implements IInputHandler {
public scrollDown(params: IParams): boolean {
let param = params.params[0] || 1;
- // make buffer local for faster access
- const buffer = this._bufferService.buffer;
-
while (param--) {
- buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
- buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
+ this._activeBuffer.lines.splice(this._activeBuffer.ybase + this._activeBuffer.scrollBottom, 1);
+ this._activeBuffer.lines.splice(this._activeBuffer.ybase + this._activeBuffer.scrollTop, 0, this._activeBuffer.getBlankLine(DEFAULT_ATTR_DATA));
}
- this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
+ this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
return true;
}
@@ -1520,17 +1504,16 @@ export class InputHandler extends Disposable implements IInputHandler {
* SL has no effect outside of the scroll margins.
*/
public scrollLeft(params: IParams): boolean {
- const buffer = this._bufferService.buffer;
- if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
+ if (this._activeBuffer.y > this._activeBuffer.scrollBottom || this._activeBuffer.y < this._activeBuffer.scrollTop) {
return true;
}
const param = params.params[0] || 1;
- for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
- const line = buffer.lines.get(buffer.ybase + y)!;
- line.deleteCells(0, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
+ for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
+ const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
+ line.deleteCells(0, param, this._activeBuffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
line.isWrapped = false;
}
- this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
+ this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
return true;
}
@@ -1554,17 +1537,16 @@ export class InputHandler extends Disposable implements IInputHandler {
* SL has no effect outside of the scroll margins.
*/
public scrollRight(params: IParams): boolean {
- const buffer = this._bufferService.buffer;
- if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
+ if (this._activeBuffer.y > this._activeBuffer.scrollBottom || this._activeBuffer.y < this._activeBuffer.scrollTop) {
return true;
}
const param = params.params[0] || 1;
- for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
- const line = buffer.lines.get(buffer.ybase + y)!;
- line.insertCells(0, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
+ for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
+ const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
+ line.insertCells(0, param, this._activeBuffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
line.isWrapped = false;
}
- this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
+ this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
return true;
}
@@ -1578,17 +1560,16 @@ export class InputHandler extends Disposable implements IInputHandler {
* DECIC has no effect outside the scrolling margins.
*/
public insertColumns(params: IParams): boolean {
- const buffer = this._bufferService.buffer;
- if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
+ if (this._activeBuffer.y > this._activeBuffer.scrollBottom || this._activeBuffer.y < this._activeBuffer.scrollTop) {
return true;
}
const param = params.params[0] || 1;
- for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
- const line = this._bufferService.buffer.lines.get(buffer.ybase + y)!;
- line.insertCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
+ for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
+ const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
+ line.insertCells(this._activeBuffer.x, param, this._activeBuffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
line.isWrapped = false;
}
- this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
+ this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
return true;
}
@@ -1602,17 +1583,16 @@ export class InputHandler extends Disposable implements IInputHandler {
* DECDC has no effect outside the scrolling margins.
*/
public deleteColumns(params: IParams): boolean {
- const buffer = this._bufferService.buffer;
- if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
+ if (this._activeBuffer.y > this._activeBuffer.scrollBottom || this._activeBuffer.y < this._activeBuffer.scrollTop) {
return true;
}
const param = params.params[0] || 1;
- for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
- const line = buffer.lines.get(buffer.ybase + y)!;
- line.deleteCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
+ for (let y = this._activeBuffer.scrollTop; y <= this._activeBuffer.scrollBottom; ++y) {
+ const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
+ line.deleteCells(this._activeBuffer.x, param, this._activeBuffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
line.isWrapped = false;
}
- this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
+ this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
return true;
}
@@ -1626,15 +1606,15 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public eraseChars(params: IParams): boolean {
this._restrictCursor();
- const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + this._bufferService.buffer.y);
+ const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + this._activeBuffer.y);
if (line) {
line.replaceCells(
- this._bufferService.buffer.x,
- this._bufferService.buffer.x + (params.params[0] || 1),
- this._bufferService.buffer.getNullCell(this._eraseAttrData()),
+ this._activeBuffer.x,
+ this._activeBuffer.x + (params.params[0] || 1),
+ this._activeBuffer.getNullCell(this._eraseAttrData()),
this._eraseAttrData()
);
- this._dirtyRowService.markDirty(this._bufferService.buffer.y);
+ this._dirtyRowService.markDirty(this._activeBuffer.y);
}
return true;
}
@@ -2570,8 +2550,8 @@ export class InputHandler extends Disposable implements IInputHandler {
break;
case 6:
// cursor position
- const y = this._bufferService.buffer.y + 1;
- const x = this._bufferService.buffer.x + 1;
+ const y = this._activeBuffer.y + 1;
+ const x = this._activeBuffer.x + 1;
this._coreService.triggerDataEvent(`${C0.ESC}[${y};${x}R`);
break;
}
@@ -2585,8 +2565,8 @@ export class InputHandler extends Disposable implements IInputHandler {
switch (params.params[0]) {
case 6:
// cursor position
- const y = this._bufferService.buffer.y + 1;
- const x = this._bufferService.buffer.x + 1;
+ const y = this._activeBuffer.y + 1;
+ const x = this._activeBuffer.x + 1;
this._coreService.triggerDataEvent(`${C0.ESC}[?${y};${x}R`);
break;
case 15:
@@ -2631,18 +2611,18 @@ export class InputHandler extends Disposable implements IInputHandler {
public softReset(params: IParams): boolean {
this._coreService.isCursorHidden = false;
this._onRequestSyncScrollBar.fire();
- this._bufferService.buffer.scrollTop = 0;
- this._bufferService.buffer.scrollBottom = this._bufferService.rows - 1;
+ this._activeBuffer.scrollTop = 0;
+ this._activeBuffer.scrollBottom = this._bufferService.rows - 1;
this._curAttrData = DEFAULT_ATTR_DATA.clone();
this._coreService.reset();
this._charsetService.reset();
// reset DECSC data
- this._bufferService.buffer.savedX = 0;
- this._bufferService.buffer.savedY = this._bufferService.buffer.ybase;
- this._bufferService.buffer.savedCurAttrData.fg = this._curAttrData.fg;
- this._bufferService.buffer.savedCurAttrData.bg = this._curAttrData.bg;
- this._bufferService.buffer.savedCharset = this._charsetService.charset;
+ this._activeBuffer.savedX = 0;
+ this._activeBuffer.savedY = this._activeBuffer.ybase;
+ this._activeBuffer.savedCurAttrData.fg = this._curAttrData.fg;
+ this._activeBuffer.savedCurAttrData.bg = this._curAttrData.bg;
+ this._activeBuffer.savedCharset = this._charsetService.charset;
// reset DECOM
this._coreService.decPrivateModes.origin = false;
@@ -2705,8 +2685,8 @@ export class InputHandler extends Disposable implements IInputHandler {
}
if (bottom > top) {
- this._bufferService.buffer.scrollTop = top - 1;
- this._bufferService.buffer.scrollBottom = bottom - 1;
+ this._activeBuffer.scrollTop = top - 1;
+ this._activeBuffer.scrollBottom = bottom - 1;
this._setCursor(0, 0);
}
return true;
@@ -2801,11 +2781,11 @@ export class InputHandler extends Disposable implements IInputHandler {
* @vt: #Y ESC SC "Save Cursor" "ESC 7" "Save cursor position, charmap and text attributes."
*/
public saveCursor(params?: IParams): boolean {
- this._bufferService.buffer.savedX = this._bufferService.buffer.x;
- this._bufferService.buffer.savedY = this._bufferService.buffer.ybase + this._bufferService.buffer.y;
- this._bufferService.buffer.savedCurAttrData.fg = this._curAttrData.fg;
- this._bufferService.buffer.savedCurAttrData.bg = this._curAttrData.bg;
- this._bufferService.buffer.savedCharset = this._charsetService.charset;
+ this._activeBuffer.savedX = this._activeBuffer.x;
+ this._activeBuffer.savedY = this._activeBuffer.ybase + this._activeBuffer.y;
+ this._activeBuffer.savedCurAttrData.fg = this._curAttrData.fg;
+ this._activeBuffer.savedCurAttrData.bg = this._curAttrData.bg;
+ this._activeBuffer.savedCharset = this._charsetService.charset;
return true;
}
@@ -2819,13 +2799,13 @@ export class InputHandler extends Disposable implements IInputHandler {
* @vt: #Y ESC RC "Restore Cursor" "ESC 8" "Restore cursor position, charmap and text attributes."
*/
public restoreCursor(params?: IParams): boolean {
- this._bufferService.buffer.x = this._bufferService.buffer.savedX || 0;
- this._bufferService.buffer.y = Math.max(this._bufferService.buffer.savedY - this._bufferService.buffer.ybase, 0);
- this._curAttrData.fg = this._bufferService.buffer.savedCurAttrData.fg;
- this._curAttrData.bg = this._bufferService.buffer.savedCurAttrData.bg;
+ this._activeBuffer.x = this._activeBuffer.savedX || 0;
+ this._activeBuffer.y = Math.max(this._activeBuffer.savedY - this._activeBuffer.ybase, 0);
+ this._curAttrData.fg = this._activeBuffer.savedCurAttrData.fg;
+ this._curAttrData.bg = this._activeBuffer.savedCurAttrData.bg;
this._charsetService.charset = (this as any)._savedCharset;
- if (this._bufferService.buffer.savedCharset) {
- this._charsetService.charset = this._bufferService.buffer.savedCharset;
+ if (this._activeBuffer.savedCharset) {
+ this._charsetService.charset = this._activeBuffer.savedCharset;
}
this._restrictCursor();
return true;
@@ -2907,7 +2887,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* @vt: #Y ESC NEL "Next Line" "ESC E" "Move the cursor to the beginning of the next row."
*/
public nextLine(): boolean {
- this._bufferService.buffer.x = 0;
+ this._activeBuffer.x = 0;
this.index();
return true;
}
@@ -2987,13 +2967,12 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public index(): boolean {
this._restrictCursor();
- const buffer = this._bufferService.buffer;
- this._bufferService.buffer.y++;
- if (buffer.y === buffer.scrollBottom + 1) {
- buffer.y--;
+ this._activeBuffer.y++;
+ if (this._activeBuffer.y === this._activeBuffer.scrollBottom + 1) {
+ this._activeBuffer.y--;
this._bufferService.scroll(this._eraseAttrData());
- } else if (buffer.y >= this._bufferService.rows) {
- buffer.y = this._bufferService.rows - 1;
+ } else if (this._activeBuffer.y >= this._bufferService.rows) {
+ this._activeBuffer.y = this._bufferService.rows - 1;
}
this._restrictCursor();
return true;
@@ -3010,7 +2989,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* @vt: #Y ESC HTS "Horizontal Tabulation Set" "ESC H" "Places a tab stop at the current cursor position."
*/
public tabSet(): boolean {
- this._bufferService.buffer.tabs[this._bufferService.buffer.x] = true;
+ this._activeBuffer.tabs[this._activeBuffer.x] = true;
return true;
}
@@ -3025,17 +3004,16 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public reverseIndex(): boolean {
this._restrictCursor();
- const buffer = this._bufferService.buffer;
- if (buffer.y === buffer.scrollTop) {
+ if (this._activeBuffer.y === this._activeBuffer.scrollTop) {
// possibly move the code below to term.reverseScroll();
// test: echo -ne '\e[1;1H\e[44m\eM\e[0m'
// blankLine(true) is xterm/linux behavior
- const scrollRegionHeight = buffer.scrollBottom - buffer.scrollTop;
- buffer.lines.shiftElements(buffer.ybase + buffer.y, scrollRegionHeight, 1);
- buffer.lines.set(buffer.ybase + buffer.y, buffer.getBlankLine(this._eraseAttrData()));
- this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
+ const scrollRegionHeight = this._activeBuffer.scrollBottom - this._activeBuffer.scrollTop;
+ this._activeBuffer.lines.shiftElements(this._activeBuffer.ybase + this._activeBuffer.y, scrollRegionHeight, 1);
+ this._activeBuffer.lines.set(this._activeBuffer.ybase + this._activeBuffer.y, this._activeBuffer.getBlankLine(this._eraseAttrData()));
+ this._dirtyRowService.markRangeDirty(this._activeBuffer.scrollTop, this._activeBuffer.scrollBottom);
} else {
- buffer.y--;
+ this._activeBuffer.y--;
this._restrictCursor(); // quickfix to not run out of bounds
}
return true;
@@ -3096,12 +3074,11 @@ export class InputHandler extends Disposable implements IInputHandler {
cell.fg = this._curAttrData.fg;
cell.bg = this._curAttrData.bg;
- const buffer = this._bufferService.buffer;
this._setCursor(0, 0);
for (let yOffset = 0; yOffset < this._bufferService.rows; ++yOffset) {
- const row = buffer.ybase + buffer.y + yOffset;
- const line = buffer.lines.get(row);
+ const row = this._activeBuffer.ybase + this._activeBuffer.y + yOffset;
+ const line = this._activeBuffer.lines.get(row);
if (line) {
line.fill(cell);
line.isWrapped = false;
From ffef3dba002e91ecac6b0d6a888fac1076cc7279 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Wed, 1 Sep 2021 06:41:10 -0700
Subject: [PATCH 3/8] Avoid property use and float->number conversion
---
src/browser/Viewport.ts | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts
index 9ce14daf..fecad811 100644
--- a/src/browser/Viewport.ts
+++ b/src/browser/Viewport.ts
@@ -8,6 +8,8 @@ import { addDisposableDomListener } from 'browser/Lifecycle';
import { IColorSet, IViewport } from 'browser/Types';
import { ICharSizeService, IRenderService } from 'browser/services/Services';
import { IBufferService, IOptionsService } from 'common/services/Services';
+import { IBuffer } from 'common/buffer/Types';
+import { IRenderDimensions } from 'browser/renderer/Types';
const FALLBACK_SCROLL_BAR_WIDTH = 15;
@@ -18,12 +20,15 @@ const FALLBACK_SCROLL_BAR_WIDTH = 15;
export class Viewport extends Disposable implements IViewport {
public scrollBarWidth: number = 0;
private _currentRowHeight: number = 0;
+ private _currentScaledCellHeight: number = 0;
private _lastRecordedBufferLength: number = 0;
private _lastRecordedViewportHeight: number = 0;
private _lastRecordedBufferHeight: number = 0;
private _lastTouchY: number = 0;
private _lastScrollTop: number = 0;
private _lastHadScrollBar: boolean = false;
+ private _activeBuffer: IBuffer;
+ private _renderDimensions: IRenderDimensions;
// Stores a partial line amount when scrolling, this is used to keep track of how much of a line
// is scrolled so we can "scroll" over partial lines and feel natural on touchpads. This is a
@@ -51,6 +56,12 @@ export class Viewport extends Disposable implements IViewport {
this._lastHadScrollBar = true;
this.register(addDisposableDomListener(this._viewportElement, 'scroll', this._onScroll.bind(this)));
+ // Track properties used in performance critical code manually to avoid using slow getters
+ this._activeBuffer = this._bufferService.buffer;
+ this.register(this._bufferService.buffers.onBufferActivate(e => this._activeBuffer = e.activeBuffer));
+ this._renderDimensions = this._renderService.dimensions;
+ this.register(this._renderService.onDimensionsChange(e => this._renderDimensions = e));
+
// Perform this async to ensure the ICharSizeService is ready.
setTimeout(() => this.syncScrollArea(), 0);
}
@@ -79,6 +90,7 @@ export class Viewport extends Disposable implements IViewport {
private _innerRefresh(): void {
if (this._charSizeService.height > 0) {
this._currentRowHeight = this._renderService.dimensions.scaledCellHeight / window.devicePixelRatio;
+ this._currentScaledCellHeight = this._renderService.dimensions.scaledCellHeight;
this._lastRecordedViewportHeight = this._viewportElement.offsetHeight;
const newBufferHeight = Math.round(this._currentRowHeight * this._lastRecordedBufferLength) + (this._lastRecordedViewportHeight - this._renderService.dimensions.canvasHeight);
if (this._lastRecordedBufferHeight !== newBufferHeight) {
@@ -126,8 +138,7 @@ export class Viewport extends Disposable implements IViewport {
}
// If the buffer position doesn't match last scroll top
- const newScrollTop = this._bufferService.buffer.ydisp * this._currentRowHeight;
- if (this._lastScrollTop !== newScrollTop) {
+ if (this._lastScrollTop !== this._activeBuffer.ydisp * this._currentRowHeight) {
this._refresh(immediate);
return;
}
@@ -139,7 +150,7 @@ export class Viewport extends Disposable implements IViewport {
}
// If row height changed
- if (this._renderService.dimensions.scaledCellHeight / window.devicePixelRatio !== this._currentRowHeight) {
+ if (this._renderDimensions.scaledCellHeight !== this._currentScaledCellHeight) {
this._refresh(immediate);
return;
}
From 263c6d75bfccc34c0e926d44f9e79ab533b20bfd Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Wed, 1 Sep 2021 06:44:34 -0700
Subject: [PATCH 4/8] Avoid scrollTop call in hot code
This seems to have been added in f6d5abf but it's not clear why, scroll APIs
seem to work fine without it and using a DOM API here is causing slowness
---
src/browser/Viewport.ts | 6 ------
1 file changed, 6 deletions(-)
diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts
index fecad811..3c9bea4c 100644
--- a/src/browser/Viewport.ts
+++ b/src/browser/Viewport.ts
@@ -143,12 +143,6 @@ export class Viewport extends Disposable implements IViewport {
return;
}
- // If element's scroll top changed, this can happen when hiding the element
- if (this._lastScrollTop !== this._viewportElement.scrollTop) {
- this._refresh(immediate);
- return;
- }
-
// If row height changed
if (this._renderDimensions.scaledCellHeight !== this._currentScaledCellHeight) {
this._refresh(immediate);
From 3c8f600c572f16b63008e9d1bb6f56bf13325fdd Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 1 Sep 2021 11:27:14 -0700
Subject: [PATCH 5/8] fix #3348
---
addons/xterm-addon-webgl/src/WebglRenderer.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts
index 75b6230c..a3b8ddef 100644
--- a/addons/xterm-addon-webgl/src/WebglRenderer.ts
+++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts
@@ -108,7 +108,9 @@ export class WebglRenderer extends Disposable implements IRenderer {
for (const l of this._renderLayers) {
l.dispose();
}
- this._core.screenElement!.removeChild(this._canvas);
+ if (this._canvas.parentNode) {
+ this._core.screenElement?.removeChild(this._canvas);
+ }
super.dispose();
}
From 199e477349f8c5be149b1c9b57d23624ab1b65d6 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 1 Sep 2021 13:45:49 -0700
Subject: [PATCH 6/8] 3 -> 1 line
---
addons/xterm-addon-webgl/src/WebglRenderer.ts | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts
index a3b8ddef..9d8bde79 100644
--- a/addons/xterm-addon-webgl/src/WebglRenderer.ts
+++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts
@@ -108,9 +108,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
for (const l of this._renderLayers) {
l.dispose();
}
- if (this._canvas.parentNode) {
- this._core.screenElement?.removeChild(this._canvas);
- }
+ this._canvas.parentElement?.removeChild(this._canvas);
super.dispose();
}
From 453688a555b9589666c161b69e5167235d9c4b8a Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Thu, 2 Sep 2021 06:26:31 -0700
Subject: [PATCH 7/8] Split up unicode surrogates tests to avoid timeout
Fixes #3441
---
src/browser/Terminal.test.ts | 144 +++++++++++++++++------------------
1 file changed, 71 insertions(+), 73 deletions(-)
diff --git a/src/browser/Terminal.test.ts b/src/browser/Terminal.test.ts
index 84e6a87e..a102d93f 100644
--- a/src/browser/Terminal.test.ts
+++ b/src/browser/Terminal.test.ts
@@ -732,80 +732,78 @@ describe('Terminal', () => {
});
describe('unicode - surrogates', () => {
- it('2 characters per cell', async function (): Promise {
- this.timeout(10000); // This is needed because istanbul patches code and slows it down
- const high = String.fromCharCode(0xD800);
- const cell = new CellData();
- for (let i = 0xDC00; i <= 0xDCFF; ++i) {
- await term.writeP(high + String.fromCharCode(i));
- const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
- assert.equal(tchar.getChars(), high + String.fromCharCode(i));
- assert.equal(tchar.getChars().length, 2);
- assert.equal(tchar.getWidth(), 1);
- assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), '');
- term.reset();
- }
- });
- it('2 characters at last cell', async () => {
- const high = String.fromCharCode(0xD800);
- const cell = new CellData();
- for (let i = 0xDC00; i <= 0xDCFF; ++i) {
- term.buffer.x = term.cols - 1;
- await term.writeP(high + String.fromCharCode(i));
- assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars(), high + String.fromCharCode(i));
- assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars().length, 2);
- assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), '');
- term.reset();
- }
- });
- it('2 characters per cell over line end with autowrap', async function (): Promise {
- this.timeout(10000);
- const high = String.fromCharCode(0xD800);
- const cell = new CellData();
- for (let i = 0xDC00; i <= 0xDCFF; ++i) {
- term.buffer.x = term.cols - 1;
-
- await term.writeP('a' + high + String.fromCharCode(i));
- assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), 'a');
- assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), high + String.fromCharCode(i));
- assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars().length, 2);
- assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), '');
- term.reset();
- }
- });
- it('2 characters per cell over line end without autowrap', async function (): Promise {
- this.timeout(10000);
- const high = String.fromCharCode(0xD800);
- const cell = new CellData();
- for (let i = 0xDC00; i <= 0xDCFF; ++i) {
- term.buffer.x = term.cols - 1;
- await term.writeP('\x1b[?7l'); // Disable wraparound mode
- const width = wcwidth((0xD800 - 0xD800) * 0x400 + i - 0xDC00 + 0x10000);
- if (width !== 1) {
- continue;
+ for (let i = 0xDC00; i <= 0xDCF0; i += 0x10) {
+ const range = `0x${i.toString(16).toUpperCase()}-0x${(i + 0xF).toString(16).toUpperCase()}`;
+ it(`${range}: 2 characters per cell`, async function (): Promise {
+ const high = String.fromCharCode(0xD800);
+ const cell = new CellData();
+ for (let j = i; j <= i + 0xF; j++) {
+ await term.writeP(high + String.fromCharCode(j));
+ const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
+ assert.equal(tchar.getChars(), high + String.fromCharCode(j));
+ assert.equal(tchar.getChars().length, 2);
+ assert.equal(tchar.getWidth(), 1);
+ assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), '');
+ term.reset();
}
- await term.writeP('a' + high + String.fromCharCode(i));
- // auto wraparound mode should cut off the rest of the line
- assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), high + String.fromCharCode(i));
- assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars().length, 2);
- assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), '');
- term.reset();
- }
- });
- it('splitted surrogates', async function (): Promise {
- this.timeout(10000);
- const high = String.fromCharCode(0xD800);
- const cell = new CellData();
- for (let i = 0xDC00; i <= 0xDCFF; ++i) {
- await term.writeP(high + String.fromCharCode(i));
- const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
- assert.equal(tchar.getChars(), high + String.fromCharCode(i));
- assert.equal(tchar.getChars().length, 2);
- assert.equal(tchar.getWidth(), 1);
- assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), '');
- term.reset();
- }
- });
+ });
+ it(`${range}: 2 characters at last cell`, async () => {
+ const high = String.fromCharCode(0xD800);
+ const cell = new CellData();
+ term.buffer.x = term.cols - 1;
+ for (let j = i; j <= i + 0xF; j++) {
+ await term.writeP(high + String.fromCharCode(j));
+ assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars(), high + String.fromCharCode(j));
+ assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars().length, 2);
+ assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), '');
+ term.reset();
+ }
+ });
+ it(`${range}: 2 characters per cell over line end with autowrap`, async function (): Promise {
+ const high = String.fromCharCode(0xD800);
+ const cell = new CellData();
+ for (let j = i; j <= i + 0xF; j++) {
+ term.buffer.x = term.cols - 1;
+ await term.writeP('a' + high + String.fromCharCode(j));
+ assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), 'a');
+ assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), high + String.fromCharCode(j));
+ assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars().length, 2);
+ assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), '');
+ term.reset();
+ }
+ });
+ it(`${range}: 2 characters per cell over line end without autowrap`, async function (): Promise {
+ const high = String.fromCharCode(0xD800);
+ const cell = new CellData();
+ for (let j = i; j <= i + 0xF; j++) {
+ term.buffer.x = term.cols - 1;
+ await term.writeP('\x1b[?7l'); // Disable wraparound mode
+ const width = wcwidth((0xD800 - 0xD800) * 0x400 + j - 0xDC00 + 0x10000);
+ if (width !== 1) {
+ continue;
+ }
+ await term.writeP('a' + high + String.fromCharCode(j));
+ // auto wraparound mode should cut off the rest of the line
+ assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), high + String.fromCharCode(j));
+ assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars().length, 2);
+ assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), '');
+ term.reset();
+ }
+ });
+ it(`${range}: splitted surrogates`, async function (): Promise {
+ const high = String.fromCharCode(0xD800);
+ const cell = new CellData();
+ for (let j = i; j <= i + 0xF; j++) {
+ await term.writeP(high + String.fromCharCode(j));
+ const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
+ assert.equal(tchar.getChars(), high + String.fromCharCode(j));
+ assert.equal(tchar.getChars().length, 2);
+ assert.equal(tchar.getWidth(), 1);
+ assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), '');
+ term.reset();
+ }
+ });
+ }
});
describe('unicode - combining characters', () => {
From 20460a2be19c72956d0519fa49658a437f54416c Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Thu, 2 Sep 2021 07:34:04 -0700
Subject: [PATCH 8/8] Fire buffer activate event on buffer service reset
---
src/common/buffer/BufferSet.ts | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/common/buffer/BufferSet.ts b/src/common/buffer/BufferSet.ts
index b74c4eac..de220e8f 100644
--- a/src/common/buffer/BufferSet.ts
+++ b/src/common/buffer/BufferSet.ts
@@ -42,6 +42,10 @@ export class BufferSet extends Disposable implements IBufferSet {
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
this._alt = new Buffer(false, this._optionsService, this._bufferService);
this._activeBuffer = this._normal;
+ this._onBufferActivate.fire({
+ activeBuffer: this._normal,
+ inactiveBuffer: this._alt
+ });
this.setupTabStops();
}