diff --git a/.eslintrc.json b/.eslintrc.json
index 41767bed..32f8e992 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -9,7 +9,6 @@
"project": [
"src/browser/tsconfig.json",
"src/common/tsconfig.json",
- "src/tsconfig.json",
"test/api/tsconfig.json",
"test/benchmark/tsconfig.json",
"addons/xterm-addon-attach/src/tsconfig.json",
diff --git a/addons/xterm-addon-serialize/benchmark/SerializeAddon.benchmark.ts b/addons/xterm-addon-serialize/benchmark/SerializeAddon.benchmark.ts
index fbc16e03..c1764d8e 100644
--- a/addons/xterm-addon-serialize/benchmark/SerializeAddon.benchmark.ts
+++ b/addons/xterm-addon-serialize/benchmark/SerializeAddon.benchmark.ts
@@ -7,7 +7,7 @@ import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark';
import { spawn } from 'node-pty';
import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder';
-import { Terminal } from 'public/Terminal';
+import { Terminal } from 'browser/public/Terminal';
import { SerializeAddon } from 'SerializeAddon';
class TestTerminal extends Terminal {
diff --git a/addons/xterm-addon-serialize/benchmark/tsconfig.json b/addons/xterm-addon-serialize/benchmark/tsconfig.json
index 4e62ed59..42aaaa1b 100644
--- a/addons/xterm-addon-serialize/benchmark/tsconfig.json
+++ b/addons/xterm-addon-serialize/benchmark/tsconfig.json
@@ -11,8 +11,6 @@
"paths": {
"common/*": ["../../../src/common/*"],
"browser/*": ["../../../src/browser/*"],
- "public/*": ["../../../src/public/*"],
- "Terminal": ["../../../src/Terminal"],
"SerializeAddon": ["../src/SerializeAddon"]
}
},
diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts
index daf7f6fc..7165edb3 100644
--- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts
+++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts
@@ -3,7 +3,7 @@
* @license MIT
*/
-import { ITerminalOptions } from '../../../src/Types';
+import { ITerminalOptions } from '../../../src/common/Types';
import { ITheme } from 'xterm';
import { assert } from 'chai';
import { openTerminal, pollFor, writeSync, getBrowserType } from '../../../out-test/api/TestUtils';
diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts
index 0333fd20..ac997c96 100644
--- a/addons/xterm-addon-webgl/src/WebglRenderer.ts
+++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts
@@ -3,7 +3,6 @@
* @license MIT
*/
-import { ITerminal } from '../../../src/Types';
import { GlyphRenderer } from './GlyphRenderer';
import { LinkRenderLayer } from './renderLayer/LinkRenderLayer';
import { CursorRenderLayer } from './renderLayer/CursorRenderLayer';
@@ -17,7 +16,7 @@ import { NULL_CELL_CODE } from 'common/buffer/Constants';
import { Terminal, IEvent } from 'xterm';
import { IRenderLayer } from './renderLayer/Types';
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/Types';
-import { IColorSet } from 'browser/Types';
+import { ITerminal, IColorSet } from 'browser/Types';
import { EventEmitter } from 'common/EventEmitter';
import { CellData } from 'common/buffer/CellData';
@@ -52,8 +51,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
this._core = (this._terminal as any)._core;
this._renderLayers = [
- new LinkRenderLayer(this._core.screenElement, 2, this._colors, this._core),
- new CursorRenderLayer(this._core.screenElement, 3, this._colors, this._onRequestRedraw)
+ new LinkRenderLayer(this._core.screenElement!, 2, this._colors, this._core),
+ new CursorRenderLayer(this._core.screenElement!, 3, this._colors, this._onRequestRedraw)
];
this.dimensions = {
scaledCharWidth: 0,
@@ -83,7 +82,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
if (!this._gl) {
throw new Error('WebGL2 not supported ' + this._gl);
}
- this._core.screenElement.appendChild(this._canvas);
+ this._core.screenElement!.appendChild(this._canvas);
this._rectangleRenderer = new RectangleRenderer(this._terminal, this._colors, this._gl, this.dimensions);
this._glyphRenderer = new GlyphRenderer(this._terminal, this._colors, this._gl, this.dimensions);
@@ -91,12 +90,12 @@ export class WebglRenderer extends Disposable implements IRenderer {
// Update dimensions and acquire char atlas
this.onCharSizeChanged();
- this._isAttached = document.body.contains(this._core.screenElement);
+ this._isAttached = document.body.contains(this._core.screenElement!);
}
public dispose(): void {
this._renderLayers.forEach(l => l.dispose());
- this._core.screenElement.removeChild(this._canvas);
+ this._core.screenElement!.removeChild(this._canvas);
super.dispose();
}
@@ -150,8 +149,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
this._canvas.style.height = `${this.dimensions.canvasHeight}px`;
// Resize the screen
- this._core.screenElement.style.width = `${this.dimensions.canvasWidth}px`;
- this._core.screenElement.style.height = `${this.dimensions.canvasHeight}px`;
+ this._core.screenElement!.style.width = `${this.dimensions.canvasWidth}px`;
+ this._core.screenElement!.style.height = `${this.dimensions.canvasHeight}px`;
this._glyphRenderer.setDimensions(this.dimensions);
this._glyphRenderer.onResize();
@@ -229,7 +228,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
public renderRows(start: number, end: number): void {
if (!this._isAttached) {
- if (document.body.contains(this._core.screenElement) && (this._core as any)._charSizeService.width && (this._core as any)._charSizeService.height) {
+ if (document.body.contains(this._core.screenElement!) && (this._core as any)._charSizeService.width && (this._core as any)._charSizeService.height) {
this._updateDimensions();
this._refreshCharAtlas();
this._isAttached = true;
diff --git a/addons/xterm-addon-webgl/src/renderLayer/LinkRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/LinkRenderLayer.ts
index 10cf9c1f..8f6b3e6d 100644
--- a/addons/xterm-addon-webgl/src/renderLayer/LinkRenderLayer.ts
+++ b/addons/xterm-addon-webgl/src/renderLayer/LinkRenderLayer.ts
@@ -3,18 +3,17 @@
* @license MIT
*/
-import { ILinkifierAccessor } from '../../../../src/Types';
import { Terminal } from 'xterm';
import { BaseRenderLayer } from './BaseRenderLayer';
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
import { is256Color } from '../atlas/CharAtlasUtils';
-import { IColorSet, ILinkifierEvent } from 'browser/Types';
+import { ITerminal, IColorSet, ILinkifierEvent } from 'browser/Types';
import { IRenderDimensions } from 'browser/renderer/Types';
export class LinkRenderLayer extends BaseRenderLayer {
private _state: ILinkifierEvent | undefined;
- constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ILinkifierAccessor) {
+ constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ITerminal) {
super(container, 'link', zIndex, true, colors);
terminal.linkifier.onShowLinkUnderline(e => this._onShowLinkUnderline(e));
terminal.linkifier.onHideLinkUnderline(e => this._onHideLinkUnderline(e));
diff --git a/bin/test.js b/bin/test.js
index 1380a512..b9725acd 100644
--- a/bin/test.js
+++ b/bin/test.js
@@ -13,7 +13,6 @@ const env = { ...process.env };
env.NODE_PATH = path.resolve(__dirname, '../out');
let testFiles = [
- './out/*test.js',
'./out/**/*test.js',
'./addons/**/out/*test.js',
];
diff --git a/demo/client.ts b/demo/client.ts
index b009a534..eaa6f8c1 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -8,7 +8,7 @@
///
// Use tsc version (yarn watch)
-import { Terminal } from '../out/public/Terminal';
+import { Terminal } from '../out/browser/public/Terminal';
import { AttachAddon } from '../addons/xterm-addon-attach/out/AttachAddon';
import { FitAddon } from '../addons/xterm-addon-fit/out/FitAddon';
import { SearchAddon, ISearchOptions } from '../addons/xterm-addon-search/out/SearchAddon';
diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts
deleted file mode 100644
index 84d4dae5..00000000
--- a/src/InputHandler.test.ts
+++ /dev/null
@@ -1,1497 +0,0 @@
-/**
- * Copyright (c) 2017 The xterm.js authors. All rights reserved.
- * @license MIT
- */
-
-import { assert, expect } from 'chai';
-import { InputHandler } from './InputHandler';
-import { MockInputHandlingTerminal, TestTerminal } from './TestUtils.test';
-import { Terminal } from './Terminal';
-import { IBufferLine, IAttributeData } from 'common/Types';
-import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
-import { CellData } from 'common/buffer/CellData';
-import { Attributes } from 'common/buffer/Constants';
-import { AttributeData } from 'common/buffer/AttributeData';
-import { Params } from 'common/parser/Params';
-import { MockCoreService, MockBufferService, MockDirtyRowService, MockOptionsService, MockLogService, MockCoreMouseService, MockCharsetService, MockUnicodeService } from 'common/TestUtils.test';
-import { IBufferService } from 'common/services/Services';
-import { DEFAULT_OPTIONS } from 'common/services/OptionsService';
-import { clone } from 'common/Clone';
-
-function getCursor(term: TestTerminal): number[] {
- return [
- term.buffer.x,
- term.buffer.y
- ];
-}
-
-function getLines(term: TestTerminal, limit: number = term.rows): string[] {
- const res: string[] = [];
- for (let i = 0; i < limit; ++i) {
- res.push(term.buffer.lines.get(i).translateToString(true));
- }
- return res;
-}
-
-class TestInputHandler extends InputHandler {
- public get curAttrData(): IAttributeData { return (this as any)._curAttrData; }
-}
-
-describe('InputHandler', () => {
- describe('save and restore cursor', () => {
- const terminal = new MockInputHandlingTerminal();
- const bufferService = new MockBufferService(80, 30);
- bufferService.buffer.x = 1;
- bufferService.buffer.y = 2;
- bufferService.buffer.ybase = 0;
- const inputHandler = new TestInputHandler(terminal, bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any);
- inputHandler.curAttrData.fg = 3;
- // Save cursor position
- inputHandler.saveCursor();
- assert.equal(bufferService.buffer.x, 1);
- assert.equal(bufferService.buffer.y, 2);
- assert.equal(inputHandler.curAttrData.fg, 3);
- // Change cursor position
- bufferService.buffer.x = 10;
- bufferService.buffer.y = 20;
- inputHandler.curAttrData.fg = 30;
- // Restore cursor position
- inputHandler.restoreCursor();
- assert.equal(bufferService.buffer.x, 1);
- assert.equal(bufferService.buffer.y, 2);
- assert.equal(inputHandler.curAttrData.fg, 3);
- });
- describe('setCursorStyle', () => {
- it('should call Terminal.setOption with correct params', () => {
- const optionsService = new MockOptionsService();
- const inputHandler = new InputHandler(new MockInputHandlingTerminal(), new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), optionsService, new MockCoreMouseService(), new MockUnicodeService(), {} as any);
-
- inputHandler.setCursorStyle(Params.fromArray([0]));
- assert.equal(optionsService.options['cursorStyle'], 'block');
- assert.equal(optionsService.options['cursorBlink'], true);
-
- optionsService.options = clone(DEFAULT_OPTIONS);
- inputHandler.setCursorStyle(Params.fromArray([1]));
- assert.equal(optionsService.options['cursorStyle'], 'block');
- assert.equal(optionsService.options['cursorBlink'], true);
-
- optionsService.options = clone(DEFAULT_OPTIONS);
- inputHandler.setCursorStyle(Params.fromArray([2]));
- assert.equal(optionsService.options['cursorStyle'], 'block');
- assert.equal(optionsService.options['cursorBlink'], false);
-
- optionsService.options = clone(DEFAULT_OPTIONS);
- inputHandler.setCursorStyle(Params.fromArray([3]));
- assert.equal(optionsService.options['cursorStyle'], 'underline');
- assert.equal(optionsService.options['cursorBlink'], true);
-
- optionsService.options = clone(DEFAULT_OPTIONS);
- inputHandler.setCursorStyle(Params.fromArray([4]));
- assert.equal(optionsService.options['cursorStyle'], 'underline');
- assert.equal(optionsService.options['cursorBlink'], false);
-
- optionsService.options = clone(DEFAULT_OPTIONS);
- inputHandler.setCursorStyle(Params.fromArray([5]));
- assert.equal(optionsService.options['cursorStyle'], 'bar');
- assert.equal(optionsService.options['cursorBlink'], true);
-
- optionsService.options = clone(DEFAULT_OPTIONS);
- inputHandler.setCursorStyle(Params.fromArray([6]));
- assert.equal(optionsService.options['cursorStyle'], 'bar');
- assert.equal(optionsService.options['cursorBlink'], false);
- });
- });
- describe('setMode', () => {
- it('should toggle Terminal.bracketedPasteMode', () => {
- const terminal = new MockInputHandlingTerminal();
- terminal.bracketedPasteMode = false;
- const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any);
- // Set bracketed paste mode
- inputHandler.setModePrivate(Params.fromArray([2004]));
- assert.equal(terminal.bracketedPasteMode, true);
- // Reset bracketed paste mode
- inputHandler.resetModePrivate(Params.fromArray([2004]));
- assert.equal(terminal.bracketedPasteMode, false);
- });
- });
- describe('regression tests', function(): void {
- function termContent(bufferService: IBufferService, trim: boolean): string[] {
- const result = [];
- for (let i = 0; i < bufferService.rows; ++i) result.push(bufferService.buffer.lines.get(i).translateToString(trim));
- return result;
- }
-
- it('insertChars', function(): void {
- const term = new Terminal();
- const bufferService = new MockBufferService(80, 30);
- const inputHandler = new InputHandler(term, bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any);
-
- // insert some data in first and second line
- inputHandler.parse(Array(bufferService.cols - 9).join('a'));
- inputHandler.parse('1234567890');
- inputHandler.parse(Array(bufferService.cols - 9).join('a'));
- inputHandler.parse('1234567890');
- const line1: IBufferLine = bufferService.buffer.lines.get(0);
- expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890');
-
- // insert one char from params = [0]
- bufferService.buffer.y = 0;
- bufferService.buffer.x = 70;
- inputHandler.insertChars(Params.fromArray([0]));
- expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 123456789');
-
- // insert one char from params = [1]
- bufferService.buffer.y = 0;
- bufferService.buffer.x = 70;
- inputHandler.insertChars(Params.fromArray([1]));
- expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 12345678');
-
- // insert two chars from params = [2]
- bufferService.buffer.y = 0;
- bufferService.buffer.x = 70;
- inputHandler.insertChars(Params.fromArray([2]));
- expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 123456');
-
- // insert 10 chars from params = [10]
- bufferService.buffer.y = 0;
- bufferService.buffer.x = 70;
- inputHandler.insertChars(Params.fromArray([10]));
- expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' ');
- expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a'));
- });
- it('deleteChars', function(): void {
- const term = new Terminal();
- const bufferService = new MockBufferService(80, 30);
- const inputHandler = new InputHandler(term, bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any);
-
- // insert some data in first and second line
- inputHandler.parse(Array(bufferService.cols - 9).join('a'));
- inputHandler.parse('1234567890');
- inputHandler.parse(Array(bufferService.cols - 9).join('a'));
- inputHandler.parse('1234567890');
- const line1: IBufferLine = bufferService.buffer.lines.get(0);
- expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890');
-
- // delete one char from params = [0]
- bufferService.buffer.y = 0;
- bufferService.buffer.x = 70;
- inputHandler.deleteChars(Params.fromArray([0]));
- expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '234567890 ');
- expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '234567890');
-
- // insert one char from params = [1]
- bufferService.buffer.y = 0;
- bufferService.buffer.x = 70;
- inputHandler.deleteChars(Params.fromArray([1]));
- expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '34567890 ');
- expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '34567890');
-
- // insert two chars from params = [2]
- bufferService.buffer.y = 0;
- bufferService.buffer.x = 70;
- inputHandler.deleteChars(Params.fromArray([2]));
- expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '567890 ');
- expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '567890');
-
- // insert 10 chars from params = [10]
- bufferService.buffer.y = 0;
- bufferService.buffer.x = 70;
- inputHandler.deleteChars(Params.fromArray([10]));
- expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' ');
- expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a'));
- });
- it('eraseInLine', function(): void {
- const term = new Terminal();
- const bufferService = new MockBufferService(80, 30);
- const inputHandler = new InputHandler(term, bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any);
-
- // fill 6 lines to test 3 different states
- inputHandler.parse(Array(bufferService.cols + 1).join('a'));
- inputHandler.parse(Array(bufferService.cols + 1).join('a'));
- inputHandler.parse(Array(bufferService.cols + 1).join('a'));
-
- // params[0] - right erase
- bufferService.buffer.y = 0;
- bufferService.buffer.x = 70;
- inputHandler.eraseInLine(Params.fromArray([0]));
- expect(bufferService.buffer.lines.get(0).translateToString(false)).equals(Array(71).join('a') + ' ');
-
- // params[1] - left erase
- bufferService.buffer.y = 1;
- bufferService.buffer.x = 70;
- inputHandler.eraseInLine(Params.fromArray([1]));
- expect(bufferService.buffer.lines.get(1).translateToString(false)).equals(Array(71).join(' ') + ' aaaaaaaaa');
-
- // params[1] - left erase
- bufferService.buffer.y = 2;
- bufferService.buffer.x = 70;
- inputHandler.eraseInLine(Params.fromArray([2]));
- expect(bufferService.buffer.lines.get(2).translateToString(false)).equals(Array(bufferService.cols + 1).join(' '));
-
- });
- it('eraseInDisplay', function(): void {
- const term = new Terminal({cols: 80, rows: 7});
- const bufferService = new MockBufferService(80, 7);
- const inputHandler = new InputHandler(term, bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any);
-
- // fill display with a's
- for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
-
- // params [0] - right and below erase
- bufferService.buffer.y = 5;
- bufferService.buffer.x = 40;
- inputHandler.eraseInDisplay(Params.fromArray([0]));
- expect(termContent(bufferService, false)).eql([
- Array(bufferService.cols + 1).join('a'),
- Array(bufferService.cols + 1).join('a'),
- Array(bufferService.cols + 1).join('a'),
- Array(bufferService.cols + 1).join('a'),
- Array(bufferService.cols + 1).join('a'),
- Array(40 + 1).join('a') + Array(bufferService.cols - 40 + 1).join(' '),
- Array(bufferService.cols + 1).join(' ')
- ]);
- expect(termContent(bufferService, true)).eql([
- Array(bufferService.cols + 1).join('a'),
- Array(bufferService.cols + 1).join('a'),
- Array(bufferService.cols + 1).join('a'),
- Array(bufferService.cols + 1).join('a'),
- Array(bufferService.cols + 1).join('a'),
- Array(40 + 1).join('a'),
- ''
- ]);
-
- // reset
- bufferService.buffer.y = 0;
- bufferService.buffer.x = 0;
- for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
-
- // params [1] - left and above
- bufferService.buffer.y = 5;
- bufferService.buffer.x = 40;
- inputHandler.eraseInDisplay(Params.fromArray([1]));
- expect(termContent(bufferService, false)).eql([
- Array(bufferService.cols + 1).join(' '),
- Array(bufferService.cols + 1).join(' '),
- Array(bufferService.cols + 1).join(' '),
- Array(bufferService.cols + 1).join(' '),
- Array(bufferService.cols + 1).join(' '),
- Array(41 + 1).join(' ') + Array(bufferService.cols - 41 + 1).join('a'),
- Array(bufferService.cols + 1).join('a')
- ]);
- expect(termContent(bufferService, true)).eql([
- '',
- '',
- '',
- '',
- '',
- Array(41 + 1).join(' ') + Array(bufferService.cols - 41 + 1).join('a'),
- Array(bufferService.cols + 1).join('a')
- ]);
-
- // reset
- bufferService.buffer.y = 0;
- bufferService.buffer.x = 0;
- for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
-
- // params [2] - whole screen
- bufferService.buffer.y = 5;
- bufferService.buffer.x = 40;
- inputHandler.eraseInDisplay(Params.fromArray([2]));
- expect(termContent(bufferService, false)).eql([
- Array(bufferService.cols + 1).join(' '),
- Array(bufferService.cols + 1).join(' '),
- Array(bufferService.cols + 1).join(' '),
- Array(bufferService.cols + 1).join(' '),
- Array(bufferService.cols + 1).join(' '),
- Array(bufferService.cols + 1).join(' '),
- Array(bufferService.cols + 1).join(' ')
- ]);
- expect(termContent(bufferService, true)).eql([
- '',
- '',
- '',
- '',
- '',
- '',
- ''
- ]);
-
- // reset and add a wrapped line
- bufferService.buffer.y = 0;
- bufferService.buffer.x = 0;
- inputHandler.parse(Array(bufferService.cols + 1).join('a')); // line 0
- inputHandler.parse(Array(bufferService.cols + 10).join('a')); // line 1 and 2
- for (let i = 3; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
-
- // params[1] left and above with wrap
- // confirm precondition that line 2 is wrapped
- expect(bufferService.buffer.lines.get(2).isWrapped).true;
- bufferService.buffer.y = 2;
- bufferService.buffer.x = 40;
- inputHandler.eraseInDisplay(Params.fromArray([1]));
- expect(bufferService.buffer.lines.get(2).isWrapped).false;
-
- // reset and add a wrapped line
- bufferService.buffer.y = 0;
- bufferService.buffer.x = 0;
- inputHandler.parse(Array(bufferService.cols + 1).join('a')); // line 0
- inputHandler.parse(Array(bufferService.cols + 10).join('a')); // line 1 and 2
- for (let i = 3; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
-
- // params[1] left and above with wrap
- // confirm precondition that line 2 is wrapped
- expect(bufferService.buffer.lines.get(2).isWrapped).true;
- bufferService.buffer.y = 1;
- bufferService.buffer.x = 90; // Cursor is beyond last column
- inputHandler.eraseInDisplay(Params.fromArray([1]));
- expect(bufferService.buffer.lines.get(2).isWrapped).false;
- });
- });
- it('convertEol setting', function(): void {
- // not converting
- const termNotConverting = new Terminal({cols: 15, rows: 10});
- (termNotConverting as any)._inputHandler.parse('Hello\nWorld');
- expect(termNotConverting.buffer.lines.get(0).translateToString(false)).equals('Hello ');
- expect(termNotConverting.buffer.lines.get(1).translateToString(false)).equals(' World ');
- expect(termNotConverting.buffer.lines.get(0).translateToString(true)).equals('Hello');
- expect(termNotConverting.buffer.lines.get(1).translateToString(true)).equals(' World');
-
- // converting
- const termConverting = new Terminal({cols: 15, rows: 10, convertEol: true});
- (termConverting as any)._inputHandler.parse('Hello\nWorld');
- expect(termConverting.buffer.lines.get(0).translateToString(false)).equals('Hello ');
- expect(termConverting.buffer.lines.get(1).translateToString(false)).equals('World ');
- expect(termConverting.buffer.lines.get(0).translateToString(true)).equals('Hello');
- expect(termConverting.buffer.lines.get(1).translateToString(true)).equals('World');
- });
- describe('print', () => {
- it('should not cause an infinite loop (regression test)', () => {
- const term = new Terminal();
- const inputHandler = new InputHandler(term, new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any);
- const container = new Uint32Array(10);
- container[0] = 0x200B;
- inputHandler.print(container, 0, 1);
- });
- });
-
- describe('alt screen', () => {
- let term: Terminal;
- let bufferService: IBufferService;
- let handler: InputHandler;
-
- beforeEach(() => {
- term = new Terminal();
- bufferService = new MockBufferService(80, 30);
- handler = new InputHandler(term, bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any);
- });
- it('should handle DECSET/DECRST 47 (alt screen buffer)', () => {
- handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST');
- expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('');
- expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
- // Text color of 'TEST' should be red
- expect((bufferService.buffer.lines.get(1).loadCell(4, new CellData()).getFgColor())).to.equal(1);
- });
- it('should handle DECSET/DECRST 1047 (alt screen buffer)', () => {
- handler.parse('\x1b[?1047h\r\n\x1b[31mJUNK\x1b[?1047lTEST');
- expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('');
- expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
- // Text color of 'TEST' should be red
- expect((bufferService.buffer.lines.get(1).loadCell(4, new CellData()).getFgColor())).to.equal(1);
- });
- it('should handle DECSET/DECRST 1048 (alt screen cursor)', () => {
- handler.parse('\x1b[?1048h\r\n\x1b[31mJUNK\x1b[?1048lTEST');
- expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
- expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('JUNK');
- // Text color of 'TEST' should be default
- expect(bufferService.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
- // Text color of 'JUNK' should be red
- expect((bufferService.buffer.lines.get(1).loadCell(0, new CellData()).getFgColor())).to.equal(1);
- });
- it('should handle DECSET/DECRST 1049 (alt screen buffer+cursor)', () => {
- handler.parse('\x1b[?1049h\r\n\x1b[31mJUNK\x1b[?1049lTEST');
- expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
- expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('');
- // Text color of 'TEST' should be default
- expect(bufferService.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
- });
- it('should handle DECSET/DECRST 1049 - maintains saved cursor for alt buffer', () => {
- handler.parse('\x1b[?1049h\r\n\x1b[31m\x1b[s\x1b[?1049lTEST');
- expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
- // Text color of 'TEST' should be default
- expect(bufferService.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
- handler.parse('\x1b[?1049h\x1b[uTEST');
- expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('TEST');
- // Text color of 'TEST' should be red
- expect((bufferService.buffer.lines.get(1).loadCell(0, new CellData()).getFgColor())).to.equal(1);
- });
- it('should handle DECSET/DECRST 1049 - clears alt buffer with erase attributes', () => {
- handler.parse('\x1b[42m\x1b[?1049h');
- // Buffer should be filled with green background
- expect(bufferService.buffer.lines.get(20).loadCell(10, new CellData()).getBgColor()).to.equal(2);
- });
- });
-
- describe('text attributes', () => {
- let term: TestTerminal;
- beforeEach(() => {
- term = new TestTerminal();
- });
- it('bold', () => {
- term.writeSync('\x1b[1m');
- assert.equal(!!term.curAttrData.isBold(), true);
- term.writeSync('\x1b[22m');
- assert.equal(!!term.curAttrData.isBold(), false);
- });
- it('dim', () => {
- term.writeSync('\x1b[2m');
- assert.equal(!!term.curAttrData.isDim(), true);
- term.writeSync('\x1b[22m');
- assert.equal(!!term.curAttrData.isDim(), false);
- });
- it('italic', () => {
- term.writeSync('\x1b[3m');
- assert.equal(!!term.curAttrData.isItalic(), true);
- term.writeSync('\x1b[23m');
- assert.equal(!!term.curAttrData.isItalic(), false);
- });
- it('underline', () => {
- term.writeSync('\x1b[4m');
- assert.equal(!!term.curAttrData.isUnderline(), true);
- term.writeSync('\x1b[24m');
- assert.equal(!!term.curAttrData.isUnderline(), false);
- });
- it('blink', () => {
- term.writeSync('\x1b[5m');
- assert.equal(!!term.curAttrData.isBlink(), true);
- term.writeSync('\x1b[25m');
- assert.equal(!!term.curAttrData.isBlink(), false);
- });
- it('inverse', () => {
- term.writeSync('\x1b[7m');
- assert.equal(!!term.curAttrData.isInverse(), true);
- term.writeSync('\x1b[27m');
- assert.equal(!!term.curAttrData.isInverse(), false);
- });
- it('invisible', () => {
- term.writeSync('\x1b[8m');
- assert.equal(!!term.curAttrData.isInvisible(), true);
- term.writeSync('\x1b[28m');
- assert.equal(!!term.curAttrData.isInvisible(), false);
- });
- it('colormode palette 16', () => {
- assert.equal(term.curAttrData.getFgColorMode(), 0); // DEFAULT
- assert.equal(term.curAttrData.getBgColorMode(), 0); // DEFAULT
- // lower 8 colors
- for (let i = 0; i < 8; ++i) {
- term.writeSync(`\x1b[${i + 30};${i + 40}m`);
- assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P16);
- assert.equal(term.curAttrData.getFgColor(), i);
- assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P16);
- assert.equal(term.curAttrData.getBgColor(), i);
- }
- // reset to DEFAULT
- term.writeSync(`\x1b[39;49m`);
- assert.equal(term.curAttrData.getFgColorMode(), 0);
- assert.equal(term.curAttrData.getBgColorMode(), 0);
- });
- it('colormode palette 256', () => {
- assert.equal(term.curAttrData.getFgColorMode(), 0); // DEFAULT
- assert.equal(term.curAttrData.getBgColorMode(), 0); // DEFAULT
- // lower 8 colors
- for (let i = 0; i < 256; ++i) {
- term.writeSync(`\x1b[38;5;${i};48;5;${i}m`);
- assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P256);
- assert.equal(term.curAttrData.getFgColor(), i);
- assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P256);
- assert.equal(term.curAttrData.getBgColor(), i);
- }
- // reset to DEFAULT
- term.writeSync(`\x1b[39;49m`);
- assert.equal(term.curAttrData.getFgColorMode(), 0);
- assert.equal(term.curAttrData.getFgColor(), -1);
- assert.equal(term.curAttrData.getBgColorMode(), 0);
- assert.equal(term.curAttrData.getBgColor(), -1);
- });
- it('colormode RGB', () => {
- assert.equal(term.curAttrData.getFgColorMode(), 0); // DEFAULT
- assert.equal(term.curAttrData.getBgColorMode(), 0); // DEFAULT
- term.writeSync(`\x1b[38;2;1;2;3;48;2;4;5;6m`);
- assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_RGB);
- assert.equal(term.curAttrData.getFgColor(), 1 << 16 | 2 << 8 | 3);
- assert.deepEqual(AttributeData.toColorRGB(term.curAttrData.getFgColor()), [1, 2, 3]);
- assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_RGB);
- assert.deepEqual(AttributeData.toColorRGB(term.curAttrData.getBgColor()), [4, 5, 6]);
- // reset to DEFAULT
- term.writeSync(`\x1b[39;49m`);
- assert.equal(term.curAttrData.getFgColorMode(), 0);
- assert.equal(term.curAttrData.getFgColor(), -1);
- assert.equal(term.curAttrData.getBgColorMode(), 0);
- assert.equal(term.curAttrData.getBgColor(), -1);
- });
- it('colormode transition RGB to 256', () => {
- // enter RGB for FG and BG
- term.writeSync(`\x1b[38;2;1;2;3;48;2;4;5;6m`);
- // enter 256 for FG and BG
- term.writeSync(`\x1b[38;5;255;48;5;255m`);
- assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P256);
- assert.equal(term.curAttrData.getFgColor(), 255);
- assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P256);
- assert.equal(term.curAttrData.getBgColor(), 255);
- });
- it('colormode transition RGB to 16', () => {
- // enter RGB for FG and BG
- term.writeSync(`\x1b[38;2;1;2;3;48;2;4;5;6m`);
- // enter 16 for FG and BG
- term.writeSync(`\x1b[37;47m`);
- assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P16);
- assert.equal(term.curAttrData.getFgColor(), 7);
- assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P16);
- assert.equal(term.curAttrData.getBgColor(), 7);
- });
- it('colormode transition 16 to 256', () => {
- // enter 16 for FG and BG
- term.writeSync(`\x1b[37;47m`);
- // enter 256 for FG and BG
- term.writeSync(`\x1b[38;5;255;48;5;255m`);
- assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P256);
- assert.equal(term.curAttrData.getFgColor(), 255);
- assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P256);
- assert.equal(term.curAttrData.getBgColor(), 255);
- });
- it('colormode transition 256 to 16', () => {
- // enter 256 for FG and BG
- term.writeSync(`\x1b[38;5;255;48;5;255m`);
- // enter 16 for FG and BG
- term.writeSync(`\x1b[37;47m`);
- assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P16);
- assert.equal(term.curAttrData.getFgColor(), 7);
- assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P16);
- assert.equal(term.curAttrData.getBgColor(), 7);
- });
- it('should zero missing RGB values', () => {
- term.writeSync(`\x1b[38;2;1;2;3m`);
- term.writeSync(`\x1b[38;2;5m`);
- assert.deepEqual(AttributeData.toColorRGB(term.curAttrData.getFgColor()), [5, 0, 0]);
- });
- });
- describe('colon notation', () => {
- let termColon: TestTerminal;
- let termSemicolon: TestTerminal;
- beforeEach(() => {
- termColon = new TestTerminal();
- termSemicolon = new TestTerminal();
- });
- describe('should equal to semicolon', () => {
- it('CSI 38:2::50:100:150 m', () => {
- termColon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.writeSync('\x1b[38;2;50;100;150m');
- termColon.writeSync('\x1b[38:2::50:100:150m');
- assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 38:2::50:100: m', () => {
- termColon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.writeSync('\x1b[38;2;50;100;m');
- termColon.writeSync('\x1b[38:2::50:100:m');
- assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 0);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 38:2::50:: m', () => {
- termColon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.writeSync('\x1b[38;2;50;;m');
- termColon.writeSync('\x1b[38:2::50::m');
- assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 0 << 8 | 0);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 38:2:::: m', () => {
- termColon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.writeSync('\x1b[38;2;;;m');
- termColon.writeSync('\x1b[38:2::::m');
- assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 0 << 16 | 0 << 8 | 0);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 38;2::50:100:150 m', () => {
- termColon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.writeSync('\x1b[38;2;50;100;150m');
- termColon.writeSync('\x1b[38;2::50:100:150m');
- assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 38;2;50:100:150 m', () => {
- termColon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.writeSync('\x1b[38;2;50;100;150m');
- termColon.writeSync('\x1b[38;2;50:100:150m');
- assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 38;2;50;100:150 m', () => {
- termColon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.writeSync('\x1b[38;2;50;100;150m');
- termColon.writeSync('\x1b[38;2;50;100:150m');
- assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 38:5:50 m', () => {
- termColon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.writeSync('\x1b[38;5;50m');
- termColon.writeSync('\x1b[38:5:50m');
- assert.equal(termSemicolon.curAttrData.fg & 0xFF, 50);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 38:5: m', () => {
- termColon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.writeSync('\x1b[38;5;m');
- termColon.writeSync('\x1b[38:5:m');
- assert.equal(termSemicolon.curAttrData.fg & 0xFF, 0);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 38;5:50 m', () => {
- termColon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.writeSync('\x1b[38;5;50m');
- termColon.writeSync('\x1b[38;5:50m');
- assert.equal(termSemicolon.curAttrData.fg & 0xFF, 50);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- });
- describe('should fill early sequence end with default of 0', () => {
- it('CSI 38:2 m', () => {
- termColon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.writeSync('\x1b[38;2m');
- termColon.writeSync('\x1b[38:2m');
- assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 0 << 16 | 0 << 8 | 0);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 38:5 m', () => {
- termColon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.curAttrData.fg = 0xFFFFFFFF;
- termSemicolon.writeSync('\x1b[38;5m');
- termColon.writeSync('\x1b[38:5m');
- assert.equal(termSemicolon.curAttrData.fg & 0xFF, 0);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- });
- describe('should not interfere with leading/following SGR attrs', () => {
- it('CSI 1 ; 38:2::50:100:150 ; 4 m', () => {
- termSemicolon.writeSync('\x1b[1;38;2;50;100;150;4m');
- termColon.writeSync('\x1b[1;38:2::50:100:150;4m');
- assert.equal(!!termSemicolon.curAttrData.isBold(), true);
- assert.equal(!!termSemicolon.curAttrData.isUnderline(), true);
- assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 1 ; 38:2::50:100: ; 4 m', () => {
- termSemicolon.writeSync('\x1b[1;38;2;50;100;;4m');
- termColon.writeSync('\x1b[1;38:2::50:100:;4m');
- assert.equal(!!termSemicolon.curAttrData.isBold(), true);
- assert.equal(!!termSemicolon.curAttrData.isUnderline(), true);
- assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 0);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 1 ; 38:2::50:100 ; 4 m', () => {
- termSemicolon.writeSync('\x1b[1;38;2;50;100;;4m');
- termColon.writeSync('\x1b[1;38:2::50:100;4m');
- assert.equal(!!termSemicolon.curAttrData.isBold(), true);
- assert.equal(!!termSemicolon.curAttrData.isUnderline(), true);
- assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 0);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 1 ; 38:2:: ; 4 m', () => {
- termSemicolon.writeSync('\x1b[1;38;2;;;;4m');
- termColon.writeSync('\x1b[1;38:2::;4m');
- assert.equal(!!termSemicolon.curAttrData.isBold(), true);
- assert.equal(!!termSemicolon.curAttrData.isUnderline(), true);
- assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 0);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- it('CSI 1 ; 38;2:: ; 4 m', () => {
- termSemicolon.writeSync('\x1b[1;38;2;;;;4m');
- termColon.writeSync('\x1b[1;38;2::;4m');
- assert.equal(!!termSemicolon.curAttrData.isBold(), true);
- assert.equal(!!termSemicolon.curAttrData.isUnderline(), true);
- assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 0);
- assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg);
- });
- });
- });
- describe('cursor positioning', () => {
- let term: TestTerminal;
- beforeEach(() => {
- term = new TestTerminal({cols: 10, rows: 10});
- });
- it('cursor forward (CUF)', () => {
- term.writeSync('\x1b[C');
- assert.deepEqual(getCursor(term), [1, 0]);
- term.writeSync('\x1b[1C');
- assert.deepEqual(getCursor(term), [2, 0]);
- term.writeSync('\x1b[4C');
- assert.deepEqual(getCursor(term), [6, 0]);
- term.writeSync('\x1b[100C');
- assert.deepEqual(getCursor(term), [9, 0]);
- // should not change y
- term.buffer.x = 8;
- term.buffer.y = 4;
- term.writeSync('\x1b[C');
- assert.deepEqual(getCursor(term), [9, 4]);
- });
- it('cursor backward (CUB)', () => {
- term.writeSync('\x1b[D');
- assert.deepEqual(getCursor(term), [0, 0]);
- term.writeSync('\x1b[1D');
- assert.deepEqual(getCursor(term), [0, 0]);
- // place cursor at end of first line
- term.writeSync('\x1b[100C');
- term.writeSync('\x1b[D');
- assert.deepEqual(getCursor(term), [8, 0]);
- term.writeSync('\x1b[1D');
- assert.deepEqual(getCursor(term), [7, 0]);
- term.writeSync('\x1b[4D');
- assert.deepEqual(getCursor(term), [3, 0]);
- term.writeSync('\x1b[100D');
- assert.deepEqual(getCursor(term), [0, 0]);
- // should not change y
- term.buffer.x = 4;
- term.buffer.y = 4;
- term.writeSync('\x1b[D');
- assert.deepEqual(getCursor(term), [3, 4]);
- });
- it('cursor down (CUD)', () => {
- term.writeSync('\x1b[B');
- assert.deepEqual(getCursor(term), [0, 1]);
- term.writeSync('\x1b[1B');
- assert.deepEqual(getCursor(term), [0, 2]);
- term.writeSync('\x1b[4B');
- assert.deepEqual(getCursor(term), [0, 6]);
- term.writeSync('\x1b[100B');
- assert.deepEqual(getCursor(term), [0, 9]);
- // should not change x
- term.buffer.x = 8;
- term.buffer.y = 0;
- term.writeSync('\x1b[B');
- assert.deepEqual(getCursor(term), [8, 1]);
- });
- it('cursor up (CUU)', () => {
- term.writeSync('\x1b[A');
- assert.deepEqual(getCursor(term), [0, 0]);
- term.writeSync('\x1b[1A');
- assert.deepEqual(getCursor(term), [0, 0]);
- // place cursor at beginning of last row
- term.writeSync('\x1b[100B');
- term.writeSync('\x1b[A');
- assert.deepEqual(getCursor(term), [0, 8]);
- term.writeSync('\x1b[1A');
- assert.deepEqual(getCursor(term), [0, 7]);
- term.writeSync('\x1b[4A');
- assert.deepEqual(getCursor(term), [0, 3]);
- term.writeSync('\x1b[100A');
- assert.deepEqual(getCursor(term), [0, 0]);
- // should not change x
- term.buffer.x = 8;
- term.buffer.y = 9;
- term.writeSync('\x1b[A');
- assert.deepEqual(getCursor(term), [8, 8]);
- });
- it('cursor next line (CNL)', () => {
- term.writeSync('\x1b[E');
- assert.deepEqual(getCursor(term), [0, 1]);
- term.writeSync('\x1b[1E');
- assert.deepEqual(getCursor(term), [0, 2]);
- term.writeSync('\x1b[4E');
- assert.deepEqual(getCursor(term), [0, 6]);
- term.writeSync('\x1b[100E');
- assert.deepEqual(getCursor(term), [0, 9]);
- // should reset x to zero
- term.buffer.x = 8;
- term.buffer.y = 0;
- term.writeSync('\x1b[E');
- assert.deepEqual(getCursor(term), [0, 1]);
- });
- it('cursor previous line (CPL)', () => {
- term.writeSync('\x1b[F');
- assert.deepEqual(getCursor(term), [0, 0]);
- term.writeSync('\x1b[1F');
- assert.deepEqual(getCursor(term), [0, 0]);
- // place cursor at beginning of last row
- term.writeSync('\x1b[100E');
- term.writeSync('\x1b[F');
- assert.deepEqual(getCursor(term), [0, 8]);
- term.writeSync('\x1b[1F');
- assert.deepEqual(getCursor(term), [0, 7]);
- term.writeSync('\x1b[4F');
- assert.deepEqual(getCursor(term), [0, 3]);
- term.writeSync('\x1b[100F');
- assert.deepEqual(getCursor(term), [0, 0]);
- // should reset x to zero
- term.buffer.x = 8;
- term.buffer.y = 9;
- term.writeSync('\x1b[F');
- assert.deepEqual(getCursor(term), [0, 8]);
- });
- it('cursor character absolute (CHA)', () => {
- term.writeSync('\x1b[G');
- assert.deepEqual(getCursor(term), [0, 0]);
- term.writeSync('\x1b[1G');
- assert.deepEqual(getCursor(term), [0, 0]);
- term.writeSync('\x1b[2G');
- assert.deepEqual(getCursor(term), [1, 0]);
- term.writeSync('\x1b[5G');
- assert.deepEqual(getCursor(term), [4, 0]);
- term.writeSync('\x1b[100G');
- assert.deepEqual(getCursor(term), [9, 0]);
- });
- it('cursor position (CUP)', () => {
- term.buffer.x = 5;
- term.buffer.y = 5;
- term.writeSync('\x1b[H');
- assert.deepEqual(getCursor(term), [0, 0]);
- term.buffer.x = 5;
- term.buffer.y = 5;
- term.writeSync('\x1b[1H');
- assert.deepEqual(getCursor(term), [0, 0]);
- term.buffer.x = 5;
- term.buffer.y = 5;
- term.writeSync('\x1b[1;1H');
- assert.deepEqual(getCursor(term), [0, 0]);
- term.buffer.x = 5;
- term.buffer.y = 5;
- term.writeSync('\x1b[8H');
- assert.deepEqual(getCursor(term), [0, 7]);
- term.buffer.x = 5;
- term.buffer.y = 5;
- term.writeSync('\x1b[;8H');
- assert.deepEqual(getCursor(term), [7, 0]);
- term.buffer.x = 5;
- term.buffer.y = 5;
- term.writeSync('\x1b[100;100H');
- assert.deepEqual(getCursor(term), [9, 9]);
- });
- it('horizontal position absolute (HPA)', () => {
- term.writeSync('\x1b[`');
- assert.deepEqual(getCursor(term), [0, 0]);
- term.writeSync('\x1b[1`');
- assert.deepEqual(getCursor(term), [0, 0]);
- term.writeSync('\x1b[2`');
- assert.deepEqual(getCursor(term), [1, 0]);
- term.writeSync('\x1b[5`');
- assert.deepEqual(getCursor(term), [4, 0]);
- term.writeSync('\x1b[100`');
- assert.deepEqual(getCursor(term), [9, 0]);
- });
- it('horizontal position relative (HPR)', () => {
- term.writeSync('\x1b[a');
- assert.deepEqual(getCursor(term), [1, 0]);
- term.writeSync('\x1b[1a');
- assert.deepEqual(getCursor(term), [2, 0]);
- term.writeSync('\x1b[4a');
- assert.deepEqual(getCursor(term), [6, 0]);
- term.writeSync('\x1b[100a');
- assert.deepEqual(getCursor(term), [9, 0]);
- // should not change y
- term.buffer.x = 8;
- term.buffer.y = 4;
- term.writeSync('\x1b[a');
- assert.deepEqual(getCursor(term), [9, 4]);
- });
- it('vertical position absolute (VPA)', () => {
- term.writeSync('\x1b[d');
- assert.deepEqual(getCursor(term), [0, 0]);
- term.writeSync('\x1b[1d');
- assert.deepEqual(getCursor(term), [0, 0]);
- term.writeSync('\x1b[2d');
- assert.deepEqual(getCursor(term), [0, 1]);
- term.writeSync('\x1b[5d');
- assert.deepEqual(getCursor(term), [0, 4]);
- term.writeSync('\x1b[100d');
- assert.deepEqual(getCursor(term), [0, 9]);
- // should not change x
- term.buffer.x = 8;
- term.buffer.y = 4;
- term.writeSync('\x1b[d');
- assert.deepEqual(getCursor(term), [8, 0]);
- });
- it('vertical position relative (VPR)', () => {
- term.writeSync('\x1b[e');
- assert.deepEqual(getCursor(term), [0, 1]);
- term.writeSync('\x1b[1e');
- assert.deepEqual(getCursor(term), [0, 2]);
- term.writeSync('\x1b[4e');
- assert.deepEqual(getCursor(term), [0, 6]);
- term.writeSync('\x1b[100e');
- assert.deepEqual(getCursor(term), [0, 9]);
- // should not change x
- term.buffer.x = 8;
- term.buffer.y = 4;
- term.writeSync('\x1b[e');
- assert.deepEqual(getCursor(term), [8, 5]);
- });
- describe('should clamp cursor into addressible range', () => {
- it('CUF', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[C');
- assert.deepEqual(getCursor(term), [9, 9]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[C');
- assert.deepEqual(getCursor(term), [1, 0]);
- });
- it('CUB', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[D');
- assert.deepEqual(getCursor(term), [8, 9]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[D');
- assert.deepEqual(getCursor(term), [0, 0]);
- });
- it('CUD', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[B');
- assert.deepEqual(getCursor(term), [9, 9]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[B');
- assert.deepEqual(getCursor(term), [0, 1]);
- });
- it('CUU', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[A');
- assert.deepEqual(getCursor(term), [9, 8]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[A');
- assert.deepEqual(getCursor(term), [0, 0]);
- });
- it('CNL', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[E');
- assert.deepEqual(getCursor(term), [0, 9]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[E');
- assert.deepEqual(getCursor(term), [0, 1]);
- });
- it('CPL', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[F');
- assert.deepEqual(getCursor(term), [0, 8]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[F');
- assert.deepEqual(getCursor(term), [0, 0]);
- });
- it('CHA', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[5G');
- assert.deepEqual(getCursor(term), [4, 9]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[5G');
- assert.deepEqual(getCursor(term), [4, 0]);
- });
- it('CUP', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[5;5H');
- assert.deepEqual(getCursor(term), [4, 4]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[5;5H');
- assert.deepEqual(getCursor(term), [4, 4]);
- });
- it('HPA', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[5`');
- assert.deepEqual(getCursor(term), [4, 9]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[5`');
- assert.deepEqual(getCursor(term), [4, 0]);
- });
- it('HPR', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[a');
- assert.deepEqual(getCursor(term), [9, 9]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[a');
- assert.deepEqual(getCursor(term), [1, 0]);
- });
- it('VPA', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[5d');
- assert.deepEqual(getCursor(term), [9, 4]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[5d');
- assert.deepEqual(getCursor(term), [0, 4]);
- });
- it('VPR', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[e');
- assert.deepEqual(getCursor(term), [9, 9]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[e');
- assert.deepEqual(getCursor(term), [0, 1]);
- });
- it('DCH', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[P');
- assert.deepEqual(getCursor(term), [9, 9]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[P');
- assert.deepEqual(getCursor(term), [0, 0]);
- });
- it('DCH - should delete last cell', () => {
- term.writeSync('0123456789\x1b[P');
- assert.equal(term.buffer.lines.get(0).translateToString(false), '012345678 ');
- });
- it('ECH', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[X');
- assert.deepEqual(getCursor(term), [9, 9]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[X');
- assert.deepEqual(getCursor(term), [0, 0]);
- });
- it('ECH - should delete last cell', () => {
- term.writeSync('0123456789\x1b[X');
- assert.equal(term.buffer.lines.get(0).translateToString(false), '012345678 ');
- });
- it('ICH', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[@');
- assert.deepEqual(getCursor(term), [9, 9]);
- term.buffer.x = -10000;
- term.buffer.y = -10000;
- term.writeSync('\x1b[@');
- assert.deepEqual(getCursor(term), [0, 0]);
- });
- it('ICH - should delete last cell', () => {
- term.writeSync('0123456789\x1b[@');
- assert.equal(term.buffer.lines.get(0).translateToString(false), '012345678 ');
- });
- });
- });
- describe('DECSTBM - scroll margins', () => {
- let term: TestTerminal;
- beforeEach(() => {
- term = new TestTerminal({cols: 10, rows: 10});
- });
- it('should default to whole viewport', () => {
- term.writeSync('\x1b[r');
- assert.equal(term.buffer.scrollTop, 0);
- assert.equal(term.buffer.scrollBottom, 9);
- term.writeSync('\x1b[3;7r');
- assert.equal(term.buffer.scrollTop, 2);
- assert.equal(term.buffer.scrollBottom, 6);
- term.writeSync('\x1b[0;0r');
- assert.equal(term.buffer.scrollTop, 0);
- assert.equal(term.buffer.scrollBottom, 9);
- });
- it('should clamp bottom', () => {
- term.writeSync('\x1b[3;1000r');
- assert.equal(term.buffer.scrollTop, 2);
- assert.equal(term.buffer.scrollBottom, 9);
- });
- it('should only apply for top < bottom', () => {
- term.writeSync('\x1b[7;2r');
- assert.equal(term.buffer.scrollTop, 0);
- assert.equal(term.buffer.scrollBottom, 9);
- });
- it('should home cursor', () => {
- term.buffer.x = 10000;
- term.buffer.y = 10000;
- term.writeSync('\x1b[2;7r');
- assert.deepEqual(getCursor(term), [0, 0]);
- });
- });
- describe('scroll margins', () => {
- let term: TestTerminal;
- beforeEach(() => {
- term = new TestTerminal({cols: 10, rows: 10});
- });
- it('scrollUp', () => {
- term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Sm');
- assert.deepEqual(getLines(term), ['m', '3', '', '', '4', '5', '6', '7', '8', '9']);
- });
- it('scrollDown', () => {
- term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Tm');
- assert.deepEqual(getLines(term), ['m', '', '', '1', '4', '5', '6', '7', '8', '9']);
- });
- it('insertLines - out of margins', () => {
- term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r');
- assert.equal(term.buffer.scrollTop, 2);
- assert.equal(term.buffer.scrollBottom, 5);
- term.writeSync('\x1b[2Lm');
- assert.deepEqual(getLines(term), ['m', '1', '2', '3', '4', '5', '6', '7', '8', '9']);
- term.writeSync('\x1b[2H\x1b[2Ln');
- assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', '6', '7', '8', '9']);
- // skip below scrollbottom
- term.writeSync('\x1b[7H\x1b[2Lo');
- assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', '7', '8', '9']);
- term.writeSync('\x1b[8H\x1b[2Lp');
- assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', '9']);
- term.writeSync('\x1b[100H\x1b[2Lq');
- assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', 'q']);
- });
- it('insertLines - within margins', () => {
- term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r');
- assert.equal(term.buffer.scrollTop, 2);
- assert.equal(term.buffer.scrollBottom, 5);
- term.writeSync('\x1b[3H\x1b[2Lm');
- assert.deepEqual(getLines(term), ['0', '1', 'm', '', '2', '3', '6', '7', '8', '9']);
- term.writeSync('\x1b[6H\x1b[2Ln');
- assert.deepEqual(getLines(term), ['0', '1', 'm', '', '2', 'n', '6', '7', '8', '9']);
- });
- it('deleteLines - out of margins', () => {
- term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r');
- assert.equal(term.buffer.scrollTop, 2);
- assert.equal(term.buffer.scrollBottom, 5);
- term.writeSync('\x1b[2Mm');
- assert.deepEqual(getLines(term), ['m', '1', '2', '3', '4', '5', '6', '7', '8', '9']);
- term.writeSync('\x1b[2H\x1b[2Mn');
- assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', '6', '7', '8', '9']);
- // skip below scrollbottom
- term.writeSync('\x1b[7H\x1b[2Mo');
- assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', '7', '8', '9']);
- term.writeSync('\x1b[8H\x1b[2Mp');
- assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', '9']);
- term.writeSync('\x1b[100H\x1b[2Mq');
- assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', 'q']);
- });
- it('deleteLines - within margins', () => {
- term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r');
- assert.equal(term.buffer.scrollTop, 2);
- assert.equal(term.buffer.scrollBottom, 5);
- term.writeSync('\x1b[6H\x1b[2Mm');
- assert.deepEqual(getLines(term), ['0', '1', '2', '3', '4', 'm', '6', '7', '8', '9']);
- term.writeSync('\x1b[3H\x1b[2Mn');
- assert.deepEqual(getLines(term), ['0', '1', 'n', 'm', '', '', '6', '7', '8', '9']);
- });
- });
- describe('SL/SR/DECIC/DECDC', () => {
- let term: TestTerminal;
- beforeEach(() => {
- term = new TestTerminal({cols: 5, rows: 5, scrollback: 1});
- });
- it('SL (scrollLeft)', () => {
- term.writeSync('12345'.repeat(6));
- term.writeSync('\x1b[ @');
- assert.deepEqual(getLines(term, term.rows + 1), ['12345', '2345', '2345', '2345', '2345', '2345']);
- term.writeSync('\x1b[0 @');
- assert.deepEqual(getLines(term, term.rows + 1), ['12345', '345', '345', '345', '345', '345']);
- term.writeSync('\x1b[2 @');
- assert.deepEqual(getLines(term, term.rows + 1), ['12345', '5', '5', '5', '5', '5']);
- });
- it('SR (scrollRight)', () => {
- term.writeSync('12345'.repeat(6));
- term.writeSync('\x1b[ A');
- assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1234', ' 1234', ' 1234', ' 1234', ' 1234']);
- term.writeSync('\x1b[0 A');
- assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 123', ' 123', ' 123', ' 123', ' 123']);
- term.writeSync('\x1b[2 A');
- assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1', ' 1', ' 1', ' 1', ' 1']);
- });
- it('insertColumns (DECIC)', () => {
- term.writeSync('12345'.repeat(6));
- term.writeSync('\x1b[3;3H');
- term.writeSync('\x1b[\'}');
- assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
- term.reset();
- term.writeSync('12345'.repeat(6));
- term.writeSync('\x1b[3;3H');
- term.writeSync('\x1b[1\'}');
- assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
- term.reset();
- term.writeSync('12345'.repeat(6));
- term.writeSync('\x1b[3;3H');
- term.writeSync('\x1b[2\'}');
- assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 3', '12 3', '12 3', '12 3', '12 3']);
- });
- it('deleteColumns (DECDC)', () => {
- term.writeSync('12345'.repeat(6));
- term.writeSync('\x1b[3;3H');
- term.writeSync('\x1b[\'~');
- assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']);
- term.reset();
- term.writeSync('12345'.repeat(6));
- term.writeSync('\x1b[3;3H');
- term.writeSync('\x1b[1\'~');
- assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']);
- term.reset();
- term.writeSync('12345'.repeat(6));
- term.writeSync('\x1b[3;3H');
- term.writeSync('\x1b[2\'~');
- assert.deepEqual(getLines(term, term.rows + 1), ['12345', '125', '125', '125', '125', '125']);
- });
- });
- it('should parse big chunks in smaller subchunks', () => {
- // max single chunk size is hardcoded as 131072
- const calls: any[] = [];
- const term = new TestTerminal({cols: 10, rows: 10});
- (term as any)._inputHandler._parser.parse = (data: Uint32Array, length: number) => {
- calls.push([data.length, length]);
- };
- term.writeSync('12345');
- term.writeSync('a'.repeat(10000));
- term.writeSync('a'.repeat(200000));
- term.writeSync('a'.repeat(300000));
- assert.deepEqual(calls, [
- [4096, 5],
- [10000, 10000],
- [131072, 131072], [131072, 200000 - 131072],
- [131072, 131072], [131072, 131072], [131072, 300000 - 131072 - 131072]
- ]);
- });
- describe('windowOptions', () => {
- it('all should be disabled by default and not report', () => {
- const term = new TestTerminal({cols: 10, rows: 10});
- const stack: string[] = [];
- term.onData(data => stack.push(data));
- term.writeSync('\x1b[14t');
- term.writeSync('\x1b[16t');
- term.writeSync('\x1b[18t');
- term.writeSync('\x1b[20t');
- term.writeSync('\x1b[21t');
- assert.deepEqual(stack, []);
- });
- it('14 - GetWinSizePixels', () => {
- const term = new TestTerminal({cols: 10, rows: 10, windowOptions: {getWinSizePixels: true}});
- const stack: string[] = [];
- term.onData(data => stack.push(data));
- term.writeSync('\x1b[14t');
- // does not report in test terminal due to missing renderer
- assert.deepEqual(stack, []);
- });
- it('16 - GetCellSizePixels', () => {
- const term = new TestTerminal({cols: 10, rows: 10, windowOptions: {getCellSizePixels: true}});
- const stack: string[] = [];
- term.onData(data => stack.push(data));
- term.writeSync('\x1b[16t');
- // does not report in test terminal due to missing renderer
- assert.deepEqual(stack, []);
- });
- it('18 - GetWinSizeChars', () => {
- const term = new TestTerminal({cols: 10, rows: 10, windowOptions: {getWinSizeChars: true}});
- const stack: string[] = [];
- term.onData(data => stack.push(data));
- term.writeSync('\x1b[18t');
- assert.deepEqual(stack, ['\x1b[8;10;10t']);
- term.resize(50, 20);
- term.writeSync('\x1b[18t');
- assert.deepEqual(stack, ['\x1b[8;10;10t', '\x1b[8;20;50t']);
- });
- it('22/23 - PushTitle/PopTitle', () => {
- const term = new TestTerminal({cols: 10, rows: 10, windowOptions: {pushTitle: true, popTitle: true}});
- const stack: string[] = [];
- term.onTitleChange(data => stack.push(data));
- term.writeSync('\x1b]0;1\x07');
- term.writeSync('\x1b[22t');
- term.writeSync('\x1b]0;2\x07');
- term.writeSync('\x1b[22t');
- term.writeSync('\x1b]0;3\x07');
- term.writeSync('\x1b[22t');
- assert.deepEqual((term as any)._inputHandler._windowTitleStack, ['1', '2', '3']);
- assert.deepEqual((term as any)._inputHandler._iconNameStack, ['1', '2', '3']);
- assert.deepEqual(stack, ['1', '2', '3']);
- term.writeSync('\x1b[23t');
- term.writeSync('\x1b[23t');
- term.writeSync('\x1b[23t');
- term.writeSync('\x1b[23t'); // one more to test "overflow"
- assert.deepEqual((term as any)._inputHandler._windowTitleStack, []);
- assert.deepEqual((term as any)._inputHandler._iconNameStack, []);
- assert.deepEqual(stack, ['1', '2', '3', '3', '2', '1']);
- });
- it('22/23 - PushTitle/PopTitle with ;1', () => {
- const term = new TestTerminal({cols: 10, rows: 10, windowOptions: {pushTitle: true, popTitle: true}});
- const stack: string[] = [];
- term.onTitleChange(data => stack.push(data));
- term.writeSync('\x1b]0;1\x07');
- term.writeSync('\x1b[22;1t');
- term.writeSync('\x1b]0;2\x07');
- term.writeSync('\x1b[22;1t');
- term.writeSync('\x1b]0;3\x07');
- term.writeSync('\x1b[22;1t');
- assert.deepEqual((term as any)._inputHandler._windowTitleStack, []);
- assert.deepEqual((term as any)._inputHandler._iconNameStack, ['1', '2', '3']);
- assert.deepEqual(stack, ['1', '2', '3']);
- term.writeSync('\x1b[23;1t');
- term.writeSync('\x1b[23;1t');
- term.writeSync('\x1b[23;1t');
- term.writeSync('\x1b[23;1t'); // one more to test "overflow"
- assert.deepEqual((term as any)._inputHandler._windowTitleStack, []);
- assert.deepEqual((term as any)._inputHandler._iconNameStack, []);
- assert.deepEqual(stack, ['1', '2', '3']);
- });
- it('22/23 - PushTitle/PopTitle with ;2', () => {
- const term = new TestTerminal({cols: 10, rows: 10, windowOptions: {pushTitle: true, popTitle: true}});
- const stack: string[] = [];
- term.onTitleChange(data => stack.push(data));
- term.writeSync('\x1b]0;1\x07');
- term.writeSync('\x1b[22;2t');
- term.writeSync('\x1b]0;2\x07');
- term.writeSync('\x1b[22;2t');
- term.writeSync('\x1b]0;3\x07');
- term.writeSync('\x1b[22;2t');
- assert.deepEqual((term as any)._inputHandler._windowTitleStack, ['1', '2', '3']);
- assert.deepEqual((term as any)._inputHandler._iconNameStack, []);
- assert.deepEqual(stack, ['1', '2', '3']);
- term.writeSync('\x1b[23;2t');
- term.writeSync('\x1b[23;2t');
- term.writeSync('\x1b[23;2t');
- term.writeSync('\x1b[23;2t'); // one more to test "overflow"
- assert.deepEqual((term as any)._inputHandler._windowTitleStack, []);
- assert.deepEqual((term as any)._inputHandler._iconNameStack, []);
- assert.deepEqual(stack, ['1', '2', '3', '3', '2', '1']);
- });
- it('DECCOLM - should only work with "SetWinLines" (24) enabled', () => {
- // disabled
- const term = new TestTerminal({cols: 10, rows: 10});
- term.writeSync('\x1b[?3l');
- assert.equal((term as any)._bufferService.cols, 10);
- term.writeSync('\x1b[?3h');
- assert.equal((term as any)._bufferService.cols, 10);
- // enabled
- const term2 = new TestTerminal({cols: 10, rows: 10, windowOptions: {setWinLines: true}});
- term2.writeSync('\x1b[?3l');
- assert.equal((term2 as any)._bufferService.cols, 80);
- term2.writeSync('\x1b[?3h');
- assert.equal((term2 as any)._bufferService.cols, 132);
- });
- });
- describe('should correctly reset cells taken by wide chars', () => {
- let term: TestTerminal;
- beforeEach(() => {
- term = new TestTerminal({cols: 10, rows: 5, scrollback: 1});
- term.writeSync('¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥');
- });
- it('print', () => {
- term.writeSync('\x1b[H#');
- assert.deepEqual(getLines(term), ['# ¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
- term.writeSync('\x1b[1;6H######');
- assert.deepEqual(getLines(term), ['# ¥ #####', '# ¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
- term.writeSync('#');
- assert.deepEqual(getLines(term), ['# ¥ #####', '##¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
- term.writeSync('#');
- assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
- term.writeSync('\x1b[3;9H#');
- assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥#', '¥¥¥¥¥', '']);
- term.writeSync('#');
- assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '¥¥¥¥¥', '']);
- term.writeSync('#');
- assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '# ¥¥¥¥', '']);
- term.writeSync('\x1b[4;10H#');
- assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '# ¥¥¥ #', '']);
- });
- it('EL', () => {
- term.writeSync('\x1b[1;6H\x1b[K#');
- assert.deepEqual(getLines(term), ['¥¥ #', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
- term.writeSync('\x1b[2;5H\x1b[1K');
- assert.deepEqual(getLines(term), ['¥¥ #', ' ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
- term.writeSync('\x1b[3;6H\x1b[1K');
- assert.deepEqual(getLines(term), ['¥¥ #', ' ¥¥', ' ¥¥', '¥¥¥¥¥', '']);
- });
- it('ICH', () => {
- term.writeSync('\x1b[1;6H\x1b[@');
- assert.deepEqual(getLines(term), ['¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
- term.writeSync('\x1b[2;4H\x1b[2@');
- assert.deepEqual(getLines(term), ['¥¥ ¥', '¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
- term.writeSync('\x1b[3;4H\x1b[3@');
- assert.deepEqual(getLines(term), ['¥¥ ¥', '¥ ¥¥', '¥ ¥', '¥¥¥¥¥', '']);
- term.writeSync('\x1b[4;4H\x1b[4@');
- assert.deepEqual(getLines(term), ['¥¥ ¥', '¥ ¥¥', '¥ ¥', '¥ ¥', '']);
- });
- it('DCH', () => {
- term.writeSync('\x1b[1;6H\x1b[P');
- assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
- term.writeSync('\x1b[2;6H\x1b[2P');
- assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
- term.writeSync('\x1b[3;6H\x1b[3P');
- assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥ ¥', '¥¥¥¥¥', '']);
- });
- it('ECH', () => {
- term.writeSync('\x1b[1;6H\x1b[X');
- assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
- term.writeSync('\x1b[2;6H\x1b[2X');
- assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
- term.writeSync('\x1b[3;6H\x1b[3X');
- assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥ ¥', '¥¥¥¥¥', '']);
- });
- });
- describe('DECSTR', () => {
- let term: TestTerminal;
- beforeEach(() => {
- term = new TestTerminal({cols: 10, rows: 5, scrollback: 1});
- term.writeSync('01234567890123');
- });
- it('should reset IRM', () => {
- term.writeSync('\x1b[4h');
- assert.equal(term.insertMode, true);
- term.writeSync('\x1b[!p');
- assert.equal(term.insertMode, false);
- });
- it('should reset cursor visibility', () => {
- term.writeSync('\x1b[?25l');
- assert.equal((term as any)._coreService.isCursorHidden, true);
- term.writeSync('\x1b[!p');
- assert.equal((term as any)._coreService.isCursorHidden, false);
- });
- it('should reset scroll margins', () => {
- term.writeSync('\x1b[2;4r');
- assert.equal((term as any)._bufferService.buffer.scrollTop, 1);
- assert.equal((term as any)._bufferService.buffer.scrollBottom, 3);
- term.writeSync('\x1b[!p');
- assert.equal((term as any)._bufferService.buffer.scrollTop, 0);
- assert.equal((term as any)._bufferService.buffer.scrollBottom, term.rows - 1);
- });
- it('should reset text attributes', () => {
- term.writeSync('\x1b[1;2;32;43m');
- assert.equal(!!term.curAttrData.isBold(), true);
- term.writeSync('\x1b[!p');
- assert.equal(!!term.curAttrData.isBold(), false);
- assert.equal(term.curAttrData.fg, 0);
- assert.equal(term.curAttrData.bg, 0);
- });
- it('should reset DECSC data', () => {
- term.writeSync('\x1b7');
- assert.equal((term as any)._bufferService.buffer.savedX, 4);
- assert.equal((term as any)._bufferService.buffer.savedY, 1);
- term.writeSync('\x1b[!p');
- assert.equal((term as any)._bufferService.buffer.savedX, 0);
- assert.equal((term as any)._bufferService.buffer.savedY, 0);
- });
- it('should reset DECOM', () => {
- term.writeSync('\x1b[?6h');
- assert.equal((term as any)._coreService.decPrivateModes.origin, true);
- term.writeSync('\x1b[!p');
- assert.equal((term as any)._coreService.decPrivateModes.origin, false);
- });
- });
-});
diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts
deleted file mode 100644
index cac0a43d..00000000
--- a/src/TestUtils.test.ts
+++ /dev/null
@@ -1,360 +0,0 @@
-/**
- * Copyright (c) 2017 The xterm.js authors. All rights reserved.
- * @license MIT
- */
-
-import { IRenderer, IRenderDimensions, CharacterJoinerHandler, IRequestRedrawEvent } from 'browser/renderer/Types';
-import { IInputHandlingTerminal, ICompositionHelper, ITerminal, IBrowser, ITerminalOptions } from './Types';
-import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types';
-import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset } from 'common/Types';
-import { Buffer } from 'common/buffer/Buffer';
-import * as Browser from 'common/Platform';
-import { IDisposable, IMarker, IEvent, ISelectionPosition, ILinkProvider } from 'xterm';
-import { Terminal } from './Terminal';
-import { AttributeData } from 'common/buffer/AttributeData';
-import { IColorManager, IColorSet, ILinkMatcherOptions, ILinkifier, IViewport, ILinkifier2 } from 'browser/Types';
-import { IOptionsService, IUnicodeService } from 'common/services/Services';
-import { EventEmitter } from 'common/EventEmitter';
-import { IParams, IFunctionIdentifier } from 'common/parser/Types';
-import { ISelectionService } from 'browser/services/Services';
-
-export class TestTerminal extends Terminal {
- public get curAttrData(): IAttributeData { return (this as any)._inputHandler._curAttrData; }
- public keyDown(ev: any): boolean { return this._keyDown(ev); }
- public keyPress(ev: any): boolean { return this._keyPress(ev); }
-}
-
-export class MockTerminal implements ITerminal {
- public onBlur: IEvent;
- public onFocus: IEvent;
- public onA11yChar: IEvent;
- public onA11yTab: IEvent;
- public onCursorMove: IEvent;
- public onLineFeed: IEvent;
- public onSelectionChange: IEvent;
- public onData: IEvent;
- public onBinary: IEvent;
- public onTitleChange: IEvent;
- public onScroll: IEvent;
- public onKey: IEvent<{ key: string, domEvent: KeyboardEvent }>;
- public onRender: IEvent<{ start: number, end: number }>;
- public onResize: IEvent<{ cols: number, rows: number }>;
- public markers: IMarker[];
- public optionsService: IOptionsService;
- public unicodeService: IUnicodeService;
- public addMarker(cursorYOffset: number): IMarker {
- throw new Error('Method not implemented.');
- }
- public selectLines(start: number, end: number): void {
- throw new Error('Method not implemented.');
- }
- public scrollToLine(line: number): void {
- throw new Error('Method not implemented.');
- }
- public static string: any;
- public setOption(key: any, value: any): void {
- throw new Error('Method not implemented.');
- }
- public blur(): void {
- throw new Error('Method not implemented.');
- }
- public focus(): void {
- throw new Error('Method not implemented.');
- }
- public resize(columns: number, rows: number): void {
- throw new Error('Method not implemented.');
- }
- public writeln(data: string): void {
- throw new Error('Method not implemented.');
- }
- public paste(data: string): void {
- throw new Error('Method not implemented.');
- }
- public open(parent: HTMLElement): void {
- throw new Error('Method not implemented.');
- }
- public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void {
- throw new Error('Method not implemented.');
- }
- public addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable {
- throw new Error('Method not implemented.');
- }
- public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable {
- throw new Error('Method not implemented.');
- }
- public addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable {
- throw new Error('Method not implemented.');
- }
- public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
- throw new Error('Method not implemented.');
- }
- public registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => boolean | void, options?: ILinkMatcherOptions): number {
- throw new Error('Method not implemented.');
- }
- public deregisterLinkMatcher(matcherId: number): void {
- throw new Error('Method not implemented.');
- }
- public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
- throw new Error('Method not implemented.');
- }
- public hasSelection(): boolean {
- throw new Error('Method not implemented.');
- }
- public getSelection(): string {
- throw new Error('Method not implemented.');
- }
- public getSelectionPosition(): ISelectionPosition | undefined {
- throw new Error('Method not implemented.');
- }
- public clearSelection(): void {
- throw new Error('Method not implemented.');
- }
- public select(column: number, row: number, length: number): void {
- throw new Error('Method not implemented.');
- }
- public selectAll(): void {
- throw new Error('Method not implemented.');
- }
- public dispose(): void {
- throw new Error('Method not implemented.');
- }
- public scrollPages(pageCount: number): void {
- throw new Error('Method not implemented.');
- }
- public scrollToTop(): void {
- throw new Error('Method not implemented.');
- }
- public scrollToBottom(): void {
- throw new Error('Method not implemented.');
- }
- public clear(): void {
- throw new Error('Method not implemented.');
- }
- public write(data: string): void {
- throw new Error('Method not implemented.');
- }
- public writeUtf8(data: Uint8Array): void {
- throw new Error('Method not implemented.');
- }
- public bracketedPasteMode: boolean;
- public renderer: IRenderer;
- public linkifier: ILinkifier;
- public linkifier2: ILinkifier2;
- public isFocused: boolean;
- public options: ITerminalOptions = {};
- public element: HTMLElement;
- public screenElement: HTMLElement;
- public rowContainer: HTMLElement;
- public selectionContainer: HTMLElement;
- public selectionService: ISelectionService;
- public textarea: HTMLTextAreaElement;
- public rows: number;
- public cols: number;
- public browser: IBrowser = Browser;
- public writeBuffer: string[];
- public children: HTMLElement[];
- public cursorHidden: boolean;
- public cursorState: number;
- public scrollback: number;
- public buffers: IBufferSet;
- public buffer: IBuffer;
- public viewport: IViewport;
- public applicationCursor: boolean;
- public handler(data: string): void {
- throw new Error('Method not implemented.');
- }
- public on(event: string, callback: (...args: any[]) => void): void {
- throw new Error('Method not implemented.');
- }
- public off(type: string, listener: XtermListener): void {
- throw new Error('Method not implemented.');
- }
- public addDisposableListener(type: string, handler: XtermListener): IDisposable {
- throw new Error('Method not implemented.');
- }
- public scrollLines(disp: number, suppressScrollEvent: boolean): void {
- throw new Error('Method not implemented.');
- }
- public scrollToRow(absoluteRow: number): number {
- throw new Error('Method not implemented.');
- }
- public cancel(ev: Event, force?: boolean): void {
- throw new Error('Method not implemented.');
- }
- public log(text: string): void {
- throw new Error('Method not implemented.');
- }
- public emit(event: string, data: any): void {
- throw new Error('Method not implemented.');
- }
- public reset(): void {
- throw new Error('Method not implemented.');
- }
- public showCursor(): void {
- throw new Error('Method not implemented.');
- }
- public refresh(start: number, end: number): void {
- throw new Error('Method not implemented.');
- }
- public registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; }
- public deregisterCharacterJoiner(joinerId: number): void { }
-}
-
-export class MockInputHandlingTerminal implements IInputHandlingTerminal {
- public onA11yCharEmitter: EventEmitter;
- public onA11yTabEmitter: EventEmitter;
- public insertMode: boolean;
- public bracketedPasteMode: boolean;
- public sendFocus: boolean;
- public buffers: IBufferSet;
- public buffer: IBuffer = new MockBuffer();
- public viewport: IViewport;
- public scroll(eraseAttr: IAttributeData, isWrapped?: boolean): void {
- throw new Error('Method not implemented.');
- }
- public is(term: string): boolean {
- throw new Error('Method not implemented.');
- }
- public resize(x: number, y: number): void {
- throw new Error('Method not implemented.');
- }
- public showCursor(): void {
- throw new Error('Method not implemented.');
- }
- public handler(data: string): void {
- throw new Error('Method not implemented.');
- }
- public handleTitle(title: string): void {
- throw new Error('Method not implemented.');
- }
-}
-
-export class MockBuffer implements IBuffer {
- public markers: IMarker[];
- public addMarker(y: number): IMarker {
- throw new Error('Method not implemented.');
- }
- public isCursorInViewport: boolean;
- public lines: ICircularList;
- public ydisp: number;
- public ybase: number;
- public hasScrollback: boolean;
- public y: number;
- public x: number;
- public tabs: any;
- public scrollBottom: number;
- public scrollTop: number;
- public savedY: number;
- public savedX: number;
- public savedCharset: ICharset | null;
- public savedCurAttrData = new AttributeData();
- public translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string {
- return Buffer.prototype.translateBufferLineToString.apply(this, arguments);
- }
- public getWrappedRangeForLine(y: number): { first: number, last: number } {
- return Buffer.prototype.getWrappedRangeForLine.apply(this, arguments);
- }
- public nextStop(x?: number): number {
- throw new Error('Method not implemented.');
- }
- public prevStop(x?: number): number {
- throw new Error('Method not implemented.');
- }
- public setLines(lines: ICircularList): void {
- this.lines = lines;
- }
- public getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine {
- return Buffer.prototype.getBlankLine.apply(this, arguments);
- }
- public stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[] {
- return Buffer.prototype.stringIndexToBufferIndex.apply(this, arguments);
- }
- public iterator(trimRight: boolean, startIndex?: number, endIndex?: number): IBufferStringIterator {
- return Buffer.prototype.iterator.apply(this, arguments);
- }
- public getNullCell(attr?: IAttributeData): ICellData {
- throw new Error('Method not implemented.');
- }
- public getWhitespaceCell(attr?: IAttributeData): ICellData {
- throw new Error('Method not implemented.');
- }
-}
-
-export class MockRenderer implements IRenderer {
- public onRequestRedraw: IEvent;
- public onCanvasResize: IEvent<{ width: number, height: number }>;
- public onRender: IEvent<{ start: number, end: number }>;
- public dispose(): void {
- throw new Error('Method not implemented.');
- }
- public colorManager: IColorManager;
- public on(type: string, listener: XtermListener): void {
- throw new Error('Method not implemented.');
- }
- public off(type: string, listener: XtermListener): void {
- throw new Error('Method not implemented.');
- }
- public emit(type: string, data?: any): void {
- throw new Error('Method not implemented.');
- }
- public addDisposableListener(type: string, handler: XtermListener): IDisposable {
- throw new Error('Method not implemented.');
- }
- public dimensions: IRenderDimensions;
- public setColors(colors: IColorSet): void {
- throw new Error('Method not implemented.');
- }
- public onResize(cols: number, rows: number): void { }
- public onCharSizeChanged(): void { }
- public onBlur(): void { }
- public onFocus(): void { }
- public onSelectionChanged(start: [number, number], end: [number, number]): void { }
- public onCursorMove(): void { }
- public onOptionsChanged(): void { }
- public onDevicePixelRatioChange(): void { }
- public clear(): void { }
- public renderRows(start: number, end: number): void { }
- public registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; }
- public deregisterCharacterJoiner(): boolean { return true; }
-}
-
-export class MockViewport implements IViewport {
- public dispose(): void {
- throw new Error('Method not implemented.');
- }
- public scrollBarWidth: number = 0;
- public onThemeChange(colors: IColorSet): void {
- throw new Error('Method not implemented.');
- }
- public onWheel(ev: WheelEvent): boolean {
- throw new Error('Method not implemented.');
- }
- public onTouchStart(ev: TouchEvent): void {
- throw new Error('Method not implemented.');
- }
- public onTouchMove(ev: TouchEvent): boolean {
- throw new Error('Method not implemented.');
- }
- public syncScrollArea(): void { }
- public getLinesScrolled(ev: WheelEvent): number {
- throw new Error('Method not implemented.');
- }
-}
-
-export class MockCompositionHelper implements ICompositionHelper {
- public compositionstart(): void {
- throw new Error('Method not implemented.');
- }
- public compositionupdate(ev: CompositionEvent): void {
- throw new Error('Method not implemented.');
- }
- public compositionend(): void {
- throw new Error('Method not implemented.');
- }
- public updateCompositionElements(dontRecurse?: boolean): void {
- throw new Error('Method not implemented.');
- }
- public keydown(ev: KeyboardEvent): boolean {
- return true;
- }
-}
diff --git a/src/Types.d.ts b/src/Types.d.ts
deleted file mode 100644
index cb0ba844..00000000
--- a/src/Types.d.ts
+++ /dev/null
@@ -1,236 +0,0 @@
-/**
- * Copyright (c) 2017 The xterm.js authors. All rights reserved.
- * @license MIT
- */
-
-import { ITerminalOptions as IPublicTerminalOptions, IDisposable, IMarker, ISelectionPosition, ILinkProvider } from 'xterm';
-import { ICharset, IAttributeData, CharData, CoreMouseEventType } from 'common/Types';
-import { IEvent, IEventEmitter } from 'common/EventEmitter';
-import { IColorSet, ILinkifier, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types';
-import { IOptionsService, IUnicodeService } from 'common/services/Services';
-import { IBuffer, IBufferSet } from 'common/buffer/Types';
-import { IParams, IFunctionIdentifier } from 'common/parser/Types';
-
-export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
-
-export type LineData = CharData[];
-
-/**
- * This interface encapsulates everything needed from the Terminal by the
- * InputHandler. This cleanly separates the large amount of methods needed by
- * InputHandler cleanly from the ITerminal interface.
- */
-export interface IInputHandlingTerminal {
- insertMode: boolean;
- bracketedPasteMode: boolean;
- sendFocus: boolean;
-
- buffers: IBufferSet;
- buffer: IBuffer;
- viewport: IViewport;
-
- onA11yCharEmitter: IEventEmitter;
- onA11yTabEmitter: IEventEmitter;
-
- scroll(eraseAttr: IAttributeData, isWrapped?: boolean): void;
- is(term: string): boolean;
- resize(x: number, y: number): void;
- showCursor(): void;
- handleTitle(title: string): void;
-}
-
-export interface ICompositionHelper {
- compositionstart(): void;
- compositionupdate(ev: CompositionEvent): void;
- compositionend(): void;
- updateCompositionElements(dontRecurse?: boolean): void;
- keydown(ev: KeyboardEvent): boolean;
-}
-
-/**
- * Calls the parser and handles actions generated by the parser.
- */
-export interface IInputHandler {
- parse(data: string | Uint8Array): void;
- print(data: Uint32Array, start: number, end: number): void;
-
- /** C0 BEL */ bell(): void;
- /** C0 LF */ lineFeed(): void;
- /** C0 CR */ carriageReturn(): void;
- /** C0 BS */ backspace(): void;
- /** C0 HT */ tab(): void;
- /** C0 SO */ shiftOut(): void;
- /** C0 SI */ shiftIn(): void;
-
- /** CSI @ */ insertChars(params: IParams): void;
- /** CSI SP @ */ scrollLeft(params: IParams): void;
- /** CSI A */ cursorUp(params: IParams): void;
- /** CSI SP A */ scrollRight(params: IParams): void;
- /** CSI B */ cursorDown(params: IParams): void;
- /** CSI C */ cursorForward(params: IParams): void;
- /** CSI D */ cursorBackward(params: IParams): void;
- /** CSI E */ cursorNextLine(params: IParams): void;
- /** CSI F */ cursorPrecedingLine(params: IParams): void;
- /** CSI G */ cursorCharAbsolute(params: IParams): void;
- /** CSI H */ cursorPosition(params: IParams): void;
- /** CSI I */ cursorForwardTab(params: IParams): void;
- /** CSI J */ eraseInDisplay(params: IParams): void;
- /** CSI K */ eraseInLine(params: IParams): void;
- /** CSI L */ insertLines(params: IParams): void;
- /** CSI M */ deleteLines(params: IParams): void;
- /** CSI P */ deleteChars(params: IParams): void;
- /** CSI S */ scrollUp(params: IParams): void;
- /** CSI T */ scrollDown(params: IParams, collect?: string): void;
- /** CSI X */ eraseChars(params: IParams): void;
- /** CSI Z */ cursorBackwardTab(params: IParams): void;
- /** CSI ` */ charPosAbsolute(params: IParams): void;
- /** CSI a */ hPositionRelative(params: IParams): void;
- /** CSI b */ repeatPrecedingCharacter(params: IParams): void;
- /** CSI c */ sendDeviceAttributesPrimary(params: IParams): void;
- /** CSI > c */ sendDeviceAttributesSecondary(params: IParams): void;
- /** CSI d */ linePosAbsolute(params: IParams): void;
- /** CSI e */ vPositionRelative(params: IParams): void;
- /** CSI f */ hVPosition(params: IParams): void;
- /** CSI g */ tabClear(params: IParams): void;
- /** CSI h */ setMode(params: IParams, collect?: string): void;
- /** CSI l */ resetMode(params: IParams, collect?: string): void;
- /** CSI m */ charAttributes(params: IParams): void;
- /** CSI n */ deviceStatus(params: IParams, collect?: string): void;
- /** CSI p */ softReset(params: IParams, collect?: string): void;
- /** CSI q */ setCursorStyle(params: IParams, collect?: string): void;
- /** CSI r */ setScrollRegion(params: IParams, collect?: string): void;
- /** CSI s */ saveCursor(params: IParams): void;
- /** CSI u */ restoreCursor(params: IParams): void;
- /** CSI ' } */ insertColumns(params: IParams): void;
- /** CSI ' ~ */ deleteColumns(params: IParams): void;
- /** OSC 0
- OSC 2 */ setTitle(data: string): void;
- /** ESC E */ nextLine(): void;
- /** ESC = */ keypadApplicationMode(): void;
- /** ESC > */ keypadNumericMode(): void;
- /** ESC % G
- ESC % @ */ selectDefaultCharset(): void;
- /** ESC ( C
- ESC ) C
- ESC * C
- ESC + C
- ESC - C
- ESC . C
- ESC / C */ selectCharset(collectAndFlag: string): void;
- /** ESC D */ index(): void;
- /** ESC H */ tabSet(): void;
- /** ESC M */ reverseIndex(): void;
- /** ESC c */ fullReset(): void;
- /** ESC n
- ESC o
- ESC |
- ESC }
- ESC ~ */ setgLevel(level: number): void;
- /** ESC # 8 */ screenAlignmentPattern(): void;
-}
-
-export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor {
- screenElement: HTMLElement;
- browser: IBrowser;
- buffer: IBuffer;
- buffers: IBufferSet;
- viewport: IViewport;
- bracketedPasteMode: boolean;
- optionsService: IOptionsService;
- // TODO: We should remove options once components adopt optionsService
- options: ITerminalOptions;
- unicodeService: IUnicodeService;
-
- onBlur: IEvent;
- onFocus: IEvent;
- onA11yChar: IEvent;
- onA11yTab: IEvent;
-
- scrollLines(disp: number, suppressScrollEvent?: boolean): void;
- cancel(ev: Event, force?: boolean): boolean | void;
- showCursor(): void;
-}
-
-// Portions of the public API that are required by the internal Terminal
-export interface IPublicTerminal extends IDisposable {
- textarea: HTMLTextAreaElement | undefined;
- rows: number;
- cols: number;
- buffer: IBuffer;
- markers: IMarker[];
- onCursorMove: IEvent;
- onData: IEvent;
- onBinary: IEvent;
- onKey: IEvent<{ key: string, domEvent: KeyboardEvent }>;
- onLineFeed: IEvent;
- onScroll: IEvent;
- onSelectionChange: IEvent;
- onRender: IEvent<{ start: number, end: number }>;
- onResize: IEvent<{ cols: number, rows: number }>;
- onTitleChange: IEvent;
- blur(): void;
- focus(): void;
- resize(columns: number, rows: number): void;
- open(parent: HTMLElement): void;
- attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void;
- addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable;
- addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable;
- addEscHandler(id: IFunctionIdentifier, callback: () => boolean): IDisposable;
- addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable;
- registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number;
- deregisterLinkMatcher(matcherId: number): void;
- registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
- registerCharacterJoiner(handler: (text: string) => [number, number][]): number;
- deregisterCharacterJoiner(joinerId: number): void;
- addMarker(cursorYOffset: number): IMarker;
- hasSelection(): boolean;
- getSelection(): string;
- getSelectionPosition(): ISelectionPosition | undefined;
- clearSelection(): void;
- select(column: number, row: number, length: number): void;
- selectAll(): void;
- selectLines(start: number, end: number): void;
- dispose(): void;
- scrollLines(amount: number): void;
- scrollPages(pageCount: number): void;
- scrollToTop(): void;
- scrollToBottom(): void;
- scrollToLine(line: number): void;
- clear(): void;
- write(data: string | Uint8Array, callback?: () => void): void;
- paste(data: string): void;
- refresh(start: number, end: number): void;
- reset(): void;
-}
-
-export interface IBufferAccessor {
- buffer: IBuffer;
-}
-
-export interface IElementAccessor {
- readonly element: HTMLElement | undefined;
-}
-
-export interface ILinkifierAccessor {
- linkifier: ILinkifier;
- linkifier2: ILinkifier2;
-}
-
-// TODO: The options that are not in the public API should be reviewed
-export interface ITerminalOptions extends IPublicTerminalOptions {
- [key: string]: any;
- cancelEvents?: boolean;
- convertEol?: boolean;
- termName?: string;
-}
-
-export interface IBrowser {
- isNode: boolean;
- userAgent: string;
- platform: string;
- isFirefox: boolean;
- isMac: boolean;
- isIpad: boolean;
- isIphone: boolean;
- isWindows: boolean;
-}
diff --git a/src/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts
similarity index 97%
rename from src/AccessibilityManager.ts
rename to src/browser/AccessibilityManager.ts
index 78391501..2a34d6a3 100644
--- a/src/AccessibilityManager.ts
+++ b/src/browser/AccessibilityManager.ts
@@ -3,8 +3,8 @@
* @license MIT
*/
-import * as Strings from './browser/LocalizableStrings';
-import { ITerminal } from './Types';
+import * as Strings from 'browser/LocalizableStrings';
+import { ITerminal } from 'browser/Types';
import { IBuffer } from 'common/buffer/Types';
import { isMac } from 'common/Platform';
import { RenderDebouncer } from 'browser/RenderDebouncer';
@@ -79,6 +79,9 @@ export class AccessibilityManager extends Disposable {
this._liveRegion.setAttribute('aria-live', 'assertive');
this._accessibilityTreeRoot.appendChild(this._liveRegion);
+ if (!this._terminal.element) {
+ throw new Error('Cannot enable accessibility before Terminal.open');
+ }
this._terminal.element.insertAdjacentElement('afterbegin', this._accessibilityTreeRoot);
this.register(this._renderRowsDebouncer);
@@ -103,7 +106,7 @@ export class AccessibilityManager extends Disposable {
public dispose(): void {
super.dispose();
- this._terminal.element.removeChild(this._accessibilityTreeRoot);
+ this._terminal.element?.removeChild(this._accessibilityTreeRoot);
this._rowElements.length = 0;
}
diff --git a/src/browser/Clipboard.ts b/src/browser/Clipboard.ts
index a7c48bfb..5fe1e8fc 100644
--- a/src/browser/Clipboard.ts
+++ b/src/browser/Clipboard.ts
@@ -42,17 +42,17 @@ export function copyHandler(ev: ClipboardEvent, selectionService: ISelectionServ
* @param ev The original paste event to be handled
* @param term The terminal on which to apply the handled paste event
*/
-export function handlePasteEvent(ev: ClipboardEvent, textarea: HTMLTextAreaElement, bracketedPasteMode: boolean, coreService: ICoreService): void {
+export function handlePasteEvent(ev: ClipboardEvent, textarea: HTMLTextAreaElement, coreService: ICoreService): void {
ev.stopPropagation();
if (ev.clipboardData) {
const text = ev.clipboardData.getData('text/plain');
- paste(text, textarea, bracketedPasteMode, coreService);
+ paste(text, textarea, coreService);
}
}
-export function paste(text: string, textarea: HTMLTextAreaElement, bracketedPasteMode: boolean, coreService: ICoreService): void {
+export function paste(text: string, textarea: HTMLTextAreaElement, coreService: ICoreService): void {
text = prepareTextForTerminal(text);
- text = bracketTextForPaste(text, bracketedPasteMode);
+ text = bracketTextForPaste(text, coreService.decPrivateModes.bracketedPasteMode);
coreService.triggerDataEvent(text, true);
textarea.value = '';
}
diff --git a/src/browser/Linkifier2.ts b/src/browser/Linkifier2.ts
index 54812169..458b1bff 100644
--- a/src/browser/Linkifier2.ts
+++ b/src/browser/Linkifier2.ts
@@ -3,7 +3,7 @@
* @license MIT
*/
-import { ILinkifier2, ILinkProvider, IBufferCellPosition, ILink, ILinkifierEvent, ILinkDecorations } from './Types';
+import { ILinkifier2, ILinkProvider, IBufferCellPosition, ILink, ILinkifierEvent, ILinkDecorations } from 'browser/Types';
import { IDisposable } from 'common/Types';
import { IMouseService, IRenderService } from './services/Services';
import { IBufferService } from 'common/services/Services';
diff --git a/src/browser/RenderDebouncer.ts b/src/browser/RenderDebouncer.ts
index 814afcef..2a06fdd6 100644
--- a/src/browser/RenderDebouncer.ts
+++ b/src/browser/RenderDebouncer.ts
@@ -26,7 +26,7 @@ export class RenderDebouncer implements IDisposable {
}
}
- public refresh(rowStart: number, rowEnd: number, rowCount: number): void {
+ public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void {
this._rowCount = rowCount;
// Get the min/max row start/end for the arg values
rowStart = rowStart !== undefined ? rowStart : 0;
diff --git a/src/Terminal.test.ts b/src/browser/Terminal.test.ts
similarity index 74%
rename from src/Terminal.test.ts
rename to src/browser/Terminal.test.ts
index 4d590699..729c0d0f 100644
--- a/src/Terminal.test.ts
+++ b/src/browser/Terminal.test.ts
@@ -4,7 +4,7 @@
*/
import { assert, expect } from 'chai';
-import { MockViewport, MockCompositionHelper, MockRenderer, TestTerminal } from './TestUtils.test';
+import { MockViewport, MockCompositionHelper, MockRenderer, TestTerminal } from 'browser/TestUtils.test';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { CellData } from 'common/buffer/CellData';
import { IBufferService, IUnicodeService } from 'common/services/Services';
@@ -121,7 +121,7 @@ describe('Terminal', () => {
assert.equal(e, 'title');
done();
});
- term.handleTitle('title');
+ term.write('\x1b]2;title\x07');
});
});
@@ -140,7 +140,6 @@ describe('Terminal', () => {
};
beforeEach(() => {
- term.showCursor = () => { };
term.clearSelection = () => { };
});
@@ -396,62 +395,62 @@ describe('Terminal', () => {
describe('scroll() function', () => {
describe('when scrollback > 0', () => {
it('should create a new line and scroll', () => {
- term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
- term.buffer.lines.get(INIT_ROWS - 1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
+ term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
+ term.buffer.lines.get(INIT_ROWS - 1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.y = INIT_ROWS - 1; // Move cursor to last line
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS + 1);
- assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'a');
- assert.equal(term.buffer.lines.get(INIT_ROWS - 1).loadCell(0, new CellData()).getChars(), 'b');
- assert.equal(term.buffer.lines.get(INIT_ROWS).loadCell(0, new CellData()).getChars(), '');
+ assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'a');
+ assert.equal(term.buffer.lines.get(INIT_ROWS - 1)!.loadCell(0, new CellData()).getChars(), 'b');
+ assert.equal(term.buffer.lines.get(INIT_ROWS)!.loadCell(0, new CellData()).getChars(), '');
});
it('should properly scroll inside a scroll region (scrollTop set)', () => {
- term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
- term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
- term.buffer.lines.get(2).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
+ term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
+ term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
+ term.buffer.lines.get(2)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.y = INIT_ROWS - 1; // Move cursor to last line
term.buffer.scrollTop = 1;
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS);
- assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'a');
- assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), 'c');
+ assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'a');
+ assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), 'c');
});
it('should properly scroll inside a scroll region (scrollBottom set)', () => {
- term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
- term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
- term.buffer.lines.get(2).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
- term.buffer.lines.get(3).setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
- term.buffer.lines.get(4).setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
+ term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
+ term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
+ term.buffer.lines.get(2)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
+ term.buffer.lines.get(3)!.setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
+ term.buffer.lines.get(4)!.setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
term.buffer.y = 3;
term.buffer.scrollBottom = 3;
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS + 1);
- assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'a', '\'a\' should be pushed to the scrollback');
- assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), 'b');
- assert.equal(term.buffer.lines.get(2).loadCell(0, new CellData()).getChars(), 'c');
- assert.equal(term.buffer.lines.get(3).loadCell(0, new CellData()).getChars(), 'd');
- assert.equal(term.buffer.lines.get(4).loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
- assert.equal(term.buffer.lines.get(5).loadCell(0, new CellData()).getChars(), 'e');
+ assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'a', '\'a\' should be pushed to the scrollback');
+ assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), 'b');
+ assert.equal(term.buffer.lines.get(2)!.loadCell(0, new CellData()).getChars(), 'c');
+ assert.equal(term.buffer.lines.get(3)!.loadCell(0, new CellData()).getChars(), 'd');
+ assert.equal(term.buffer.lines.get(4)!.loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
+ assert.equal(term.buffer.lines.get(5)!.loadCell(0, new CellData()).getChars(), 'e');
});
it('should properly scroll inside a scroll region (scrollTop and scrollBottom set)', () => {
- term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
- term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
- term.buffer.lines.get(2).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
- term.buffer.lines.get(3).setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
- term.buffer.lines.get(4).setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
+ term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
+ term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
+ term.buffer.lines.get(2)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
+ term.buffer.lines.get(3)!.setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
+ term.buffer.lines.get(4)!.setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
term.buffer.y = INIT_ROWS - 1; // Move cursor to last line
term.buffer.scrollTop = 1;
term.buffer.scrollBottom = 3;
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS);
- assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'a');
- assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), 'c', '\'b\' should be removed from the buffer');
- assert.equal(term.buffer.lines.get(2).loadCell(0, new CellData()).getChars(), 'd');
- assert.equal(term.buffer.lines.get(3).loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
- assert.equal(term.buffer.lines.get(4).loadCell(0, new CellData()).getChars(), 'e');
+ assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'a');
+ assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), 'c', '\'b\' should be removed from the buffer');
+ assert.equal(term.buffer.lines.get(2)!.loadCell(0, new CellData()).getChars(), 'd');
+ assert.equal(term.buffer.lines.get(3)!.loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
+ assert.equal(term.buffer.lines.get(4)!.loadCell(0, new CellData()).getChars(), 'e');
});
});
@@ -462,65 +461,65 @@ describe('Terminal', () => {
});
it('should create a new line and shift everything up', () => {
- term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
- term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
- term.buffer.lines.get(INIT_ROWS - 1).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
+ term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
+ term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
+ term.buffer.lines.get(INIT_ROWS - 1)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.y = INIT_ROWS - 1; // Move cursor to last line
assert.equal(term.buffer.lines.length, INIT_ROWS);
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS);
// 'a' gets pushed out of buffer
- assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'b');
- assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), '');
- assert.equal(term.buffer.lines.get(INIT_ROWS - 2).loadCell(0, new CellData()).getChars(), 'c');
- assert.equal(term.buffer.lines.get(INIT_ROWS - 1).loadCell(0, new CellData()).getChars(), '');
+ assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'b');
+ assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), '');
+ assert.equal(term.buffer.lines.get(INIT_ROWS - 2)!.loadCell(0, new CellData()).getChars(), 'c');
+ assert.equal(term.buffer.lines.get(INIT_ROWS - 1)!.loadCell(0, new CellData()).getChars(), '');
});
it('should properly scroll inside a scroll region (scrollTop set)', () => {
- term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
- term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
- term.buffer.lines.get(2).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
+ term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
+ term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
+ term.buffer.lines.get(2)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.y = INIT_ROWS - 1; // Move cursor to last line
term.buffer.scrollTop = 1;
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS);
- assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'a');
- assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), 'c');
+ assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'a');
+ assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), 'c');
});
it('should properly scroll inside a scroll region (scrollBottom set)', () => {
- term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
- term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
- term.buffer.lines.get(2).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
- term.buffer.lines.get(3).setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
- term.buffer.lines.get(4).setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
+ term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
+ term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
+ term.buffer.lines.get(2)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
+ term.buffer.lines.get(3)!.setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
+ term.buffer.lines.get(4)!.setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
term.buffer.y = 3;
term.buffer.scrollBottom = 3;
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS);
- assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'b');
- assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), 'c');
- assert.equal(term.buffer.lines.get(2).loadCell(0, new CellData()).getChars(), 'd');
- assert.equal(term.buffer.lines.get(3).loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
- assert.equal(term.buffer.lines.get(4).loadCell(0, new CellData()).getChars(), 'e');
+ assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'b');
+ assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), 'c');
+ assert.equal(term.buffer.lines.get(2)!.loadCell(0, new CellData()).getChars(), 'd');
+ assert.equal(term.buffer.lines.get(3)!.loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
+ assert.equal(term.buffer.lines.get(4)!.loadCell(0, new CellData()).getChars(), 'e');
});
it('should properly scroll inside a scroll region (scrollTop and scrollBottom set)', () => {
- term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
- term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
- term.buffer.lines.get(2).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
- term.buffer.lines.get(3).setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
- term.buffer.lines.get(4).setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
+ term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
+ term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
+ term.buffer.lines.get(2)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
+ term.buffer.lines.get(3)!.setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
+ term.buffer.lines.get(4)!.setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
term.buffer.y = INIT_ROWS - 1; // Move cursor to last line
term.buffer.scrollTop = 1;
term.buffer.scrollBottom = 3;
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS);
- assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'a');
- assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), 'c', '\'b\' should be removed from the buffer');
- assert.equal(term.buffer.lines.get(2).loadCell(0, new CellData()).getChars(), 'd');
- assert.equal(term.buffer.lines.get(3).loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
- assert.equal(term.buffer.lines.get(4).loadCell(0, new CellData()).getChars(), 'e');
+ assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'a');
+ assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), 'c', '\'b\' should be removed from the buffer');
+ assert.equal(term.buffer.lines.get(2)!.loadCell(0, new CellData()).getChars(), 'd');
+ assert.equal(term.buffer.lines.get(3)!.loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
+ assert.equal(term.buffer.lines.get(4)!.loadCell(0, new CellData()).getChars(), 'e');
});
});
});
@@ -531,7 +530,6 @@ describe('Terminal', () => {
let evKeyPress: any;
beforeEach(() => {
- term.showCursor = () => { };
term.clearSelection = () => { };
// term.compositionHelper = {
// isComposing: false,
@@ -724,11 +722,11 @@ describe('Terminal', () => {
const cell = new CellData();
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
term.writeSync(high + String.fromCharCode(i));
- const tchar = term.buffer.lines.get(0).loadCell(0, cell);
+ const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
expect(tchar.getChars().length).eql(2);
expect(tchar.getWidth()).eql(1);
- expect(term.buffer.lines.get(0).loadCell(1, cell).getChars()).eql('');
+ expect(term.buffer.lines.get(0)!.loadCell(1, cell).getChars()).eql('');
term.reset();
}
});
@@ -738,9 +736,9 @@ describe('Terminal', () => {
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
term.buffer.x = term.cols - 1;
term.writeSync(high + String.fromCharCode(i));
- expect(term.buffer.lines.get(0).loadCell(term.buffer.x - 1, cell).getChars()).eql(high + String.fromCharCode(i));
- expect(term.buffer.lines.get(0).loadCell(term.buffer.x - 1, cell).getChars().length).eql(2);
- expect(term.buffer.lines.get(1).loadCell(0, cell).getChars()).eql('');
+ expect(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars()).eql(high + String.fromCharCode(i));
+ expect(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars().length).eql(2);
+ expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars()).eql('');
term.reset();
}
});
@@ -751,10 +749,10 @@ describe('Terminal', () => {
term.buffer.x = term.cols - 1;
term.writeSync('a' + high + String.fromCharCode(i));
- expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars()).eql('a');
- expect(term.buffer.lines.get(1).loadCell(0, cell).getChars()).eql(high + String.fromCharCode(i));
- expect(term.buffer.lines.get(1).loadCell(0, cell).getChars().length).eql(2);
- expect(term.buffer.lines.get(1).loadCell(1, cell).getChars()).eql('');
+ expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars()).eql('a');
+ expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars()).eql(high + String.fromCharCode(i));
+ expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars().length).eql(2);
+ expect(term.buffer.lines.get(1)!.loadCell(1, cell).getChars()).eql('');
term.reset();
}
});
@@ -770,9 +768,9 @@ describe('Terminal', () => {
}
term.writeSync('a' + high + String.fromCharCode(i));
// auto wraparound mode should cut off the rest of the line
- expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars()).eql(high + String.fromCharCode(i));
- expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars().length).eql(2);
- expect(term.buffer.lines.get(1).loadCell(1, cell).getChars()).eql('');
+ expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars()).eql(high + String.fromCharCode(i));
+ expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars().length).eql(2);
+ expect(term.buffer.lines.get(1)!.loadCell(1, cell).getChars()).eql('');
term.reset();
}
});
@@ -782,11 +780,11 @@ describe('Terminal', () => {
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
term.writeSync(high);
term.writeSync(String.fromCharCode(i));
- const tchar = term.buffer.lines.get(0).loadCell(0, cell);
+ const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
expect(tchar.getChars().length).eql(2);
expect(tchar.getWidth()).eql(1);
- expect(term.buffer.lines.get(0).loadCell(1, cell).getChars()).eql('');
+ expect(term.buffer.lines.get(0)!.loadCell(1, cell).getChars()).eql('');
term.reset();
}
});
@@ -796,7 +794,7 @@ describe('Terminal', () => {
const cell = new CellData();
it('café', () => {
term.writeSync('cafe\u0301');
- term.buffer.lines.get(0).loadCell(3, cell);
+ term.buffer.lines.get(0)!.loadCell(3, cell);
expect(cell.getChars()).eql('e\u0301');
expect(cell.getChars().length).eql(2);
expect(cell.getWidth()).eql(1);
@@ -804,11 +802,11 @@ describe('Terminal', () => {
it('café - end of line', () => {
term.buffer.x = term.cols - 1 - 3;
term.writeSync('cafe\u0301');
- term.buffer.lines.get(0).loadCell(term.cols - 1, cell);
+ term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
expect(cell.getChars()).eql('e\u0301');
expect(cell.getChars().length).eql(2);
expect(cell.getWidth()).eql(1);
- term.buffer.lines.get(0).loadCell(1, cell);
+ term.buffer.lines.get(0)!.loadCell(1, cell);
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
expect(cell.getWidth()).eql(1);
@@ -816,12 +814,12 @@ describe('Terminal', () => {
it('multiple combined é', () => {
term.writeSync(Array(100).join('e\u0301'));
for (let i = 0; i < term.cols; ++i) {
- term.buffer.lines.get(0).loadCell(i, cell);
+ term.buffer.lines.get(0)!.loadCell(i, cell);
expect(cell.getChars()).eql('e\u0301');
expect(cell.getChars().length).eql(2);
expect(cell.getWidth()).eql(1);
}
- term.buffer.lines.get(1).loadCell(0, cell);
+ term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('e\u0301');
expect(cell.getChars().length).eql(2);
expect(cell.getWidth()).eql(1);
@@ -829,12 +827,12 @@ describe('Terminal', () => {
it('multiple surrogate with combined', () => {
term.writeSync(Array(100).join('\uD800\uDC00\u0301'));
for (let i = 0; i < term.cols; ++i) {
- term.buffer.lines.get(0).loadCell(i, cell);
+ term.buffer.lines.get(0)!.loadCell(i, cell);
expect(cell.getChars()).eql('\uD800\uDC00\u0301');
expect(cell.getChars().length).eql(3);
expect(cell.getWidth()).eql(1);
}
- term.buffer.lines.get(1).loadCell(0, cell);
+ term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('\uD800\uDC00\u0301');
expect(cell.getChars().length).eql(3);
expect(cell.getWidth()).eql(1);
@@ -857,7 +855,7 @@ describe('Terminal', () => {
it('line of ¥ even', () => {
term.writeSync(Array(50).join('¥'));
for (let i = 0; i < term.cols; ++i) {
- term.buffer.lines.get(0).loadCell(i, cell);
+ term.buffer.lines.get(0)!.loadCell(i, cell);
if (i % 2) {
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
@@ -868,7 +866,7 @@ describe('Terminal', () => {
expect(cell.getWidth()).eql(2);
}
}
- term.buffer.lines.get(1).loadCell(0, cell);
+ term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('¥');
expect(cell.getChars().length).eql(1);
expect(cell.getWidth()).eql(2);
@@ -877,7 +875,7 @@ describe('Terminal', () => {
term.buffer.x = 1;
term.writeSync(Array(50).join('¥'));
for (let i = 1; i < term.cols - 1; ++i) {
- term.buffer.lines.get(0).loadCell(i, cell);
+ term.buffer.lines.get(0)!.loadCell(i, cell);
if (!(i % 2)) {
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
@@ -888,11 +886,11 @@ describe('Terminal', () => {
expect(cell.getWidth()).eql(2);
}
}
- term.buffer.lines.get(0).loadCell(term.cols - 1, cell);
+ term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
expect(cell.getWidth()).eql(1);
- term.buffer.lines.get(1).loadCell(0, cell);
+ term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('¥');
expect(cell.getChars().length).eql(1);
expect(cell.getWidth()).eql(2);
@@ -901,7 +899,7 @@ describe('Terminal', () => {
term.buffer.x = 1;
term.writeSync(Array(50).join('¥\u0301'));
for (let i = 1; i < term.cols - 1; ++i) {
- term.buffer.lines.get(0).loadCell(i, cell);
+ term.buffer.lines.get(0)!.loadCell(i, cell);
if (!(i % 2)) {
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
@@ -912,11 +910,11 @@ describe('Terminal', () => {
expect(cell.getWidth()).eql(2);
}
}
- term.buffer.lines.get(0).loadCell(term.cols - 1, cell);
+ term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
expect(cell.getWidth()).eql(1);
- term.buffer.lines.get(1).loadCell(0, cell);
+ term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('¥\u0301');
expect(cell.getChars().length).eql(2);
expect(cell.getWidth()).eql(2);
@@ -924,7 +922,7 @@ describe('Terminal', () => {
it('line of ¥ with combining even', () => {
term.writeSync(Array(50).join('¥\u0301'));
for (let i = 0; i < term.cols; ++i) {
- term.buffer.lines.get(0).loadCell(i, cell);
+ term.buffer.lines.get(0)!.loadCell(i, cell);
if (i % 2) {
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
@@ -935,7 +933,7 @@ describe('Terminal', () => {
expect(cell.getWidth()).eql(2);
}
}
- term.buffer.lines.get(1).loadCell(0, cell);
+ term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('¥\u0301');
expect(cell.getChars().length).eql(2);
expect(cell.getWidth()).eql(2);
@@ -944,7 +942,7 @@ describe('Terminal', () => {
term.buffer.x = 1;
term.writeSync(Array(50).join('\ud843\ude6d\u0301'));
for (let i = 1; i < term.cols - 1; ++i) {
- term.buffer.lines.get(0).loadCell(i, cell);
+ term.buffer.lines.get(0)!.loadCell(i, cell);
if (!(i % 2)) {
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
@@ -955,11 +953,11 @@ describe('Terminal', () => {
expect(cell.getWidth()).eql(2);
}
}
- term.buffer.lines.get(0).loadCell(term.cols - 1, cell);
+ term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
expect(cell.getWidth()).eql(1);
- term.buffer.lines.get(1).loadCell(0, cell);
+ term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('\ud843\ude6d\u0301');
expect(cell.getChars().length).eql(3);
expect(cell.getWidth()).eql(2);
@@ -967,7 +965,7 @@ describe('Terminal', () => {
it('line of surrogate fullwidth with combining even', () => {
term.writeSync(Array(50).join('\ud843\ude6d\u0301'));
for (let i = 0; i < term.cols; ++i) {
- term.buffer.lines.get(0).loadCell(i, cell);
+ term.buffer.lines.get(0)!.loadCell(i, cell);
if (i % 2) {
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
@@ -978,7 +976,7 @@ describe('Terminal', () => {
expect(cell.getWidth()).eql(2);
}
}
- term.buffer.lines.get(1).loadCell(0, cell);
+ term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('\ud843\ude6d\u0301');
expect(cell.getChars().length).eql(3);
expect(cell.getWidth()).eql(2);
@@ -991,42 +989,42 @@ describe('Terminal', () => {
term.writeSync(Array(9).join('0123456789').slice(-80));
term.buffer.x = 10;
term.buffer.y = 0;
- term.insertMode = true;
+ term.write('\x1b[4h');
term.writeSync('abcde');
- expect(term.buffer.lines.get(0).length).eql(term.cols);
- expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('a');
- expect(term.buffer.lines.get(0).loadCell(14, cell).getChars()).eql('e');
- expect(term.buffer.lines.get(0).loadCell(15, cell).getChars()).eql('0');
- expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql('4');
+ expect(term.buffer.lines.get(0)!.length).eql(term.cols);
+ expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('a');
+ expect(term.buffer.lines.get(0)!.loadCell(14, cell).getChars()).eql('e');
+ expect(term.buffer.lines.get(0)!.loadCell(15, cell).getChars()).eql('0');
+ expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql('4');
});
it('fullwidth - insert', () => {
term.writeSync(Array(9).join('0123456789').slice(-80));
term.buffer.x = 10;
term.buffer.y = 0;
- term.insertMode = true;
+ term.write('\x1b[4h');
term.writeSync('¥¥¥');
- expect(term.buffer.lines.get(0).length).eql(term.cols);
- expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('¥');
- expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('');
- expect(term.buffer.lines.get(0).loadCell(14, cell).getChars()).eql('¥');
- expect(term.buffer.lines.get(0).loadCell(15, cell).getChars()).eql('');
- expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql('3');
+ expect(term.buffer.lines.get(0)!.length).eql(term.cols);
+ expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('¥');
+ expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('');
+ expect(term.buffer.lines.get(0)!.loadCell(14, cell).getChars()).eql('¥');
+ expect(term.buffer.lines.get(0)!.loadCell(15, cell).getChars()).eql('');
+ expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql('3');
});
it('fullwidth - right border', () => {
term.writeSync(Array(41).join('¥'));
term.buffer.x = 10;
term.buffer.y = 0;
- term.insertMode = true;
+ term.write('\x1b[4h');
term.writeSync('a');
- expect(term.buffer.lines.get(0).length).eql(term.cols);
- expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('a');
- expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('¥');
- expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql(''); // fullwidth char got replaced
+ expect(term.buffer.lines.get(0)!.length).eql(term.cols);
+ expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('a');
+ expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('¥');
+ expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql(''); // fullwidth char got replaced
term.writeSync('b');
- expect(term.buffer.lines.get(0).length).eql(term.cols);
- expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('b');
- expect(term.buffer.lines.get(0).loadCell(12, cell).getChars()).eql('¥');
- expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql(''); // empty cell after fullwidth
+ expect(term.buffer.lines.get(0)!.length).eql(term.cols);
+ expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('b');
+ expect(term.buffer.lines.get(0)!.loadCell(12, cell).getChars()).eql('¥');
+ expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql(''); // empty cell after fullwidth
});
});
@@ -1271,9 +1269,9 @@ describe('Terminal', () => {
terminal.writeSync(input);
const s = terminal.buffer.iterator(true).next().content;
assert.equal(input, s);
- const stringIndex = s.match(/😃/).index;
+ const stringIndex = s.match(/😃/)!.index!;
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, stringIndex);
- assert(terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars(), '😃');
+ assert(terminal.buffer.lines.get(bufferIndex[0])!.loadCell(bufferIndex[1], new CellData()).getChars(), '😃');
});
it('multiline fullwidth chars with offset 1 (currently tests for broken behavior)', () => {
@@ -1300,7 +1298,7 @@ describe('Terminal', () => {
assert.equal(input, s);
for (let i = 0; i < input.length; ++i) {
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
- assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
+ assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0])!.loadCell(bufferIndex[1], new CellData()).getChars());
}
});
@@ -1318,7 +1316,7 @@ describe('Terminal', () => {
: (i % 3 === 1)
? input.substr(i, 2)
: input.substr(i - 1, 2),
- terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
+ terminal.buffer.lines.get(bufferIndex[0])!.loadCell(bufferIndex[1], new CellData()).getChars());
}
});
@@ -1372,15 +1370,15 @@ describe('Terminal', () => {
const normalTerminal = new TestTerminal({rows: 5, cols: 10, windowsMode: false});
normalTerminal.writeSync(data.join(''));
- assert.equal(normalTerminal.buffer.lines.get(0).isWrapped, false);
- assert.equal(normalTerminal.buffer.lines.get(1).isWrapped, false);
- assert.equal(normalTerminal.buffer.lines.get(2).isWrapped, false);
+ assert.equal(normalTerminal.buffer.lines.get(0)!.isWrapped, false);
+ assert.equal(normalTerminal.buffer.lines.get(1)!.isWrapped, false);
+ assert.equal(normalTerminal.buffer.lines.get(2)!.isWrapped, false);
const windowsModeTerminal = new TestTerminal({rows: 5, cols: 10, windowsMode: true});
windowsModeTerminal.writeSync(data.join(''));
- assert.equal(windowsModeTerminal.buffer.lines.get(0).isWrapped, false);
- assert.equal(windowsModeTerminal.buffer.lines.get(1).isWrapped, true, 'This line should wrap in Windows mode as the previous line ends in a non-null character');
- assert.equal(windowsModeTerminal.buffer.lines.get(2).isWrapped, false);
+ assert.equal(windowsModeTerminal.buffer.lines.get(0)!.isWrapped, false);
+ assert.equal(windowsModeTerminal.buffer.lines.get(1)!.isWrapped, true, 'This line should wrap in Windows mode as the previous line ends in a non-null character');
+ assert.equal(windowsModeTerminal.buffer.lines.get(2)!.isWrapped, false);
});
it('should mark lines as wrapped when the line ends in a non-null character after a CUP', () => {
@@ -1392,15 +1390,99 @@ describe('Terminal', () => {
const normalTerminal = new TestTerminal({rows: 5, cols: 10, windowsMode: false});
normalTerminal.writeSync(data.join(''));
- assert.equal(normalTerminal.buffer.lines.get(0).isWrapped, false);
- assert.equal(normalTerminal.buffer.lines.get(1).isWrapped, false);
- assert.equal(normalTerminal.buffer.lines.get(2).isWrapped, false);
+ assert.equal(normalTerminal.buffer.lines.get(0)!.isWrapped, false);
+ assert.equal(normalTerminal.buffer.lines.get(1)!.isWrapped, false);
+ assert.equal(normalTerminal.buffer.lines.get(2)!.isWrapped, false);
const windowsModeTerminal = new TestTerminal({rows: 5, cols: 10, windowsMode: true});
windowsModeTerminal.writeSync(data.join(''));
- assert.equal(windowsModeTerminal.buffer.lines.get(0).isWrapped, false);
- assert.equal(windowsModeTerminal.buffer.lines.get(1).isWrapped, true, 'This line should wrap in Windows mode as the previous line ends in a non-null character');
- assert.equal(windowsModeTerminal.buffer.lines.get(2).isWrapped, false);
+ assert.equal(windowsModeTerminal.buffer.lines.get(0)!.isWrapped, false);
+ assert.equal(windowsModeTerminal.buffer.lines.get(1)!.isWrapped, true, 'This line should wrap in Windows mode as the previous line ends in a non-null character');
+ assert.equal(windowsModeTerminal.buffer.lines.get(2)!.isWrapped, false);
+ });
+ });
+ it('convertEol setting', function(): void {
+ // not converting
+ const termNotConverting = new TestTerminal({cols: 15, rows: 10});
+ termNotConverting.writeSync('Hello\nWorld');
+ expect(termNotConverting.buffer.lines.get(0)!.translateToString(false)).equals('Hello ');
+ expect(termNotConverting.buffer.lines.get(1)!.translateToString(false)).equals(' World ');
+ expect(termNotConverting.buffer.lines.get(0)!.translateToString(true)).equals('Hello');
+ expect(termNotConverting.buffer.lines.get(1)!.translateToString(true)).equals(' World');
+
+ // converting
+ const termConverting = new TestTerminal({cols: 15, rows: 10, convertEol: true});
+ termConverting.writeSync('Hello\nWorld');
+ expect(termConverting.buffer.lines.get(0)!.translateToString(false)).equals('Hello ');
+ expect(termConverting.buffer.lines.get(1)!.translateToString(false)).equals('World ');
+ expect(termConverting.buffer.lines.get(0)!.translateToString(true)).equals('Hello');
+ expect(termConverting.buffer.lines.get(1)!.translateToString(true)).equals('World');
+ });
+ describe('Terminal InputHandler integration', () => {
+ function getLines(term: TestTerminal, limit: number = term.rows): string[] {
+ const res: string[] = [];
+ for (let i = 0; i < limit; ++i) {
+ res.push(term.buffer.lines.get(i)!.translateToString(true));
+ }
+ return res;
+ }
+
+ // This suite cannot live in InputHandler unless Terminal.scroll moved into IBufferService
+ describe('SL/SR/DECIC/DECDC', () => {
+ let term: TestTerminal;
+ beforeEach(() => {
+ term = new TestTerminal({cols: 5, rows: 5, scrollback: 1});
+ });
+ it('SL (scrollLeft)', () => {
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[ @');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '2345', '2345', '2345', '2345', '2345']);
+ term.writeSync('\x1b[0 @');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '345', '345', '345', '345', '345']);
+ term.writeSync('\x1b[2 @');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '5', '5', '5', '5', '5']);
+ });
+ it('SR (scrollRight)', () => {
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[ A');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1234', ' 1234', ' 1234', ' 1234', ' 1234']);
+ term.writeSync('\x1b[0 A');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 123', ' 123', ' 123', ' 123', ' 123']);
+ term.writeSync('\x1b[2 A');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1', ' 1', ' 1', ' 1', ' 1']);
+ });
+ it('insertColumns (DECIC)', () => {
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[3;3H');
+ term.writeSync('\x1b[\'}');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
+ term.reset();
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[3;3H');
+ term.writeSync('\x1b[1\'}');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
+ term.reset();
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[3;3H');
+ term.writeSync('\x1b[2\'}');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 3', '12 3', '12 3', '12 3', '12 3']);
+ });
+ it('deleteColumns (DECDC)', () => {
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[3;3H');
+ term.writeSync('\x1b[\'~');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']);
+ term.reset();
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[3;3H');
+ term.writeSync('\x1b[1\'~');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']);
+ term.reset();
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[3;3H');
+ term.writeSync('\x1b[2\'~');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '125', '125', '125', '125', '125']);
+ });
});
});
});
diff --git a/src/Terminal.ts b/src/browser/Terminal.ts
similarity index 59%
rename from src/Terminal.ts
rename to src/browser/Terminal.ts
index e3a3801d..7f3ead61 100644
--- a/src/Terminal.ts
+++ b/src/browser/Terminal.ts
@@ -21,13 +21,13 @@
* http://linux.die.net/man/7/urxvt
*/
-import { IInputHandlingTerminal, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, CustomKeyEventHandler } from './Types';
+import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types';
import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types';
import { CompositionHelper } from 'browser/input/CompositionHelper';
import { Viewport } from 'browser/Viewport';
import { rightClickHandler, moveTextAreaUnderMouseCursor, handlePasteEvent, copyHandler, paste } from 'browser/Clipboard';
import { C0 } from 'common/data/EscapeSequences';
-import { InputHandler } from './InputHandler';
+import { WindowsOptionsReportType } from '../common/InputHandler';
import { Renderer } from 'browser/renderer/Renderer';
import { Linkifier } from 'browser/Linkifier';
import { SelectionService } from 'browser/services/SelectionService';
@@ -39,89 +39,50 @@ import { MouseZoneManager } from 'browser/MouseZoneManager';
import { AccessibilityManager } from './AccessibilityManager';
import { ITheme, IMarker, IDisposable, ISelectionPosition, ILinkProvider } from 'xterm';
import { DomRenderer } from 'browser/renderer/dom/DomRenderer';
-import { IKeyboardEvent, KeyboardResultType, IBufferLine, IAttributeData, CoreMouseEventType, CoreMouseButton, CoreMouseAction } from 'common/Types';
+import { IKeyboardEvent, KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions } from 'common/Types';
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
-import { EventEmitter, IEvent } from 'common/EventEmitter';
+import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
-import { updateWindowsModeWrappedState } from 'common/WindowsMode';
import { ColorManager } from 'browser/ColorManager';
import { RenderService } from 'browser/services/RenderService';
-import { IOptionsService, IBufferService, ICoreMouseService, ICoreService, ILogService, IDirtyRowService, IInstantiationService, ICharsetService, IUnicodeService } from 'common/services/Services';
-import { OptionsService } from 'common/services/OptionsService';
import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService } from 'browser/services/Services';
import { CharSizeService } from 'browser/services/CharSizeService';
-import { BufferService, MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService';
-import { Disposable } from 'common/Lifecycle';
-import { IBufferSet, IBuffer } from 'common/buffer/Types';
+import { IBuffer } from 'common/buffer/Types';
import { MouseService } from 'browser/services/MouseService';
-import { IParams, IFunctionIdentifier } from 'common/parser/Types';
-import { CoreService } from 'common/services/CoreService';
-import { LogService } from 'common/services/LogService';
-import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types';
-import { DirtyRowService } from 'common/services/DirtyRowService';
-import { InstantiationService } from 'common/services/InstantiationService';
-import { CoreMouseService } from 'common/services/CoreMouseService';
-import { WriteBuffer } from 'common/input/WriteBuffer';
import { Linkifier2 } from 'browser/Linkifier2';
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
-import { UnicodeService } from 'common/services/UnicodeService';
-import { CharsetService } from 'common/services/CharsetService';
+import { CoreTerminal } from 'common/CoreTerminal';
+import { ITerminalOptions as IInitializedTerminalOptions } from 'common/services/Services';
// Let it work inside Node.js for automated testing purposes.
-const document = (typeof window !== 'undefined') ? window.document : null;
+const document: Document = (typeof window !== 'undefined') ? window.document : null as any;
+export class Terminal extends CoreTerminal implements ITerminal {
+ public textarea: HTMLTextAreaElement | undefined;
+ public element: HTMLElement | undefined;
+ public screenElement: HTMLElement | undefined;
-export class Terminal extends Disposable implements ITerminal, IDisposable, IInputHandlingTerminal {
- public textarea: HTMLTextAreaElement;
- public element: HTMLElement;
- public screenElement: HTMLElement;
+ private _document: Document | undefined;
+ private _viewportScrollArea: HTMLElement | undefined;
+ private _viewportElement: HTMLElement | undefined;
+ private _helperContainer: HTMLElement | undefined;
+ private _compositionView: HTMLElement | undefined;
- private _document: Document;
- private _viewportScrollArea: HTMLElement;
- private _viewportElement: HTMLElement;
- private _helperContainer: HTMLElement;
- private _compositionView: HTMLElement;
-
- private _visualBellTimer: number;
+ // private _visualBellTimer: number;
public browser: IBrowser = Browser;
// TODO: We should remove options once components adopt optionsService
- public get options(): ITerminalOptions { return this.optionsService.options; }
+ public get options(): IInitializedTerminalOptions { return this.optionsService.options; }
- private _customKeyEventHandler: CustomKeyEventHandler;
-
- // common services
- private _bufferService: IBufferService;
- private _coreService: ICoreService;
- private _charsetService: ICharsetService;
- private _coreMouseService: ICoreMouseService;
- private _dirtyRowService: IDirtyRowService;
- private _instantiationService: IInstantiationService;
- private _logService: ILogService;
- public optionsService: IOptionsService;
- public unicodeService: IUnicodeService;
+ private _customKeyEventHandler: CustomKeyEventHandler | undefined;
// browser services
- private _charSizeService: ICharSizeService;
- private _mouseService: IMouseService;
- private _renderService: IRenderService;
- private _selectionService: ISelectionService;
- private _soundService: ISoundService;
-
- // modes
- public insertMode: boolean;
- public bracketedPasteMode: boolean;
-
- // mouse properties
- public mouseEvents: CoreMouseEventType = CoreMouseEventType.NONE;
- public sendFocus: boolean;
-
- // write buffer
- private _writeBuffer: WriteBuffer;
-
- // Store if user went browsing history in scrollback
- private _userScrolling: boolean;
+ private _charSizeService: ICharSizeService | undefined;
+ private _mouseService: IMouseService | undefined;
+ private _renderService: IRenderService | undefined;
+ private _selectionService: ISelectionService | undefined;
+ private _soundService: ISoundService | undefined;
/**
* Records whether the keydown event has already been handled and triggered a data event, if so
@@ -130,39 +91,21 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
*/
private _keyDownHandled: boolean = false;
- private _inputHandler: InputHandler;
public linkifier: ILinkifier;
public linkifier2: ILinkifier2;
- public viewport: IViewport;
- private _compositionHelper: ICompositionHelper;
- private _mouseZoneManager: IMouseZoneManager;
- private _accessibilityManager: AccessibilityManager;
- private _colorManager: ColorManager;
- private _theme: ITheme;
- private _windowsMode: IDisposable | undefined;
-
- // bufferline to clone/copy from for new blank lines
- private _blankLine: IBufferLine = null;
-
- public get cols(): number { return this._bufferService.cols; }
- public get rows(): number { return this._bufferService.rows; }
+ public viewport: IViewport | undefined;
+ private _compositionHelper: ICompositionHelper | undefined;
+ private _mouseZoneManager: IMouseZoneManager | undefined;
+ private _accessibilityManager: AccessibilityManager | undefined;
+ private _colorManager: ColorManager | undefined;
+ private _theme: ITheme | undefined;
private _onCursorMove = new EventEmitter();
public get onCursorMove(): IEvent { return this._onCursorMove.event; }
- private _onData = new EventEmitter();
- public get onData(): IEvent { return this._onData.event; }
- private _onBinary = new EventEmitter();
- public get onBinary(): IEvent { return this._onBinary.event; }
private _onKey = new EventEmitter<{ key: string, domEvent: KeyboardEvent }>();
public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._onKey.event; }
- private _onLineFeed = new EventEmitter();
- public get onLineFeed(): IEvent { return this._onLineFeed.event; }
private _onRender = new EventEmitter<{ start: number, end: number }>();
public get onRender(): IEvent<{ start: number, end: number }> { return this._onRender.event; }
- private _onResize = new EventEmitter<{ cols: number, rows: number }>();
- public get onResize(): IEvent<{ cols: number, rows: number }> { return this._onResize.event; }
- private _onScroll = new EventEmitter();
- public get onScroll(): IEvent { return this._onScroll.event; }
private _onSelectionChange = new EventEmitter();
public get onSelectionChange(): IEvent { return this._onSelectionChange.event; }
private _onTitleChange = new EventEmitter();
@@ -172,10 +115,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
public get onFocus(): IEvent { return this._onFocus.event; }
private _onBlur = new EventEmitter();
public get onBlur(): IEvent { return this._onBlur.event; }
- public onA11yCharEmitter = new EventEmitter();
- public get onA11yChar(): IEvent { return this.onA11yCharEmitter.event; }
- public onA11yTabEmitter = new EventEmitter();
- public get onA11yTab(): IEvent { return this.onA11yTabEmitter.event; }
+ private _onA11yCharEmitter = new EventEmitter();
+ public get onA11yChar(): IEvent { return this._onA11yCharEmitter.event; }
+ private _onA11yTabEmitter = new EventEmitter();
+ public get onA11yTab(): IEvent { return this._onA11yTabEmitter.event; }
/**
* Creates a new `Terminal` object.
@@ -192,33 +135,26 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
constructor(
options: ITerminalOptions = {}
) {
- super();
+ super(options);
- // Setup and initialize common services
- this._instantiationService = new InstantiationService();
- this.optionsService = new OptionsService(options);
- this._instantiationService.setService(IOptionsService, this.optionsService);
- this._bufferService = this._instantiationService.createInstance(BufferService);
- this._instantiationService.setService(IBufferService, this._bufferService);
- this._logService = this._instantiationService.createInstance(LogService);
- this._instantiationService.setService(ILogService, this._logService);
- this._coreService = this._instantiationService.createInstance(CoreService, () => this.scrollToBottom());
- this._instantiationService.setService(ICoreService, this._coreService);
- this._coreService.onData(e => this._onData.fire(e));
- this._coreService.onBinary(e => this._onBinary.fire(e));
- this._coreMouseService = this._instantiationService.createInstance(CoreMouseService);
- this._instantiationService.setService(ICoreMouseService, this._coreMouseService);
- this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService);
- this._instantiationService.setService(IDirtyRowService, this._dirtyRowService);
- this.unicodeService = this._instantiationService.createInstance(UnicodeService);
- this._instantiationService.setService(IUnicodeService, this.unicodeService);
- this._charsetService = this._instantiationService.createInstance(CharsetService);
- this._instantiationService.setService(ICharsetService, this._charsetService);
-
- this._setupOptionsListeners();
this._setup();
- this._writeBuffer = new WriteBuffer(data => this._inputHandler.parse(data));
+ this.linkifier = this._instantiationService.createInstance(Linkifier);
+ this.linkifier2 = this._instantiationService.createInstance(Linkifier2);
+
+ // Setup InputHandler listeners
+ this.register(this._inputHandler.onRequestBell(() => this.bell()));
+ this.register(this._inputHandler.onRequestRefreshRows((start, end) => this.refresh(start, end)));
+ this.register(this._inputHandler.onRequestReset(() => this.reset()));
+ this.register(this._inputHandler.onRequestScroll((eraseAttr, isWrapped) => this.scroll(eraseAttr, isWrapped || undefined)));
+ this.register(this._inputHandler.onRequestWindowsOptionsReport(type => this._reportWindowsOptions(type)));
+ this.register(forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove));
+ this.register(forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange));
+ this.register(forwardEvent(this._inputHandler.onA11yChar, this._onA11yCharEmitter));
+ this.register(forwardEvent(this._inputHandler.onA11yTab, this._onA11yTabEmitter));
+
+ // Setup listeners
+ this._bufferService.onResize(e => this._afterResize(e.cols, e.rows));
}
public dispose(): void {
@@ -226,62 +162,16 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
return;
}
super.dispose();
- this._windowsMode?.dispose();
- this._windowsMode = undefined;
this._renderService?.dispose();
- this._customKeyEventHandler = null;
+ this._customKeyEventHandler = undefined;
this.write = () => { };
this.element?.parentNode?.removeChild(this.element);
}
- private _setup(): void {
- this._customKeyEventHandler = null;
+ protected _setup(): void {
+ super._setup();
- // modes
- this.insertMode = false;
- this.bracketedPasteMode = false;
-
- this._userScrolling = false;
-
- if (this._inputHandler) {
- this._inputHandler.reset();
- } else {
- // Register input handler and refire/handle events
- this._inputHandler = new InputHandler(this, this._bufferService, this._charsetService, this._coreService, this._dirtyRowService, this._logService, this.optionsService, this._coreMouseService, this.unicodeService, this._instantiationService);
- this._inputHandler.onRequestBell(() => this.bell());
- this._inputHandler.onRequestRefreshRows((start, end) => this.refresh(start, end));
- this._inputHandler.onRequestReset(() => this.reset());
- this._inputHandler.onCursorMove(() => this._onCursorMove.fire());
- this._inputHandler.onLineFeed(() => this._onLineFeed.fire());
- this.register(this._inputHandler);
- }
-
- if (!this.linkifier) {
- this.linkifier = this._instantiationService.createInstance(Linkifier);
- }
- if (!this.linkifier2) {
- this.linkifier2 = this._instantiationService.createInstance(Linkifier2);
- }
-
- if (this.options.windowsMode) {
- this._enableWindowsMode();
- }
- }
-
- private _enableWindowsMode(): void {
- if (!this._windowsMode) {
- const disposables: IDisposable[] = [];
- disposables.push(this.onLineFeed(updateWindowsModeWrappedState.bind(null, this._bufferService)));
- disposables.push(this.addCsiHandler({ final: 'H' }, () => {
- updateWindowsModeWrappedState(this._bufferService);
- return false;
- }));
- this._windowsMode = {
- dispose: () => {
- disposables.forEach(d => d.dispose());
- }
- };
- }
+ this._customKeyEventHandler = undefined;
}
/**
@@ -291,10 +181,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
return this.buffers.active;
}
- public get buffers(): IBufferSet {
- return this._bufferService.buffers;
- }
-
/**
* Focus the terminal. Delegates focus handling to the terminal's DOM element.
*/
@@ -304,80 +190,71 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
}
- private _setupOptionsListeners(): void {
+ protected _updateOptions(key: string): void {
+ super._updateOptions(key);
+
// TODO: These listeners should be owned by individual components
- this.optionsService.onOptionChange(key => {
- switch (key) {
- case 'fontFamily':
- case 'fontSize':
- // When the font changes the size of the cells may change which requires a renderer clear
- this._renderService?.clear();
- this._charSizeService?.measure();
- break;
- case 'cursorBlink':
- case 'cursorStyle':
- // The DOM renderer needs a row refresh to update the cursor styles
- this.refresh(this.buffer.y, this.buffer.y);
- break;
- case 'drawBoldTextInBrightColors':
- case 'letterSpacing':
- case 'lineHeight':
- case 'fontWeight':
- case 'fontWeightBold':
- case 'minimumContrastRatio':
- // When the font changes the size of the cells may change which requires a renderer clear
- if (this._renderService) {
- this._renderService.clear();
- this._renderService.onResize(this.cols, this.rows);
- this.refresh(0, this.rows - 1);
+ switch (key) {
+ case 'fontFamily':
+ case 'fontSize':
+ // When the font changes the size of the cells may change which requires a renderer clear
+ this._renderService?.clear();
+ this._charSizeService?.measure();
+ break;
+ case 'cursorBlink':
+ case 'cursorStyle':
+ // The DOM renderer needs a row refresh to update the cursor styles
+ this.refresh(this.buffer.y, this.buffer.y);
+ break;
+ case 'drawBoldTextInBrightColors':
+ case 'letterSpacing':
+ case 'lineHeight':
+ case 'fontWeight':
+ case 'fontWeightBold':
+ case 'minimumContrastRatio':
+ // When the font changes the size of the cells may change which requires a renderer clear
+ if (this._renderService) {
+ this._renderService.clear();
+ this._renderService.onResize(this.cols, this.rows);
+ this.refresh(0, this.rows - 1);
+ }
+ break;
+ case 'rendererType':
+ if (this._renderService) {
+ this._renderService.setRenderer(this._createRenderer());
+ this._renderService.onResize(this.cols, this.rows);
+ }
+ break;
+ case 'scrollback':
+ this.viewport?.syncScrollArea();
+ break;
+ case 'screenReaderMode':
+ if (this.optionsService.options.screenReaderMode) {
+ if (!this._accessibilityManager && this._renderService) {
+ this._accessibilityManager = new AccessibilityManager(this, this._renderService);
}
- break;
- case 'rendererType':
- if (this._renderService) {
- this._renderService.setRenderer(this._createRenderer());
- this._renderService.onResize(this.cols, this.rows);
- }
- break;
- case 'scrollback':
- this.buffers.resize(this.cols, this.rows);
- this.viewport?.syncScrollArea();
- break;
- case 'screenReaderMode':
- if (this.optionsService.options.screenReaderMode) {
- if (!this._accessibilityManager && this._renderService) {
- this._accessibilityManager = new AccessibilityManager(this, this._renderService);
- }
- } else {
- this._accessibilityManager?.dispose();
- this._accessibilityManager = null;
- }
- break;
- case 'tabStopWidth': this.buffers.setupTabStops(); break;
- case 'theme':
- this._setTheme(this.optionsService.options.theme);
- break;
- case 'windowsMode':
- if (this.optionsService.options.windowsMode) {
- this._enableWindowsMode();
- } else {
- this._windowsMode?.dispose();
- this._windowsMode = undefined;
- }
- break;
- }
- });
+ } else {
+ this._accessibilityManager?.dispose();
+ this._accessibilityManager = undefined;
+ }
+ break;
+ case 'tabStopWidth': this.buffers.setupTabStops(); break;
+ case 'theme':
+ this._setTheme(this.optionsService.options.theme);
+ break;
+ }
}
/**
* Binds the desired focus behavior on a given terminal object.
*/
private _onTextAreaFocus(ev: KeyboardEvent): void {
- if (this.sendFocus) {
+ if (this._coreService.decPrivateModes.sendFocus) {
this._coreService.triggerDataEvent(C0.ESC + '[I');
}
this.updateCursorStyle(ev);
- this.element.classList.add('focus');
- this.showCursor();
+ this.element!.classList.add('focus');
+ this._showCursor();
this._onFocus.fire();
}
@@ -386,7 +263,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
* textarea.
*/
public blur(): void {
- return this.textarea.blur();
+ return this.textarea?.blur();
}
/**
@@ -395,12 +272,12 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
private _onTextAreaBlur(): void {
// Text can safely be removed on blur. Doing it earlier could interfere with
// screen readers reading it out.
- this.textarea.value = '';
+ this.textarea!.value = '';
this.refresh(this.buffer.y, this.buffer.y);
- if (this.sendFocus) {
+ if (this._coreService.decPrivateModes.sendFocus) {
this._coreService.triggerDataEvent(C0.ESC + '[O');
}
- this.element.classList.remove('focus');
+ this.element!.classList.remove('focus');
this._onBlur.fire();
}
@@ -411,29 +288,29 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._bindKeys();
// Bind clipboard functionality
- this.register(addDisposableDomListener(this.element, 'copy', (event: ClipboardEvent) => {
+ this.register(addDisposableDomListener(this.element!, 'copy', (event: ClipboardEvent) => {
// If mouse events are active it means the selection manager is disabled and
// copy should be handled by the host program.
if (!this.hasSelection()) {
return;
}
- copyHandler(event, this._selectionService);
+ copyHandler(event, this._selectionService!);
}));
- const pasteHandlerWrapper = (event: ClipboardEvent): void => handlePasteEvent(event, this.textarea, this.bracketedPasteMode, this._coreService);
- this.register(addDisposableDomListener(this.textarea, 'paste', pasteHandlerWrapper));
- this.register(addDisposableDomListener(this.element, 'paste', pasteHandlerWrapper));
+ const pasteHandlerWrapper = (event: ClipboardEvent): void => handlePasteEvent(event, this.textarea!, this._coreService);
+ this.register(addDisposableDomListener(this.textarea!, 'paste', pasteHandlerWrapper));
+ this.register(addDisposableDomListener(this.element!, 'paste', pasteHandlerWrapper));
// Handle right click context menus
if (Browser.isFirefox) {
// Firefox doesn't appear to fire the contextmenu event on right click
- this.register(addDisposableDomListener(this.element, 'mousedown', (event: MouseEvent) => {
+ this.register(addDisposableDomListener(this.element!, 'mousedown', (event: MouseEvent) => {
if (event.button === 2) {
- rightClickHandler(event, this.textarea, this.screenElement, this._selectionService, this.options.rightClickSelectsWord);
+ rightClickHandler(event, this.textarea!, this.screenElement!, this._selectionService!, this.options.rightClickSelectsWord);
}
}));
} else {
- this.register(addDisposableDomListener(this.element, 'contextmenu', (event: MouseEvent) => {
- rightClickHandler(event, this.textarea, this.screenElement, this._selectionService, this.options.rightClickSelectsWord);
+ this.register(addDisposableDomListener(this.element!, 'contextmenu', (event: MouseEvent) => {
+ rightClickHandler(event, this.textarea!, this.screenElement!, this._selectionService!, this.options.rightClickSelectsWord);
}));
}
@@ -443,9 +320,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
if (Browser.isLinux) {
// Use auxclick event over mousedown the latter doesn't seem to work. Note
// that the regular click event doesn't fire for the middle mouse button.
- this.register(addDisposableDomListener(this.element, 'auxclick', (event: MouseEvent) => {
+ this.register(addDisposableDomListener(this.element!, 'auxclick', (event: MouseEvent) => {
if (event.button === 1) {
- moveTextAreaUnderMouseCursor(event, this.textarea, this.screenElement);
+ moveTextAreaUnderMouseCursor(event, this.textarea!, this.screenElement!);
}
}));
}
@@ -455,13 +332,13 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
* Apply key handling to the terminal
*/
private _bindKeys(): void {
- this.register(addDisposableDomListener(this.textarea, 'keyup', (ev: KeyboardEvent) => this._keyUp(ev), true));
- this.register(addDisposableDomListener(this.textarea, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true));
- this.register(addDisposableDomListener(this.textarea, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true));
- this.register(addDisposableDomListener(this.textarea, 'compositionstart', () => this._compositionHelper.compositionstart()));
- this.register(addDisposableDomListener(this.textarea, 'compositionupdate', (e: CompositionEvent) => this._compositionHelper.compositionupdate(e)));
- this.register(addDisposableDomListener(this.textarea, 'compositionend', () => this._compositionHelper.compositionend()));
- this.register(this.onRender(() => this._compositionHelper.updateCompositionElements()));
+ this.register(addDisposableDomListener(this.textarea!, 'keyup', (ev: KeyboardEvent) => this._keyUp(ev), true));
+ this.register(addDisposableDomListener(this.textarea!, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true));
+ this.register(addDisposableDomListener(this.textarea!, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true));
+ this.register(addDisposableDomListener(this.textarea!, 'compositionstart', () => this._compositionHelper!.compositionstart()));
+ this.register(addDisposableDomListener(this.textarea!, 'compositionupdate', (e: CompositionEvent) => this._compositionHelper!.compositionupdate(e)));
+ this.register(addDisposableDomListener(this.textarea!, 'compositionend', () => this._compositionHelper!.compositionend()));
+ this.register(this.onRender(() => this._compositionHelper!.updateCompositionElements()));
this.register(this.onRender(e => this._queueLinkification(e.start, e.end)));
}
@@ -479,7 +356,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._logService.debug('Terminal.open was called on an element that was not attached to the DOM');
}
- this._document = parent.ownerDocument;
+ this._document = parent.ownerDocument!;
// Create main element container
this.element = this._document.createElement('div');
@@ -535,16 +412,15 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this.element.appendChild(fragment);
this._theme = this.options.theme || this._theme;
- this.options.theme = undefined;
this._colorManager = new ColorManager(document, this.options.allowTransparency);
- this.optionsService.onOptionChange(e => this._colorManager.onOptionsChange(e));
+ this.optionsService.onOptionChange(e => this._colorManager!.onOptionsChange(e));
this._colorManager.setTheme(this._theme);
const renderer = this._createRenderer();
this._renderService = this._instantiationService.createInstance(RenderService, renderer, this.rows, this.screenElement);
this._instantiationService.setService(IRenderService, this._renderService);
this._renderService.onRenderedBufferChange(e => this._onRender.fire(e));
- this.onResize(e => this._renderService.resize(e.cols, e.rows));
+ this.onResize(e => this._renderService!.resize(e.cols, e.rows));
this._soundService = this._instantiationService.createInstance(SoundService);
this._instantiationService.setService(ISoundService, this._soundService);
@@ -557,13 +433,14 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._viewportScrollArea
);
this.viewport.onThemeChange(this._colorManager.colors);
+ this.register(this._inputHandler.onRequestSyncScrollBar(() => this.viewport!.syncScrollArea()));
this.register(this.viewport);
- this.register(this.onCursorMove(() => this._renderService.onCursorMove()));
- this.register(this.onResize(() => this._renderService.onResize(this.cols, this.rows)));
- this.register(this.onBlur(() => this._renderService.onBlur()));
- this.register(this.onFocus(() => this._renderService.onFocus()));
- this.register(this._renderService.onDimensionsChange(() => this.viewport.syncScrollArea()));
+ this.register(this.onCursorMove(() => this._renderService!.onCursorMove()));
+ this.register(this.onResize(() => this._renderService!.onResize(this.cols, this.rows)));
+ this.register(this.onBlur(() => this._renderService!.onBlur()));
+ this.register(this.onFocus(() => this._renderService!.onFocus()));
+ this.register(this._renderService.onDimensionsChange(() => this.viewport!.syncScrollArea()));
this._selectionService = this._instantiationService.createInstance(SelectionService,
(amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent),
@@ -571,32 +448,32 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this.screenElement);
this._instantiationService.setService(ISelectionService, this._selectionService);
this.register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire()));
- this.register(this._selectionService.onRedrawRequest(e => this._renderService.onSelectionChanged(e.start, e.end, e.columnSelectMode)));
+ this.register(this._selectionService.onRedrawRequest(e => this._renderService!.onSelectionChanged(e.start, e.end, e.columnSelectMode)));
this.register(this._selectionService.onLinuxMouseSelection(text => {
// If there's a new selection, put it into the textarea, focus and select it
// in order to register it as a selection on the OS. This event is fired
// only on Linux to enable middle click to paste selection.
- this.textarea.value = text;
- this.textarea.focus();
- this.textarea.select();
+ this.textarea!.value = text;
+ this.textarea!.focus();
+ this.textarea!.select();
}));
this.register(this.onScroll(() => {
- this.viewport.syncScrollArea();
- this._selectionService.refresh();
+ this.viewport!.syncScrollArea();
+ this._selectionService!.refresh();
}));
- this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this._selectionService.refresh()));
+ this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this._selectionService!.refresh()));
this._mouseZoneManager = this._instantiationService.createInstance(MouseZoneManager, this.element, this.screenElement);
this.register(this._mouseZoneManager);
- this.register(this.onScroll(() => this._mouseZoneManager.clearAll()));
+ this.register(this.onScroll(() => this._mouseZoneManager!.clearAll()));
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
this.linkifier2.attachToDom(this.element, this._mouseService, this._renderService);
// This event listener must be registered aftre MouseZoneManager is created
- this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService.onMouseDown(e)));
+ this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.onMouseDown(e)));
// apply mouse event classes set by escape codes before terminal was attached
- if (this.mouseEvents) {
+ if (this._coreMouseService.areMouseEventsActive) {
this._selectionService.disable();
this.element.classList.add('enable-mouse-events');
} else {
@@ -625,8 +502,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
private _createRenderer(): IRenderer {
switch (this.options.rendererType) {
- case 'canvas': return this._instantiationService.createInstance(Renderer, this._colorManager.colors, this.screenElement, this.linkifier, this.linkifier2);
- case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager.colors, this.element, this.screenElement, this._viewportElement, this.linkifier, this.linkifier2);
+ case 'canvas': return this._instantiationService.createInstance(Renderer, this._colorManager!.colors, this.screenElement!, this.linkifier, this.linkifier2);
+ case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager!.colors, this.element!, this.screenElement!, this._viewportElement!, this.linkifier, this.linkifier2);
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
}
}
@@ -638,8 +515,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
private _setTheme(theme: ITheme): void {
this._theme = theme;
this._colorManager?.setTheme(theme);
- this._renderService?.setColors(this._colorManager.colors);
- this.viewport?.onThemeChange(this._colorManager.colors);
+ this._renderService?.setColors(this._colorManager!.colors);
+ this.viewport?.onThemeChange(this._colorManager!.colors);
}
/**
@@ -659,18 +536,18 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
*/
public bindMouse(): void {
const self = this;
- const el = this.element;
+ const el = this.element!;
// send event to CoreMouseService
function sendEvent(ev: MouseEvent | WheelEvent): boolean {
// get mouse coordinates
- const pos = self._mouseService.getRawByteCoords(ev, self.screenElement, self.cols, self.rows);
+ const pos = self._mouseService!.getRawByteCoords(ev, self.screenElement!, self.cols, self.rows);
if (!pos) {
return false;
}
let but: CoreMouseButton;
- let action: CoreMouseAction;
+ let action: CoreMouseAction | undefined;
switch ((ev).overrideType || ev.type) {
case 'mousemove':
action = CoreMouseAction.MOVE;
@@ -739,14 +616,14 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
mousedrag: null,
mousemove: null
};
- const eventListeners: { [key: string]: (ev: Event) => void } = {
+ const eventListeners: { [key: string]: (ev: any) => void | boolean } = {
mouseup: (ev: MouseEvent) => {
sendEvent(ev);
if (!ev.buttons) {
// if no other button is held remove global handlers
- this._document.removeEventListener('mouseup', requestedEvents.mouseup);
+ this._document!.removeEventListener('mouseup', requestedEvents.mouseup!);
if (requestedEvents.mousedrag) {
- this._document.removeEventListener('mousemove', requestedEvents.mousedrag);
+ this._document!.removeEventListener('mousemove', requestedEvents.mousedrag);
}
}
return this.cancel(ev);
@@ -771,23 +648,22 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
};
this._coreMouseService.onProtocolChange(events => {
// apply global changes on events
- this.mouseEvents = events;
if (events) {
if (this.optionsService.options.logLevel === 'debug') {
this._logService.debug('Binding to mouse events:', this._coreMouseService.explainEvents(events));
}
- this.element.classList.add('enable-mouse-events');
- this._selectionService.disable();
+ this.element!.classList.add('enable-mouse-events');
+ this._selectionService!.disable();
} else {
this._logService.debug('Unbinding from mouse events.');
- this.element.classList.remove('enable-mouse-events');
- this._selectionService.enable();
+ this.element!.classList.remove('enable-mouse-events');
+ this._selectionService!.enable();
}
// add/remove handlers from requestedEvents
if (!(events & CoreMouseEventType.MOVE)) {
- el.removeEventListener('mousemove', requestedEvents.mousemove);
+ el.removeEventListener('mousemove', requestedEvents.mousemove!);
requestedEvents.mousemove = null;
} else if (!requestedEvents.mousemove) {
el.addEventListener('mousemove', eventListeners.mousemove);
@@ -795,7 +671,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
if (!(events & CoreMouseEventType.WHEEL)) {
- el.removeEventListener('wheel', requestedEvents.wheel);
+ el.removeEventListener('wheel', requestedEvents.wheel!);
requestedEvents.wheel = null;
} else if (!requestedEvents.wheel) {
el.addEventListener('wheel', eventListeners.wheel);
@@ -803,14 +679,14 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
if (!(events & CoreMouseEventType.UP)) {
- this._document.removeEventListener('mouseup', requestedEvents.mouseup);
+ this._document!.removeEventListener('mouseup', requestedEvents.mouseup!);
requestedEvents.mouseup = null;
} else if (!requestedEvents.mouseup) {
requestedEvents.mouseup = eventListeners.mouseup;
}
if (!(events & CoreMouseEventType.DRAG)) {
- this._document.removeEventListener('mousemove', requestedEvents.mousedrag);
+ this._document!.removeEventListener('mousemove', requestedEvents.mousedrag!);
requestedEvents.mousedrag = null;
} else if (!requestedEvents.mousedrag) {
requestedEvents.mousedrag = eventListeners.mousedrag;
@@ -829,7 +705,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// Don't send the mouse button to the pty if mouse events are disabled or
// if the selection manager is having selection forced (ie. a modifier is
// held).
- if (!this.mouseEvents || this._selectionService.shouldForceSelection(ev)) {
+ if (!this._coreMouseService.areMouseEventsActive || this._selectionService!.shouldForceSelection(ev)) {
return;
}
@@ -840,10 +716,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// Note: Other emulators also do this for 'mousedown' while a button
// is held, we currently limit 'mousedown' to the terminal only.
if (requestedEvents.mouseup) {
- this._document.addEventListener('mouseup', requestedEvents.mouseup);
+ this._document!.addEventListener('mouseup', requestedEvents.mouseup);
}
if (requestedEvents.mousedrag) {
- this._document.addEventListener('mousemove', requestedEvents.mousedrag);
+ this._document!.addEventListener('mousemove', requestedEvents.mousedrag);
}
return this.cancel(ev);
@@ -854,7 +730,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// Convert wheel events into up/down events when the buffer does not have scrollback, this
// enables scrolling in apps hosted in the alt buffer such as vim or tmux.
if (!this.buffer.hasScrollback) {
- const amount = this.viewport.getLinesScrolled(ev);
+ const amount = this.viewport!.getLinesScrolled(ev);
// Do nothing if there's no vertical scroll
if (amount === 0) {
@@ -877,20 +753,20 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// the shell for example
this.register(addDisposableDomListener(el, 'wheel', (ev: WheelEvent) => {
if (requestedEvents.wheel) return;
- if (!this.viewport.onWheel(ev)) {
+ if (!this.viewport!.onWheel(ev)) {
return this.cancel(ev);
}
}));
this.register(addDisposableDomListener(el, 'touchstart', (ev: TouchEvent) => {
- if (this.mouseEvents) return;
- this.viewport.onTouchStart(ev);
+ if (this._coreMouseService.areMouseEventsActive) return;
+ this.viewport!.onTouchStart(ev);
return this.cancel(ev);
}));
this.register(addDisposableDomListener(el, 'touchmove', (ev: TouchEvent) => {
- if (this.mouseEvents) return;
- if (!this.viewport.onTouchMove(ev)) {
+ if (this._coreMouseService.areMouseEventsActive) return;
+ if (!this.viewport!.onTouchMove(ev)) {
return this.cancel(ev);
}
}));
@@ -921,150 +797,29 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
*/
public updateCursorStyle(ev: KeyboardEvent): void {
if (this._selectionService && this._selectionService.shouldColumnSelect(ev)) {
- this.element.classList.add('column-select');
+ this.element!.classList.add('column-select');
} else {
- this.element.classList.remove('column-select');
+ this.element!.classList.remove('column-select');
}
}
/**
* Display the cursor element
*/
- public showCursor(): void {
+ private _showCursor(): void {
if (!this._coreService.isCursorInitialized) {
this._coreService.isCursorInitialized = true;
this.refresh(this.buffer.y, this.buffer.y);
}
}
- /**
- * Scroll the terminal down 1 row, creating a blank line.
- * @param isWrapped Whether the new line is wrapped from the previous line.
- */
- public scroll(eraseAttr: IAttributeData, isWrapped: boolean = false): void {
- let newLine: IBufferLine;
- newLine = this._blankLine;
- if (!newLine || newLine.length !== this.cols || newLine.getFg(0) !== eraseAttr.fg || newLine.getBg(0) !== eraseAttr.bg) {
- newLine = this.buffer.getBlankLine(eraseAttr, isWrapped);
- this._blankLine = newLine;
- }
- newLine.isWrapped = isWrapped;
-
- const topRow = this.buffer.ybase + this.buffer.scrollTop;
- const bottomRow = this.buffer.ybase + this.buffer.scrollBottom;
-
- if (this.buffer.scrollTop === 0) {
- // Determine whether the buffer is going to be trimmed after insertion.
- const willBufferBeTrimmed = this.buffer.lines.isFull;
-
- // Insert the line using the fastest method
- if (bottomRow === this.buffer.lines.length - 1) {
- if (willBufferBeTrimmed) {
- this.buffer.lines.recycle().copyFrom(newLine);
- } else {
- this.buffer.lines.push(newLine.clone());
- }
- } else {
- this.buffer.lines.splice(bottomRow + 1, 0, newLine.clone());
- }
-
- // Only adjust ybase and ydisp when the buffer is not trimmed
- if (!willBufferBeTrimmed) {
- this.buffer.ybase++;
- // Only scroll the ydisp with ybase if the user has not scrolled up
- if (!this._userScrolling) {
- this.buffer.ydisp++;
- }
- } else {
- // When the buffer is full and the user has scrolled up, keep the text
- // stable unless ydisp is right at the top
- if (this._userScrolling) {
- this.buffer.ydisp = Math.max(this.buffer.ydisp - 1, 0);
- }
- }
- } else {
- // scrollTop is non-zero which means no line will be going to the
- // scrollback, instead we can just shift them in-place.
- const scrollRegionHeight = bottomRow - topRow + 1 /* as it's zero-based */;
- this.buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1);
- this.buffer.lines.set(bottomRow, newLine.clone());
- }
-
- // Move the viewport to the bottom of the buffer unless the user is
- // scrolling.
- if (!this._userScrolling) {
- this.buffer.ydisp = this.buffer.ybase;
- }
-
- // Flag rows that need updating
- this._dirtyRowService.markRangeDirty(this.buffer.scrollTop, this.buffer.scrollBottom);
-
- this._onScroll.fire(this.buffer.ydisp);
- }
-
- /**
- * Scroll the display of the terminal
- * @param disp The number of lines to scroll down (negative scroll up).
- * @param suppressScrollEvent Don't emit the scroll event as scrollLines. This is used
- * to avoid unwanted events being handled by the viewport when the event was triggered from the
- * viewport originally.
- */
public scrollLines(disp: number, suppressScrollEvent?: boolean): void {
- if (disp < 0) {
- if (this.buffer.ydisp === 0) {
- return;
- }
- this._userScrolling = true;
- } else if (disp + this.buffer.ydisp >= this.buffer.ybase) {
- this._userScrolling = false;
- }
-
- const oldYdisp = this.buffer.ydisp;
- this.buffer.ydisp = Math.max(Math.min(this.buffer.ydisp + disp, this.buffer.ybase), 0);
-
- // No change occurred, don't trigger scroll/refresh
- if (oldYdisp === this.buffer.ydisp) {
- return;
- }
-
- if (!suppressScrollEvent) {
- this._onScroll.fire(this.buffer.ydisp);
- }
-
+ super.scrollLines(disp, suppressScrollEvent);
this.refresh(0, this.rows - 1);
}
- /**
- * Scroll the display of the terminal by a number of pages.
- * @param pageCount The number of pages to scroll (negative scrolls up).
- */
- public scrollPages(pageCount: number): void {
- this.scrollLines(pageCount * (this.rows - 1));
- }
-
- /**
- * Scrolls the display of the terminal to the top.
- */
- public scrollToTop(): void {
- this.scrollLines(-this.buffer.ydisp);
- }
-
- /**
- * Scrolls the display of the terminal to the bottom.
- */
- public scrollToBottom(): void {
- this.scrollLines(this.buffer.ybase - this.buffer.ydisp);
- }
-
- public scrollToLine(line: number): void {
- const scrollAmount = line - this.buffer.ydisp;
- if (scrollAmount !== 0) {
- this.scrollLines(scrollAmount);
- }
- }
-
public paste(data: string): void {
- paste(data, this.textarea, this.bracketedPasteMode, this._coreService);
+ paste(data, this.textarea!, this._coreService);
}
/**
@@ -1080,25 +835,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._customKeyEventHandler = customKeyEventHandler;
}
- /** Add handler for ESC escape sequence. See xterm.d.ts for details. */
- public addEscHandler(id: IFunctionIdentifier, callback: () => boolean): IDisposable {
- return this._inputHandler.addEscHandler(id, callback);
- }
-
- /** Add handler for DCS escape sequence. See xterm.d.ts for details. */
- public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable {
- return this._inputHandler.addDcsHandler(id, callback);
- }
-
- /** Add handler for CSI escape sequence. See xterm.d.ts for details. */
- public addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable {
- return this._inputHandler.addCsiHandler(id, callback);
- }
- /** Add handler for OSC escape sequence. See xterm.d.ts for details. */
- public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
- return this._inputHandler.addOscHandler(ident, callback);
- }
-
/**
* Registers a link matcher, allowing custom link patterns to be matched and
* handled.
@@ -1130,13 +866,13 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
public registerCharacterJoiner(handler: CharacterJoinerHandler): number {
- const joinerId = this._renderService.registerCharacterJoiner(handler);
+ const joinerId = this._renderService!.registerCharacterJoiner(handler);
this.refresh(0, this.rows - 1);
return joinerId;
}
public deregisterCharacterJoiner(joinerId: number): void {
- if (this._renderService.deregisterCharacterJoiner(joinerId)) {
+ if (this._renderService!.deregisterCharacterJoiner(joinerId)) {
this.refresh(0, this.rows - 1);
}
}
@@ -1145,7 +881,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
return this.buffer.markers;
}
- public addMarker(cursorYOffset: number): IMarker {
+ public addMarker(cursorYOffset: number): IMarker | undefined {
// Disallow markers on the alt buffer
if (this.buffer !== this.buffers.normal) {
return;
@@ -1168,7 +904,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
* @param length The length of the selection.
*/
public select(column: number, row: number, length: number): void {
- this._selectionService.setSelection(column, row, length);
+ this._selectionService!.setSelection(column, row, length);
}
/**
@@ -1180,15 +916,15 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
public getSelectionPosition(): ISelectionPosition | undefined {
- if (!this._selectionService.hasSelection) {
+ if (!this._selectionService || !this._selectionService.hasSelection) {
return undefined;
}
return {
- startColumn: this._selectionService.selectionStart[0],
- startRow: this._selectionService.selectionStart[1],
- endColumn: this._selectionService.selectionEnd[0],
- endRow: this._selectionService.selectionEnd[1]
+ startColumn: this._selectionService.selectionStart![0],
+ startRow: this._selectionService.selectionStart![1],
+ endColumn: this._selectionService.selectionEnd![0],
+ endRow: this._selectionService.selectionEnd![1]
};
}
@@ -1216,14 +952,14 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
* - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent
* @param ev The keydown event to be handled.
*/
- protected _keyDown(event: KeyboardEvent): boolean {
+ protected _keyDown(event: KeyboardEvent): boolean | undefined {
this._keyDownHandled = false;
if (this._customKeyEventHandler && this._customKeyEventHandler(event) === false) {
return false;
}
- if (!this._compositionHelper.keydown(event)) {
+ if (!this._compositionHelper!.keydown(event)) {
if (this.buffer.ybase !== this.buffer.ydisp) {
this.scrollToBottom();
}
@@ -1261,11 +997,11 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// will announce deleted characters. This will not work 100% of the time but it should cover
// most scenarios.
if (result.key === C0.ETX || result.key === C0.CR) {
- this.textarea.value = '';
+ this.textarea!.value = '';
}
this._onKey.fire({ key: result.key, domEvent: event });
- this.showCursor();
+ this._showCursor();
this._coreService.triggerDataEvent(result.key, true);
// Cancel events when not in screen reader mode so events don't get bubbled up and handled by
@@ -1342,7 +1078,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
key = String.fromCharCode(key);
this._onKey.fire({ key, domEvent: ev });
- this.showCursor();
+ this._showCursor();
this._coreService.triggerDataEvent(key, true);
return true;
@@ -1354,16 +1090,16 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
*/
public bell(): void {
if (this._soundBell()) {
- this._soundService.playBellSound();
+ this._soundService!.playBellSound();
}
- if (this._visualBell()) {
- this.element.classList.add('visual-bell-active');
- clearTimeout(this._visualBellTimer);
- this._visualBellTimer = window.setTimeout(() => {
- this.element.classList.remove('visual-bell-active');
- }, 200);
- }
+ // if (this._visualBell()) {
+ // this.element.classList.add('visual-bell-active');
+ // clearTimeout(this._visualBellTimer);
+ // this._visualBellTimer = window.setTimeout(() => {
+ // this.element.classList.remove('visual-bell-active');
+ // }, 200);
+ // }
}
/**
@@ -1373,10 +1109,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
* @param y The number of rows to resize to.
*/
public resize(x: number, y: number): void {
- if (isNaN(x) || isNaN(y)) {
- return;
- }
-
if (x === this.cols && y === this.rows) {
// Check if we still need to measure the char size (fixes #785).
if (this._charSizeService && !this._charSizeService.hasValidSize) {
@@ -1385,22 +1117,15 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
return;
}
- if (x < MINIMUM_COLS) x = MINIMUM_COLS;
- if (y < MINIMUM_ROWS) y = MINIMUM_ROWS;
-
- this.buffers.resize(x, y);
-
- this._bufferService.resize(x, y);
- this.buffers.setupTabStops(this.cols);
+ super.resize(x, y);
+ }
+ private _afterResize(x: number, y: number): void {
this._charSizeService?.measure();
// Sync the scroll area to make sure scroll events don't fire and scroll the viewport to an
// invalid location
this.viewport?.syncScrollArea(true);
-
- this.refresh(0, this.rows - 1);
- this._onResize.fire({ cols: x, rows: y });
}
/**
@@ -1411,7 +1136,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// Don't clear if it's already clear
return;
}
- this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y));
+ this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!);
this.buffer.lines.length = 1;
this.buffer.ydisp = 0;
this.buffer.ybase = 0;
@@ -1423,44 +1148,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._onScroll.fire(this.buffer.ydisp);
}
- /**
- * Evaluate if the current terminal is the given argument.
- * @param term The terminal name to evaluate
- */
- public is(term: string): boolean {
- return (this.options.termName + '').indexOf(term) === 0;
- }
-
- /**
- * Emit the data event and populate the given data.
- * @param data The data to populate in the event.
- */
- // public handler(data: string): void {
- // // Prevents all events to pty process if stdin is disabled
- // if (this.options.disableStdin) {
- // return;
- // }
-
- // // Clear the selection if the selection manager is available and has an active selection
- // if (this.selectionService && this.selectionService.hasSelection) {
- // this.selectionService.clearSelection();
- // }
-
- // // Input is being sent to the terminal, the terminal should focus the prompt.
- // if (this.buffer.ybase !== this.buffer.ydisp) {
- // this.scrollToBottom();
- // }
- // this._onData.fire(data);
- // }
-
- /**
- * Emit the 'title' event and populate the given title.
- * @param title The title to populate in the event.
- */
- public handleTitle(title: string): void {
- this._onTitleChange.fire(title);
- }
-
/**
* Reset terminal.
* Note: Calling this directly from JS is synchronous but does not clear
@@ -1477,26 +1164,40 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this.options.rows = this.rows;
this.options.cols = this.cols;
const customKeyEventHandler = this._customKeyEventHandler;
- const userScrolling = this._userScrolling;
this._setup();
- this._bufferService.reset();
- this._charsetService.reset();
- this._coreService.reset();
- this._coreMouseService.reset();
+ super.reset();
this._selectionService?.reset();
// reattach
this._customKeyEventHandler = customKeyEventHandler;
- this._userScrolling = userScrolling;
// do a full screen refresh
this.refresh(0, this.rows - 1);
this.viewport?.syncScrollArea();
}
+ private _reportWindowsOptions(type: WindowsOptionsReportType): void {
+ if (!this._renderService) {
+ return;
+ }
+
+ switch (type) {
+ case WindowsOptionsReportType.GET_WIN_SIZE_PIXELS:
+ const canvasWidth = this._renderService.dimensions.scaledCanvasWidth.toFixed(0);
+ const canvasHeight = this._renderService.dimensions.scaledCanvasHeight.toFixed(0);
+ this._coreService.triggerDataEvent(`${C0.ESC}[4;${canvasHeight};${canvasWidth}t`);
+ break;
+ case WindowsOptionsReportType.GET_CELL_SIZE_PIXELS:
+ const cellWidth = this._renderService.dimensions.scaledCellWidth.toFixed(0);
+ const cellHeight = this._renderService.dimensions.scaledCellHeight.toFixed(0);
+ this._coreService.triggerDataEvent(`${C0.ESC}[6;${cellHeight};${cellWidth}t`);
+ break;
+ }
+ }
+
// TODO: Remove cancel function and cancelEvents option
- public cancel(ev: Event, force?: boolean): boolean {
+ public cancel(ev: Event, force?: boolean): boolean | undefined {
if (!this.options.cancelEvents && !force) {
return;
}
@@ -1516,14 +1217,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// return this.options.bellStyle === 'sound' ||
// this.options.bellStyle === 'both';
}
-
- public write(data: string | Uint8Array, callback?: () => void): void {
- this._writeBuffer.write(data, callback);
- }
-
- public writeSync(data: string | Uint8Array): void {
- this._writeBuffer.writeSync(data);
- }
}
/**
diff --git a/src/Terminal2.test.ts b/src/browser/Terminal2.test.ts
similarity index 96%
rename from src/Terminal2.test.ts
rename to src/browser/Terminal2.test.ts
index f7046240..32a47b21 100644
--- a/src/Terminal2.test.ts
+++ b/src/browser/Terminal2.test.ts
@@ -8,14 +8,14 @@ import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs';
import * as pty from 'node-pty';
-import { Terminal } from './Terminal';
+import { Terminal } from 'browser/Terminal';
import { IDisposable } from 'xterm';
// all test files expect terminal in 80x25
const COLS = 80;
const ROWS = 25;
-const TESTFILES = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '..')});
+const TESTFILES = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '../..')});
const SKIP_FILES = [
't0084-CBT.in',
't0101-NLM.in',
@@ -124,7 +124,7 @@ function terminalToString(term: Terminal): string {
let result = '';
let lineText = '';
for (let line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) {
- lineText = term.buffer.lines.get(line).translateToString(true);
+ lineText = term.buffer.lines.get(line)!.translateToString(true);
// rtrim empty cells as xterm does
lineText = lineText.replace(/\s+$/, '');
result += lineText;
diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts
index 96d1eae0..2d6f6cbb 100644
--- a/src/browser/TestUtils.test.ts
+++ b/src/browser/TestUtils.test.ts
@@ -3,13 +3,331 @@
* @license MIT
*/
+import { IDisposable, IMarker, ISelectionPosition, ILinkProvider } from 'xterm';
import { IEvent, EventEmitter } from 'common/EventEmitter';
-import { ICharSizeService, IMouseService, IRenderService } from 'browser/services/Services';
-import { IRenderDimensions, IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types';
-import { IColorSet } from 'browser/Types';
+import { ICharSizeService, IMouseService, IRenderService, ISelectionService } from 'browser/services/Services';
+import { IRenderDimensions, IRenderer, CharacterJoinerHandler, IRequestRedrawEvent } from 'browser/renderer/Types';
+import { IColorSet, ILinkMatcherOptions, ITerminal, ILinkifier, ILinkifier2, IBrowser, IViewport, IColorManager, ICompositionHelper } from 'browser/Types';
+import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types';
+import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions } from 'common/Types';
+import { Buffer } from 'common/buffer/Buffer';
+import * as Browser from 'common/Platform';
+import { Terminal } from 'browser/Terminal';
+import { IUnicodeService, IOptionsService } from 'common/services/Services';
+import { IFunctionIdentifier, IParams } from 'common/parser/Types';
+import { AttributeData } from 'common/buffer/AttributeData';
+
+export class TestTerminal extends Terminal {
+ public get curAttrData(): IAttributeData { return (this as any)._inputHandler._curAttrData; }
+ public keyDown(ev: any): boolean | undefined { return this._keyDown(ev); }
+ public keyPress(ev: any): boolean { return this._keyPress(ev); }
+}
+
+export class MockTerminal implements ITerminal {
+ public onBlur!: IEvent;
+ public onFocus!: IEvent;
+ public onA11yChar!: IEvent;
+ public onA11yTab!: IEvent;
+ public onCursorMove!: IEvent;
+ public onLineFeed!: IEvent;
+ public onSelectionChange!: IEvent;
+ public onData!: IEvent;
+ public onBinary!: IEvent;
+ public onTitleChange!: IEvent;
+ public onScroll!: IEvent;
+ public onKey!: IEvent<{ key: string, domEvent: KeyboardEvent }>;
+ public onRender!: IEvent<{ start: number, end: number }>;
+ public onResize!: IEvent<{ cols: number, rows: number }>;
+ public markers!: IMarker[];
+ public optionsService!: IOptionsService;
+ public unicodeService!: IUnicodeService;
+ public addMarker(cursorYOffset: number): IMarker {
+ throw new Error('Method not implemented.');
+ }
+ public selectLines(start: number, end: number): void {
+ throw new Error('Method not implemented.');
+ }
+ public scrollToLine(line: number): void {
+ throw new Error('Method not implemented.');
+ }
+ public static string: any;
+ public setOption(key: any, value: any): void {
+ throw new Error('Method not implemented.');
+ }
+ public blur(): void {
+ throw new Error('Method not implemented.');
+ }
+ public focus(): void {
+ throw new Error('Method not implemented.');
+ }
+ public resize(columns: number, rows: number): void {
+ throw new Error('Method not implemented.');
+ }
+ public writeln(data: string): void {
+ throw new Error('Method not implemented.');
+ }
+ public paste(data: string): void {
+ throw new Error('Method not implemented.');
+ }
+ public open(parent: HTMLElement): void {
+ throw new Error('Method not implemented.');
+ }
+ public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void {
+ throw new Error('Method not implemented.');
+ }
+ public addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable {
+ throw new Error('Method not implemented.');
+ }
+ public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable {
+ throw new Error('Method not implemented.');
+ }
+ public addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable {
+ throw new Error('Method not implemented.');
+ }
+ public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
+ throw new Error('Method not implemented.');
+ }
+ public registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => boolean | void, options?: ILinkMatcherOptions): number {
+ throw new Error('Method not implemented.');
+ }
+ public deregisterLinkMatcher(matcherId: number): void {
+ throw new Error('Method not implemented.');
+ }
+ public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
+ throw new Error('Method not implemented.');
+ }
+ public hasSelection(): boolean {
+ throw new Error('Method not implemented.');
+ }
+ public getSelection(): string {
+ throw new Error('Method not implemented.');
+ }
+ public getSelectionPosition(): ISelectionPosition | undefined {
+ throw new Error('Method not implemented.');
+ }
+ public clearSelection(): void {
+ throw new Error('Method not implemented.');
+ }
+ public select(column: number, row: number, length: number): void {
+ throw new Error('Method not implemented.');
+ }
+ public selectAll(): void {
+ throw new Error('Method not implemented.');
+ }
+ public dispose(): void {
+ throw new Error('Method not implemented.');
+ }
+ public scrollPages(pageCount: number): void {
+ throw new Error('Method not implemented.');
+ }
+ public scrollToTop(): void {
+ throw new Error('Method not implemented.');
+ }
+ public scrollToBottom(): void {
+ throw new Error('Method not implemented.');
+ }
+ public clear(): void {
+ throw new Error('Method not implemented.');
+ }
+ public write(data: string): void {
+ throw new Error('Method not implemented.');
+ }
+ public writeUtf8(data: Uint8Array): void {
+ throw new Error('Method not implemented.');
+ }
+ public bracketedPasteMode!: boolean;
+ public renderer!: IRenderer;
+ public linkifier!: ILinkifier;
+ public linkifier2!: ILinkifier2;
+ public isFocused!: boolean;
+ public options: ITerminalOptions = {};
+ public element!: HTMLElement;
+ public screenElement!: HTMLElement;
+ public rowContainer!: HTMLElement;
+ public selectionContainer!: HTMLElement;
+ public selectionService!: ISelectionService;
+ public textarea!: HTMLTextAreaElement;
+ public rows!: number;
+ public cols!: number;
+ public browser: IBrowser = Browser;
+ public writeBuffer!: string[];
+ public children!: HTMLElement[];
+ public cursorHidden!: boolean;
+ public cursorState!: number;
+ public scrollback!: number;
+ public buffers!: IBufferSet;
+ public buffer!: IBuffer;
+ public viewport!: IViewport;
+ public applicationCursor!: boolean;
+ public handler(data: string): void {
+ throw new Error('Method not implemented.');
+ }
+ public on(event: string, callback: (...args: any[]) => void): void {
+ throw new Error('Method not implemented.');
+ }
+ public off(type: string, listener: XtermListener): void {
+ throw new Error('Method not implemented.');
+ }
+ public addDisposableListener(type: string, handler: XtermListener): IDisposable {
+ throw new Error('Method not implemented.');
+ }
+ public scrollLines(disp: number): void {
+ throw new Error('Method not implemented.');
+ }
+ public scrollToRow(absoluteRow: number): number {
+ throw new Error('Method not implemented.');
+ }
+ public cancel(ev: Event, force?: boolean): void {
+ throw new Error('Method not implemented.');
+ }
+ public log(text: string): void {
+ throw new Error('Method not implemented.');
+ }
+ public emit(event: string, data: any): void {
+ throw new Error('Method not implemented.');
+ }
+ public reset(): void {
+ throw new Error('Method not implemented.');
+ }
+ public refresh(start: number, end: number): void {
+ throw new Error('Method not implemented.');
+ }
+ public registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; }
+ public deregisterCharacterJoiner(joinerId: number): void { }
+}
+
+export class MockBuffer implements IBuffer {
+ public markers!: IMarker[];
+ public addMarker(y: number): IMarker {
+ throw new Error('Method not implemented.');
+ }
+ public isCursorInViewport!: boolean;
+ public lines!: ICircularList;
+ public ydisp!: number;
+ public ybase!: number;
+ public hasScrollback!: boolean;
+ public y!: number;
+ public x!: number;
+ public tabs: any;
+ public scrollBottom!: number;
+ public scrollTop!: number;
+ public savedY!: number;
+ public savedX!: number;
+ public savedCharset: ICharset | undefined;
+ public savedCurAttrData = new AttributeData();
+ public translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string {
+ return Buffer.prototype.translateBufferLineToString.apply(this, arguments as any);
+ }
+ public getWrappedRangeForLine(y: number): { first: number, last: number } {
+ return Buffer.prototype.getWrappedRangeForLine.apply(this, arguments as any);
+ }
+ public nextStop(x?: number): number {
+ throw new Error('Method not implemented.');
+ }
+ public prevStop(x?: number): number {
+ throw new Error('Method not implemented.');
+ }
+ public setLines(lines: ICircularList): void {
+ this.lines = lines;
+ }
+ public getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine {
+ return Buffer.prototype.getBlankLine.apply(this, arguments as any);
+ }
+ public stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[] {
+ return Buffer.prototype.stringIndexToBufferIndex.apply(this, arguments as any);
+ }
+ public iterator(trimRight: boolean, startIndex?: number, endIndex?: number): IBufferStringIterator {
+ return Buffer.prototype.iterator.apply(this, arguments as any);
+ }
+ public getNullCell(attr?: IAttributeData): ICellData {
+ throw new Error('Method not implemented.');
+ }
+ public getWhitespaceCell(attr?: IAttributeData): ICellData {
+ throw new Error('Method not implemented.');
+ }
+}
+
+export class MockRenderer implements IRenderer {
+ public onRequestRedraw!: IEvent;
+ public onCanvasResize!: IEvent<{ width: number, height: number }>;
+ public onRender!: IEvent<{ start: number, end: number }>;
+ public dispose(): void {
+ throw new Error('Method not implemented.');
+ }
+ public colorManager!: IColorManager;
+ public on(type: string, listener: XtermListener): void {
+ throw new Error('Method not implemented.');
+ }
+ public off(type: string, listener: XtermListener): void {
+ throw new Error('Method not implemented.');
+ }
+ public emit(type: string, data?: any): void {
+ throw new Error('Method not implemented.');
+ }
+ public addDisposableListener(type: string, handler: XtermListener): IDisposable {
+ throw new Error('Method not implemented.');
+ }
+ public dimensions!: IRenderDimensions;
+ public setColors(colors: IColorSet): void {
+ throw new Error('Method not implemented.');
+ }
+ public onResize(cols: number, rows: number): void { }
+ public onCharSizeChanged(): void { }
+ public onBlur(): void { }
+ public onFocus(): void { }
+ public onSelectionChanged(start: [number, number], end: [number, number]): void { }
+ public onCursorMove(): void { }
+ public onOptionsChanged(): void { }
+ public onDevicePixelRatioChange(): void { }
+ public clear(): void { }
+ public renderRows(start: number, end: number): void { }
+ public registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; }
+ public deregisterCharacterJoiner(): boolean { return true; }
+}
+
+export class MockViewport implements IViewport {
+ public dispose(): void {
+ throw new Error('Method not implemented.');
+ }
+ public scrollBarWidth: number = 0;
+ public onThemeChange(colors: IColorSet): void {
+ throw new Error('Method not implemented.');
+ }
+ public onWheel(ev: WheelEvent): boolean {
+ throw new Error('Method not implemented.');
+ }
+ public onTouchStart(ev: TouchEvent): void {
+ throw new Error('Method not implemented.');
+ }
+ public onTouchMove(ev: TouchEvent): boolean {
+ throw new Error('Method not implemented.');
+ }
+ public syncScrollArea(): void { }
+ public getLinesScrolled(ev: WheelEvent): number {
+ throw new Error('Method not implemented.');
+ }
+}
+
+export class MockCompositionHelper implements ICompositionHelper {
+ public compositionstart(): void {
+ throw new Error('Method not implemented.');
+ }
+ public compositionupdate(ev: CompositionEvent): void {
+ throw new Error('Method not implemented.');
+ }
+ public compositionend(): void {
+ throw new Error('Method not implemented.');
+ }
+ public updateCompositionElements(dontRecurse?: boolean): void {
+ throw new Error('Method not implemented.');
+ }
+ public keydown(ev: KeyboardEvent): boolean {
+ return true;
+ }
+}
export class MockCharSizeService implements ICharSizeService {
- public serviceBrand: any;
+ public serviceBrand: undefined;
public get hasValidSize(): boolean { return this.width > 0 && this.height > 0; }
public onCharSizeChange: IEvent = new EventEmitter().event;
constructor(public width: number, public height: number) {}
@@ -17,7 +335,7 @@ export class MockCharSizeService implements ICharSizeService {
}
export class MockMouseService implements IMouseService {
- public serviceBrand: any;
+ public serviceBrand: undefined;
public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined {
throw new Error('Not implemented');
}
@@ -28,7 +346,7 @@ export class MockMouseService implements IMouseService {
}
export class MockRenderService implements IRenderService {
- public serviceBrand: any;
+ public serviceBrand: undefined;
public onDimensionsChange: IEvent = new EventEmitter().event;
public onRenderedBufferChange: IEvent<{ start: number, end: number }, void> = new EventEmitter<{ start: number, end: number }>().event;
public onRefreshRequest: IEvent<{ start: number, end: number}, void> = new EventEmitter<{ start: number, end: number }>().event;
diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts
index f4645543..76aa0c8c 100644
--- a/src/browser/Types.d.ts
+++ b/src/browser/Types.d.ts
@@ -3,9 +3,107 @@
* @license MIT
*/
+import { IDisposable, IMarker, ISelectionPosition } from 'xterm';
import { IEvent } from 'common/EventEmitter';
-import { IDisposable } from 'common/Types';
+import { ICoreTerminal, CharData, ITerminalOptions } from 'common/Types';
import { IMouseService, IRenderService } from './services/Services';
+import { IBuffer, IBufferSet } from 'common/buffer/Types';
+import { IFunctionIdentifier, IParams } from 'common/parser/Types';
+
+export interface ITerminal extends IPublicTerminal, ICoreTerminal {
+ element: HTMLElement | undefined;
+ screenElement: HTMLElement | undefined;
+ browser: IBrowser;
+ buffer: IBuffer;
+ buffers: IBufferSet;
+ viewport: IViewport | undefined;
+ // TODO: We should remove options once components adopt optionsService
+ options: ITerminalOptions;
+ linkifier: ILinkifier;
+ linkifier2: ILinkifier2;
+
+ onBlur: IEvent;
+ onFocus: IEvent;
+ onA11yChar: IEvent;
+ onA11yTab: IEvent;
+
+ cancel(ev: Event, force?: boolean): boolean | void;
+}
+
+// Portions of the public API that are required by the internal Terminal
+export interface IPublicTerminal extends IDisposable {
+ textarea: HTMLTextAreaElement | undefined;
+ rows: number;
+ cols: number;
+ buffer: IBuffer;
+ markers: IMarker[];
+ onCursorMove: IEvent;
+ onData: IEvent;
+ onBinary: IEvent;
+ onKey: IEvent<{ key: string, domEvent: KeyboardEvent }>;
+ onLineFeed: IEvent;
+ onScroll: IEvent;
+ onSelectionChange: IEvent;
+ onRender: IEvent<{ start: number, end: number }>;
+ onResize: IEvent<{ cols: number, rows: number }>;
+ onTitleChange: IEvent;
+ blur(): void;
+ focus(): void;
+ resize(columns: number, rows: number): void;
+ open(parent: HTMLElement): void;
+ attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void;
+ addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable;
+ addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable;
+ addEscHandler(id: IFunctionIdentifier, callback: () => boolean): IDisposable;
+ addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable;
+ registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number;
+ deregisterLinkMatcher(matcherId: number): void;
+ registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
+ registerCharacterJoiner(handler: (text: string) => [number, number][]): number;
+ deregisterCharacterJoiner(joinerId: number): void;
+ addMarker(cursorYOffset: number): IMarker | undefined;
+ hasSelection(): boolean;
+ getSelection(): string;
+ getSelectionPosition(): ISelectionPosition | undefined;
+ clearSelection(): void;
+ select(column: number, row: number, length: number): void;
+ selectAll(): void;
+ selectLines(start: number, end: number): void;
+ dispose(): void;
+ scrollLines(amount: number): void;
+ scrollPages(pageCount: number): void;
+ scrollToTop(): void;
+ scrollToBottom(): void;
+ scrollToLine(line: number): void;
+ clear(): void;
+ write(data: string | Uint8Array, callback?: () => void): void;
+ paste(data: string): void;
+ refresh(start: number, end: number): void;
+ reset(): void;
+}
+
+export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
+
+export type LineData = CharData[];
+
+export interface ICompositionHelper {
+ compositionstart(): void;
+ compositionupdate(ev: CompositionEvent): void;
+ compositionend(): void;
+ updateCompositionElements(dontRecurse?: boolean): void;
+ keydown(ev: KeyboardEvent): boolean;
+}
+
+export interface IBrowser {
+ isNode: boolean;
+ userAgent: string;
+ platform: string;
+ isFirefox: boolean;
+ isMac: boolean;
+ isIpad: boolean;
+ isIphone: boolean;
+ isWindows: boolean;
+}
export interface IColorManager {
colors: IColorSet;
diff --git a/src/public/AddonManager.test.ts b/src/browser/public/AddonManager.test.ts
similarity index 90%
rename from src/public/AddonManager.test.ts
rename to src/browser/public/AddonManager.test.ts
index ea229095..e947976a 100644
--- a/src/public/AddonManager.test.ts
+++ b/src/browser/public/AddonManager.test.ts
@@ -42,9 +42,9 @@ describe('AddonManager', () => {
public activate(): void {}
public dispose(): void { called++; }
}
- manager.loadAddon(null, new Addon());
- manager.loadAddon(null, new Addon());
- manager.loadAddon(null, new Addon());
+ manager.loadAddon(null!, new Addon());
+ manager.loadAddon(null!, new Addon());
+ manager.loadAddon(null!, new Addon());
assert.equal(manager.addons.length, 3);
manager.dispose();
assert.equal(called, 3);
diff --git a/src/public/AddonManager.ts b/src/browser/public/AddonManager.ts
similarity index 100%
rename from src/public/AddonManager.ts
rename to src/browser/public/AddonManager.ts
diff --git a/src/public/Terminal.ts b/src/browser/public/Terminal.ts
similarity index 97%
rename from src/public/Terminal.ts
rename to src/browser/public/Terminal.ts
index 61334f08..b49fc6b8 100644
--- a/src/public/Terminal.ts
+++ b/src/browser/public/Terminal.ts
@@ -4,12 +4,12 @@
*/
import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBuffer as IBufferApi, IBufferNamespace as IBufferNamespaceApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi, IParser, IFunctionIdentifier, ILinkProvider, IUnicodeHandling, IUnicodeVersionProvider } from 'xterm';
-import { ITerminal } from '../Types';
+import { ITerminal } from 'browser/Types';
import { IBufferLine, ICellData } from 'common/Types';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
import { CellData } from 'common/buffer/CellData';
import { Terminal as TerminalCore } from '../Terminal';
-import * as Strings from '../browser/LocalizableStrings';
+import * as Strings from '../LocalizableStrings';
import { IEvent, EventEmitter } from 'common/EventEmitter';
import { AddonManager } from './AddonManager';
import { IParams } from 'common/parser/Types';
@@ -17,7 +17,7 @@ import { IParams } from 'common/parser/Types';
export class Terminal implements ITerminalApi {
private _core: ITerminal;
private _addonManager: AddonManager;
- private _parser: IParser;
+ private _parser: IParser | undefined;
constructor(options?: ITerminalOptions) {
this._core = new TerminalCore(options);
@@ -81,11 +81,11 @@ export class Terminal implements ITerminalApi {
public deregisterCharacterJoiner(joinerId: number): void {
this._core.deregisterCharacterJoiner(joinerId);
}
- public registerMarker(cursorYOffset: number): IMarker {
+ public registerMarker(cursorYOffset: number): IMarker | undefined {
this._verifyIntegers(cursorYOffset);
return this._core.addMarker(cursorYOffset);
}
- public addMarker(cursorYOffset: number): IMarker {
+ public addMarker(cursorYOffset: number): IMarker | undefined {
return this.registerMarker(cursorYOffset);
}
public hasSelection(): boolean {
diff --git a/src/browser/services/CharSizeService.ts b/src/browser/services/CharSizeService.ts
index 7623109c..79696a28 100644
--- a/src/browser/services/CharSizeService.ts
+++ b/src/browser/services/CharSizeService.ts
@@ -8,7 +8,7 @@ import { IEvent, EventEmitter } from 'common/EventEmitter';
import { ICharSizeService } from 'browser/services/Services';
export class CharSizeService implements ICharSizeService {
- public serviceBrand: any;
+ public serviceBrand: undefined;
public width: number = 0;
public height: number = 0;
diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts
index e3207d7b..985253d9 100644
--- a/src/browser/services/CoreBrowserService.ts
+++ b/src/browser/services/CoreBrowserService.ts
@@ -6,7 +6,7 @@
import { ICoreBrowserService } from './Services';
export class CoreBrowserService implements ICoreBrowserService {
- public serviceBrand: any;
+ public serviceBrand: undefined;
constructor(
private _textarea: HTMLTextAreaElement
diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts
index 39df6383..348ba64e 100644
--- a/src/browser/services/MouseService.ts
+++ b/src/browser/services/MouseService.ts
@@ -7,7 +7,7 @@ import { ICharSizeService, IRenderService, IMouseService } from './Services';
import { getCoords, getRawByteCoords } from 'browser/input/Mouse';
export class MouseService implements IMouseService {
- public serviceBrand: any;
+ public serviceBrand: undefined;
constructor(
@IRenderService private readonly _renderService: IRenderService,
diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts
index 7c8a13cc..d6b30524 100644
--- a/src/browser/services/RenderService.ts
+++ b/src/browser/services/RenderService.ts
@@ -10,7 +10,7 @@ import { Disposable } from 'common/Lifecycle';
import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
import { addDisposableDomListener } from 'browser/Lifecycle';
import { IColorSet } from 'browser/Types';
-import { IOptionsService } from 'common/services/Services';
+import { IOptionsService, IBufferService } from 'common/services/Services';
import { ICharSizeService, IRenderService } from 'browser/services/Services';
interface ISelectionState {
@@ -20,7 +20,7 @@ interface ISelectionState {
}
export class RenderService extends Disposable implements IRenderService {
- public serviceBrand: any;
+ public serviceBrand: undefined;
private _renderDebouncer: RenderDebouncer;
private _screenDprMonitor: ScreenDprMonitor;
@@ -51,7 +51,8 @@ export class RenderService extends Disposable implements IRenderService {
private _rowCount: number,
screenElement: HTMLElement,
@IOptionsService optionsService: IOptionsService,
- @ICharSizeService charSizeService: ICharSizeService
+ @ICharSizeService charSizeService: ICharSizeService,
+ @IBufferService private readonly _bufferService: IBufferService
) {
super();
this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end));
@@ -61,6 +62,7 @@ export class RenderService extends Disposable implements IRenderService {
this._screenDprMonitor.setListener(() => this.onDevicePixelRatioChange());
this.register(this._screenDprMonitor);
+ this.register(this._bufferService.onResize(e => this._fullRefresh()));
this.register(optionsService.onOptionChange(() => this._renderer.onOptionsChanged()));
this.register(charSizeService.onCharSizeChange(() => this.onCharSizeChanged()));
diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts
index 43875f1b..d70cfa11 100644
--- a/src/browser/services/SelectionService.ts
+++ b/src/browser/services/SelectionService.ts
@@ -67,7 +67,7 @@ export const enum SelectionMode {
* when the selection is ready to be redrawn (on an animation frame).
*/
export class SelectionService implements ISelectionService {
- public serviceBrand: any;
+ public serviceBrand: undefined;
protected _model: SelectionModel;
diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts
index 7a559dab..445cab05 100644
--- a/src/browser/services/Services.ts
+++ b/src/browser/services/Services.ts
@@ -12,7 +12,7 @@ import { IDisposable } from 'common/Types';
export const ICharSizeService = createDecorator('CharSizeService');
export interface ICharSizeService {
- serviceBrand: any;
+ serviceBrand: undefined;
readonly width: number;
readonly height: number;
@@ -25,14 +25,14 @@ export interface ICharSizeService {
export const ICoreBrowserService = createDecorator('CoreBrowserService');
export interface ICoreBrowserService {
- serviceBrand: any;
+ serviceBrand: undefined;
readonly isFocused: boolean;
}
export const IMouseService = createDecorator('MouseService');
export interface IMouseService {
- serviceBrand: any;
+ serviceBrand: undefined;
getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined;
getRawByteCoords(event: MouseEvent, element: HTMLElement, colCount: number, rowCount: number): { x: number, y: number } | undefined;
@@ -40,7 +40,7 @@ export interface IMouseService {
export const IRenderService = createDecorator('RenderService');
export interface IRenderService extends IDisposable {
- serviceBrand: any;
+ serviceBrand: undefined;
onDimensionsChange: IEvent;
/**
@@ -72,7 +72,7 @@ export interface IRenderService extends IDisposable {
export const ISelectionService = createDecorator('SelectionService');
export interface ISelectionService {
- serviceBrand: any;
+ serviceBrand: undefined;
readonly selectionText: string;
readonly hasSelection: boolean;
@@ -100,7 +100,7 @@ export interface ISelectionService {
export const ISoundService = createDecorator('SoundService');
export interface ISoundService {
- serviceBrand: any;
+ serviceBrand: undefined;
playBellSound(): void;
}
diff --git a/src/browser/services/SoundService.ts b/src/browser/services/SoundService.ts
index 4b54c999..8d940c13 100644
--- a/src/browser/services/SoundService.ts
+++ b/src/browser/services/SoundService.ts
@@ -7,7 +7,7 @@ import { IOptionsService } from 'common/services/Services';
import { ISoundService } from 'browser/services/Services';
export class SoundService implements ISoundService {
- public serviceBrand: any;
+ public serviceBrand: undefined;
private static _audioContext: AudioContext;
diff --git a/src/browser/tsconfig.json b/src/browser/tsconfig.json
index 7465bbd3..74b4c057 100644
--- a/src/browser/tsconfig.json
+++ b/src/browser/tsconfig.json
@@ -14,7 +14,10 @@
"common/*": [ "./common/*" ]
}
},
- "include": [ "./**/*" ],
+ "include": [
+ "./**/*",
+ "../../typings/xterm.d.ts"
+ ],
"references": [
{ "path": "../common" }
]
diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts
new file mode 100644
index 00000000..3793ae25
--- /dev/null
+++ b/src/common/CoreTerminal.ts
@@ -0,0 +1,336 @@
+/**
+ * Copyright (c) 2014-2020 The xterm.js authors. All rights reserved.
+ * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
+ * @license MIT
+ *
+ * Originally forked from (with the author's permission):
+ * Fabrice Bellard's javascript vt100 for jslinux:
+ * http://bellard.org/jslinux/
+ * Copyright (c) 2011 Fabrice Bellard
+ * The original design remains. The terminal itself
+ * has been extended to include xterm CSI codes, among
+ * other features.
+ *
+ * Terminal Emulation References:
+ * http://vt100.net/
+ * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt
+ * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ * http://invisible-island.net/vttest/
+ * http://www.inwap.com/pdp10/ansicode.txt
+ * http://linux.die.net/man/4/console_codes
+ * http://linux.die.net/man/7/urxvt
+ */
+
+import { Disposable } from 'common/Lifecycle';
+import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, ICoreMouseService, IUnicodeService, IDirtyRowService } from 'common/services/Services';
+import { InstantiationService } from 'common/services/InstantiationService';
+import { LogService } from 'common/services/LogService';
+import { BufferService, MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService';
+import { OptionsService } from 'common/services/OptionsService';
+import { ITerminalOptions, IDisposable, IBufferLine, IAttributeData, ICoreTerminal } from 'common/Types';
+import { CoreService } from 'common/services/CoreService';
+import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
+import { CoreMouseService } from 'common/services/CoreMouseService';
+import { DirtyRowService } from 'common/services/DirtyRowService';
+import { UnicodeService } from 'common/services/UnicodeService';
+import { CharsetService } from 'common/services/CharsetService';
+import { updateWindowsModeWrappedState } from 'common/WindowsMode';
+import { IFunctionIdentifier, IParams } from 'common/parser/Types';
+import { IBufferSet } from 'common/buffer/Types';
+import { InputHandler } from 'common/InputHandler';
+import { WriteBuffer } from 'common/input/WriteBuffer';
+
+export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
+ protected readonly _instantiationService: IInstantiationService;
+ protected readonly _bufferService: IBufferService;
+ protected readonly _logService: ILogService;
+ protected readonly _coreService: ICoreService;
+ protected readonly _charsetService: ICharsetService;
+ protected readonly _coreMouseService: ICoreMouseService;
+ protected readonly _dirtyRowService: IDirtyRowService;
+
+ public readonly unicodeService: IUnicodeService;
+ public readonly optionsService: IOptionsService;
+
+ protected _inputHandler: InputHandler;
+ private _writeBuffer: WriteBuffer;
+ private _windowsMode: IDisposable | undefined;
+ /** An IBufferline to clone/copy from for new blank lines */
+ private _cachedBlankLine: IBufferLine | undefined;
+
+ private _onBinary = new EventEmitter();
+ public get onBinary(): IEvent { return this._onBinary.event; }
+ private _onData = new EventEmitter();
+ public get onData(): IEvent { return this._onData.event; }
+ protected _onLineFeed = new EventEmitter();
+ public get onLineFeed(): IEvent { return this._onLineFeed.event; }
+ private _onResize = new EventEmitter<{ cols: number, rows: number }>();
+ public get onResize(): IEvent<{ cols: number, rows: number }> { return this._onResize.event; }
+ protected _onScroll = new EventEmitter();
+ public get onScroll(): IEvent { return this._onScroll.event; }
+
+ public get cols(): number { return this._bufferService.cols; }
+ public get rows(): number { return this._bufferService.rows; }
+ public get buffers(): IBufferSet { return this._bufferService.buffers; }
+
+ constructor(
+ options: ITerminalOptions
+ ) {
+ super();
+
+ // Setup and initialize services
+ this._instantiationService = new InstantiationService();
+ this.optionsService = new OptionsService(options);
+ this._instantiationService.setService(IOptionsService, this.optionsService);
+ this._bufferService = this._instantiationService.createInstance(BufferService);
+ this._instantiationService.setService(IBufferService, this._bufferService);
+ this._logService = this._instantiationService.createInstance(LogService);
+ this._instantiationService.setService(ILogService, this._logService);
+ this._coreService = this._instantiationService.createInstance(CoreService, () => this.scrollToBottom());
+ this._instantiationService.setService(ICoreService, this._coreService);
+ this._coreMouseService = this._instantiationService.createInstance(CoreMouseService);
+ this._instantiationService.setService(ICoreMouseService, this._coreMouseService);
+ this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService);
+ this._instantiationService.setService(IDirtyRowService, this._dirtyRowService);
+ this.unicodeService = this._instantiationService.createInstance(UnicodeService);
+ this._instantiationService.setService(IUnicodeService, this.unicodeService);
+ this._charsetService = this._instantiationService.createInstance(CharsetService);
+ this._instantiationService.setService(ICharsetService, this._charsetService);
+
+ // Register input handler and handle/forward events
+ this._inputHandler = new InputHandler(this._bufferService, this._charsetService, this._coreService, this._dirtyRowService, this._logService, this.optionsService, this._coreMouseService, this.unicodeService);
+ this.register(forwardEvent(this._inputHandler.onLineFeed, this._onLineFeed));
+ this.register(this._inputHandler);
+
+ // Setup listeners
+ this.register(forwardEvent(this._bufferService.onResize, this._onResize));
+ this.register(forwardEvent(this._coreService.onData, this._onData));
+ this.register(forwardEvent(this._coreService.onBinary, this._onBinary));
+ this.register(this.optionsService.onOptionChange(key => this._updateOptions(key)));
+
+ // Setup WriteBuffer
+ this._writeBuffer = new WriteBuffer(data => this._inputHandler.parse(data));
+ }
+
+ public dispose(): void {
+ if (this._isDisposed) {
+ return;
+ }
+ super.dispose();
+ this._windowsMode?.dispose();
+ this._windowsMode = undefined;
+ }
+
+ public write(data: string | Uint8Array, callback?: () => void): void {
+ this._writeBuffer.write(data, callback);
+ }
+
+ public writeSync(data: string | Uint8Array): void {
+ this._writeBuffer.writeSync(data);
+ }
+
+ public resize(x: number, y: number): void {
+ if (isNaN(x) || isNaN(y)) {
+ return;
+ }
+
+ x = Math.max(x, MINIMUM_COLS);
+ y = Math.max(y, MINIMUM_ROWS);
+
+ this._bufferService.resize(x, y);
+ }
+
+ /**
+ * Scroll the terminal down 1 row, creating a blank line.
+ * @param isWrapped Whether the new line is wrapped from the previous line.
+ */
+ public scroll(eraseAttr: IAttributeData, isWrapped: boolean = false): void {
+ const buffer = this._bufferService.buffer;
+
+ let newLine: IBufferLine | undefined;
+ newLine = this._cachedBlankLine;
+ if (!newLine || newLine.length !== this.cols || newLine.getFg(0) !== eraseAttr.fg || newLine.getBg(0) !== eraseAttr.bg) {
+ newLine = buffer.getBlankLine(eraseAttr, isWrapped);
+ this._cachedBlankLine = newLine;
+ }
+ newLine.isWrapped = isWrapped;
+
+ const topRow = buffer.ybase + buffer.scrollTop;
+ const bottomRow = buffer.ybase + buffer.scrollBottom;
+
+ if (buffer.scrollTop === 0) {
+ // Determine whether the buffer is going to be trimmed after insertion.
+ const willBufferBeTrimmed = buffer.lines.isFull;
+
+ // Insert the line using the fastest method
+ if (bottomRow === buffer.lines.length - 1) {
+ if (willBufferBeTrimmed) {
+ buffer.lines.recycle().copyFrom(newLine);
+ } else {
+ buffer.lines.push(newLine.clone());
+ }
+ } else {
+ buffer.lines.splice(bottomRow + 1, 0, newLine.clone());
+ }
+
+ // Only adjust ybase and ydisp when the buffer is not trimmed
+ if (!willBufferBeTrimmed) {
+ buffer.ybase++;
+ // Only scroll the ydisp with ybase if the user has not scrolled up
+ if (!this._bufferService.isUserScrolling) {
+ buffer.ydisp++;
+ }
+ } else {
+ // When the buffer is full and the user has scrolled up, keep the text
+ // stable unless ydisp is right at the top
+ if (this._bufferService.isUserScrolling) {
+ buffer.ydisp = Math.max(buffer.ydisp - 1, 0);
+ }
+ }
+ } else {
+ // scrollTop is non-zero which means no line will be going to the
+ // scrollback, instead we can just shift them in-place.
+ const scrollRegionHeight = bottomRow - topRow + 1 /* as it's zero-based */;
+ buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1);
+ buffer.lines.set(bottomRow, newLine.clone());
+ }
+
+ // Move the viewport to the bottom of the buffer unless the user is
+ // scrolling.
+ if (!this._bufferService.isUserScrolling) {
+ buffer.ydisp = buffer.ybase;
+ }
+
+ // Flag rows that need updating
+ this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
+
+ this._onScroll.fire(buffer.ydisp);
+ }
+
+ /**
+ * Scroll the display of the terminal
+ * @param disp The number of lines to scroll down (negative scroll up).
+ * @param suppressScrollEvent Don't emit the scroll event as scrollLines. This is used
+ * to avoid unwanted events being handled by the viewport when the event was triggered from the
+ * viewport originally.
+ */
+ public scrollLines(disp: number, suppressScrollEvent?: boolean): void {
+ const buffer = this._bufferService.buffer;
+ if (disp < 0) {
+ if (buffer.ydisp === 0) {
+ return;
+ }
+ this._bufferService.isUserScrolling = true;
+ } else if (disp + buffer.ydisp >= buffer.ybase) {
+ this._bufferService.isUserScrolling = false;
+ }
+
+ const oldYdisp = buffer.ydisp;
+ buffer.ydisp = Math.max(Math.min(buffer.ydisp + disp, buffer.ybase), 0);
+
+ // No change occurred, don't trigger scroll/refresh
+ if (oldYdisp === buffer.ydisp) {
+ return;
+ }
+
+ if (!suppressScrollEvent) {
+ this._onScroll.fire(buffer.ydisp);
+ }
+ }
+
+ /**
+ * Scroll the display of the terminal by a number of pages.
+ * @param pageCount The number of pages to scroll (negative scrolls up).
+ */
+ public scrollPages(pageCount: number): void {
+ this.scrollLines(pageCount * (this.rows - 1));
+ }
+
+ /**
+ * Scrolls the display of the terminal to the top.
+ */
+ public scrollToTop(): void {
+ this.scrollLines(-this._bufferService.buffer.ydisp);
+ }
+
+ /**
+ * Scrolls the display of the terminal to the bottom.
+ */
+ public scrollToBottom(): void {
+ this.scrollLines(this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp);
+ }
+
+ public scrollToLine(line: number): void {
+ const scrollAmount = line - this._bufferService.buffer.ydisp;
+ if (scrollAmount !== 0) {
+ this.scrollLines(scrollAmount);
+ }
+ }
+
+ /** Add handler for ESC escape sequence. See xterm.d.ts for details. */
+ public addEscHandler(id: IFunctionIdentifier, callback: () => boolean): IDisposable {
+ return this._inputHandler.addEscHandler(id, callback);
+ }
+
+ /** Add handler for DCS escape sequence. See xterm.d.ts for details. */
+ public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable {
+ return this._inputHandler.addDcsHandler(id, callback);
+ }
+
+ /** Add handler for CSI escape sequence. See xterm.d.ts for details. */
+ public addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable {
+ return this._inputHandler.addCsiHandler(id, callback);
+ }
+
+ /** Add handler for OSC escape sequence. See xterm.d.ts for details. */
+ public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
+ return this._inputHandler.addOscHandler(ident, callback);
+ }
+
+ protected _setup(): void {
+ if (this.optionsService.options.windowsMode) {
+ this._enableWindowsMode();
+ }
+ }
+
+ public reset(): void {
+ this._inputHandler.reset();
+ this._bufferService.reset();
+ this._charsetService.reset();
+ this._coreService.reset();
+ this._coreMouseService.reset();
+ }
+
+ protected _updateOptions(key: string): void {
+ // TODO: These listeners should be owned by individual components
+ switch (key) {
+ case 'scrollback':
+ this.buffers.resize(this.cols, this.rows);
+ break;
+ case 'windowsMode':
+ if (this.optionsService.options.windowsMode) {
+ this._enableWindowsMode();
+ } else {
+ this._windowsMode?.dispose();
+ this._windowsMode = undefined;
+ }
+ break;
+ }
+ }
+
+ protected _enableWindowsMode(): void {
+ if (!this._windowsMode) {
+ const disposables: IDisposable[] = [];
+ disposables.push(this.onLineFeed(updateWindowsModeWrappedState.bind(null, this._bufferService)));
+ disposables.push(this.addCsiHandler({ final: 'H' }, () => {
+ updateWindowsModeWrappedState(this._bufferService);
+ return false;
+ }));
+ this._windowsMode = {
+ dispose: () => {
+ disposables.forEach(d => d.dispose());
+ }
+ };
+ }
+ }
+}
diff --git a/src/common/EventEmitter.ts b/src/common/EventEmitter.ts
index 301574e4..4684809f 100644
--- a/src/common/EventEmitter.ts
+++ b/src/common/EventEmitter.ts
@@ -63,3 +63,7 @@ export class EventEmitter implements IEventEmitter {
this._disposed = true;
}
}
+
+export function forwardEvent(from: IEvent, to: IEventEmitter): IDisposable {
+ return from(e => to.fire(e));
+}
diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts
new file mode 100644
index 00000000..8f3ff9af
--- /dev/null
+++ b/src/common/InputHandler.test.ts
@@ -0,0 +1,1427 @@
+/**
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
+ * @license MIT
+ */
+
+import { assert, expect } from 'chai';
+import { InputHandler } from 'common/InputHandler';
+import { IBufferLine, IAttributeData } from 'common/Types';
+import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
+import { CellData } from 'common/buffer/CellData';
+import { Attributes } from 'common/buffer/Constants';
+import { AttributeData } from 'common/buffer/AttributeData';
+import { Params } from 'common/parser/Params';
+import { MockCoreService, MockBufferService, MockDirtyRowService, MockOptionsService, MockLogService, MockCoreMouseService, MockCharsetService, MockUnicodeService } from 'common/TestUtils.test';
+import { IBufferService, ICoreService } from 'common/services/Services';
+import { DEFAULT_OPTIONS } from 'common/services/OptionsService';
+import { clone } from 'common/Clone';
+import { BufferService } from 'common/services/BufferService';
+import { CoreService } from 'common/services/CoreService';
+
+function getCursor(bufferService: IBufferService): number[] {
+ return [
+ bufferService.buffer.x,
+ bufferService.buffer.y
+ ];
+}
+
+function getLines(bufferService: IBufferService, limit: number = bufferService.rows): string[] {
+ const res: string[] = [];
+ for (let i = 0; i < limit; ++i) {
+ res.push(bufferService.buffer.lines.get(i)!.translateToString(true));
+ }
+ return res;
+}
+
+class TestInputHandler extends InputHandler {
+ public get curAttrData(): IAttributeData { return (this as any)._curAttrData; }
+ public get windowTitleStack(): string[] { return this._windowTitleStack; }
+ public get iconNameStack(): string[] { return this._iconNameStack; }
+}
+
+describe('InputHandler', () => {
+ let bufferService: IBufferService;
+ let coreService: ICoreService;
+ let optionsService: MockOptionsService;
+ let inputHandler: TestInputHandler;
+
+ beforeEach(() => {
+ optionsService = new MockOptionsService();
+ bufferService = new BufferService(optionsService);
+ bufferService.resize(80, 30);
+ coreService = new CoreService(() => {}, bufferService, new MockLogService(), optionsService);
+
+ inputHandler = new TestInputHandler(bufferService, new MockCharsetService(), coreService, new MockDirtyRowService(), new MockLogService(), optionsService, new MockCoreMouseService(), new MockUnicodeService());
+ });
+
+ it('save and restore cursor', () => {
+ bufferService.buffer.x = 1;
+ bufferService.buffer.y = 2;
+ bufferService.buffer.ybase = 0;
+ inputHandler.curAttrData.fg = 3;
+ // Save cursor position
+ inputHandler.saveCursor();
+ assert.equal(bufferService.buffer.x, 1);
+ assert.equal(bufferService.buffer.y, 2);
+ assert.equal(inputHandler.curAttrData.fg, 3);
+ // Change cursor position
+ bufferService.buffer.x = 10;
+ bufferService.buffer.y = 20;
+ inputHandler.curAttrData.fg = 30;
+ // Restore cursor position
+ inputHandler.restoreCursor();
+ assert.equal(bufferService.buffer.x, 1);
+ assert.equal(bufferService.buffer.y, 2);
+ assert.equal(inputHandler.curAttrData.fg, 3);
+ });
+ describe('setCursorStyle', () => {
+ it('should call Terminal.setOption with correct params', () => {
+ inputHandler.setCursorStyle(Params.fromArray([0]));
+ assert.equal(optionsService.options['cursorStyle'], 'block');
+ assert.equal(optionsService.options['cursorBlink'], true);
+
+ optionsService.options = clone(DEFAULT_OPTIONS);
+ inputHandler.setCursorStyle(Params.fromArray([1]));
+ assert.equal(optionsService.options['cursorStyle'], 'block');
+ assert.equal(optionsService.options['cursorBlink'], true);
+
+ optionsService.options = clone(DEFAULT_OPTIONS);
+ inputHandler.setCursorStyle(Params.fromArray([2]));
+ assert.equal(optionsService.options['cursorStyle'], 'block');
+ assert.equal(optionsService.options['cursorBlink'], false);
+
+ optionsService.options = clone(DEFAULT_OPTIONS);
+ inputHandler.setCursorStyle(Params.fromArray([3]));
+ assert.equal(optionsService.options['cursorStyle'], 'underline');
+ assert.equal(optionsService.options['cursorBlink'], true);
+
+ optionsService.options = clone(DEFAULT_OPTIONS);
+ inputHandler.setCursorStyle(Params.fromArray([4]));
+ assert.equal(optionsService.options['cursorStyle'], 'underline');
+ assert.equal(optionsService.options['cursorBlink'], false);
+
+ optionsService.options = clone(DEFAULT_OPTIONS);
+ inputHandler.setCursorStyle(Params.fromArray([5]));
+ assert.equal(optionsService.options['cursorStyle'], 'bar');
+ assert.equal(optionsService.options['cursorBlink'], true);
+
+ optionsService.options = clone(DEFAULT_OPTIONS);
+ inputHandler.setCursorStyle(Params.fromArray([6]));
+ assert.equal(optionsService.options['cursorStyle'], 'bar');
+ assert.equal(optionsService.options['cursorBlink'], false);
+ });
+ });
+ describe('setMode', () => {
+ it('should toggle bracketedPasteMode', () => {
+ const coreService = new MockCoreService();
+ const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), coreService, new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
+ // Set bracketed paste mode
+ inputHandler.setModePrivate(Params.fromArray([2004]));
+ assert.equal(coreService.decPrivateModes.bracketedPasteMode, true);
+ // Reset bracketed paste mode
+ inputHandler.resetModePrivate(Params.fromArray([2004]));
+ assert.equal(coreService.decPrivateModes.bracketedPasteMode, false);
+ });
+ });
+ describe('regression tests', function(): void {
+ function termContent(bufferService: IBufferService, trim: boolean): string[] {
+ const result = [];
+ for (let i = 0; i < bufferService.rows; ++i) result.push(bufferService.buffer.lines.get(i)!.translateToString(trim));
+ return result;
+ }
+
+ it('insertChars', function(): void {
+ const bufferService = new MockBufferService(80, 30);
+ const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
+
+ // insert some data in first and second line
+ inputHandler.parse(Array(bufferService.cols - 9).join('a'));
+ inputHandler.parse('1234567890');
+ inputHandler.parse(Array(bufferService.cols - 9).join('a'));
+ inputHandler.parse('1234567890');
+ const line1: IBufferLine = bufferService.buffer.lines.get(0)!;
+ expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890');
+
+ // insert one char from params = [0]
+ bufferService.buffer.y = 0;
+ bufferService.buffer.x = 70;
+ inputHandler.insertChars(Params.fromArray([0]));
+ expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 123456789');
+
+ // insert one char from params = [1]
+ bufferService.buffer.y = 0;
+ bufferService.buffer.x = 70;
+ inputHandler.insertChars(Params.fromArray([1]));
+ expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 12345678');
+
+ // insert two chars from params = [2]
+ bufferService.buffer.y = 0;
+ bufferService.buffer.x = 70;
+ inputHandler.insertChars(Params.fromArray([2]));
+ expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 123456');
+
+ // insert 10 chars from params = [10]
+ bufferService.buffer.y = 0;
+ bufferService.buffer.x = 70;
+ inputHandler.insertChars(Params.fromArray([10]));
+ expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' ');
+ expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a'));
+ });
+ it('deleteChars', function(): void {
+ const bufferService = new MockBufferService(80, 30);
+ const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
+
+ // insert some data in first and second line
+ inputHandler.parse(Array(bufferService.cols - 9).join('a'));
+ inputHandler.parse('1234567890');
+ inputHandler.parse(Array(bufferService.cols - 9).join('a'));
+ inputHandler.parse('1234567890');
+ const line1: IBufferLine = bufferService.buffer.lines.get(0)!;
+ expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890');
+
+ // delete one char from params = [0]
+ bufferService.buffer.y = 0;
+ bufferService.buffer.x = 70;
+ inputHandler.deleteChars(Params.fromArray([0]));
+ expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '234567890 ');
+ expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '234567890');
+
+ // insert one char from params = [1]
+ bufferService.buffer.y = 0;
+ bufferService.buffer.x = 70;
+ inputHandler.deleteChars(Params.fromArray([1]));
+ expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '34567890 ');
+ expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '34567890');
+
+ // insert two chars from params = [2]
+ bufferService.buffer.y = 0;
+ bufferService.buffer.x = 70;
+ inputHandler.deleteChars(Params.fromArray([2]));
+ expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '567890 ');
+ expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '567890');
+
+ // insert 10 chars from params = [10]
+ bufferService.buffer.y = 0;
+ bufferService.buffer.x = 70;
+ inputHandler.deleteChars(Params.fromArray([10]));
+ expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' ');
+ expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a'));
+ });
+ it('eraseInLine', function(): void {
+ const bufferService = new MockBufferService(80, 30);
+ const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
+
+ // fill 6 lines to test 3 different states
+ inputHandler.parse(Array(bufferService.cols + 1).join('a'));
+ inputHandler.parse(Array(bufferService.cols + 1).join('a'));
+ inputHandler.parse(Array(bufferService.cols + 1).join('a'));
+
+ // params[0] - right erase
+ bufferService.buffer.y = 0;
+ bufferService.buffer.x = 70;
+ inputHandler.eraseInLine(Params.fromArray([0]));
+ expect(bufferService.buffer.lines.get(0)!.translateToString(false)).equals(Array(71).join('a') + ' ');
+
+ // params[1] - left erase
+ bufferService.buffer.y = 1;
+ bufferService.buffer.x = 70;
+ inputHandler.eraseInLine(Params.fromArray([1]));
+ expect(bufferService.buffer.lines.get(1)!.translateToString(false)).equals(Array(71).join(' ') + ' aaaaaaaaa');
+
+ // params[1] - left erase
+ bufferService.buffer.y = 2;
+ bufferService.buffer.x = 70;
+ inputHandler.eraseInLine(Params.fromArray([2]));
+ expect(bufferService.buffer.lines.get(2)!.translateToString(false)).equals(Array(bufferService.cols + 1).join(' '));
+
+ });
+ it('eraseInDisplay', function(): void {
+ const bufferService = new MockBufferService(80, 7);
+ const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
+
+ // fill display with a's
+ for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
+
+ // params [0] - right and below erase
+ bufferService.buffer.y = 5;
+ bufferService.buffer.x = 40;
+ inputHandler.eraseInDisplay(Params.fromArray([0]));
+ expect(termContent(bufferService, false)).eql([
+ Array(bufferService.cols + 1).join('a'),
+ Array(bufferService.cols + 1).join('a'),
+ Array(bufferService.cols + 1).join('a'),
+ Array(bufferService.cols + 1).join('a'),
+ Array(bufferService.cols + 1).join('a'),
+ Array(40 + 1).join('a') + Array(bufferService.cols - 40 + 1).join(' '),
+ Array(bufferService.cols + 1).join(' ')
+ ]);
+ expect(termContent(bufferService, true)).eql([
+ Array(bufferService.cols + 1).join('a'),
+ Array(bufferService.cols + 1).join('a'),
+ Array(bufferService.cols + 1).join('a'),
+ Array(bufferService.cols + 1).join('a'),
+ Array(bufferService.cols + 1).join('a'),
+ Array(40 + 1).join('a'),
+ ''
+ ]);
+
+ // reset
+ bufferService.buffer.y = 0;
+ bufferService.buffer.x = 0;
+ for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
+
+ // params [1] - left and above
+ bufferService.buffer.y = 5;
+ bufferService.buffer.x = 40;
+ inputHandler.eraseInDisplay(Params.fromArray([1]));
+ expect(termContent(bufferService, false)).eql([
+ Array(bufferService.cols + 1).join(' '),
+ Array(bufferService.cols + 1).join(' '),
+ Array(bufferService.cols + 1).join(' '),
+ Array(bufferService.cols + 1).join(' '),
+ Array(bufferService.cols + 1).join(' '),
+ Array(41 + 1).join(' ') + Array(bufferService.cols - 41 + 1).join('a'),
+ Array(bufferService.cols + 1).join('a')
+ ]);
+ expect(termContent(bufferService, true)).eql([
+ '',
+ '',
+ '',
+ '',
+ '',
+ Array(41 + 1).join(' ') + Array(bufferService.cols - 41 + 1).join('a'),
+ Array(bufferService.cols + 1).join('a')
+ ]);
+
+ // reset
+ bufferService.buffer.y = 0;
+ bufferService.buffer.x = 0;
+ for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
+
+ // params [2] - whole screen
+ bufferService.buffer.y = 5;
+ bufferService.buffer.x = 40;
+ inputHandler.eraseInDisplay(Params.fromArray([2]));
+ expect(termContent(bufferService, false)).eql([
+ Array(bufferService.cols + 1).join(' '),
+ Array(bufferService.cols + 1).join(' '),
+ Array(bufferService.cols + 1).join(' '),
+ Array(bufferService.cols + 1).join(' '),
+ Array(bufferService.cols + 1).join(' '),
+ Array(bufferService.cols + 1).join(' '),
+ Array(bufferService.cols + 1).join(' ')
+ ]);
+ expect(termContent(bufferService, true)).eql([
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ ''
+ ]);
+
+ // reset and add a wrapped line
+ bufferService.buffer.y = 0;
+ bufferService.buffer.x = 0;
+ inputHandler.parse(Array(bufferService.cols + 1).join('a')); // line 0
+ inputHandler.parse(Array(bufferService.cols + 10).join('a')); // line 1 and 2
+ for (let i = 3; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
+
+ // params[1] left and above with wrap
+ // confirm precondition that line 2 is wrapped
+ expect(bufferService.buffer.lines.get(2)!.isWrapped).true;
+ bufferService.buffer.y = 2;
+ bufferService.buffer.x = 40;
+ inputHandler.eraseInDisplay(Params.fromArray([1]));
+ expect(bufferService.buffer.lines.get(2)!.isWrapped).false;
+
+ // reset and add a wrapped line
+ bufferService.buffer.y = 0;
+ bufferService.buffer.x = 0;
+ inputHandler.parse(Array(bufferService.cols + 1).join('a')); // line 0
+ inputHandler.parse(Array(bufferService.cols + 10).join('a')); // line 1 and 2
+ for (let i = 3; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
+
+ // params[1] left and above with wrap
+ // confirm precondition that line 2 is wrapped
+ expect(bufferService.buffer.lines.get(2)!.isWrapped).true;
+ bufferService.buffer.y = 1;
+ bufferService.buffer.x = 90; // Cursor is beyond last column
+ inputHandler.eraseInDisplay(Params.fromArray([1]));
+ expect(bufferService.buffer.lines.get(2)!.isWrapped).false;
+ });
+ });
+ describe('print', () => {
+ it('should not cause an infinite loop (regression test)', () => {
+ const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
+ const container = new Uint32Array(10);
+ container[0] = 0x200B;
+ inputHandler.print(container, 0, 1);
+ });
+ });
+
+ describe('alt screen', () => {
+ let bufferService: IBufferService;
+ let handler: InputHandler;
+
+ beforeEach(() => {
+ bufferService = new MockBufferService(80, 30);
+ handler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
+ });
+ it('should handle DECSET/DECRST 47 (alt screen buffer)', () => {
+ handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST');
+ expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('');
+ expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
+ // Text color of 'TEST' should be red
+ expect((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor())).to.equal(1);
+ });
+ it('should handle DECSET/DECRST 1047 (alt screen buffer)', () => {
+ handler.parse('\x1b[?1047h\r\n\x1b[31mJUNK\x1b[?1047lTEST');
+ expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('');
+ expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
+ // Text color of 'TEST' should be red
+ expect((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor())).to.equal(1);
+ });
+ it('should handle DECSET/DECRST 1048 (alt screen cursor)', () => {
+ handler.parse('\x1b[?1048h\r\n\x1b[31mJUNK\x1b[?1048lTEST');
+ expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
+ expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('JUNK');
+ // Text color of 'TEST' should be default
+ expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
+ // Text color of 'JUNK' should be red
+ expect((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor())).to.equal(1);
+ });
+ it('should handle DECSET/DECRST 1049 (alt screen buffer+cursor)', () => {
+ handler.parse('\x1b[?1049h\r\n\x1b[31mJUNK\x1b[?1049lTEST');
+ expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
+ expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('');
+ // Text color of 'TEST' should be default
+ expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
+ });
+ it('should handle DECSET/DECRST 1049 - maintains saved cursor for alt buffer', () => {
+ handler.parse('\x1b[?1049h\r\n\x1b[31m\x1b[s\x1b[?1049lTEST');
+ expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
+ // Text color of 'TEST' should be default
+ expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
+ handler.parse('\x1b[?1049h\x1b[uTEST');
+ expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('TEST');
+ // Text color of 'TEST' should be red
+ expect((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor())).to.equal(1);
+ });
+ it('should handle DECSET/DECRST 1049 - clears alt buffer with erase attributes', () => {
+ handler.parse('\x1b[42m\x1b[?1049h');
+ // Buffer should be filled with green background
+ expect(bufferService.buffer.lines.get(20)!.loadCell(10, new CellData()).getBgColor()).to.equal(2);
+ });
+ });
+
+ describe('text attributes', () => {
+ it('bold', () => {
+ inputHandler.parse('\x1b[1m');
+ assert.equal(!!inputHandler.curAttrData.isBold(), true);
+ inputHandler.parse('\x1b[22m');
+ assert.equal(!!inputHandler.curAttrData.isBold(), false);
+ });
+ it('dim', () => {
+ inputHandler.parse('\x1b[2m');
+ assert.equal(!!inputHandler.curAttrData.isDim(), true);
+ inputHandler.parse('\x1b[22m');
+ assert.equal(!!inputHandler.curAttrData.isDim(), false);
+ });
+ it('italic', () => {
+ inputHandler.parse('\x1b[3m');
+ assert.equal(!!inputHandler.curAttrData.isItalic(), true);
+ inputHandler.parse('\x1b[23m');
+ assert.equal(!!inputHandler.curAttrData.isItalic(), false);
+ });
+ it('underline', () => {
+ inputHandler.parse('\x1b[4m');
+ assert.equal(!!inputHandler.curAttrData.isUnderline(), true);
+ inputHandler.parse('\x1b[24m');
+ assert.equal(!!inputHandler.curAttrData.isUnderline(), false);
+ });
+ it('blink', () => {
+ inputHandler.parse('\x1b[5m');
+ assert.equal(!!inputHandler.curAttrData.isBlink(), true);
+ inputHandler.parse('\x1b[25m');
+ assert.equal(!!inputHandler.curAttrData.isBlink(), false);
+ });
+ it('inverse', () => {
+ inputHandler.parse('\x1b[7m');
+ assert.equal(!!inputHandler.curAttrData.isInverse(), true);
+ inputHandler.parse('\x1b[27m');
+ assert.equal(!!inputHandler.curAttrData.isInverse(), false);
+ });
+ it('invisible', () => {
+ inputHandler.parse('\x1b[8m');
+ assert.equal(!!inputHandler.curAttrData.isInvisible(), true);
+ inputHandler.parse('\x1b[28m');
+ assert.equal(!!inputHandler.curAttrData.isInvisible(), false);
+ });
+ it('colormode palette 16', () => {
+ assert.equal(inputHandler.curAttrData.getFgColorMode(), 0); // DEFAULT
+ assert.equal(inputHandler.curAttrData.getBgColorMode(), 0); // DEFAULT
+ // lower 8 colors
+ for (let i = 0; i < 8; ++i) {
+ inputHandler.parse(`\x1b[${i + 30};${i + 40}m`);
+ assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_P16);
+ assert.equal(inputHandler.curAttrData.getFgColor(), i);
+ assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_P16);
+ assert.equal(inputHandler.curAttrData.getBgColor(), i);
+ }
+ // reset to DEFAULT
+ inputHandler.parse(`\x1b[39;49m`);
+ assert.equal(inputHandler.curAttrData.getFgColorMode(), 0);
+ assert.equal(inputHandler.curAttrData.getBgColorMode(), 0);
+ });
+ it('colormode palette 256', () => {
+ assert.equal(inputHandler.curAttrData.getFgColorMode(), 0); // DEFAULT
+ assert.equal(inputHandler.curAttrData.getBgColorMode(), 0); // DEFAULT
+ // lower 8 colors
+ for (let i = 0; i < 256; ++i) {
+ inputHandler.parse(`\x1b[38;5;${i};48;5;${i}m`);
+ assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_P256);
+ assert.equal(inputHandler.curAttrData.getFgColor(), i);
+ assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_P256);
+ assert.equal(inputHandler.curAttrData.getBgColor(), i);
+ }
+ // reset to DEFAULT
+ inputHandler.parse(`\x1b[39;49m`);
+ assert.equal(inputHandler.curAttrData.getFgColorMode(), 0);
+ assert.equal(inputHandler.curAttrData.getFgColor(), -1);
+ assert.equal(inputHandler.curAttrData.getBgColorMode(), 0);
+ assert.equal(inputHandler.curAttrData.getBgColor(), -1);
+ });
+ it('colormode RGB', () => {
+ assert.equal(inputHandler.curAttrData.getFgColorMode(), 0); // DEFAULT
+ assert.equal(inputHandler.curAttrData.getBgColorMode(), 0); // DEFAULT
+ inputHandler.parse(`\x1b[38;2;1;2;3;48;2;4;5;6m`);
+ assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_RGB);
+ assert.equal(inputHandler.curAttrData.getFgColor(), 1 << 16 | 2 << 8 | 3);
+ assert.deepEqual(AttributeData.toColorRGB(inputHandler.curAttrData.getFgColor()), [1, 2, 3]);
+ assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_RGB);
+ assert.deepEqual(AttributeData.toColorRGB(inputHandler.curAttrData.getBgColor()), [4, 5, 6]);
+ // reset to DEFAULT
+ inputHandler.parse(`\x1b[39;49m`);
+ assert.equal(inputHandler.curAttrData.getFgColorMode(), 0);
+ assert.equal(inputHandler.curAttrData.getFgColor(), -1);
+ assert.equal(inputHandler.curAttrData.getBgColorMode(), 0);
+ assert.equal(inputHandler.curAttrData.getBgColor(), -1);
+ });
+ it('colormode transition RGB to 256', () => {
+ // enter RGB for FG and BG
+ inputHandler.parse(`\x1b[38;2;1;2;3;48;2;4;5;6m`);
+ // enter 256 for FG and BG
+ inputHandler.parse(`\x1b[38;5;255;48;5;255m`);
+ assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_P256);
+ assert.equal(inputHandler.curAttrData.getFgColor(), 255);
+ assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_P256);
+ assert.equal(inputHandler.curAttrData.getBgColor(), 255);
+ });
+ it('colormode transition RGB to 16', () => {
+ // enter RGB for FG and BG
+ inputHandler.parse(`\x1b[38;2;1;2;3;48;2;4;5;6m`);
+ // enter 16 for FG and BG
+ inputHandler.parse(`\x1b[37;47m`);
+ assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_P16);
+ assert.equal(inputHandler.curAttrData.getFgColor(), 7);
+ assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_P16);
+ assert.equal(inputHandler.curAttrData.getBgColor(), 7);
+ });
+ it('colormode transition 16 to 256', () => {
+ // enter 16 for FG and BG
+ inputHandler.parse(`\x1b[37;47m`);
+ // enter 256 for FG and BG
+ inputHandler.parse(`\x1b[38;5;255;48;5;255m`);
+ assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_P256);
+ assert.equal(inputHandler.curAttrData.getFgColor(), 255);
+ assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_P256);
+ assert.equal(inputHandler.curAttrData.getBgColor(), 255);
+ });
+ it('colormode transition 256 to 16', () => {
+ // enter 256 for FG and BG
+ inputHandler.parse(`\x1b[38;5;255;48;5;255m`);
+ // enter 16 for FG and BG
+ inputHandler.parse(`\x1b[37;47m`);
+ assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_P16);
+ assert.equal(inputHandler.curAttrData.getFgColor(), 7);
+ assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_P16);
+ assert.equal(inputHandler.curAttrData.getBgColor(), 7);
+ });
+ it('should zero missing RGB values', () => {
+ inputHandler.parse(`\x1b[38;2;1;2;3m`);
+ inputHandler.parse(`\x1b[38;2;5m`);
+ assert.deepEqual(AttributeData.toColorRGB(inputHandler.curAttrData.getFgColor()), [5, 0, 0]);
+ });
+ });
+ describe('colon notation', () => {
+ let inputHandler2: TestInputHandler;
+ beforeEach(() => {
+ inputHandler2 = new TestInputHandler(bufferService, new MockCharsetService(), coreService, new MockDirtyRowService(), new MockLogService(), optionsService, new MockCoreMouseService(), new MockUnicodeService());
+ });
+ describe('should equal to semicolon', () => {
+ it('CSI 38:2::50:100:150 m', () => {
+ inputHandler.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.parse('\x1b[38;2;50;100;150m');
+ inputHandler.parse('\x1b[38:2::50:100:150m');
+ assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 38:2::50:100: m', () => {
+ inputHandler.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.parse('\x1b[38;2;50;100;m');
+ inputHandler.parse('\x1b[38:2::50:100:m');
+ assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 0);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 38:2::50:: m', () => {
+ inputHandler.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.parse('\x1b[38;2;50;;m');
+ inputHandler.parse('\x1b[38:2::50::m');
+ assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 0 << 8 | 0);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 38:2:::: m', () => {
+ inputHandler.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.parse('\x1b[38;2;;;m');
+ inputHandler.parse('\x1b[38:2::::m');
+ assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 0 << 16 | 0 << 8 | 0);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 38;2::50:100:150 m', () => {
+ inputHandler.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.parse('\x1b[38;2;50;100;150m');
+ inputHandler.parse('\x1b[38;2::50:100:150m');
+ assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 38;2;50:100:150 m', () => {
+ inputHandler.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.parse('\x1b[38;2;50;100;150m');
+ inputHandler.parse('\x1b[38;2;50:100:150m');
+ assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 38;2;50;100:150 m', () => {
+ inputHandler.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.parse('\x1b[38;2;50;100;150m');
+ inputHandler.parse('\x1b[38;2;50;100:150m');
+ assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 38:5:50 m', () => {
+ inputHandler.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.parse('\x1b[38;5;50m');
+ inputHandler.parse('\x1b[38:5:50m');
+ assert.equal(inputHandler2.curAttrData.fg & 0xFF, 50);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 38:5: m', () => {
+ inputHandler.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.parse('\x1b[38;5;m');
+ inputHandler.parse('\x1b[38:5:m');
+ assert.equal(inputHandler2.curAttrData.fg & 0xFF, 0);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 38;5:50 m', () => {
+ inputHandler.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.parse('\x1b[38;5;50m');
+ inputHandler.parse('\x1b[38;5:50m');
+ assert.equal(inputHandler2.curAttrData.fg & 0xFF, 50);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ });
+ describe('should fill early sequence end with default of 0', () => {
+ it('CSI 38:2 m', () => {
+ inputHandler.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.parse('\x1b[38;2m');
+ inputHandler.parse('\x1b[38:2m');
+ assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 0 << 16 | 0 << 8 | 0);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 38:5 m', () => {
+ inputHandler.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.curAttrData.fg = 0xFFFFFFFF;
+ inputHandler2.parse('\x1b[38;5m');
+ inputHandler.parse('\x1b[38:5m');
+ assert.equal(inputHandler2.curAttrData.fg & 0xFF, 0);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ });
+ describe('should not interfere with leading/following SGR attrs', () => {
+ it('CSI 1 ; 38:2::50:100:150 ; 4 m', () => {
+ inputHandler2.parse('\x1b[1;38;2;50;100;150;4m');
+ inputHandler.parse('\x1b[1;38:2::50:100:150;4m');
+ assert.equal(!!inputHandler2.curAttrData.isBold(), true);
+ assert.equal(!!inputHandler2.curAttrData.isUnderline(), true);
+ assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 1 ; 38:2::50:100: ; 4 m', () => {
+ inputHandler2.parse('\x1b[1;38;2;50;100;;4m');
+ inputHandler.parse('\x1b[1;38:2::50:100:;4m');
+ assert.equal(!!inputHandler2.curAttrData.isBold(), true);
+ assert.equal(!!inputHandler2.curAttrData.isUnderline(), true);
+ assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 0);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 1 ; 38:2::50:100 ; 4 m', () => {
+ inputHandler2.parse('\x1b[1;38;2;50;100;;4m');
+ inputHandler.parse('\x1b[1;38:2::50:100;4m');
+ assert.equal(!!inputHandler2.curAttrData.isBold(), true);
+ assert.equal(!!inputHandler2.curAttrData.isUnderline(), true);
+ assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 0);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 1 ; 38:2:: ; 4 m', () => {
+ inputHandler2.parse('\x1b[1;38;2;;;;4m');
+ inputHandler.parse('\x1b[1;38:2::;4m');
+ assert.equal(!!inputHandler2.curAttrData.isBold(), true);
+ assert.equal(!!inputHandler2.curAttrData.isUnderline(), true);
+ assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 0);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ it('CSI 1 ; 38;2:: ; 4 m', () => {
+ inputHandler2.parse('\x1b[1;38;2;;;;4m');
+ inputHandler.parse('\x1b[1;38;2::;4m');
+ assert.equal(!!inputHandler2.curAttrData.isBold(), true);
+ assert.equal(!!inputHandler2.curAttrData.isUnderline(), true);
+ assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 0);
+ assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg);
+ });
+ });
+ });
+ describe('cursor positioning', () => {
+ beforeEach(() => {
+ bufferService.resize(10, 10);
+ });
+ it('cursor forward (CUF)', () => {
+ inputHandler.parse('\x1b[C');
+ assert.deepEqual(getCursor(bufferService), [1, 0]);
+ inputHandler.parse('\x1b[1C');
+ assert.deepEqual(getCursor(bufferService), [2, 0]);
+ inputHandler.parse('\x1b[4C');
+ assert.deepEqual(getCursor(bufferService), [6, 0]);
+ inputHandler.parse('\x1b[100C');
+ assert.deepEqual(getCursor(bufferService), [9, 0]);
+ // should not change y
+ bufferService.buffer.x = 8;
+ bufferService.buffer.y = 4;
+ inputHandler.parse('\x1b[C');
+ assert.deepEqual(getCursor(bufferService), [9, 4]);
+ });
+ it('cursor backward (CUB)', () => {
+ inputHandler.parse('\x1b[D');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ inputHandler.parse('\x1b[1D');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ // place cursor at end of first line
+ inputHandler.parse('\x1b[100C');
+ inputHandler.parse('\x1b[D');
+ assert.deepEqual(getCursor(bufferService), [8, 0]);
+ inputHandler.parse('\x1b[1D');
+ assert.deepEqual(getCursor(bufferService), [7, 0]);
+ inputHandler.parse('\x1b[4D');
+ assert.deepEqual(getCursor(bufferService), [3, 0]);
+ inputHandler.parse('\x1b[100D');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ // should not change y
+ bufferService.buffer.x = 4;
+ bufferService.buffer.y = 4;
+ inputHandler.parse('\x1b[D');
+ assert.deepEqual(getCursor(bufferService), [3, 4]);
+ });
+ it('cursor down (CUD)', () => {
+ inputHandler.parse('\x1b[B');
+ assert.deepEqual(getCursor(bufferService), [0, 1]);
+ inputHandler.parse('\x1b[1B');
+ assert.deepEqual(getCursor(bufferService), [0, 2]);
+ inputHandler.parse('\x1b[4B');
+ assert.deepEqual(getCursor(bufferService), [0, 6]);
+ inputHandler.parse('\x1b[100B');
+ assert.deepEqual(getCursor(bufferService), [0, 9]);
+ // should not change x
+ bufferService.buffer.x = 8;
+ bufferService.buffer.y = 0;
+ inputHandler.parse('\x1b[B');
+ assert.deepEqual(getCursor(bufferService), [8, 1]);
+ });
+ it('cursor up (CUU)', () => {
+ inputHandler.parse('\x1b[A');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ inputHandler.parse('\x1b[1A');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ // place cursor at beginning of last row
+ inputHandler.parse('\x1b[100B');
+ inputHandler.parse('\x1b[A');
+ assert.deepEqual(getCursor(bufferService), [0, 8]);
+ inputHandler.parse('\x1b[1A');
+ assert.deepEqual(getCursor(bufferService), [0, 7]);
+ inputHandler.parse('\x1b[4A');
+ assert.deepEqual(getCursor(bufferService), [0, 3]);
+ inputHandler.parse('\x1b[100A');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ // should not change x
+ bufferService.buffer.x = 8;
+ bufferService.buffer.y = 9;
+ inputHandler.parse('\x1b[A');
+ assert.deepEqual(getCursor(bufferService), [8, 8]);
+ });
+ it('cursor next line (CNL)', () => {
+ inputHandler.parse('\x1b[E');
+ assert.deepEqual(getCursor(bufferService), [0, 1]);
+ inputHandler.parse('\x1b[1E');
+ assert.deepEqual(getCursor(bufferService), [0, 2]);
+ inputHandler.parse('\x1b[4E');
+ assert.deepEqual(getCursor(bufferService), [0, 6]);
+ inputHandler.parse('\x1b[100E');
+ assert.deepEqual(getCursor(bufferService), [0, 9]);
+ // should reset x to zero
+ bufferService.buffer.x = 8;
+ bufferService.buffer.y = 0;
+ inputHandler.parse('\x1b[E');
+ assert.deepEqual(getCursor(bufferService), [0, 1]);
+ });
+ it('cursor previous line (CPL)', () => {
+ inputHandler.parse('\x1b[F');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ inputHandler.parse('\x1b[1F');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ // place cursor at beginning of last row
+ inputHandler.parse('\x1b[100E');
+ inputHandler.parse('\x1b[F');
+ assert.deepEqual(getCursor(bufferService), [0, 8]);
+ inputHandler.parse('\x1b[1F');
+ assert.deepEqual(getCursor(bufferService), [0, 7]);
+ inputHandler.parse('\x1b[4F');
+ assert.deepEqual(getCursor(bufferService), [0, 3]);
+ inputHandler.parse('\x1b[100F');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ // should reset x to zero
+ bufferService.buffer.x = 8;
+ bufferService.buffer.y = 9;
+ inputHandler.parse('\x1b[F');
+ assert.deepEqual(getCursor(bufferService), [0, 8]);
+ });
+ it('cursor character absolute (CHA)', () => {
+ inputHandler.parse('\x1b[G');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ inputHandler.parse('\x1b[1G');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ inputHandler.parse('\x1b[2G');
+ assert.deepEqual(getCursor(bufferService), [1, 0]);
+ inputHandler.parse('\x1b[5G');
+ assert.deepEqual(getCursor(bufferService), [4, 0]);
+ inputHandler.parse('\x1b[100G');
+ assert.deepEqual(getCursor(bufferService), [9, 0]);
+ });
+ it('cursor position (CUP)', () => {
+ bufferService.buffer.x = 5;
+ bufferService.buffer.y = 5;
+ inputHandler.parse('\x1b[H');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ bufferService.buffer.x = 5;
+ bufferService.buffer.y = 5;
+ inputHandler.parse('\x1b[1H');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ bufferService.buffer.x = 5;
+ bufferService.buffer.y = 5;
+ inputHandler.parse('\x1b[1;1H');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ bufferService.buffer.x = 5;
+ bufferService.buffer.y = 5;
+ inputHandler.parse('\x1b[8H');
+ assert.deepEqual(getCursor(bufferService), [0, 7]);
+ bufferService.buffer.x = 5;
+ bufferService.buffer.y = 5;
+ inputHandler.parse('\x1b[;8H');
+ assert.deepEqual(getCursor(bufferService), [7, 0]);
+ bufferService.buffer.x = 5;
+ bufferService.buffer.y = 5;
+ inputHandler.parse('\x1b[100;100H');
+ assert.deepEqual(getCursor(bufferService), [9, 9]);
+ });
+ it('horizontal position absolute (HPA)', () => {
+ inputHandler.parse('\x1b[`');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ inputHandler.parse('\x1b[1`');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ inputHandler.parse('\x1b[2`');
+ assert.deepEqual(getCursor(bufferService), [1, 0]);
+ inputHandler.parse('\x1b[5`');
+ assert.deepEqual(getCursor(bufferService), [4, 0]);
+ inputHandler.parse('\x1b[100`');
+ assert.deepEqual(getCursor(bufferService), [9, 0]);
+ });
+ it('horizontal position relative (HPR)', () => {
+ inputHandler.parse('\x1b[a');
+ assert.deepEqual(getCursor(bufferService), [1, 0]);
+ inputHandler.parse('\x1b[1a');
+ assert.deepEqual(getCursor(bufferService), [2, 0]);
+ inputHandler.parse('\x1b[4a');
+ assert.deepEqual(getCursor(bufferService), [6, 0]);
+ inputHandler.parse('\x1b[100a');
+ assert.deepEqual(getCursor(bufferService), [9, 0]);
+ // should not change y
+ bufferService.buffer.x = 8;
+ bufferService.buffer.y = 4;
+ inputHandler.parse('\x1b[a');
+ assert.deepEqual(getCursor(bufferService), [9, 4]);
+ });
+ it('vertical position absolute (VPA)', () => {
+ inputHandler.parse('\x1b[d');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ inputHandler.parse('\x1b[1d');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ inputHandler.parse('\x1b[2d');
+ assert.deepEqual(getCursor(bufferService), [0, 1]);
+ inputHandler.parse('\x1b[5d');
+ assert.deepEqual(getCursor(bufferService), [0, 4]);
+ inputHandler.parse('\x1b[100d');
+ assert.deepEqual(getCursor(bufferService), [0, 9]);
+ // should not change x
+ bufferService.buffer.x = 8;
+ bufferService.buffer.y = 4;
+ inputHandler.parse('\x1b[d');
+ assert.deepEqual(getCursor(bufferService), [8, 0]);
+ });
+ it('vertical position relative (VPR)', () => {
+ inputHandler.parse('\x1b[e');
+ assert.deepEqual(getCursor(bufferService), [0, 1]);
+ inputHandler.parse('\x1b[1e');
+ assert.deepEqual(getCursor(bufferService), [0, 2]);
+ inputHandler.parse('\x1b[4e');
+ assert.deepEqual(getCursor(bufferService), [0, 6]);
+ inputHandler.parse('\x1b[100e');
+ assert.deepEqual(getCursor(bufferService), [0, 9]);
+ // should not change x
+ bufferService.buffer.x = 8;
+ bufferService.buffer.y = 4;
+ inputHandler.parse('\x1b[e');
+ assert.deepEqual(getCursor(bufferService), [8, 5]);
+ });
+ describe('should clamp cursor into addressible range', () => {
+ it('CUF', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[C');
+ assert.deepEqual(getCursor(bufferService), [9, 9]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[C');
+ assert.deepEqual(getCursor(bufferService), [1, 0]);
+ });
+ it('CUB', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[D');
+ assert.deepEqual(getCursor(bufferService), [8, 9]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[D');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ });
+ it('CUD', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[B');
+ assert.deepEqual(getCursor(bufferService), [9, 9]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[B');
+ assert.deepEqual(getCursor(bufferService), [0, 1]);
+ });
+ it('CUU', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[A');
+ assert.deepEqual(getCursor(bufferService), [9, 8]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[A');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ });
+ it('CNL', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[E');
+ assert.deepEqual(getCursor(bufferService), [0, 9]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[E');
+ assert.deepEqual(getCursor(bufferService), [0, 1]);
+ });
+ it('CPL', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[F');
+ assert.deepEqual(getCursor(bufferService), [0, 8]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[F');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ });
+ it('CHA', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[5G');
+ assert.deepEqual(getCursor(bufferService), [4, 9]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[5G');
+ assert.deepEqual(getCursor(bufferService), [4, 0]);
+ });
+ it('CUP', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[5;5H');
+ assert.deepEqual(getCursor(bufferService), [4, 4]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[5;5H');
+ assert.deepEqual(getCursor(bufferService), [4, 4]);
+ });
+ it('HPA', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[5`');
+ assert.deepEqual(getCursor(bufferService), [4, 9]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[5`');
+ assert.deepEqual(getCursor(bufferService), [4, 0]);
+ });
+ it('HPR', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[a');
+ assert.deepEqual(getCursor(bufferService), [9, 9]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[a');
+ assert.deepEqual(getCursor(bufferService), [1, 0]);
+ });
+ it('VPA', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[5d');
+ assert.deepEqual(getCursor(bufferService), [9, 4]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[5d');
+ assert.deepEqual(getCursor(bufferService), [0, 4]);
+ });
+ it('VPR', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[e');
+ assert.deepEqual(getCursor(bufferService), [9, 9]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[e');
+ assert.deepEqual(getCursor(bufferService), [0, 1]);
+ });
+ it('DCH', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[P');
+ assert.deepEqual(getCursor(bufferService), [9, 9]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[P');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ });
+ it('DCH - should delete last cell', () => {
+ inputHandler.parse('0123456789\x1b[P');
+ assert.equal(bufferService.buffer.lines.get(0)!.translateToString(false), '012345678 ');
+ });
+ it('ECH', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[X');
+ assert.deepEqual(getCursor(bufferService), [9, 9]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[X');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ });
+ it('ECH - should delete last cell', () => {
+ inputHandler.parse('0123456789\x1b[X');
+ assert.equal(bufferService.buffer.lines.get(0)!.translateToString(false), '012345678 ');
+ });
+ it('ICH', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[@');
+ assert.deepEqual(getCursor(bufferService), [9, 9]);
+ bufferService.buffer.x = -10000;
+ bufferService.buffer.y = -10000;
+ inputHandler.parse('\x1b[@');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ });
+ it('ICH - should delete last cell', () => {
+ inputHandler.parse('0123456789\x1b[@');
+ assert.equal(bufferService.buffer.lines.get(0)!.translateToString(false), '012345678 ');
+ });
+ });
+ });
+ describe('DECSTBM - scroll margins', () => {
+ beforeEach(() => {
+ bufferService.resize(10, 10);
+ });
+ it('should default to whole viewport', () => {
+ inputHandler.parse('\x1b[r');
+ assert.equal(bufferService.buffer.scrollTop, 0);
+ assert.equal(bufferService.buffer.scrollBottom, 9);
+ inputHandler.parse('\x1b[3;7r');
+ assert.equal(bufferService.buffer.scrollTop, 2);
+ assert.equal(bufferService.buffer.scrollBottom, 6);
+ inputHandler.parse('\x1b[0;0r');
+ assert.equal(bufferService.buffer.scrollTop, 0);
+ assert.equal(bufferService.buffer.scrollBottom, 9);
+ });
+ it('should clamp bottom', () => {
+ inputHandler.parse('\x1b[3;1000r');
+ assert.equal(bufferService.buffer.scrollTop, 2);
+ assert.equal(bufferService.buffer.scrollBottom, 9);
+ });
+ it('should only apply for top < bottom', () => {
+ inputHandler.parse('\x1b[7;2r');
+ assert.equal(bufferService.buffer.scrollTop, 0);
+ assert.equal(bufferService.buffer.scrollBottom, 9);
+ });
+ it('should home cursor', () => {
+ bufferService.buffer.x = 10000;
+ bufferService.buffer.y = 10000;
+ inputHandler.parse('\x1b[2;7r');
+ assert.deepEqual(getCursor(bufferService), [0, 0]);
+ });
+ });
+ describe('scroll margins', () => {
+ beforeEach(() => {
+ bufferService.resize(10, 10);
+ });
+ it('scrollUp', () => {
+ inputHandler.parse('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Sm');
+ assert.deepEqual(getLines(bufferService), ['m', '3', '', '', '4', '5', '6', '7', '8', '9']);
+ });
+ it('scrollDown', () => {
+ inputHandler.parse('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Tm');
+ assert.deepEqual(getLines(bufferService), ['m', '', '', '1', '4', '5', '6', '7', '8', '9']);
+ });
+ it('insertLines - out of margins', () => {
+ inputHandler.parse('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r');
+ assert.equal(bufferService.buffer.scrollTop, 2);
+ assert.equal(bufferService.buffer.scrollBottom, 5);
+ inputHandler.parse('\x1b[2Lm');
+ assert.deepEqual(getLines(bufferService), ['m', '1', '2', '3', '4', '5', '6', '7', '8', '9']);
+ inputHandler.parse('\x1b[2H\x1b[2Ln');
+ assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', '6', '7', '8', '9']);
+ // skip below scrollbottom
+ inputHandler.parse('\x1b[7H\x1b[2Lo');
+ assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', 'o', '7', '8', '9']);
+ inputHandler.parse('\x1b[8H\x1b[2Lp');
+ assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', '9']);
+ inputHandler.parse('\x1b[100H\x1b[2Lq');
+ assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', 'q']);
+ });
+ it('insertLines - within margins', () => {
+ inputHandler.parse('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r');
+ assert.equal(bufferService.buffer.scrollTop, 2);
+ assert.equal(bufferService.buffer.scrollBottom, 5);
+ inputHandler.parse('\x1b[3H\x1b[2Lm');
+ assert.deepEqual(getLines(bufferService), ['0', '1', 'm', '', '2', '3', '6', '7', '8', '9']);
+ inputHandler.parse('\x1b[6H\x1b[2Ln');
+ assert.deepEqual(getLines(bufferService), ['0', '1', 'm', '', '2', 'n', '6', '7', '8', '9']);
+ });
+ it('deleteLines - out of margins', () => {
+ inputHandler.parse('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r');
+ assert.equal(bufferService.buffer.scrollTop, 2);
+ assert.equal(bufferService.buffer.scrollBottom, 5);
+ inputHandler.parse('\x1b[2Mm');
+ assert.deepEqual(getLines(bufferService), ['m', '1', '2', '3', '4', '5', '6', '7', '8', '9']);
+ inputHandler.parse('\x1b[2H\x1b[2Mn');
+ assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', '6', '7', '8', '9']);
+ // skip below scrollbottom
+ inputHandler.parse('\x1b[7H\x1b[2Mo');
+ assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', 'o', '7', '8', '9']);
+ inputHandler.parse('\x1b[8H\x1b[2Mp');
+ assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', '9']);
+ inputHandler.parse('\x1b[100H\x1b[2Mq');
+ assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', 'q']);
+ });
+ it('deleteLines - within margins', () => {
+ inputHandler.parse('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r');
+ assert.equal(bufferService.buffer.scrollTop, 2);
+ assert.equal(bufferService.buffer.scrollBottom, 5);
+ inputHandler.parse('\x1b[6H\x1b[2Mm');
+ assert.deepEqual(getLines(bufferService), ['0', '1', '2', '3', '4', 'm', '6', '7', '8', '9']);
+ inputHandler.parse('\x1b[3H\x1b[2Mn');
+ assert.deepEqual(getLines(bufferService), ['0', '1', 'n', 'm', '', '', '6', '7', '8', '9']);
+ });
+ });
+ it('should parse big chunks in smaller subchunks', () => {
+ // max single chunk size is hardcoded as 131072
+ const calls: any[] = [];
+ bufferService.resize(10, 10);
+ (inputHandler as any)._parser.parse = (data: Uint32Array, length: number) => {
+ calls.push([data.length, length]);
+ };
+ inputHandler.parse('12345');
+ inputHandler.parse('a'.repeat(10000));
+ inputHandler.parse('a'.repeat(200000));
+ inputHandler.parse('a'.repeat(300000));
+ assert.deepEqual(calls, [
+ [4096, 5],
+ [10000, 10000],
+ [131072, 131072], [131072, 200000 - 131072],
+ [131072, 131072], [131072, 131072], [131072, 300000 - 131072 - 131072]
+ ]);
+ });
+ describe('windowOptions', () => {
+ it('all should be disabled by default and not report', () => {
+ bufferService.resize(10, 10);
+ const stack: string[] = [];
+ coreService.onData(data => stack.push(data));
+ inputHandler.parse('\x1b[14t');
+ inputHandler.parse('\x1b[16t');
+ inputHandler.parse('\x1b[18t');
+ inputHandler.parse('\x1b[20t');
+ inputHandler.parse('\x1b[21t');
+ assert.deepEqual(stack, []);
+ });
+ it('14 - GetWinSizePixels', () => {
+ bufferService.resize(10, 10);
+ optionsService.options.windowOptions.getWinSizePixels = true;
+ const stack: string[] = [];
+ coreService.onData(data => stack.push(data));
+ inputHandler.parse('\x1b[14t');
+ // does not report in test terminal due to missing renderer
+ assert.deepEqual(stack, []);
+ });
+ it('16 - GetCellSizePixels', () => {
+ bufferService.resize(10, 10);
+ optionsService.options.windowOptions.getCellSizePixels = true;
+ const stack: string[] = [];
+ coreService.onData(data => stack.push(data));
+ inputHandler.parse('\x1b[16t');
+ // does not report in test terminal due to missing renderer
+ assert.deepEqual(stack, []);
+ });
+ it('18 - GetWinSizeChars', () => {
+ bufferService.resize(10, 10);
+ optionsService.options.windowOptions.getWinSizeChars = true;
+ const stack: string[] = [];
+ coreService.onData(data => stack.push(data));
+ inputHandler.parse('\x1b[18t');
+ assert.deepEqual(stack, ['\x1b[8;10;10t']);
+ bufferService.resize(50, 20);
+ inputHandler.parse('\x1b[18t');
+ assert.deepEqual(stack, ['\x1b[8;10;10t', '\x1b[8;20;50t']);
+ });
+ it('22/23 - PushTitle/PopTitle', () => {
+ bufferService.resize(10, 10);
+ optionsService.options.windowOptions.pushTitle = true;
+ optionsService.options.windowOptions.popTitle = true;
+ const stack: string[] = [];
+ inputHandler.onTitleChange(data => stack.push(data));
+ inputHandler.parse('\x1b]0;1\x07');
+ inputHandler.parse('\x1b[22t');
+ inputHandler.parse('\x1b]0;2\x07');
+ inputHandler.parse('\x1b[22t');
+ inputHandler.parse('\x1b]0;3\x07');
+ inputHandler.parse('\x1b[22t');
+ assert.deepEqual(inputHandler.windowTitleStack, ['1', '2', '3']);
+ assert.deepEqual(inputHandler.iconNameStack, ['1', '2', '3']);
+ assert.deepEqual(stack, ['1', '2', '3']);
+ inputHandler.parse('\x1b[23t');
+ inputHandler.parse('\x1b[23t');
+ inputHandler.parse('\x1b[23t');
+ inputHandler.parse('\x1b[23t'); // one more to test "overflow"
+ assert.deepEqual(inputHandler.windowTitleStack, []);
+ assert.deepEqual(inputHandler.iconNameStack, []);
+ assert.deepEqual(stack, ['1', '2', '3', '3', '2', '1']);
+ });
+ it('22/23 - PushTitle/PopTitle with ;1', () => {
+ bufferService.resize(10, 10);
+ optionsService.options.windowOptions.pushTitle = true;
+ optionsService.options.windowOptions.popTitle = true;
+ const stack: string[] = [];
+ inputHandler.onTitleChange(data => stack.push(data));
+ inputHandler.parse('\x1b]0;1\x07');
+ inputHandler.parse('\x1b[22;1t');
+ inputHandler.parse('\x1b]0;2\x07');
+ inputHandler.parse('\x1b[22;1t');
+ inputHandler.parse('\x1b]0;3\x07');
+ inputHandler.parse('\x1b[22;1t');
+ assert.deepEqual(inputHandler.windowTitleStack, []);
+ assert.deepEqual(inputHandler.iconNameStack, ['1', '2', '3']);
+ assert.deepEqual(stack, ['1', '2', '3']);
+ inputHandler.parse('\x1b[23;1t');
+ inputHandler.parse('\x1b[23;1t');
+ inputHandler.parse('\x1b[23;1t');
+ inputHandler.parse('\x1b[23;1t'); // one more to test "overflow"
+ assert.deepEqual(inputHandler.windowTitleStack, []);
+ assert.deepEqual(inputHandler.iconNameStack, []);
+ assert.deepEqual(stack, ['1', '2', '3']);
+ });
+ it('22/23 - PushTitle/PopTitle with ;2', () => {
+ bufferService.resize(10, 10);
+ optionsService.options.windowOptions.pushTitle = true;
+ optionsService.options.windowOptions.popTitle = true;
+ const stack: string[] = [];
+ inputHandler.onTitleChange(data => stack.push(data));
+ inputHandler.parse('\x1b]0;1\x07');
+ inputHandler.parse('\x1b[22;2t');
+ inputHandler.parse('\x1b]0;2\x07');
+ inputHandler.parse('\x1b[22;2t');
+ inputHandler.parse('\x1b]0;3\x07');
+ inputHandler.parse('\x1b[22;2t');
+ assert.deepEqual(inputHandler.windowTitleStack, ['1', '2', '3']);
+ assert.deepEqual(inputHandler.iconNameStack, []);
+ assert.deepEqual(stack, ['1', '2', '3']);
+ inputHandler.parse('\x1b[23;2t');
+ inputHandler.parse('\x1b[23;2t');
+ inputHandler.parse('\x1b[23;2t');
+ inputHandler.parse('\x1b[23;2t'); // one more to test "overflow"
+ assert.deepEqual(inputHandler.windowTitleStack, []);
+ assert.deepEqual(inputHandler.iconNameStack, []);
+ assert.deepEqual(stack, ['1', '2', '3', '3', '2', '1']);
+ });
+ it('DECCOLM - should only work with "SetWinLines" (24) enabled', () => {
+ // disabled
+ bufferService.resize(10, 10);
+ inputHandler.parse('\x1b[?3l');
+ assert.equal(bufferService.cols, 10);
+ inputHandler.parse('\x1b[?3h');
+ assert.equal(bufferService.cols, 10);
+ // enabled
+ inputHandler.reset();
+ optionsService.options.windowOptions.setWinLines = true;
+ inputHandler.parse('\x1b[?3l');
+ assert.equal(bufferService.cols, 80);
+ inputHandler.parse('\x1b[?3h');
+ assert.equal(bufferService.cols, 132);
+ });
+ });
+ describe('should correctly reset cells taken by wide chars', () => {
+ beforeEach(() => {
+ bufferService.resize(10, 5);
+ optionsService.options.scrollback = 1;
+ inputHandler.parse('¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥');
+ });
+ it('print', () => {
+ inputHandler.parse('\x1b[H#');
+ assert.deepEqual(getLines(bufferService), ['# ¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
+ inputHandler.parse('\x1b[1;6H######');
+ assert.deepEqual(getLines(bufferService), ['# ¥ #####', '# ¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
+ inputHandler.parse('#');
+ assert.deepEqual(getLines(bufferService), ['# ¥ #####', '##¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
+ inputHandler.parse('#');
+ assert.deepEqual(getLines(bufferService), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
+ inputHandler.parse('\x1b[3;9H#');
+ assert.deepEqual(getLines(bufferService), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥#', '¥¥¥¥¥', '']);
+ inputHandler.parse('#');
+ assert.deepEqual(getLines(bufferService), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '¥¥¥¥¥', '']);
+ inputHandler.parse('#');
+ assert.deepEqual(getLines(bufferService), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '# ¥¥¥¥', '']);
+ inputHandler.parse('\x1b[4;10H#');
+ assert.deepEqual(getLines(bufferService), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '# ¥¥¥ #', '']);
+ });
+ it('EL', () => {
+ inputHandler.parse('\x1b[1;6H\x1b[K#');
+ assert.deepEqual(getLines(bufferService), ['¥¥ #', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
+ inputHandler.parse('\x1b[2;5H\x1b[1K');
+ assert.deepEqual(getLines(bufferService), ['¥¥ #', ' ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
+ inputHandler.parse('\x1b[3;6H\x1b[1K');
+ assert.deepEqual(getLines(bufferService), ['¥¥ #', ' ¥¥', ' ¥¥', '¥¥¥¥¥', '']);
+ });
+ it('ICH', () => {
+ inputHandler.parse('\x1b[1;6H\x1b[@');
+ assert.deepEqual(getLines(bufferService), ['¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
+ inputHandler.parse('\x1b[2;4H\x1b[2@');
+ assert.deepEqual(getLines(bufferService), ['¥¥ ¥', '¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
+ inputHandler.parse('\x1b[3;4H\x1b[3@');
+ assert.deepEqual(getLines(bufferService), ['¥¥ ¥', '¥ ¥¥', '¥ ¥', '¥¥¥¥¥', '']);
+ inputHandler.parse('\x1b[4;4H\x1b[4@');
+ assert.deepEqual(getLines(bufferService), ['¥¥ ¥', '¥ ¥¥', '¥ ¥', '¥ ¥', '']);
+ });
+ it('DCH', () => {
+ inputHandler.parse('\x1b[1;6H\x1b[P');
+ assert.deepEqual(getLines(bufferService), ['¥¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
+ inputHandler.parse('\x1b[2;6H\x1b[2P');
+ assert.deepEqual(getLines(bufferService), ['¥¥ ¥¥', '¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
+ inputHandler.parse('\x1b[3;6H\x1b[3P');
+ assert.deepEqual(getLines(bufferService), ['¥¥ ¥¥', '¥¥ ¥', '¥¥ ¥', '¥¥¥¥¥', '']);
+ });
+ it('ECH', () => {
+ inputHandler.parse('\x1b[1;6H\x1b[X');
+ assert.deepEqual(getLines(bufferService), ['¥¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
+ inputHandler.parse('\x1b[2;6H\x1b[2X');
+ assert.deepEqual(getLines(bufferService), ['¥¥ ¥¥', '¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '']);
+ inputHandler.parse('\x1b[3;6H\x1b[3X');
+ assert.deepEqual(getLines(bufferService), ['¥¥ ¥¥', '¥¥ ¥', '¥¥ ¥', '¥¥¥¥¥', '']);
+ });
+ });
+ describe('DECSTR', () => {
+ beforeEach(() => {
+ bufferService.resize(10, 5);
+ optionsService.options.scrollback = 1;
+ inputHandler.parse('01234567890123');
+ });
+ it('should reset IRM', () => {
+ inputHandler.parse('\x1b[4h');
+ assert.equal(coreService.modes.insertMode, true);
+ inputHandler.parse('\x1b[!p');
+ assert.equal(coreService.modes.insertMode, false);
+ });
+ it('should reset cursor visibility', () => {
+ inputHandler.parse('\x1b[?25l');
+ assert.equal(coreService.isCursorHidden, true);
+ inputHandler.parse('\x1b[!p');
+ assert.equal(coreService.isCursorHidden, false);
+ });
+ it('should reset scroll margins', () => {
+ inputHandler.parse('\x1b[2;4r');
+ assert.equal(bufferService.buffer.scrollTop, 1);
+ assert.equal(bufferService.buffer.scrollBottom, 3);
+ inputHandler.parse('\x1b[!p');
+ assert.equal(bufferService.buffer.scrollTop, 0);
+ assert.equal(bufferService.buffer.scrollBottom, bufferService.rows - 1);
+ });
+ it('should reset text attributes', () => {
+ inputHandler.parse('\x1b[1;2;32;43m');
+ assert.equal(!!inputHandler.curAttrData.isBold(), true);
+ inputHandler.parse('\x1b[!p');
+ assert.equal(!!inputHandler.curAttrData.isBold(), false);
+ assert.equal(inputHandler.curAttrData.fg, 0);
+ assert.equal(inputHandler.curAttrData.bg, 0);
+ });
+ it('should reset DECSC data', () => {
+ inputHandler.parse('\x1b7');
+ assert.equal(bufferService.buffer.savedX, 4);
+ assert.equal(bufferService.buffer.savedY, 1);
+ inputHandler.parse('\x1b[!p');
+ assert.equal(bufferService.buffer.savedX, 0);
+ assert.equal(bufferService.buffer.savedY, 0);
+ });
+ it('should reset DECOM', () => {
+ inputHandler.parse('\x1b[?6h');
+ assert.equal(coreService.decPrivateModes.origin, true);
+ inputHandler.parse('\x1b[!p');
+ assert.equal(coreService.decPrivateModes.origin, false);
+ });
+ });
+});
diff --git a/src/InputHandler.ts b/src/common/InputHandler.ts
similarity index 96%
rename from src/InputHandler.ts
rename to src/common/InputHandler.ts
index 3717256e..1ec2a68d 100644
--- a/src/InputHandler.ts
+++ b/src/common/InputHandler.ts
@@ -4,7 +4,7 @@
* @license MIT
*/
-import { IInputHandler, IInputHandlingTerminal } from './Types';
+import { IInputHandler, IAttributeData, IDisposable, IWindowOptions } from 'common/Types';
import { C0, C1 } from 'common/data/EscapeSequences';
import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets';
import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser';
@@ -17,11 +17,9 @@ import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IFunctionId
import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content } from 'common/buffer/Constants';
import { CellData } from 'common/buffer/CellData';
import { AttributeData } from 'common/buffer/AttributeData';
-import { IAttributeData, IDisposable, IWindowOptions } from 'common/Types';
-import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService, ICharsetService, IUnicodeService, IInstantiationService } from 'common/services/Services';
+import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService, ICharsetService, IUnicodeService } from 'common/services/Services';
import { OscHandler } from 'common/parser/OscParser';
import { DcsHandler } from 'common/parser/DcsParser';
-import { IRenderService } from 'browser/services/Services';
/**
* Map collect to glevel. Used in `selectCharset`.
@@ -94,7 +92,10 @@ function paramToWindowOption(n: number, opts: IWindowOptions): boolean {
return false;
}
-
+export enum WindowsOptionsReportType {
+ GET_WIN_SIZE_PIXELS = 0,
+ GET_CELL_SIZE_PIXELS = 1
+}
/**
* DCS subparser implementations
@@ -218,27 +219,39 @@ export class InputHandler extends Disposable implements IInputHandler {
private _workCell: CellData = new CellData();
private _windowTitle = '';
private _iconName = '';
- private _windowTitleStack: string[] = [];
- private _iconNameStack: string[] = [];
+ protected _windowTitleStack: string[] = [];
+ protected _iconNameStack: string[] = [];
private _curAttrData: IAttributeData = DEFAULT_ATTR_DATA.clone();
private _eraseAttrDataInternal: IAttributeData = DEFAULT_ATTR_DATA.clone();
+ private _onRequestBell = new EventEmitter();
+ public get onRequestBell(): IEvent { return this._onRequestBell.event; }
private _onRequestRefreshRows = new EventEmitter();
public get onRequestRefreshRows(): IEvent { return this._onRequestRefreshRows.event; }
private _onRequestReset = new EventEmitter();
public get onRequestReset(): IEvent { return this._onRequestReset.event; }
- private _onRequestBell = new EventEmitter();
- public get onRequestBell(): IEvent { return this._onRequestBell.event; }
+ private _onRequestScroll = new EventEmitter();
+ public get onRequestScroll(): IEvent { return this._onRequestScroll.event; }
+ private _onRequestSyncScrollBar = new EventEmitter();
+ public get onRequestSyncScrollBar(): IEvent { return this._onRequestSyncScrollBar.event; }
+ private _onRequestWindowsOptionsReport = new EventEmitter();
+ public get onRequestWindowsOptionsReport(): IEvent { return this._onRequestWindowsOptionsReport.event; }
+
+ private _onA11yChar = new EventEmitter();
+ public get onA11yChar(): IEvent { return this._onA11yChar.event; }
+ private _onA11yTab = new EventEmitter();
+ public get onA11yTab(): IEvent { return this._onA11yTab.event; }
private _onCursorMove = new EventEmitter();
public get onCursorMove(): IEvent { return this._onCursorMove.event; }
private _onLineFeed = new EventEmitter();
public get onLineFeed(): IEvent { return this._onLineFeed.event; }
private _onScroll = new EventEmitter();
public get onScroll(): IEvent { return this._onScroll.event; }
+ private _onTitleChange = new EventEmitter();
+ public get onTitleChange(): IEvent { return this._onTitleChange.event; }
constructor(
- private _terminal: IInputHandlingTerminal,
private readonly _bufferService: IBufferService,
private readonly _charsetService: ICharsetService,
private readonly _coreService: ICoreService,
@@ -247,9 +260,8 @@ export class InputHandler extends Disposable implements IInputHandler {
private readonly _optionsService: IOptionsService,
private readonly _coreMouseService: ICoreMouseService,
private readonly _unicodeService: IUnicodeService,
- private readonly _instantiationService: IInstantiationService,
- private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser())
- {
+ private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser()
+ ) {
super();
this.register(this._parser);
@@ -489,9 +501,9 @@ export class InputHandler extends Disposable implements IInputHandler {
const screenReaderMode = this._optionsService.options.screenReaderMode;
const cols = this._bufferService.cols;
const wraparoundMode = this._coreService.decPrivateModes.wraparound;
- const insertMode = this._terminal.insertMode;
+ const insertMode = this._coreService.modes.insertMode;
const curAttr = this._curAttrData;
- let bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
+ let bufferRow = buffer.lines.get(buffer.ybase + buffer.y)!;
this._dirtyRowService.markDirty(buffer.y);
@@ -518,7 +530,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
if (screenReaderMode) {
- this._terminal.onA11yCharEmitter.fire(stringFromCodePoint(code));
+ this._onA11yChar.fire(stringFromCodePoint(code));
}
// insert combining char at last cursor position
@@ -548,17 +560,17 @@ export class InputHandler extends Disposable implements IInputHandler {
buffer.y++;
if (buffer.y === buffer.scrollBottom + 1) {
buffer.y--;
- this._terminal.scroll(this._eraseAttrData(), true);
+ this._onRequestScroll.fire(this._eraseAttrData(), true);
} else {
if (buffer.y >= this._bufferService.rows) {
buffer.y = this._bufferService.rows - 1;
}
// The line already exists (eg. the initial viewport), mark it as a
// wrapped line
- buffer.lines.get(buffer.y).isWrapped = true;
+ buffer.lines.get(buffer.ybase + buffer.y)!.isWrapped = true;
}
// row changed, get it again
- bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
+ bufferRow = buffer.lines.get(buffer.ybase + buffer.y)!;
} else {
buffer.x = cols - 1;
if (chWidth === 2) {
@@ -687,7 +699,7 @@ export class InputHandler extends Disposable implements IInputHandler {
buffer.y++;
if (buffer.y === buffer.scrollBottom + 1) {
buffer.y--;
- this._terminal.scroll(this._eraseAttrData());
+ this._onRequestScroll.fire(this._eraseAttrData());
} else if (buffer.y >= this._bufferService.rows) {
buffer.y = this._bufferService.rows - 1;
}
@@ -736,7 +748,7 @@ export class InputHandler extends Disposable implements IInputHandler {
const originalX = this._bufferService.buffer.x;
this._bufferService.buffer.x = this._bufferService.buffer.nextStop();
if (this._optionsService.options.screenReaderMode) {
- this._terminal.onA11yTabEmitter.fire(this._bufferService.buffer.x - originalX);
+ this._onA11yTab.fire(this._bufferService.buffer.x - originalX);
}
}
@@ -1024,7 +1036,7 @@ 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._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y)!;
line.replaceCells(
start,
end,
@@ -1042,7 +1054,7 @@ 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);
+ const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y)!;
line.fill(this._bufferService.buffer.getNullCell(this._eraseAttrData()));
line.isWrapped = false;
}
@@ -1091,7 +1103,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._eraseInBufferLine(j, 0, this._bufferService.buffer.x + 1, true);
if (this._bufferService.buffer.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._bufferService.buffer.lines.get(j + 1)!.isWrapped = false;
}
while (j--) {
this._resetBufferLine(j);
@@ -1178,7 +1190,7 @@ export class InputHandler extends Disposable implements IInputHandler {
return;
}
- const row: number = buffer.y + buffer.ybase;
+ const row: number = buffer.ybase + buffer.y;
const scrollBottomRowsOffset = this._bufferService.rows - 1 - buffer.scrollBottom;
const scrollBottomAbsolute = this._bufferService.rows - 1 + buffer.ybase - scrollBottomRowsOffset + 1;
@@ -1213,7 +1225,7 @@ export class InputHandler extends Disposable implements IInputHandler {
return;
}
- const row: number = buffer.y + buffer.ybase;
+ const row: number = buffer.ybase + buffer.y;
let j: number;
j = this._bufferService.rows - 1 - buffer.scrollBottom;
@@ -1242,7 +1254,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public insertChars(params: IParams): void {
this._restrictCursor();
- const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase);
+ const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + this._bufferService.buffer.y);
if (line) {
line.insertCells(
this._bufferService.buffer.x,
@@ -1267,7 +1279,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public deleteChars(params: IParams): void {
this._restrictCursor();
- const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase);
+ const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + this._bufferService.buffer.y);
if (line) {
line.deleteCells(
this._bufferService.buffer.x,
@@ -1343,7 +1355,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
const param = params.params[0] || 1;
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
- const line = buffer.lines.get(buffer.ybase + y);
+ const line = buffer.lines.get(buffer.ybase + y)!;
line.deleteCells(0, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
line.isWrapped = false;
}
@@ -1376,7 +1388,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
const param = params.params[0] || 1;
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
- const line = buffer.lines.get(buffer.ybase + y);
+ const line = buffer.lines.get(buffer.ybase + y)!;
line.insertCells(0, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
line.isWrapped = false;
}
@@ -1399,7 +1411,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
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);
+ const line = this._bufferService.buffer.lines.get(buffer.ybase + y)!;
line.insertCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
line.isWrapped = false;
}
@@ -1422,7 +1434,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
const param = params.params[0] || 1;
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
- const line = buffer.lines.get(buffer.ybase + y);
+ const line = buffer.lines.get(buffer.ybase + y)!;
line.deleteCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
line.isWrapped = false;
}
@@ -1439,7 +1451,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public eraseChars(params: IParams): void {
this._restrictCursor();
- const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase);
+ const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + this._bufferService.buffer.y);
if (line) {
line.replaceCells(
this._bufferService.buffer.x,
@@ -1520,9 +1532,9 @@ export class InputHandler extends Disposable implements IInputHandler {
if (params.params[0] > 0) {
return;
}
- if (this._terminal.is('xterm') || this._terminal.is('rxvt-unicode') || this._terminal.is('screen')) {
+ if (this._is('xterm') || this._is('rxvt-unicode') || this._is('screen')) {
this._coreService.triggerDataEvent(C0.ESC + '[?1;2c');
- } else if (this._terminal.is('linux')) {
+ } else if (this._is('linux')) {
this._coreService.triggerDataEvent(C0.ESC + '[?6c');
}
}
@@ -1558,19 +1570,27 @@ export class InputHandler extends Disposable implements IInputHandler {
// xterm and urxvt
// seem to spit this
// out around ~370 times (?).
- if (this._terminal.is('xterm')) {
+ if (this._is('xterm')) {
this._coreService.triggerDataEvent(C0.ESC + '[>0;276;0c');
- } else if (this._terminal.is('rxvt-unicode')) {
+ } else if (this._is('rxvt-unicode')) {
this._coreService.triggerDataEvent(C0.ESC + '[>85;95;0c');
- } else if (this._terminal.is('linux')) {
+ } else if (this._is('linux')) {
// not supported by linux console.
// linux console echoes parameters.
this._coreService.triggerDataEvent(params.params[0] + 'c');
- } else if (this._terminal.is('screen')) {
+ } else if (this._is('screen')) {
this._coreService.triggerDataEvent(C0.ESC + '[>83;40003;0c');
}
}
+ /**
+ * Evaluate if the current terminal is the given argument.
+ * @param term The terminal name to evaluate
+ */
+ private _is(term: string): boolean {
+ return (this._optionsService.options.termName + '').indexOf(term) === 0;
+ }
+
/**
* CSI Pm h Set Mode (SM).
* Ps = 2 -> Keyboard Action Mode (AM).
@@ -1587,15 +1607,12 @@ export class InputHandler extends Disposable implements IInputHandler {
* | 4 | Insert Mode (IRM). | #Y |
* | 12 | Send/receive (SRM). Always off. | #N |
* | 20 | Automatic Newline (LNM). Always off. | #N |
- *
- *
- * FIXME: why is LNM commented out?
*/
public setMode(params: IParams): void {
for (let i = 0; i < params.length; i++) {
switch (params.params[i]) {
case 4:
- this._terminal.insertMode = true;
+ this._coreService.modes.insertMode = true;
break;
case 20:
// this._t.convertEol = true;
@@ -1736,7 +1753,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* through `options.windowsOptions`.
*/
if (this._optionsService.options.windowOptions.setWinLines) {
- this._terminal.resize(132, this._bufferService.rows);
+ this._bufferService.resize(132, this._bufferService.rows);
this._onRequestReset.fire();
}
break;
@@ -1753,7 +1770,7 @@ export class InputHandler extends Disposable implements IInputHandler {
case 66:
this._logService.debug('Serial port requested application keypad.');
this._coreService.decPrivateModes.applicationKeypad = true;
- this._terminal.viewport?.syncScrollArea();
+ this._onRequestSyncScrollBar.fire();
break;
case 9: // X10 Mouse
// no release, no motion, no wheel, no modifiers.
@@ -1774,7 +1791,7 @@ export class InputHandler extends Disposable implements IInputHandler {
case 1004: // send focusin/focusout events
// focusin: ^[[I
// focusout: ^[[O
- this._terminal.sendFocus = true;
+ this._coreService.decPrivateModes.sendFocus = true;
break;
case 1005: // utf8 ext mode mouse - removed in #2507
this._logService.debug('DECSET 1005 not supported (see #2507)');
@@ -1797,12 +1814,12 @@ export class InputHandler extends Disposable implements IInputHandler {
case 47: // alt screen buffer
case 1047: // alt screen buffer
this._bufferService.buffers.activateAltBuffer(this._eraseAttrData());
+ this._coreService.isCursorInitialized = true;
this._onRequestRefreshRows.fire(0, this._bufferService.rows - 1);
- this._terminal.viewport?.syncScrollArea();
- this._terminal.showCursor();
+ this._onRequestSyncScrollBar.fire();
break;
case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
- this._terminal.bracketedPasteMode = true;
+ this._coreService.decPrivateModes.bracketedPasteMode = true;
break;
}
}
@@ -1833,7 +1850,7 @@ export class InputHandler extends Disposable implements IInputHandler {
for (let i = 0; i < params.length; i++) {
switch (params.params[i]) {
case 4:
- this._terminal.insertMode = false;
+ this._coreService.modes.insertMode = false;
break;
case 20:
// this._t.convertEol = false;
@@ -1963,7 +1980,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* through `options.windowsOptions`.
*/
if (this._optionsService.options.windowOptions.setWinLines) {
- this._terminal.resize(80, this._bufferService.rows);
+ this._bufferService.resize(80, this._bufferService.rows);
this._onRequestReset.fire();
}
break;
@@ -1980,7 +1997,7 @@ export class InputHandler extends Disposable implements IInputHandler {
case 66:
this._logService.debug('Switching back to normal keypad.');
this._coreService.decPrivateModes.applicationKeypad = false;
- this._terminal.viewport?.syncScrollArea();
+ this._onRequestSyncScrollBar.fire();
break;
case 9: // X10 Mouse
case 1000: // vt200 mouse
@@ -1989,7 +2006,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._coreMouseService.activeProtocol = 'NONE';
break;
case 1004: // send focusin/focusout events
- this._terminal.sendFocus = false;
+ this._coreService.decPrivateModes.sendFocus = false;
break;
case 1005: // utf8 ext mode mouse - removed in #2507
this._logService.debug('DECRST 1005 not supported (see #2507)');
@@ -2015,12 +2032,12 @@ export class InputHandler extends Disposable implements IInputHandler {
if (params.params[i] === 1049) {
this.restoreCursor();
}
+ this._coreService.isCursorInitialized = true;
this._onRequestRefreshRows.fire(0, this._bufferService.rows - 1);
- this._terminal.viewport?.syncScrollArea();
- this._terminal.showCursor();
+ this._onRequestSyncScrollBar.fire();
break;
case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
- this._terminal.bracketedPasteMode = false;
+ this._coreService.decPrivateModes.bracketedPasteMode = false;
break;
}
}
@@ -2046,7 +2063,7 @@ export class InputHandler extends Disposable implements IInputHandler {
do {
accu[advance + cSpace] = params.params[pos + advance];
if (params.hasSubParams(pos + advance)) {
- const subparams = params.getSubParams(pos + advance);
+ const subparams = params.getSubParams(pos + advance)!;
let i = 0;
do {
if (accu[1] === 5) {
@@ -2356,8 +2373,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public softReset(params: IParams): void {
this._coreService.isCursorHidden = false;
- this._terminal.insertMode = false;
- this._terminal.viewport?.syncScrollArea();
+ this._onRequestSyncScrollBar.fire();
this._bufferService.buffer.scrollTop = 0;
this._bufferService.buffer.scrollBottom = this._bufferService.rows - 1;
this._curAttrData = DEFAULT_ATTR_DATA.clone();
@@ -2471,22 +2487,14 @@ export class InputHandler extends Disposable implements IInputHandler {
return;
}
const second = (params.length > 1) ? params.params[1] : 0;
- const rs = this._instantiationService.getService(IRenderService);
switch (params.params[0]) {
case 14: // GetWinSizePixels, returns CSI 4 ; height ; width t
- if (rs && second !== 2) {
- console.log(rs.dimensions);
- const w = rs.dimensions.scaledCanvasWidth.toFixed(0);
- const h = rs.dimensions.scaledCanvasHeight.toFixed(0);
- this._coreService.triggerDataEvent(`${C0.ESC}[4;${h};${w}t`);
+ if (second !== 2) {
+ this._onRequestWindowsOptionsReport.fire(WindowsOptionsReportType.GET_WIN_SIZE_PIXELS);
}
break;
case 16: // GetCellSizePixels, returns CSI 6 ; height ; width t
- if (rs) {
- const w = rs.dimensions.scaledCellWidth.toFixed(0);
- const h = rs.dimensions.scaledCellHeight.toFixed(0);
- this._coreService.triggerDataEvent(`${C0.ESC}[6;${h};${w}t`);
- }
+ this._onRequestWindowsOptionsReport.fire(WindowsOptionsReportType.GET_CELL_SIZE_PIXELS);
break;
case 18: // GetWinSizeChars, returns CSI 8 ; height ; width t
if (this._bufferService) {
@@ -2510,12 +2518,12 @@ export class InputHandler extends Disposable implements IInputHandler {
case 23: // PopTitle
if (second === 0 || second === 2) {
if (this._windowTitleStack.length) {
- this.setTitle(this._windowTitleStack.pop());
+ this.setTitle(this._windowTitleStack.pop()!);
}
}
if (second === 0 || second === 1) {
if (this._iconNameStack.length) {
- this.setIconName(this._iconNameStack.pop());
+ this.setIconName(this._iconNameStack.pop()!);
}
}
break;
@@ -2573,7 +2581,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public setTitle(data: string): void {
this._windowTitle = data;
- this._terminal.handleTitle(data);
+ this._onTitleChange.fire(data);
}
/**
@@ -2606,7 +2614,7 @@ export class InputHandler extends Disposable implements IInputHandler {
public keypadApplicationMode(): void {
this._logService.debug('Serial port requested application keypad.');
this._coreService.decPrivateModes.applicationKeypad = true;
- this._terminal.viewport?.syncScrollArea();
+ this._onRequestSyncScrollBar.fire();
}
/**
@@ -2617,7 +2625,7 @@ export class InputHandler extends Disposable implements IInputHandler {
public keypadNumericMode(): void {
this._logService.debug('Switching back to normal keypad.');
this._coreService.decPrivateModes.applicationKeypad = false;
- this._terminal.viewport?.syncScrollArea();
+ this._onRequestSyncScrollBar.fire();
}
/**
@@ -2674,7 +2682,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._bufferService.buffer.y++;
if (buffer.y === buffer.scrollBottom + 1) {
buffer.y--;
- this._terminal.scroll(this._eraseAttrData());
+ this._onRequestScroll.fire(this._eraseAttrData());
} else if (buffer.y >= this._bufferService.rows) {
buffer.y = this._bufferService.rows - 1;
}
@@ -2712,8 +2720,8 @@ export class InputHandler extends Disposable implements IInputHandler {
// 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.y + buffer.ybase, scrollRegionHeight, 1);
- buffer.lines.set(buffer.y + buffer.ybase, buffer.getBlankLine(this._eraseAttrData()));
+ 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);
} else {
buffer.y--;
@@ -2778,9 +2786,12 @@ export class InputHandler extends Disposable implements IInputHandler {
this._setCursor(0, 0);
for (let yOffset = 0; yOffset < this._bufferService.rows; ++yOffset) {
- const row = buffer.y + buffer.ybase + yOffset;
- buffer.lines.get(row).fill(cell);
- buffer.lines.get(row).isWrapped = false;
+ const row = buffer.ybase + buffer.y + yOffset;
+ const line = buffer.lines.get(row);
+ if (line) {
+ line.fill(cell);
+ line.isWrapped = false;
+ }
}
this._dirtyRowService.markAllDirty();
this._setCursor(0, 0);
diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts
index cfa1abd7..71faed99 100644
--- a/src/common/TestUtils.test.ts
+++ b/src/common/TestUtils.test.ts
@@ -9,13 +9,15 @@ import { clone } from 'common/Clone';
import { DEFAULT_OPTIONS } from 'common/services/OptionsService';
import { IBufferSet, IBuffer } from 'common/buffer/Types';
import { BufferSet } from 'common/buffer/BufferSet';
-import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset } from 'common/Types';
+import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset, IModes } from 'common/Types';
import { UnicodeV6 } from 'common/input/UnicodeV6';
export class MockBufferService implements IBufferService {
public serviceBrand: any;
public get buffer(): IBuffer { return this.buffers.active; }
public buffers: IBufferSet = {} as any;
+ public onResize: IEvent<{ cols: number, rows: number }> = new EventEmitter<{ cols: number, rows: number }>().event;
+ public isUserScrolling: boolean = false;
constructor(
public cols: number,
public rows: number,
@@ -31,6 +33,7 @@ export class MockBufferService implements IBufferService {
}
export class MockCoreMouseService implements ICoreMouseService {
+ public areMouseEventsActive: boolean = false;
public activeEncoding: string = '';
public activeProtocol: string = '';
public addEncoding(name: string): void {}
@@ -47,7 +50,6 @@ export class MockCharsetService implements ICharsetService {
public serviceBrand: any;
public charset: ICharset | undefined;
public glevel: number = 0;
- public charsets: ReadonlyArray = [];
public reset(): void {}
public setgLevel(g: number): void {}
public setgCharset(g: number, charset: ICharset): void {}
@@ -58,10 +60,15 @@ export class MockCoreService implements ICoreService {
public isCursorInitialized: boolean = false;
public isCursorHidden: boolean = false;
public isFocused: boolean = false;
+ public modes: IModes = {
+ insertMode: false
+ };
public decPrivateModes: IDecPrivateModes = {
applicationCursorKeys: false,
applicationKeypad: false,
+ bracketedPasteMode: false,
origin: false,
+ sendFocus: false,
wraparound: true
};
public onData: IEvent = new EventEmitter().event;
diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts
index b250b45e..4fcb627b 100644
--- a/src/common/Types.d.ts
+++ b/src/common/Types.d.ts
@@ -3,13 +3,29 @@
* @license MIT
*/
+import { ITerminalOptions as IPublicTerminalOptions } from 'xterm';
import { IEvent, IEventEmitter } from 'common/EventEmitter';
import { IDeleteEvent, IInsertEvent } from 'common/CircularList';
+import { IParams } from 'common/parser/Types';
+import { IOptionsService, IUnicodeService } from 'common/services/Services';
+
+export interface ICoreTerminal {
+ optionsService: IOptionsService;
+ unicodeService: IUnicodeService;
+}
export interface IDisposable {
dispose(): void;
}
+// TODO: The options that are not in the public API should be reviewed
+export interface ITerminalOptions extends IPublicTerminalOptions {
+ [key: string]: any;
+ cancelEvents?: boolean;
+ convertEol?: boolean;
+ termName?: string;
+}
+
export type XtermListener = (...args: any[]) => void;
/**
@@ -41,7 +57,7 @@ export interface ICircularList {
get(index: number): T | undefined;
set(index: number, value: T): void;
push(value: T): void;
- recycle(): T | undefined;
+ recycle(): T;
pop(): T | undefined;
splice(start: number, deleteCount: number, ...items: T[]): void;
trimStart(count: number): void;
@@ -150,11 +166,16 @@ export interface IMarker extends IDisposable {
readonly isDisposed: boolean;
readonly line: number;
}
+export interface IModes {
+ insertMode: boolean;
+}
export interface IDecPrivateModes {
applicationCursorKeys: boolean;
applicationKeypad: boolean;
+ bracketedPasteMode: boolean;
origin: boolean;
+ sendFocus: boolean;
wraparound: boolean; // defaults: xterm - true, vt100 - false
}
@@ -286,3 +307,88 @@ export interface IWindowOptions {
popTitle?: boolean;
setWinLines?: boolean;
}
+
+/**
+ * Calls the parser and handles actions generated by the parser.
+ */
+export interface IInputHandler {
+ onTitleChange: IEvent;
+ onRequestScroll: IEvent;
+
+ parse(data: string | Uint8Array): void;
+ print(data: Uint32Array, start: number, end: number): void;
+
+ /** C0 BEL */ bell(): void;
+ /** C0 LF */ lineFeed(): void;
+ /** C0 CR */ carriageReturn(): void;
+ /** C0 BS */ backspace(): void;
+ /** C0 HT */ tab(): void;
+ /** C0 SO */ shiftOut(): void;
+ /** C0 SI */ shiftIn(): void;
+
+ /** CSI @ */ insertChars(params: IParams): void;
+ /** CSI SP @ */ scrollLeft(params: IParams): void;
+ /** CSI A */ cursorUp(params: IParams): void;
+ /** CSI SP A */ scrollRight(params: IParams): void;
+ /** CSI B */ cursorDown(params: IParams): void;
+ /** CSI C */ cursorForward(params: IParams): void;
+ /** CSI D */ cursorBackward(params: IParams): void;
+ /** CSI E */ cursorNextLine(params: IParams): void;
+ /** CSI F */ cursorPrecedingLine(params: IParams): void;
+ /** CSI G */ cursorCharAbsolute(params: IParams): void;
+ /** CSI H */ cursorPosition(params: IParams): void;
+ /** CSI I */ cursorForwardTab(params: IParams): void;
+ /** CSI J */ eraseInDisplay(params: IParams): void;
+ /** CSI K */ eraseInLine(params: IParams): void;
+ /** CSI L */ insertLines(params: IParams): void;
+ /** CSI M */ deleteLines(params: IParams): void;
+ /** CSI P */ deleteChars(params: IParams): void;
+ /** CSI S */ scrollUp(params: IParams): void;
+ /** CSI T */ scrollDown(params: IParams, collect?: string): void;
+ /** CSI X */ eraseChars(params: IParams): void;
+ /** CSI Z */ cursorBackwardTab(params: IParams): void;
+ /** CSI ` */ charPosAbsolute(params: IParams): void;
+ /** CSI a */ hPositionRelative(params: IParams): void;
+ /** CSI b */ repeatPrecedingCharacter(params: IParams): void;
+ /** CSI c */ sendDeviceAttributesPrimary(params: IParams): void;
+ /** CSI > c */ sendDeviceAttributesSecondary(params: IParams): void;
+ /** CSI d */ linePosAbsolute(params: IParams): void;
+ /** CSI e */ vPositionRelative(params: IParams): void;
+ /** CSI f */ hVPosition(params: IParams): void;
+ /** CSI g */ tabClear(params: IParams): void;
+ /** CSI h */ setMode(params: IParams, collect?: string): void;
+ /** CSI l */ resetMode(params: IParams, collect?: string): void;
+ /** CSI m */ charAttributes(params: IParams): void;
+ /** CSI n */ deviceStatus(params: IParams, collect?: string): void;
+ /** CSI p */ softReset(params: IParams, collect?: string): void;
+ /** CSI q */ setCursorStyle(params: IParams, collect?: string): void;
+ /** CSI r */ setScrollRegion(params: IParams, collect?: string): void;
+ /** CSI s */ saveCursor(params: IParams): void;
+ /** CSI u */ restoreCursor(params: IParams): void;
+ /** CSI ' } */ insertColumns(params: IParams): void;
+ /** CSI ' ~ */ deleteColumns(params: IParams): void;
+ /** OSC 0
+ OSC 2 */ setTitle(data: string): void;
+ /** ESC E */ nextLine(): void;
+ /** ESC = */ keypadApplicationMode(): void;
+ /** ESC > */ keypadNumericMode(): void;
+ /** ESC % G
+ ESC % @ */ selectDefaultCharset(): void;
+ /** ESC ( C
+ ESC ) C
+ ESC * C
+ ESC + C
+ ESC - C
+ ESC . C
+ ESC / C */ selectCharset(collectAndFlag: string): void;
+ /** ESC D */ index(): void;
+ /** ESC H */ tabSet(): void;
+ /** ESC M */ reverseIndex(): void;
+ /** ESC c */ fullReset(): void;
+ /** ESC n
+ ESC o
+ ESC |
+ ESC }
+ ESC ~ */ setgLevel(level: number): void;
+ /** ESC # 8 */ screenAlignmentPattern(): void;
+}
diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts
index 152bab7a..1de39dbd 100644
--- a/src/common/buffer/Buffer.ts
+++ b/src/common/buffer/Buffer.ts
@@ -36,7 +36,7 @@ export class Buffer implements IBuffer {
public savedY: number = 0;
public savedX: number = 0;
public savedCurAttrData = DEFAULT_ATTR_DATA.clone();
- public savedCharset: ICharset | null = DEFAULT_CHARSET;
+ public savedCharset: ICharset | undefined = DEFAULT_CHARSET;
public markers: Marker[] = [];
private _nullCell: ICellData = CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
private _whitespaceCell: ICellData = CellData.fromCharData([0, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE]);
diff --git a/src/common/buffer/Types.d.ts b/src/common/buffer/Types.d.ts
index e229d69c..c271f33c 100644
--- a/src/common/buffer/Types.d.ts
+++ b/src/common/buffer/Types.d.ts
@@ -31,7 +31,7 @@ export interface IBuffer {
hasScrollback: boolean;
savedY: number;
savedX: number;
- savedCharset: ICharset | null;
+ savedCharset: ICharset | undefined;
savedCurAttrData: IAttributeData;
isCursorInViewport: boolean;
markers: IMarker[];
diff --git a/src/common/data/Charsets.ts b/src/common/data/Charsets.ts
index 01087d8b..c72d5a23 100644
--- a/src/common/data/Charsets.ts
+++ b/src/common/data/Charsets.ts
@@ -10,12 +10,12 @@ import { ICharset } from 'common/Types';
* to be represented within the terminal with only 8-bit encoding. See ISO 2022
* for a discussion on character sets. Only VT100 character sets are supported.
*/
-export const CHARSETS: { [key: string]: ICharset | null } = {};
+export const CHARSETS: { [key: string]: ICharset | undefined } = {};
/**
* The default character set, US.
*/
-export const DEFAULT_CHARSET: ICharset | null = CHARSETS['B'];
+export const DEFAULT_CHARSET: ICharset | undefined = CHARSETS['B'];
/**
* DEC Special Character and Line Drawing Set.
@@ -74,7 +74,7 @@ CHARSETS['A'] = {
* United States character set
* ESC (B
*/
-CHARSETS['B'] = null;
+CHARSETS['B'] = undefined;
/**
* Dutch character set
diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts
index 1b6da792..8de44e43 100644
--- a/src/common/services/BufferService.ts
+++ b/src/common/services/BufferService.ts
@@ -6,6 +6,7 @@
import { IBufferService, IOptionsService } from 'common/services/Services';
import { BufferSet } from 'common/buffer/BufferSet';
import { IBufferSet, IBuffer } from 'common/buffer/Types';
+import { EventEmitter, IEvent } from 'common/EventEmitter';
export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars
export const MINIMUM_ROWS = 1;
@@ -16,6 +17,11 @@ export class BufferService implements IBufferService {
public cols: number;
public rows: number;
public buffers: IBufferSet;
+ /** Whether the user is scrolling (locks the scroll position) */
+ public isUserScrolling: boolean = false;
+
+ private _onResize = new EventEmitter<{ cols: number, rows: number }>();
+ public get onResize(): IEvent<{ cols: number, rows: number }> { return this._onResize.event; }
public get buffer(): IBuffer { return this.buffers.active; }
@@ -30,9 +36,13 @@ export class BufferService implements IBufferService {
public resize(cols: number, rows: number): void {
this.cols = cols;
this.rows = rows;
+ this.buffers.resize(cols, rows);
+ this.buffers.setupTabStops(this.cols);
+ this._onResize.fire({ cols, rows });
}
public reset(): void {
this.buffers = new BufferSet(this._optionsService, this);
+ this.isUserScrolling = false;
}
}
diff --git a/src/common/services/CharsetService.ts b/src/common/services/CharsetService.ts
index 47a015f6..c5381065 100644
--- a/src/common/services/CharsetService.ts
+++ b/src/common/services/CharsetService.ts
@@ -10,22 +10,23 @@ export class CharsetService implements ICharsetService {
public serviceBrand: any;
public charset: ICharset | undefined;
- public charsets: ICharset[] = [];
public glevel: number = 0;
+ private _charsets: (ICharset | undefined)[] = [];
+
public reset(): void {
this.charset = undefined;
- this.charsets = [];
+ this._charsets = [];
this.glevel = 0;
}
public setgLevel(g: number): void {
this.glevel = g;
- this.charset = this.charsets[g];
+ this.charset = this._charsets[g];
}
- public setgCharset(g: number, charset: ICharset): void {
- this.charsets[g] = charset;
+ public setgCharset(g: number, charset: ICharset | undefined): void {
+ this._charsets[g] = charset;
if (this.glevel === g) {
this.charset = charset;
}
diff --git a/src/common/services/CoreMouseService.ts b/src/common/services/CoreMouseService.ts
index c0ed3c6a..29a84761 100644
--- a/src/common/services/CoreMouseService.ts
+++ b/src/common/services/CoreMouseService.ts
@@ -192,6 +192,10 @@ export class CoreMouseService implements ICoreMouseService {
return this._activeProtocol;
}
+ public get areMouseEventsActive(): boolean {
+ return this._protocols[this._activeProtocol].events !== 0;
+ }
+
public set activeProtocol(name: string) {
if (!this._protocols[name]) {
throw new Error(`unknown protocol "${name}"`);
diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts
index 2f0ff72f..55d425aa 100644
--- a/src/common/services/CoreService.ts
+++ b/src/common/services/CoreService.ts
@@ -5,13 +5,19 @@
import { ICoreService, ILogService, IOptionsService, IBufferService } from 'common/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter';
-import { IDecPrivateModes } from 'common/Types';
+import { IDecPrivateModes, IModes } from 'common/Types';
import { clone } from 'common/Clone';
+const DEFAULT_MODES: IModes = Object.freeze({
+ insertMode: false
+});
+
const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({
applicationCursorKeys: false,
applicationKeypad: false,
+ bracketedPasteMode: false,
origin: false,
+ sendFocus: false,
wraparound: true // defaults: xterm - true, vt100 - false
});
@@ -20,6 +26,7 @@ export class CoreService implements ICoreService {
public isCursorInitialized: boolean = false;
public isCursorHidden: boolean = false;
+ public modes: IModes;
public decPrivateModes: IDecPrivateModes;
private _onData = new EventEmitter();
@@ -36,10 +43,12 @@ export class CoreService implements ICoreService {
@ILogService private readonly _logService: ILogService,
@IOptionsService private readonly _optionsService: IOptionsService
) {
+ this.modes = clone(DEFAULT_MODES);
this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES);
}
public reset(): void {
+ this.modes = clone(DEFAULT_MODES);
this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES);
}
diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts
index 8a3e9b28..e123bd8b 100644
--- a/src/common/services/Services.ts
+++ b/src/common/services/Services.ts
@@ -5,19 +5,20 @@
import { IEvent } from 'common/EventEmitter';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
-import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions } from 'common/Types';
+import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions, IModes } from 'common/Types';
import { createDecorator } from 'common/services/ServiceRegistry';
export const IBufferService = createDecorator('BufferService');
export interface IBufferService {
- serviceBrand: any;
+ serviceBrand: undefined;
readonly cols: number;
readonly rows: number;
readonly buffer: IBuffer;
readonly buffers: IBufferSet;
+ isUserScrolling: boolean;
- // TODO: Move resize event here
+ onResize: IEvent<{ cols: number, rows: number }>;
resize(cols: number, rows: number): void;
reset(): void;
@@ -27,6 +28,7 @@ export const ICoreMouseService = createDecorator('CoreMouseSe
export interface ICoreMouseService {
activeProtocol: string;
activeEncoding: string;
+ areMouseEventsActive: boolean;
addProtocol(name: string, protocol: ICoreMouseProtocol): void;
addEncoding(name: string, encoding: CoreMouseEncoding): void;
reset(): void;
@@ -51,12 +53,12 @@ export interface ICoreMouseService {
/**
* Human readable version of mouse events.
*/
- explainEvents(events: CoreMouseEventType): {[event: string]: boolean};
+ explainEvents(events: CoreMouseEventType): { [event: string]: boolean };
}
export const ICoreService = createDecorator('CoreService');
export interface ICoreService {
- serviceBrand: any;
+ serviceBrand: undefined;
/**
* Initially the cursor will not be visible until the first time the terminal
@@ -65,6 +67,7 @@ export interface ICoreService {
isCursorInitialized: boolean;
isCursorHidden: boolean;
+ readonly modes: IModes;
readonly decPrivateModes: IDecPrivateModes;
readonly onData: IEvent;
@@ -92,11 +95,10 @@ export interface ICoreService {
export const ICharsetService = createDecorator('CharsetService');
export interface ICharsetService {
- serviceBrand: any;
+ serviceBrand: undefined;
charset: ICharset | undefined;
readonly glevel: number;
- readonly charsets: ReadonlyArray;
reset(): void;
@@ -111,12 +113,12 @@ export interface ICharsetService {
* @param g
* @param charset
*/
- setgCharset(g: number, charset: ICharset): void;
+ setgCharset(g: number, charset: ICharset | undefined): void;
}
export const IDirtyRowService = createDecorator('DirtyRowService');
export interface IDirtyRowService {
- serviceBrand: any;
+ serviceBrand: undefined;
readonly start: number;
readonly end: number;
@@ -132,61 +134,32 @@ export interface IServiceIdentifier {
type: T;
}
-export interface IConstructorSignature0 {
- new(...services: { serviceBrand: any }[]): T;
-}
+export interface IBrandedService {
+ serviceBrand: undefined;
+};
-export interface IConstructorSignature1 {
- new(first: A1, ...services: { serviceBrand: any }[]): T;
-}
-
-export interface IConstructorSignature2 {
- new(first: A1, second: A2, ...services: { serviceBrand: any }[]): T;
-}
-
-export interface IConstructorSignature3 {
- new(first: A1, second: A2, third: A3, ...services: { serviceBrand: any }[]): T;
-}
-
-export interface IConstructorSignature4 {
- new(first: A1, second: A2, third: A3, fourth: A4, ...services: { serviceBrand: any }[]): T;
-}
-
-export interface IConstructorSignature5 {
- new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, ...services: { serviceBrand: any }[]): T;
-}
-
-export interface IConstructorSignature6 {
- new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, ...services: { serviceBrand: any }[]): T;
-}
-
-export interface IConstructorSignature7 {
- new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: { serviceBrand: any }[]): T;
-}
-
-export interface IConstructorSignature8 {
- new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { serviceBrand: any }[]): T;
-}
+type GetLeadingNonServiceArgs =
+ Args extends [...IBrandedService[]] ? []
+ : Args extends [infer A1, ...IBrandedService[]] ? [A1]
+ : Args extends [infer A1, infer A2, ...IBrandedService[]] ? [A1, A2]
+ : Args extends [infer A1, infer A2, infer A3, ...IBrandedService[]] ? [A1, A2, A3]
+ : Args extends [infer A1, infer A2, infer A3, infer A4, ...IBrandedService[]] ? [A1, A2, A3, A4]
+ : Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, ...IBrandedService[]] ? [A1, A2, A3, A4, A5]
+ : Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, ...IBrandedService[]] ? [A1, A2, A3, A4, A5, A6]
+ : Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7, ...IBrandedService[]] ? [A1, A2, A3, A4, A5, A6, A7]
+ : Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7, infer A8, ...IBrandedService[]] ? [A1, A2, A3, A4, A5, A6, A7, A8]
+ : never;
export const IInstantiationService = createDecorator('InstantiationService');
export interface IInstantiationService {
setService(id: IServiceIdentifier, instance: T): void;
getService(id: IServiceIdentifier): T | undefined;
-
- createInstance(ctor: IConstructorSignature0): T;
- createInstance(ctor: IConstructorSignature1, first: A1): T;
- createInstance(ctor: IConstructorSignature2, first: A1, second: A2): T;
- createInstance(ctor: IConstructorSignature3, first: A1, second: A2, third: A3): T;
- createInstance(ctor: IConstructorSignature4, first: A1, second: A2, third: A3, fourth: A4): T;
- createInstance(ctor: IConstructorSignature5, first: A1, second: A2, third: A3, fourth: A4, fifth: A5): T;
- createInstance(ctor: IConstructorSignature6, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6): T;
- createInstance(ctor: IConstructorSignature7, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7): T;
- createInstance(ctor: IConstructorSignature8, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8): T;
+ createInstance any, R extends InstanceType>(t: Ctor, ...args: GetLeadingNonServiceArgs>): R;
}
export const ILogService = createDecorator('LogService');
export interface ILogService {
- serviceBrand: any;
+ serviceBrand: undefined;
debug(message: any, ...optionalParams: any[]): void;
info(message: any, ...optionalParams: any[]): void;
@@ -196,7 +169,7 @@ export interface ILogService {
export const IOptionsService = createDecorator('OptionsService');
export interface IOptionsService {
- serviceBrand: any;
+ serviceBrand: undefined;
readonly options: ITerminalOptions;
@@ -311,7 +284,7 @@ export interface ITheme {
export const IUnicodeService = createDecorator('UnicodeService');
export interface IUnicodeService {
- serviceBrand: any;
+ serviceBrand: undefined;
/** Register an Unicode version provider. */
register(provider: IUnicodeVersionProvider): void;
/** Registered Unicode versions. */
diff --git a/src/common/tsconfig.json b/src/common/tsconfig.json
index 59050a0b..7f35c80c 100644
--- a/src/common/tsconfig.json
+++ b/src/common/tsconfig.json
@@ -10,5 +10,8 @@
],
"baseUrl": ".."
},
- "include": [ "./**/*" ]
+ "include": [
+ "./**/*",
+ "../../typings/xterm.d.ts"
+ ]
}
diff --git a/src/tsconfig-base.json b/src/tsconfig-base.json
index 84d0c924..0cd951a7 100644
--- a/src/tsconfig-base.json
+++ b/src/tsconfig-base.json
@@ -8,6 +8,7 @@
"removeComments": true,
"pretty": true,
- "incremental": true
+ "incremental": true,
+ "experimentalDecorators": true
}
}
diff --git a/src/tsconfig.json b/src/tsconfig.json
deleted file mode 100644
index 97576668..00000000
--- a/src/tsconfig.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "extends": "./tsconfig-base",
- "compilerOptions": {
- "module": "commonjs",
- "lib": [
- "dom",
- "es5",
- "es6",
- "scripthost",
- "es2015.promise"
- ],
- "rootDir": ".",
- "outDir": "../out",
- "baseUrl": ".",
- "paths": {
- "common/*": [ "./common/*" ],
- "browser/*": [ "./browser/*" ]
- },
-
- "noUnusedLocals": true,
- "noImplicitAny": true
- },
- "include": [
- "./**/*",
- "../typings/xterm.d.ts"
- ],
- "exclude": [
- "./addons/**/*"
- ],
- "references": [
- { "path": "./common" },
- { "path": "./browser" }
- ]
-}
diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts
index 6829fd0a..75e00152 100644
--- a/test/api/Terminal.api.ts
+++ b/test/api/Terminal.api.ts
@@ -539,7 +539,7 @@ describe('API Integration Tests', function(): void {
assert.equal(await page.evaluate(`window.term.buffer.active.getLine(0).translateToString()`), 'norm ');
assert.equal(await page.evaluate(`window.term.buffer.normal.getLine(0).translateToString()`), 'norm ');
assert.equal(await page.evaluate(`window.term.buffer.alternate.getLine(0)`), undefined);
- })
+ });
});
it('dispose', async () => {
diff --git a/test/benchmark/EscapeSequenceParser.benchmark.ts b/test/benchmark/EscapeSequenceParser.benchmark.ts
index 68247715..e4591dde 100644
--- a/test/benchmark/EscapeSequenceParser.benchmark.ts
+++ b/test/benchmark/EscapeSequenceParser.benchmark.ts
@@ -19,9 +19,9 @@ function toUtf32(s: string): Uint32Array {
}
class DcsHandler implements IDcsHandler {
- hook(params: IParams): void {}
- put(data: Uint32Array, start: number, end: number): void {}
- unhook(): void {}
+ public hook(params: IParams): void {}
+ public put(data: Uint32Array, start: number, end: number): void {}
+ public unhook(): void {}
}
diff --git a/test/benchmark/Terminal.benchmark.ts b/test/benchmark/Terminal.benchmark.ts
index 71a36cc7..76139cb6 100644
--- a/test/benchmark/Terminal.benchmark.ts
+++ b/test/benchmark/Terminal.benchmark.ts
@@ -7,7 +7,7 @@ import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark';
import { spawn } from 'node-pty';
import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder';
-import { Terminal } from 'Terminal';
+import { Terminal } from 'browser/Terminal';
perfContext('Terminal: ls -lR /usr/lib', () => {
let content = '';
diff --git a/tsconfig.all.json b/tsconfig.all.json
index 8143ea91..a8febb72 100644
--- a/tsconfig.all.json
+++ b/tsconfig.all.json
@@ -2,7 +2,7 @@
"files": [],
"include": [],
"references": [
- { "path": "./src" },
+ { "path": "./src/browser" },
{ "path": "./test/api" },
{ "path": "./test/benchmark" },
{ "path": "./addons/xterm-addon-attach/src" },
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 3487c410..dfda8583 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -799,13 +799,14 @@ declare module 'xterm' {
* (EXPERIMENTAL) Adds a marker to the normal buffer and returns it. If the
* alt buffer is active, undefined is returned.
* @param cursorYOffset The y position offset of the marker from the cursor.
+ * @returns The new marker or undefined.
*/
- registerMarker(cursorYOffset: number): IMarker;
+ registerMarker(cursorYOffset: number): IMarker | undefined;
/**
* @deprecated use `registerMarker` instead.
*/
- addMarker(cursorYOffset: number): IMarker;
+ addMarker(cursorYOffset: number): IMarker | undefined;
/**
* Gets whether the terminal has an active selection.
diff --git a/webpack.config.js b/webpack.config.js
index a9a60241..4f9087b5 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -12,7 +12,7 @@ const path = require('path');
* output by tsc (because of `baseUrl` and `paths` in `tsconfig.json`.
*/
module.exports = {
- entry: './out/public/Terminal.js',
+ entry: './out/browser/public/Terminal.js',
devtool: 'source-map',
module: {
rules: [