From d858022f23fc45f32935dd20ec4f1cb4d98336fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 4 Jul 2019 17:45:10 +0200 Subject: [PATCH 01/69] fix several escape sequence files --- src/InputHandler.ts | 14 ++++++++++++ src/Terminal2.test.ts | 51 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 7250bdb5..abfc00a6 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -540,6 +540,9 @@ export class InputHandler extends Disposable implements IInputHandler { * Backspace (Ctrl-H). */ public backspace(): void { + if (this._terminal.buffer.x >= this._terminal.cols) { + this._terminal.buffer.x = this._terminal.cols - 1; + } if (this._terminal.buffer.x > 0) { this._terminal.buffer.x--; } @@ -580,6 +583,9 @@ export class InputHandler extends Disposable implements IInputHandler { * Insert Ps (Blank) Character(s) (default = 1) (ICH). */ public insertChars(params: number[]): void { + if (this._terminal.buffer.x >= this._terminal.cols) { + this._terminal.buffer.x = this._terminal.cols - 1; + } this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).insertCells( this._terminal.buffer.x, params[0] || 1, @@ -845,6 +851,9 @@ export class InputHandler extends Disposable implements IInputHandler { * Ps = 2 -> Selective Erase All. */ public eraseInLine(params: number[]): void { + if (this._terminal.buffer.x >= this._terminal.cols) { + this._terminal.buffer.x = this._terminal.cols - 1; + } switch (params[0]) { case 0: this._eraseInBufferLine(this._terminal.buffer.y, this._terminal.buffer.x, this._terminal.cols); @@ -886,6 +895,7 @@ export class InputHandler extends Disposable implements IInputHandler { // this.maxRange(); this._terminal.updateRange(buffer.y); this._terminal.updateRange(buffer.scrollBottom); + buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only? } /** @@ -916,6 +926,7 @@ export class InputHandler extends Disposable implements IInputHandler { // this.maxRange(); this._terminal.updateRange(buffer.y); this._terminal.updateRange(buffer.scrollBottom); + buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only? } /** @@ -1169,6 +1180,9 @@ export class InputHandler extends Disposable implements IInputHandler { * [1,1]) (HVP). */ public hVPosition(params: number[]): void { + if (params.length < 2) { + params.push(1); + } if (params[0] < 1) params[0] = 1; if (params[1] < 1) params[1] = 1; diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index c8164c54..91568c41 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -105,20 +105,53 @@ if (os.platform() !== 'win32') { // omit stack trace for escape sequence files Error.stackTraceLimit = 0; const files = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '..')}); - // for (let i = 0; i < files.length; ++i) console.debug(i, files[i]); // only successful tests for now - const skip = [ - 10, 16, 17, 19, 32, 34, 35, 36, 39, - 40, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, - 63, 68 + const skipFilename = [ + // 't0008-BS.in', + // 't0014-CAN.in', + // 't0015-SUB.in', + // 't0017-SD.in', + // 't0035-HVP.in', + // 't0050-ICH.in', + // 't0051-IL.in', + // 't0052-DL.in', + // 't0055-EL.in', + 't0056-ED.in', + 't0060-DECSC.in', + 't0061-CSI_s.in', + 't0070-DECSTBM_LF.in', + 't0071-DECSTBM_IND.in', + 't0072-DECSTBM_NEL.in', + 't0074-DECSTBM_SU_SD.in', + 't0075-DECSTBM_CUU_CUD.in', + 't0076-DECSTBM_IL_DL.in', + 't0077-DECSTBM_quirks.in', + 't0080-HT.in', + 't0082-HTS.in', + 't0083-CHT.in', + 't0084-CBT.in', + // 't0090-alt_screen.in', + 't0091-alt_screen_ED3.in', + // 't0092-alt_screen_DECSC.in', + // 't0100-IRM.in', + 't0101-NLM.in', + 't0103-reverse_wrap.in', + 't0504-vim.in' ]; - // These are failing on macOS only if (os.platform() === 'darwin') { - skip.push(3, 7, 11, 67); + // These are failing on macOS only + skipFilename.push( + 't0003-line_wrap.in', + 't0005-CR.in', + 't0009-NEL.in', + 't0503-zsh_ls_color.in' + ); } for (let i = 0; i < files.length; i++) { - if (skip.indexOf(i) >= 0) { + // if (skip.indexOf(i) >= 0) { + // continue; + // } + if (skipFilename.indexOf(files[i].split('/').slice(-1)[0]) >= 0) { continue; } ((filename: string) => { From 82ff113107a7d60504dd5cd56321e152fed380f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 4 Jul 2019 23:50:56 +0200 Subject: [PATCH 02/69] add DECALN --- src/InputHandler.ts | 29 ++++++++++++++++++++++++++++- src/Types.d.ts | 1 + 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index abfc00a6..754cc4a0 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -15,7 +15,7 @@ import { StringToUtf32, stringFromCodePoint, utf32ToString, Utf8ToUtf32 } from ' import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { IParsingState, IDcsHandler, IEscapeSequenceParser } from 'common/parser/Types'; -import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags } from 'common/buffer/Constants'; +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 { ICoreService } from 'common/services/Services'; @@ -280,6 +280,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._parser.setEscHandler('.' + flag, () => this.selectCharset('.' + flag)); this._parser.setEscHandler('/' + flag, () => this.selectCharset('/' + flag)); // TODO: supported? } + this._parser.setEscHandler('#8', () => this.screenAlignmentPattern()); /** * error handler @@ -2100,4 +2101,30 @@ export class InputHandler extends Disposable implements IInputHandler { public setgLevel(level: number): void { this._terminal.setgLevel(level); // TODO: save to move from terminal? } + + /** + * ESC # 8 + * DEC mnemonic: DECALN (https://vt100.net/docs/vt510-rm/DECALN.html) + * This control function fills the complete screen area with + * a test pattern (E) used for adjusting screen alignment. + */ + public screenAlignmentPattern(): void { + // prepare cell data + const cell = new CellData(); + cell.content = 1 << Content.WIDTH_SHIFT | 'E'.charCodeAt(0); + cell.fg = this._terminal.curAttrData.fg; + cell.bg = this._terminal.curAttrData.bg; + + const buffer = this._terminal.buffer; + + this.cursorPosition([1, 1]); + for (let yOffset = 0; yOffset < this._terminal.rows; ++yOffset) { + let row = buffer.y + buffer.ybase + yOffset; + buffer.lines.get(row).fill(cell); + buffer.lines.get(row).isWrapped = false; + } + this._terminal.updateRange(0); + this._terminal.updateRange(this._terminal.rows); + this.cursorPosition([1, 1]); + } } diff --git a/src/Types.d.ts b/src/Types.d.ts index eaf6196b..0a45f792 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -168,6 +168,7 @@ export interface IInputHandler { ESC | ESC } ESC ~ */ setgLevel(level: number): void; + /** ESC # 8 */ screenAlignmentPattern(): void; } export interface ILinkMatcher { From 44035d14ab12bb5661db711593745481baedc969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 5 Jul 2019 00:56:57 +0200 Subject: [PATCH 03/69] fix no wraparound mode --- fixtures/escape_sequence_files/t0102-DECAWM.text | 2 +- src/InputHandler.ts | 4 ++-- src/Terminal.test.ts | 9 +++++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/fixtures/escape_sequence_files/t0102-DECAWM.text b/fixtures/escape_sequence_files/t0102-DECAWM.text index c1f22de2..6621f2b8 100644 --- a/fixtures/escape_sequence_files/t0102-DECAWM.text +++ b/fixtures/escape_sequence_files/t0102-DECAWM.text @@ -2,7 +2,7 @@ efgh -------- set: wraparound ----------------------------------------------abcd efgh --------- unset: no wraparound -------------------------------------------abcd +-------- unset: no wraparound -------------------------------------------abch this should be immediately below "no wraparound" diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 754cc4a0..f97e4ba3 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -431,12 +431,12 @@ export class InputHandler extends Disposable implements IInputHandler { // row changed, get it again bufferRow = buffer.lines.get(buffer.y + buffer.ybase); } else { + buffer.x = cols - 1; if (chWidth === 2) { // FIXME: check for xterm behavior // What to do here? We got a wide char that does not fit into last cell continue; } - // FIXME: Do we have to set buffer.x to cols - 1, if not wrapping? } } @@ -2119,7 +2119,7 @@ export class InputHandler extends Disposable implements IInputHandler { this.cursorPosition([1, 1]); for (let yOffset = 0; yOffset < this._terminal.rows; ++yOffset) { - let row = buffer.y + buffer.ybase + yOffset; + const row = buffer.y + buffer.ybase + yOffset; buffer.lines.get(row).fill(cell); buffer.lines.get(row).isWrapped = false; } diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index f50b32a7..dad8d46a 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -8,6 +8,7 @@ import { Terminal } from './Terminal'; import { MockViewport, MockCompositionHelper, MockRenderer } from './TestUtils.test'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; +import { wcwidth } from 'common/CharWidth'; const INIT_COLS = 80; const INIT_ROWS = 24; @@ -750,10 +751,14 @@ describe('Terminal', () => { for (let i = 0xDC00; i <= 0xDCFF; ++i) { term.buffer.x = term.cols - 1; term.wraparoundMode = false; + const width = wcwidth((0xD800 - 0xD800) * 0x400 + i - 0xDC00 + 0x10000); + if (width !== 1) { + continue; + } term.write('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('a'); - expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars().length).eql(1); + 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(); } From d18b7996244547b5abedeb273d109cd3a207594a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Fri, 5 Jul 2019 02:04:42 +0200 Subject: [PATCH 04/69] fix HT, HTS, CHT --- src/InputHandler.ts | 6 ++++++ src/Terminal2.test.ts | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index f97e4ba3..18eb9fa6 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -554,6 +554,9 @@ export class InputHandler extends Disposable implements IInputHandler { * Horizontal Tab (HT) (Ctrl-I). */ public tab(): void { + if (this._terminal.buffer.x >= this._terminal.cols) { + return; + } const originalX = this._terminal.buffer.x; this._terminal.buffer.x = this._terminal.buffer.nextStop(); if (this._terminal.options.screenReaderMode) { @@ -746,6 +749,9 @@ export class InputHandler extends Disposable implements IInputHandler { * Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). */ public cursorForwardTab(params: number[]): void { + if (this._terminal.buffer.x >= this._terminal.cols) { + return; + } let param = params[0] || 1; while (param--) { this._terminal.buffer.x = this._terminal.buffer.nextStop(); diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index 91568c41..6cc6f733 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -126,9 +126,9 @@ if (os.platform() !== 'win32') { 't0075-DECSTBM_CUU_CUD.in', 't0076-DECSTBM_IL_DL.in', 't0077-DECSTBM_quirks.in', - 't0080-HT.in', - 't0082-HTS.in', - 't0083-CHT.in', + // 't0080-HT.in', + // 't0082-HTS.in', + // 't0083-CHT.in', 't0084-CBT.in', // 't0090-alt_screen.in', 't0091-alt_screen_ED3.in', From eb7b8f90207acbe964d6b0ab60109a0df8e643df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 6 Jul 2019 15:59:18 +0200 Subject: [PATCH 05/69] restrict cursor movements --- src/InputHandler.test.ts | 339 +++++++++++++++++++++++++++++++++++++++ src/InputHandler.ts | 122 ++++++-------- 2 files changed, 387 insertions(+), 74 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index e93fc945..553997cd 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -546,4 +546,343 @@ describe('InputHandler', () => { assert.deepEqual(AttributeData.toColorRGB(term.curAttrData.getFgColor()), [5, 0, 0]); }); }); + describe('cursor positioning', () => { + let term: TestTerminal; + beforeEach(() => { + term = new TestTerminal({cols: 10, rows: 10}); + }); + function getCursor(term: TestTerminal): number[] { + return [ + term.buffer.x, + term.buffer.y + ]; + } + 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]); + }); + }); + }); }); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 18eb9fa6..ed7812f2 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -598,6 +598,22 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.updateRange(this._terminal.buffer.y); } + // restrict cursor changes to addressible cols/rows + private _restrictCursor(): void { + // cols + if (this._terminal.buffer.x < 0) { + this._terminal.buffer.x = 0; + } else if (this._terminal.buffer.x >= this._terminal.cols) { + this._terminal.buffer.x = this._terminal.cols - 1; + } + // rows + if (this._terminal.buffer.y < 0) { + this._terminal.buffer.y = 0; + } else if (this._terminal.buffer.y >= this._terminal.rows) { + this._terminal.buffer.y = this._terminal.rows - 1; + } + } + /** * CSI Ps A * Cursor Up Ps Times (default = 1) (CUU). @@ -607,10 +623,9 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } + this._restrictCursor(); this._terminal.buffer.y -= param; - if (this._terminal.buffer.y < 0) { - this._terminal.buffer.y = 0; - } + this._restrictCursor(); } /** @@ -622,14 +637,9 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } + this._restrictCursor(); this._terminal.buffer.y += param; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - // If the end of the line is hit, prevent this action from wrapping around to the next line. - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x--; - } + this._restrictCursor(); } /** @@ -641,10 +651,9 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } + this._restrictCursor(); this._terminal.buffer.x += param; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } + this._restrictCursor(); } /** @@ -656,49 +665,42 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } - // If the end of the line is hit, prevent this action from wrapping around to the next line. - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x--; - } + this._restrictCursor(); this._terminal.buffer.x -= param; - if (this._terminal.buffer.x < 0) { - this._terminal.buffer.x = 0; - } + this._restrictCursor(); } /** * CSI Ps E * Cursor Next Line Ps Times (default = 1) (CNL). - * same as CSI Ps B ? + * Other than cursorDown (CUD) also set the cursor to first column. */ public cursorNextLine(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } + this._restrictCursor(); this._terminal.buffer.y += param; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } this._terminal.buffer.x = 0; + this._restrictCursor(); } /** * CSI Ps F - * Cursor Preceding Line Ps Times (default = 1) (CNL). - * reuse CSI Ps A ? + * Cursor Previous Line Ps Times (default = 1) (CPL). + * Other than cursorUp (CUU) also set the cursor to first column. */ public cursorPrecedingLine(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } + this._restrictCursor(); this._terminal.buffer.y -= param; - if (this._terminal.buffer.y < 0) { - this._terminal.buffer.y = 0; - } this._terminal.buffer.x = 0; + this._restrictCursor(); } @@ -711,7 +713,9 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } + this._restrictCursor(); this._terminal.buffer.x = param - 1; + this._restrictCursor(); } /** @@ -720,7 +724,7 @@ export class InputHandler extends Disposable implements IInputHandler { */ public cursorPosition(params: number[]): void { let col: number; - let row: number = params[0] - 1; + const row: number = params[0] - 1; if (params.length >= 2) { col = params[1] - 1; @@ -728,20 +732,10 @@ export class InputHandler extends Disposable implements IInputHandler { col = 0; } - if (row < 0) { - row = 0; - } else if (row >= this._terminal.rows) { - row = this._terminal.rows - 1; - } - - if (col < 0) { - col = 0; - } else if (col >= this._terminal.cols) { - col = this._terminal.cols - 1; - } - + this._restrictCursor(); this._terminal.buffer.x = col; this._terminal.buffer.y = row; + this._restrictCursor(); } /** @@ -1017,32 +1011,31 @@ export class InputHandler extends Disposable implements IInputHandler { /** * CSI Pm ` Character Position Absolute * [column] (default = [row,1]) (HPA). + * Currently same functionality as CHA. */ public charPosAbsolute(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } + this._restrictCursor(); this._terminal.buffer.x = param - 1; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } + this._restrictCursor(); } /** * CSI Pm a Character Position Relative * [columns] (default = [row,col+1]) (HPR) - * reuse CSI Ps C ? + * Currently same functionality as CUF. */ public hPositionRelative(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } + this._restrictCursor(); this._terminal.buffer.x += param; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } + this._restrictCursor(); } /** @@ -1155,10 +1148,9 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } + this._restrictCursor(); this._terminal.buffer.y = param - 1; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } + this._restrictCursor(); } /** @@ -1171,37 +1163,19 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } + this._restrictCursor(); this._terminal.buffer.y += param; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - // If the end of the line is hit, prevent this action from wrapping around to the next line. - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x--; - } + this._restrictCursor(); } /** * CSI Ps ; Ps f * Horizontal and Vertical Position [row;column] (default = * [1,1]) (HVP). + * Same as CUP. */ public hVPosition(params: number[]): void { - if (params.length < 2) { - params.push(1); - } - if (params[0] < 1) params[0] = 1; - if (params[1] < 1) params[1] = 1; - - this._terminal.buffer.y = params[0] - 1; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - - this._terminal.buffer.x = params[1] - 1; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } + this.cursorPosition(params); } /** From a2bf5fe975c927e1a6f85ff83cc32e8233646eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 6 Jul 2019 16:13:21 +0200 Subject: [PATCH 06/69] fix typo --- src/InputHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index ed7812f2..6ae2cdeb 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -598,7 +598,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.updateRange(this._terminal.buffer.y); } - // restrict cursor changes to addressible cols/rows + // restrict cursor changes to addressable cols/rows private _restrictCursor(): void { // cols if (this._terminal.buffer.x < 0) { From fa9857aedf66ea19c08108acb20895cea5e1b3c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 6 Jul 2019 16:47:48 +0200 Subject: [PATCH 07/69] simplify cursor movements --- src/InputHandler.ts | 69 ++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 6ae2cdeb..1ac02944 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -598,7 +598,11 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.updateRange(this._terminal.buffer.y); } - // restrict cursor changes to addressable cols/rows + /** + * FIXME: + * - create Cursor class living on Buffer + * - move these private cursor methods to Cursor class as API + */ private _restrictCursor(): void { // cols if (this._terminal.buffer.x < 0) { @@ -614,6 +618,19 @@ export class InputHandler extends Disposable implements IInputHandler { } } + private _setCursor(x: number, y: number): void { + this._terminal.buffer.x = x; + this._terminal.buffer.y = y; + this._restrictCursor(); + } + + private _moveCursor(x: number, y: number): void { + // for relative changes we have to make sure we are within 0 .. cols/rows - 1 + // before calculating the new position + this._restrictCursor(); + this._setCursor(this._terminal.buffer.x + x, this._terminal.buffer.y + y); + } + /** * CSI Ps A * Cursor Up Ps Times (default = 1) (CUU). @@ -623,9 +640,7 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } - this._restrictCursor(); - this._terminal.buffer.y -= param; - this._restrictCursor(); + this._moveCursor(0, -param); } /** @@ -637,9 +652,7 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } - this._restrictCursor(); - this._terminal.buffer.y += param; - this._restrictCursor(); + this._moveCursor(0, param); } /** @@ -651,9 +664,7 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } - this._restrictCursor(); - this._terminal.buffer.x += param; - this._restrictCursor(); + this._moveCursor(param, 0); } /** @@ -665,9 +676,7 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } - this._restrictCursor(); - this._terminal.buffer.x -= param; - this._restrictCursor(); + this._moveCursor(-param, 0); } /** @@ -680,10 +689,8 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } - this._restrictCursor(); - this._terminal.buffer.y += param; + this._moveCursor(0, param); this._terminal.buffer.x = 0; - this._restrictCursor(); } @@ -697,10 +704,8 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } - this._restrictCursor(); - this._terminal.buffer.y -= param; + this._moveCursor(0, -param); this._terminal.buffer.x = 0; - this._restrictCursor(); } @@ -713,9 +718,7 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } - this._restrictCursor(); - this._terminal.buffer.x = param - 1; - this._restrictCursor(); + this._setCursor(param - 1, this._terminal.buffer.y); } /** @@ -731,11 +734,7 @@ export class InputHandler extends Disposable implements IInputHandler { } else { col = 0; } - - this._restrictCursor(); - this._terminal.buffer.x = col; - this._terminal.buffer.y = row; - this._restrictCursor(); + this._setCursor(col, row); } /** @@ -1018,9 +1017,7 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } - this._restrictCursor(); - this._terminal.buffer.x = param - 1; - this._restrictCursor(); + this._setCursor(param - 1, this._terminal.buffer.y); } /** @@ -1033,9 +1030,7 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } - this._restrictCursor(); - this._terminal.buffer.x += param; - this._restrictCursor(); + this._moveCursor(param, 0); } /** @@ -1148,9 +1143,7 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } - this._restrictCursor(); - this._terminal.buffer.y = param - 1; - this._restrictCursor(); + this._setCursor(this._terminal.buffer.x, param - 1); } /** @@ -1163,9 +1156,7 @@ export class InputHandler extends Disposable implements IInputHandler { if (param < 1) { param = 1; } - this._restrictCursor(); - this._terminal.buffer.y += param; - this._restrictCursor(); + this._moveCursor(0, param); } /** From 6d22772d16273e583aa205a95255f0ef2f54211c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sat, 6 Jul 2019 18:09:38 +0200 Subject: [PATCH 08/69] fix REP to be in line with vttest --- src/common/parser/EscapeSequenceParser.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index d8d4e02e..9725b8bc 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -457,10 +457,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP } break; case ParserAction.EXECUTE: - this.precedingCodepoint = 0; callback = this._executeHandlers[code]; if (callback) callback(); else this._executeHandlerFb(code); + this.precedingCodepoint = 0; break; case ParserAction.IGNORE: break; @@ -479,10 +479,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP // inject values: currently not implemented break; case ParserAction.CSI_DISPATCH: - // dont reset preceding codepoint for REP itself - if (code !== 98) { // 'b' - this.precedingCodepoint = 0; - } // Trigger CSI Handler const handlers = this._csiHandlers[code]; let j = handlers ? handlers.length - 1 : -1; @@ -495,6 +491,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP if (j < 0) { this._csiHandlerFb(collect, params, code); } + this.precedingCodepoint = 0; break; case ParserAction.PARAM: // inner loop: digits (0x30 - 0x39) and ; (0x3b) @@ -508,10 +505,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP collect += String.fromCharCode(code); break; case ParserAction.ESC_DISPATCH: - this.precedingCodepoint = 0; callback = this._escHandlers[collect + String.fromCharCode(code)]; if (callback) callback(collect, code); else this._escHandlerFb(collect, code); + this.precedingCodepoint = 0; break; case ParserAction.CLEAR: osc = ''; @@ -519,7 +516,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP collect = ''; break; case ParserAction.DCS_HOOK: - this.precedingCodepoint = 0; dcsHandler = this._dcsHandlers[collect + String.fromCharCode(code)]; if (!dcsHandler) dcsHandler = this._dcsHandlerFb; dcsHandler.hook(collect, params, code); @@ -546,6 +542,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP osc = ''; params = [0]; collect = ''; + this.precedingCodepoint = 0; break; case ParserAction.OSC_START: osc = ''; @@ -561,7 +558,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP } break; case ParserAction.OSC_END: - this.precedingCodepoint = 0; if (osc && code !== 0x18 && code !== 0x1a) { // NOTE: OSC subparsing is not part of the original parser // we do basic identifier parsing here to offer a jump table for OSC as well @@ -592,6 +588,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP osc = ''; params = [0]; collect = ''; + this.precedingCodepoint = 0; break; } currentState = transition & TableAccess.TRANSITION_STATE_MASK; From 204847b134a675bfc48fbe811c87cf93fcb6fb28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 7 Jul 2019 19:53:02 +0200 Subject: [PATCH 09/69] fix DECSC --- .../escape_sequence_files/t0600-vttest1.in | 35 +++++++++++++++++++ src/InputHandler.ts | 3 ++ src/Terminal2.test.ts | 7 ++-- 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 fixtures/escape_sequence_files/t0600-vttest1.in diff --git a/fixtures/escape_sequence_files/t0600-vttest1.in b/fixtures/escape_sequence_files/t0600-vttest1.in new file mode 100644 index 00000000..dff924d4 --- /dev/null +++ b/fixtures/escape_sequence_files/t0600-vttest1.in @@ -0,0 +1,35 @@ +Test of autowrap, mixing control and print characters. + + +The left/right margins should have letters in order: + + +[?6hAa +aBB b +C cC + +DdEe +eFF f +G gG + +HhIi +iJJ j +K kK + +LlMm +mNN n +O oO + +PpQq +qRR r +S sS + +TtUu +uVV v +W wW + +XxYy +yZZ z +[?6lPush + + diff --git a/src/InputHandler.ts b/src/InputHandler.ts index b6a82150..bc9528e0 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -1249,6 +1249,7 @@ export class InputHandler extends Disposable implements IInputHandler { case 3: // 132 col mode this._terminal.savedCols = this._terminal.cols; this._terminal.resize(132, this._terminal.rows); + this._terminal.reset(); break; case 6: this._terminal.originMode = true; @@ -1447,6 +1448,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.resize(this._terminal.savedCols, this._terminal.rows); } delete this._terminal.savedCols; + this._terminal.reset(); break; case 6: this._terminal.originMode = false; @@ -1919,6 +1921,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.buffer.y = Math.max(this._terminal.buffer.savedY - this._terminal.buffer.ybase, 0); this._terminal.curAttrData.fg = this._terminal.buffer.savedCurAttrData.fg; this._terminal.curAttrData.bg = this._terminal.buffer.savedCurAttrData.bg; + this._restrictCursor(); } diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index 6cc6f733..fe291656 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -117,8 +117,8 @@ if (os.platform() !== 'win32') { // 't0052-DL.in', // 't0055-EL.in', 't0056-ED.in', - 't0060-DECSC.in', - 't0061-CSI_s.in', + // 't0060-DECSC.in', + // 't0061-CSI_s.in', 't0070-DECSTBM_LF.in', 't0071-DECSTBM_IND.in', 't0072-DECSTBM_NEL.in', @@ -136,7 +136,8 @@ if (os.platform() !== 'win32') { // 't0100-IRM.in', 't0101-NLM.in', 't0103-reverse_wrap.in', - 't0504-vim.in' + 't0504-vim.in', + 't0600-vttest1.in' // FIXME: fix height and create .text ]; if (os.platform() === 'darwin') { // These are failing on macOS only From cd1d194dbdfdb1d722d12ebeb03a36e5f3239743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 7 Jul 2019 20:39:37 +0200 Subject: [PATCH 10/69] apply cursor restrictions --- src/InputHandler.ts | 15 +++++++++------ src/Terminal2.test.ts | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index bc9528e0..e7b21c0f 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -541,9 +541,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Backspace (Ctrl-H). */ public backspace(): void { - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } + this._restrictCursor(); if (this._terminal.buffer.x > 0) { this._terminal.buffer.x--; } @@ -587,9 +585,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Insert Ps (Blank) Character(s) (default = 1) (ICH). */ public insertChars(params: IParams): void { - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } + this._restrictCursor(); this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).insertCells( this._terminal.buffer.x, params.params[0] || 1, @@ -841,6 +837,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Insert Ps Line(s) (default = 1) (IL). */ public insertLines(params: IParams): void { + this._restrictCursor(); let param = params.params[0] || 1; // make buffer local for faster access @@ -868,6 +865,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Delete Ps Line(s) (default = 1) (DL). */ public deleteLines(params: IParams): void { + this._restrictCursor(); let param = params.params[0] || 1; // make buffer local for faster access @@ -959,6 +957,9 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). */ public cursorBackwardTab(params: IParams): void { + if (this._terminal.buffer.x >= this._terminal.cols) { + return; + } let param = params.params[0] || 1; // make buffer local for faster access @@ -2017,6 +2018,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Moves the cursor down one line in the same column. */ public index(): void { + this._restrictCursor(); this._terminal.index(); // TODO: save to move from terminal? } @@ -2039,6 +2041,7 @@ export class InputHandler extends Disposable implements IInputHandler { * the page scrolls down. */ public reverseIndex(): void { + this._restrictCursor(); this._terminal.reverseIndex(); // TODO: save to move from terminal? } diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index fe291656..8c560761 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -116,7 +116,7 @@ if (os.platform() !== 'win32') { // 't0051-IL.in', // 't0052-DL.in', // 't0055-EL.in', - 't0056-ED.in', + // 't0056-ED.in', // 't0060-DECSC.in', // 't0061-CSI_s.in', 't0070-DECSTBM_LF.in', From d26a64372745e297278c9ddf2c332d0665660b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 7 Jul 2019 20:45:54 +0200 Subject: [PATCH 11/69] use fill for _resetBufferLine --- src/InputHandler.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index e7b21c0f..96832dfe 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -740,7 +740,9 @@ export class InputHandler extends Disposable implements IInputHandler { * @param y row index */ private _resetBufferLine(y: number): void { - this._eraseInBufferLine(y, 0, this._terminal.cols, true); + const line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + y); + line.fill(this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())); + line.isWrapped = false; } /** @@ -756,6 +758,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Ps = 2 -> Selective Erase All. */ public eraseInDisplay(params: IParams): void { + this._restrictCursor(); let j; switch (params.params[0]) { case 0: @@ -815,9 +818,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Ps = 2 -> Selective Erase All. */ public eraseInLine(params: IParams): void { - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } + this._restrictCursor(); switch (params.params[0]) { case 0: this._eraseInBufferLine(this._terminal.buffer.y, this._terminal.buffer.x, this._terminal.cols); From 9a64c549f8fe34c7c2fe77163f1af50258337c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 7 Jul 2019 20:54:12 +0200 Subject: [PATCH 12/69] fix ED3 test --- .../t0091-alt_screen_ED3.text | 48 +++++++++---------- src/Terminal2.test.ts | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/fixtures/escape_sequence_files/t0091-alt_screen_ED3.text b/fixtures/escape_sequence_files/t0091-alt_screen_ED3.text index 4a326a40..e481aec1 100644 --- a/fixtures/escape_sequence_files/t0091-alt_screen_ED3.text +++ b/fixtures/escape_sequence_files/t0091-alt_screen_ED3.text @@ -1,25 +1,25 @@ - n - o - p - q - r - s - t - u - v - w - x - y - z - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 +o +p +q +r +s +t +u +v +w +x +y +z +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 + +11 - 11 diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index 8c560761..9ad84736 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -131,7 +131,7 @@ if (os.platform() !== 'win32') { // 't0083-CHT.in', 't0084-CBT.in', // 't0090-alt_screen.in', - 't0091-alt_screen_ED3.in', + // 't0091-alt_screen_ED3.in', // 't0092-alt_screen_DECSC.in', // 't0100-IRM.in', 't0101-NLM.in', From 926c298371c085324a973fc5fb9476bea66184a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 7 Jul 2019 21:10:38 +0200 Subject: [PATCH 13/69] fix DCH --- src/InputHandler.test.ts | 14 ++++++++++++++ src/InputHandler.ts | 16 ++++++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index a39ad9b9..2ebac006 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -1034,6 +1034,20 @@ describe('InputHandler', () => { 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 '); + }); }); }); }); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 96832dfe..7cb6cd2d 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -895,12 +895,16 @@ export class InputHandler extends Disposable implements IInputHandler { * Delete Ps Character(s) (default = 1) (DCH). */ public deleteChars(params: IParams): void { - this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).deleteCells( - this._terminal.buffer.x, - params.params[0] || 1, - this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) - ); - this._terminal.updateRange(this._terminal.buffer.y); + this._restrictCursor(); + const line = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase); + if (line) { + line.deleteCells( + this._terminal.buffer.x, + params.params[0] || 1, + this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) + ); + this._terminal.updateRange(this._terminal.buffer.y); + } } /** From 7e7730d176f5037ea7baf7848a91eeb100902f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 7 Jul 2019 21:15:14 +0200 Subject: [PATCH 14/69] fix ECH --- src/InputHandler.test.ts | 14 ++++++++++++++ src/InputHandler.ts | 16 ++++++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 2ebac006..4329fa1a 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -1048,6 +1048,20 @@ describe('InputHandler', () => { 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 '); + }); }); }); }); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 7cb6cd2d..718efcc8 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -950,12 +950,16 @@ export class InputHandler extends Disposable implements IInputHandler { * Erase Ps Character(s) (default = 1) (ECH). */ public eraseChars(params: IParams): void { - this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).replaceCells( - this._terminal.buffer.x, - this._terminal.buffer.x + (params.params[0] || 1), - this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) - ); - this._terminal.updateRange(this._terminal.buffer.y); + this._restrictCursor(); + const line = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase); + if (line) { + line.replaceCells( + this._terminal.buffer.x, + this._terminal.buffer.x + (params.params[0] || 1), + this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) + ); + this._terminal.updateRange(this._terminal.buffer.y); + } } /** From 9d4cafc4c9983ebf73422d8f122329240c3988e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 7 Jul 2019 21:18:03 +0200 Subject: [PATCH 15/69] fix ICH --- src/InputHandler.test.ts | 14 ++++++++++++++ src/InputHandler.ts | 15 +++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 4329fa1a..541547f8 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -1062,6 +1062,20 @@ describe('InputHandler', () => { 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 '); + }); }); }); }); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 718efcc8..5fae95f9 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -586,12 +586,15 @@ export class InputHandler extends Disposable implements IInputHandler { */ public insertChars(params: IParams): void { this._restrictCursor(); - this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).insertCells( - this._terminal.buffer.x, - params.params[0] || 1, - this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) - ); - this._terminal.updateRange(this._terminal.buffer.y); + const line = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase); + if (line) { + line.insertCells( + this._terminal.buffer.x, + params.params[0] || 1, + this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) + ); + this._terminal.updateRange(this._terminal.buffer.y); + } } /** From ef58f1fef8569b5616076774ba223d33a1c12388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 7 Jul 2019 22:19:45 +0200 Subject: [PATCH 16/69] add vttest wrapping testfile --- fixtures/escape_sequence_files/run_tests.py | 73 +++++++++++++++++++ .../{t0600-vttest1.in => t0300-vttest1.in} | 0 .../escape_sequence_files/t0300-vttest1.text | 25 +++++++ src/Terminal2.test.ts | 3 +- 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 fixtures/escape_sequence_files/run_tests.py rename fixtures/escape_sequence_files/{t0600-vttest1.in => t0300-vttest1.in} (100%) create mode 100644 fixtures/escape_sequence_files/t0300-vttest1.text diff --git a/fixtures/escape_sequence_files/run_tests.py b/fixtures/escape_sequence_files/run_tests.py new file mode 100644 index 00000000..08efbf3f --- /dev/null +++ b/fixtures/escape_sequence_files/run_tests.py @@ -0,0 +1,73 @@ +from glob import glob +import os +import sys +import termios +import atexit + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + + +def enable_echo(fd, enabled): + (iflag, oflag, cflag, lflag, ispeed, ospeed, cc) = termios.tcgetattr(fd) + if enabled: + lflag |= termios.ECHO + else: + lflag &= ~termios.ECHO + new_attr = [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] + termios.tcsetattr(fd, termios.TCSANOW, new_attr) + +atexit.register(enable_echo, sys.stdin.fileno(), True) + +output = [] + + +def log(append=False, *s): + if append: + output[-1] += ' ' + ' '.join(str(part) for part in s) + else: + output.append(' '.join(str(part) for part in s)) + + +def reset_terminal(): + sys.stdout.write('\x1bc\x1b[H') + sys.stdout.flush() + + +def test(): + count = 0 + passed = 0 + for i, testfile in enumerate(sorted(glob(os.path.join(BASE_DIR, '*.in')))): + count += 1 + log(False, os.path.basename(testfile)) + reset_terminal() + with open(testfile) as test: + sys.stdout.write('\x1b]0;%s\x07' % os.path.basename(testfile)) + sys.stdout.write(test.read()+'\x1bt') + sys.stdout.flush() + with open(os.path.join(os.path.dirname(testfile), + os.path.basename(testfile).split('.')[0]+'.text')) as expected: + terminal_output = sys.stdin.read() + if not terminal_output: + # we are in xterm + continue + if terminal_output != expected.read(): + log(True, '\x1b[31merror\x1b[0m') + with open(os.path.join(os.path.dirname(testfile), 'output', + os.path.basename(testfile)), 'w') as t_out: + t_out.write(terminal_output) + else: + passed += 1 + log(True, '\x1b[32mpass\x1b[0m') + return count, passed + + +if __name__ == '__main__': + enable_echo(sys.stdin.fileno(), False) + count, passed = test() + enable_echo(sys.stdin.fileno(), True) + reset_terminal() + for i in range(len(output)/2+1): + if not (i+1) % 25: + sys.stdin.read() + print ''.join(i.ljust(40) for i in output[i*2:i*2+2]) + print '\x1b[33mcoverage: %s/%s (%d%%) tests passed.\x1b[0m' % (passed, count, passed*100/count) diff --git a/fixtures/escape_sequence_files/t0600-vttest1.in b/fixtures/escape_sequence_files/t0300-vttest1.in similarity index 100% rename from fixtures/escape_sequence_files/t0600-vttest1.in rename to fixtures/escape_sequence_files/t0300-vttest1.in diff --git a/fixtures/escape_sequence_files/t0300-vttest1.text b/fixtures/escape_sequence_files/t0300-vttest1.text new file mode 100644 index 00000000..d4dd93d7 --- /dev/null +++ b/fixtures/escape_sequence_files/t0300-vttest1.text @@ -0,0 +1,25 @@ +Test of autowrap, mixing control and print characters. + +I i +J j +K k +L l +M m +N n +O o +P p +Q q +R r +S s +T t +U u +V v +W w +X x +Y y +Z z + +Push + + + diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index 9ad84736..f0d54e5e 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -137,7 +137,8 @@ if (os.platform() !== 'win32') { 't0101-NLM.in', 't0103-reverse_wrap.in', 't0504-vim.in', - 't0600-vttest1.in' // FIXME: fix height and create .text + // vttest related files + 't0300-vttest1.in' ]; if (os.platform() === 'darwin') { // These are failing on macOS only From 46f89cd4ffaf9b01155f9ca699451c2252b9dd05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 7 Jul 2019 22:22:19 +0200 Subject: [PATCH 17/69] fix NPE --- src/Terminal.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Terminal.ts b/src/Terminal.ts index ccec42e8..ccbca340 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1846,6 +1846,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp * ESC M Reverse Index (RI is 0x8d). * * Move the cursor up one row, inserting a new blank line if necessary. + * FIXME: This method is seriously broken. */ public reverseIndex(): void { if (this.buffer.y === this.buffer.scrollTop) { @@ -1859,6 +1860,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this.updateRange(this.buffer.scrollBottom); } else { this.buffer.y--; + (this._inputHandler as any)._restrictCursor(); // quickfix to not run out of bounds } } From e2366fa83d9a1c302e5716f777b4e29e5d032431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 7 Jul 2019 23:37:02 +0200 Subject: [PATCH 18/69] rewrite DECSTBM --- src/InputHandler.test.ts | 27 +++++++++++++++++++++++++++ src/InputHandler.ts | 17 +++++++++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 541547f8..68550c9f 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -1078,4 +1078,31 @@ describe('InputHandler', () => { }); }); }); + 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); + }); + }); }); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 5fae95f9..ca4fa2da 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -1904,10 +1904,19 @@ export class InputHandler extends Disposable implements IInputHandler { if (collect) { return; } - this._terminal.buffer.scrollTop = (params.params[0] || 1) - 1; - this._terminal.buffer.scrollBottom = (params.length > 1 && params.params[1] && params.params[1] <= this._terminal.rows ? params.params[1] : this._terminal.rows) - 1; - this._terminal.buffer.x = 0; - this._terminal.buffer.y = 0; + + const top = params.params[0] || 1; + let bottom: number; + + if (params.length < 2 || (bottom = params.params[1]) > this._terminal.rows || bottom === 0) { + bottom = this._terminal.rows; + } + + if (bottom > top) { + this._terminal.buffer.scrollTop = top - 1; + this._terminal.buffer.scrollBottom = bottom - 1; + this._setCursor(0, 0); + } } From 41cc99a9199eded8729d959e0f69baf1a2b4425b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 8 Jul 2019 00:19:21 +0200 Subject: [PATCH 19/69] DECSTBM SU/SD --- .../t0074-DECSTBM_SU_SD.text | 39 ++++++++++--------- src/InputHandler.test.ts | 21 ++++++++++ src/Terminal2.test.ts | 2 +- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.text b/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.text index 948e5d43..02caf57d 100644 --- a/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.text +++ b/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.text @@ -1,24 +1,25 @@ a - b - c - d - f - g - h - i +b +c +d +f +g +h +i - j - k - l - m +j +k +l +m - n - o - p - q - r - s - w - x +n +o +p +q +r +s +w +x + diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 68550c9f..c21ca5f9 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -1105,4 +1105,25 @@ describe('InputHandler', () => { assert.equal(term.buffer.scrollBottom, 9); }); }); + describe('scrolling', () => { + let term: TestTerminal; + beforeEach(() => { + term = new TestTerminal({cols: 10, rows: 10}); + }); + 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; + } + it('scrollUp with margins', () => { + 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 with margins', () => { + 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']); + }); + }); }); diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index f0d54e5e..b65a2e59 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -122,7 +122,7 @@ if (os.platform() !== 'win32') { 't0070-DECSTBM_LF.in', 't0071-DECSTBM_IND.in', 't0072-DECSTBM_NEL.in', - 't0074-DECSTBM_SU_SD.in', + // 't0074-DECSTBM_SU_SD.in', 't0075-DECSTBM_CUU_CUD.in', 't0076-DECSTBM_IL_DL.in', 't0077-DECSTBM_quirks.in', From 8654133b150dec67dad814afe863a4b1c772d288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 8 Jul 2019 00:39:05 +0200 Subject: [PATCH 20/69] move leftover sequence methods to InputHandler --- src/InputHandler.ts | 24 +++++++++++++++++--- src/Terminal.ts | 51 +------------------------------------------ src/TestUtils.test.ts | 9 -------- src/Types.d.ts | 3 --- 4 files changed, 22 insertions(+), 65 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index ca4fa2da..7bdd5fe4 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -2040,7 +2040,12 @@ export class InputHandler extends Disposable implements IInputHandler { */ public index(): void { this._restrictCursor(); - this._terminal.index(); // TODO: save to move from terminal? + this._terminal.buffer.y++; + if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) { + this._terminal.buffer.y--; + this._terminal.scroll(); + } + this._restrictCursor(); } /** @@ -2051,7 +2056,7 @@ export class InputHandler extends Disposable implements IInputHandler { * the value of the active column when the terminal receives an HTS. */ public tabSet(): void { - this._terminal.tabSet(); // TODO: save to move from terminal? + this._terminal.buffer.tabs[this._terminal.buffer.x] = true; } /** @@ -2063,7 +2068,20 @@ export class InputHandler extends Disposable implements IInputHandler { */ public reverseIndex(): void { this._restrictCursor(); - this._terminal.reverseIndex(); // TODO: save to move from terminal? + const buffer = this._terminal.buffer; + if (buffer.y === buffer.scrollTop) { + // possibly move the code below to term.reverseScroll(); + // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' + // blankLine(true) is xterm/linux behavior + const scrollRegionHeight = buffer.scrollBottom - buffer.scrollTop; + buffer.lines.shiftElements(buffer.y + buffer.ybase, scrollRegionHeight, 1); + buffer.lines.set(buffer.y + buffer.ybase, buffer.getBlankLine(this._terminal.eraseAttrData())); + this._terminal.updateRange(buffer.scrollTop); + this._terminal.updateRange(buffer.scrollBottom); + } else { + buffer.y--; + this._restrictCursor(); // quickfix to not run out of bounds + } } /** diff --git a/src/Terminal.ts b/src/Terminal.ts index ccbca340..d40d622f 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1824,48 +1824,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp } /** - * ESC - */ - - /** - * ESC D Index (IND is 0x84). - */ - public index(): void { - this.buffer.y++; - if (this.buffer.y > this.buffer.scrollBottom) { - this.buffer.y--; - this.scroll(); - } - // If the end of the line is hit, prevent this action from wrapping around to the next line. - if (this.buffer.x >= this.cols) { - this.buffer.x--; - } - } - - /** - * ESC M Reverse Index (RI is 0x8d). - * - * Move the cursor up one row, inserting a new blank line if necessary. - * FIXME: This method is seriously broken. - */ - public reverseIndex(): void { - if (this.buffer.y === this.buffer.scrollTop) { - // possibly move the code below to term.reverseScroll(); - // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' - // blankLine(true) is xterm/linux behavior - const scrollRegionHeight = this.buffer.scrollBottom - this.buffer.scrollTop; - this.buffer.lines.shiftElements(this.buffer.y + this.buffer.ybase, scrollRegionHeight, 1); - this.buffer.lines.set(this.buffer.y + this.buffer.ybase, this.buffer.getBlankLine(this.eraseAttrData())); - this.updateRange(this.buffer.scrollTop); - this.updateRange(this.buffer.scrollBottom); - } else { - this.buffer.y--; - (this._inputHandler as any)._restrictCursor(); // quickfix to not run out of bounds - } - } - - /** - * ESC c Full Reset (RIS). + * Full reset of the terminal. */ public reset(): void { /** @@ -1907,14 +1866,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp } } - - /** - * ESC H Tab Set (HTS is 0x88). - */ - public tabSet(): void { - this.buffer.tabs[this.buffer.x] = true; - } - // TODO: Remove cancel function and cancelEvents option public cancel(ev: Event, force?: boolean): boolean { if (!this.options.cancelEvents && !force) { diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index 62ae609e..15f30565 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -295,21 +295,12 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal { addDisposableListener(type: string, handler: XtermListener): IDisposable { throw new Error('Method not implemented.'); } - tabSet(): void { - throw new Error('Method not implemented.'); - } handler(data: string): void { throw new Error('Method not implemented.'); } handleTitle(title: string): void { throw new Error('Method not implemented.'); } - index(): void { - throw new Error('Method not implemented.'); - } - reverseIndex(): void { - throw new Error('Method not implemented.'); - } } export class MockBuffer implements IBuffer { diff --git a/src/Types.d.ts b/src/Types.d.ts index 1ebc800e..0614d626 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -70,10 +70,7 @@ export interface IInputHandlingTerminal { showCursor(): void; refresh(start: number, end: number): void; error(text: string, data?: any): void; - tabSet(): void; handleTitle(title: string): void; - index(): void; - reverseIndex(): void; } export interface IViewport extends IDisposable { From 5122fdc441e247b330003db559d5754f701ed97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 8 Jul 2019 00:53:13 +0200 Subject: [PATCH 21/69] document wonky usage of Terminal.reset --- src/Terminal.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index d40d622f..c2b79ea3 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1824,7 +1824,12 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp } /** - * Full reset of the terminal. + * Reset terminal. + * Note: Calling this directly from JS is synchronous but does not clear + * input buffers and does not reset the parser, thus the terminal will + * continue to apply pending input data. + * If you need in band reset (synchronous with input data) consider + * using DECSTR (soft reset, CSI ! p) or RIS instead (hard reset, ESC c). */ public reset(): void { /** From bc867bf12b56f78f94983ff11cc79d8fab4a2572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 8 Jul 2019 02:11:46 +0200 Subject: [PATCH 22/69] partial fix DECSTBM IL/DL --- .../t0076-DECSTBM_IL_DL.text | 11 +-- src/InputHandler.test.ts | 75 ++++++++++++++++--- src/InputHandler.ts | 8 ++ src/Terminal2.test.ts | 4 +- 4 files changed, 82 insertions(+), 16 deletions(-) diff --git a/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.text b/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.text index 92c10331..f89893ba 100644 --- a/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.text +++ b/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.text @@ -1,3 +1,4 @@ + 6 C 8 ^^^^ 9 vvvv DL on line 11, expected: ACD_ 10 A @@ -12,14 +13,14 @@ 19 vvvv IL on line 21, expected: A_ 20 A + 22 ^^^^ - -23 vvvv IL on line 24, expected: _A +24 A 25 B -26 ^^^^ -28 A +27 vvvv DL on line 28, expected: B_ +28 A 29 B 30 ^^^^ 31 -32 +32 \ No newline at end of file diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index c21ca5f9..6d4a3ca8 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -15,6 +15,13 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { Params } from 'common/parser/Params'; import { MockCoreService } from 'common/TestUtils.test'; +function getCursor(term: TestTerminal): number[] { + return [ + term.buffer.x, + term.buffer.y + ]; +} + describe('InputHandler', () => { describe('save and restore cursor', () => { const terminal = new MockInputHandlingTerminal(); @@ -702,12 +709,6 @@ describe('InputHandler', () => { beforeEach(() => { term = new TestTerminal({cols: 10, rows: 10}); }); - function getCursor(term: TestTerminal): number[] { - return [ - term.buffer.x, - term.buffer.y - ]; - } it('cursor forward (CUF)', () => { term.writeSync('\x1b[C'); assert.deepEqual(getCursor(term), [1, 0]); @@ -1104,8 +1105,14 @@ describe('InputHandler', () => { 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('scrolling', () => { + describe('scroll margins', () => { let term: TestTerminal; beforeEach(() => { term = new TestTerminal({cols: 10, rows: 10}); @@ -1117,13 +1124,63 @@ describe('InputHandler', () => { } return res; } - it('scrollUp with margins', () => { + 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 with margins', () => { + 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']); + }); }); }); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 7bdd5fe4..1f1dee2e 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -847,6 +847,10 @@ export class InputHandler extends Disposable implements IInputHandler { // make buffer local for faster access const buffer = this._terminal.buffer; + if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) { + return; + } + const row: number = buffer.y + buffer.ybase; const scrollBottomRowsOffset = this._terminal.rows - 1 - buffer.scrollBottom; @@ -875,6 +879,10 @@ export class InputHandler extends Disposable implements IInputHandler { // make buffer local for faster access const buffer = this._terminal.buffer; + if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) { + return; + } + const row: number = buffer.y + buffer.ybase; let j: number; diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index b65a2e59..0555368b 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -119,12 +119,12 @@ if (os.platform() !== 'win32') { // 't0056-ED.in', // 't0060-DECSC.in', // 't0061-CSI_s.in', - 't0070-DECSTBM_LF.in', + 't0070-DECSTBM_LF.in', // lineFeed not working correctly 't0071-DECSTBM_IND.in', 't0072-DECSTBM_NEL.in', // 't0074-DECSTBM_SU_SD.in', 't0075-DECSTBM_CUU_CUD.in', - 't0076-DECSTBM_IL_DL.in', + 't0076-DECSTBM_IL_DL.in', // not working due to lineFeed 't0077-DECSTBM_quirks.in', // 't0080-HT.in', // 't0082-HTS.in', From cb25e2d4e0ce1ddd67260c4242b2902aac147d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 8 Jul 2019 02:42:02 +0200 Subject: [PATCH 23/69] save/restore charset in DECSC/DECRC --- src/InputHandler.ts | 5 +++++ src/TestUtils.test.ts | 3 ++- src/common/buffer/Buffer.ts | 4 +++- src/common/buffer/Types.d.ts | 3 ++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 1f1dee2e..14d18dfd 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -1938,6 +1938,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.buffer.savedY = this._terminal.buffer.ybase + this._terminal.buffer.y; this._terminal.buffer.savedCurAttrData.fg = this._terminal.curAttrData.fg; this._terminal.buffer.savedCurAttrData.bg = this._terminal.curAttrData.bg; + this._terminal.buffer.savedCharset = this._terminal.charset; } @@ -1951,6 +1952,10 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.buffer.y = Math.max(this._terminal.buffer.savedY - this._terminal.buffer.ybase, 0); this._terminal.curAttrData.fg = this._terminal.buffer.savedCurAttrData.fg; this._terminal.curAttrData.bg = this._terminal.buffer.savedCurAttrData.bg; + this._terminal.charset = (this as any)._savedCharset; + if (this._terminal.buffer.savedCharset) { + this._terminal.charset = this._terminal.buffer.savedCharset; + } this._restrictCursor(); } diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index 15f30565..e9a76f7f 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -6,7 +6,7 @@ import { IRenderer, IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types'; import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminal, IBrowser, ITerminalOptions, ILinkifier, ILinkMatcherOptions } from './Types'; import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types'; -import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener } from 'common/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 } from 'xterm'; @@ -320,6 +320,7 @@ export class MockBuffer implements IBuffer { scrollTop: number; savedY: number; savedX: number; + savedCharset: ICharset | null; savedCurAttrData = new AttributeData(); translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string { return Buffer.prototype.translateBufferLineToString.apply(this, arguments); diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index 9c36a5a4..1b3e5d86 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -5,13 +5,14 @@ import { CircularList, IInsertEvent } from 'common/CircularList'; import { IBuffer, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult } from 'common/buffer/Types'; -import { IBufferLine, ICellData, IAttributeData } from 'common/Types'; +import { IBufferLine, ICellData, IAttributeData, ICharset } from 'common/Types'; import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from 'common/buffer/Constants'; import { reflowLargerApplyNewLayout, reflowLargerCreateNewLayout, reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths, getWrappedLineTrimmedLength } from 'common/buffer/BufferReflow'; import { Marker } from 'common/buffer/Marker'; import { IOptionsService, IBufferService } from 'common/services/Services'; +import { DEFAULT_CHARSET } from 'common/data/Charsets'; export const MAX_BUFFER_SIZE = 4294967295; // 2^32 - 1 @@ -35,6 +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 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 2606df2f..532230ec 100644 --- a/src/common/buffer/Types.d.ts +++ b/src/common/buffer/Types.d.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IAttributeData, ICircularList, IBufferLine, ICellData, IMarker } from 'common/Types'; +import { IAttributeData, ICircularList, IBufferLine, ICellData, IMarker, ICharset } from 'common/Types'; import { IEvent } from 'common/EventEmitter'; // BufferIndex denotes a position in the buffer: [rowIndex, colIndex] @@ -31,6 +31,7 @@ export interface IBuffer { hasScrollback: boolean; savedY: number; savedX: number; + savedCharset: ICharset | null; savedCurAttrData: IAttributeData; isCursorInViewport: boolean; markers: IMarker[]; From e91c223bb097b8dd52423137dc63bde8e4dfe486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 8 Jul 2019 23:09:53 +0200 Subject: [PATCH 24/69] respect DECSTBM margins in DECOM mode --- src/InputHandler.ts | 16 ++++++++++++++-- src/Terminal2.test.ts | 5 +++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 14d18dfd..658b9445 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -615,11 +615,23 @@ export class InputHandler extends Disposable implements IInputHandler { } else if (this._terminal.buffer.y >= this._terminal.rows) { this._terminal.buffer.y = this._terminal.rows - 1; } + if (this._terminal.originMode) { + if (this._terminal.buffer.y < this._terminal.buffer.scrollTop) { + this._terminal.buffer.y = this._terminal.buffer.scrollTop; + } else if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) { + this._terminal.buffer.y = this._terminal.buffer.scrollBottom; + } + } } private _setCursor(x: number, y: number): void { - this._terminal.buffer.x = x; - this._terminal.buffer.y = y; + if (this._terminal.originMode) { + this._terminal.buffer.x = x; + this._terminal.buffer.y = this._terminal.buffer.scrollTop + y; + } else { + this._terminal.buffer.x = x; + this._terminal.buffer.y = y; + } this._restrictCursor(); } diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index 0555368b..e1af13fc 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -136,9 +136,10 @@ if (os.platform() !== 'win32') { // 't0100-IRM.in', 't0101-NLM.in', 't0103-reverse_wrap.in', - 't0504-vim.in', + 't0504-vim.in' + // vttest related files - 't0300-vttest1.in' + // 't0300-vttest1.in' ]; if (os.platform() === 'darwin') { // These are failing on macOS only From 3d6f87ea7f071a3ddd740553d8908521fba68c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 8 Jul 2019 23:22:46 +0200 Subject: [PATCH 25/69] reset cursor on DECOM --- src/InputHandler.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 658b9445..8e78a5b2 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -1286,6 +1286,7 @@ export class InputHandler extends Disposable implements IInputHandler { break; case 6: this._terminal.originMode = true; + this._setCursor(0, 0); break; case 7: this._terminal.wraparoundMode = true; @@ -1485,6 +1486,7 @@ export class InputHandler extends Disposable implements IInputHandler { break; case 6: this._terminal.originMode = false; + this._setCursor(0, 0); break; case 7: this._terminal.wraparoundMode = false; From 691a8addfc1ad81e0ed503b11966afcbdf7bd966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 8 Jul 2019 23:59:46 +0200 Subject: [PATCH 26/69] cleanup; doc; move cursor methods next to each other --- src/InputHandler.ts | 213 +++++++++++++++++++++++------------------- src/Terminal2.test.ts | 3 - 2 files changed, 115 insertions(+), 101 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 8e78a5b2..5d8c4922 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -581,26 +581,15 @@ export class InputHandler extends Disposable implements IInputHandler { } /** - * CSI Ps @ - * Insert Ps (Blank) Character(s) (default = 1) (ICH). + * Cursor movements. + * + * TODO: + * - create Cursor class living on Buffer + * - move private cursor methods to Cursor class as API */ - public insertChars(params: IParams): void { - this._restrictCursor(); - const line = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase); - if (line) { - line.insertCells( - this._terminal.buffer.x, - params.params[0] || 1, - this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) - ); - this._terminal.updateRange(this._terminal.buffer.y); - } - } /** - * FIXME: - * - create Cursor class living on Buffer - * - move these private cursor methods to Cursor class as API + * Restrict cursor to viewport size / scroll margin (origin mode). */ private _restrictCursor(): void { // cols @@ -624,6 +613,9 @@ export class InputHandler extends Disposable implements IInputHandler { } } + /** + * Set absolute cursor position. + */ private _setCursor(x: number, y: number): void { if (this._terminal.originMode) { this._terminal.buffer.x = x; @@ -635,6 +627,9 @@ export class InputHandler extends Disposable implements IInputHandler { this._restrictCursor(); } + /** + * Set relative cursor position. + */ private _moveCursor(x: number, y: number): void { // for relative changes we have to make sure we are within 0 .. cols/rows - 1 // before calculating the new position @@ -684,7 +679,6 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.buffer.x = 0; } - /** * CSI Ps F * Cursor Previous Line Ps Times (default = 1) (CPL). @@ -695,7 +689,6 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.buffer.x = 0; } - /** * CSI Ps G * Cursor Character Absolute [column] (default = [row,1]) (CHA). @@ -716,6 +709,68 @@ export class InputHandler extends Disposable implements IInputHandler { (params.params[0] || 1) - 1); } + /** + * CSI Pm ` Character Position Absolute + * [column] (default = [row,1]) (HPA). + * Currently same functionality as CHA. + */ + public charPosAbsolute(params: IParams): void { + this._setCursor((params.params[0] || 1) - 1, this._terminal.buffer.y); + } + + /** + * CSI Pm a Character Position Relative + * [columns] (default = [row,col+1]) (HPR) + * Currently same functionality as CUF. + */ + public hPositionRelative(params: IParams): void { + this._moveCursor(params.params[0] || 1, 0); + } + + /** + * CSI Pm d Vertical Position Absolute (VPA) + * [row] (default = [1,column]) + */ + public linePosAbsolute(params: IParams): void { + this._setCursor(this._terminal.buffer.x, (params.params[0] || 1) - 1); + } + + /** + * CSI Pm e Vertical Position Relative (VPR) + * [rows] (default = [row+1,column]) + * reuse CSI Ps B ? + */ + public vPositionRelative(params: IParams): void { + this._moveCursor(0, params.params[0] || 1); + } + + /** + * CSI Ps ; Ps f + * Horizontal and Vertical Position [row;column] (default = + * [1,1]) (HVP). + * Same as CUP. + */ + public hVPosition(params: IParams): void { + this.cursorPosition(params); + } + + /** + * CSI Ps g Tab Clear (TBC). + * Ps = 0 -> Clear Current Column (default). + * Ps = 3 -> Clear All. + * Potentially: + * Ps = 2 -> Clear Stops on Line. + * http://vt100.net/annarbor/aaa-ug/section6.html + */ + public tabClear(params: IParams): void { + const param = params.params[0]; + if (param === 0) { + delete this._terminal.buffer.tabs[this._terminal.buffer.x]; + } else if (param === 3) { + this._terminal.buffer.tabs = {}; + } + } + /** * CSI Ps I * Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). @@ -730,6 +785,24 @@ export class InputHandler extends Disposable implements IInputHandler { } } + /** + * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). + */ + public cursorBackwardTab(params: IParams): void { + if (this._terminal.buffer.x >= this._terminal.cols) { + return; + } + let param = params.params[0] || 1; + + // make buffer local for faster access + const buffer = this._terminal.buffer; + + while (param--) { + buffer.x = buffer.prevStop(); + } + } + + /** * Helper method to erase cells in a terminal row. * The cell gets replaced with the eraseChar of the terminal. @@ -913,6 +986,23 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only? } + /** + * CSI Ps @ + * Insert Ps (Blank) Character(s) (default = 1) (ICH). + */ + public insertChars(params: IParams): void { + this._restrictCursor(); + const line = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase); + if (line) { + line.insertCells( + this._terminal.buffer.x, + params.params[0] || 1, + this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) + ); + this._terminal.updateRange(this._terminal.buffer.y); + } + } + /** * CSI Ps P * Delete Ps Character(s) (default = 1) (DCH). @@ -985,41 +1075,6 @@ export class InputHandler extends Disposable implements IInputHandler { } } - /** - * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). - */ - public cursorBackwardTab(params: IParams): void { - if (this._terminal.buffer.x >= this._terminal.cols) { - return; - } - let param = params.params[0] || 1; - - // make buffer local for faster access - const buffer = this._terminal.buffer; - - while (param--) { - buffer.x = buffer.prevStop(); - } - } - - /** - * CSI Pm ` Character Position Absolute - * [column] (default = [row,1]) (HPA). - * Currently same functionality as CHA. - */ - public charPosAbsolute(params: IParams): void { - this._setCursor((params.params[0] || 1) - 1, this._terminal.buffer.y); - } - - /** - * CSI Pm a Character Position Relative - * [columns] (default = [row,col+1]) (HPR) - * Currently same functionality as CUF. - */ - public hPositionRelative(params: IParams): void { - this._moveCursor(params.params[0] || 1, 0); - } - /** * CSI Ps b Repeat the preceding graphic character Ps times (REP). * From ECMA 48 (@see http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf) @@ -1121,50 +1176,6 @@ export class InputHandler extends Disposable implements IInputHandler { } } - /** - * CSI Pm d Vertical Position Absolute (VPA) - * [row] (default = [1,column]) - */ - public linePosAbsolute(params: IParams): void { - this._setCursor(this._terminal.buffer.x, (params.params[0] || 1) - 1); - } - - /** - * CSI Pm e Vertical Position Relative (VPR) - * [rows] (default = [row+1,column]) - * reuse CSI Ps B ? - */ - public vPositionRelative(params: IParams): void { - this._moveCursor(0, params.params[0] || 1); - } - - /** - * CSI Ps ; Ps f - * Horizontal and Vertical Position [row;column] (default = - * [1,1]) (HVP). - * Same as CUP. - */ - public hVPosition(params: IParams): void { - this.cursorPosition(params); - } - - /** - * CSI Ps g Tab Clear (TBC). - * Ps = 0 -> Clear Current Column (default). - * Ps = 3 -> Clear All. - * Potentially: - * Ps = 2 -> Clear Stops on Line. - * http://vt100.net/annarbor/aaa-ug/section6.html - */ - public tabClear(params: IParams): void { - const param = params.params[0]; - if (param === 0) { - delete this._terminal.buffer.tabs[this._terminal.buffer.x]; - } else if (param === 3) { - this._terminal.buffer.tabs = {}; - } - } - /** * CSI Pm h Set Mode (SM). * Ps = 2 -> Keyboard Action Mode (AM). @@ -1280,6 +1291,7 @@ export class InputHandler extends Disposable implements IInputHandler { // set VT100 mode here break; case 3: // 132 col mode + // TODO: move DECCOLM into compat addon this._terminal.savedCols = this._terminal.cols; this._terminal.resize(132, this._terminal.rows); this._terminal.reset(); @@ -1478,6 +1490,9 @@ export class InputHandler extends Disposable implements IInputHandler { this._coreService.decPrivateModes.applicationCursorKeys = false; break; case 3: + // TODO: move DECCOLM into compat addon + // Note: This impl currently does not enforce col 80, instead reverts + // to previous terminal width before entering DECCOLM 132 if (this._terminal.cols === 132 && this._terminal.savedCols) { this._terminal.resize(this._terminal.savedCols, this._terminal.rows); } @@ -2140,6 +2155,8 @@ export class InputHandler extends Disposable implements IInputHandler { * DEC mnemonic: DECALN (https://vt100.net/docs/vt510-rm/DECALN.html) * This control function fills the complete screen area with * a test pattern (E) used for adjusting screen alignment. + * + * TODO: move DECALN into compat addon */ public screenAlignmentPattern(): void { // prepare cell data diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index e1af13fc..11728cd9 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -151,9 +151,6 @@ if (os.platform() !== 'win32') { ); } for (let i = 0; i < files.length; i++) { - // if (skip.indexOf(i) >= 0) { - // continue; - // } if (skipFilename.indexOf(files[i].split('/').slice(-1)[0]) >= 0) { continue; } From 9d2418ece63fbf7ffa831e0b1a47f3f8243b6ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Tue, 9 Jul 2019 21:41:17 +0200 Subject: [PATCH 27/69] remove comment --- src/InputHandler.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 5d8c4922..5e34c8b6 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -580,14 +580,6 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.setgLevel(0); } - /** - * Cursor movements. - * - * TODO: - * - create Cursor class living on Buffer - * - move private cursor methods to Cursor class as API - */ - /** * Restrict cursor to viewport size / scroll margin (origin mode). */ From ab25997192422dcd5ed0b0d5369818fced222d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Tue, 9 Jul 2019 22:30:00 +0200 Subject: [PATCH 28/69] change to max/min --- src/InputHandler.test.ts | 2 ++ src/InputHandler.ts | 23 ++++------------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 6d4a3ca8..d5f0ecd6 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -25,6 +25,8 @@ function getCursor(term: TestTerminal): number[] { describe('InputHandler', () => { describe('save and restore cursor', () => { const terminal = new MockInputHandlingTerminal(); + terminal.cols = 80; + terminal.rows = 30; terminal.buffer.x = 1; terminal.buffer.y = 2; terminal.buffer.ybase = 0; diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 5e34c8b6..150ed8bb 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -584,25 +584,10 @@ export class InputHandler extends Disposable implements IInputHandler { * Restrict cursor to viewport size / scroll margin (origin mode). */ private _restrictCursor(): void { - // cols - if (this._terminal.buffer.x < 0) { - this._terminal.buffer.x = 0; - } else if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } - // rows - if (this._terminal.buffer.y < 0) { - this._terminal.buffer.y = 0; - } else if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - if (this._terminal.originMode) { - if (this._terminal.buffer.y < this._terminal.buffer.scrollTop) { - this._terminal.buffer.y = this._terminal.buffer.scrollTop; - } else if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) { - this._terminal.buffer.y = this._terminal.buffer.scrollBottom; - } - } + this._terminal.buffer.x = Math.min(this._terminal.cols - 1, Math.max(0, this._terminal.buffer.x)); + this._terminal.buffer.y = this._terminal.originMode + ? Math.min(this._terminal.buffer.scrollBottom, Math.max(this._terminal.buffer.scrollTop, this._terminal.buffer.y)) + : Math.min(this._terminal.rows - 1, Math.max(0, this._terminal.buffer.y)); } /** From cc3bb83ced8c5d935bacc955ad8584d39b73e06c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Wed, 10 Jul 2019 14:57:49 +0200 Subject: [PATCH 29/69] getting rid of native access in test file --- src/Terminal2.test.ts | 257 ++++++++++++++++++------------------------ 1 file changed, 112 insertions(+), 145 deletions(-) diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index 11728cd9..178a669d 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -1,45 +1,126 @@ /** - * Copyright (c) 2016 The xterm.js authors. All rights reserved. + * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT - * - * This file contains integration tests for xterm.js. */ import * as glob from 'glob'; -import * as fs from 'fs'; -import * as os from 'os'; 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 { IViewport } from './Types'; -import { CellData } from 'common/buffer/CellData'; -import { WHITESPACE_CELL_CHAR } from 'common/buffer/Constants'; -class TestTerminal extends Terminal { - innerWrite(): void { this._innerWrite(); } +// 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 SKIP_FILES = [ + // 't0008-BS.in', + // 't0014-CAN.in', + // 't0015-SUB.in', + // 't0017-SD.in', + // 't0035-HVP.in', + // 't0050-ICH.in', + // 't0051-IL.in', + // 't0052-DL.in', + // 't0055-EL.in', + // 't0056-ED.in', + // 't0060-DECSC.in', + // 't0061-CSI_s.in', + 't0070-DECSTBM_LF.in', // lineFeed not working correctly + 't0071-DECSTBM_IND.in', + 't0072-DECSTBM_NEL.in', + // 't0074-DECSTBM_SU_SD.in', + 't0075-DECSTBM_CUU_CUD.in', + 't0076-DECSTBM_IL_DL.in', // not working due to lineFeed + 't0077-DECSTBM_quirks.in', + // 't0080-HT.in', + // 't0082-HTS.in', + // 't0083-CHT.in', + 't0084-CBT.in', + // 't0090-alt_screen.in', + // 't0091-alt_screen_ED3.in', + // 't0092-alt_screen_DECSC.in', + // 't0100-IRM.in', + 't0101-NLM.in', + 't0103-reverse_wrap.in', + 't0504-vim.in' + + // vttest related files + // 't0300-vttest1.in' +]; +if (os.platform() === 'darwin') { + // These are failing on macOS only (termios related?) + SKIP_FILES.push( + 't0003-line_wrap.in', + 't0005-CR.in', + 't0009-NEL.in', + 't0503-zsh_ls_color.in' + ); } +// filter skipFilenames +const FILES = TESTFILES.filter(value => SKIP_FILES.indexOf(value.split('/').slice(-1)[0]) === -1); -let primitivePty: any; -// fake sychronous pty write - read -// we just pipe the data from slave to master as a child program would do -// pty.js opens pipe fds with O_NONBLOCK -// just wait 10ms instead of setting fds to blocking mode -function ptyWriteRead(data: string, cb: (result: string) => void): void { - fs.writeSync(primitivePty.slave, data); - setTimeout(() => { - const b = new Buffer(64000); - const bytes = fs.readSync(primitivePty.master, b, 0, 64000, null); - cb(b.toString('utf8', 0, bytes)); +describe('Escape Sequence Files', function(): void { + this.timeout(20000); + + let ptyTerm: any = null; + let slaveEnd: any = null; + let term: Terminal; + let customHandler: any = null; + + before(() => { + ptyTerm = (pty as any).open({cols: COLS, rows: ROWS}); + slaveEnd = ptyTerm._slave; + term = new Terminal({cols: COLS, rows: ROWS}); + ptyTerm._master.on('data', (data: string) => term.write(data)); }); -} -// make sure raw pty is at x=0 and has no pending data -function ptyReset(cb: (result: string) => void): void { - ptyWriteRead('\r\n', cb); -} + after(() => { + ptyTerm.end(); + }); + + FILES.forEach(filename => { + it(filename.split('/').slice(-1)[0], async () => { + // reset terminal and handler + if (customHandler) { + customHandler.dispose(); + } + slaveEnd.write('\r\n'); + term.reset(); + slaveEnd.write('\x1bc\x1b[H'); + + // register handler to trigger viewport scraping, wait for it to finish + let content = ''; + await new Promise(resolve => { + customHandler = term.addOscHandler(12345, () => { + // grab terminal viewport content + content = terminalToString(term); + resolve(); + return true; + }); + // write file to slave + slaveEnd.write(fs.readFileSync(filename, 'utf8')); + // trigger custom sequence + slaveEnd.write('\x1b]12345;\x07'); + }); + + // compare with expected output (right trimmed) + const expected = fs.readFileSync(filename.split('.')[0] + '.text', 'utf8'); + const expectedRightTrimmed = expected.split('\n').map(l => l.replace(/\s+$/, '')).join('\n'); + if (content !== expectedRightTrimmed) { + throw new Error(formatError(fs.readFileSync(filename, 'utf8'), content, expected)); + } + }); + }); +}); + +/** + * Helpers + */ -/* debug helpers */ // generate colorful noisy output to compare xterm and emulator cell states function formatError(input: string, output: string, expected: string): string { function addLineNumber(start: number, color: string): (s: string) => string { @@ -51,10 +132,10 @@ function formatError(input: string, output: string, expected: string): string { } const line80 = '12345678901234567890123456789012345678901234567890123456789012345678901234567890'; let s = ''; - s += '\n\x1b[34m' + JSON.stringify(input); - s += '\n\x1b[33m ' + line80 + '\n'; + s += `\n\x1b[34m${JSON.stringify(input)}`; + s += `\n\x1b[33m ${line80}\n`; s += output.split('\n').map(addLineNumber(0, '\x1b[31m')).join('\n'); - s += '\n\x1b[33m ' + line80 + '\n'; + s += `\n\x1b[33m ${line80}\n`; s += expected.split('\n').map(addLineNumber(0, '\x1b[32m')).join('\n'); return s; } @@ -64,10 +145,7 @@ function terminalToString(term: Terminal): string { let result = ''; let lineText = ''; for (let line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) { - lineText = ''; - for (let cell = 0; cell < term.cols; ++cell) { - lineText += term.buffer.lines.get(line).loadCell(cell, new CellData()).getChars() || WHITESPACE_CELL_CHAR; - } + lineText = term.buffer.lines.get(line).translateToString(true); // rtrim empty cells as xterm does lineText = lineText.replace(/\s+$/, ''); result += lineText; @@ -75,114 +153,3 @@ function terminalToString(term: Terminal): string { } return result; } - -// Skip tests on Windows since pty.open isn't supported -if (os.platform() !== 'win32') { - const consoleLog = console.log; - - // expect files need terminal at 80x25! - const cols = 80; - const rows = 25; - - /** some helpers for pty interaction */ - // we need a pty in between to get the termios decorations - // for the basic test cases a raw pty device is enough - primitivePty = (pty).native.open(cols, rows); - - /** tests */ - describe('xterm output comparison', function(): void { - this.timeout(10000); - let xterm: TestTerminal; - - beforeEach(() => { - xterm = new TestTerminal({ cols: cols, rows: rows }); - xterm.refresh = () => {}; - xterm.viewport = { - syncScrollArea: () => {} - }; - }); - - // omit stack trace for escape sequence files - Error.stackTraceLimit = 0; - const files = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '..')}); - // only successful tests for now - const skipFilename = [ - // 't0008-BS.in', - // 't0014-CAN.in', - // 't0015-SUB.in', - // 't0017-SD.in', - // 't0035-HVP.in', - // 't0050-ICH.in', - // 't0051-IL.in', - // 't0052-DL.in', - // 't0055-EL.in', - // 't0056-ED.in', - // 't0060-DECSC.in', - // 't0061-CSI_s.in', - 't0070-DECSTBM_LF.in', // lineFeed not working correctly - 't0071-DECSTBM_IND.in', - 't0072-DECSTBM_NEL.in', - // 't0074-DECSTBM_SU_SD.in', - 't0075-DECSTBM_CUU_CUD.in', - 't0076-DECSTBM_IL_DL.in', // not working due to lineFeed - 't0077-DECSTBM_quirks.in', - // 't0080-HT.in', - // 't0082-HTS.in', - // 't0083-CHT.in', - 't0084-CBT.in', - // 't0090-alt_screen.in', - // 't0091-alt_screen_ED3.in', - // 't0092-alt_screen_DECSC.in', - // 't0100-IRM.in', - 't0101-NLM.in', - 't0103-reverse_wrap.in', - 't0504-vim.in' - - // vttest related files - // 't0300-vttest1.in' - ]; - if (os.platform() === 'darwin') { - // These are failing on macOS only - skipFilename.push( - 't0003-line_wrap.in', - 't0005-CR.in', - 't0009-NEL.in', - 't0503-zsh_ls_color.in' - ); - } - for (let i = 0; i < files.length; i++) { - if (skipFilename.indexOf(files[i].split('/').slice(-1)[0]) >= 0) { - continue; - } - ((filename: string) => { - const inFile = fs.readFileSync(filename, 'utf8'); - it(filename.split('/').slice(-1)[0], done => { - ptyReset(() => { - ptyWriteRead(inFile, fromPty => { - // uncomment this to get log from terminal - // console.log = function(){}; - - // Perform a synchronous .write(data) - xterm.writeBuffer.push(fromPty); - xterm.innerWrite(); - - const fromEmulator = terminalToString(xterm); - console.log = consoleLog; - const expected = fs.readFileSync(filename.split('.')[0] + '.text', 'utf8'); - - // Some of the tests have whitespace on the right of lines, we trim all the linex - // from xterm.js so ignore this for now at least. - const expectedRightTrimmed = expected.split('\n').map(l => l.replace(/\s+$/, '')).join('\n'); - if (fromEmulator !== expectedRightTrimmed) { - // uncomment to get noisy output - throw new Error(formatError(inFile, fromEmulator, expected)); - // throw new Error('mismatch'); - } - done(); - }); - }); - }); - })(files[i]); - } - }); -} From 24b26c73118776bfd9e995bf0cc29363830cc9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Wed, 10 Jul 2019 23:39:05 +0200 Subject: [PATCH 30/69] force closing of pty --- src/Terminal2.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index 178a669d..ad306f32 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -79,7 +79,8 @@ describe('Escape Sequence Files', function(): void { }); after(() => { - ptyTerm.end(); + ptyTerm._master.end(); + ptyTerm._master.destroy(); }); FILES.forEach(filename => { From eb707bb1d7a1473635c4e0052c709304c9675ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 11 Jul 2019 02:41:20 +0200 Subject: [PATCH 31/69] better typing in test file --- src/Terminal2.test.ts | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/src/Terminal2.test.ts b/src/Terminal2.test.ts index ad306f32..cbd790d5 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -9,6 +9,7 @@ import * as os from 'os'; import * as fs from 'fs'; import * as pty from 'node-pty'; import { Terminal } from './Terminal'; +import { IDisposable } from 'xterm'; // all test files expect terminal in 80x25 const COLS = 80; @@ -16,39 +17,16 @@ const ROWS = 25; const TESTFILES = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '..')}); const SKIP_FILES = [ - // 't0008-BS.in', - // 't0014-CAN.in', - // 't0015-SUB.in', - // 't0017-SD.in', - // 't0035-HVP.in', - // 't0050-ICH.in', - // 't0051-IL.in', - // 't0052-DL.in', - // 't0055-EL.in', - // 't0056-ED.in', - // 't0060-DECSC.in', - // 't0061-CSI_s.in', 't0070-DECSTBM_LF.in', // lineFeed not working correctly 't0071-DECSTBM_IND.in', 't0072-DECSTBM_NEL.in', - // 't0074-DECSTBM_SU_SD.in', 't0075-DECSTBM_CUU_CUD.in', 't0076-DECSTBM_IL_DL.in', // not working due to lineFeed 't0077-DECSTBM_quirks.in', - // 't0080-HT.in', - // 't0082-HTS.in', - // 't0083-CHT.in', 't0084-CBT.in', - // 't0090-alt_screen.in', - // 't0091-alt_screen_ED3.in', - // 't0092-alt_screen_DECSC.in', - // 't0100-IRM.in', 't0101-NLM.in', 't0103-reverse_wrap.in', 't0504-vim.in' - - // vttest related files - // 't0300-vttest1.in' ]; if (os.platform() === 'darwin') { // These are failing on macOS only (termios related?) @@ -66,10 +44,10 @@ const FILES = TESTFILES.filter(value => SKIP_FILES.indexOf(value.split('/').slic describe('Escape Sequence Files', function(): void { this.timeout(20000); - let ptyTerm: any = null; - let slaveEnd: any = null; + let ptyTerm: any; + let slaveEnd: any; let term: Terminal; - let customHandler: any = null; + let customHandler: IDisposable | undefined; before(() => { ptyTerm = (pty as any).open({cols: COLS, rows: ROWS}); @@ -95,8 +73,9 @@ describe('Escape Sequence Files', function(): void { // register handler to trigger viewport scraping, wait for it to finish let content = ''; + const OSC_CODE = 12345; await new Promise(resolve => { - customHandler = term.addOscHandler(12345, () => { + customHandler = term.addOscHandler(OSC_CODE, () => { // grab terminal viewport content content = terminalToString(term); resolve(); @@ -105,7 +84,7 @@ describe('Escape Sequence Files', function(): void { // write file to slave slaveEnd.write(fs.readFileSync(filename, 'utf8')); // trigger custom sequence - slaveEnd.write('\x1b]12345;\x07'); + slaveEnd.write(`\x1b]${OSC_CODE};\x07`); }); // compare with expected output (right trimmed) From da9b4b1a692590e2f4f3eb738108fe8832342c6d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 11 Jul 2019 09:34:45 -0700 Subject: [PATCH 32/69] Don't trigger selection when mouse events are on Fixes #2301 --- src/Terminal.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Terminal.ts b/src/Terminal.ts index 1e51f439..c625114b 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -644,6 +644,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp } else { this._selectionService.enable(); } + this._inputHandler.setBrowserServices(this._selectionService); if (this.options.screenReaderMode) { // Note that this must be done *after* the renderer is created in order to From 7a37bd880e4f0df1d3213fcbf54c8b78ddf07349 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 11 Jul 2019 11:04:11 -0700 Subject: [PATCH 33/69] Add test for mouse events --- test/api/InputHandler.api.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/api/InputHandler.api.ts b/test/api/InputHandler.api.ts index d9c9bf89..dfd16ddb 100644 --- a/test/api/InputHandler.api.ts +++ b/test/api/InputHandler.api.ts @@ -232,6 +232,31 @@ describe('InputHandler Integration Tests', function(): void { describe('SM: Set Mode', () => { describe('CSI ? Pm h', () => { + it('Pm = 1003, Set Use All Motion (any event) Mouse Tracking', async() => { + const coords = await page.evaluate(` + (function() { + const rect = window.term.element.getBoundingClientRect(); + return {left: rect.left, top: rect.top, bottom: rect.bottom, right: rect.right}; + })(); + `); + // Click and drag and ensure there is a selection + await page.mouse.click((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 2); + await page.mouse.down(); + await page.mouse.move((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 4); + assert.ok(await page.evaluate(`window.term.getSelection().length`) > 0, 'mouse events are off so there should be a selection'); + await page.mouse.up(); + // Clear selection + await page.mouse.click((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 2); + assert.equal(await page.evaluate(`window.term.getSelection().length`), 0); + // Enable mouse events + await page.evaluate(`window.term.write('\x1b[?1003h')`); + // Click and drag and ensure there is no selection + await page.mouse.click((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 2); + await page.mouse.down(); + await page.mouse.move((coords.left + coords.right) / 2, (coords.top + coords.bottom) / 4); + assert.equal(await page.evaluate(`window.term.getSelection().length`), 0, 'mouse events are on so there should be no selection'); + await page.mouse.up(); + }); it('Pm = 2004, Set bracketed paste mode', async function(): Promise { assert.equal(await simulatePaste('foo'), 'foo'); await page.evaluate(`window.term.write('\x1b[?2004h')`); From b929f8210115101ba02b3d881cf75dfaf4bc859e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 00:23:56 -0700 Subject: [PATCH 34/69] Move CompositionHelper into browser --- src/Terminal.ts | 4 +-- .../input}/CompositionHelper.test.ts | 22 +++------------- src/{ => browser/input}/CompositionHelper.ts | 26 +++++++------------ src/browser/services/SelectionService.test.ts | 2 +- 4 files changed, 17 insertions(+), 37 deletions(-) rename src/{ => browser/input}/CompositionHelper.test.ts (94%) rename src/{ => browser/input}/CompositionHelper.ts (90%) diff --git a/src/Terminal.ts b/src/Terminal.ts index 54ae3322..695cf103 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -23,7 +23,7 @@ import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, IMouseZoneManager } from './Types'; import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; -import { CompositionHelper } from './CompositionHelper'; +import { CompositionHelper } from './browser/input/CompositionHelper'; import { Viewport } from './Viewport'; import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './Clipboard'; import { C0 } from 'common/data/EscapeSequences'; @@ -581,7 +581,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._compositionView = document.createElement('div'); this._compositionView.classList.add('composition-view'); - this._compositionHelper = new CompositionHelper(this.textarea, this._compositionView, this, this._charSizeService, this._coreService); + this._compositionHelper = new CompositionHelper(this.textarea, this._compositionView, this._bufferService, this.optionsService, this._charSizeService, this._coreService); this._helperContainer.appendChild(this._compositionView); // Performance: Add viewport and helper elements from the fragment diff --git a/src/CompositionHelper.test.ts b/src/browser/input/CompositionHelper.test.ts similarity index 94% rename from src/CompositionHelper.test.ts rename to src/browser/input/CompositionHelper.test.ts index 64207fa0..b9a4f668 100644 --- a/src/CompositionHelper.test.ts +++ b/src/browser/input/CompositionHelper.test.ts @@ -4,13 +4,11 @@ */ import { assert } from 'chai'; -import { CompositionHelper } from './CompositionHelper'; -import { ITerminal } from './Types'; +import { CompositionHelper } from 'browser/input/CompositionHelper'; import { MockCharSizeService } from 'browser/TestUtils.test'; -import { MockCoreService } from '../out/common/TestUtils.test'; +import { MockCoreService, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; describe('CompositionHelper', () => { - let terminal: ITerminal; let compositionHelper: CompositionHelper; let compositionView: HTMLElement; let textarea: HTMLTextAreaElement; @@ -38,25 +36,13 @@ describe('CompositionHelper', () => { top: 0 } } as any; - terminal = { - element: { - querySelector: () => { - return { offsetLeft: 0, offsetTop: 0 }; - } - }, - buffer: { - isCursorInViewport: true - }, - options: { - lineHeight: 1 - } - } as any; const coreService = new MockCoreService(); coreService.triggerDataEvent = (text: string) => { handledText += text; }; handledText = ''; - compositionHelper = new CompositionHelper(textarea, compositionView, terminal, new MockCharSizeService(10, 10), coreService); + const bufferService = new MockBufferService(10, 5); + compositionHelper = new CompositionHelper(textarea, compositionView, bufferService, new MockOptionsService(), new MockCharSizeService(10, 10), coreService); }); describe('Input', () => { diff --git a/src/CompositionHelper.ts b/src/browser/input/CompositionHelper.ts similarity index 90% rename from src/CompositionHelper.ts rename to src/browser/input/CompositionHelper.ts index 010585f8..a8c8bb14 100644 --- a/src/CompositionHelper.ts +++ b/src/browser/input/CompositionHelper.ts @@ -3,9 +3,8 @@ * @license MIT */ -import { ITerminal } from './Types'; import { ICharSizeService } from 'browser/services/Services'; -import { ICoreService } from 'common/services/Services'; +import { IBufferService, ICoreService, IOptionsService } from 'common/services/Services'; interface IPosition { start: number; @@ -35,22 +34,17 @@ export class CompositionHelper { */ private _isSendingComposition: boolean; - /** - * Creates a new CompositionHelper. - * @param _textarea The textarea that xterm uses for input. - * @param _compositionView The element to display the in-progress composition in. - * @param _terminal The Terminal to forward the finished composition to. - */ constructor( private readonly _textarea: HTMLTextAreaElement, private readonly _compositionView: HTMLElement, - private readonly _terminal: ITerminal, + private readonly _bufferService: IBufferService, + private readonly _optionsService: IOptionsService, private readonly _charSizeService: ICharSizeService, private readonly _coreService: ICoreService ) { this._isComposing = false; this._isSendingComposition = false; - this._compositionPosition = { start: null, end: null }; + this._compositionPosition = { start: 0, end: 0 }; } /** @@ -198,17 +192,17 @@ export class CompositionHelper { return; } - if (this._terminal.buffer.isCursorInViewport) { - const cellHeight = Math.ceil(this._charSizeService.height * this._terminal.options.lineHeight); - const cursorTop = this._terminal.buffer.y * cellHeight; - const cursorLeft = this._terminal.buffer.x * this._charSizeService.width; + if (this._bufferService.buffer.isCursorInViewport) { + const cellHeight = Math.ceil(this._charSizeService.height * this._optionsService.options.lineHeight); + const cursorTop = this._bufferService.buffer.y * cellHeight; + const cursorLeft = this._bufferService.buffer.x * this._charSizeService.width; this._compositionView.style.left = cursorLeft + 'px'; this._compositionView.style.top = cursorTop + 'px'; this._compositionView.style.height = cellHeight + 'px'; this._compositionView.style.lineHeight = cellHeight + 'px'; - this._compositionView.style.fontFamily = this._terminal.options.fontFamily; - this._compositionView.style.fontSize = this._terminal.options.fontSize + 'px'; + this._compositionView.style.fontFamily = this._optionsService.options.fontFamily; + this._compositionView.style.fontSize = this._optionsService.options.fontSize + 'px'; // Sync the textarea to the exact position of the composition view so the IME knows where the // text is. const compositionViewBounds = this._compositionView.getBoundingClientRect(); diff --git a/src/browser/services/SelectionService.test.ts b/src/browser/services/SelectionService.test.ts index fc9cafdd..b00fa29f 100644 --- a/src/browser/services/SelectionService.test.ts +++ b/src/browser/services/SelectionService.test.ts @@ -13,7 +13,7 @@ import { IBufferService, IOptionsService } from 'common/services/Services'; import { MockCharSizeService, MockMouseService } from 'browser/TestUtils.test'; import { CellData } from 'common/buffer/CellData'; import { IBuffer } from 'common/buffer/Types'; -import { isWindows } from '../../../out/common/Platform'; +import { isWindows } from 'common/Platform'; class TestSelectionService extends SelectionService { constructor( From 27a946286af3da1a907e728c1bbb50cb4236187e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 00:25:37 -0700 Subject: [PATCH 35/69] Make browser import absolute --- src/Terminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 695cf103..f519ae58 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -23,7 +23,7 @@ import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, IMouseZoneManager } from './Types'; import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; -import { CompositionHelper } from './browser/input/CompositionHelper'; +import { CompositionHelper } from 'browser/input/CompositionHelper'; import { Viewport } from './Viewport'; import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './Clipboard'; import { C0 } from 'common/data/EscapeSequences'; From dd239b98110d8a0437edda141d6b85357e5e9e5e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 00:52:54 -0700 Subject: [PATCH 36/69] Use IBufferService for buffer access in InputHandler --- src/InputHandler.test.ts | 270 ++++++++++++++++++++------------------- src/InputHandler.ts | 208 +++++++++++++++--------------- src/Terminal.ts | 2 +- 3 files changed, 246 insertions(+), 234 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index d5f0ecd6..ff477b9c 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -13,7 +13,8 @@ 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 } from 'common/TestUtils.test'; +import { MockCoreService, MockBufferService } from 'common/TestUtils.test'; +import { IBufferService } from 'common/services/Services'; function getCursor(term: TestTerminal): number[] { return [ @@ -27,30 +28,31 @@ describe('InputHandler', () => { const terminal = new MockInputHandlingTerminal(); terminal.cols = 80; terminal.rows = 30; - terminal.buffer.x = 1; - terminal.buffer.y = 2; - terminal.buffer.ybase = 0; terminal.curAttrData.fg = 3; - const inputHandler = new InputHandler(terminal, new MockCoreService()); + const bufferService = new MockBufferService(80, 30); + bufferService.buffer.x = 1; + bufferService.buffer.y = 2; + bufferService.buffer.ybase = 0; + const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService()); // Save cursor position inputHandler.saveCursor(); - assert.equal(terminal.buffer.x, 1); - assert.equal(terminal.buffer.y, 2); + assert.equal(bufferService.buffer.x, 1); + assert.equal(bufferService.buffer.y, 2); assert.equal(terminal.curAttrData.fg, 3); // Change cursor position - terminal.buffer.x = 10; - terminal.buffer.y = 20; + bufferService.buffer.x = 10; + bufferService.buffer.y = 20; terminal.curAttrData.fg = 30; // Restore cursor position inputHandler.restoreCursor(); - assert.equal(terminal.buffer.x, 1); - assert.equal(terminal.buffer.y, 2); + assert.equal(bufferService.buffer.x, 1); + assert.equal(bufferService.buffer.y, 2); assert.equal(terminal.curAttrData.fg, 3); }); describe('setCursorStyle', () => { it('should call Terminal.setOption with correct params', () => { const terminal = new MockInputHandlingTerminal(); - const inputHandler = new InputHandler(terminal, new MockCoreService()); + const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService()); const collect = ' '; inputHandler.setCursorStyle(Params.fromArray([0]), collect); @@ -93,7 +95,7 @@ describe('InputHandler', () => { const terminal = new MockInputHandlingTerminal(); const collect = '?'; terminal.bracketedPasteMode = false; - const inputHandler = new InputHandler(terminal, new MockCoreService()); + const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService()); // Set bracketed paste mode inputHandler.setMode(Params.fromArray([2004]), collect); assert.equal(terminal.bracketedPasteMode, true); @@ -103,92 +105,95 @@ describe('InputHandler', () => { }); }); describe('regression tests', function(): void { - function termContent(term: Terminal, trim: boolean): string[] { + function termContent(bufferService: IBufferService, trim: boolean): string[] { const result = []; - for (let i = 0; i < term.rows; ++i) result.push(term.buffer.lines.get(i).translateToString(trim)); + 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 inputHandler = new InputHandler(term, new MockCoreService()); + const bufferService = new MockBufferService(80, 30); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService()); // insert some data in first and second line inputHandler.parse(Array(term.cols - 9).join('a')); inputHandler.parse('1234567890'); inputHandler.parse(Array(term.cols - 9).join('a')); inputHandler.parse('1234567890'); - const line1: IBufferLine = term.buffer.lines.get(0); + const line1: IBufferLine = bufferService.buffer.lines.get(0); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '1234567890'); // insert one char from params = [0] - term.buffer.y = 0; - term.buffer.x = 70; + bufferService.buffer.y = 0; + bufferService.buffer.x = 70; inputHandler.insertChars(Params.fromArray([0])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' 123456789'); // insert one char from params = [1] - term.buffer.y = 0; - term.buffer.x = 70; + bufferService.buffer.y = 0; + bufferService.buffer.x = 70; inputHandler.insertChars(Params.fromArray([1])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' 12345678'); // insert two chars from params = [2] - term.buffer.y = 0; - term.buffer.x = 70; + bufferService.buffer.y = 0; + bufferService.buffer.x = 70; inputHandler.insertChars(Params.fromArray([2])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' 123456'); // insert 10 chars from params = [10] - term.buffer.y = 0; - term.buffer.x = 70; + bufferService.buffer.y = 0; + bufferService.buffer.x = 70; inputHandler.insertChars(Params.fromArray([10])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' '); expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a')); }); it('deleteChars', function(): void { const term = new Terminal(); - const inputHandler = new InputHandler(term, new MockCoreService()); + const bufferService = new MockBufferService(80, 30); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService()); // insert some data in first and second line inputHandler.parse(Array(term.cols - 9).join('a')); inputHandler.parse('1234567890'); inputHandler.parse(Array(term.cols - 9).join('a')); inputHandler.parse('1234567890'); - const line1: IBufferLine = term.buffer.lines.get(0); + const line1: IBufferLine = bufferService.buffer.lines.get(0); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '1234567890'); // delete one char from params = [0] - term.buffer.y = 0; - term.buffer.x = 70; + bufferService.buffer.y = 0; + bufferService.buffer.x = 70; inputHandler.deleteChars(Params.fromArray([0])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '234567890 '); expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a') + '234567890'); // insert one char from params = [1] - term.buffer.y = 0; - term.buffer.x = 70; + bufferService.buffer.y = 0; + bufferService.buffer.x = 70; inputHandler.deleteChars(Params.fromArray([1])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '34567890 '); expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a') + '34567890'); // insert two chars from params = [2] - term.buffer.y = 0; - term.buffer.x = 70; + bufferService.buffer.y = 0; + bufferService.buffer.x = 70; inputHandler.deleteChars(Params.fromArray([2])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '567890 '); expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a') + '567890'); // insert 10 chars from params = [10] - term.buffer.y = 0; - term.buffer.x = 70; + bufferService.buffer.y = 0; + bufferService.buffer.x = 70; inputHandler.deleteChars(Params.fromArray([10])); expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' '); expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a')); }); it('eraseInLine', function(): void { const term = new Terminal(); - const inputHandler = new InputHandler(term, new MockCoreService()); + const bufferService = new MockBufferService(80, 30); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService()); // fill 6 lines to test 3 different states inputHandler.parse(Array(term.cols + 1).join('a')); @@ -196,101 +201,102 @@ describe('InputHandler', () => { inputHandler.parse(Array(term.cols + 1).join('a')); // params[0] - right erase - term.buffer.y = 0; - term.buffer.x = 70; + bufferService.buffer.y = 0; + bufferService.buffer.x = 70; inputHandler.eraseInLine(Params.fromArray([0])); - expect(term.buffer.lines.get(0).translateToString(false)).equals(Array(71).join('a') + ' '); + expect(bufferService.buffer.lines.get(0).translateToString(false)).equals(Array(71).join('a') + ' '); // params[1] - left erase - term.buffer.y = 1; - term.buffer.x = 70; + bufferService.buffer.y = 1; + bufferService.buffer.x = 70; inputHandler.eraseInLine(Params.fromArray([1])); - expect(term.buffer.lines.get(1).translateToString(false)).equals(Array(71).join(' ') + ' aaaaaaaaa'); + expect(bufferService.buffer.lines.get(1).translateToString(false)).equals(Array(71).join(' ') + ' aaaaaaaaa'); // params[1] - left erase - term.buffer.y = 2; - term.buffer.x = 70; + bufferService.buffer.y = 2; + bufferService.buffer.x = 70; inputHandler.eraseInLine(Params.fromArray([2])); - expect(term.buffer.lines.get(2).translateToString(false)).equals(Array(term.cols + 1).join(' ')); + expect(bufferService.buffer.lines.get(2).translateToString(false)).equals(Array(term.cols + 1).join(' ')); }); it('eraseInDisplay', function(): void { const term = new Terminal({cols: 80, rows: 7}); - const inputHandler = new InputHandler(term, new MockCoreService()); + const bufferService = new MockBufferService(80, 7); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService()); // fill display with a's - for (let i = 0; i < term.rows; ++i) inputHandler.parse(Array(term.cols + 1).join('a')); + for (let i = 0; i < term.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a')); // params [0] - right and below erase - term.buffer.y = 5; - term.buffer.x = 40; + bufferService.buffer.y = 5; + bufferService.buffer.x = 40; inputHandler.eraseInDisplay(Params.fromArray([0])); - expect(termContent(term, false)).eql([ - Array(term.cols + 1).join('a'), - Array(term.cols + 1).join('a'), - Array(term.cols + 1).join('a'), - Array(term.cols + 1).join('a'), - Array(term.cols + 1).join('a'), - Array(40 + 1).join('a') + Array(term.cols - 40 + 1).join(' '), - Array(term.cols + 1).join(' ') + 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(term, true)).eql([ - Array(term.cols + 1).join('a'), - Array(term.cols + 1).join('a'), - Array(term.cols + 1).join('a'), - Array(term.cols + 1).join('a'), - Array(term.cols + 1).join('a'), + 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 - term.buffer.y = 0; - term.buffer.x = 0; - for (let i = 0; i < term.rows; ++i) inputHandler.parse(Array(term.cols + 1).join('a')); + 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 - term.buffer.y = 5; - term.buffer.x = 40; + bufferService.buffer.y = 5; + bufferService.buffer.x = 40; inputHandler.eraseInDisplay(Params.fromArray([1])); - expect(termContent(term, false)).eql([ - Array(term.cols + 1).join(' '), - Array(term.cols + 1).join(' '), - Array(term.cols + 1).join(' '), - Array(term.cols + 1).join(' '), - Array(term.cols + 1).join(' '), - Array(41 + 1).join(' ') + Array(term.cols - 41 + 1).join('a'), - Array(term.cols + 1).join('a') + 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(term, true)).eql([ + expect(termContent(bufferService, true)).eql([ '', '', '', '', '', - Array(41 + 1).join(' ') + Array(term.cols - 41 + 1).join('a'), - Array(term.cols + 1).join('a') + Array(41 + 1).join(' ') + Array(bufferService.cols - 41 + 1).join('a'), + Array(bufferService.cols + 1).join('a') ]); // reset - term.buffer.y = 0; - term.buffer.x = 0; - for (let i = 0; i < term.rows; ++i) inputHandler.parse(Array(term.cols + 1).join('a')); + 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 - term.buffer.y = 5; - term.buffer.x = 40; + bufferService.buffer.y = 5; + bufferService.buffer.x = 40; inputHandler.eraseInDisplay(Params.fromArray([2])); - expect(termContent(term, false)).eql([ - Array(term.cols + 1).join(' '), - Array(term.cols + 1).join(' '), - Array(term.cols + 1).join(' '), - Array(term.cols + 1).join(' '), - Array(term.cols + 1).join(' '), - Array(term.cols + 1).join(' '), - Array(term.cols + 1).join(' ') + 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(term, true)).eql([ + expect(termContent(bufferService, true)).eql([ '', '', '', @@ -301,34 +307,34 @@ describe('InputHandler', () => { ]); // reset and add a wrapped line - term.buffer.y = 0; - term.buffer.x = 0; - inputHandler.parse(Array(term.cols + 1).join('a')); // line 0 - inputHandler.parse(Array(term.cols + 10).join('a')); // line 1 and 2 - for (let i = 3; i < term.rows; ++i) inputHandler.parse(Array(term.cols + 1).join('a')); + 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(term.buffer.lines.get(2).isWrapped).true; - term.buffer.y = 2; - term.buffer.x = 40; + expect(bufferService.buffer.lines.get(2).isWrapped).true; + bufferService.buffer.y = 2; + bufferService.buffer.x = 40; inputHandler.eraseInDisplay(Params.fromArray([1])); - expect(term.buffer.lines.get(2).isWrapped).false; + expect(bufferService.buffer.lines.get(2).isWrapped).false; // reset and add a wrapped line - term.buffer.y = 0; - term.buffer.x = 0; - inputHandler.parse(Array(term.cols + 1).join('a')); // line 0 - inputHandler.parse(Array(term.cols + 10).join('a')); // line 1 and 2 - for (let i = 3; i < term.rows; ++i) inputHandler.parse(Array(term.cols + 1).join('a')); + 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(term.buffer.lines.get(2).isWrapped).true; - term.buffer.y = 1; - term.buffer.x = 90; // Cursor is beyond last column + 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(term.buffer.lines.get(2).isWrapped).false; + expect(bufferService.buffer.lines.get(2).isWrapped).false; }); }); it('convertEol setting', function(): void { @@ -351,7 +357,7 @@ describe('InputHandler', () => { describe('print', () => { it('should not cause an infinite loop (regression test)', () => { const term = new Terminal(); - const inputHandler = new InputHandler(term, new MockCoreService()); + const inputHandler = new InputHandler(term, new MockBufferService(80, 30), new MockCoreService()); const container = new Uint32Array(10); container[0] = 0x200B; inputHandler.print(container, 0, 1); @@ -360,56 +366,58 @@ describe('InputHandler', () => { describe('alt screen', () => { let term: Terminal; + let bufferService: IBufferService; let handler: InputHandler; beforeEach(() => { term = new Terminal(); - handler = new InputHandler(term, new MockCoreService()); + bufferService = new MockBufferService(80, 30); + handler = new InputHandler(term, bufferService, new MockCoreService()); }); it('should handle DECSET/DECRST 47 (alt screen buffer)', () => { handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST'); - expect(term.buffer.translateBufferLineToString(0, true)).to.equal(''); - expect(term.buffer.translateBufferLineToString(1, true)).to.equal(' TEST'); + 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((term.buffer.lines.get(1).loadCell(4, new CellData()).getFgColor())).to.equal(1); + 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(term.buffer.translateBufferLineToString(0, true)).to.equal(''); - expect(term.buffer.translateBufferLineToString(1, true)).to.equal(' TEST'); + 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((term.buffer.lines.get(1).loadCell(4, new CellData()).getFgColor())).to.equal(1); + 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(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST'); - expect(term.buffer.translateBufferLineToString(1, true)).to.equal('JUNK'); + 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(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg); + 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((term.buffer.lines.get(1).loadCell(0, new CellData()).getFgColor())).to.equal(1); + 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(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST'); - expect(term.buffer.translateBufferLineToString(1, true)).to.equal(''); + 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(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg); + 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(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST'); + expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST'); // Text color of 'TEST' should be default - expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg); + expect(bufferService.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg); handler.parse('\x1b[?1049h\x1b[uTEST'); - expect(term.buffer.translateBufferLineToString(1, true)).to.equal('TEST'); + expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('TEST'); // Text color of 'TEST' should be red - expect((term.buffer.lines.get(1).loadCell(0, new CellData()).getFgColor())).to.equal(1); + 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(term.buffer.lines.get(20).loadCell(10, new CellData()).getBgColor()).to.equal(2); + expect(bufferService.buffer.lines.get(20).loadCell(10, new CellData()).getBgColor()).to.equal(2); }); }); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 150ed8bb..11565354 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -19,7 +19,7 @@ import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content import { CellData } from 'common/buffer/CellData'; import { AttributeData } from 'common/buffer/AttributeData'; import { IAttributeData, IDisposable } from 'common/Types'; -import { ICoreService } from 'common/services/Services'; +import { ICoreService, IBufferService } from 'common/services/Services'; import { ISelectionService } from 'browser/services/Services'; /** @@ -41,7 +41,10 @@ const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1, class DECRQSS implements IDcsHandler { private _data: Uint32Array = new Uint32Array(0); - constructor(private _terminal: any) { } + constructor( + private _terminal: any, + private _bufferService: IBufferService + ) { } hook(collect: string, params: IParams, flag: number): void { this._data = new Uint32Array(0); @@ -61,8 +64,8 @@ class DECRQSS implements IDcsHandler { case '"p': // DECSCL return this._terminal.handler(`${C0.ESC}P1$r61"p${C0.ESC}\\`); case 'r': // DECSTBM - const pt = '' + (this._terminal.buffer.scrollTop + 1) + - ';' + (this._terminal.buffer.scrollBottom + 1) + 'r'; + const pt = '' + (this._bufferService.buffer.scrollTop + 1) + + ';' + (this._bufferService.buffer.scrollBottom + 1) + 'r'; return this._terminal.handler(`${C0.ESC}P1$r${pt}${C0.ESC}\\`); case 'm': // SGR // TODO: report real settings instead of 0m @@ -124,6 +127,7 @@ export class InputHandler extends Disposable implements IInputHandler { constructor( protected _terminal: IInputHandlingTerminal, + private _bufferService: IBufferService, private _coreService: ICoreService, private _parser: IEscapeSequenceParser = new EscapeSequenceParser()) { @@ -293,7 +297,7 @@ export class InputHandler extends Disposable implements IInputHandler { /** * DCS handler */ - this._parser.setDcsHandler('$q', new DECRQSS(this._terminal)); + this._parser.setDcsHandler('$q', new DECRQSS(this._terminal, this._bufferService)); } public dispose(): void { @@ -312,7 +316,7 @@ export class InputHandler extends Disposable implements IInputHandler { return; } - let buffer = this._terminal.buffer; + let buffer = this._bufferService.buffer; const cursorStartX = buffer.x; const cursorStartY = buffer.y; @@ -326,7 +330,7 @@ export class InputHandler extends Disposable implements IInputHandler { } this._parser.parse(this._parseBuffer, this._stringDecoder.decode(data, this._parseBuffer)); - buffer = this._terminal.buffer; + buffer = this._bufferService.buffer; if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) { this._onCursorMove.fire(); } @@ -338,7 +342,7 @@ export class InputHandler extends Disposable implements IInputHandler { return; } - let buffer = this._terminal.buffer; + let buffer = this._bufferService.buffer; const cursorStartX = buffer.x; const cursorStartY = buffer.y; @@ -352,7 +356,7 @@ export class InputHandler extends Disposable implements IInputHandler { } this._parser.parse(this._parseBuffer, this._utf8Decoder.decode(data, this._parseBuffer)); - buffer = this._terminal.buffer; + buffer = this._bufferService.buffer; if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) { this._onCursorMove.fire(); } @@ -361,7 +365,7 @@ export class InputHandler extends Disposable implements IInputHandler { public print(data: Uint32Array, start: number, end: number): void { let code: number; let chWidth: number; - const buffer = this._terminal.buffer; + const buffer = this._bufferService.buffer; const charset = this._terminal.charset; const screenReaderMode = this._terminal.options.screenReaderMode; const cols = this._terminal.cols; @@ -510,7 +514,7 @@ export class InputHandler extends Disposable implements IInputHandler { */ public lineFeed(): void { // make buffer local for faster access - const buffer = this._terminal.buffer; + const buffer = this._bufferService.buffer; if (this._terminal.options.convertEol) { buffer.x = 0; @@ -533,7 +537,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Carriage Return (Ctrl-M). */ public carriageReturn(): void { - this._terminal.buffer.x = 0; + this._bufferService.buffer.x = 0; } /** @@ -542,8 +546,8 @@ export class InputHandler extends Disposable implements IInputHandler { */ public backspace(): void { this._restrictCursor(); - if (this._terminal.buffer.x > 0) { - this._terminal.buffer.x--; + if (this._bufferService.buffer.x > 0) { + this._bufferService.buffer.x--; } } @@ -552,13 +556,13 @@ export class InputHandler extends Disposable implements IInputHandler { * Horizontal Tab (HT) (Ctrl-I). */ public tab(): void { - if (this._terminal.buffer.x >= this._terminal.cols) { + if (this._bufferService.buffer.x >= this._terminal.cols) { return; } - const originalX = this._terminal.buffer.x; - this._terminal.buffer.x = this._terminal.buffer.nextStop(); + const originalX = this._bufferService.buffer.x; + this._bufferService.buffer.x = this._bufferService.buffer.nextStop(); if (this._terminal.options.screenReaderMode) { - this._terminal.onA11yTabEmitter.fire(this._terminal.buffer.x - originalX); + this._terminal.onA11yTabEmitter.fire(this._bufferService.buffer.x - originalX); } } @@ -584,10 +588,10 @@ export class InputHandler extends Disposable implements IInputHandler { * Restrict cursor to viewport size / scroll margin (origin mode). */ private _restrictCursor(): void { - this._terminal.buffer.x = Math.min(this._terminal.cols - 1, Math.max(0, this._terminal.buffer.x)); - this._terminal.buffer.y = this._terminal.originMode - ? Math.min(this._terminal.buffer.scrollBottom, Math.max(this._terminal.buffer.scrollTop, this._terminal.buffer.y)) - : Math.min(this._terminal.rows - 1, Math.max(0, this._terminal.buffer.y)); + this._bufferService.buffer.x = Math.min(this._terminal.cols - 1, Math.max(0, this._bufferService.buffer.x)); + this._bufferService.buffer.y = this._terminal.originMode + ? Math.min(this._bufferService.buffer.scrollBottom, Math.max(this._bufferService.buffer.scrollTop, this._bufferService.buffer.y)) + : Math.min(this._terminal.rows - 1, Math.max(0, this._bufferService.buffer.y)); } /** @@ -595,11 +599,11 @@ export class InputHandler extends Disposable implements IInputHandler { */ private _setCursor(x: number, y: number): void { if (this._terminal.originMode) { - this._terminal.buffer.x = x; - this._terminal.buffer.y = this._terminal.buffer.scrollTop + y; + this._bufferService.buffer.x = x; + this._bufferService.buffer.y = this._bufferService.buffer.scrollTop + y; } else { - this._terminal.buffer.x = x; - this._terminal.buffer.y = y; + this._bufferService.buffer.x = x; + this._bufferService.buffer.y = y; } this._restrictCursor(); } @@ -611,7 +615,7 @@ export class InputHandler extends Disposable implements IInputHandler { // for relative changes we have to make sure we are within 0 .. cols/rows - 1 // before calculating the new position this._restrictCursor(); - this._setCursor(this._terminal.buffer.x + x, this._terminal.buffer.y + y); + this._setCursor(this._bufferService.buffer.x + x, this._bufferService.buffer.y + y); } /** @@ -653,7 +657,7 @@ export class InputHandler extends Disposable implements IInputHandler { */ public cursorNextLine(params: IParams): void { this._moveCursor(0, params.params[0] || 1); - this._terminal.buffer.x = 0; + this._bufferService.buffer.x = 0; } /** @@ -663,7 +667,7 @@ export class InputHandler extends Disposable implements IInputHandler { */ public cursorPrecedingLine(params: IParams): void { this._moveCursor(0, -(params.params[0] || 1)); - this._terminal.buffer.x = 0; + this._bufferService.buffer.x = 0; } /** @@ -671,7 +675,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Cursor Character Absolute [column] (default = [row,1]) (CHA). */ public cursorCharAbsolute(params: IParams): void { - this._setCursor((params.params[0] || 1) - 1, this._terminal.buffer.y); + this._setCursor((params.params[0] || 1) - 1, this._bufferService.buffer.y); } /** @@ -692,7 +696,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Currently same functionality as CHA. */ public charPosAbsolute(params: IParams): void { - this._setCursor((params.params[0] || 1) - 1, this._terminal.buffer.y); + this._setCursor((params.params[0] || 1) - 1, this._bufferService.buffer.y); } /** @@ -709,7 +713,7 @@ export class InputHandler extends Disposable implements IInputHandler { * [row] (default = [1,column]) */ public linePosAbsolute(params: IParams): void { - this._setCursor(this._terminal.buffer.x, (params.params[0] || 1) - 1); + this._setCursor(this._bufferService.buffer.x, (params.params[0] || 1) - 1); } /** @@ -742,9 +746,9 @@ export class InputHandler extends Disposable implements IInputHandler { public tabClear(params: IParams): void { const param = params.params[0]; if (param === 0) { - delete this._terminal.buffer.tabs[this._terminal.buffer.x]; + delete this._bufferService.buffer.tabs[this._bufferService.buffer.x]; } else if (param === 3) { - this._terminal.buffer.tabs = {}; + this._bufferService.buffer.tabs = {}; } } @@ -753,12 +757,12 @@ export class InputHandler extends Disposable implements IInputHandler { * Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). */ public cursorForwardTab(params: IParams): void { - if (this._terminal.buffer.x >= this._terminal.cols) { + if (this._bufferService.buffer.x >= this._terminal.cols) { return; } let param = params.params[0] || 1; while (param--) { - this._terminal.buffer.x = this._terminal.buffer.nextStop(); + this._bufferService.buffer.x = this._bufferService.buffer.nextStop(); } } @@ -766,13 +770,13 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). */ public cursorBackwardTab(params: IParams): void { - if (this._terminal.buffer.x >= this._terminal.cols) { + if (this._bufferService.buffer.x >= this._terminal.cols) { return; } let param = params.params[0] || 1; // make buffer local for faster access - const buffer = this._terminal.buffer; + const buffer = this._bufferService.buffer; while (param--) { buffer.x = buffer.prevStop(); @@ -788,11 +792,11 @@ export class InputHandler extends Disposable implements IInputHandler { * @param end end - 1 is last erased cell */ private _eraseInBufferLine(y: number, start: number, end: number, clearWrap: boolean = false): void { - const line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + y); + const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y); line.replaceCells( start, end, - this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) + this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()) ); if (clearWrap) { line.isWrapped = false; @@ -805,8 +809,8 @@ export class InputHandler extends Disposable implements IInputHandler { * @param y row index */ private _resetBufferLine(y: number): void { - const line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + y); - line.fill(this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())); + const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y); + line.fill(this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData())); line.isWrapped = false; } @@ -827,22 +831,22 @@ export class InputHandler extends Disposable implements IInputHandler { let j; switch (params.params[0]) { case 0: - j = this._terminal.buffer.y; + j = this._bufferService.buffer.y; this._terminal.updateRange(j); - this._eraseInBufferLine(j++, this._terminal.buffer.x, this._terminal.cols, this._terminal.buffer.x === 0); + this._eraseInBufferLine(j++, this._bufferService.buffer.x, this._terminal.cols, this._bufferService.buffer.x === 0); for (; j < this._terminal.rows; j++) { this._resetBufferLine(j); } this._terminal.updateRange(j); break; case 1: - j = this._terminal.buffer.y; + j = this._bufferService.buffer.y; this._terminal.updateRange(j); // Deleted front part of line and everything before. This line will no longer be wrapped. - this._eraseInBufferLine(j, 0, this._terminal.buffer.x + 1, true); - if (this._terminal.buffer.x + 1 >= this._terminal.cols) { + this._eraseInBufferLine(j, 0, this._bufferService.buffer.x + 1, true); + if (this._bufferService.buffer.x + 1 >= this._terminal.cols) { // Deleted entire previous line. This next line can no longer be wrapped. - this._terminal.buffer.lines.get(j + 1).isWrapped = false; + this._bufferService.buffer.lines.get(j + 1).isWrapped = false; } while (j--) { this._resetBufferLine(j); @@ -859,11 +863,11 @@ export class InputHandler extends Disposable implements IInputHandler { break; case 3: // Clear scrollback (everything not in viewport) - const scrollBackSize = this._terminal.buffer.lines.length - this._terminal.rows; + const scrollBackSize = this._bufferService.buffer.lines.length - this._terminal.rows; if (scrollBackSize > 0) { - this._terminal.buffer.lines.trimStart(scrollBackSize); - this._terminal.buffer.ybase = Math.max(this._terminal.buffer.ybase - scrollBackSize, 0); - this._terminal.buffer.ydisp = Math.max(this._terminal.buffer.ydisp - scrollBackSize, 0); + this._bufferService.buffer.lines.trimStart(scrollBackSize); + this._bufferService.buffer.ybase = Math.max(this._bufferService.buffer.ybase - scrollBackSize, 0); + this._bufferService.buffer.ydisp = Math.max(this._bufferService.buffer.ydisp - scrollBackSize, 0); // Force a scroll event to refresh viewport this._onScroll.fire(0); } @@ -886,16 +890,16 @@ export class InputHandler extends Disposable implements IInputHandler { this._restrictCursor(); switch (params.params[0]) { case 0: - this._eraseInBufferLine(this._terminal.buffer.y, this._terminal.buffer.x, this._terminal.cols); + this._eraseInBufferLine(this._bufferService.buffer.y, this._bufferService.buffer.x, this._terminal.cols); break; case 1: - this._eraseInBufferLine(this._terminal.buffer.y, 0, this._terminal.buffer.x + 1); + this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.buffer.x + 1); break; case 2: - this._eraseInBufferLine(this._terminal.buffer.y, 0, this._terminal.cols); + this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._terminal.cols); break; } - this._terminal.updateRange(this._terminal.buffer.y); + this._terminal.updateRange(this._bufferService.buffer.y); } /** @@ -907,7 +911,7 @@ export class InputHandler extends Disposable implements IInputHandler { let param = params.params[0] || 1; // make buffer local for faster access - const buffer = this._terminal.buffer; + const buffer = this._bufferService.buffer; if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) { return; @@ -939,7 +943,7 @@ export class InputHandler extends Disposable implements IInputHandler { let param = params.params[0] || 1; // make buffer local for faster access - const buffer = this._terminal.buffer; + const buffer = this._bufferService.buffer; if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) { return; @@ -969,14 +973,14 @@ export class InputHandler extends Disposable implements IInputHandler { */ public insertChars(params: IParams): void { this._restrictCursor(); - const line = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase); + const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase); if (line) { line.insertCells( - this._terminal.buffer.x, + this._bufferService.buffer.x, params.params[0] || 1, - this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) + this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()) ); - this._terminal.updateRange(this._terminal.buffer.y); + this._terminal.updateRange(this._bufferService.buffer.y); } } @@ -986,14 +990,14 @@ export class InputHandler extends Disposable implements IInputHandler { */ public deleteChars(params: IParams): void { this._restrictCursor(); - const line = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase); + const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase); if (line) { line.deleteCells( - this._terminal.buffer.x, + this._bufferService.buffer.x, params.params[0] || 1, - this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) + this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()) ); - this._terminal.updateRange(this._terminal.buffer.y); + this._terminal.updateRange(this._bufferService.buffer.y); } } @@ -1004,7 +1008,7 @@ export class InputHandler extends Disposable implements IInputHandler { let param = params.params[0] || 1; // make buffer local for faster access - const buffer = this._terminal.buffer; + const buffer = this._bufferService.buffer; while (param--) { buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1); @@ -1023,7 +1027,7 @@ export class InputHandler extends Disposable implements IInputHandler { let param = params.params[0] || 1; // make buffer local for faster access - const buffer = this._terminal.buffer; + const buffer = this._bufferService.buffer; while (param--) { buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1); @@ -1041,14 +1045,14 @@ export class InputHandler extends Disposable implements IInputHandler { */ public eraseChars(params: IParams): void { this._restrictCursor(); - const line = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase); + const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase); if (line) { line.replaceCells( - this._terminal.buffer.x, - this._terminal.buffer.x + (params.params[0] || 1), - this._terminal.buffer.getNullCell(this._terminal.eraseAttrData()) + this._bufferService.buffer.x, + this._bufferService.buffer.x + (params.params[0] || 1), + this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()) ); - this._terminal.updateRange(this._terminal.buffer.y); + this._terminal.updateRange(this._bufferService.buffer.y); } } @@ -1349,7 +1353,7 @@ export class InputHandler extends Disposable implements IInputHandler { // FALL-THROUGH case 47: // alt screen buffer case 1047: // alt screen buffer - this._terminal.buffers.activateAltBuffer(this._terminal.eraseAttrData()); + this._bufferService.buffers.activateAltBuffer(this._terminal.eraseAttrData()); this._terminal.refresh(0, this._terminal.rows - 1); if (this._terminal.viewport) { this._terminal.viewport.syncScrollArea(); @@ -1531,7 +1535,7 @@ export class InputHandler extends Disposable implements IInputHandler { case 47: // normal screen buffer case 1047: // normal screen buffer - clearing it first // Ensure the selection manager has the correct buffer - this._terminal.buffers.activateNormalBuffer(); + this._bufferService.buffers.activateNormalBuffer(); if (param === 1049) { this.restoreCursor(); } @@ -1815,8 +1819,8 @@ export class InputHandler extends Disposable implements IInputHandler { break; case 6: // cursor position - const y = this._terminal.buffer.y + 1; - const x = this._terminal.buffer.x + 1; + const y = this._bufferService.buffer.y + 1; + const x = this._bufferService.buffer.x + 1; this._coreService.triggerDataEvent(`${C0.ESC}[${y};${x}R`); break; } @@ -1826,8 +1830,8 @@ export class InputHandler extends Disposable implements IInputHandler { switch (params.params[0]) { case 6: // cursor position - const y = this._terminal.buffer.y + 1; - const x = this._terminal.buffer.x + 1; + const y = this._bufferService.buffer.y + 1; + const x = this._bufferService.buffer.x + 1; this._coreService.triggerDataEvent(`${C0.ESC}[?${y};${x}R`); break; case 15: @@ -1865,10 +1869,10 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.viewport.syncScrollArea(); } this._coreService.decPrivateModes.applicationCursorKeys = false; - this._terminal.buffer.scrollTop = 0; - this._terminal.buffer.scrollBottom = this._terminal.rows - 1; + this._bufferService.buffer.scrollTop = 0; + this._bufferService.buffer.scrollBottom = this._terminal.rows - 1; this._terminal.curAttrData = DEFAULT_ATTR_DATA.clone(); - this._terminal.buffer.x = this._terminal.buffer.y = 0; // ? + this._bufferService.buffer.x = this._bufferService.buffer.y = 0; // ? this._terminal.charset = null; this._terminal.glevel = 0; // ?? this._terminal.charsets = [null]; // ?? @@ -1927,8 +1931,8 @@ export class InputHandler extends Disposable implements IInputHandler { } if (bottom > top) { - this._terminal.buffer.scrollTop = top - 1; - this._terminal.buffer.scrollBottom = bottom - 1; + this._bufferService.buffer.scrollTop = top - 1; + this._bufferService.buffer.scrollBottom = bottom - 1; this._setCursor(0, 0); } } @@ -1940,11 +1944,11 @@ export class InputHandler extends Disposable implements IInputHandler { * Save cursor (ANSI.SYS). */ public saveCursor(params?: IParams): void { - this._terminal.buffer.savedX = this._terminal.buffer.x; - this._terminal.buffer.savedY = this._terminal.buffer.ybase + this._terminal.buffer.y; - this._terminal.buffer.savedCurAttrData.fg = this._terminal.curAttrData.fg; - this._terminal.buffer.savedCurAttrData.bg = this._terminal.curAttrData.bg; - this._terminal.buffer.savedCharset = this._terminal.charset; + this._bufferService.buffer.savedX = this._bufferService.buffer.x; + this._bufferService.buffer.savedY = this._bufferService.buffer.ybase + this._bufferService.buffer.y; + this._bufferService.buffer.savedCurAttrData.fg = this._terminal.curAttrData.fg; + this._bufferService.buffer.savedCurAttrData.bg = this._terminal.curAttrData.bg; + this._bufferService.buffer.savedCharset = this._terminal.charset; } @@ -1954,13 +1958,13 @@ export class InputHandler extends Disposable implements IInputHandler { * Restore cursor (ANSI.SYS). */ public restoreCursor(params?: IParams): void { - this._terminal.buffer.x = this._terminal.buffer.savedX || 0; - this._terminal.buffer.y = Math.max(this._terminal.buffer.savedY - this._terminal.buffer.ybase, 0); - this._terminal.curAttrData.fg = this._terminal.buffer.savedCurAttrData.fg; - this._terminal.curAttrData.bg = this._terminal.buffer.savedCurAttrData.bg; + this._bufferService.buffer.x = this._bufferService.buffer.savedX || 0; + this._bufferService.buffer.y = Math.max(this._bufferService.buffer.savedY - this._bufferService.buffer.ybase, 0); + this._terminal.curAttrData.fg = this._bufferService.buffer.savedCurAttrData.fg; + this._terminal.curAttrData.bg = this._bufferService.buffer.savedCurAttrData.bg; this._terminal.charset = (this as any)._savedCharset; - if (this._terminal.buffer.savedCharset) { - this._terminal.charset = this._terminal.buffer.savedCharset; + if (this._bufferService.buffer.savedCharset) { + this._terminal.charset = this._bufferService.buffer.savedCharset; } this._restrictCursor(); } @@ -1982,7 +1986,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Moves cursor to first position on next line. */ public nextLine(): void { - this._terminal.buffer.x = 0; + this._bufferService.buffer.x = 0; this.index(); } @@ -2059,9 +2063,9 @@ export class InputHandler extends Disposable implements IInputHandler { */ public index(): void { this._restrictCursor(); - this._terminal.buffer.y++; - if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) { - this._terminal.buffer.y--; + this._bufferService.buffer.y++; + if (this._bufferService.buffer.y > this._bufferService.buffer.scrollBottom) { + this._bufferService.buffer.y--; this._terminal.scroll(); } this._restrictCursor(); @@ -2075,7 +2079,7 @@ export class InputHandler extends Disposable implements IInputHandler { * the value of the active column when the terminal receives an HTS. */ public tabSet(): void { - this._terminal.buffer.tabs[this._terminal.buffer.x] = true; + this._bufferService.buffer.tabs[this._bufferService.buffer.x] = true; } /** @@ -2087,7 +2091,7 @@ export class InputHandler extends Disposable implements IInputHandler { */ public reverseIndex(): void { this._restrictCursor(); - const buffer = this._terminal.buffer; + const buffer = this._bufferService.buffer; if (buffer.y === buffer.scrollTop) { // possibly move the code below to term.reverseScroll(); // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' @@ -2142,7 +2146,7 @@ export class InputHandler extends Disposable implements IInputHandler { cell.fg = this._terminal.curAttrData.fg; cell.bg = this._terminal.curAttrData.bg; - const buffer = this._terminal.buffer; + const buffer = this._bufferService.buffer; this._setCursor(0, 0); for (let yOffset = 0; yOffset < this._terminal.rows; ++yOffset) { diff --git a/src/Terminal.ts b/src/Terminal.ts index f519ae58..8c5c2bd8 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -297,7 +297,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._userScrolling = false; // Register input handler and refire/handle events - this._inputHandler = new InputHandler(this, this._coreService); + this._inputHandler = new InputHandler(this, this._bufferService, this._coreService); this._inputHandler.onCursorMove(() => this._onCursorMove.fire()); this._inputHandler.onLineFeed(() => this._onLineFeed.fire()); this.register(this._inputHandler); From 49fa86d6bbf129e0daba53e92998df49309fb2e2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 01:38:02 -0700 Subject: [PATCH 37/69] Get rows/cols in input hander from buffer service --- src/InputHandler.test.ts | 50 +++++++++++++++++----------------- src/InputHandler.ts | 58 ++++++++++++++++++++-------------------- 2 files changed, 53 insertions(+), 55 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index ff477b9c..352eec77 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -26,8 +26,6 @@ function getCursor(term: TestTerminal): number[] { describe('InputHandler', () => { describe('save and restore cursor', () => { const terminal = new MockInputHandlingTerminal(); - terminal.cols = 80; - terminal.rows = 30; terminal.curAttrData.fg = 3; const bufferService = new MockBufferService(80, 30); bufferService.buffer.x = 1; @@ -117,37 +115,37 @@ describe('InputHandler', () => { const inputHandler = new InputHandler(term, bufferService, new MockCoreService()); // insert some data in first and second line - inputHandler.parse(Array(term.cols - 9).join('a')); + inputHandler.parse(Array(bufferService.cols - 9).join('a')); inputHandler.parse('1234567890'); - inputHandler.parse(Array(term.cols - 9).join('a')); + 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(term.cols - 9).join('a') + '1234567890'); + 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(term.cols - 9).join('a') + ' 123456789'); + 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(term.cols - 9).join('a') + ' 12345678'); + 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(term.cols - 9).join('a') + ' 123456'); + 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(term.cols - 9).join('a') + ' '); - expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a')); + 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(); @@ -155,40 +153,40 @@ describe('InputHandler', () => { const inputHandler = new InputHandler(term, bufferService, new MockCoreService()); // insert some data in first and second line - inputHandler.parse(Array(term.cols - 9).join('a')); + inputHandler.parse(Array(bufferService.cols - 9).join('a')); inputHandler.parse('1234567890'); - inputHandler.parse(Array(term.cols - 9).join('a')); + 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(term.cols - 9).join('a') + '1234567890'); + 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(term.cols - 9).join('a') + '234567890 '); - expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a') + '234567890'); + 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(term.cols - 9).join('a') + '34567890 '); - expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a') + '34567890'); + 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(term.cols - 9).join('a') + '567890 '); - expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a') + '567890'); + 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(term.cols - 9).join('a') + ' '); - expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a')); + 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(); @@ -196,9 +194,9 @@ describe('InputHandler', () => { const inputHandler = new InputHandler(term, bufferService, new MockCoreService()); // fill 6 lines to test 3 different states - inputHandler.parse(Array(term.cols + 1).join('a')); - inputHandler.parse(Array(term.cols + 1).join('a')); - inputHandler.parse(Array(term.cols + 1).join('a')); + 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; @@ -216,7 +214,7 @@ describe('InputHandler', () => { bufferService.buffer.y = 2; bufferService.buffer.x = 70; inputHandler.eraseInLine(Params.fromArray([2])); - expect(bufferService.buffer.lines.get(2).translateToString(false)).equals(Array(term.cols + 1).join(' ')); + expect(bufferService.buffer.lines.get(2).translateToString(false)).equals(Array(bufferService.cols + 1).join(' ')); }); it('eraseInDisplay', function(): void { @@ -225,7 +223,7 @@ describe('InputHandler', () => { const inputHandler = new InputHandler(term, bufferService, new MockCoreService()); // fill display with a's - for (let i = 0; i < term.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a')); + 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; diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 11565354..507c7d85 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -368,7 +368,7 @@ export class InputHandler extends Disposable implements IInputHandler { const buffer = this._bufferService.buffer; const charset = this._terminal.charset; const screenReaderMode = this._terminal.options.screenReaderMode; - const cols = this._terminal.cols; + const cols = this._bufferService.cols; const wraparoundMode = this._terminal.wraparoundMode; const insertMode = this._terminal.insertMode; const curAttr = this._terminal.curAttrData; @@ -525,7 +525,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.scroll(); } // If the end of the line is hit, prevent this action from wrapping around to the next line. - if (buffer.x >= this._terminal.cols) { + if (buffer.x >= this._bufferService.cols) { buffer.x--; } @@ -556,7 +556,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Horizontal Tab (HT) (Ctrl-I). */ public tab(): void { - if (this._bufferService.buffer.x >= this._terminal.cols) { + if (this._bufferService.buffer.x >= this._bufferService.cols) { return; } const originalX = this._bufferService.buffer.x; @@ -588,10 +588,10 @@ export class InputHandler extends Disposable implements IInputHandler { * Restrict cursor to viewport size / scroll margin (origin mode). */ private _restrictCursor(): void { - this._bufferService.buffer.x = Math.min(this._terminal.cols - 1, Math.max(0, this._bufferService.buffer.x)); + this._bufferService.buffer.x = Math.min(this._bufferService.cols - 1, Math.max(0, this._bufferService.buffer.x)); this._bufferService.buffer.y = this._terminal.originMode ? Math.min(this._bufferService.buffer.scrollBottom, Math.max(this._bufferService.buffer.scrollTop, this._bufferService.buffer.y)) - : Math.min(this._terminal.rows - 1, Math.max(0, this._bufferService.buffer.y)); + : Math.min(this._bufferService.rows - 1, Math.max(0, this._bufferService.buffer.y)); } /** @@ -757,7 +757,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). */ public cursorForwardTab(params: IParams): void { - if (this._bufferService.buffer.x >= this._terminal.cols) { + if (this._bufferService.buffer.x >= this._bufferService.cols) { return; } let param = params.params[0] || 1; @@ -770,7 +770,7 @@ export class InputHandler extends Disposable implements IInputHandler { * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). */ public cursorBackwardTab(params: IParams): void { - if (this._bufferService.buffer.x >= this._terminal.cols) { + if (this._bufferService.buffer.x >= this._bufferService.cols) { return; } let param = params.params[0] || 1; @@ -833,8 +833,8 @@ export class InputHandler extends Disposable implements IInputHandler { case 0: j = this._bufferService.buffer.y; this._terminal.updateRange(j); - this._eraseInBufferLine(j++, this._bufferService.buffer.x, this._terminal.cols, this._bufferService.buffer.x === 0); - for (; j < this._terminal.rows; j++) { + this._eraseInBufferLine(j++, this._bufferService.buffer.x, this._bufferService.cols, this._bufferService.buffer.x === 0); + for (; j < this._bufferService.rows; j++) { this._resetBufferLine(j); } this._terminal.updateRange(j); @@ -844,7 +844,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.updateRange(j); // Deleted front part of line and everything before. This line will no longer be wrapped. this._eraseInBufferLine(j, 0, this._bufferService.buffer.x + 1, true); - if (this._bufferService.buffer.x + 1 >= this._terminal.cols) { + 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; } @@ -854,7 +854,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.updateRange(0); break; case 2: - j = this._terminal.rows; + j = this._bufferService.rows; this._terminal.updateRange(j - 1); while (j--) { this._resetBufferLine(j); @@ -863,7 +863,7 @@ export class InputHandler extends Disposable implements IInputHandler { break; case 3: // Clear scrollback (everything not in viewport) - const scrollBackSize = this._bufferService.buffer.lines.length - this._terminal.rows; + const scrollBackSize = this._bufferService.buffer.lines.length - this._bufferService.rows; if (scrollBackSize > 0) { this._bufferService.buffer.lines.trimStart(scrollBackSize); this._bufferService.buffer.ybase = Math.max(this._bufferService.buffer.ybase - scrollBackSize, 0); @@ -890,13 +890,13 @@ export class InputHandler extends Disposable implements IInputHandler { this._restrictCursor(); switch (params.params[0]) { case 0: - this._eraseInBufferLine(this._bufferService.buffer.y, this._bufferService.buffer.x, this._terminal.cols); + this._eraseInBufferLine(this._bufferService.buffer.y, this._bufferService.buffer.x, this._bufferService.cols); break; case 1: this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.buffer.x + 1); break; case 2: - this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._terminal.cols); + this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.cols); break; } this._terminal.updateRange(this._bufferService.buffer.y); @@ -919,8 +919,8 @@ export class InputHandler extends Disposable implements IInputHandler { const row: number = buffer.y + buffer.ybase; - const scrollBottomRowsOffset = this._terminal.rows - 1 - buffer.scrollBottom; - const scrollBottomAbsolute = this._terminal.rows - 1 + buffer.ybase - scrollBottomRowsOffset + 1; + const scrollBottomRowsOffset = this._bufferService.rows - 1 - buffer.scrollBottom; + const scrollBottomAbsolute = this._bufferService.rows - 1 + buffer.ybase - scrollBottomRowsOffset + 1; while (param--) { // test: echo -e '\e[44m\e[1L\e[0m' // blankLine(true) - xterm/linux behavior @@ -952,8 +952,8 @@ export class InputHandler extends Disposable implements IInputHandler { const row: number = buffer.y + buffer.ybase; let j: number; - j = this._terminal.rows - 1 - buffer.scrollBottom; - j = this._terminal.rows - 1 + buffer.ybase - j; + j = this._bufferService.rows - 1 - buffer.scrollBottom; + j = this._bufferService.rows - 1 + buffer.ybase - j; while (param--) { // test: echo -e '\e[44m\e[1M\e[0m' // blankLine(true) - xterm/linux behavior @@ -1273,8 +1273,8 @@ export class InputHandler extends Disposable implements IInputHandler { break; case 3: // 132 col mode // TODO: move DECCOLM into compat addon - this._terminal.savedCols = this._terminal.cols; - this._terminal.resize(132, this._terminal.rows); + this._terminal.savedCols = this._bufferService.cols; + this._terminal.resize(132, this._bufferService.rows); this._terminal.reset(); break; case 6: @@ -1354,7 +1354,7 @@ export class InputHandler extends Disposable implements IInputHandler { case 47: // alt screen buffer case 1047: // alt screen buffer this._bufferService.buffers.activateAltBuffer(this._terminal.eraseAttrData()); - this._terminal.refresh(0, this._terminal.rows - 1); + this._terminal.refresh(0, this._bufferService.rows - 1); if (this._terminal.viewport) { this._terminal.viewport.syncScrollArea(); } @@ -1474,8 +1474,8 @@ export class InputHandler extends Disposable implements IInputHandler { // TODO: move DECCOLM into compat addon // Note: This impl currently does not enforce col 80, instead reverts // to previous terminal width before entering DECCOLM 132 - if (this._terminal.cols === 132 && this._terminal.savedCols) { - this._terminal.resize(this._terminal.savedCols, this._terminal.rows); + if (this._bufferService.cols === 132 && this._terminal.savedCols) { + this._terminal.resize(this._terminal.savedCols, this._bufferService.rows); } delete this._terminal.savedCols; this._terminal.reset(); @@ -1539,7 +1539,7 @@ export class InputHandler extends Disposable implements IInputHandler { if (param === 1049) { this.restoreCursor(); } - this._terminal.refresh(0, this._terminal.rows - 1); + this._terminal.refresh(0, this._bufferService.rows - 1); if (this._terminal.viewport) { this._terminal.viewport.syncScrollArea(); } @@ -1870,7 +1870,7 @@ export class InputHandler extends Disposable implements IInputHandler { } this._coreService.decPrivateModes.applicationCursorKeys = false; this._bufferService.buffer.scrollTop = 0; - this._bufferService.buffer.scrollBottom = this._terminal.rows - 1; + this._bufferService.buffer.scrollBottom = this._bufferService.rows - 1; this._terminal.curAttrData = DEFAULT_ATTR_DATA.clone(); this._bufferService.buffer.x = this._bufferService.buffer.y = 0; // ? this._terminal.charset = null; @@ -1926,8 +1926,8 @@ export class InputHandler extends Disposable implements IInputHandler { const top = params.params[0] || 1; let bottom: number; - if (params.length < 2 || (bottom = params.params[1]) > this._terminal.rows || bottom === 0) { - bottom = this._terminal.rows; + if (params.length < 2 || (bottom = params.params[1]) > this._bufferService.rows || bottom === 0) { + bottom = this._bufferService.rows; } if (bottom > top) { @@ -2149,13 +2149,13 @@ export class InputHandler extends Disposable implements IInputHandler { const buffer = this._bufferService.buffer; this._setCursor(0, 0); - for (let yOffset = 0; yOffset < this._terminal.rows; ++yOffset) { + 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; } this._terminal.updateRange(0); - this._terminal.updateRange(this._terminal.rows); + this._terminal.updateRange(this._bufferService.rows); this._setCursor(0, 0); } } From af9c050e93bc347868c8dcf689411f1fd5ca9ae9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 01:46:01 -0700 Subject: [PATCH 38/69] Remove ITerminal usage in DECRQSS --- src/InputHandler.test.ts | 20 ++++++++++---------- src/InputHandler.ts | 29 ++++++++++++++++------------- src/Terminal.ts | 2 +- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 352eec77..2b274811 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -13,7 +13,7 @@ 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 } from 'common/TestUtils.test'; +import { MockCoreService, MockBufferService, MockOptionsService } from 'common/TestUtils.test'; import { IBufferService } from 'common/services/Services'; function getCursor(term: TestTerminal): number[] { @@ -31,7 +31,7 @@ describe('InputHandler', () => { bufferService.buffer.x = 1; bufferService.buffer.y = 2; bufferService.buffer.ybase = 0; - const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService()); + const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService(), new MockOptionsService()); // Save cursor position inputHandler.saveCursor(); assert.equal(bufferService.buffer.x, 1); @@ -50,7 +50,7 @@ describe('InputHandler', () => { describe('setCursorStyle', () => { it('should call Terminal.setOption with correct params', () => { const terminal = new MockInputHandlingTerminal(); - const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService()); + const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockOptionsService()); const collect = ' '; inputHandler.setCursorStyle(Params.fromArray([0]), collect); @@ -93,7 +93,7 @@ describe('InputHandler', () => { const terminal = new MockInputHandlingTerminal(); const collect = '?'; terminal.bracketedPasteMode = false; - const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService()); + const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockOptionsService()); // Set bracketed paste mode inputHandler.setMode(Params.fromArray([2004]), collect); assert.equal(terminal.bracketedPasteMode, true); @@ -112,7 +112,7 @@ describe('InputHandler', () => { it('insertChars', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockOptionsService()); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -150,7 +150,7 @@ describe('InputHandler', () => { it('deleteChars', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockOptionsService()); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -191,7 +191,7 @@ describe('InputHandler', () => { it('eraseInLine', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockOptionsService()); // fill 6 lines to test 3 different states inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -220,7 +220,7 @@ describe('InputHandler', () => { 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 MockCoreService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockOptionsService()); // fill display with a's for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -355,7 +355,7 @@ describe('InputHandler', () => { 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 MockCoreService()); + const inputHandler = new InputHandler(term, new MockBufferService(80, 30), new MockCoreService(), new MockOptionsService()); const container = new Uint32Array(10); container[0] = 0x200B; inputHandler.print(container, 0, 1); @@ -370,7 +370,7 @@ describe('InputHandler', () => { beforeEach(() => { term = new Terminal(); bufferService = new MockBufferService(80, 30); - handler = new InputHandler(term, bufferService, new MockCoreService()); + handler = new InputHandler(term, bufferService, new MockCoreService(), new MockOptionsService()); }); it('should handle DECSET/DECRST 47 (alt screen buffer)', () => { handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST'); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 507c7d85..9b09ad83 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -19,7 +19,7 @@ import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content import { CellData } from 'common/buffer/CellData'; import { AttributeData } from 'common/buffer/AttributeData'; import { IAttributeData, IDisposable } from 'common/Types'; -import { ICoreService, IBufferService } from 'common/services/Services'; +import { ICoreService, IBufferService, IOptionsService } from 'common/services/Services'; import { ISelectionService } from 'browser/services/Services'; /** @@ -42,8 +42,9 @@ class DECRQSS implements IDcsHandler { private _data: Uint32Array = new Uint32Array(0); constructor( - private _terminal: any, - private _bufferService: IBufferService + private _bufferService: IBufferService, + private _coreService: ICoreService, + private _optionsService: IOptionsService ) { } hook(collect: string, params: IParams, flag: number): void { @@ -60,25 +61,26 @@ class DECRQSS implements IDcsHandler { switch (data) { // valid: DCS 1 $ r Pt ST (xterm) case '"q': // DECSCA - return this._terminal.handler(`${C0.ESC}P1$r0"q${C0.ESC}\\`); + return this._coreService.triggerDataEvent(`${C0.ESC}P1$r0"q${C0.ESC}\\`); case '"p': // DECSCL - return this._terminal.handler(`${C0.ESC}P1$r61"p${C0.ESC}\\`); + return this._coreService.triggerDataEvent(`${C0.ESC}P1$r61"p${C0.ESC}\\`); case 'r': // DECSTBM const pt = '' + (this._bufferService.buffer.scrollTop + 1) + ';' + (this._bufferService.buffer.scrollBottom + 1) + 'r'; - return this._terminal.handler(`${C0.ESC}P1$r${pt}${C0.ESC}\\`); + return this._coreService.triggerDataEvent(`${C0.ESC}P1$r${pt}${C0.ESC}\\`); case 'm': // SGR // TODO: report real settings instead of 0m - return this._terminal.handler(`${C0.ESC}P1$r0m${C0.ESC}\\`); + return this._coreService.triggerDataEvent(`${C0.ESC}P1$r0m${C0.ESC}\\`); case ' q': // DECSCUSR const STYLES: {[key: string]: number} = {'block': 2, 'underline': 4, 'bar': 6}; - let style = STYLES[this._terminal.getOption('cursorStyle')]; - style -= this._terminal.getOption('cursorBlink'); - return this._terminal.handler(`${C0.ESC}P1$r${style} q${C0.ESC}\\`); + let style = STYLES[this._optionsService.options.cursorStyle]; + style -= this._optionsService.options.cursorBlink ? 1 : 0; + return this._coreService.triggerDataEvent(`${C0.ESC}P1$r${style} q${C0.ESC}\\`); default: // invalid: DCS 0 $ r Pt ST (xterm) - this._terminal.error('Unknown DCS $q %s', data); - this._terminal.handler(`${C0.ESC}P0$r${C0.ESC}\\`); + // TODO: Move this into a log service + console.error('Unknown DCS $q %s', data); + this._coreService.triggerDataEvent(`${C0.ESC}P0$r${C0.ESC}\\`); } } } @@ -129,6 +131,7 @@ export class InputHandler extends Disposable implements IInputHandler { protected _terminal: IInputHandlingTerminal, private _bufferService: IBufferService, private _coreService: ICoreService, + private _optionsService: IOptionsService, private _parser: IEscapeSequenceParser = new EscapeSequenceParser()) { super(); @@ -297,7 +300,7 @@ export class InputHandler extends Disposable implements IInputHandler { /** * DCS handler */ - this._parser.setDcsHandler('$q', new DECRQSS(this._terminal, this._bufferService)); + this._parser.setDcsHandler('$q', new DECRQSS(this._bufferService, this._coreService, this._optionsService)); } public dispose(): void { diff --git a/src/Terminal.ts b/src/Terminal.ts index 8c5c2bd8..c79d871a 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -297,7 +297,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._userScrolling = false; // Register input handler and refire/handle events - this._inputHandler = new InputHandler(this, this._bufferService, this._coreService); + this._inputHandler = new InputHandler(this, this._bufferService, this._coreService, this.optionsService); this._inputHandler.onCursorMove(() => this._onCursorMove.fire()); this._inputHandler.onLineFeed(() => this._onLineFeed.fire()); this.register(this._inputHandler); From 94199ae124870bd2cd94379c33015f6bc6f1e66c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 09:36:21 -0700 Subject: [PATCH 39/69] Introduce logLevel option Part of #1560 --- src/InputHandler.test.ts | 20 +++---- src/InputHandler.ts | 40 +++++++------- src/Terminal.ts | 7 ++- src/common/TestUtils.test.ts | 9 +++- src/common/services/LogService.ts | 78 +++++++++++++++++++++++++++ src/common/services/OptionsService.ts | 1 + src/common/services/Services.d.ts | 13 ++++- typings/xterm.d.ts | 17 ++++++ 8 files changed, 148 insertions(+), 37 deletions(-) create mode 100644 src/common/services/LogService.ts diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 2b274811..53d0bc49 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -13,7 +13,7 @@ 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, MockOptionsService } from 'common/TestUtils.test'; +import { MockCoreService, MockBufferService, MockOptionsService, MockLogService } from 'common/TestUtils.test'; import { IBufferService } from 'common/services/Services'; function getCursor(term: TestTerminal): number[] { @@ -31,7 +31,7 @@ describe('InputHandler', () => { bufferService.buffer.x = 1; bufferService.buffer.y = 2; bufferService.buffer.ybase = 0; - const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService(), new MockOptionsService()); + const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); // Save cursor position inputHandler.saveCursor(); assert.equal(bufferService.buffer.x, 1); @@ -50,7 +50,7 @@ describe('InputHandler', () => { describe('setCursorStyle', () => { it('should call Terminal.setOption with correct params', () => { const terminal = new MockInputHandlingTerminal(); - const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockOptionsService()); + const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService()); const collect = ' '; inputHandler.setCursorStyle(Params.fromArray([0]), collect); @@ -93,7 +93,7 @@ describe('InputHandler', () => { const terminal = new MockInputHandlingTerminal(); const collect = '?'; terminal.bracketedPasteMode = false; - const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockOptionsService()); + const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService()); // Set bracketed paste mode inputHandler.setMode(Params.fromArray([2004]), collect); assert.equal(terminal.bracketedPasteMode, true); @@ -112,7 +112,7 @@ describe('InputHandler', () => { it('insertChars', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -150,7 +150,7 @@ describe('InputHandler', () => { it('deleteChars', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -191,7 +191,7 @@ describe('InputHandler', () => { it('eraseInLine', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); // fill 6 lines to test 3 different states inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -220,7 +220,7 @@ describe('InputHandler', () => { 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 MockCoreService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); // fill display with a's for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -355,7 +355,7 @@ describe('InputHandler', () => { 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 MockCoreService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService()); const container = new Uint32Array(10); container[0] = 0x200B; inputHandler.print(container, 0, 1); @@ -370,7 +370,7 @@ describe('InputHandler', () => { beforeEach(() => { term = new Terminal(); bufferService = new MockBufferService(80, 30); - handler = new InputHandler(term, bufferService, new MockCoreService(), new MockOptionsService()); + handler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); }); it('should handle DECSET/DECRST 47 (alt screen buffer)', () => { handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST'); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 9b09ad83..da94209d 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -19,7 +19,7 @@ import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content import { CellData } from 'common/buffer/CellData'; import { AttributeData } from 'common/buffer/AttributeData'; import { IAttributeData, IDisposable } from 'common/Types'; -import { ICoreService, IBufferService, IOptionsService } from 'common/services/Services'; +import { ICoreService, IBufferService, IOptionsService, ILogService } from 'common/services/Services'; import { ISelectionService } from 'browser/services/Services'; /** @@ -44,6 +44,7 @@ class DECRQSS implements IDcsHandler { constructor( private _bufferService: IBufferService, private _coreService: ICoreService, + private _logService: ILogService, private _optionsService: IOptionsService ) { } @@ -78,8 +79,7 @@ class DECRQSS implements IDcsHandler { return this._coreService.triggerDataEvent(`${C0.ESC}P1$r${style} q${C0.ESC}\\`); default: // invalid: DCS 0 $ r Pt ST (xterm) - // TODO: Move this into a log service - console.error('Unknown DCS $q %s', data); + this._logService.error('Unknown DCS $q %s', data); this._coreService.triggerDataEvent(`${C0.ESC}P0$r${C0.ESC}\\`); } } @@ -131,6 +131,7 @@ export class InputHandler extends Disposable implements IInputHandler { protected _terminal: IInputHandlingTerminal, private _bufferService: IBufferService, private _coreService: ICoreService, + private _logService: ILogService, private _optionsService: IOptionsService, private _parser: IEscapeSequenceParser = new EscapeSequenceParser()) { @@ -142,16 +143,16 @@ export class InputHandler extends Disposable implements IInputHandler { * custom fallback handlers */ this._parser.setCsiHandlerFallback((collect: string, params: IParams, flag: number) => { - this._terminal.error('Unknown CSI code: ', { collect, params: params.toArray(), flag: String.fromCharCode(flag) }); + this._logService.error('Unknown CSI code: ', { collect, params: params.toArray(), flag: String.fromCharCode(flag) }); }); this._parser.setEscHandlerFallback((collect: string, flag: number) => { - this._terminal.error('Unknown ESC code: ', { collect, flag: String.fromCharCode(flag) }); + this._logService.error('Unknown ESC code: ', { collect, flag: String.fromCharCode(flag) }); }); this._parser.setExecuteHandlerFallback((code: number) => { - this._terminal.error('Unknown EXECUTE code: ', { code }); + this._logService.error('Unknown EXECUTE code: ', { code }); }); this._parser.setOscHandlerFallback((identifier: number, data: string) => { - this._terminal.error('Unknown OSC code: ', { identifier, data }); + this._logService.error('Unknown OSC code: ', { identifier, data }); }); /** @@ -293,14 +294,14 @@ export class InputHandler extends Disposable implements IInputHandler { * error handler */ this._parser.setErrorHandler((state: IParsingState) => { - this._terminal.error('Parsing error: ', state); + this._logService.error('Parsing error: ', state); return state; }); /** * DCS handler */ - this._parser.setDcsHandler('$q', new DECRQSS(this._bufferService, this._coreService, this._optionsService)); + this._parser.setDcsHandler('$q', new DECRQSS(this._bufferService, this._coreService, this._logService, this._optionsService)); } public dispose(): void { @@ -323,10 +324,7 @@ export class InputHandler extends Disposable implements IInputHandler { const cursorStartX = buffer.x; const cursorStartY = buffer.y; - // TODO: Consolidate debug/logging #1560 - if ((this._terminal).debug) { - this._terminal.log('data: ' + data); - } + this._logService.debug('data: ' + data); if (this._parseBuffer.length < data.length) { this._parseBuffer = new Uint32Array(data.length); @@ -350,9 +348,7 @@ export class InputHandler extends Disposable implements IInputHandler { const cursorStartY = buffer.y; // TODO: Consolidate debug/logging #1560 - if ((this._terminal).debug) { - this._terminal.log('data: ' + data); - } + this._logService.debug('data: ' + data); if (this._parseBuffer.length < data.length) { this._parseBuffer = new Uint32Array(data.length); @@ -1291,7 +1287,7 @@ export class InputHandler extends Disposable implements IInputHandler { // this.cursorBlink = true; break; case 66: - this._terminal.log('Serial port requested application keypad.'); + this._logService.info('Serial port requested application keypad.'); this._terminal.applicationKeypad = true; if (this._terminal.viewport) { this._terminal.viewport.syncScrollArea(); @@ -1319,7 +1315,7 @@ export class InputHandler extends Disposable implements IInputHandler { if (this._selectionService) { this._selectionService.disable(); } - this._terminal.log('Binding to mouse events.'); + this._logService.info('Binding to mouse events.'); break; case 1004: // send focusin/focusout events // focusin: ^[[I @@ -1494,7 +1490,7 @@ export class InputHandler extends Disposable implements IInputHandler { // this.cursorBlink = false; break; case 66: - this._terminal.log('Switching back to normal keypad.'); + this._logService.info('Switching back to normal keypad.'); this._terminal.applicationKeypad = false; if (this._terminal.viewport) { this._terminal.viewport.syncScrollArea(); @@ -1785,7 +1781,7 @@ export class InputHandler extends Disposable implements IInputHandler { attr.bg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK); attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK); } else { - this._terminal.error('Unknown SGR attribute: %d.', p); + this._logService.error('Unknown SGR attribute: %d.', p); } } } @@ -1999,7 +1995,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Enables the numeric keypad to send application sequences to the host. */ public keypadApplicationMode(): void { - this._terminal.log('Serial port requested application keypad.'); + this._logService.info('Serial port requested application keypad.'); this._terminal.applicationKeypad = true; if (this._terminal.viewport) { this._terminal.viewport.syncScrollArea(); @@ -2012,7 +2008,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Enables the keypad to send numeric characters to the host. */ public keypadNumericMode(): void { - this._terminal.log('Switching back to normal keypad.'); + this._logService.info('Switching back to normal keypad.'); this._terminal.applicationKeypad = false; if (this._terminal.viewport) { this._terminal.viewport.syncScrollArea(); diff --git a/src/Terminal.ts b/src/Terminal.ts index c79d871a..8fc41263 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -47,7 +47,7 @@ import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { applyWindowsMode } from './WindowsMode'; import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; -import { IOptionsService, IBufferService, ICoreService } from 'common/services/Services'; +import { IOptionsService, IBufferService, ICoreService, ILogService } from 'common/services/Services'; import { OptionsService } from 'common/services/OptionsService'; import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService } from 'browser/services/Services'; import { CharSizeService } from 'browser/services/CharSizeService'; @@ -58,6 +58,7 @@ import { Attributes } from 'common/buffer/Constants'; import { MouseService } from 'browser/services/MouseService'; import { IParams } from 'common/parser/Types'; import { CoreService } from 'common/services/CoreService'; +import { LogService } from 'common/services/LogService'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -110,6 +111,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp // common services private _bufferService: IBufferService; private _coreService: ICoreService; + private _logService: ILogService; public optionsService: IOptionsService; // browser services @@ -241,6 +243,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._bufferService = new BufferService(this.optionsService); this._coreService = new CoreService(() => this.scrollToBottom(), this._bufferService, this.optionsService); this._coreService.onData(e => this._onData.fire(e)); + this._logService = new LogService(this.optionsService); this._setupOptionsListeners(); this._setup(); @@ -297,7 +300,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._userScrolling = false; // Register input handler and refire/handle events - this._inputHandler = new InputHandler(this, this._bufferService, this._coreService, this.optionsService); + this._inputHandler = new InputHandler(this, this._bufferService, this._coreService, this._logService, this.optionsService); this._inputHandler.onCursorMove(() => this._onCursorMove.fire()); this._inputHandler.onLineFeed(() => this._onLineFeed.fire()); this.register(this._inputHandler); diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index e3a6c9b9..c786a06d 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IBufferService, ICoreService, IOptionsService, ITerminalOptions, IPartialTerminalOptions } from 'common/services/Services'; +import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IPartialTerminalOptions } from 'common/services/Services'; import { IEvent, EventEmitter } from 'common/EventEmitter'; import { clone } from 'common/Clone'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; @@ -36,6 +36,13 @@ export class MockCoreService implements ICoreService { triggerDataEvent(data: string, wasUserInput?: boolean): void {} } +export class MockLogService implements ILogService { + debug(message: any, ...optionalParams: any[]): void {} + info(message: any, ...optionalParams: any[]): void {} + warn(message: any, ...optionalParams: any[]): void {} + error(message: any, ...optionalParams: any[]): void {} +} + export class MockOptionsService implements IOptionsService { options: ITerminalOptions = clone(DEFAULT_OPTIONS); onOptionChange: IEvent = new EventEmitter().event; diff --git a/src/common/services/LogService.ts b/src/common/services/LogService.ts new file mode 100644 index 00000000..78e59e1c --- /dev/null +++ b/src/common/services/LogService.ts @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { ILogService, IOptionsService } from 'common/services/Services'; + +interface IConsole { + log(message?: any, ...optionalParams: any[]): void; + error(message?: any, ...optionalParams: any[]): void; + info(message?: any, ...optionalParams: any[]): void; + trace(message?: any, ...optionalParams: any[]): void; + warn(message?: any, ...optionalParams: any[]): void; +} + +// console is available on both node.js and browser contexts but the common +// module doesn't depend on them so we need to explicitly declare it. +declare const console: IConsole; + + +export enum LogLevel { + Debug = 0, + Info = 1, + Warn = 2, + Error = 3, + Off = 4 +} + +const optionsKeyToLogLevel: { [key: string]: LogLevel } = { + debug: LogLevel.Debug, + info: LogLevel.Info, + warn: LogLevel.Warn, + error: LogLevel.Error, + off: LogLevel.Off +}; + +export class LogService implements ILogService { + private _logLevel!: LogLevel; + + constructor( + private readonly _optionsService: IOptionsService + ) { + this._updateLogLevel(); + this._optionsService.onOptionChange(key => { + if (key === 'logLevel') { + this._updateLogLevel(); + } + }) + } + + private _updateLogLevel(): void { + this._logLevel = optionsKeyToLogLevel[this._optionsService.options.logLevel]; + } + + debug(message: any, ...optionalParams: any[]): void { + if (this._logLevel <= LogLevel.Debug) { + console.log.call(console, message, optionalParams); + } + } + + info(message: any, ...optionalParams: any[]): void { + if (this._logLevel <= LogLevel.Info) { + console.info.call(console, message, optionalParams); + } + } + + warn(message: any, ...optionalParams: any[]): void { + if (this._logLevel <= LogLevel.Warn) { + console.warn.call(console, message, optionalParams); + } + } + + error(message: any, ...optionalParams: any[]): void { + if (this._logLevel <= LogLevel.Error) { + console.error.call(console, message, optionalParams); + } + } +} diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index ab041488..8b58de8d 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -29,6 +29,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ fontWeightBold: 'bold', lineHeight: 1.0, letterSpacing: 0, + logLevel: 'info', scrollback: 1000, screenReaderMode: false, macOptionIsMeta: false, diff --git a/src/common/services/Services.d.ts b/src/common/services/Services.d.ts index f46f3d76..9ef5defc 100644 --- a/src/common/services/Services.d.ts +++ b/src/common/services/Services.d.ts @@ -38,6 +38,13 @@ export interface ICoreService { triggerDataEvent(data: string, wasUserInput?: boolean): void; } +export interface ILogService { + debug(message: any, ...optionalParams: any[]): void; + info(message: any, ...optionalParams: any[]): void; + warn(message: any, ...optionalParams: any[]): void; + error(message: any, ...optionalParams: any[]): void; +} + export interface IOptionsService { readonly options: ITerminalOptions; @@ -48,7 +55,7 @@ export interface IOptionsService { } export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'; - +export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off'; export type RendererType = 'dom' | 'canvas'; export interface IPartialTerminalOptions { @@ -66,6 +73,7 @@ export interface IPartialTerminalOptions { fontWeightBold?: FontWeight; letterSpacing?: number; lineHeight?: number; + logLevel?: LogLevel; macOptionIsMeta?: boolean; macOptionClickForcesSelection?: boolean; rendererType?: RendererType; @@ -86,6 +94,7 @@ export interface ITerminalOptions { cols: number; cursorBlink: boolean; cursorStyle: 'block' | 'underline' | 'bar'; + debug: boolean; disableStdin: boolean; drawBoldTextInBrightColors: boolean; fontSize: number; @@ -94,6 +103,7 @@ export interface ITerminalOptions { fontWeightBold: FontWeight; letterSpacing: number; lineHeight: number; + logLevel: LogLevel; macOptionIsMeta: boolean; macOptionClickForcesSelection: boolean; rendererType: RendererType; @@ -109,7 +119,6 @@ export interface ITerminalOptions { [key: string]: any; cancelEvents: boolean; convertEol: boolean; - debug: boolean; screenKeys: boolean; termName: string; useFlowControl: boolean; diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index c915a4ba..c4062db8 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -15,6 +15,11 @@ declare module 'xterm' { */ export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'; + /** + * A string representing log level. + */ + export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off'; + /** * A string representing a renderer type. */ @@ -107,6 +112,18 @@ declare module 'xterm' { */ lineHeight?: number; + /** + * What log level to use, this will log for all levels below and including + * what is set: + * + * 1. debug + * 2. info (default) + * 3. warn + * 4. error + * 5. off + */ + logLevel?: LogLevel; + /** * Whether to treat option as the meta key. */ From 2977a53cc1340982d31276e898f4ad5d4280567f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 09:44:12 -0700 Subject: [PATCH 40/69] Use log service everywhere, fix log printing Fixes #1560 --- addons/xterm-addon-webgl/src/WebglUtils.ts | 4 ++-- demo/client.ts | 1 + src/InputHandler.ts | 4 ++-- src/Linkifier.test.ts | 3 ++- src/Linkifier.ts | 6 +++-- src/Terminal.ts | 23 +------------------ src/Types.d.ts | 3 --- src/browser/services/SelectionService.test.ts | 3 --- src/common/services/LogService.ts | 8 +++---- 9 files changed, 16 insertions(+), 39 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglUtils.ts b/addons/xterm-addon-webgl/src/WebglUtils.ts index ff62388e..841ad067 100644 --- a/addons/xterm-addon-webgl/src/WebglUtils.ts +++ b/addons/xterm-addon-webgl/src/WebglUtils.ts @@ -24,7 +24,7 @@ export function createProgram(gl: WebGLRenderingContext, vertexSource: string, f return program; } - console.log(gl.getProgramInfoLog(program)); + console.error(gl.getProgramInfoLog(program)); gl.deleteProgram(program); } @@ -37,7 +37,7 @@ export function createShader(gl: WebGLRenderingContext, type: number, source: st return shader; } - console.log(gl.getShaderInfoLog(shader)); + console.error(gl.getShaderInfoLog(shader)); gl.deleteShader(shader); } diff --git a/demo/client.ts b/demo/client.ts index e2259841..3bd96c8c 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -235,6 +235,7 @@ function initOptions(term: TerminalType): void { fontFamily: null, fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], + logLevel: ['debug', 'info', 'warn', 'error', 'off'], rendererType: ['dom', 'canvas'], wordSeparator: null }; diff --git a/src/InputHandler.ts b/src/InputHandler.ts index da94209d..f554a9ad 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -324,7 +324,7 @@ export class InputHandler extends Disposable implements IInputHandler { const cursorStartX = buffer.x; const cursorStartY = buffer.y; - this._logService.debug('data: ' + data); + this._logService.debug('parsing data', data); if (this._parseBuffer.length < data.length) { this._parseBuffer = new Uint32Array(data.length); @@ -348,7 +348,7 @@ export class InputHandler extends Disposable implements IInputHandler { const cursorStartY = buffer.y; // TODO: Consolidate debug/logging #1560 - this._logService.debug('data: ' + data); + this._logService.debug('parsing data', data); if (this._parseBuffer.length < data.length) { this._parseBuffer = new Uint32Array(data.length); diff --git a/src/Linkifier.test.ts b/src/Linkifier.test.ts index 9e6588af..ac1fb8b3 100644 --- a/src/Linkifier.test.ts +++ b/src/Linkifier.test.ts @@ -11,10 +11,11 @@ import { MockBuffer, MockTerminal, TestTerminal } from './TestUtils.test'; import { CircularList } from 'common/CircularList'; import { BufferLine } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; +import { MockLogService } from 'common/TestUtils.test'; class TestLinkifier extends Linkifier { constructor(terminal: ITerminal) { - super(terminal); + super(terminal, new MockLogService()); Linkifier._timeBeforeLatency = 0; } diff --git a/src/Linkifier.ts b/src/Linkifier.ts index ded6f87a..5398d3c5 100644 --- a/src/Linkifier.ts +++ b/src/Linkifier.ts @@ -8,6 +8,7 @@ import { IBufferStringIteratorResult } from 'common/buffer/Types'; import { MouseZone } from './MouseZoneManager'; import { getStringCellWidth } from 'common/CharWidth'; import { EventEmitter, IEvent } from 'common/EventEmitter'; +import { ILogService } from 'common/services/Services'; /** * Limit of the unwrapping line expansion (overscan) at the top and bottom @@ -42,7 +43,8 @@ export class Linkifier implements ILinkifier { public get onLinkTooltip(): IEvent { return this._onLinkTooltip.event; } constructor( - protected _terminal: ITerminal + protected _terminal: ITerminal, + private _logService: ILogService ) { this._rowsToLinkify = { start: null, @@ -212,7 +214,7 @@ export class Linkifier implements ILinkifier { // since this is most likely a bug the regex itself we simply do nothing here // DEBUG: print match and throw if ((this._terminal).debug) { - console.log({match, matcher}); + this._logService.error({ match, matcher }); throw new Error('match found without corresponding matchIndex'); } break; diff --git a/src/Terminal.ts b/src/Terminal.ts index 8fc41263..a8912b3f 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -88,7 +88,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp * The HTMLElement that the terminal is created in, set by Terminal.open. */ private _parent: HTMLElement; - private _context: Window; private _document: Document; private _viewportScrollArea: HTMLElement; private _viewportElement: HTMLElement; @@ -306,7 +305,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this.register(this._inputHandler); this._selectionService = this._selectionService || null; - this.linkifier = this.linkifier || new Linkifier(this); + this.linkifier = this.linkifier || new Linkifier(this, this._logService); this._mouseZoneManager = this._mouseZoneManager || null; if (this.options.windowsMode) { @@ -537,8 +536,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp throw new Error('Terminal requires a parent element.'); } - // Grab global elements - this._context = this._parent.ownerDocument.defaultView; this._document = this._parent.ownerDocument; // Create main element container @@ -1679,24 +1676,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp } } - /** - * Log the current state to the console. - */ - public log(text: string, data?: any): void { - if (!this.options.debug) return; - if (!this._context.console || !this._context.console.log) return; - this._context.console.log(text, data); - } - - /** - * Log the current state as error to the console. - */ - public error(text: string, data?: any): void { - if (!this.options.debug) return; - if (!this._context.console || !this._context.console.error) return; - this._context.console.error(text, data); - } - /** * Resizes the terminal. * diff --git a/src/Types.d.ts b/src/Types.d.ts index 0614d626..238f70dc 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -65,11 +65,9 @@ export interface IInputHandlingTerminal { is(term: string): boolean; setgCharset(g: number, charset: ICharset): void; resize(x: number, y: number): void; - log(text: string, data?: any): void; reset(): void; showCursor(): void; refresh(start: number, end: number): void; - error(text: string, data?: any): void; handleTitle(title: string): void; } @@ -212,7 +210,6 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc scrollLines(disp: number, suppressScrollEvent?: boolean): void; cancel(ev: Event, force?: boolean): boolean | void; - log(text: string): void; showCursor(): void; } diff --git a/src/browser/services/SelectionService.test.ts b/src/browser/services/SelectionService.test.ts index b00fa29f..62ca3819 100644 --- a/src/browser/services/SelectionService.test.ts +++ b/src/browser/services/SelectionService.test.ts @@ -13,7 +13,6 @@ import { IBufferService, IOptionsService } from 'common/services/Services'; import { MockCharSizeService, MockMouseService } from 'browser/TestUtils.test'; import { CellData } from 'common/buffer/CellData'; import { IBuffer } from 'common/buffer/Types'; -import { isWindows } from 'common/Platform'; class TestSelectionService extends SelectionService { constructor( @@ -360,8 +359,6 @@ describe('SelectionService', () => { buffer.lines.set(3, stringToRow('4')); buffer.lines.set(4, stringToRow('5')); selectionService.selectAll(); - console.log(selectionService.selectionText.length); - console.log(isWindows); assert.equal(selectionService.selectionText, '1\n2\n3\n4\n5'); }); }); diff --git a/src/common/services/LogService.ts b/src/common/services/LogService.ts index 78e59e1c..b89ee41e 100644 --- a/src/common/services/LogService.ts +++ b/src/common/services/LogService.ts @@ -54,25 +54,25 @@ export class LogService implements ILogService { debug(message: any, ...optionalParams: any[]): void { if (this._logLevel <= LogLevel.Debug) { - console.log.call(console, message, optionalParams); + console.log.call(console, message, ...optionalParams); } } info(message: any, ...optionalParams: any[]): void { if (this._logLevel <= LogLevel.Info) { - console.info.call(console, message, optionalParams); + console.info.call(console, message, ...optionalParams); } } warn(message: any, ...optionalParams: any[]): void { if (this._logLevel <= LogLevel.Warn) { - console.warn.call(console, message, optionalParams); + console.warn.call(console, message, ...optionalParams); } } error(message: any, ...optionalParams: any[]): void { if (this._logLevel <= LogLevel.Error) { - console.error.call(console, message, optionalParams); + console.error.call(console, message, ...optionalParams); } } } From cd01d8ad56122d3749d8199bf09c96f9b43824c9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 09:47:11 -0700 Subject: [PATCH 41/69] Lint --- src/common/services/LogService.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/common/services/LogService.ts b/src/common/services/LogService.ts index b89ee41e..62027513 100644 --- a/src/common/services/LogService.ts +++ b/src/common/services/LogService.ts @@ -19,19 +19,19 @@ declare const console: IConsole; export enum LogLevel { - Debug = 0, - Info = 1, - Warn = 2, - Error = 3, - Off = 4 + DEBUG = 0, + INFO = 1, + WARN = 2, + ERROR = 3, + OFF = 4 } const optionsKeyToLogLevel: { [key: string]: LogLevel } = { - debug: LogLevel.Debug, - info: LogLevel.Info, - warn: LogLevel.Warn, - error: LogLevel.Error, - off: LogLevel.Off + debug: LogLevel.DEBUG, + info: LogLevel.INFO, + warn: LogLevel.WARN, + error: LogLevel.ERROR, + off: LogLevel.OFF }; export class LogService implements ILogService { @@ -45,7 +45,7 @@ export class LogService implements ILogService { if (key === 'logLevel') { this._updateLogLevel(); } - }) + }); } private _updateLogLevel(): void { @@ -53,25 +53,25 @@ export class LogService implements ILogService { } debug(message: any, ...optionalParams: any[]): void { - if (this._logLevel <= LogLevel.Debug) { + if (this._logLevel <= LogLevel.DEBUG) { console.log.call(console, message, ...optionalParams); } } info(message: any, ...optionalParams: any[]): void { - if (this._logLevel <= LogLevel.Info) { + if (this._logLevel <= LogLevel.INFO) { console.info.call(console, message, ...optionalParams); } } warn(message: any, ...optionalParams: any[]): void { - if (this._logLevel <= LogLevel.Warn) { + if (this._logLevel <= LogLevel.WARN) { console.warn.call(console, message, ...optionalParams); } } error(message: any, ...optionalParams: any[]): void { - if (this._logLevel <= LogLevel.Error) { + if (this._logLevel <= LogLevel.ERROR) { console.error.call(console, message, ...optionalParams); } } From f3145431350920006b39f347e74bc78888f8055f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 09:48:16 -0700 Subject: [PATCH 42/69] Log bad linkifier instead of throwing --- src/Linkifier.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Linkifier.ts b/src/Linkifier.ts index 5398d3c5..769f3a50 100644 --- a/src/Linkifier.ts +++ b/src/Linkifier.ts @@ -212,11 +212,7 @@ export class Linkifier implements ILinkifier { if (!uri) { // something matched but does not comply with the given matchIndex // since this is most likely a bug the regex itself we simply do nothing here - // DEBUG: print match and throw - if ((this._terminal).debug) { - this._logService.error({ match, matcher }); - throw new Error('match found without corresponding matchIndex'); - } + this._logService.debug('match found without corresponding matchIndex', match, matcher); break; } From 0c038352f3f0b429fce1b28b76ea3f34af1f7f8d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 09:55:53 -0700 Subject: [PATCH 43/69] Remove debug internal option completely and add option APIs --- demo/client.ts | 1 - src/InputHandler.ts | 1 - src/Types.d.ts | 1 - src/common/services/OptionsService.ts | 1 - src/common/services/Services.d.ts | 1 - src/public/Terminal.ts | 7 ++++--- typings/xterm.d.ts | 12 +++++++++--- 7 files changed, 13 insertions(+), 11 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index 3bd96c8c..a28a720c 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -220,7 +220,6 @@ function initOptions(term: TerminalType): void { // Internal only options 'cancelEvents', 'convertEol', - 'debug', 'handler', 'screenKeys', 'termName', diff --git a/src/InputHandler.ts b/src/InputHandler.ts index f554a9ad..0f4da041 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -347,7 +347,6 @@ export class InputHandler extends Disposable implements IInputHandler { const cursorStartX = buffer.x; const cursorStartY = buffer.y; - // TODO: Consolidate debug/logging #1560 this._logService.debug('parsing data', data); if (this._parseBuffer.length < data.length) { diff --git a/src/Types.d.ts b/src/Types.d.ts index 238f70dc..1aa05b6c 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -279,7 +279,6 @@ export interface ITerminalOptions extends IPublicTerminalOptions { [key: string]: any; cancelEvents?: boolean; convertEol?: boolean; - debug?: boolean; handler?: (data: string) => void; screenKeys?: boolean; termName?: string; diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 8b58de8d..7396491e 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -45,7 +45,6 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ convertEol: false, termName: 'xterm', screenKeys: false, - debug: false, cancelEvents: false, useFlowControl: false, wordSeparator: ' ()[]{}\'"' diff --git a/src/common/services/Services.d.ts b/src/common/services/Services.d.ts index 9ef5defc..9a98ca92 100644 --- a/src/common/services/Services.d.ts +++ b/src/common/services/Services.d.ts @@ -94,7 +94,6 @@ export interface ITerminalOptions { cols: number; cursorBlink: boolean; cursorStyle: 'block' | 'underline' | 'bar'; - debug: boolean; disableStdin: boolean; drawBoldTextInBrightColors: boolean; fontSize: number; diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index f06d003a..5d270ca7 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -133,8 +133,8 @@ export class Terminal implements ITerminalApi { public writeUtf8(data: Uint8Array): void { this._core.writeUtf8(data); } - public getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold' | 'rendererType' | 'termName' | 'wordSeparator'): string; - public getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean; + public getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold' | 'logLevel' | 'rendererType' | 'termName' | 'wordSeparator'): string; + public getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean; public getOption(key: 'colors'): string[]; public getOption(key: 'cols' | 'fontSize' | 'letterSpacing' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback'): number; public getOption(key: 'handler'): (data: string) => void; @@ -144,9 +144,10 @@ export class Terminal implements ITerminalApi { } public setOption(key: 'bellSound' | 'fontFamily' | 'termName' | 'wordSeparator', value: string): void; public setOption(key: 'fontWeight' | 'fontWeightBold', value: 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'): void; + public setOption(key: 'logLevel', value: 'debug' | 'info' | 'warn' | 'error' | 'off'): void; public setOption(key: 'bellStyle', value: 'none' | 'visual' | 'sound' | 'both'): void; public setOption(key: 'cursorStyle', value: 'block' | 'underline' | 'bar'): void; - public setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void; + public setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void; public setOption(key: 'colors', value: string[]): void; public setOption(key: 'fontSize' | 'letterSpacing' | 'lineHeight' | 'tabStopWidth' | 'scrollback', value: number): void; public setOption(key: 'handler', value: (data: string) => void): void; diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index c4062db8..d9e28b26 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -693,12 +693,12 @@ declare module 'xterm' { * Retrieves an option's value from the terminal. * @param key The option key. */ - getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold'| 'rendererType' | 'termName' | 'wordSeparator'): string; + getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold' | 'logLevel' | 'rendererType' | 'termName' | 'wordSeparator'): string; /** * Retrieves an option's value from the terminal. * @param key The option key. */ - getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode'): boolean; + getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode'): boolean; /** * Retrieves an option's value from the terminal. * @param key The option key. @@ -732,6 +732,12 @@ declare module 'xterm' { * @param value The option value. */ setOption(key: 'fontWeight' | 'fontWeightBold', value: null | 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'): void; + /** + * Sets an option on the terminal. + * @param key The option key. + * @param value The option value. + */ + setOption(key: 'logLevel', value: LogLevel): void; /** * Sets an option on the terminal. * @param key The option key. @@ -749,7 +755,7 @@ declare module 'xterm' { * @param key The option key. * @param value The option value. */ - setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode', value: boolean): void; + setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode', value: boolean): void; /** * Sets an option on the terminal. * @param key The option key. From 46002599d3b2d28ee36eb93c16d77e7d408bcf32 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 10:09:48 -0700 Subject: [PATCH 44/69] Prefix all logging with 'xterm.js' This makes it easier to differentiate messages in large apps --- src/common/services/LogService.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/common/services/LogService.ts b/src/common/services/LogService.ts index 62027513..f7480078 100644 --- a/src/common/services/LogService.ts +++ b/src/common/services/LogService.ts @@ -34,6 +34,8 @@ const optionsKeyToLogLevel: { [key: string]: LogLevel } = { off: LogLevel.OFF }; +const LOG_PREFIX = 'xterm.js: '; + export class LogService implements ILogService { private _logLevel!: LogLevel; @@ -52,27 +54,27 @@ export class LogService implements ILogService { this._logLevel = optionsKeyToLogLevel[this._optionsService.options.logLevel]; } - debug(message: any, ...optionalParams: any[]): void { + debug(message: string, ...optionalParams: any[]): void { if (this._logLevel <= LogLevel.DEBUG) { - console.log.call(console, message, ...optionalParams); + console.log.call(console, LOG_PREFIX + message, ...optionalParams); } } - info(message: any, ...optionalParams: any[]): void { + info(message: string, ...optionalParams: any[]): void { if (this._logLevel <= LogLevel.INFO) { - console.info.call(console, message, ...optionalParams); + console.info.call(console, LOG_PREFIX + message, ...optionalParams); } } - warn(message: any, ...optionalParams: any[]): void { + warn(message: string, ...optionalParams: any[]): void { if (this._logLevel <= LogLevel.WARN) { - console.warn.call(console, message, ...optionalParams); + console.warn.call(console, LOG_PREFIX + message, ...optionalParams); } } - error(message: any, ...optionalParams: any[]): void { + error(message: string, ...optionalParams: any[]): void { if (this._logLevel <= LogLevel.ERROR) { - console.error.call(console, message, ...optionalParams); + console.error.call(console, LOG_PREFIX + message, ...optionalParams); } } } From a696f4747a46367177fa683e37587210933789f0 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 13:18:44 -0700 Subject: [PATCH 45/69] Move clipboard to browser --- src/Terminal.ts | 2 +- src/{ => browser}/Clipboard.test.ts | 2 +- src/{ => browser}/Clipboard.ts | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) rename src/{ => browser}/Clipboard.test.ts (95%) rename src/{ => browser}/Clipboard.ts (97%) diff --git a/src/Terminal.ts b/src/Terminal.ts index a8912b3f..a97eeeed 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -25,7 +25,7 @@ import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; import { CompositionHelper } from 'browser/input/CompositionHelper'; import { Viewport } from './Viewport'; -import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './Clipboard'; +import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './browser/Clipboard'; import { C0 } from 'common/data/EscapeSequences'; import { InputHandler } from './InputHandler'; import { Renderer } from './renderer/Renderer'; diff --git a/src/Clipboard.test.ts b/src/browser/Clipboard.test.ts similarity index 95% rename from src/Clipboard.test.ts rename to src/browser/Clipboard.test.ts index 07c0f66e..aa7bdabd 100644 --- a/src/Clipboard.test.ts +++ b/src/browser/Clipboard.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import * as Clipboard from './Clipboard'; +import * as Clipboard from 'browser/Clipboard'; describe('evaluatePastedTextProcessing', () => { it('should replace carriage return and/or line feed with carriage return', () => { diff --git a/src/Clipboard.ts b/src/browser/Clipboard.ts similarity index 97% rename from src/Clipboard.ts rename to src/browser/Clipboard.ts index f4b415de..f56868cc 100644 --- a/src/Clipboard.ts +++ b/src/browser/Clipboard.ts @@ -29,7 +29,9 @@ export function bracketTextForPaste(text: string, bracketedPasteMode: boolean): * @param ev The original copy event to be handled */ export function copyHandler(ev: ClipboardEvent, selectionService: ISelectionService): void { - ev.clipboardData.setData('text/plain', selectionService.selectionText); + if (ev.clipboardData) { + ev.clipboardData.setData('text/plain', selectionService.selectionText); + } // Prevent or the original text will be copied. ev.preventDefault(); } From 1ffa568a2fe16ae7d87e4ba6a7dc324390467594 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 13:47:17 -0700 Subject: [PATCH 46/69] Remove ITerminal from Linkifier --- src/Linkifier.test.ts | 49 +++++++++++++++++++++---------------------- src/Linkifier.ts | 47 ++++++++++++++++++++++------------------- src/Terminal.ts | 6 ++---- src/Types.d.ts | 2 +- 4 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/Linkifier.test.ts b/src/Linkifier.test.ts index ac1fb8b3..973e6d9b 100644 --- a/src/Linkifier.test.ts +++ b/src/Linkifier.test.ts @@ -4,23 +4,23 @@ */ import { assert } from 'chai'; -import { IMouseZoneManager, IMouseZone, ILinkMatcher, ITerminal } from './Types'; +import { IMouseZoneManager, IMouseZone, ILinkMatcher } from './Types'; import { IBufferLine } from 'common/Types'; import { Linkifier } from './Linkifier'; -import { MockBuffer, MockTerminal, TestTerminal } from './TestUtils.test'; -import { CircularList } from 'common/CircularList'; +import { TestTerminal } from './TestUtils.test'; import { BufferLine } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; -import { MockLogService } from 'common/TestUtils.test'; +import { MockLogService, MockBufferService } from 'common/TestUtils.test'; +import { IBufferService } from 'common/services/Services'; class TestLinkifier extends Linkifier { - constructor(terminal: ITerminal) { - super(terminal, new MockLogService()); + constructor(bufferService: IBufferService) { + super(bufferService, new MockLogService()); Linkifier._timeBeforeLatency = 0; } public get linkMatchers(): ILinkMatcher[] { return this._linkMatchers; } - public linkifyRows(): void { super.linkifyRows(0, this._terminal.buffer.lines.length - 1); } + public linkifyRows(): void { super.linkifyRows(0, this._bufferService.buffer.lines.length - 1); } } class TestMouseZoneManager implements IMouseZoneManager { @@ -37,18 +37,13 @@ class TestMouseZoneManager implements IMouseZoneManager { } describe('Linkifier', () => { - let terminal: ITerminal; + let bufferService: IBufferService; let linkifier: TestLinkifier; let mouseZoneManager: TestMouseZoneManager; beforeEach(() => { - terminal = new MockTerminal(); - (terminal as any).cols = 100; - (terminal as any).rows = 10; - terminal.buffer = new MockBuffer(); - (terminal.buffer).setLines(new CircularList(20)); - terminal.buffer.ydisp = 0; - linkifier = new TestLinkifier(terminal); + bufferService = new MockBufferService(100, 10); + linkifier = new TestLinkifier(bufferService); mouseZoneManager = new TestMouseZoneManager(); }); @@ -61,13 +56,12 @@ describe('Linkifier', () => { } function addRow(text: string): void { - terminal.buffer.lines.push(stringToRow(text)); + bufferService.buffer.lines.push(stringToRow(text)); } function assertLinkifiesRow(rowText: string, linkMatcherRegex: RegExp, links: {x: number, length: number}[], done: MochaDone): void { addRow(rowText); linkifier.registerLinkMatcher(linkMatcherRegex, () => {}); - (terminal as any).rows = terminal.buffer.lines.length - 1; linkifier.linkifyRows(); // Allow linkify to happen setTimeout(() => { @@ -75,8 +69,8 @@ describe('Linkifier', () => { links.forEach((l, i) => { assert.equal(mouseZoneManager.zones[i].x1, l.x + 1); assert.equal(mouseZoneManager.zones[i].x2, l.x + l.length + 1); - assert.equal(mouseZoneManager.zones[i].y1, terminal.buffer.lines.length); - assert.equal(mouseZoneManager.zones[i].y2, terminal.buffer.lines.length); + assert.equal(mouseZoneManager.zones[i].y1, bufferService.buffer.lines.length); + assert.equal(mouseZoneManager.zones[i].y2, bufferService.buffer.lines.length); }); done(); }, 0); @@ -111,7 +105,7 @@ describe('Linkifier', () => { describe('after attachToDom', () => { beforeEach(() => { - linkifier.attachToDom(mouseZoneManager); + linkifier.attachToDom(null, mouseZoneManager); }); describe('link matcher', () => { @@ -144,19 +138,23 @@ describe('Linkifier', () => { }); describe('multi-line links', () => { it('should match links that start on line 1/2 of a wrapped line and end on the last character of line 1/2', done => { - (terminal as any).cols = 4; + bufferService.resize(4, bufferService.rows); + bufferService.buffer.lines.length = 0; assertLinkifiesMultiLineLink('12345', /1234/, [{x1: 0, x2: 4, y1: 0, y2: 0}], done); }); it('should match links that start on line 1/2 of a wrapped line and wrap to line 2/2', done => { - (terminal as any).cols = 4; + bufferService.resize(4, bufferService.rows); + bufferService.buffer.lines.length = 0; assertLinkifiesMultiLineLink('12345', /12345/, [{x1: 0, x2: 1, y1: 0, y2: 1}], done); }); it('should match links that start and end on line 2/2 of a wrapped line', done => { - (terminal as any).cols = 4; + bufferService.resize(4, bufferService.rows); + bufferService.buffer.lines.length = 0; assertLinkifiesMultiLineLink('12345678', /5678/, [{x1: 0, x2: 4, y1: 1, y2: 1}], done); }); it('should match links that start on line 2/3 of a wrapped line and wrap to line 3/3', done => { - (terminal as any).cols = 4; + bufferService.resize(4, bufferService.rows); + bufferService.buffer.lines.length = 0; assertLinkifiesMultiLineLink('123456789', /56789/, [{x1: 0, x2: 1, y1: 1, y2: 2}], done); }); }); @@ -164,6 +162,7 @@ describe('Linkifier', () => { describe('validationCallback', () => { it('should enable link if true', done => { + bufferService.buffer.lines.length = 0; addRow('test'); linkifier.registerLinkMatcher(/test/, () => done(), { validationCallback: (url, cb) => { @@ -251,7 +250,7 @@ describe('Linkifier', () => { terminal = new TestTerminal({cols: 10, rows: 5}); linkifier = new TestLinkifier(terminal); mouseZoneManager = new TestMouseZoneManager(); - linkifier.attachToDom(mouseZoneManager); + linkifier.attachToDom(null, mouseZoneManager); }); function assertLinkifiesInTerminal(rowText: string, linkMatcherRegex: RegExp, links: {x1: number, y1: number, x2: number, y2: number}[], done: MochaDone): void { diff --git a/src/Linkifier.ts b/src/Linkifier.ts index 769f3a50..ee585a48 100644 --- a/src/Linkifier.ts +++ b/src/Linkifier.ts @@ -3,12 +3,12 @@ * @license MIT */ -import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, ITerminal, IMouseZoneManager } from './Types'; +import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, IMouseZoneManager } from './Types'; import { IBufferStringIteratorResult } from 'common/buffer/Types'; import { MouseZone } from './MouseZoneManager'; import { getStringCellWidth } from 'common/CharWidth'; import { EventEmitter, IEvent } from 'common/EventEmitter'; -import { ILogService } from 'common/services/Services'; +import { ILogService, IBufferService } from 'common/services/Services'; /** * Limit of the unwrapping line expansion (overscan) at the top and bottom @@ -30,7 +30,9 @@ export class Linkifier implements ILinkifier { protected _linkMatchers: ILinkMatcher[] = []; - private _mouseZoneManager: IMouseZoneManager; + private _mouseZoneManager: IMouseZoneManager | undefined; + private _element: HTMLElement | undefined; + private _rowsTimeoutId: number; private _nextLinkMatcherId = 0; private _rowsToLinkify: { start: number, end: number }; @@ -43,8 +45,8 @@ export class Linkifier implements ILinkifier { public get onLinkTooltip(): IEvent { return this._onLinkTooltip.event; } constructor( - protected _terminal: ITerminal, - private _logService: ILogService + protected readonly _bufferService: IBufferService, + private readonly _logService: ILogService ) { this._rowsToLinkify = { start: null, @@ -56,7 +58,8 @@ export class Linkifier implements ILinkifier { * Attaches the linkifier to the DOM, enabling linkification. * @param mouseZoneManager The mouse zone manager to register link zones with. */ - public attachToDom(mouseZoneManager: IMouseZoneManager): void { + public attachToDom(element: HTMLElement, mouseZoneManager: IMouseZoneManager): void { + this._element = element; this._mouseZoneManager = mouseZoneManager; } @@ -95,7 +98,7 @@ export class Linkifier implements ILinkifier { */ private _linkifyRows(): void { this._rowsTimeoutId = null; - const buffer = this._terminal.buffer; + const buffer = this._bufferService.buffer; // Ensure the start row exists const absoluteRowIndexStart = buffer.ydisp + this._rowsToLinkify.start; @@ -104,7 +107,7 @@ export class Linkifier implements ILinkifier { } // Invalidate bad end row values (if a resize happened) - const absoluteRowIndexEnd = buffer.ydisp + Math.min(this._rowsToLinkify.end, this._terminal.rows) + 1; + const absoluteRowIndexEnd = buffer.ydisp + Math.min(this._rowsToLinkify.end, this._bufferService.rows) + 1; // Iterate over the range of unwrapped content strings within start..end // (excluding). @@ -116,8 +119,8 @@ export class Linkifier implements ILinkifier { // the viewport to +OVERSCAN_CHAR_LIMIT chars (overscan) at top and bottom. // This comes with the tradeoff that matches longer than OVERSCAN_CHAR_LIMIT // chars will not match anymore at the viewport borders. - const overscanLineLimit = Math.ceil(OVERSCAN_CHAR_LIMIT / this._terminal.cols); - const iterator = this._terminal.buffer.iterator( + const overscanLineLimit = Math.ceil(OVERSCAN_CHAR_LIMIT / this._bufferService.cols); + const iterator = this._bufferService.buffer.iterator( false, absoluteRowIndexStart, absoluteRowIndexEnd, overscanLineLimit, overscanLineLimit); while (iterator.hasNext()) { const lineData: IBufferStringIteratorResult = iterator.next(); @@ -228,13 +231,13 @@ export class Linkifier implements ILinkifier { } // get the buffer index as [absolute row, col] for the match - const bufferIndex = this._terminal.buffer.stringIndexToBufferIndex(rowIndex, stringIndex); + const bufferIndex = this._bufferService.buffer.stringIndexToBufferIndex(rowIndex, stringIndex); if (bufferIndex[0] < 0) { // invalid bufferIndex (should not have happened) break; } - const line = this._terminal.buffer.lines.get(bufferIndex[0]); + const line = this._bufferService.buffer.lines.get(bufferIndex[0]); const attr = line.getFg(bufferIndex[1]); let fg: number | undefined; if (attr) { @@ -248,11 +251,11 @@ export class Linkifier implements ILinkifier { return; } if (isValid) { - this._addLink(bufferIndex[1], bufferIndex[0] - this._terminal.buffer.ydisp, uri, matcher, fg); + this._addLink(bufferIndex[1], bufferIndex[0] - this._bufferService.buffer.ydisp, uri, matcher, fg); } }); } else { - this._addLink(bufferIndex[1], bufferIndex[0] - this._terminal.buffer.ydisp, uri, matcher, fg); + this._addLink(bufferIndex[1], bufferIndex[0] - this._bufferService.buffer.ydisp, uri, matcher, fg); } } } @@ -267,12 +270,12 @@ export class Linkifier implements ILinkifier { */ private _addLink(x: number, y: number, uri: string, matcher: ILinkMatcher, fg: number): void { const width = getStringCellWidth(uri); - const x1 = x % this._terminal.cols; - const y1 = y + Math.floor(x / this._terminal.cols); - let x2 = (x1 + width) % this._terminal.cols; - let y2 = y1 + Math.floor((x1 + width) / this._terminal.cols); + const x1 = x % this._bufferService.cols; + const y1 = y + Math.floor(x / this._bufferService.cols); + let x2 = (x1 + width) % this._bufferService.cols; + let y2 = y1 + Math.floor((x1 + width) / this._bufferService.cols); if (x2 === 0) { - x2 = this._terminal.cols; + x2 = this._bufferService.cols; y2--; } @@ -289,7 +292,7 @@ export class Linkifier implements ILinkifier { }, () => { this._onLinkHover.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg)); - this._terminal.element.classList.add('xterm-cursor-pointer'); + this._element.classList.add('xterm-cursor-pointer'); }, e => { this._onLinkTooltip.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg)); @@ -299,7 +302,7 @@ export class Linkifier implements ILinkifier { }, () => { this._onLinkLeave.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg)); - this._terminal.element.classList.remove('xterm-cursor-pointer'); + this._element.classList.remove('xterm-cursor-pointer'); if (matcher.hoverLeaveCallback) { matcher.hoverLeaveCallback(); } @@ -314,6 +317,6 @@ export class Linkifier implements ILinkifier { } private _createLinkHoverEvent(x1: number, y1: number, x2: number, y2: number, fg: number): ILinkifierEvent { - return { x1, y1, x2, y2, cols: this._terminal.cols, fg }; + return { x1, y1, x2, y2, cols: this._bufferService.cols, fg }; } } diff --git a/src/Terminal.ts b/src/Terminal.ts index a97eeeed..c3b392a5 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -304,9 +304,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._inputHandler.onLineFeed(() => this._onLineFeed.fire()); this.register(this._inputHandler); - this._selectionService = this._selectionService || null; - this.linkifier = this.linkifier || new Linkifier(this, this._logService); - this._mouseZoneManager = this._mouseZoneManager || null; + this.linkifier = this.linkifier || new Linkifier(this._bufferService, this._logService); if (this.options.windowsMode) { this._windowsMode = applyWindowsMode(this); @@ -603,7 +601,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._mouseZoneManager = new MouseZoneManager(this, this._mouseService); this.register(this._mouseZoneManager); this.register(this.onScroll(() => this._mouseZoneManager.clearAll())); - this.linkifier.attachToDom(this._mouseZoneManager); + this.linkifier.attachToDom(this.element, this._mouseZoneManager); this.viewport = new Viewport(this, this._viewportElement, this._viewportScrollArea, this._renderService.dimensions, this._charSizeService); this.viewport.onThemeChange(this._colorManager.colors); diff --git a/src/Types.d.ts b/src/Types.d.ts index 1aa05b6c..1c56ef1b 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -290,7 +290,7 @@ export interface ILinkifier { onLinkLeave: IEvent; onLinkTooltip: IEvent; - attachToDom(mouseZoneManager: IMouseZoneManager): void; + attachToDom(element: HTMLElement, mouseZoneManager: IMouseZoneManager): void; linkifyRows(start: number, end: number): void; registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number; deregisterLinkMatcher(matcherId: number): boolean; From f0a02e318832626e611e8405658f3da8dcc9badf Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 13:53:16 -0700 Subject: [PATCH 47/69] Move linkifier types into browser --- src/Linkifier.test.ts | 2 +- src/Linkifier.ts | 18 ++++++- src/MouseZoneManager.ts | 18 +------ src/Terminal.ts | 3 +- src/TestUtils.test.ts | 4 +- src/Types.d.ts | 88 +------------------------------- src/browser/Types.d.ts | 89 +++++++++++++++++++++++++++++++++ src/renderer/LinkRenderLayer.ts | 4 +- src/renderer/dom/DomRenderer.ts | 4 +- 9 files changed, 117 insertions(+), 113 deletions(-) diff --git a/src/Linkifier.test.ts b/src/Linkifier.test.ts index 973e6d9b..9849e03d 100644 --- a/src/Linkifier.test.ts +++ b/src/Linkifier.test.ts @@ -4,7 +4,7 @@ */ import { assert } from 'chai'; -import { IMouseZoneManager, IMouseZone, ILinkMatcher } from './Types'; +import { IMouseZoneManager, IMouseZone, ILinkMatcher } from 'browser/Types'; import { IBufferLine } from 'common/Types'; import { Linkifier } from './Linkifier'; import { TestTerminal } from './TestUtils.test'; diff --git a/src/Linkifier.ts b/src/Linkifier.ts index ee585a48..0d31cf51 100644 --- a/src/Linkifier.ts +++ b/src/Linkifier.ts @@ -3,9 +3,8 @@ * @license MIT */ -import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, IMouseZoneManager } from './Types'; +import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, IMouseZoneManager, IMouseZone } from 'browser/Types'; import { IBufferStringIteratorResult } from 'common/buffer/Types'; -import { MouseZone } from './MouseZoneManager'; import { getStringCellWidth } from 'common/CharWidth'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { ILogService, IBufferService } from 'common/services/Services'; @@ -320,3 +319,18 @@ export class Linkifier implements ILinkifier { return { x1, y1, x2, y2, cols: this._bufferService.cols, fg }; } } + +export class MouseZone implements IMouseZone { + constructor( + public x1: number, + public y1: number, + public x2: number, + public y2: number, + public clickCallback: (e: MouseEvent) => any, + public hoverCallback: (e: MouseEvent) => any, + public tooltipCallback: (e: MouseEvent) => any, + public leaveCallback: () => void, + public willLinkActivate: (e: MouseEvent) => boolean + ) { + } +} diff --git a/src/MouseZoneManager.ts b/src/MouseZoneManager.ts index de724b88..92ae9b1d 100644 --- a/src/MouseZoneManager.ts +++ b/src/MouseZoneManager.ts @@ -3,10 +3,11 @@ * @license MIT */ -import { ITerminal, IMouseZoneManager, IMouseZone } from './Types'; +import { ITerminal } from './Types'; import { Disposable } from 'common/Lifecycle'; import { addDisposableDomListener } from 'browser/Lifecycle'; import { IMouseService } from 'browser/services/Services'; +import { IMouseZoneManager, IMouseZone } from 'browser/Types'; const HOVER_DURATION = 500; @@ -230,18 +231,3 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { return null; } } - -export class MouseZone implements IMouseZone { - constructor( - public x1: number, - public y1: number, - public x2: number, - public y2: number, - public clickCallback: (e: MouseEvent) => any, - public hoverCallback: (e: MouseEvent) => any, - public tooltipCallback: (e: MouseEvent) => any, - public leaveCallback: () => void, - public willLinkActivate: (e: MouseEvent) => boolean - ) { - } -} diff --git a/src/Terminal.ts b/src/Terminal.ts index c3b392a5..aadc73a9 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -21,7 +21,7 @@ * http://linux.die.net/man/7/urxvt */ -import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, IMouseZoneManager } from './Types'; +import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, CustomKeyEventHandler } from './Types'; import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; import { CompositionHelper } from 'browser/input/CompositionHelper'; import { Viewport } from './Viewport'; @@ -59,6 +59,7 @@ import { MouseService } from 'browser/services/MouseService'; import { IParams } from 'common/parser/Types'; import { CoreService } from 'common/services/CoreService'; import { LogService } from 'common/services/LogService'; +import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions } from 'browser/Types'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index e9a76f7f..5cee2e28 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -4,7 +4,7 @@ */ import { IRenderer, IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types'; -import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminal, IBrowser, ITerminalOptions, ILinkifier, ILinkMatcherOptions } from './Types'; +import { IInputHandlingTerminal, IViewport, 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'; @@ -12,7 +12,7 @@ import * as Browser from 'common/Platform'; import { IDisposable, IMarker, IEvent, ISelectionPosition } from 'xterm'; import { Terminal } from './Terminal'; import { AttributeData } from 'common/buffer/AttributeData'; -import { IColorManager, IColorSet } from 'browser/Types'; +import { IColorManager, IColorSet, ILinkMatcherOptions, ILinkifier } from 'browser/Types'; import { IOptionsService } from 'common/services/Services'; import { EventEmitter } from 'common/EventEmitter'; import { IParams } from 'common/parser/Types'; diff --git a/src/Types.d.ts b/src/Types.d.ts index 1c56ef1b..1a647927 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -6,7 +6,7 @@ import { ITerminalOptions as IPublicTerminalOptions, IDisposable, IMarker, ISelectionPosition } from 'xterm'; import { ICharset, IAttributeData, CharData } from 'common/Types'; import { IEvent, IEventEmitter } from 'common/EventEmitter'; -import { IColorSet } from 'browser/Types'; +import { IColorSet, ILinkifier, ILinkMatcherOptions } from 'browser/Types'; import { IOptionsService } from 'common/services/Services'; import { IBuffer, IBufferSet } from 'common/buffer/Types'; import { IParams } from 'common/parser/Types'; @@ -15,9 +15,6 @@ export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean; export type LineData = CharData[]; -export type LinkMatcherHandler = (event: MouseEvent, uri: string) => void; -export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void; - /** * This interface encapsulates everything needed from the Terminal by the * InputHandler. This cleanly separates the large amount of methods needed by @@ -167,27 +164,6 @@ export interface IInputHandler { /** ESC # 8 */ screenAlignmentPattern(): void; } -export interface ILinkMatcher { - id: number; - regex: RegExp; - handler: LinkMatcherHandler; - hoverTooltipCallback?: LinkMatcherHandler; - hoverLeaveCallback?: () => void; - matchIndex?: number; - validationCallback?: LinkMatcherValidationCallback; - priority?: number; - willLinkActivate?: (event: MouseEvent, uri: string) => boolean; -} - -export interface ILinkifierEvent { - x1: number; - y1: number; - x2: number; - y2: number; - cols: number; - fg: number; -} - export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor { screenElement: HTMLElement; browser: IBrowser; @@ -285,51 +261,6 @@ export interface ITerminalOptions extends IPublicTerminalOptions { useFlowControl?: boolean; } -export interface ILinkifier { - onLinkHover: IEvent; - onLinkLeave: IEvent; - onLinkTooltip: IEvent; - - attachToDom(element: HTMLElement, mouseZoneManager: IMouseZoneManager): void; - linkifyRows(start: number, end: number): void; - registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number; - deregisterLinkMatcher(matcherId: number): boolean; -} - -export interface ILinkMatcherOptions { - /** - * The index of the link from the regex.match(text) call. This defaults to 0 - * (for regular expressions without capture groups). - */ - matchIndex?: number; - /** - * A callback that validates an individual link, returning true if valid and - * false if invalid. - */ - validationCallback?: LinkMatcherValidationCallback; - /** - * A callback that fires when the mouse hovers over a link. - */ - tooltipCallback?: LinkMatcherHandler; - /** - * A callback that fires when the mouse leaves a link that was hovered. - */ - leaveCallback?: () => void; - /** - * The priority of the link matcher, this defines the order in which the link - * matcher is evaluated relative to others, from highest to lowest. The - * default value is 0. - */ - priority?: number; - /** - * A callback that fires when the mousedown and click events occur that - * determines whether a link will be activated upon click. This enables - * only activating a link when a certain modifier is held down, if not the - * mouse event will continue propagation (eg. double click to select word). - */ - willLinkActivate?: (event: MouseEvent, uri: string) => boolean; -} - export interface IBrowser { isNode: boolean; userAgent: string; @@ -340,20 +271,3 @@ export interface IBrowser { isIphone: boolean; isWindows: boolean; } - -export interface IMouseZoneManager extends IDisposable { - add(zone: IMouseZone): void; - clearAll(start?: number, end?: number): void; -} - -export interface IMouseZone { - x1: number; - x2: number; - y1: number; - y2: number; - clickCallback: (e: MouseEvent) => any; - hoverCallback: (e: MouseEvent) => any | undefined; - tooltipCallback: (e: MouseEvent) => any | undefined; - leaveCallback: () => any | undefined; - willLinkActivate: (e: MouseEvent) => boolean; -} diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts index ef725ba6..9add34fe 100644 --- a/src/browser/Types.d.ts +++ b/src/browser/Types.d.ts @@ -3,6 +3,9 @@ * @license MIT */ +import { IEvent } from 'common/EventEmitter'; +import { IDisposable } from 'common/Types'; + export interface IColorManager { colors: IColorSet; } @@ -20,3 +23,89 @@ export interface IColorSet { selection: IColor; ansi: IColor[]; } + +export type LinkMatcherHandler = (event: MouseEvent, uri: string) => void; +export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void; + +export interface ILinkMatcher { + id: number; + regex: RegExp; + handler: LinkMatcherHandler; + hoverTooltipCallback?: LinkMatcherHandler; + hoverLeaveCallback?: () => void; + matchIndex?: number; + validationCallback?: LinkMatcherValidationCallback; + priority?: number; + willLinkActivate?: (event: MouseEvent, uri: string) => boolean; +} + +export interface ILinkifierEvent { + x1: number; + y1: number; + x2: number; + y2: number; + cols: number; + fg: number; +} + +export interface ILinkifier { + onLinkHover: IEvent; + onLinkLeave: IEvent; + onLinkTooltip: IEvent; + + attachToDom(element: HTMLElement, mouseZoneManager: IMouseZoneManager): void; + linkifyRows(start: number, end: number): void; + registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number; + deregisterLinkMatcher(matcherId: number): boolean; +} + +export interface ILinkMatcherOptions { + /** + * The index of the link from the regex.match(text) call. This defaults to 0 + * (for regular expressions without capture groups). + */ + matchIndex?: number; + /** + * A callback that validates an individual link, returning true if valid and + * false if invalid. + */ + validationCallback?: LinkMatcherValidationCallback; + /** + * A callback that fires when the mouse hovers over a link. + */ + tooltipCallback?: LinkMatcherHandler; + /** + * A callback that fires when the mouse leaves a link that was hovered. + */ + leaveCallback?: () => void; + /** + * The priority of the link matcher, this defines the order in which the link + * matcher is evaluated relative to others, from highest to lowest. The + * default value is 0. + */ + priority?: number; + /** + * A callback that fires when the mousedown and click events occur that + * determines whether a link will be activated upon click. This enables + * only activating a link when a certain modifier is held down, if not the + * mouse event will continue propagation (eg. double click to select word). + */ + willLinkActivate?: (event: MouseEvent, uri: string) => boolean; +} + +export interface IMouseZoneManager extends IDisposable { + add(zone: IMouseZone): void; + clearAll(start?: number, end?: number): void; +} + +export interface IMouseZone { + x1: number; + x2: number; + y1: number; + y2: number; + clickCallback: (e: MouseEvent) => any; + hoverCallback: (e: MouseEvent) => any | undefined; + tooltipCallback: (e: MouseEvent) => any | undefined; + leaveCallback: () => any | undefined; + willLinkActivate: (e: MouseEvent) => boolean; +} diff --git a/src/renderer/LinkRenderLayer.ts b/src/renderer/LinkRenderLayer.ts index f32b5ad9..6e6ba2e4 100644 --- a/src/renderer/LinkRenderLayer.ts +++ b/src/renderer/LinkRenderLayer.ts @@ -3,12 +3,12 @@ * @license MIT */ -import { ILinkifierEvent, ITerminal, ILinkifierAccessor } from '../Types'; +import { ITerminal, ILinkifierAccessor } from '../Types'; import { IRenderDimensions } from 'browser/renderer/Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { is256Color } from './atlas/CharAtlasUtils'; -import { IColorSet } from 'browser/Types'; +import { IColorSet, ILinkifierEvent } from 'browser/Types'; export class LinkRenderLayer extends BaseRenderLayer { private _state: ILinkifierEvent = null; diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index a6b6e868..60bf50da 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -4,11 +4,11 @@ */ import { IRenderer, IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types'; -import { ILinkifierEvent, ITerminal } from '../../Types'; +import { ITerminal } from '../../Types'; import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_BLINK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from 'browser/renderer/dom/DomRendererRowFactory'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { Disposable } from 'common/Lifecycle'; -import { IColorSet } from 'browser/Types'; +import { IColorSet, ILinkifierEvent } from 'browser/Types'; import { ICharSizeService } from 'browser/services/Services'; import { IOptionsService } from 'common/services/Services'; From 0cd4093e53507191a84a580b91a1f16badbcdf1a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 14:15:28 -0700 Subject: [PATCH 48/69] Move Linkifier to browser --- .../src/renderLayer/LinkRenderLayer.ts | 8 +- src/Terminal.ts | 4 +- src/{ => browser}/Linkifier.test.ts | 188 +++++++++--------- src/{ => browser}/Linkifier.ts | 50 +++-- src/browser/Types.d.ts | 6 +- src/browser/tsconfig.json | 2 +- 6 files changed, 136 insertions(+), 122 deletions(-) rename src/{ => browser}/Linkifier.test.ts (62%) rename src/{ => browser}/Linkifier.ts (89%) diff --git a/addons/xterm-addon-webgl/src/renderLayer/LinkRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/LinkRenderLayer.ts index a29d2cfd..118aedc3 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/LinkRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/LinkRenderLayer.ts @@ -3,12 +3,12 @@ * @license MIT */ -import { ILinkifierEvent, ILinkifierAccessor } from '../../../../src/Types'; +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 } from 'browser/Types'; +import { IColorSet, ILinkifierEvent } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/Types'; export class LinkRenderLayer extends BaseRenderLayer { @@ -45,9 +45,9 @@ export class LinkRenderLayer extends BaseRenderLayer { private _onLinkHover(e: ILinkifierEvent): void { if (e.fg === INVERTED_DEFAULT_COLOR) { this._ctx.fillStyle = this._colors.background.css; - } else if (is256Color(e.fg)) { + } else if (e.fg !== undefined && is256Color(e.fg)) { // 256 color support - this._ctx.fillStyle = this._colors.ansi[e.fg].css; + this._ctx.fillStyle = this._colors.ansi[e.fg!].css; } else { this._ctx.fillStyle = this._colors.foreground.css; } diff --git a/src/Terminal.ts b/src/Terminal.ts index aadc73a9..6b0f4946 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -25,11 +25,11 @@ import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; import { CompositionHelper } from 'browser/input/CompositionHelper'; import { Viewport } from './Viewport'; -import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './browser/Clipboard'; +import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from 'browser/Clipboard'; import { C0 } from 'common/data/EscapeSequences'; import { InputHandler } from './InputHandler'; import { Renderer } from './renderer/Renderer'; -import { Linkifier } from './Linkifier'; +import { Linkifier } from 'browser/Linkifier'; import { SelectionService } from './browser/services/SelectionService'; import * as Browser from 'common/Platform'; import { addDisposableDomListener } from 'browser/Lifecycle'; diff --git a/src/Linkifier.test.ts b/src/browser/Linkifier.test.ts similarity index 62% rename from src/Linkifier.test.ts rename to src/browser/Linkifier.test.ts index 9849e03d..bf2cc984 100644 --- a/src/Linkifier.test.ts +++ b/src/browser/Linkifier.test.ts @@ -6,8 +6,8 @@ import { assert } from 'chai'; import { IMouseZoneManager, IMouseZone, ILinkMatcher } from 'browser/Types'; import { IBufferLine } from 'common/Types'; -import { Linkifier } from './Linkifier'; -import { TestTerminal } from './TestUtils.test'; +import { Linkifier } from 'browser/Linkifier'; +// import { TestTerminal } from '../TestUtils.test'; import { BufferLine } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; import { MockLogService, MockBufferService } from 'common/TestUtils.test'; @@ -105,7 +105,7 @@ describe('Linkifier', () => { describe('after attachToDom', () => { beforeEach(() => { - linkifier.attachToDom(null, mouseZoneManager); + linkifier.attachToDom(undefined as any, mouseZoneManager); }); describe('link matcher', () => { @@ -241,98 +241,98 @@ describe('Linkifier', () => { }); }); }); - describe('unicode handling', () => { - let terminal: TestTerminal; + // describe('unicode handling', () => { + // let terminal: TestTerminal; - // other than the tests above unicode testing needs the full terminal instance - // to get the special handling of fullwidth, surrogate and combining chars in the input handler - beforeEach(() => { - terminal = new TestTerminal({cols: 10, rows: 5}); - linkifier = new TestLinkifier(terminal); - mouseZoneManager = new TestMouseZoneManager(); - linkifier.attachToDom(null, mouseZoneManager); - }); + // // other than the tests above unicode testing needs the full terminal instance + // // to get the special handling of fullwidth, surrogate and combining chars in the input handler + // beforeEach(() => { + // terminal = new TestTerminal({cols: 10, rows: 5}); + // linkifier = new TestLinkifier(terminal); + // mouseZoneManager = new TestMouseZoneManager(); + // linkifier.attachToDom(undefined as any, mouseZoneManager); + // }); - function assertLinkifiesInTerminal(rowText: string, linkMatcherRegex: RegExp, links: {x1: number, y1: number, x2: number, y2: number}[], done: MochaDone): void { - terminal.writeSync(rowText); - linkifier.registerLinkMatcher(linkMatcherRegex, () => {}); - linkifier.linkifyRows(); - // Allow linkify to happen - setTimeout(() => { - assert.equal(mouseZoneManager.zones.length, links.length); - links.forEach((l, i) => { - assert.equal(mouseZoneManager.zones[i].x1, l.x1 + 1); - assert.equal(mouseZoneManager.zones[i].x2, l.x2 + 1); - assert.equal(mouseZoneManager.zones[i].y1, l.y1 + 1); - assert.equal(mouseZoneManager.zones[i].y2, l.y2 + 1); - }); - done(); - }, 0); - } + // function assertLinkifiesInTerminal(rowText: string, linkMatcherRegex: RegExp, links: {x1: number, y1: number, x2: number, y2: number}[], done: MochaDone): void { + // terminal.writeSync(rowText); + // linkifier.registerLinkMatcher(linkMatcherRegex, () => {}); + // linkifier.linkifyRows(); + // // Allow linkify to happen + // setTimeout(() => { + // assert.equal(mouseZoneManager.zones.length, links.length); + // links.forEach((l, i) => { + // assert.equal(mouseZoneManager.zones[i].x1, l.x1 + 1); + // assert.equal(mouseZoneManager.zones[i].x2, l.x2 + 1); + // assert.equal(mouseZoneManager.zones[i].y1, l.y1 + 1); + // assert.equal(mouseZoneManager.zones[i].y2, l.y2 + 1); + // }); + // done(); + // }, 0); + // } - describe('unicode before the match', () => { - it('combining - match within one line', function(done: () => void): void { - assertLinkifiesInTerminal('e\u0301e\u0301e\u0301 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); - }); - it('combining - match over two lines', function(done: () => void): void { - assertLinkifiesInTerminal('e\u0301e\u0301e\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); - }); - it('surrogate - match within one line', function(done: () => void): void { - assertLinkifiesInTerminal('𝄞𝄞𝄞 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); - }); - it('surrogate - match over two lines', function(done: () => void): void { - assertLinkifiesInTerminal('𝄞𝄞𝄞 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); - }); - it('combining surrogate - match within one line', function(done: () => void): void { - assertLinkifiesInTerminal('𓂀\u0301𓂀\u0301𓂀\u0301 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); - }); - it('combining surrogate - match over two lines', function(done: () => void): void { - assertLinkifiesInTerminal('𓂀\u0301𓂀\u0301𓂀\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); - }); - it('fullwidth - match within one line', function(done: () => void): void { - assertLinkifiesInTerminal('12 foo', /foo/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); - }); - it('fullwidth - match over two lines', function(done: () => void): void { - assertLinkifiesInTerminal('12 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); - }); - it('combining fullwidth - match within one line', function(done: () => void): void { - assertLinkifiesInTerminal('¥\u0301¥\u0301 foo', /foo/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); - }); - it('combining fullwidth - match over two lines', function(done: () => void): void { - assertLinkifiesInTerminal('¥\u0301¥\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); - }); - }); - describe('unicode within the match', () => { - it('combining - match within one line', function(done: () => void): void { - assertLinkifiesInTerminal('test cafe\u0301', /cafe\u0301/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); - }); - it('combining - match over two lines', function(done: () => void): void { - assertLinkifiesInTerminal('testtest cafe\u0301', /cafe\u0301/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); - }); - it('surrogate - match within one line', function(done: () => void): void { - assertLinkifiesInTerminal('test a𝄞b', /a𝄞b/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); - }); - it('surrogate - match over two lines', function(done: () => void): void { - assertLinkifiesInTerminal('testtest a𝄞b', /a𝄞b/, [{x1: 9, x2: 2, y1: 0, y2: 1}], done); - }); - it('combining surrogate - match within one line', function(done: () => void): void { - assertLinkifiesInTerminal('test a𓂀\u0301b', /a𓂀\u0301b/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); - }); - it('combining surrogate - match over two lines', function(done: () => void): void { - assertLinkifiesInTerminal('testtest a𓂀\u0301b', /a𓂀\u0301b/, [{x1: 9, x2: 2, y1: 0, y2: 1}], done); - }); - it('fullwidth - match within one line', function(done: () => void): void { - assertLinkifiesInTerminal('test a1b', /a1b/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); - }); - it('fullwidth - match over two lines', function(done: () => void): void { - assertLinkifiesInTerminal('testtest a1b', /a1b/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); - }); - it('combining fullwidth - match within one line', function(done: () => void): void { - assertLinkifiesInTerminal('test a¥\u0301b', /a¥\u0301b/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); - }); - it('combining fullwidth - match over two lines', function(done: () => void): void { - assertLinkifiesInTerminal('testtest a¥\u0301b', /a¥\u0301b/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); - }); - }); - }); + // describe('unicode before the match', () => { + // it('combining - match within one line', function(done: () => void): void { + // assertLinkifiesInTerminal('e\u0301e\u0301e\u0301 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); + // }); + // it('combining - match over two lines', function(done: () => void): void { + // assertLinkifiesInTerminal('e\u0301e\u0301e\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); + // }); + // it('surrogate - match within one line', function(done: () => void): void { + // assertLinkifiesInTerminal('𝄞𝄞𝄞 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); + // }); + // it('surrogate - match over two lines', function(done: () => void): void { + // assertLinkifiesInTerminal('𝄞𝄞𝄞 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); + // }); + // it('combining surrogate - match within one line', function(done: () => void): void { + // assertLinkifiesInTerminal('𓂀\u0301𓂀\u0301𓂀\u0301 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); + // }); + // it('combining surrogate - match over two lines', function(done: () => void): void { + // assertLinkifiesInTerminal('𓂀\u0301𓂀\u0301𓂀\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); + // }); + // it('fullwidth - match within one line', function(done: () => void): void { + // assertLinkifiesInTerminal('12 foo', /foo/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); + // }); + // it('fullwidth - match over two lines', function(done: () => void): void { + // assertLinkifiesInTerminal('12 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); + // }); + // it('combining fullwidth - match within one line', function(done: () => void): void { + // assertLinkifiesInTerminal('¥\u0301¥\u0301 foo', /foo/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); + // }); + // it('combining fullwidth - match over two lines', function(done: () => void): void { + // assertLinkifiesInTerminal('¥\u0301¥\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); + // }); + // }); + // describe('unicode within the match', () => { + // it('combining - match within one line', function(done: () => void): void { + // assertLinkifiesInTerminal('test cafe\u0301', /cafe\u0301/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); + // }); + // it('combining - match over two lines', function(done: () => void): void { + // assertLinkifiesInTerminal('testtest cafe\u0301', /cafe\u0301/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); + // }); + // it('surrogate - match within one line', function(done: () => void): void { + // assertLinkifiesInTerminal('test a𝄞b', /a𝄞b/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); + // }); + // it('surrogate - match over two lines', function(done: () => void): void { + // assertLinkifiesInTerminal('testtest a𝄞b', /a𝄞b/, [{x1: 9, x2: 2, y1: 0, y2: 1}], done); + // }); + // it('combining surrogate - match within one line', function(done: () => void): void { + // assertLinkifiesInTerminal('test a𓂀\u0301b', /a𓂀\u0301b/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); + // }); + // it('combining surrogate - match over two lines', function(done: () => void): void { + // assertLinkifiesInTerminal('testtest a𓂀\u0301b', /a𓂀\u0301b/, [{x1: 9, x2: 2, y1: 0, y2: 1}], done); + // }); + // it('fullwidth - match within one line', function(done: () => void): void { + // assertLinkifiesInTerminal('test a1b', /a1b/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); + // }); + // it('fullwidth - match over two lines', function(done: () => void): void { + // assertLinkifiesInTerminal('testtest a1b', /a1b/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); + // }); + // it('combining fullwidth - match within one line', function(done: () => void): void { + // assertLinkifiesInTerminal('test a¥\u0301b', /a¥\u0301b/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); + // }); + // it('combining fullwidth - match over two lines', function(done: () => void): void { + // assertLinkifiesInTerminal('testtest a¥\u0301b', /a¥\u0301b/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); + // }); + // }); + // }); }); diff --git a/src/Linkifier.ts b/src/browser/Linkifier.ts similarity index 89% rename from src/Linkifier.ts rename to src/browser/Linkifier.ts index 0d31cf51..a5454ac5 100644 --- a/src/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, IMouseZoneManager, IMouseZone } from 'browser/Types'; +import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, IMouseZoneManager, IMouseZone, IRegisteredLinkMatcher } from 'browser/Types'; import { IBufferStringIteratorResult } from 'common/buffer/Types'; import { getStringCellWidth } from 'common/CharWidth'; import { EventEmitter, IEvent } from 'common/EventEmitter'; @@ -27,14 +27,14 @@ export class Linkifier implements ILinkifier { */ protected static _timeBeforeLatency = 200; - protected _linkMatchers: ILinkMatcher[] = []; + protected _linkMatchers: IRegisteredLinkMatcher[] = []; private _mouseZoneManager: IMouseZoneManager | undefined; private _element: HTMLElement | undefined; - private _rowsTimeoutId: number; + private _rowsTimeoutId: number | undefined; private _nextLinkMatcherId = 0; - private _rowsToLinkify: { start: number, end: number }; + private _rowsToLinkify: { start: number | undefined, end: number | undefined }; private _onLinkHover = new EventEmitter(); public get onLinkHover(): IEvent { return this._onLinkHover.event; } @@ -48,8 +48,8 @@ export class Linkifier implements ILinkifier { private readonly _logService: ILogService ) { this._rowsToLinkify = { - start: null, - end: null + start: undefined, + end: undefined }; } @@ -74,7 +74,7 @@ export class Linkifier implements ILinkifier { } // Increase range to linkify - if (this._rowsToLinkify.start === null) { + if (this._rowsToLinkify.start === undefined || this._rowsToLinkify.end === undefined) { this._rowsToLinkify.start = start; this._rowsToLinkify.end = end; } else { @@ -96,9 +96,14 @@ export class Linkifier implements ILinkifier { * Linkifies the rows requested. */ private _linkifyRows(): void { - this._rowsTimeoutId = null; + this._rowsTimeoutId = undefined; const buffer = this._bufferService.buffer; + if (this._rowsToLinkify.start === undefined || this._rowsToLinkify.end === undefined) { + this._logService.debug('_rowToLinkify was unset before _linkifyRows was called'); + return; + } + // Ensure the start row exists const absoluteRowIndexStart = buffer.ydisp + this._rowsToLinkify.start; if (absoluteRowIndexStart >= buffer.lines.length) { @@ -128,8 +133,8 @@ export class Linkifier implements ILinkifier { } } - this._rowsToLinkify.start = null; - this._rowsToLinkify.end = null; + this._rowsToLinkify.start = undefined; + this._rowsToLinkify.end = undefined; } /** @@ -146,7 +151,7 @@ export class Linkifier implements ILinkifier { if (!handler) { throw new Error('handler must be defined'); } - const matcher: ILinkMatcher = { + const matcher: IRegisteredLinkMatcher = { id: this._nextLinkMatcherId++, regex, handler, @@ -167,7 +172,7 @@ export class Linkifier implements ILinkifier { * considered after older link matchers. * @param matcher The link matcher to be added. */ - private _addLinkMatcherToList(matcher: ILinkMatcher): void { + private _addLinkMatcherToList(matcher: IRegisteredLinkMatcher): void { if (this._linkMatchers.length === 0) { this._linkMatchers.push(matcher); return; @@ -237,12 +242,13 @@ export class Linkifier implements ILinkifier { } const line = this._bufferService.buffer.lines.get(bufferIndex[0]); - const attr = line.getFg(bufferIndex[1]); - let fg: number | undefined; - if (attr) { - fg = (attr >> 9) & 0x1ff; + if (!line) { + break; } + const attr = line.getFg(bufferIndex[1]); + const fg = attr ? (attr >> 9) & 0x1ff : undefined; + if (matcher.validationCallback) { matcher.validationCallback(uri, isValid => { // Discard link if the line has already changed @@ -267,7 +273,11 @@ export class Linkifier implements ILinkifier { * @param matcher The link matcher for the link. * @param fg The link color for hover event. */ - private _addLink(x: number, y: number, uri: string, matcher: ILinkMatcher, fg: number): void { + private _addLink(x: number, y: number, uri: string, matcher: ILinkMatcher, fg: number | undefined): void { + if (!this._mouseZoneManager || !this._element) { + return; + } + const width = getStringCellWidth(uri); const x1 = x % this._bufferService.cols; const y1 = y + Math.floor(x / this._bufferService.cols); @@ -291,7 +301,7 @@ export class Linkifier implements ILinkifier { }, () => { this._onLinkHover.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg)); - this._element.classList.add('xterm-cursor-pointer'); + this._element!.classList.add('xterm-cursor-pointer'); }, e => { this._onLinkTooltip.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg)); @@ -301,7 +311,7 @@ export class Linkifier implements ILinkifier { }, () => { this._onLinkLeave.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg)); - this._element.classList.remove('xterm-cursor-pointer'); + this._element!.classList.remove('xterm-cursor-pointer'); if (matcher.hoverLeaveCallback) { matcher.hoverLeaveCallback(); } @@ -315,7 +325,7 @@ export class Linkifier implements ILinkifier { )); } - private _createLinkHoverEvent(x1: number, y1: number, x2: number, y2: number, fg: number): ILinkifierEvent { + private _createLinkHoverEvent(x1: number, y1: number, x2: number, y2: number, fg: number | undefined): ILinkifierEvent { return { x1, y1, x2, y2, cols: this._bufferService.cols, fg }; } } diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts index 9add34fe..985244b3 100644 --- a/src/browser/Types.d.ts +++ b/src/browser/Types.d.ts @@ -39,13 +39,17 @@ export interface ILinkMatcher { willLinkActivate?: (event: MouseEvent, uri: string) => boolean; } +export interface IRegisteredLinkMatcher extends ILinkMatcher { + priority: number; +} + export interface ILinkifierEvent { x1: number; y1: number; x2: number; y2: number; cols: number; - fg: number; + fg: number | undefined; } export interface ILinkifier { diff --git a/src/browser/tsconfig.json b/src/browser/tsconfig.json index 06818413..7465bbd3 100644 --- a/src/browser/tsconfig.json +++ b/src/browser/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "lib": [ "dom", - "es5", + "es2015", ], "outDir": "../../out", "types": [ From 2eec18e7f5e1d386c4defc833e89814e81903f2e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 14:35:31 -0700 Subject: [PATCH 49/69] Remove ITerminal from MouseZoneManager --- src/MouseZoneManager.ts | 38 +++++++++++++++++++++-------------- src/Terminal.ts | 10 ++++----- src/browser/Linkifier.test.ts | 2 +- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/MouseZoneManager.ts b/src/MouseZoneManager.ts index 92ae9b1d..42eb6545 100644 --- a/src/MouseZoneManager.ts +++ b/src/MouseZoneManager.ts @@ -3,11 +3,11 @@ * @license MIT */ -import { ITerminal } from './Types'; import { Disposable } from 'common/Lifecycle'; import { addDisposableDomListener } from 'browser/Lifecycle'; -import { IMouseService } from 'browser/services/Services'; +import { IMouseService, ISelectionService } from 'browser/services/Services'; import { IMouseZoneManager, IMouseZone } from 'browser/Types'; +import { IBufferService } from 'common/services/Services'; const HOVER_DURATION = 500; @@ -33,12 +33,15 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { private _initialSelectionLength: number; constructor( - private _terminal: ITerminal, - private _mouseService: IMouseService + private readonly _element: HTMLElement, + private readonly _screenElement: HTMLElement, + private readonly _bufferService: IBufferService, + private readonly _mouseService: IMouseService, + private readonly _selectionService: ISelectionService ) { super(); - this.register(addDisposableDomListener(this._terminal.element, 'mousedown', e => this._onMouseDown(e))); + this.register(addDisposableDomListener(this._element, 'mousedown', e => this._onMouseDown(e))); // These events are expensive, only listen to it when mouse zones are active this._mouseMoveListener = e => this._onMouseMove(e); @@ -67,7 +70,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { // Clear all if start/end weren't set if (!end) { start = 0; - end = this._terminal.rows - 1; + end = this._bufferService.rows - 1; } // Iterate through zones and clear them out if they're within the range @@ -93,18 +96,18 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { private _activate(): void { if (!this._areZonesActive) { this._areZonesActive = true; - this._terminal.element.addEventListener('mousemove', this._mouseMoveListener); - this._terminal.element.addEventListener('mouseleave', this._mouseLeaveListener); - this._terminal.element.addEventListener('click', this._clickListener); + this._element.addEventListener('mousemove', this._mouseMoveListener); + this._element.addEventListener('mouseleave', this._mouseLeaveListener); + this._element.addEventListener('click', this._clickListener); } } private _deactivate(): void { if (this._areZonesActive) { this._areZonesActive = false; - this._terminal.element.removeEventListener('mousemove', this._mouseMoveListener); - this._terminal.element.removeEventListener('mouseleave', this._mouseLeaveListener); - this._terminal.element.removeEventListener('click', this._clickListener); + this._element.removeEventListener('mousemove', this._mouseMoveListener); + this._element.removeEventListener('mouseleave', this._mouseLeaveListener); + this._element.removeEventListener('click', this._clickListener); } } @@ -162,7 +165,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { private _onMouseDown(e: MouseEvent): void { // Store current terminal selection length, to check if we're performing // a selection operation - this._initialSelectionLength = this._terminal.getSelection().length; + this._initialSelectionLength = this._getSelectionLength(); // Ignore the event if there are no zones active if (!this._areZonesActive) { @@ -196,7 +199,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { // Find the active zone and click it if found and no selection was // being performed const zone = this._findZoneEventAt(e); - const currentSelectionLength = this._terminal.getSelection().length; + const currentSelectionLength = this._getSelectionLength(); if (zone && currentSelectionLength === this._initialSelectionLength) { zone.clickCallback(e); @@ -205,8 +208,13 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { } } + private _getSelectionLength(): number { + const selectionText = this._selectionService.selectionText; + return selectionText ? selectionText.length : 0; + } + private _findZoneEventAt(e: MouseEvent): IMouseZone { - const coords = this._mouseService.getCoords(e, this._terminal.screenElement, this._terminal.cols, this._terminal.rows); + const coords = this._mouseService.getCoords(e, this._screenElement, this._bufferService.cols, this._bufferService.rows); if (!coords) { return null; } diff --git a/src/Terminal.ts b/src/Terminal.ts index 6b0f4946..d9a6d77b 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -599,11 +599,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._soundService = new SoundService(this.optionsService); this._mouseService = new MouseService(this._renderService, this._charSizeService); - this._mouseZoneManager = new MouseZoneManager(this, this._mouseService); - this.register(this._mouseZoneManager); - this.register(this.onScroll(() => this._mouseZoneManager.clearAll())); - this.linkifier.attachToDom(this.element, this._mouseZoneManager); - this.viewport = new Viewport(this, this._viewportElement, this._viewportScrollArea, this._renderService.dimensions, this._charSizeService); this.viewport.onThemeChange(this._colorManager.colors); this.register(this.viewport); @@ -636,6 +631,11 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp })); this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this._selectionService.refresh())); + this._mouseZoneManager = new MouseZoneManager(this.element, this.screenElement, this._bufferService, this._mouseService, this._selectionService); + this.register(this._mouseZoneManager); + this.register(this.onScroll(() => this._mouseZoneManager.clearAll())); + this.linkifier.attachToDom(this.element, this._mouseZoneManager); + // apply mouse event classes set by escape codes before terminal was attached this.element.classList.toggle('enable-mouse-events', this.mouseEvents); if (this.mouseEvents) { diff --git a/src/browser/Linkifier.test.ts b/src/browser/Linkifier.test.ts index bf2cc984..f15eebcc 100644 --- a/src/browser/Linkifier.test.ts +++ b/src/browser/Linkifier.test.ts @@ -105,7 +105,7 @@ describe('Linkifier', () => { describe('after attachToDom', () => { beforeEach(() => { - linkifier.attachToDom(undefined as any, mouseZoneManager); + linkifier.attachToDom({} as any, mouseZoneManager); }); describe('link matcher', () => { From 78f015308af1c644589a2459d5f9051c1df878c6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 17:28:09 -0700 Subject: [PATCH 50/69] Re-enable linkifier unicode tests in Terminal.test.ts --- src/Terminal.test.ts | 132 ++++++++++++++++++++++++++++++++-- src/TestUtils.test.ts | 2 + src/browser/Linkifier.test.ts | 99 +------------------------ 3 files changed, 129 insertions(+), 104 deletions(-) diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index dad8d46a..b83692e7 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -4,20 +4,18 @@ */ import { assert, expect } from 'chai'; -import { Terminal } from './Terminal'; -import { MockViewport, MockCompositionHelper, MockRenderer } from './TestUtils.test'; +import { MockViewport, MockCompositionHelper, MockRenderer, TestTerminal } from './TestUtils.test'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; import { wcwidth } from 'common/CharWidth'; +import { IBufferService } from 'common/services/Services'; +import { Linkifier } from 'browser/Linkifier'; +import { MockLogService } from 'common/TestUtils.test'; +import { IRegisteredLinkMatcher, IMouseZoneManager, IMouseZone } from 'browser/Types'; const INIT_COLS = 80; const INIT_ROWS = 24; -class TestTerminal extends Terminal { - public keyDown(ev: any): boolean { return this._keyDown(ev); } - public keyPress(ev: any): boolean { return this._keyPress(ev); } -} - describe('Terminal', () => { let term: TestTerminal; const termOptions = { @@ -1024,4 +1022,124 @@ describe('Terminal', () => { expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql(''); // empty cell after fullwidth }); }); + + describe('Linkifier unicode handling', () => { + let terminal: TestTerminal; + let linkifier: TestLinkifier; + let mouseZoneManager: TestMouseZoneManager; + + // other than the tests above unicode testing needs the full terminal instance + // to get the special handling of fullwidth, surrogate and combining chars in the input handler + beforeEach(() => { + terminal = new TestTerminal({ cols: 10, rows: 5 }); + linkifier = new TestLinkifier((terminal as any)._bufferService); + mouseZoneManager = new TestMouseZoneManager(); + linkifier.attachToDom({} as any, mouseZoneManager); + }); + + function assertLinkifiesInTerminal(rowText: string, linkMatcherRegex: RegExp, links: {x1: number, y1: number, x2: number, y2: number}[], done: MochaDone): void { + terminal.writeSync(rowText); + linkifier.registerLinkMatcher(linkMatcherRegex, () => {}); + linkifier.linkifyRows(); + // Allow linkify to happen + setTimeout(() => { + assert.equal(mouseZoneManager.zones.length, links.length); + links.forEach((l, i) => { + assert.equal(mouseZoneManager.zones[i].x1, l.x1 + 1); + assert.equal(mouseZoneManager.zones[i].x2, l.x2 + 1); + assert.equal(mouseZoneManager.zones[i].y1, l.y1 + 1); + assert.equal(mouseZoneManager.zones[i].y2, l.y2 + 1); + }); + done(); + }, 0); + } + + describe('unicode before the match', () => { + it('combining - match within one line', function(done: () => void): void { + assertLinkifiesInTerminal('e\u0301e\u0301e\u0301 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); + }); + it('combining - match over two lines', function(done: () => void): void { + assertLinkifiesInTerminal('e\u0301e\u0301e\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); + }); + it('surrogate - match within one line', function(done: () => void): void { + assertLinkifiesInTerminal('𝄞𝄞𝄞 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); + }); + it('surrogate - match over two lines', function(done: () => void): void { + assertLinkifiesInTerminal('𝄞𝄞𝄞 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); + }); + it('combining surrogate - match within one line', function(done: () => void): void { + assertLinkifiesInTerminal('𓂀\u0301𓂀\u0301𓂀\u0301 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); + }); + it('combining surrogate - match over two lines', function(done: () => void): void { + assertLinkifiesInTerminal('𓂀\u0301𓂀\u0301𓂀\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); + }); + it('fullwidth - match within one line', function(done: () => void): void { + assertLinkifiesInTerminal('12 foo', /foo/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); + }); + it('fullwidth - match over two lines', function(done: () => void): void { + assertLinkifiesInTerminal('12 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); + }); + it('combining fullwidth - match within one line', function(done: () => void): void { + assertLinkifiesInTerminal('¥\u0301¥\u0301 foo', /foo/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); + }); + it('combining fullwidth - match over two lines', function(done: () => void): void { + assertLinkifiesInTerminal('¥\u0301¥\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); + }); + }); + describe('unicode within the match', () => { + it('combining - match within one line', function(done: () => void): void { + assertLinkifiesInTerminal('test cafe\u0301', /cafe\u0301/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); + }); + it('combining - match over two lines', function(done: () => void): void { + assertLinkifiesInTerminal('testtest cafe\u0301', /cafe\u0301/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); + }); + it('surrogate - match within one line', function(done: () => void): void { + assertLinkifiesInTerminal('test a𝄞b', /a𝄞b/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); + }); + it('surrogate - match over two lines', function(done: () => void): void { + assertLinkifiesInTerminal('testtest a𝄞b', /a𝄞b/, [{x1: 9, x2: 2, y1: 0, y2: 1}], done); + }); + it('combining surrogate - match within one line', function(done: () => void): void { + assertLinkifiesInTerminal('test a𓂀\u0301b', /a𓂀\u0301b/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); + }); + it('combining surrogate - match over two lines', function(done: () => void): void { + assertLinkifiesInTerminal('testtest a𓂀\u0301b', /a𓂀\u0301b/, [{x1: 9, x2: 2, y1: 0, y2: 1}], done); + }); + it('fullwidth - match within one line', function(done: () => void): void { + assertLinkifiesInTerminal('test a1b', /a1b/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); + }); + it('fullwidth - match over two lines', function(done: () => void): void { + assertLinkifiesInTerminal('testtest a1b', /a1b/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); + }); + it('combining fullwidth - match within one line', function(done: () => void): void { + assertLinkifiesInTerminal('test a¥\u0301b', /a¥\u0301b/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); + }); + it('combining fullwidth - match over two lines', function(done: () => void): void { + assertLinkifiesInTerminal('testtest a¥\u0301b', /a¥\u0301b/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); + }); + }); + }); }); + +class TestLinkifier extends Linkifier { + constructor(bufferService: IBufferService) { + super(bufferService, new MockLogService()); + Linkifier._timeBeforeLatency = 0; + } + + public get linkMatchers(): IRegisteredLinkMatcher[] { return this._linkMatchers; } + public linkifyRows(): void { super.linkifyRows(0, this._bufferService.buffer.lines.length - 1); } +} + +class TestMouseZoneManager implements IMouseZoneManager { + dispose(): void { + } + public clears: number = 0; + public zones: IMouseZone[] = []; + add(zone: IMouseZone): void { + this.zones.push(zone); + } + clearAll(): void { + this.clears++; + } +} diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index 5cee2e28..195321a2 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -23,6 +23,8 @@ export class TestTerminal extends Terminal { this.writeBuffer.push(data); this._innerWrite(); } + keyDown(ev: any): boolean { return this._keyDown(ev); } + keyPress(ev: any): boolean { return this._keyPress(ev); } } export class MockTerminal implements ITerminal { diff --git a/src/browser/Linkifier.test.ts b/src/browser/Linkifier.test.ts index f15eebcc..128f786d 100644 --- a/src/browser/Linkifier.test.ts +++ b/src/browser/Linkifier.test.ts @@ -4,10 +4,9 @@ */ import { assert } from 'chai'; -import { IMouseZoneManager, IMouseZone, ILinkMatcher } from 'browser/Types'; +import { IMouseZoneManager, IMouseZone, IRegisteredLinkMatcher } from 'browser/Types'; import { IBufferLine } from 'common/Types'; import { Linkifier } from 'browser/Linkifier'; -// import { TestTerminal } from '../TestUtils.test'; import { BufferLine } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; import { MockLogService, MockBufferService } from 'common/TestUtils.test'; @@ -19,7 +18,7 @@ class TestLinkifier extends Linkifier { Linkifier._timeBeforeLatency = 0; } - public get linkMatchers(): ILinkMatcher[] { return this._linkMatchers; } + public get linkMatchers(): IRegisteredLinkMatcher[] { return this._linkMatchers; } public linkifyRows(): void { super.linkifyRows(0, this._bufferService.buffer.lines.length - 1); } } @@ -241,98 +240,4 @@ describe('Linkifier', () => { }); }); }); - // describe('unicode handling', () => { - // let terminal: TestTerminal; - - // // other than the tests above unicode testing needs the full terminal instance - // // to get the special handling of fullwidth, surrogate and combining chars in the input handler - // beforeEach(() => { - // terminal = new TestTerminal({cols: 10, rows: 5}); - // linkifier = new TestLinkifier(terminal); - // mouseZoneManager = new TestMouseZoneManager(); - // linkifier.attachToDom(undefined as any, mouseZoneManager); - // }); - - // function assertLinkifiesInTerminal(rowText: string, linkMatcherRegex: RegExp, links: {x1: number, y1: number, x2: number, y2: number}[], done: MochaDone): void { - // terminal.writeSync(rowText); - // linkifier.registerLinkMatcher(linkMatcherRegex, () => {}); - // linkifier.linkifyRows(); - // // Allow linkify to happen - // setTimeout(() => { - // assert.equal(mouseZoneManager.zones.length, links.length); - // links.forEach((l, i) => { - // assert.equal(mouseZoneManager.zones[i].x1, l.x1 + 1); - // assert.equal(mouseZoneManager.zones[i].x2, l.x2 + 1); - // assert.equal(mouseZoneManager.zones[i].y1, l.y1 + 1); - // assert.equal(mouseZoneManager.zones[i].y2, l.y2 + 1); - // }); - // done(); - // }, 0); - // } - - // describe('unicode before the match', () => { - // it('combining - match within one line', function(done: () => void): void { - // assertLinkifiesInTerminal('e\u0301e\u0301e\u0301 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); - // }); - // it('combining - match over two lines', function(done: () => void): void { - // assertLinkifiesInTerminal('e\u0301e\u0301e\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); - // }); - // it('surrogate - match within one line', function(done: () => void): void { - // assertLinkifiesInTerminal('𝄞𝄞𝄞 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); - // }); - // it('surrogate - match over two lines', function(done: () => void): void { - // assertLinkifiesInTerminal('𝄞𝄞𝄞 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); - // }); - // it('combining surrogate - match within one line', function(done: () => void): void { - // assertLinkifiesInTerminal('𓂀\u0301𓂀\u0301𓂀\u0301 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); - // }); - // it('combining surrogate - match over two lines', function(done: () => void): void { - // assertLinkifiesInTerminal('𓂀\u0301𓂀\u0301𓂀\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); - // }); - // it('fullwidth - match within one line', function(done: () => void): void { - // assertLinkifiesInTerminal('12 foo', /foo/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); - // }); - // it('fullwidth - match over two lines', function(done: () => void): void { - // assertLinkifiesInTerminal('12 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); - // }); - // it('combining fullwidth - match within one line', function(done: () => void): void { - // assertLinkifiesInTerminal('¥\u0301¥\u0301 foo', /foo/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); - // }); - // it('combining fullwidth - match over two lines', function(done: () => void): void { - // assertLinkifiesInTerminal('¥\u0301¥\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); - // }); - // }); - // describe('unicode within the match', () => { - // it('combining - match within one line', function(done: () => void): void { - // assertLinkifiesInTerminal('test cafe\u0301', /cafe\u0301/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); - // }); - // it('combining - match over two lines', function(done: () => void): void { - // assertLinkifiesInTerminal('testtest cafe\u0301', /cafe\u0301/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); - // }); - // it('surrogate - match within one line', function(done: () => void): void { - // assertLinkifiesInTerminal('test a𝄞b', /a𝄞b/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); - // }); - // it('surrogate - match over two lines', function(done: () => void): void { - // assertLinkifiesInTerminal('testtest a𝄞b', /a𝄞b/, [{x1: 9, x2: 2, y1: 0, y2: 1}], done); - // }); - // it('combining surrogate - match within one line', function(done: () => void): void { - // assertLinkifiesInTerminal('test a𓂀\u0301b', /a𓂀\u0301b/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); - // }); - // it('combining surrogate - match over two lines', function(done: () => void): void { - // assertLinkifiesInTerminal('testtest a𓂀\u0301b', /a𓂀\u0301b/, [{x1: 9, x2: 2, y1: 0, y2: 1}], done); - // }); - // it('fullwidth - match within one line', function(done: () => void): void { - // assertLinkifiesInTerminal('test a1b', /a1b/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); - // }); - // it('fullwidth - match over two lines', function(done: () => void): void { - // assertLinkifiesInTerminal('testtest a1b', /a1b/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); - // }); - // it('combining fullwidth - match within one line', function(done: () => void): void { - // assertLinkifiesInTerminal('test a¥\u0301b', /a¥\u0301b/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); - // }); - // it('combining fullwidth - match over two lines', function(done: () => void): void { - // assertLinkifiesInTerminal('testtest a¥\u0301b', /a¥\u0301b/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); - // }); - // }); - // }); }); From 4dfe3a607964bde809f270ca5d885a393cba369d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 17:43:23 -0700 Subject: [PATCH 51/69] Move MouseZoneManager to browser --- src/Terminal.ts | 2 +- src/{ => browser}/MouseZoneManager.ts | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) rename src/{ => browser}/MouseZoneManager.ts (93%) diff --git a/src/Terminal.ts b/src/Terminal.ts index d9a6d77b..ef231b5d 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -35,7 +35,7 @@ import * as Browser from 'common/Platform'; import { addDisposableDomListener } from 'browser/Lifecycle'; import * as Strings from './browser/LocalizableStrings'; import { SoundService } from 'browser/services/SoundService'; -import { MouseZoneManager } from './MouseZoneManager'; +import { MouseZoneManager } from './browser/MouseZoneManager'; import { AccessibilityManager } from './AccessibilityManager'; import { ITheme, IMarker, IDisposable, ISelectionPosition } from 'xterm'; import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; diff --git a/src/MouseZoneManager.ts b/src/browser/MouseZoneManager.ts similarity index 93% rename from src/MouseZoneManager.ts rename to src/browser/MouseZoneManager.ts index 42eb6545..589428f8 100644 --- a/src/MouseZoneManager.ts +++ b/src/browser/MouseZoneManager.ts @@ -27,10 +27,10 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { private _mouseLeaveListener: (e: MouseEvent) => any; private _clickListener: (e: MouseEvent) => any; - private _tooltipTimeout: number = null; - private _currentZone: IMouseZone = null; - private _lastHoverCoords: [number, number] = [null, null]; - private _initialSelectionLength: number; + private _tooltipTimeout: number | undefined; + private _currentZone: IMouseZone | undefined; + private _lastHoverCoords: [number | undefined, number | undefined] = [undefined, undefined]; + private _initialSelectionLength: number = 0; constructor( private readonly _element: HTMLElement, @@ -68,7 +68,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { } // Clear all if start/end weren't set - if (!end) { + if (!start || !end) { start = 0; end = this._bufferService.rows - 1; } @@ -81,7 +81,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { (zone.y1 < start && zone.y2 > end + 1)) { if (this._currentZone && this._currentZone === zone) { this._currentZone.leaveCallback(); - this._currentZone = null; + this._currentZone = undefined; } this._zones.splice(i--, 1); } @@ -133,7 +133,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { // is being hovered if (this._currentZone) { this._currentZone.leaveCallback(); - this._currentZone = null; + this._currentZone = undefined; if (this._tooltipTimeout) { clearTimeout(this._tooltipTimeout); } @@ -155,7 +155,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { } private _onTooltip(e: MouseEvent): void { - this._tooltipTimeout = null; + this._tooltipTimeout = undefined; const zone = this._findZoneEventAt(e); if (zone && zone.tooltipCallback) { zone.tooltipCallback(e); @@ -188,7 +188,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { // leaves the terminal element if (this._currentZone) { this._currentZone.leaveCallback(); - this._currentZone = null; + this._currentZone = undefined; if (this._tooltipTimeout) { clearTimeout(this._tooltipTimeout); } @@ -213,10 +213,10 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { return selectionText ? selectionText.length : 0; } - private _findZoneEventAt(e: MouseEvent): IMouseZone { + private _findZoneEventAt(e: MouseEvent): IMouseZone | undefined { const coords = this._mouseService.getCoords(e, this._screenElement, this._bufferService.cols, this._bufferService.rows); if (!coords) { - return null; + return undefined; } const x = coords[0]; const y = coords[1]; @@ -236,6 +236,6 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { } } } - return null; + return undefined; } } From 8aa296933d2415601a6e95d53964ea6b71211f77 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 17:48:57 -0700 Subject: [PATCH 52/69] Break Viewport's dependency on ITerminal --- src/Terminal.ts | 13 +++++++++--- src/TestUtils.test.ts | 4 ++-- src/Types.d.ts | 12 +---------- src/Viewport.ts | 46 +++++++++++++++++++----------------------- src/browser/Types.d.ts | 10 +++++++++ 5 files changed, 44 insertions(+), 41 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index ef231b5d..75537ab4 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -21,7 +21,7 @@ * http://linux.die.net/man/7/urxvt */ -import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, CustomKeyEventHandler } from './Types'; +import { IInputHandlingTerminal, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, CustomKeyEventHandler } from './Types'; import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; import { CompositionHelper } from 'browser/input/CompositionHelper'; import { Viewport } from './Viewport'; @@ -59,7 +59,7 @@ import { MouseService } from 'browser/services/MouseService'; import { IParams } from 'common/parser/Types'; import { CoreService } from 'common/services/CoreService'; import { LogService } from 'common/services/LogService'; -import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions } from 'browser/Types'; +import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport } from 'browser/Types'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -599,7 +599,14 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._soundService = new SoundService(this.optionsService); this._mouseService = new MouseService(this._renderService, this._charSizeService); - this.viewport = new Viewport(this, this._viewportElement, this._viewportScrollArea, this._renderService.dimensions, this._charSizeService); + this.viewport = new Viewport( + (amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent), + this._viewportElement, + this._viewportScrollArea, + this._bufferService, + this._charSizeService, + this._renderService + ); this.viewport.onThemeChange(this._colorManager.colors); this.register(this.viewport); diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index 195321a2..0ee948d5 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -4,7 +4,7 @@ */ import { IRenderer, IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types'; -import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminal, IBrowser, ITerminalOptions } from './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'; @@ -12,7 +12,7 @@ import * as Browser from 'common/Platform'; import { IDisposable, IMarker, IEvent, ISelectionPosition } from 'xterm'; import { Terminal } from './Terminal'; import { AttributeData } from 'common/buffer/AttributeData'; -import { IColorManager, IColorSet, ILinkMatcherOptions, ILinkifier } from 'browser/Types'; +import { IColorManager, IColorSet, ILinkMatcherOptions, ILinkifier, IViewport } from 'browser/Types'; import { IOptionsService } from 'common/services/Services'; import { EventEmitter } from 'common/EventEmitter'; import { IParams } from 'common/parser/Types'; diff --git a/src/Types.d.ts b/src/Types.d.ts index 1a647927..124aa428 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -6,7 +6,7 @@ import { ITerminalOptions as IPublicTerminalOptions, IDisposable, IMarker, ISelectionPosition } from 'xterm'; import { ICharset, IAttributeData, CharData } from 'common/Types'; import { IEvent, IEventEmitter } from 'common/EventEmitter'; -import { IColorSet, ILinkifier, ILinkMatcherOptions } from 'browser/Types'; +import { IColorSet, ILinkifier, ILinkMatcherOptions, IViewport } from 'browser/Types'; import { IOptionsService } from 'common/services/Services'; import { IBuffer, IBufferSet } from 'common/buffer/Types'; import { IParams } from 'common/parser/Types'; @@ -68,16 +68,6 @@ export interface IInputHandlingTerminal { handleTitle(title: string): void; } -export interface IViewport extends IDisposable { - scrollBarWidth: number; - syncScrollArea(): void; - getLinesScrolled(ev: WheelEvent): number; - onWheel(ev: WheelEvent): void; - onTouchStart(ev: TouchEvent): void; - onTouchMove(ev: TouchEvent): void; - onThemeChange(colors: IColorSet): void; -} - export interface ICompositionHelper { compositionstart(): void; compositionupdate(ev: CompositionEvent): void; diff --git a/src/Viewport.ts b/src/Viewport.ts index cd5a282c..b2fd4e68 100644 --- a/src/Viewport.ts +++ b/src/Viewport.ts @@ -3,12 +3,11 @@ * @license MIT */ -import { ITerminal, IViewport } from './Types'; import { Disposable } from 'common/Lifecycle'; import { addDisposableDomListener } from 'browser/Lifecycle'; -import { IColorSet } from 'browser/Types'; -import { IRenderDimensions } from 'browser/renderer/Types'; -import { ICharSizeService } from 'browser/services/Services'; +import { IColorSet, IViewport } from 'browser/Types'; +import { ICharSizeService, IRenderService } from 'browser/services/Services'; +import { IBufferService } from 'common/services/Services'; const FALLBACK_SCROLL_BAR_WIDTH = 15; @@ -34,11 +33,12 @@ export class Viewport extends Disposable implements IViewport { private _ignoreNextScrollEvent: boolean = false; constructor( - private _terminal: ITerminal, - private _viewportElement: HTMLElement, - private _scrollArea: HTMLElement, - private _dimensions: IRenderDimensions, - private _charSizeService: ICharSizeService + private readonly _scrollLines: (amount: number, suppressEvent: boolean) => void, + private readonly _viewportElement: HTMLElement, + private readonly _scrollArea: HTMLElement, + private readonly _bufferService: IBufferService, + private readonly _charSizeService: ICharSizeService, + private readonly _renderService: IRenderService ) { super(); @@ -52,10 +52,6 @@ export class Viewport extends Disposable implements IViewport { setTimeout(() => this.syncScrollArea(), 0); } - public onDimensionsChance(dimensions: IRenderDimensions): void { - this._dimensions = dimensions; - } - public onThemeChange(colors: IColorSet): void { this._viewportElement.style.backgroundColor = colors.background.css; } @@ -72,9 +68,9 @@ export class Viewport extends Disposable implements IViewport { private _innerRefresh(): void { if (this._charSizeService.height > 0) { - this._currentRowHeight = this._dimensions.scaledCellHeight / window.devicePixelRatio; + this._currentRowHeight = this._renderService.dimensions.scaledCellHeight / window.devicePixelRatio; this._lastRecordedViewportHeight = this._viewportElement.offsetHeight; - const newBufferHeight = Math.round(this._currentRowHeight * this._lastRecordedBufferLength) + (this._lastRecordedViewportHeight - this._dimensions.canvasHeight); + const newBufferHeight = Math.round(this._currentRowHeight * this._lastRecordedBufferLength) + (this._lastRecordedViewportHeight - this._renderService.dimensions.canvasHeight); if (this._lastRecordedBufferHeight !== newBufferHeight) { this._lastRecordedBufferHeight = newBufferHeight; this._scrollArea.style.height = this._lastRecordedBufferHeight + 'px'; @@ -82,7 +78,7 @@ export class Viewport extends Disposable implements IViewport { } // Sync scrollTop - const scrollTop = this._terminal.buffer.ydisp * this._currentRowHeight; + const scrollTop = this._bufferService.buffer.ydisp * this._currentRowHeight; if (this._viewportElement.scrollTop !== scrollTop) { // Ignore the next scroll event which will be triggered by setting the scrollTop as we do not // want this event to scroll the terminal @@ -98,20 +94,20 @@ export class Viewport extends Disposable implements IViewport { */ public syncScrollArea(): void { // If buffer height changed - if (this._lastRecordedBufferLength !== this._terminal.buffer.lines.length) { - this._lastRecordedBufferLength = this._terminal.buffer.lines.length; + if (this._lastRecordedBufferLength !== this._bufferService.buffer.lines.length) { + this._lastRecordedBufferLength = this._bufferService.buffer.lines.length; this._refresh(); return; } // If viewport height changed - if (this._lastRecordedViewportHeight !== this._dimensions.canvasHeight) { + if (this._lastRecordedViewportHeight !== this._renderService.dimensions.canvasHeight) { this._refresh(); return; } // If the buffer position doesn't match last scroll top - const newScrollTop = this._terminal.buffer.ydisp * this._currentRowHeight; + const newScrollTop = this._bufferService.buffer.ydisp * this._currentRowHeight; if (this._lastScrollTop !== newScrollTop) { this._refresh(); return; @@ -124,7 +120,7 @@ export class Viewport extends Disposable implements IViewport { } // If row height changed - if (this._dimensions.scaledCellHeight / window.devicePixelRatio !== this._currentRowHeight) { + if (this._renderService.dimensions.scaledCellHeight / window.devicePixelRatio !== this._currentRowHeight) { this._refresh(); return; } @@ -152,8 +148,8 @@ export class Viewport extends Disposable implements IViewport { } const newRow = Math.round(this._lastScrollTop / this._currentRowHeight); - const diff = newRow - this._terminal.buffer.ydisp; - this._terminal.scrollLines(diff, true); + const diff = newRow - this._bufferService.buffer.ydisp; + this._scrollLines(diff, true); } /** @@ -183,7 +179,7 @@ export class Viewport extends Disposable implements IViewport { if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) { amount *= this._currentRowHeight; } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { - amount *= this._currentRowHeight * this._terminal.rows; + amount *= this._currentRowHeight * this._bufferService.rows; } return amount; } @@ -207,7 +203,7 @@ export class Viewport extends Disposable implements IViewport { amount = Math.floor(Math.abs(this._wheelPartialScroll)) * (this._wheelPartialScroll > 0 ? 1 : -1); this._wheelPartialScroll %= 1; } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { - amount *= this._terminal.rows; + amount *= this._bufferService.rows; } return amount; } diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts index 985244b3..c9167316 100644 --- a/src/browser/Types.d.ts +++ b/src/browser/Types.d.ts @@ -24,6 +24,16 @@ export interface IColorSet { ansi: IColor[]; } +export interface IViewport extends IDisposable { + scrollBarWidth: number; + syncScrollArea(): void; + getLinesScrolled(ev: WheelEvent): number; + onWheel(ev: WheelEvent): void; + onTouchStart(ev: TouchEvent): void; + onTouchMove(ev: TouchEvent): void; + onThemeChange(colors: IColorSet): void; +} + export type LinkMatcherHandler = (event: MouseEvent, uri: string) => void; export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void; From 9aaaf6d8dbe9269ac690a8b30b5ddc6c2e1a23f0 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 17:50:43 -0700 Subject: [PATCH 53/69] Move Viewport to browser --- src/Terminal.ts | 6 +++--- src/{ => browser}/Viewport.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/{ => browser}/Viewport.ts (99%) diff --git a/src/Terminal.ts b/src/Terminal.ts index 75537ab4..e1d0a4d7 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -24,7 +24,7 @@ import { IInputHandlingTerminal, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, CustomKeyEventHandler } from './Types'; import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; import { CompositionHelper } from 'browser/input/CompositionHelper'; -import { Viewport } from './Viewport'; +import { Viewport } from 'browser/Viewport'; import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from 'browser/Clipboard'; import { C0 } from 'common/data/EscapeSequences'; import { InputHandler } from './InputHandler'; @@ -33,9 +33,9 @@ import { Linkifier } from 'browser/Linkifier'; import { SelectionService } from './browser/services/SelectionService'; import * as Browser from 'common/Platform'; import { addDisposableDomListener } from 'browser/Lifecycle'; -import * as Strings from './browser/LocalizableStrings'; +import * as Strings from 'browser/LocalizableStrings'; import { SoundService } from 'browser/services/SoundService'; -import { MouseZoneManager } from './browser/MouseZoneManager'; +import { MouseZoneManager } from 'browser/MouseZoneManager'; import { AccessibilityManager } from './AccessibilityManager'; import { ITheme, IMarker, IDisposable, ISelectionPosition } from 'xterm'; import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; diff --git a/src/Viewport.ts b/src/browser/Viewport.ts similarity index 99% rename from src/Viewport.ts rename to src/browser/Viewport.ts index b2fd4e68..270d4c67 100644 --- a/src/Viewport.ts +++ b/src/browser/Viewport.ts @@ -21,7 +21,7 @@ export class Viewport extends Disposable implements IViewport { private _lastRecordedBufferLength: number = 0; private _lastRecordedViewportHeight: number = 0; private _lastRecordedBufferHeight: number = 0; - private _lastTouchY: number; + private _lastTouchY: number = 0; private _lastScrollTop: number = 0; // Stores a partial line amount when scrolling, this is used to keep track of how much of a line From c078f3db487c33be1828a4d64dc351e946423d99 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 18:25:22 -0700 Subject: [PATCH 54/69] Move range updating to DirtyRowService --- src/InputHandler.test.ts | 20 +++--- src/InputHandler.ts | 86 ++++++++++---------------- src/Terminal.ts | 59 +++--------------- src/Types.d.ts | 1 - src/common/TestUtils.test.ts | 11 +++- src/common/Types.d.ts | 5 ++ src/common/services/DirtyRowService.ts | 51 +++++++++++++++ src/common/services/Services.d.ts | 10 +++ src/renderer/dom/DomRenderer.ts | 3 - 9 files changed, 127 insertions(+), 119 deletions(-) create mode 100644 src/common/services/DirtyRowService.ts diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 53d0bc49..558cfe7e 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -13,7 +13,7 @@ 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, MockOptionsService, MockLogService } from 'common/TestUtils.test'; +import { MockCoreService, MockBufferService, MockDirtyRowService, MockOptionsService, MockLogService } from 'common/TestUtils.test'; import { IBufferService } from 'common/services/Services'; function getCursor(term: TestTerminal): number[] { @@ -31,7 +31,7 @@ describe('InputHandler', () => { bufferService.buffer.x = 1; bufferService.buffer.y = 2; bufferService.buffer.ybase = 0; - const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // Save cursor position inputHandler.saveCursor(); assert.equal(bufferService.buffer.x, 1); @@ -50,7 +50,7 @@ describe('InputHandler', () => { describe('setCursorStyle', () => { it('should call Terminal.setOption with correct params', () => { const terminal = new MockInputHandlingTerminal(); - const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); const collect = ' '; inputHandler.setCursorStyle(Params.fromArray([0]), collect); @@ -93,7 +93,7 @@ describe('InputHandler', () => { const terminal = new MockInputHandlingTerminal(); const collect = '?'; terminal.bracketedPasteMode = false; - const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // Set bracketed paste mode inputHandler.setMode(Params.fromArray([2004]), collect); assert.equal(terminal.bracketedPasteMode, true); @@ -112,7 +112,7 @@ describe('InputHandler', () => { it('insertChars', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -150,7 +150,7 @@ describe('InputHandler', () => { it('deleteChars', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -191,7 +191,7 @@ describe('InputHandler', () => { it('eraseInLine', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // fill 6 lines to test 3 different states inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -220,7 +220,7 @@ describe('InputHandler', () => { 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 MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // fill display with a's for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -355,7 +355,7 @@ describe('InputHandler', () => { 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 MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); const container = new Uint32Array(10); container[0] = 0x200B; inputHandler.print(container, 0, 1); @@ -370,7 +370,7 @@ describe('InputHandler', () => { beforeEach(() => { term = new Terminal(); bufferService = new MockBufferService(80, 30); - handler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); + handler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); }); it('should handle DECSET/DECRST 47 (alt screen buffer)', () => { handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST'); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 0f4da041..cc767a16 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -19,7 +19,7 @@ import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content import { CellData } from 'common/buffer/CellData'; import { AttributeData } from 'common/buffer/AttributeData'; import { IAttributeData, IDisposable } from 'common/Types'; -import { ICoreService, IBufferService, IOptionsService, ILogService } from 'common/services/Services'; +import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService } from 'common/services/Services'; import { ISelectionService } from 'browser/services/Services'; /** @@ -128,12 +128,13 @@ export class InputHandler extends Disposable implements IInputHandler { public get onScroll(): IEvent { return this._onScroll.event; } constructor( - protected _terminal: IInputHandlingTerminal, - private _bufferService: IBufferService, - private _coreService: ICoreService, - private _logService: ILogService, - private _optionsService: IOptionsService, - private _parser: IEscapeSequenceParser = new EscapeSequenceParser()) + protected _terminal: IInputHandlingTerminal, + private readonly _bufferService: IBufferService, + private readonly _coreService: ICoreService, + private readonly _dirtyRowService: IDirtyRowService, + private readonly _logService: ILogService, + private readonly _optionsService: IOptionsService, + private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser()) { super(); @@ -306,7 +307,6 @@ export class InputHandler extends Disposable implements IInputHandler { public dispose(): void { super.dispose(); - this._terminal = null; } // TODO: When InputHandler moves into common, browser dependencies need to move out @@ -315,11 +315,6 @@ export class InputHandler extends Disposable implements IInputHandler { } public parse(data: string): void { - // Ensure the terminal is not disposed - if (!this._terminal) { - return; - } - let buffer = this._bufferService.buffer; const cursorStartX = buffer.x; const cursorStartY = buffer.y; @@ -338,11 +333,6 @@ export class InputHandler extends Disposable implements IInputHandler { } public parseUtf8(data: Uint8Array): void { - // Ensure the terminal is not disposed - if (!this._terminal) { - return; - } - let buffer = this._bufferService.buffer; const cursorStartX = buffer.x; const cursorStartY = buffer.y; @@ -365,14 +355,14 @@ export class InputHandler extends Disposable implements IInputHandler { let chWidth: number; const buffer = this._bufferService.buffer; const charset = this._terminal.charset; - const screenReaderMode = this._terminal.options.screenReaderMode; + const screenReaderMode = this._optionsService.options.screenReaderMode; const cols = this._bufferService.cols; const wraparoundMode = this._terminal.wraparoundMode; const insertMode = this._terminal.insertMode; const curAttr = this._terminal.curAttrData; let bufferRow = buffer.lines.get(buffer.y + buffer.ybase); - this._terminal.updateRange(buffer.y); + this._dirtyRowService.markDirty(buffer.y); for (let pos = start; pos < end; ++pos) { code = data[pos]; @@ -481,7 +471,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._parser.precedingCodepoint = this._workCell.content; } } - this._terminal.updateRange(buffer.y); + this._dirtyRowService.markDirty(buffer.y); } /** @@ -514,7 +504,7 @@ export class InputHandler extends Disposable implements IInputHandler { // make buffer local for faster access const buffer = this._bufferService.buffer; - if (this._terminal.options.convertEol) { + if (this._optionsService.options.convertEol) { buffer.x = 0; } buffer.y++; @@ -559,7 +549,7 @@ export class InputHandler extends Disposable implements IInputHandler { } const originalX = this._bufferService.buffer.x; this._bufferService.buffer.x = this._bufferService.buffer.nextStop(); - if (this._terminal.options.screenReaderMode) { + if (this._optionsService.options.screenReaderMode) { this._terminal.onA11yTabEmitter.fire(this._bufferService.buffer.x - originalX); } } @@ -830,16 +820,16 @@ export class InputHandler extends Disposable implements IInputHandler { switch (params.params[0]) { case 0: j = this._bufferService.buffer.y; - this._terminal.updateRange(j); + this._dirtyRowService.markDirty(j); this._eraseInBufferLine(j++, this._bufferService.buffer.x, this._bufferService.cols, this._bufferService.buffer.x === 0); for (; j < this._bufferService.rows; j++) { this._resetBufferLine(j); } - this._terminal.updateRange(j); + this._dirtyRowService.markDirty(j); break; case 1: j = this._bufferService.buffer.y; - this._terminal.updateRange(j); + this._dirtyRowService.markDirty(j); // Deleted front part of line and everything before. This line will no longer be wrapped. this._eraseInBufferLine(j, 0, this._bufferService.buffer.x + 1, true); if (this._bufferService.buffer.x + 1 >= this._bufferService.cols) { @@ -849,15 +839,15 @@ export class InputHandler extends Disposable implements IInputHandler { while (j--) { this._resetBufferLine(j); } - this._terminal.updateRange(0); + this._dirtyRowService.markDirty(0); break; case 2: j = this._bufferService.rows; - this._terminal.updateRange(j - 1); + this._dirtyRowService.markDirty(j - 1); while (j--) { this._resetBufferLine(j); } - this._terminal.updateRange(0); + this._dirtyRowService.markDirty(0); break; case 3: // Clear scrollback (everything not in viewport) @@ -897,7 +887,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.cols); break; } - this._terminal.updateRange(this._bufferService.buffer.y); + this._dirtyRowService.markDirty(this._bufferService.buffer.y); } /** @@ -926,9 +916,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.lines.splice(row, 0, buffer.getBlankLine(this._terminal.eraseAttrData())); } - // this.maxRange(); - this._terminal.updateRange(buffer.y); - this._terminal.updateRange(buffer.scrollBottom); + this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom); buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only? } @@ -959,9 +947,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.lines.splice(j, 0, buffer.getBlankLine(this._terminal.eraseAttrData())); } - // this.maxRange(); - this._terminal.updateRange(buffer.y); - this._terminal.updateRange(buffer.scrollBottom); + this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom); buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only? } @@ -978,7 +964,7 @@ export class InputHandler extends Disposable implements IInputHandler { params.params[0] || 1, this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()) ); - this._terminal.updateRange(this._bufferService.buffer.y); + this._dirtyRowService.markDirty(this._bufferService.buffer.y); } } @@ -995,7 +981,7 @@ export class InputHandler extends Disposable implements IInputHandler { params.params[0] || 1, this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()) ); - this._terminal.updateRange(this._bufferService.buffer.y); + this._dirtyRowService.markDirty(this._bufferService.buffer.y); } } @@ -1012,9 +998,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1); buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA)); } - // this.maxRange(); - this._terminal.updateRange(buffer.scrollTop); - this._terminal.updateRange(buffer.scrollBottom); + this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom); } /** @@ -1031,9 +1015,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1); buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA)); } - // this.maxRange(); - this._terminal.updateRange(buffer.scrollTop); - this._terminal.updateRange(buffer.scrollBottom); + this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom); } } @@ -1050,7 +1032,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._bufferService.buffer.x + (params.params[0] || 1), this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()) ); - this._terminal.updateRange(this._bufferService.buffer.y); + this._dirtyRowService.markDirty(this._bufferService.buffer.y); } } @@ -1893,19 +1875,19 @@ export class InputHandler extends Disposable implements IInputHandler { switch (param) { case 1: case 2: - this._terminal.options.cursorStyle = 'block'; + this._optionsService.options.cursorStyle = 'block'; break; case 3: case 4: - this._terminal.options.cursorStyle = 'underline'; + this._optionsService.options.cursorStyle = 'underline'; break; case 5: case 6: - this._terminal.options.cursorStyle = 'bar'; + this._optionsService.options.cursorStyle = 'bar'; break; } const isBlinking = param % 2 === 1; - this._terminal.options.cursorBlink = isBlinking; + this._optionsService.options.cursorBlink = isBlinking; } } @@ -2097,8 +2079,7 @@ export class InputHandler extends Disposable implements IInputHandler { 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._terminal.eraseAttrData())); - this._terminal.updateRange(buffer.scrollTop); - this._terminal.updateRange(buffer.scrollBottom); + this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom); } else { buffer.y--; this._restrictCursor(); // quickfix to not run out of bounds @@ -2152,8 +2133,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.lines.get(row).fill(cell); buffer.lines.get(row).isWrapped = false; } - this._terminal.updateRange(0); - this._terminal.updateRange(this._bufferService.rows); + this._dirtyRowService.markAllDirty(); this._setCursor(0, 0); } } diff --git a/src/Terminal.ts b/src/Terminal.ts index e1d0a4d7..e23ea778 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -47,7 +47,7 @@ import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { applyWindowsMode } from './WindowsMode'; import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; -import { IOptionsService, IBufferService, ICoreService, ILogService } from 'common/services/Services'; +import { IOptionsService, IBufferService, ICoreService, ILogService, IDirtyRowService } from 'common/services/Services'; import { OptionsService } from 'common/services/OptionsService'; import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService } from 'browser/services/Services'; import { CharSizeService } from 'browser/services/CharSizeService'; @@ -60,6 +60,7 @@ import { IParams } from 'common/parser/Types'; import { CoreService } from 'common/services/CoreService'; import { LogService } from 'common/services/LogService'; import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport } from 'browser/Types'; +import { DirtyRowService } from 'common/services/DirtyRowService'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -111,6 +112,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp // common services private _bufferService: IBufferService; private _coreService: ICoreService; + private _dirtyRowService: IDirtyRowService; private _logService: ILogService; public optionsService: IOptionsService; @@ -148,8 +150,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp public urxvtMouse: boolean; // misc - private _refreshStart: number; - private _refreshEnd: number; public savedCols: number; public curAttrData: IAttributeData; @@ -243,6 +243,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._bufferService = new BufferService(this.optionsService); this._coreService = new CoreService(() => this.scrollToBottom(), this._bufferService, this.optionsService); this._coreService.onData(e => this._onData.fire(e)); + this._dirtyRowService = new DirtyRowService(this._bufferService); this._logService = new LogService(this.optionsService); this._setupOptionsListeners(); @@ -300,7 +301,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._userScrolling = false; // Register input handler and refire/handle events - this._inputHandler = new InputHandler(this, this._bufferService, this._coreService, this._logService, this.optionsService); + this._inputHandler = new InputHandler(this, this._bufferService, this._coreService, this._dirtyRowService, this._logService, this.optionsService); this._inputHandler.onCursorMove(() => this._onCursorMove.fire()); this._inputHandler.onLineFeed(() => this._onLineFeed.fire()); this.register(this._inputHandler); @@ -1136,8 +1137,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp } // Flag rows that need updating - this.updateRange(this.buffer.scrollTop); - this.updateRange(this.buffer.scrollBottom); + this._dirtyRowService.markRangeDirty(this.buffer.scrollTop, this.buffer.scrollBottom); this._onScroll.fire(this.buffer.ydisp); } @@ -1258,19 +1258,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._xoffSentToCatchUp = false; } - this._refreshStart = this.buffer.y; - this._refreshEnd = this.buffer.y; - - // HACK: Set the parser state based on it's state at the time of return. - // This works around the bug #662 which saw the parser state reset in the - // middle of parsing escape sequence in two chunks. For some reason the - // state of the parser resets to 0 after exiting parser.parse. This change - // just sets the state back based on the correct return statement. - this._inputHandler.parseUtf8(data); - this.updateRange(this.buffer.y); - this.refresh(this._refreshStart, this._refreshEnd); + this.refresh(this._dirtyRowService.start, this._dirtyRowService.end); if (Date.now() - startTime >= WRITE_TIMEOUT_MS) { break; @@ -1345,19 +1335,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._xoffSentToCatchUp = false; } - this._refreshStart = this.buffer.y; - this._refreshEnd = this.buffer.y; - - // HACK: Set the parser state based on it's state at the time of return. - // This works around the bug #662 which saw the parser state reset in the - // middle of parsing escape sequence in two chunks. For some reason the - // state of the parser resets to 0 after exiting parser.parse. This change - // just sets the state back based on the correct return statement. - this._inputHandler.parse(data); - this.updateRange(this.buffer.y); - this.refresh(this._refreshStart, this._refreshEnd); + this.refresh(this._dirtyRowService.start, this._dirtyRowService.end); if (Date.now() - startTime >= WRITE_TIMEOUT_MS) { break; @@ -1717,29 +1697,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._onResize.fire({ cols: x, rows: y }); } - /** - * Updates the range of rows to refresh - * @param y The number of rows to refresh next. - */ - public updateRange(y: number): void { - if (y < this._refreshStart) this._refreshStart = y; - if (y > this._refreshEnd) this._refreshEnd = y; - // if (y > this.refreshEnd) { - // this.refreshEnd = y; - // if (y > this.rows - 1) { - // this.refreshEnd = this.rows - 1; - // } - // } - } - - /** - * Set the range of refreshing to the maximum value - */ - public maxRange(): void { - this._refreshStart = 0; - this._refreshEnd = this.rows - 1; - } - /** * Clear the entire buffer, making the prompt line the new first line. */ diff --git a/src/Types.d.ts b/src/Types.d.ts index 124aa428..f96ba07d 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -55,7 +55,6 @@ export interface IInputHandlingTerminal { bell(): void; focus(): void; - updateRange(y: number): void; scroll(isWrapped?: boolean): void; setgLevel(g: number): void; eraseAttrData(): IAttributeData; diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index c786a06d..67584a36 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IPartialTerminalOptions } from 'common/services/Services'; +import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IPartialTerminalOptions, IDirtyRowService } from 'common/services/Services'; import { IEvent, EventEmitter } from 'common/EventEmitter'; import { clone } from 'common/Clone'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; @@ -36,6 +36,15 @@ export class MockCoreService implements ICoreService { triggerDataEvent(data: string, wasUserInput?: boolean): void {} } +export class MockDirtyRowService implements IDirtyRowService { + start: number = 0; + end: number = 0; + clearRange(): void {} + markDirty(y: number): void {} + markRangeDirty(y1: number, y2: number): void {} + markAllDirty(): void {} +} + export class MockLogService implements ILogService { debug(message: any, ...optionalParams: any[]): void {} info(message: any, ...optionalParams: any[]): void {} diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index 320aaeb8..b25b50a5 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -153,3 +153,8 @@ export interface IMarker extends IDisposable { export interface IDecPrivateModes { applicationCursorKeys: boolean; } + +export interface IRowRange { + start: number; + end: number; +} diff --git a/src/common/services/DirtyRowService.ts b/src/common/services/DirtyRowService.ts new file mode 100644 index 00000000..58f40dca --- /dev/null +++ b/src/common/services/DirtyRowService.ts @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IBufferService, IDirtyRowService } from 'common/services/Services'; + +export class DirtyRowService implements IDirtyRowService { + private _start!: number; + private _end!: number; + + public get start(): number { return this._start; } + public get end(): number { return this._end; } + + constructor( + private readonly _bufferService: IBufferService + ) { + this.clearRange(); + } + + public clearRange(): void { + this._start = this._bufferService.buffer.y; + this._end = this._bufferService.buffer.y; + } + + public markDirty(y: number): void { + if (y < this._start) { + this._start = y; + } else if (y > this._end) { + this._end = y; + } + } + + public markRangeDirty(y1: number, y2: number): void { + if (y1 > y2) { + const temp = y1; + y1 = y2; + y2 = temp; + } + if (y1 < this._start) { + this._start = y1; + } + if (y2 > this._end) { + this._end = y2; + } + } + + public markAllDirty(): void { + this.markRangeDirty(0, this._bufferService.rows - 1); + } +} diff --git a/src/common/services/Services.d.ts b/src/common/services/Services.d.ts index 9a98ca92..427728f1 100644 --- a/src/common/services/Services.d.ts +++ b/src/common/services/Services.d.ts @@ -38,6 +38,16 @@ export interface ICoreService { triggerDataEvent(data: string, wasUserInput?: boolean): void; } +export interface IDirtyRowService { + readonly start: number; + readonly end: number; + + clearRange(): void; + markDirty(y: number): void; + markRangeDirty(y1: number, y2: number): void; + markAllDirty(): void; +} + export interface ILogService { debug(message: any, ...optionalParams: any[]): void; info(message: any, ...optionalParams: any[]): void; diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index 60bf50da..22b50349 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -21,9 +21,6 @@ const SELECTION_CLASS = 'xterm-selection'; let nextTerminalId = 1; -// TODO: Pull into an addon when TS composite projects allow easier sharing of code (not just -// interfaces) between core and addons - /** * A fallback renderer for when canvas is slow. This is not meant to be * particularly fast or feature complete, more just stable and usable for when From 21fbe2585887eb3d5794235c02aa33f4485778da Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 19:50:16 -0700 Subject: [PATCH 55/69] Dependency Injection prototype --- src/Terminal.ts | 15 +++- src/common/services/BufferService.ts | 2 +- src/common/services/CoreService.ts | 4 +- src/common/services/DirtyRowService.ts | 2 +- src/common/services/InstantiationService.ts | 81 +++++++++++++++++++ src/common/services/ServiceRegistry.ts | 43 ++++++++++ .../services/{Services.d.ts => Services.ts} | 15 ++++ src/common/tsconfig.json | 3 + src/tsconfig-library-base.json | 3 +- tslint.json | 7 +- 10 files changed, 160 insertions(+), 15 deletions(-) create mode 100644 src/common/services/InstantiationService.ts create mode 100644 src/common/services/ServiceRegistry.ts rename src/common/services/{Services.d.ts => Services.ts} (86%) diff --git a/src/Terminal.ts b/src/Terminal.ts index e23ea778..11ef2d6e 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -47,7 +47,7 @@ import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { applyWindowsMode } from './WindowsMode'; import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; -import { IOptionsService, IBufferService, ICoreService, ILogService, IDirtyRowService } from 'common/services/Services'; +import { IOptionsService, IBufferService, ICoreService, ILogService, IDirtyRowService, IInstantiationService } from 'common/services/Services'; import { OptionsService } from 'common/services/OptionsService'; import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService } from 'browser/services/Services'; import { CharSizeService } from 'browser/services/CharSizeService'; @@ -61,6 +61,7 @@ import { CoreService } from 'common/services/CoreService'; import { LogService } from 'common/services/LogService'; import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport } from 'browser/Types'; import { DirtyRowService } from 'common/services/DirtyRowService'; +import { InstantiationService } from 'common/services/InstantiationService'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -113,6 +114,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _bufferService: IBufferService; private _coreService: ICoreService; private _dirtyRowService: IDirtyRowService; + private _instantiationService: IInstantiationService; private _logService: ILogService; public optionsService: IOptionsService; @@ -239,11 +241,16 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp super(); // Setup and initialize common services + this._instantiationService = new InstantiationService(); this.optionsService = new OptionsService(options); - this._bufferService = new BufferService(this.optionsService); - this._coreService = new CoreService(() => this.scrollToBottom(), this._bufferService, this.optionsService); + this._instantiationService.setService(IOptionsService, this.optionsService); + this._bufferService = this._instantiationService.createInstance(BufferService); + this._instantiationService.setService(IBufferService, this._bufferService); + this._coreService = this._instantiationService.createInstance(CoreService, () => this.scrollToBottom()); + this._instantiationService.setService(ICoreService, this._coreService); this._coreService.onData(e => this._onData.fire(e)); - this._dirtyRowService = new DirtyRowService(this._bufferService); + this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService); + // this._instantiationService.setService(IDirtyRowService, this._dirtyRowService); this._logService = new LogService(this.optionsService); this._setupOptionsListeners(); diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 6ff08061..e7dd1b64 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -18,7 +18,7 @@ export class BufferService implements IBufferService { public get buffer(): IBuffer { return this.buffers.active; } constructor( - private _optionsService: IOptionsService + @IOptionsService private _optionsService: IOptionsService ) { this.cols = Math.max(_optionsService.options.cols, MINIMUM_COLS); this.rows = Math.max(_optionsService.options.rows, MINIMUM_ROWS); diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index 295b5a08..da60e787 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -23,8 +23,8 @@ export class CoreService implements ICoreService { constructor( // TODO: Move this into a service private readonly _scrollToBottom: () => void, - private readonly _bufferService: IBufferService, - private readonly _optionsService: IOptionsService + @IBufferService private readonly _bufferService: IBufferService, + @IOptionsService private readonly _optionsService: IOptionsService ) { this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES); } diff --git a/src/common/services/DirtyRowService.ts b/src/common/services/DirtyRowService.ts index 58f40dca..b89fb918 100644 --- a/src/common/services/DirtyRowService.ts +++ b/src/common/services/DirtyRowService.ts @@ -13,7 +13,7 @@ export class DirtyRowService implements IDirtyRowService { public get end(): number { return this._end; } constructor( - private readonly _bufferService: IBufferService + @IBufferService private readonly _bufferService: IBufferService ) { this.clearRange(); } diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts new file mode 100644 index 00000000..fb846e7a --- /dev/null +++ b/src/common/services/InstantiationService.ts @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IInstantiationService, IServiceIdentifier } from 'common/services/Services'; +import { getServiceDependencies } from 'common/services/ServiceRegistry'; + +declare const console: any; + +export class ServiceCollection { + + private _entries = new Map, any>(); + + constructor(...entries: [IServiceIdentifier, any][]) { + for (const [id, service] of entries) { + this.set(id, service); + } + } + + set(id: IServiceIdentifier, instance: T): T { + const result = this._entries.get(id); + this._entries.set(id, instance); + return result; + } + + forEach(callback: (id: IServiceIdentifier, instance: any) => any): void { + this._entries.forEach((value, key) => callback(key, value)); + } + + has(id: IServiceIdentifier): boolean { + return this._entries.has(id); + } + + get(id: IServiceIdentifier): T { + return this._entries.get(id); + } +} + +export class InstantiationService implements IInstantiationService { + private readonly _services: ServiceCollection = new ServiceCollection(); + + constructor() { + this._services.set(IInstantiationService, this); + } + + public setService(id: IServiceIdentifier, instance: T): void { + this._services.set(id, instance); + } + + public createInstance(ctor: any, ...args: any[]): any { + const serviceDependencies = getServiceDependencies(ctor).sort((a, b) => a.index - b.index); + + let serviceArgs: any[] = []; + for (const dependency of serviceDependencies) { + let service = this._services.get(dependency.id); + if (!service) { + throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id}.`); + } + serviceArgs.push(service); + } + + let firstServiceArgPos = serviceDependencies.length > 0 ? serviceDependencies[0].index : args.length; + + // check for argument mismatches, adjust static args if needed + if (args.length !== firstServiceArgPos) { + console.warn(`[createInstance] First service dependency of ${ctor.name} at position ${ + firstServiceArgPos + 1} conflicts with ${args.length} static arguments`); + + let delta = firstServiceArgPos - args.length; + if (delta > 0) { + args = args.concat(new Array(delta)); + } else { + args = args.slice(0, firstServiceArgPos); + } + } + console.log('args', args, 'serviceArgs', serviceArgs); + // now create the instance + return new ctor(...[...args, ...serviceArgs]); + } +} diff --git a/src/common/services/ServiceRegistry.ts b/src/common/services/ServiceRegistry.ts new file mode 100644 index 00000000..2d7ed811 --- /dev/null +++ b/src/common/services/ServiceRegistry.ts @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IServiceIdentifier } from 'common/services/Services'; + +const DI_TARGET = 'di$target'; +const DI_DEPENDENCIES = 'di$dependencies'; + +export const serviceRegistry: Map> = new Map(); + +export function getServiceDependencies(ctor: any): { id: IServiceIdentifier, index: number, optional: boolean }[] { + return ctor[DI_DEPENDENCIES] || []; +} + +export function createDecorator(id: string): IServiceIdentifier { + if (serviceRegistry.has(id)) { + return serviceRegistry.get(id)!; + } + + const decorator = function (target: Function, key: string, index: number): any { + if (arguments.length !== 3) { + throw new Error('@IServiceName-decorator can only be used to decorate a parameter'); + } + + storeServiceDependency(decorator, target, index); + }; + + decorator.toString = () => id; + + serviceRegistry.set(id, decorator); + return decorator; +} + +function storeServiceDependency(id: Function, target: Function, index: number): void { + if ((target as any)[DI_TARGET] === target) { + (target as any)[DI_DEPENDENCIES].push({ id, index }); + } else { + (target as any)[DI_DEPENDENCIES] = [{ id, index }]; + (target as any)[DI_TARGET] = target; + } +} diff --git a/src/common/services/Services.d.ts b/src/common/services/Services.ts similarity index 86% rename from src/common/services/Services.d.ts rename to src/common/services/Services.ts index 427728f1..837b9b80 100644 --- a/src/common/services/Services.d.ts +++ b/src/common/services/Services.ts @@ -6,7 +6,9 @@ import { IEvent } from 'common/EventEmitter'; import { IBuffer, IBufferSet } from 'common/buffer/Types'; import { IDecPrivateModes } from 'common/Types'; +import { createDecorator } from 'common/services/ServiceRegistry'; +export const IBufferService = createDecorator('BufferService'); export interface IBufferService { readonly cols: number; readonly rows: number; @@ -19,6 +21,7 @@ export interface IBufferService { reset(): void; } +export const ICoreService = createDecorator('CoreService'); export interface ICoreService { readonly decPrivateModes: IDecPrivateModes; @@ -48,6 +51,17 @@ export interface IDirtyRowService { markAllDirty(): void; } +export interface IServiceIdentifier { + (...args: any[]): void; + type: T; +} + +export const IInstantiationService = createDecorator('InstantiationService'); +export interface IInstantiationService { + setService(id: IServiceIdentifier, instance: T): void; + createInstance(ctor: any, ...rest: any[]): any; +} + export interface ILogService { debug(message: any, ...optionalParams: any[]): void; info(message: any, ...optionalParams: any[]): void; @@ -55,6 +69,7 @@ export interface ILogService { error(message: any, ...optionalParams: any[]): void; } +export const IOptionsService = createDecorator('OptionsService'); export interface IOptionsService { readonly options: ITerminalOptions; diff --git a/src/common/tsconfig.json b/src/common/tsconfig.json index dca04f9a..59050a0b 100644 --- a/src/common/tsconfig.json +++ b/src/common/tsconfig.json @@ -1,6 +1,9 @@ { "extends": "../tsconfig-library-base", "compilerOptions": { + "lib": [ + "es2015" + ], "outDir": "../../out", "types": [ "../../node_modules/@types/mocha" diff --git a/src/tsconfig-library-base.json b/src/tsconfig-library-base.json index 7a9eedf3..e08695d7 100644 --- a/src/tsconfig-library-base.json +++ b/src/tsconfig-library-base.json @@ -3,6 +3,7 @@ "compilerOptions": { "composite": true, "strict": true, - "declarationMap": true + "declarationMap": true, + "experimentalDecorators": true } } diff --git a/tslint.json b/tslint.json index a18111bf..43662039 100644 --- a/tslint.json +++ b/tslint.json @@ -71,12 +71,6 @@ "variable-declaration": "nospace" } ], - "variable-name": [ - true, - "ban-keywords", - "check-format", - "allow-leading-underscore" - ], "whitespace": [ true, "check-branch", @@ -99,6 +93,7 @@ {"type": "member", "modifiers": ["protected"], "format": "camelCase", "leadingUnderscore": "require"}, {"type": "member", "modifiers": ["private"], "format": "camelCase", "leadingUnderscore": "require"}, {"type": "variable", "modifiers": ["const"], "format": ["camelCase", "UPPER_CASE"]}, + {"type": "variable", "modifiers": ["const", "export"], "filter": "^I.+Service$", "format": "PascalCase", "prefix": "I"}, {"type": "interface", "prefix": "I"} ], "no-else-after-return": { From 8359605951524d93580e073e8e2cf397d0505193 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 19:54:01 -0700 Subject: [PATCH 56/69] Add license and credit to VS Code --- README.md | 2 ++ src/common/services/InstantiationService.ts | 6 ++++++ src/common/services/ServiceRegistry.ts | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/README.md b/README.md index d01a0f33..0b4c64b9 100644 --- a/README.md +++ b/README.md @@ -185,3 +185,5 @@ If you contribute code to this project, you are implicitly allowing your code to Copyright (c) 2017-2019, [The xterm.js authors](https://github.com/xtermjs/xterm.js/graphs/contributors) (MIT License)
Copyright (c) 2014-2017, SourceLair, Private Company ([www.sourcelair.com](https://www.sourcelair.com/home)) (MIT License)
Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) + +Some files in this code base are heavily influenced on implementations in [Visual Studio Code](https://github.com/Microsoft/vscode) (MIT License). diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts index fb846e7a..98c23b6e 100644 --- a/src/common/services/InstantiationService.ts +++ b/src/common/services/InstantiationService.ts @@ -1,7 +1,13 @@ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT + * + * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). */ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ import { IInstantiationService, IServiceIdentifier } from 'common/services/Services'; import { getServiceDependencies } from 'common/services/ServiceRegistry'; diff --git a/src/common/services/ServiceRegistry.ts b/src/common/services/ServiceRegistry.ts index 2d7ed811..450af492 100644 --- a/src/common/services/ServiceRegistry.ts +++ b/src/common/services/ServiceRegistry.ts @@ -1,7 +1,13 @@ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT + * + * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). */ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ import { IServiceIdentifier } from 'common/services/Services'; From 5cc8ad802c42876928cd345bfba0832fde759fba Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 22:02:13 -0700 Subject: [PATCH 57/69] Get type safety in DI, fix character joiner not using buffer service --- src/Terminal.ts | 7 +-- src/common/TestUtils.test.ts | 5 ++ src/common/services/BufferService.ts | 2 + src/common/services/CoreService.ts | 2 + src/common/services/DirtyRowService.ts | 2 + src/common/services/InstantiationService.ts | 14 +---- src/common/services/LogService.ts | 4 +- src/common/services/OptionsService.ts | 2 + src/common/services/Services.ts | 59 ++++++++++++++++++++- src/renderer/Renderer.ts | 8 +-- 10 files changed, 85 insertions(+), 20 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 11ef2d6e..5cda1d24 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -250,8 +250,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._instantiationService.setService(ICoreService, this._coreService); this._coreService.onData(e => this._onData.fire(e)); this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService); - // this._instantiationService.setService(IDirtyRowService, this._dirtyRowService); + this._instantiationService.setService(IDirtyRowService, this._dirtyRowService); this._logService = new LogService(this.optionsService); + this._instantiationService.setService(ILogService, this._logService); this._setupOptionsListeners(); this._setup(); @@ -684,8 +685,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _createRenderer(): IRenderer { switch (this.options.rendererType) { - case 'canvas': return new Renderer(this, this._colorManager.colors, this._charSizeService); break; - case 'dom': return new DomRenderer(this, this._colorManager.colors, this._charSizeService, this.optionsService); break; + case 'canvas': return new Renderer(this._colorManager.colors, this, this._bufferService, this._charSizeService); + case 'dom': return new DomRenderer(this, this._colorManager.colors, this._charSizeService, this.optionsService); default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`); } } diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 67584a36..60ae3225 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -12,6 +12,7 @@ import { BufferSet } from 'common/buffer/BufferSet'; import { IDecPrivateModes } from 'common/Types'; export class MockBufferService implements IBufferService { + _serviceBrand: any; public get buffer(): IBuffer { return this.buffers.active; } public buffers: IBufferSet = {} as any; constructor( @@ -29,6 +30,7 @@ export class MockBufferService implements IBufferService { } export class MockCoreService implements ICoreService { + _serviceBrand: any; decPrivateModes: IDecPrivateModes = {} as any; onData: IEvent = new EventEmitter().event; onUserInput: IEvent = new EventEmitter().event; @@ -37,6 +39,7 @@ export class MockCoreService implements ICoreService { } export class MockDirtyRowService implements IDirtyRowService { + _serviceBrand: any; start: number = 0; end: number = 0; clearRange(): void {} @@ -46,6 +49,7 @@ export class MockDirtyRowService implements IDirtyRowService { } export class MockLogService implements ILogService { + _serviceBrand: any; debug(message: any, ...optionalParams: any[]): void {} info(message: any, ...optionalParams: any[]): void {} warn(message: any, ...optionalParams: any[]): void {} @@ -53,6 +57,7 @@ export class MockLogService implements ILogService { } export class MockOptionsService implements IOptionsService { + _serviceBrand: any; options: ITerminalOptions = clone(DEFAULT_OPTIONS); onOptionChange: IEvent = new EventEmitter().event; constructor(testOptions?: IPartialTerminalOptions) { diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index e7dd1b64..130fbee2 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -11,6 +11,8 @@ export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars export const MINIMUM_ROWS = 1; export class BufferService implements IBufferService { + _serviceBrand: any; + public cols: number; public rows: number; public buffers: IBufferSet; diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index da60e787..3887d95a 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -13,6 +13,8 @@ const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({ }); export class CoreService implements ICoreService { + _serviceBrand: any; + public decPrivateModes: IDecPrivateModes; private _onData = new EventEmitter(); diff --git a/src/common/services/DirtyRowService.ts b/src/common/services/DirtyRowService.ts index b89fb918..835d4069 100644 --- a/src/common/services/DirtyRowService.ts +++ b/src/common/services/DirtyRowService.ts @@ -6,6 +6,8 @@ import { IBufferService, IDirtyRowService } from 'common/services/Services'; export class DirtyRowService implements IDirtyRowService { + _serviceBrand: any; + private _start!: number; private _end!: number; diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts index 98c23b6e..037fbcfd 100644 --- a/src/common/services/InstantiationService.ts +++ b/src/common/services/InstantiationService.ts @@ -12,8 +12,6 @@ import { IInstantiationService, IServiceIdentifier } from 'common/services/Services'; import { getServiceDependencies } from 'common/services/ServiceRegistry'; -declare const console: any; - export class ServiceCollection { private _entries = new Map, any>(); @@ -70,17 +68,9 @@ export class InstantiationService implements IInstantiationService { // check for argument mismatches, adjust static args if needed if (args.length !== firstServiceArgPos) { - console.warn(`[createInstance] First service dependency of ${ctor.name} at position ${ - firstServiceArgPos + 1} conflicts with ${args.length} static arguments`); + throw new Error(`[createInstance] First service dependency of ${ctor.name} at position ${firstServiceArgPos + 1} conflicts with ${args.length} static arguments`); + } - let delta = firstServiceArgPos - args.length; - if (delta > 0) { - args = args.concat(new Array(delta)); - } else { - args = args.slice(0, firstServiceArgPos); - } - } - console.log('args', args, 'serviceArgs', serviceArgs); // now create the instance return new ctor(...[...args, ...serviceArgs]); } diff --git a/src/common/services/LogService.ts b/src/common/services/LogService.ts index f7480078..1e14f06c 100644 --- a/src/common/services/LogService.ts +++ b/src/common/services/LogService.ts @@ -37,10 +37,12 @@ const optionsKeyToLogLevel: { [key: string]: LogLevel } = { const LOG_PREFIX = 'xterm.js: '; export class LogService implements ILogService { + _serviceBrand: any; + private _logLevel!: LogLevel; constructor( - private readonly _optionsService: IOptionsService + @IOptionsService private readonly _optionsService: IOptionsService ) { this._updateLogLevel(); this._optionsService.onOptionChange(key => { diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 7396491e..4f534dc4 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -56,6 +56,8 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows']; export class OptionsService implements IOptionsService { + _serviceBrand: any; + public options: ITerminalOptions; private _onOptionChange = new EventEmitter(); diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 837b9b80..3433af28 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -10,6 +10,8 @@ import { createDecorator } from 'common/services/ServiceRegistry'; export const IBufferService = createDecorator('BufferService'); export interface IBufferService { + _serviceBrand: any; + readonly cols: number; readonly rows: number; readonly buffer: IBuffer; @@ -23,6 +25,8 @@ export interface IBufferService { export const ICoreService = createDecorator('CoreService'); export interface ICoreService { + _serviceBrand: any; + readonly decPrivateModes: IDecPrivateModes; readonly onData: IEvent; @@ -41,7 +45,10 @@ export interface ICoreService { triggerDataEvent(data: string, wasUserInput?: boolean): void; } +export const IDirtyRowService = createDecorator('DirtyRowService'); export interface IDirtyRowService { + _serviceBrand: any; + readonly start: number; readonly end: number; @@ -56,13 +63,61 @@ export interface IServiceIdentifier { type: T; } +export interface IConstructorSignature0 { + new(...services: { _serviceBrand: any; }[]): T; +} + +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; +} + export const IInstantiationService = createDecorator('InstantiationService'); export interface IInstantiationService { setService(id: IServiceIdentifier, instance: T): void; - createInstance(ctor: any, ...rest: any[]): any; + + 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; } +export const ILogService = createDecorator('LogService'); export interface ILogService { + _serviceBrand: any; + debug(message: any, ...optionalParams: any[]): void; info(message: any, ...optionalParams: any[]): void; warn(message: any, ...optionalParams: any[]): void; @@ -71,6 +126,8 @@ export interface ILogService { export const IOptionsService = createDecorator('OptionsService'); export interface IOptionsService { + _serviceBrand: any; + readonly options: ITerminalOptions; readonly onOptionChange: IEvent; diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index a6e8fca7..80d17cde 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -14,6 +14,7 @@ import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistr import { Disposable } from 'common/Lifecycle'; import { IColorSet } from 'browser/Types'; import { ICharSizeService } from 'browser/services/Services'; +import { IBufferService } from '../../out/common/services/Services'; export class Renderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; @@ -23,13 +24,14 @@ export class Renderer extends Disposable implements IRenderer { public dimensions: IRenderDimensions; constructor( - private _terminal: ITerminal, private _colors: IColorSet, - private _charSizeService: ICharSizeService + private readonly _terminal: ITerminal, + readonly _bufferService: IBufferService, + private readonly _charSizeService: ICharSizeService ) { super(); const allowTransparency = this._terminal.options.allowTransparency; - this._characterJoinerRegistry = new CharacterJoinerRegistry(_terminal); + this._characterJoinerRegistry = new CharacterJoinerRegistry(this._bufferService); this._renderLayers = [ new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency), From 283d1b11053c53f5ca971acd366d3618b075d2e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Jul 2019 05:04:11 +0000 Subject: [PATCH 58/69] Bump lodash from 4.17.10 to 4.17.14 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.10 to 4.17.14. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.10...4.17.14) Signed-off-by: dependabot[bot] --- yarn.lock | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1ac3af13..7cdf45ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2720,15 +2720,10 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash@^4.13.1, lodash@^4.17.10: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg== - -lodash@^4.17.11: - version "4.17.11" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== +lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11: + version "4.17.14" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" + integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw== log-symbols@2.2.0: version "2.2.0" From 88c88f11418dccd77a07321182bea7fa694f5d06 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 22:26:13 -0700 Subject: [PATCH 59/69] Adopt DI in browser services --- src/Terminal.ts | 22 +++++++++++-------- src/browser/TestUtils.test.ts | 2 ++ src/browser/services/CharSizeService.ts | 8 ++++--- src/browser/services/MouseService.ts | 6 +++-- src/browser/services/RenderService.ts | 8 ++++--- src/browser/services/SelectionService.ts | 12 +++++----- .../services/{Services.d.ts => Services.ts} | 16 ++++++++++++++ src/browser/services/SoundService.ts | 4 +++- 8 files changed, 55 insertions(+), 23 deletions(-) rename src/browser/services/{Services.d.ts => Services.ts} (82%) diff --git a/src/Terminal.ts b/src/Terminal.ts index 5cda1d24..08783230 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -251,7 +251,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._coreService.onData(e => this._onData.fire(e)); this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService); this._instantiationService.setService(IDirtyRowService, this._dirtyRowService); - this._logService = new LogService(this.optionsService); + this._logService = this._instantiationService.createInstance(LogService); this._instantiationService.setService(ILogService, this._logService); this._setupOptionsListeners(); @@ -585,7 +585,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this.register(addDisposableDomListener(this.textarea, 'blur', () => this._onTextAreaBlur())); this._helperContainer.appendChild(this.textarea); - this._charSizeService = new CharSizeService(this._document, this._helperContainer, this.optionsService); + this._charSizeService = this._instantiationService.createInstance(CharSizeService, this._document, this._helperContainer); + this._instantiationService.setService(ICharSizeService, this._charSizeService); this._compositionView = document.createElement('div'); this._compositionView.classList.add('composition-view'); @@ -601,12 +602,15 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._colorManager.setTheme(this._theme); const renderer = this._createRenderer(); - this._renderService = new RenderService(renderer, this.rows, this.screenElement, this.optionsService, this._charSizeService); + this._renderService = this._instantiationService.createInstance(RenderService, renderer, this.rows, this.screenElement); + this._instantiationService.setService(IRenderService, this._renderService); this._renderService.onRender(e => this._onRender.fire(e)); this.onResize(e => this._renderService.resize(e.cols, e.rows)); - this._soundService = new SoundService(this.optionsService); - this._mouseService = new MouseService(this._renderService, this._charSizeService); + this._soundService = this._instantiationService.createInstance(SoundService); + this._instantiationService.setService(ISoundService, this._soundService); + this._mouseService = this._instantiationService.createInstance(MouseService); + this._instantiationService.setService(IMouseService, this._mouseService); this.viewport = new Viewport( (amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent), @@ -625,11 +629,11 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this.register(this.onFocus(() => this._renderService.onFocus())); this.register(this._renderService.onDimensionsChange(() => this.viewport.syncScrollArea())); - this._selectionService = new SelectionService( + this._selectionService = this._instantiationService.createInstance(SelectionService, (amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent), - this.element, this.screenElement, this._charSizeService, this._bufferService, this._coreService, - this._mouseService, this.optionsService - ); + this.element, + this.screenElement); + this._instantiationService.setService(ISelectionService, this._selectionService); this.register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire())); this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService.onMouseDown(e))); this.register(this._selectionService.onRedrawRequest(e => this._renderService.onSelectionChanged(e.start, e.end, e.columnSelectMode))); diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index d89dc391..70624e48 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -7,6 +7,7 @@ import { IEvent, EventEmitter } from 'common/EventEmitter'; import { ICharSizeService, IMouseService } from 'browser/services/Services'; export class MockCharSizeService implements ICharSizeService { + _serviceBrand: any; get hasValidSize(): boolean { return this.width > 0 && this.height > 0; } onCharSizeChange: IEvent = new EventEmitter().event; constructor(public width: number, public height: number) {} @@ -14,6 +15,7 @@ export class MockCharSizeService implements ICharSizeService { } export class MockMouseService implements IMouseService { + _serviceBrand: any; public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined { throw new Error('Not implemented'); } diff --git a/src/browser/services/CharSizeService.ts b/src/browser/services/CharSizeService.ts index 42920bfa..6f381c69 100644 --- a/src/browser/services/CharSizeService.ts +++ b/src/browser/services/CharSizeService.ts @@ -8,6 +8,8 @@ import { IEvent, EventEmitter } from 'common/EventEmitter'; import { ICharSizeService } from 'browser/services/Services'; export class CharSizeService implements ICharSizeService { + _serviceBrand: any; + public width: number = 0; public height: number = 0; private _measureStrategy: IMeasureStrategy; @@ -18,9 +20,9 @@ export class CharSizeService implements ICharSizeService { public get onCharSizeChange(): IEvent { return this._onCharSizeChange.event; } constructor( - document: Document, - parentElement: HTMLElement, - private _optionsService: IOptionsService + readonly document: Document, + readonly parentElement: HTMLElement, + @IOptionsService private readonly _optionsService: IOptionsService ) { this._measureStrategy = new DomMeasureStrategy(document, parentElement, this._optionsService); } diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index 76968698..306cde59 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -7,9 +7,11 @@ import { ICharSizeService, IRenderService, IMouseService } from './Services'; import { getCoords, getRawByteCoords } from 'browser/input/Mouse'; export class MouseService implements IMouseService { + _serviceBrand: any; + constructor( - private readonly _renderService: IRenderService, - private readonly _charSizeService: ICharSizeService + @IRenderService private readonly _renderService: IRenderService, + @ICharSizeService private readonly _charSizeService: ICharSizeService ) { } diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index b41f1ebc..72ce77a8 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -14,6 +14,8 @@ import { IOptionsService } from 'common/services/Services'; import { ICharSizeService, IRenderService } from 'browser/services/Services'; export class RenderService extends Disposable implements IRenderService { + _serviceBrand: any; + private _renderDebouncer: RenderDebouncer; private _screenDprMonitor: ScreenDprMonitor; @@ -34,9 +36,9 @@ export class RenderService extends Disposable implements IRenderService { constructor( private _renderer: IRenderer, private _rowCount: number, - screenElement: HTMLElement, - optionsService: IOptionsService, - charSizeService: ICharSizeService + readonly screenElement: HTMLElement, + @IOptionsService readonly optionsService: IOptionsService, + @ICharSizeService readonly charSizeService: ICharSizeService ) { super(); this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end)); diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 6a9498a1..dd7fe48e 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -67,6 +67,8 @@ export const enum SelectionMode { * when the selection is ready to be redrawn (on an animation frame). */ export class SelectionService implements ISelectionService { + _serviceBrand: any; + protected _model: SelectionModel; /** @@ -114,11 +116,11 @@ export class SelectionService implements ISelectionService { private readonly _scrollLines: (amount: number, suppressEvent: boolean) => void, private readonly _element: HTMLElement, private readonly _screenElement: HTMLElement, - private readonly _charSizeService: ICharSizeService, - private readonly _bufferService: IBufferService, - private readonly _coreService: ICoreService, - private readonly _mouseService: IMouseService, - private readonly _optionsService: IOptionsService + @ICharSizeService private readonly _charSizeService: ICharSizeService, + @IBufferService private readonly _bufferService: IBufferService, + @ICoreService private readonly _coreService: ICoreService, + @IMouseService private readonly _mouseService: IMouseService, + @IOptionsService private readonly _optionsService: IOptionsService ) { // Init listeners this._mouseMoveListener = event => this._onMouseMove(event); diff --git a/src/browser/services/Services.d.ts b/src/browser/services/Services.ts similarity index 82% rename from src/browser/services/Services.d.ts rename to src/browser/services/Services.ts index 603d4109..8c626e18 100644 --- a/src/browser/services/Services.d.ts +++ b/src/browser/services/Services.ts @@ -7,8 +7,12 @@ import { IEvent } from 'common/EventEmitter'; import { IRenderDimensions, IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; import { IColorSet } from 'browser/Types'; import { ISelectionRedrawRequestEvent } from 'browser/selection/Types'; +import { createDecorator } from 'common/services/ServiceRegistry'; +export const ICharSizeService = createDecorator('CharSizeService'); export interface ICharSizeService { + _serviceBrand: any; + readonly width: number; readonly height: number; readonly hasValidSize: boolean; @@ -18,12 +22,18 @@ export interface ICharSizeService { measure(): void; } +export const IMouseService = createDecorator('MouseService'); export interface IMouseService { + _serviceBrand: any; + 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; } +export const IRenderService = createDecorator('RenderService'); export interface IRenderService { + _serviceBrand: any; + onDimensionsChange: IEvent; onRender: IEvent<{ start: number, end: number }>; onRefreshRequest: IEvent<{ start: number, end: number }>; @@ -48,7 +58,10 @@ export interface IRenderService { deregisterCharacterJoiner(joinerId: number): boolean; } +export const ISelectionService = createDecorator('SelectionService'); export interface ISelectionService { + _serviceBrand: any; + readonly selectionText: string; readonly hasSelection: boolean; readonly selectionStart: [number, number] | undefined; @@ -73,6 +86,9 @@ export interface ISelectionService { onMouseDown(event: MouseEvent): void; } +export const ISoundService = createDecorator('SoundService'); export interface ISoundService { + _serviceBrand: any; + playBellSound(): void; } diff --git a/src/browser/services/SoundService.ts b/src/browser/services/SoundService.ts index 31380031..89353f45 100644 --- a/src/browser/services/SoundService.ts +++ b/src/browser/services/SoundService.ts @@ -7,6 +7,8 @@ import { IOptionsService } from 'common/services/Services'; import { ISoundService } from 'browser/services/Services'; export class SoundService implements ISoundService { + _serviceBrand: any; + private static _audioContext: AudioContext; static get audioContext(): AudioContext | null { @@ -22,7 +24,7 @@ export class SoundService implements ISoundService { } constructor( - private _optionsService: IOptionsService + @IOptionsService private _optionsService: IOptionsService ) { } From 9315b5089710d557f690ae982d9662d5105f55c8 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 22:56:54 -0700 Subject: [PATCH 60/69] Use createInstance on service hungry objects --- src/Terminal.ts | 9 +++------ src/browser/MouseZoneManager.ts | 6 +++--- src/browser/Viewport.ts | 6 +++--- src/browser/input/CompositionHelper.ts | 8 ++++---- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 08783230..aee393b3 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -612,13 +612,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._mouseService = this._instantiationService.createInstance(MouseService); this._instantiationService.setService(IMouseService, this._mouseService); - this.viewport = new Viewport( + this.viewport = this._instantiationService.createInstance(Viewport, (amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent), this._viewportElement, - this._viewportScrollArea, - this._bufferService, - this._charSizeService, - this._renderService + this._viewportScrollArea ); this.viewport.onThemeChange(this._colorManager.colors); this.register(this.viewport); @@ -651,7 +648,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp })); this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this._selectionService.refresh())); - this._mouseZoneManager = new MouseZoneManager(this.element, this.screenElement, this._bufferService, this._mouseService, this._selectionService); + this._mouseZoneManager = this._instantiationService.createInstance(MouseZoneManager, this.element, this.screenElement); this.register(this._mouseZoneManager); this.register(this.onScroll(() => this._mouseZoneManager.clearAll())); this.linkifier.attachToDom(this.element, this._mouseZoneManager); diff --git a/src/browser/MouseZoneManager.ts b/src/browser/MouseZoneManager.ts index 589428f8..7eb7c5f8 100644 --- a/src/browser/MouseZoneManager.ts +++ b/src/browser/MouseZoneManager.ts @@ -35,9 +35,9 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { constructor( private readonly _element: HTMLElement, private readonly _screenElement: HTMLElement, - private readonly _bufferService: IBufferService, - private readonly _mouseService: IMouseService, - private readonly _selectionService: ISelectionService + @IBufferService private readonly _bufferService: IBufferService, + @IMouseService private readonly _mouseService: IMouseService, + @ISelectionService private readonly _selectionService: ISelectionService ) { super(); diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 270d4c67..9625588d 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -36,9 +36,9 @@ export class Viewport extends Disposable implements IViewport { private readonly _scrollLines: (amount: number, suppressEvent: boolean) => void, private readonly _viewportElement: HTMLElement, private readonly _scrollArea: HTMLElement, - private readonly _bufferService: IBufferService, - private readonly _charSizeService: ICharSizeService, - private readonly _renderService: IRenderService + @IBufferService private readonly _bufferService: IBufferService, + @ICharSizeService private readonly _charSizeService: ICharSizeService, + @IRenderService private readonly _renderService: IRenderService ) { super(); diff --git a/src/browser/input/CompositionHelper.ts b/src/browser/input/CompositionHelper.ts index a8c8bb14..e6994ea3 100644 --- a/src/browser/input/CompositionHelper.ts +++ b/src/browser/input/CompositionHelper.ts @@ -37,10 +37,10 @@ export class CompositionHelper { constructor( private readonly _textarea: HTMLTextAreaElement, private readonly _compositionView: HTMLElement, - private readonly _bufferService: IBufferService, - private readonly _optionsService: IOptionsService, - private readonly _charSizeService: ICharSizeService, - private readonly _coreService: ICoreService + @IBufferService private readonly _bufferService: IBufferService, + @IOptionsService private readonly _optionsService: IOptionsService, + @ICharSizeService private readonly _charSizeService: ICharSizeService, + @ICoreService private readonly _coreService: ICoreService ) { this._isComposing = false; this._isSendingComposition = false; From 358a707835a4639fef4dfa94d2801fa9912e06f9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 23:44:08 -0700 Subject: [PATCH 61/69] Fix most lint and tests --- src/InputHandler.test.ts | 46 +++++++++++---------- src/Terminal.ts | 2 +- src/common/services/InstantiationService.ts | 32 +++++++------- src/common/services/Services.ts | 38 ++++++++--------- src/renderer/Renderer.ts | 4 +- tslint.json | 2 + 6 files changed, 64 insertions(+), 60 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 558cfe7e..d4cebac1 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -15,6 +15,8 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { Params } from 'common/parser/Params'; import { MockCoreService, MockBufferService, MockDirtyRowService, MockOptionsService, MockLogService } from 'common/TestUtils.test'; import { IBufferService } from 'common/services/Services'; +import { DEFAULT_OPTIONS } from '../out/common/services/OptionsService'; +import { clone } from '../out/common/Clone'; function getCursor(term: TestTerminal): number[] { return [ @@ -49,43 +51,43 @@ describe('InputHandler', () => { }); describe('setCursorStyle', () => { it('should call Terminal.setOption with correct params', () => { - const terminal = new MockInputHandlingTerminal(); - const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); + const optionsService = new MockOptionsService(); + const inputHandler = new InputHandler(new MockInputHandlingTerminal(), new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), optionsService); const collect = ' '; inputHandler.setCursorStyle(Params.fromArray([0]), collect); - assert.equal(terminal.options['cursorStyle'], 'block'); - assert.equal(terminal.options['cursorBlink'], true); + assert.equal(optionsService.options['cursorStyle'], 'block'); + assert.equal(optionsService.options['cursorBlink'], true); - terminal.options = {}; + optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([1]), collect); - assert.equal(terminal.options['cursorStyle'], 'block'); - assert.equal(terminal.options['cursorBlink'], true); + assert.equal(optionsService.options['cursorStyle'], 'block'); + assert.equal(optionsService.options['cursorBlink'], true); - terminal.options = {}; + optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([2]), collect); - assert.equal(terminal.options['cursorStyle'], 'block'); - assert.equal(terminal.options['cursorBlink'], false); + assert.equal(optionsService.options['cursorStyle'], 'block'); + assert.equal(optionsService.options['cursorBlink'], false); - terminal.options = {}; + optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([3]), collect); - assert.equal(terminal.options['cursorStyle'], 'underline'); - assert.equal(terminal.options['cursorBlink'], true); + assert.equal(optionsService.options['cursorStyle'], 'underline'); + assert.equal(optionsService.options['cursorBlink'], true); - terminal.options = {}; + optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([4]), collect); - assert.equal(terminal.options['cursorStyle'], 'underline'); - assert.equal(terminal.options['cursorBlink'], false); + assert.equal(optionsService.options['cursorStyle'], 'underline'); + assert.equal(optionsService.options['cursorBlink'], false); - terminal.options = {}; + optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([5]), collect); - assert.equal(terminal.options['cursorStyle'], 'bar'); - assert.equal(terminal.options['cursorBlink'], true); + assert.equal(optionsService.options['cursorStyle'], 'bar'); + assert.equal(optionsService.options['cursorBlink'], true); - terminal.options = {}; + optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([6]), collect); - assert.equal(terminal.options['cursorStyle'], 'bar'); - assert.equal(terminal.options['cursorBlink'], false); + assert.equal(optionsService.options['cursorStyle'], 'bar'); + assert.equal(optionsService.options['cursorBlink'], false); }); }); describe('setMode', () => { diff --git a/src/Terminal.ts b/src/Terminal.ts index aee393b3..8760a4dd 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -590,7 +590,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._compositionView = document.createElement('div'); this._compositionView.classList.add('composition-view'); - this._compositionHelper = new CompositionHelper(this.textarea, this._compositionView, this._bufferService, this.optionsService, this._charSizeService, this._coreService); + this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView); this._helperContainer.appendChild(this._compositionView); // Performance: Add viewport and helper elements from the fragment diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts index 037fbcfd..abce8ac5 100644 --- a/src/common/services/InstantiationService.ts +++ b/src/common/services/InstantiationService.ts @@ -55,23 +55,23 @@ export class InstantiationService implements IInstantiationService { public createInstance(ctor: any, ...args: any[]): any { const serviceDependencies = getServiceDependencies(ctor).sort((a, b) => a.index - b.index); - let serviceArgs: any[] = []; - for (const dependency of serviceDependencies) { - let service = this._services.get(dependency.id); - if (!service) { - throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id}.`); - } - serviceArgs.push(service); - } - - let firstServiceArgPos = serviceDependencies.length > 0 ? serviceDependencies[0].index : args.length; - - // check for argument mismatches, adjust static args if needed - if (args.length !== firstServiceArgPos) { - throw new Error(`[createInstance] First service dependency of ${ctor.name} at position ${firstServiceArgPos + 1} conflicts with ${args.length} static arguments`); + const serviceArgs: any[] = []; + for (const dependency of serviceDependencies) { + const service = this._services.get(dependency.id); + if (!service) { + throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id}.`); + } + serviceArgs.push(service); } - // now create the instance - return new ctor(...[...args, ...serviceArgs]); + const firstServiceArgPos = serviceDependencies.length > 0 ? serviceDependencies[0].index : args.length; + + // check for argument mismatches, adjust static args if needed + if (args.length !== firstServiceArgPos) { + throw new Error(`[createInstance] First service dependency of ${ctor.name} at position ${firstServiceArgPos + 1} conflicts with ${args.length} static arguments`); + } + + // now create the instance + return new ctor(...[...args, ...serviceArgs]); } } diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 3433af28..4ff0bc0c 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -42,7 +42,7 @@ export interface ICoreService { * - Scroll to the bottom of the buffer.s * - Fire the `onUserInput` event (so selection can be cleared). */ - triggerDataEvent(data: string, wasUserInput?: boolean): void; + triggerDataEvent(data: string, wasUserInput?: boolean): void; } export const IDirtyRowService = createDecorator('DirtyRowService'); @@ -64,54 +64,54 @@ export interface IServiceIdentifier { } export interface IConstructorSignature0 { - new(...services: { _serviceBrand: any; }[]): T; + new(...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature1 { - new(first: A1, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, ...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature2 { - new(first: A1, second: A2, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, ...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature3 { - new(first: A1, second: A2, third: A3, ...services: { _serviceBrand: any; }[]): T; + 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; + 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; + 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; + 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; + 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; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { _serviceBrand: any; }[]): T; } export const IInstantiationService = createDecorator('InstantiationService'); export interface IInstantiationService { setService(id: IServiceIdentifier, instance: T): void; - 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(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; } export const ILogService = createDecorator('LogService'); diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 80d17cde..574d107a 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -26,12 +26,12 @@ export class Renderer extends Disposable implements IRenderer { constructor( private _colors: IColorSet, private readonly _terminal: ITerminal, - readonly _bufferService: IBufferService, + readonly bufferService: IBufferService, private readonly _charSizeService: ICharSizeService ) { super(); const allowTransparency = this._terminal.options.allowTransparency; - this._characterJoinerRegistry = new CharacterJoinerRegistry(this._bufferService); + this._characterJoinerRegistry = new CharacterJoinerRegistry(bufferService); this._renderLayers = [ new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency), diff --git a/tslint.json b/tslint.json index 43662039..4445e97c 100644 --- a/tslint.json +++ b/tslint.json @@ -94,6 +94,8 @@ {"type": "member", "modifiers": ["private"], "format": "camelCase", "leadingUnderscore": "require"}, {"type": "variable", "modifiers": ["const"], "format": ["camelCase", "UPPER_CASE"]}, {"type": "variable", "modifiers": ["const", "export"], "filter": "^I.+Service$", "format": "PascalCase", "prefix": "I"}, + {"type": "member", "filter": "^_serviceBrand$", "leadingUnderscore": "require"}, + {"type": "property", "filter": "^_serviceBrand$", "leadingUnderscore": "require"}, {"type": "interface", "prefix": "I"} ], "no-else-after-return": { From a0e2c9b270a9c6aef7a9d4f875da382de0a62f10 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 23:45:25 -0700 Subject: [PATCH 62/69] Fix naming issue with serviceBrand --- src/browser/TestUtils.test.ts | 4 ++-- src/browser/services/CharSizeService.ts | 2 +- src/browser/services/MouseService.ts | 2 +- src/browser/services/RenderService.ts | 2 +- src/browser/services/SelectionService.ts | 2 +- src/browser/services/Services.ts | 10 ++++----- src/browser/services/SoundService.ts | 2 +- src/common/TestUtils.test.ts | 10 ++++----- src/common/services/BufferService.ts | 2 +- src/common/services/CoreService.ts | 2 +- src/common/services/DirtyRowService.ts | 2 +- src/common/services/LogService.ts | 2 +- src/common/services/OptionsService.ts | 2 +- src/common/services/Services.ts | 28 ++++++++++++------------ tslint.json | 2 -- 15 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index 70624e48..60861112 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -7,7 +7,7 @@ import { IEvent, EventEmitter } from 'common/EventEmitter'; import { ICharSizeService, IMouseService } from 'browser/services/Services'; export class MockCharSizeService implements ICharSizeService { - _serviceBrand: any; + serviceBrand: any; get hasValidSize(): boolean { return this.width > 0 && this.height > 0; } onCharSizeChange: IEvent = new EventEmitter().event; constructor(public width: number, public height: number) {} @@ -15,7 +15,7 @@ export class MockCharSizeService implements ICharSizeService { } export class MockMouseService implements IMouseService { - _serviceBrand: any; + serviceBrand: any; public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined { throw new Error('Not implemented'); } diff --git a/src/browser/services/CharSizeService.ts b/src/browser/services/CharSizeService.ts index 6f381c69..0749a420 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 { - _serviceBrand: any; + serviceBrand: any; public width: number = 0; public height: number = 0; diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index 306cde59..b0f8c358 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 { - _serviceBrand: any; + serviceBrand: any; constructor( @IRenderService private readonly _renderService: IRenderService, diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 72ce77a8..ecf20e28 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -14,7 +14,7 @@ import { IOptionsService } from 'common/services/Services'; import { ICharSizeService, IRenderService } from 'browser/services/Services'; export class RenderService extends Disposable implements IRenderService { - _serviceBrand: any; + serviceBrand: any; private _renderDebouncer: RenderDebouncer; private _screenDprMonitor: ScreenDprMonitor; diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index dd7fe48e..e4c703eb 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 { - _serviceBrand: any; + serviceBrand: any; protected _model: SelectionModel; diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts index 8c626e18..863f3a9e 100644 --- a/src/browser/services/Services.ts +++ b/src/browser/services/Services.ts @@ -11,7 +11,7 @@ import { createDecorator } from 'common/services/ServiceRegistry'; export const ICharSizeService = createDecorator('CharSizeService'); export interface ICharSizeService { - _serviceBrand: any; + serviceBrand: any; readonly width: number; readonly height: number; @@ -24,7 +24,7 @@ export interface ICharSizeService { export const IMouseService = createDecorator('MouseService'); export interface IMouseService { - _serviceBrand: any; + serviceBrand: any; 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; @@ -32,7 +32,7 @@ export interface IMouseService { export const IRenderService = createDecorator('RenderService'); export interface IRenderService { - _serviceBrand: any; + serviceBrand: any; onDimensionsChange: IEvent; onRender: IEvent<{ start: number, end: number }>; @@ -60,7 +60,7 @@ export interface IRenderService { export const ISelectionService = createDecorator('SelectionService'); export interface ISelectionService { - _serviceBrand: any; + serviceBrand: any; readonly selectionText: string; readonly hasSelection: boolean; @@ -88,7 +88,7 @@ export interface ISelectionService { export const ISoundService = createDecorator('SoundService'); export interface ISoundService { - _serviceBrand: any; + serviceBrand: any; playBellSound(): void; } diff --git a/src/browser/services/SoundService.ts b/src/browser/services/SoundService.ts index 89353f45..1772c750 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 { - _serviceBrand: any; + serviceBrand: any; private static _audioContext: AudioContext; diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 60ae3225..ec36be2f 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -12,7 +12,7 @@ import { BufferSet } from 'common/buffer/BufferSet'; import { IDecPrivateModes } from 'common/Types'; export class MockBufferService implements IBufferService { - _serviceBrand: any; + serviceBrand: any; public get buffer(): IBuffer { return this.buffers.active; } public buffers: IBufferSet = {} as any; constructor( @@ -30,7 +30,7 @@ export class MockBufferService implements IBufferService { } export class MockCoreService implements ICoreService { - _serviceBrand: any; + serviceBrand: any; decPrivateModes: IDecPrivateModes = {} as any; onData: IEvent = new EventEmitter().event; onUserInput: IEvent = new EventEmitter().event; @@ -39,7 +39,7 @@ export class MockCoreService implements ICoreService { } export class MockDirtyRowService implements IDirtyRowService { - _serviceBrand: any; + serviceBrand: any; start: number = 0; end: number = 0; clearRange(): void {} @@ -49,7 +49,7 @@ export class MockDirtyRowService implements IDirtyRowService { } export class MockLogService implements ILogService { - _serviceBrand: any; + serviceBrand: any; debug(message: any, ...optionalParams: any[]): void {} info(message: any, ...optionalParams: any[]): void {} warn(message: any, ...optionalParams: any[]): void {} @@ -57,7 +57,7 @@ export class MockLogService implements ILogService { } export class MockOptionsService implements IOptionsService { - _serviceBrand: any; + serviceBrand: any; options: ITerminalOptions = clone(DEFAULT_OPTIONS); onOptionChange: IEvent = new EventEmitter().event; constructor(testOptions?: IPartialTerminalOptions) { diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 130fbee2..c7b6afce 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -11,7 +11,7 @@ export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars export const MINIMUM_ROWS = 1; export class BufferService implements IBufferService { - _serviceBrand: any; + serviceBrand: any; public cols: number; public rows: number; diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index 3887d95a..674cfedb 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -13,7 +13,7 @@ const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({ }); export class CoreService implements ICoreService { - _serviceBrand: any; + serviceBrand: any; public decPrivateModes: IDecPrivateModes; diff --git a/src/common/services/DirtyRowService.ts b/src/common/services/DirtyRowService.ts index 835d4069..0f2f14f5 100644 --- a/src/common/services/DirtyRowService.ts +++ b/src/common/services/DirtyRowService.ts @@ -6,7 +6,7 @@ import { IBufferService, IDirtyRowService } from 'common/services/Services'; export class DirtyRowService implements IDirtyRowService { - _serviceBrand: any; + serviceBrand: any; private _start!: number; private _end!: number; diff --git a/src/common/services/LogService.ts b/src/common/services/LogService.ts index 1e14f06c..6740ad4a 100644 --- a/src/common/services/LogService.ts +++ b/src/common/services/LogService.ts @@ -37,7 +37,7 @@ const optionsKeyToLogLevel: { [key: string]: LogLevel } = { const LOG_PREFIX = 'xterm.js: '; export class LogService implements ILogService { - _serviceBrand: any; + serviceBrand: any; private _logLevel!: LogLevel; diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 4f534dc4..9a5d2151 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -56,7 +56,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows']; export class OptionsService implements IOptionsService { - _serviceBrand: any; + serviceBrand: any; public options: ITerminalOptions; diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 4ff0bc0c..1af55e9a 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -10,7 +10,7 @@ import { createDecorator } from 'common/services/ServiceRegistry'; export const IBufferService = createDecorator('BufferService'); export interface IBufferService { - _serviceBrand: any; + serviceBrand: any; readonly cols: number; readonly rows: number; @@ -25,7 +25,7 @@ export interface IBufferService { export const ICoreService = createDecorator('CoreService'); export interface ICoreService { - _serviceBrand: any; + serviceBrand: any; readonly decPrivateModes: IDecPrivateModes; @@ -47,7 +47,7 @@ export interface ICoreService { export const IDirtyRowService = createDecorator('DirtyRowService'); export interface IDirtyRowService { - _serviceBrand: any; + serviceBrand: any; readonly start: number; readonly end: number; @@ -64,39 +64,39 @@ export interface IServiceIdentifier { } export interface IConstructorSignature0 { - new(...services: { _serviceBrand: any; }[]): T; + new(...services: { serviceBrand: any; }[]): T; } export interface IConstructorSignature1 { - new(first: A1, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, ...services: { serviceBrand: any; }[]): T; } export interface IConstructorSignature2 { - new(first: A1, second: A2, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, ...services: { serviceBrand: any; }[]): T; } export interface IConstructorSignature3 { - new(first: A1, second: A2, third: A3, ...services: { _serviceBrand: any; }[]): T; + 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; + 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; + 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; + 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; + 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; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { serviceBrand: any; }[]): T; } export const IInstantiationService = createDecorator('InstantiationService'); @@ -116,7 +116,7 @@ export interface IInstantiationService { export const ILogService = createDecorator('LogService'); export interface ILogService { - _serviceBrand: any; + serviceBrand: any; debug(message: any, ...optionalParams: any[]): void; info(message: any, ...optionalParams: any[]): void; @@ -126,7 +126,7 @@ export interface ILogService { export const IOptionsService = createDecorator('OptionsService'); export interface IOptionsService { - _serviceBrand: any; + serviceBrand: any; readonly options: ITerminalOptions; diff --git a/tslint.json b/tslint.json index 4445e97c..43662039 100644 --- a/tslint.json +++ b/tslint.json @@ -94,8 +94,6 @@ {"type": "member", "modifiers": ["private"], "format": "camelCase", "leadingUnderscore": "require"}, {"type": "variable", "modifiers": ["const"], "format": ["camelCase", "UPPER_CASE"]}, {"type": "variable", "modifiers": ["const", "export"], "filter": "^I.+Service$", "format": "PascalCase", "prefix": "I"}, - {"type": "member", "filter": "^_serviceBrand$", "leadingUnderscore": "require"}, - {"type": "property", "filter": "^_serviceBrand$", "leadingUnderscore": "require"}, {"type": "interface", "prefix": "I"} ], "no-else-after-return": { From fb6d4e2bfea968bac4c370afc70c3599edf0e3e2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 23:54:44 -0700 Subject: [PATCH 63/69] Remove imports from out --- src/InputHandler.test.ts | 4 ++-- src/renderer/Renderer.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index d4cebac1..38645a0b 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -15,8 +15,8 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { Params } from 'common/parser/Params'; import { MockCoreService, MockBufferService, MockDirtyRowService, MockOptionsService, MockLogService } from 'common/TestUtils.test'; import { IBufferService } from 'common/services/Services'; -import { DEFAULT_OPTIONS } from '../out/common/services/OptionsService'; -import { clone } from '../out/common/Clone'; +import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; +import { clone } from 'common/Clone'; function getCursor(term: TestTerminal): number[] { return [ diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 574d107a..2ed61b76 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -14,7 +14,7 @@ import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistr import { Disposable } from 'common/Lifecycle'; import { IColorSet } from 'browser/Types'; import { ICharSizeService } from 'browser/services/Services'; -import { IBufferService } from '../../out/common/services/Services'; +import { IBufferService } from 'common/services/Services'; export class Renderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; From 8deef83c60932102391777c43dd3f5db7d5bf8cd Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 00:16:20 -0700 Subject: [PATCH 64/69] Add tslint rule to prevent import from out Fixes #1996 --- package.json | 4 ++-- tslint.json | 4 ++++ yarn.lock | 63 +++++++++------------------------------------------- 3 files changed, 17 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index f26c010f..08357583 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "prepackage": "npm run build", "package": "webpack", "start": "node demo/start", - "lint": "tslint 'src/**/*.ts' './demo/**/*.ts' './addons/**/*.ts'", + "lint": "tslint 'src/**/*.ts' 'addons/**/*.ts'", "test": "npm run test-unit", "posttest": "npm run lint", "test-api": "mocha \"**/*.api.js\"", @@ -45,7 +45,7 @@ "puppeteer": "^1.15.0", "source-map-loader": "^0.2.4", "ts-loader": "^4.5.0", - "tslint": "^5.9.1", + "tslint": "^5.18.0", "tslint-consistent-codestyle": "^1.13.0", "typescript": "3.5", "utf8": "^3.0.0", diff --git a/tslint.json b/tslint.json index 43662039..cd790c14 100644 --- a/tslint.json +++ b/tslint.json @@ -20,6 +20,10 @@ true, "spaces" ], + "import-blacklist": [ + true, + [".*\\/out\\/.*"] + ], "interface-name": [ true, "always-prefix" diff --git a/yarn.lock b/yarn.lock index 1ac3af13..e804b9a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -419,11 +419,6 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -574,15 +569,6 @@ aws4@^1.6.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" integrity sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w== -babel-code-frame@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -818,17 +804,6 @@ chai@3.5.0: deep-eql "^0.1.3" type-detect "^1.0.0" -chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" @@ -1574,7 +1549,7 @@ escape-latex@1.2.0: resolved "https://registry.yarnpkg.com/escape-latex/-/escape-latex-1.2.0.tgz#07c03818cf7dac250cce517f4fda1b001ef2bca1" integrity sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw== -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -2078,13 +2053,6 @@ har-validator@~5.0.3: ajv "^5.1.0" har-schema "^2.0.0" -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -2527,17 +2495,12 @@ javascript-natural-sort@0.7.1: resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59" integrity sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k= -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= - js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.13.1, js-yaml@^3.13.1, js-yaml@^3.7.0: +js-yaml@3.13.1, js-yaml@^3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -4323,11 +4286,6 @@ supports-color@6.0.0: dependencies: has-flag "^3.0.0" -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - supports-color@^5.3.0: version "5.4.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" @@ -4503,25 +4461,26 @@ tslint@^5.17.0: tslib "^1.8.0" tsutils "^2.29.0" -tslint@^5.9.1: - version "5.10.0" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.10.0.tgz#11e26bccb88afa02dd0d9956cae3d4540b5f54c3" - integrity sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM= +tslint@^5.18.0: + version "5.18.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.18.0.tgz#f61a6ddcf372344ac5e41708095bbf043a147ac6" + integrity sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w== dependencies: - babel-code-frame "^6.22.0" + "@babel/code-frame" "^7.0.0" builtin-modules "^1.1.1" chalk "^2.3.0" commander "^2.12.1" diff "^3.2.0" glob "^7.1.1" - js-yaml "^3.7.0" + js-yaml "^3.13.1" minimatch "^3.0.4" + mkdirp "^0.5.1" resolve "^1.3.2" semver "^5.3.0" tslib "^1.8.0" - tsutils "^2.12.1" + tsutils "^2.29.0" -tsutils@^2.12.1, tsutils@^2.24.0, tsutils@^2.27.0: +tsutils@^2.24.0, tsutils@^2.27.0: version "2.27.2" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.27.2.tgz#60ba88a23d6f785ec4b89c6e8179cac9b431f1c7" integrity sha512-qf6rmT84TFMuxAKez2pIfR8UCai49iQsfB7YWVjV1bKpy/d0PWT5rEOSM6La9PiHZ0k1RRZQiwVdVJfQ3BPHgg== From b97c704c214934cbc62f8faedf7854ff3dd67357 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Jul 2019 07:19:39 +0000 Subject: [PATCH 65/69] Bump extend from 3.0.1 to 3.0.2 Bumps [extend](https://github.com/justmoon/node-extend) from 3.0.1 to 3.0.2. - [Release notes](https://github.com/justmoon/node-extend/releases) - [Changelog](https://github.com/justmoon/node-extend/blob/master/CHANGELOG.md) - [Commits](https://github.com/justmoon/node-extend/compare/v3.0.1...v3.0.2) Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7cdf45ca..8861d1f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1737,9 +1737,9 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: is-extendable "^1.0.1" extend@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" - integrity sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ= + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== external-editor@^3.0.0: version "3.0.3" From 1df737c3945b7e0260583556d628403d014de377 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 00:40:10 -0700 Subject: [PATCH 66/69] Fix decorator error --- src/AccessibilityManager.ts | 16 ++++++---------- src/Terminal.ts | 7 +++---- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/AccessibilityManager.ts b/src/AccessibilityManager.ts index 915a86d6..75393ea4 100644 --- a/src/AccessibilityManager.ts +++ b/src/AccessibilityManager.ts @@ -11,7 +11,7 @@ import { RenderDebouncer } from 'browser/RenderDebouncer'; import { addDisposableDomListener } from 'browser/Lifecycle'; import { Disposable } from 'common/Lifecycle'; import { ScreenDprMonitor } from 'browser/ScreenDprMonitor'; -import { IRenderDimensions } from 'browser/renderer/Types'; +import { IRenderService } from 'browser/services/Services'; const MAX_ROWS_TO_READ = 20; @@ -47,8 +47,8 @@ export class AccessibilityManager extends Disposable { private _charsToAnnounce: string = ''; constructor( - private _terminal: ITerminal, - private _dimensions: IRenderDimensions + private readonly _terminal: ITerminal, + private readonly _renderService: IRenderService ) { super(); this._accessibilityTreeRoot = document.createElement('div'); @@ -90,6 +90,7 @@ export class AccessibilityManager extends Disposable { this.register(this._terminal.onA11yTab(spaceCount => this._onTab(spaceCount))); this.register(this._terminal.onKey(e => this._onKey(e.key))); this.register(this._terminal.onBlur(() => this._clearLiveRegion())); + this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions())); this._screenDprMonitor = new ScreenDprMonitor(); this.register(this._screenDprMonitor); @@ -271,7 +272,7 @@ export class AccessibilityManager extends Disposable { } private _refreshRowsDimensions(): void { - if (!this._dimensions.actualCellHeight) { + if (!this._renderService.dimensions.actualCellHeight) { return; } if (this._rowElements.length !== this._terminal.rows) { @@ -282,13 +283,8 @@ export class AccessibilityManager extends Disposable { } } - public setDimensions(dimensions: IRenderDimensions): void { - this._dimensions = dimensions; - this._refreshRowsDimensions(); - } - private _refreshRowDimensions(element: HTMLElement): void { - element.style.height = `${this._dimensions.actualCellHeight}px`; + element.style.height = `${this._renderService.dimensions.actualCellHeight}px`; } private _announceCharacters(): void { diff --git a/src/Terminal.ts b/src/Terminal.ts index 8760a4dd..7a068c84 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -30,7 +30,7 @@ import { C0 } from 'common/data/EscapeSequences'; import { InputHandler } from './InputHandler'; import { Renderer } from './renderer/Renderer'; import { Linkifier } from 'browser/Linkifier'; -import { SelectionService } from './browser/services/SelectionService'; +import { SelectionService } from 'browser/services/SelectionService'; import * as Browser from 'common/Platform'; import { addDisposableDomListener } from 'browser/Lifecycle'; import * as Strings from 'browser/LocalizableStrings'; @@ -394,7 +394,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp case 'screenReaderMode': if (this.optionsService.options.screenReaderMode) { if (!this._accessibilityManager && this._renderService) { - this._accessibilityManager = new AccessibilityManager(this, this._renderService.dimensions); + this._accessibilityManager = new AccessibilityManager(this, this._renderService); } } else { if (this._accessibilityManager) { @@ -665,8 +665,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp if (this.options.screenReaderMode) { // Note that this must be done *after* the renderer is created in order to // ensure the correct order of the dprchange event - this._accessibilityManager = new AccessibilityManager(this, this._renderService.dimensions); - this._accessibilityManager.register(this._renderService.onDimensionsChange(e => this._accessibilityManager.setDimensions(e))); + this._accessibilityManager = new AccessibilityManager(this, this._renderService); } // Measure the character size From 2c50af356a9de25ad0b234a04ce3f2852735ed59 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 08:53:03 -0700 Subject: [PATCH 67/69] Remove note in readme --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 0b4c64b9..d01a0f33 100644 --- a/README.md +++ b/README.md @@ -185,5 +185,3 @@ If you contribute code to this project, you are implicitly allowing your code to Copyright (c) 2017-2019, [The xterm.js authors](https://github.com/xtermjs/xterm.js/graphs/contributors) (MIT License)
Copyright (c) 2014-2017, SourceLair, Private Company ([www.sourcelair.com](https://www.sourcelair.com/home)) (MIT License)
Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) - -Some files in this code base are heavily influenced on implementations in [Visual Studio Code](https://github.com/Microsoft/vscode) (MIT License). From 94b77aaf8cbc484801a173d621b2b4f896749432 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 08:57:09 -0700 Subject: [PATCH 68/69] Update express Fixes security issue in negotiator --- package.json | 2 +- yarn.lock | 374 +++++++++++++++++++++++++++++---------------------- 2 files changed, 215 insertions(+), 161 deletions(-) diff --git a/package.json b/package.json index 08357583..9504d6b9 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "@types/webpack": "^4.4.11", "@types/ws": "^6.0.1", "chai": "3.5.0", - "express": "4.13.4", + "express": "^4.17.1", "express-ws": "2.0.0-rc.1", "glob": "^7.0.5", "jsdom": "^11.11.0", diff --git a/yarn.lock b/yarn.lock index 46d21413..49eb86ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -330,13 +330,13 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -accepts@~1.2.12: - version "1.2.13" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.2.13.tgz#e5f1f3928c6d95fd96558c36ec3d9d0de4a6ecea" - integrity sha1-5fHzkoxtlf2WVYw27D2dDeSm7Oo= +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== dependencies: - mime-types "~2.1.6" - negotiator "0.5.3" + mime-types "~2.1.24" + negotiator "0.6.2" acorn-dynamic-import@^3.0.0: version "3.0.0" @@ -619,6 +619,22 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -746,6 +762,11 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + cacache@^10.0.4: version "10.0.4" resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" @@ -1092,12 +1113,14 @@ constants-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= -content-disposition@0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.1.tgz#87476c6a67c8daa87e32e87616df883ba7fb071b" - integrity sha1-h0dsamfI2qh+Muh2Ft+IO6f7Bxs= +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" -content-type@~1.0.1: +content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== @@ -1107,10 +1130,10 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie@0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.1.5.tgz#6ab9948a4b1ae21952cd2588530a4722d4044d7c" - integrity sha1-armUiksa4hlSzSWIUwpHItQETXw= +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== copy-concurrently@^1.0.0: version "1.0.5" @@ -1268,13 +1291,6 @@ debug@^4.0.1, debug@^4.1.0: dependencies: ms "^2.1.1" -debug@~2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" - integrity sha1-+HBX6ZWxofauaklgZkE3vFbwOdo= - dependencies: - ms "0.7.1" - decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -1360,7 +1376,7 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@~1.1.0: +depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -1478,6 +1494,11 @@ enabled@1.0.x: dependencies: env-variable "0.0.x" +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" @@ -1601,10 +1622,10 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= -etag@~1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.7.0.tgz#03d30b5f67dd6e632d2945d30d6652731a34d5d8" - integrity sha1-A9MLX2fdbmMtKUXTDWZScxo01dg= +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= events@^1.0.0: version "1.1.1" @@ -1665,36 +1686,41 @@ express-ws@2.0.0-rc.1: dependencies: ws "^1.0.0" -express@4.13.4: - version "4.13.4" - resolved "https://registry.yarnpkg.com/express/-/express-4.13.4.tgz#3c0b76f3c77590c8345739061ec0bd3ba067ec24" - integrity sha1-PAt288d1kMg0VzkGHsC9O6Bn7CQ= +express@^4.17.1: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== dependencies: - accepts "~1.2.12" + accepts "~1.3.7" array-flatten "1.1.1" - content-disposition "0.5.1" - content-type "~1.0.1" - cookie "0.1.5" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" cookie-signature "1.0.6" - debug "~2.2.0" - depd "~1.1.0" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" escape-html "~1.0.3" - etag "~1.7.0" - finalhandler "0.4.1" - fresh "0.3.0" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" merge-descriptors "1.0.1" methods "~1.1.2" on-finished "~2.3.0" - parseurl "~1.3.1" + parseurl "~1.3.3" path-to-regexp "0.1.7" - proxy-addr "~1.0.10" - qs "4.0.0" - range-parser "~1.0.3" - send "0.13.1" - serve-static "~1.10.2" - type-is "~1.6.6" - utils-merge "1.0.0" - vary "~1.0.1" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" extend-shallow@^2.0.1: version "2.0.1" @@ -1813,14 +1839,17 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" -finalhandler@0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.4.1.tgz#85a17c6c59a94717d262d61230d4b0ebe3d4a14d" - integrity sha1-haF8bFmpRxfSYtYSMNSw6+PUoU0= +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== dependencies: - debug "~2.2.0" + debug "2.6.9" + encodeurl "~1.0.2" escape-html "~1.0.3" on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" unpipe "~1.0.0" find-cache-dir@^1.0.0: @@ -1880,7 +1909,7 @@ form-data@~2.3.1: combined-stream "1.0.6" mime-types "^2.1.12" -forwarded@~0.1.0: +forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= @@ -1897,10 +1926,10 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -fresh@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f" - integrity sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8= +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= from2@^2.1.0: version "2.3.0" @@ -2143,13 +2172,27 @@ html-encoding-sniffer@^1.0.2: dependencies: whatwg-encoding "^1.0.1" -http-errors@~1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.3.1.tgz#197e22cdebd4198585e8694ef6786197b91ed942" - integrity sha1-GX4izevUGYWF6GlO9nhhl7ke2UI= +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== dependencies: - inherits "~2.0.1" - statuses "1" + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" http-signature@~1.2.0: version "1.2.0" @@ -2178,7 +2221,7 @@ iconv-lite@0.4.19: resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ== -iconv-lite@^0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -2245,6 +2288,11 @@ inherits@2.0.1: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= +inherits@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" @@ -2289,10 +2337,10 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== -ipaddr.js@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.0.5.tgz#5fa78cf301b825c78abc3042d812723049ea23c7" - integrity sha1-X6eM8wG4JceKvDBC2BJyMEnqI8c= +ipaddr.js@1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" + integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== is-accessor-descriptor@^0.1.6: version "0.1.6" @@ -2843,22 +2891,34 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" +mime-db@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== + mime-db@~1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.6: +mime-types@^2.1.12, mime-types@~2.1.17: version "2.1.18" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== dependencies: mime-db "~1.33.0" -mime@1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" - integrity sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM= +mime-types@~2.1.24: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== + dependencies: + mime-db "1.40.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@^2.0.3: version "2.4.2" @@ -2989,11 +3049,6 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" -ms@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" - integrity sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg= - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -3040,10 +3095,10 @@ needle@^2.2.1: iconv-lite "^0.4.4" sax "^1.2.4" -negotiator@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.5.3.tgz#269d5c476810ec92edbe7b6c2f28316384f9a7e8" - integrity sha1-Jp1cR2gQ7JLtvntsLygxY4T5p+g= +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== neo-async@^2.5.0: version "2.5.1" @@ -3400,10 +3455,10 @@ parse5@^3.0.2: dependencies: "@types/node" "*" -parseurl@~1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" - integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== pascalcase@^0.1.1: version "0.1.1" @@ -3513,13 +3568,13 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= -proxy-addr@~1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.0.10.tgz#0d40a82f801fc355567d2ecb65efe3f077f121c5" - integrity sha1-DUCoL4Afw1VWfS7LZe/j8HfxIcU= +proxy-addr@~2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" + integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== dependencies: - forwarded "~0.1.0" - ipaddr.js "1.0.5" + forwarded "~0.1.2" + ipaddr.js "1.9.0" proxy-from-env@^1.0.0: version "1.0.0" @@ -3620,10 +3675,10 @@ puppeteer@^1.17.0: rimraf "^2.6.1" ws "^6.1.0" -qs@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-4.0.0.tgz#c31d9b74ec27df75e543a86c78728ed8d4623607" - integrity sha1-wx2bdOwn33XlQ6hseHKO2NRiNgc= +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== qs@~6.5.1: version "6.5.2" @@ -3655,10 +3710,20 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -range-parser@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.0.3.tgz#6872823535c692e2c2a0103826afd82c2e0ff175" - integrity sha1-aHKCNTXGkuLCoBA4Jq/YLC4P8XU= +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" rc@^1.2.7: version "1.2.8" @@ -3865,7 +3930,7 @@ rxjs@^6.1.0: dependencies: tslib "^1.9.0" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -3915,55 +3980,39 @@ semver@^5.7.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== -send@0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.13.1.tgz#a30d5f4c82c8a9bae9ad00a1d9b1bdbe6f199ed7" - integrity sha1-ow1fTILIqbrprQCh2bG9vm8Zntc= +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== dependencies: - debug "~2.2.0" - depd "~1.1.0" + debug "2.6.9" + depd "~1.1.2" destroy "~1.0.4" + encodeurl "~1.0.2" escape-html "~1.0.3" - etag "~1.7.0" - fresh "0.3.0" - http-errors "~1.3.1" - mime "1.3.4" - ms "0.7.1" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" on-finished "~2.3.0" - range-parser "~1.0.3" - statuses "~1.2.1" - -send@0.13.2: - version "0.13.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.13.2.tgz#765e7607c8055452bba6f0b052595350986036de" - integrity sha1-dl52B8gFVFK7pvCwUllTUJhgNt4= - dependencies: - debug "~2.2.0" - depd "~1.1.0" - destroy "~1.0.4" - escape-html "~1.0.3" - etag "~1.7.0" - fresh "0.3.0" - http-errors "~1.3.1" - mime "1.3.4" - ms "0.7.1" - on-finished "~2.3.0" - range-parser "~1.0.3" - statuses "~1.2.1" + range-parser "~1.2.1" + statuses "~1.5.0" serialize-javascript@^1.4.0: version "1.5.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.5.0.tgz#1aa336162c88a890ddad5384baebc93a655161fe" integrity sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ== -serve-static@~1.10.2: - version "1.10.3" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.10.3.tgz#ce5a6ecd3101fed5ec09827dac22a9c29bfb0535" - integrity sha1-zlpuzTEB/tXsCYJ9rCKpwpv7BTU= +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== dependencies: + encodeurl "~1.0.2" escape-html "~1.0.3" - parseurl "~1.3.1" - send "0.13.2" + parseurl "~1.3.3" + send "0.17.1" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" @@ -4000,6 +4049,11 @@ setimmediate@^1.0.4: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" @@ -4156,16 +4210,11 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -statuses@1: +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -statuses@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.2.1.tgz#dded45cc18256d51ed40aec142489d5c61026d28" - integrity sha1-3e1FzBglbVHtQK7BQkidXGECbSg= - stealthy-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" @@ -4385,6 +4434,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + tough-cookie@>=2.3.3, tough-cookie@^2.3.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" @@ -4523,13 +4577,13 @@ type-detect@^1.0.0: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2" integrity sha1-diIXzAbbJY7EiQihKY6LlRIejqI= -type-is@~1.6.6: - version "1.6.16" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" - integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" - mime-types "~2.1.18" + mime-types "~2.1.24" typed-function@1.1.0: version "1.1.0" @@ -4597,7 +4651,7 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" -unpipe@~1.0.0: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= @@ -4666,10 +4720,10 @@ util@^0.10.3: dependencies: inherits "2.0.3" -utils-merge@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" - integrity sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg= +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= uuid@^3.1.0: version "3.3.2" @@ -4681,10 +4735,10 @@ v8-compile-cache@^2.0.0: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c" integrity sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw== -vary@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.0.1.tgz#99e4981566a286118dfb2b817357df7993376d10" - integrity sha1-meSYFWaihhGN+yuBc1ffeZM3bRA= +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= verror@1.10.0: version "1.10.0" From 8b3a649d65c5a804e248dc1dc4b6ef11c5833b56 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 09:02:29 -0700 Subject: [PATCH 69/69] Update express-ws, ts-loader and webpack --- package.json | 6 +- yarn.lock | 702 +++++++++++++++++++++++++++++---------------------- 2 files changed, 402 insertions(+), 306 deletions(-) diff --git a/package.json b/package.json index 9504d6b9..68467a89 100644 --- a/package.json +++ b/package.json @@ -37,19 +37,19 @@ "@types/ws": "^6.0.1", "chai": "3.5.0", "express": "^4.17.1", - "express-ws": "2.0.0-rc.1", + "express-ws": "^4.0.0", "glob": "^7.0.5", "jsdom": "^11.11.0", "mocha": "^6.1.4", "node-pty": "0.7.6", "puppeteer": "^1.15.0", "source-map-loader": "^0.2.4", - "ts-loader": "^4.5.0", + "ts-loader": "^6.0.4", "tslint": "^5.18.0", "tslint-consistent-codestyle": "^1.13.0", "typescript": "3.5", "utf8": "^3.0.0", - "webpack": "^4.17.1", + "webpack": "^4.35.3", "webpack-cli": "^3.1.0", "ws": "^7.0.0", "xterm-benchmark": "^0.1.3" diff --git a/yarn.lock b/yarn.lock index 49eb86ca..a67ede71 100644 --- a/yarn.lock +++ b/yarn.lock @@ -166,159 +166,161 @@ "@types/events" "*" "@types/node" "*" -"@webassemblyjs/ast@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.13.tgz#81155a570bd5803a30ec31436bc2c9c0ede38f25" - integrity sha512-49nwvW/Hx9i+OYHg+mRhKZfAlqThr11Dqz8TsrvqGKMhdI2ijy3KBJOun2Z4770TPjrIJhR6KxChQIDaz8clDA== +"@webassemblyjs/ast@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" + integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== dependencies: - "@webassemblyjs/helper-module-context" "1.5.13" - "@webassemblyjs/helper-wasm-bytecode" "1.5.13" - "@webassemblyjs/wast-parser" "1.5.13" - debug "^3.1.0" + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + +"@webassemblyjs/floating-point-hex-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" + integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== + +"@webassemblyjs/helper-api-error@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" + integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== + +"@webassemblyjs/helper-buffer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" + integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== + +"@webassemblyjs/helper-code-frame@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" + integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== + dependencies: + "@webassemblyjs/wast-printer" "1.8.5" + +"@webassemblyjs/helper-fsm@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" + integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== + +"@webassemblyjs/helper-module-context@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" + integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== + dependencies: + "@webassemblyjs/ast" "1.8.5" mamacro "^0.0.3" -"@webassemblyjs/floating-point-hex-parser@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz#29ce0baa97411f70e8cce68ce9c0f9d819a4e298" - integrity sha512-vrvvB18Kh4uyghSKb0NTv+2WZx871WL2NzwMj61jcq2bXkyhRC+8Q0oD7JGVf0+5i/fKQYQSBCNMMsDMRVAMqA== +"@webassemblyjs/helper-wasm-bytecode@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" + integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== -"@webassemblyjs/helper-api-error@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz#e49b051d67ee19a56e29b9aa8bd949b5b4442a59" - integrity sha512-dBh2CWYqjaDlvMmRP/kudxpdh30uXjIbpkLj9HQe+qtYlwvYjPRjdQXrq1cTAAOUSMTtzqbXIxEdEZmyKfcwsg== - -"@webassemblyjs/helper-buffer@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz#873bb0a1b46449231137c1262ddfd05695195a1e" - integrity sha512-v7igWf1mHcpJNbn4m7e77XOAWXCDT76Xe7Is1VQFXc4K5jRcFrl9D0NrqM4XifQ0bXiuTSkTKMYqDxu5MhNljA== +"@webassemblyjs/helper-wasm-section@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" + integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== dependencies: - debug "^3.1.0" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" -"@webassemblyjs/helper-code-frame@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz#1bd2181b6a0be14e004f0fe9f5a660d265362b58" - integrity sha512-yN6ScQQDFCiAXnVctdVO/J5NQRbwyTbQzsGzEgXsAnrxhjp0xihh+nNHQTMrq5UhOqTb5LykpJAvEv9AT0jnAQ== +"@webassemblyjs/ieee754@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" + integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== dependencies: - "@webassemblyjs/wast-printer" "1.5.13" + "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/helper-fsm@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz#cdf3d9d33005d543a5c5e5adaabf679ffa8db924" - integrity sha512-hSIKzbXjVMRvy3Jzhgu+vDd/aswJ+UMEnLRCkZDdknZO3Z9e6rp1DAs0tdLItjCFqkz9+0BeOPK/mk3eYvVzZg== - -"@webassemblyjs/helper-module-context@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz#dc29ddfb51ed657655286f94a5d72d8a489147c5" - integrity sha512-zxJXULGPLB7r+k+wIlvGlXpT4CYppRz8fLUM/xobGHc9Z3T6qlmJD9ySJ2jknuktuuiR9AjnNpKYDECyaiX+QQ== +"@webassemblyjs/leb128@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" + integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== dependencies: - debug "^3.1.0" - mamacro "^0.0.3" + "@xtuc/long" "4.2.2" -"@webassemblyjs/helper-wasm-bytecode@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz#03245817f0a762382e61733146f5773def15a747" - integrity sha512-0n3SoNGLvbJIZPhtMFq0XmmnA/YmQBXaZKQZcW8maGKwLpVcgjNrxpFZHEOLKjXJYVN5Il8vSfG7nRX50Zn+aw== +"@webassemblyjs/utf8@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" + integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== -"@webassemblyjs/helper-wasm-section@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz#efc76f44a10d3073b584b43c38a179df173d5c7d" - integrity sha512-IJ/goicOZ5TT1axZFSnlAtz4m8KEjYr12BNOANAwGFPKXM4byEDaMNXYowHMG0yKV9a397eU/NlibFaLwr1fbw== +"@webassemblyjs/wasm-edit@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" + integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/helper-buffer" "1.5.13" - "@webassemblyjs/helper-wasm-bytecode" "1.5.13" - "@webassemblyjs/wasm-gen" "1.5.13" - debug "^3.1.0" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/helper-wasm-section" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-opt" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + "@webassemblyjs/wast-printer" "1.8.5" -"@webassemblyjs/ieee754@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz#573e97c8c12e4eebb316ca5fde0203ddd90b0364" - integrity sha512-TseswvXEPpG5TCBKoLx9tT7+/GMACjC1ruo09j46ULRZWYm8XHpDWaosOjTnI7kr4SRJFzA6MWoUkAB+YCGKKg== +"@webassemblyjs/wasm-gen@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" + integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== dependencies: - ieee754 "^1.1.11" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" -"@webassemblyjs/leb128@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.13.tgz#ab52ebab9cec283c1c1897ac1da833a04a3f4cee" - integrity sha512-0NRMxrL+GG3eISGZBmLBLAVjphbN8Si15s7jzThaw1UE9e5BY1oH49/+MA1xBzxpf1OW5sf9OrPDOclk9wj2yg== +"@webassemblyjs/wasm-opt@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" + integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== dependencies: - long "4.0.0" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" -"@webassemblyjs/utf8@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.13.tgz#6b53d2cd861cf94fa99c1f12779dde692fbc2469" - integrity sha512-Ve1ilU2N48Ew0lVGB8FqY7V7hXjaC4+PeZM+vDYxEd+R2iQ0q+Wb3Rw8v0Ri0+rxhoz6gVGsnQNb4FjRiEH/Ng== - -"@webassemblyjs/wasm-edit@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz#c9cef5664c245cf11b3b3a73110c9155831724a8" - integrity sha512-X7ZNW4+Hga4f2NmqENnHke2V/mGYK/xnybJSIXImt1ulxbCOEs/A+ZK/Km2jgihjyVxp/0z0hwIcxC6PrkWtgw== +"@webassemblyjs/wasm-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" + integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/helper-buffer" "1.5.13" - "@webassemblyjs/helper-wasm-bytecode" "1.5.13" - "@webassemblyjs/helper-wasm-section" "1.5.13" - "@webassemblyjs/wasm-gen" "1.5.13" - "@webassemblyjs/wasm-opt" "1.5.13" - "@webassemblyjs/wasm-parser" "1.5.13" - "@webassemblyjs/wast-printer" "1.5.13" - debug "^3.1.0" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" -"@webassemblyjs/wasm-gen@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz#8e6ea113c4b432fa66540189e79b16d7a140700e" - integrity sha512-yfv94Se8R73zmr8GAYzezFHc3lDwE/lBXQddSiIZEKZFuqy7yWtm3KMwA1uGbv5G1WphimJxboXHR80IgX1hQA== +"@webassemblyjs/wast-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" + integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/helper-wasm-bytecode" "1.5.13" - "@webassemblyjs/ieee754" "1.5.13" - "@webassemblyjs/leb128" "1.5.13" - "@webassemblyjs/utf8" "1.5.13" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/floating-point-hex-parser" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-code-frame" "1.8.5" + "@webassemblyjs/helper-fsm" "1.8.5" + "@xtuc/long" "4.2.2" -"@webassemblyjs/wasm-opt@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz#147aad7717a7ee4211c36b21a5f4c30dddf33138" - integrity sha512-IkXSkgzVhQ0QYAdIayuCWMmXSYx0dHGU8Ah/AxJf1gBvstMWVnzJnBwLsXLyD87VSBIcsqkmZ28dVb0mOC3oBg== +"@webassemblyjs/wast-printer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" + integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/helper-buffer" "1.5.13" - "@webassemblyjs/wasm-gen" "1.5.13" - "@webassemblyjs/wasm-parser" "1.5.13" - debug "^3.1.0" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + "@xtuc/long" "4.2.2" -"@webassemblyjs/wasm-parser@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz#6f46516c5bb23904fbdf58009233c2dd8a54c72f" - integrity sha512-XnYoIcu2iqq8/LrtmdnN3T+bRjqYFjRHqWbqK3osD/0r/Fcv4d9ecRzjVtC29ENEuNTK4mQ9yyxCBCbK8S/cpg== - dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/helper-api-error" "1.5.13" - "@webassemblyjs/helper-wasm-bytecode" "1.5.13" - "@webassemblyjs/ieee754" "1.5.13" - "@webassemblyjs/leb128" "1.5.13" - "@webassemblyjs/utf8" "1.5.13" +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== -"@webassemblyjs/wast-parser@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz#5727a705d397ae6a3ae99d7f5460acf2ec646eea" - integrity sha512-Lbz65T0LQ1LgzKiUytl34CwuhMNhaCLgrh0JW4rJBN6INnBB8NMwUfQM+FxTnLY9qJ+lHJL/gCM5xYhB9oWi4A== - dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/floating-point-hex-parser" "1.5.13" - "@webassemblyjs/helper-api-error" "1.5.13" - "@webassemblyjs/helper-code-frame" "1.5.13" - "@webassemblyjs/helper-fsm" "1.5.13" - long "^3.2.0" - mamacro "^0.0.3" - -"@webassemblyjs/wast-printer@1.5.13": - version "1.5.13" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz#bb34d528c14b4f579e7ec11e793ec50ad7cd7c95" - integrity sha512-QcwogrdqcBh8Z+eUF8SG+ag5iwQSXxQJELBEHmLkk790wgQgnIMmntT2sMAMw53GiFNckArf5X0bsCA44j3lWQ== - dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/wast-parser" "1.5.13" - long "^3.2.0" +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== abab@^1.0.4: version "1.0.4" @@ -338,13 +340,6 @@ accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -acorn-dynamic-import@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz#901ceee4c7faaef7e07ad2a47e890675da50a278" - integrity sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg== - dependencies: - acorn "^5.0.0" - acorn-globals@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.1.0.tgz#ab716025dbe17c54d3ef81d32ece2b2d99fe2538" @@ -357,10 +352,10 @@ acorn@^5.0.0, acorn@^5.3.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" integrity sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ== -acorn@^5.6.2: - version "5.7.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.2.tgz#91fa871883485d06708800318404e72bfb26dcc5" - integrity sha512-cJrKCNcr2kv8dlDnbw+JPUGjHZzo4myaxOLmpOX8a+rgX94YeTcTMv/LFJUSByRpc+i4GgVnnhLxvMu/2Y+rqw== +acorn@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.2.0.tgz#67f0da2fc339d6cfb5d6fb244fd449f33cd8bbe3" + integrity sha512-8oe72N3WPMjA+2zVG71Ia0nXZ8DpQH+QyyHO+p06jT8eg8FGG3FbcUIi8KziHlAfheJQZeoqbvq1mQSQHXKYLw== agent-base@^4.1.0: version "4.2.1" @@ -369,6 +364,11 @@ agent-base@^4.1.0: dependencies: es6-promisify "^5.0.0" +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== + ajv-keywords@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" @@ -604,15 +604,20 @@ big.js@^3.1.3: resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + binary-extensions@^1.0.0: version "1.11.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" integrity sha1-RqoXUftqL5PuXmibsQh9SxTGwgU= -bluebird@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" - integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA== +bluebird@^3.5.5: + version "3.5.5" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" + integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" @@ -659,6 +664,13 @@ braces@^2.3.0, braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -767,23 +779,24 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -cacache@^10.0.4: - version "10.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" - integrity sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA== +cacache@^11.3.2: + version "11.3.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.3.tgz#8bd29df8c6a718a6ebd2d010da4d7972ae3bbadc" + integrity sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA== dependencies: - bluebird "^3.5.1" - chownr "^1.0.1" - glob "^7.1.2" - graceful-fs "^4.1.11" - lru-cache "^4.1.1" - mississippi "^2.0.0" + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + lru-cache "^5.1.1" + mississippi "^3.0.0" mkdirp "^0.5.1" move-concurrently "^1.0.1" promise-inflight "^1.0.1" - rimraf "^2.6.2" - ssri "^5.2.4" - unique-filename "^1.1.0" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" y18n "^4.0.0" cache-base@^1.0.1: @@ -873,6 +886,11 @@ chownr@^1.0.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" integrity sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE= +chownr@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6" + integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A== + chrome-devtools-frontend@1.0.445684: version "1.0.445684" resolved "https://registry.yarnpkg.com/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.445684.tgz#8540131836024df2b70fe90d0322af368931d762" @@ -1061,11 +1079,6 @@ commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== -commander@~2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" - integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== - commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -1679,12 +1692,12 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -express-ws@2.0.0-rc.1: - version "2.0.0-rc.1" - resolved "https://registry.yarnpkg.com/express-ws/-/express-ws-2.0.0-rc.1.tgz#9a7c7fabdf1fb8d91dbeedcc30a6f2b5bcb76826" - integrity sha1-mnx/q98fuNkdvu3MMKbytby3aCY= +express-ws@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/express-ws/-/express-ws-4.0.0.tgz#dabd8dc974516418902a41fe6e30ed949b4d36c4" + integrity sha512-KEyUw8AwRET2iFjFsI1EJQrJ/fHeGiJtgpYgEWG3yDv4l/To/m3a2GaYfeGyB3lsWdvbesjF5XCMx+SVBgAAYw== dependencies: - ws "^1.0.0" + ws "^5.2.0" express@^4.17.1: version "4.17.1" @@ -1822,6 +1835,11 @@ fecha@^2.3.3: resolved "https://registry.yarnpkg.com/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd" integrity sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg== +figgy-pudding@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" + integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== + figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -1839,6 +1857,13 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -1852,14 +1877,14 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-cache-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" - integrity sha1-kojj6ePMN0hxfTnq3hfPcfww7m8= +find-cache-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== dependencies: commondir "^1.0.1" - make-dir "^1.0.0" - pkg-dir "^2.0.0" + make-dir "^2.0.0" + pkg-dir "^3.0.0" find-up@3.0.0, find-up@^3.0.0: version "3.0.0" @@ -2030,7 +2055,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob@7.1.3, glob@^7.1.2: +glob@7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== @@ -2054,12 +2079,29 @@ glob@^7.0.5, glob@^7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.3, glob@^7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-modules-path@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.3.0.tgz#b0e2bac6beac39745f7db5c59d26a36a0b94f7dc" integrity sha512-HchvMJNYh9dGSCy8pOQ2O8u/hoXaL+0XhnrwH0RyLiSXMMTl9W3N6KUU73+JFOg5PGjtzl6VZzUQsnrpm7Szag== -graceful-fs@^4.1.11, graceful-fs@^4.1.2: +graceful-fs@^4.1.15: + version "4.2.0" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.0.tgz#8d8fdc73977cb04104721cb53666c1ca64cd328b" + integrity sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg== + +graceful-fs@^4.1.2: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= @@ -2235,7 +2277,7 @@ iconv-lite@^0.4.4: dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.1.11, ieee754@^1.1.4: +ieee754@^1.1.4: version "1.1.12" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" integrity sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA== @@ -2470,6 +2512,11 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -2511,6 +2558,11 @@ is-windows@^1.0.2: resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -2623,6 +2675,13 @@ json5@^0.5.0: resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -2705,6 +2764,15 @@ loader-utils@^1.0.2, loader-utils@^1.1.0: emojis-list "^2.0.0" json5 "^0.5.0" +loader-utils@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== + dependencies: + big.js "^5.2.2" + emojis-list "^2.0.0" + json5 "^1.0.1" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -2754,17 +2822,7 @@ logform@^2.1.1: ms "^2.1.1" triple-beam "^1.3.0" -long@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" - integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== - -long@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" - integrity sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s= - -lru-cache@^4.0.1, lru-cache@^4.1.1: +lru-cache@^4.0.1: version "4.1.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" integrity sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA== @@ -2772,12 +2830,20 @@ lru-cache@^4.0.1, lru-cache@^4.1.1: pseudomap "^1.0.2" yallist "^2.1.2" -make-dir@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" - integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: - pify "^3.0.0" + yallist "^3.0.2" + +make-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" mamacro@^0.0.3: version "0.0.3" @@ -2883,6 +2949,14 @@ micromatch@^3.1.4, micromatch@^3.1.8: snapdragon "^0.8.1" to-regex "^3.0.2" +micromatch@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -2977,10 +3051,10 @@ minizlib@^1.1.0: dependencies: minipass "^2.2.1" -mississippi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" - integrity sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw== +mississippi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== dependencies: concat-stream "^1.5.0" duplexify "^3.4.2" @@ -2988,7 +3062,7 @@ mississippi@^2.0.0: flush-write-stream "^1.0.0" from2 "^2.1.0" parallel-transform "^1.1.0" - pump "^2.0.1" + pump "^3.0.0" pumpify "^1.3.3" stream-each "^1.1.0" through2 "^2.0.0" @@ -3319,11 +3393,6 @@ optionator@^0.8.1: type-check "~0.3.2" wordwrap "~1.0.0" -options@>=0.0.5: - version "0.0.6" - resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" - integrity sha1-7CLTEoBrtT5zF3Pnza788cZDEo8= - os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" @@ -3521,10 +3590,15 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +picomatch@^2.0.5: + version "2.0.7" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" + integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pkg-dir@^2.0.0: version "2.0.0" @@ -3533,6 +3607,13 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" @@ -3607,7 +3688,7 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" -pump@^2.0.0, pump@^2.0.1: +pump@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== @@ -3894,13 +3975,20 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: +rimraf@^2.5.4, rimraf@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w== dependencies: glob "^7.0.5" +rimraf@^2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -3952,12 +4040,13 @@ sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -schema-utils@^0.4.4, schema-utils@^0.4.5: - version "0.4.7" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" - integrity sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ== +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== dependencies: ajv "^6.1.0" + ajv-errors "^1.0.0" ajv-keywords "^3.1.0" seed-random@2.2.0: @@ -3965,21 +4054,21 @@ seed-random@2.2.0: resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54" integrity sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ= -semver@^5.0.1: - version "5.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" - integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== - semver@^5.3.0, semver@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== -semver@^5.7.0: +semver@^5.6.0, semver@^5.7.0: version "5.7.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== +semver@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.2.0.tgz#4d813d9590aaf8a9192693d6c85b9344de5901db" + integrity sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A== + send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" @@ -3999,10 +4088,10 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" -serialize-javascript@^1.4.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.5.0.tgz#1aa336162c88a890ddad5384baebc93a655161fe" - integrity sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ== +serialize-javascript@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65" + integrity sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA== serve-static@1.14.1: version "1.14.1" @@ -4147,6 +4236,14 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" +source-map-support@~0.5.12: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" @@ -4190,12 +4287,12 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" -ssri@^5.2.4: - version "5.3.0" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.3.0.tgz#ba3872c9c6d33a0704a7d71ff045e5ec48999d06" - integrity sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ== +ssri@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" + integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== dependencies: - safe-buffer "^5.1.1" + figgy-pudding "^3.5.1" stack-trace@0.0.x: version "0.0.10" @@ -4354,6 +4451,11 @@ tapable@^1.0.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2" integrity sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg== +tapable@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + tar@^4: version "4.4.4" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.4.tgz#ec8409fae9f665a4355cc3b4087d0820232bb8cd" @@ -4367,6 +4469,31 @@ tar@^4: safe-buffer "^5.1.2" yallist "^3.0.2" +terser-webpack-plugin@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.3.0.tgz#69aa22426299f4b5b3775cbed8cb2c5d419aa1d4" + integrity sha512-W2YWmxPjjkUcOWa4pBEv4OP4er1aeQJlSo2UhtCFQCuRXEHjOFscO8VyWHj9JLlA0RzQb8Y2/Ta78XZvT54uGg== + dependencies: + cacache "^11.3.2" + find-cache-dir "^2.0.0" + is-wsl "^1.1.0" + loader-utils "^1.2.3" + schema-utils "^1.0.0" + serialize-javascript "^1.7.0" + source-map "^0.6.1" + terser "^4.0.0" + webpack-sources "^1.3.0" + worker-farm "^1.7.0" + +terser@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.1.2.tgz#b2656c8a506f7ce805a3f300a2ff48db022fa391" + integrity sha512-jvNoEQSPXJdssFwqPSgWjsOrb+ELoE+ILpHPKXC83tIxOlh2U75F1KuB2luLD/3a6/7K3Vw5pDn+hvu0C4AzSw== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + text-hex@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" @@ -4424,6 +4551,13 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" @@ -4466,16 +4600,16 @@ triple-beam@^1.2.0, triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== -ts-loader@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-4.5.0.tgz#a1ce70b2dc799941fb2197605f0d67874097859b" - integrity sha512-ihgVaSmgrX4crGV4n7yuoHPoCHbDzj9aepCZR9TgIx4SgJ9gdnB6xLHgUBb7bsFM/f0K6x9iXa65KY/Fu1Klkw== +ts-loader@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-6.0.4.tgz#bc331ad91a887a60632d94c9f79448666f2c4b63" + integrity sha512-p2zJYe7OtwR+49kv4gs7v4dMrfYD1IPpOtqiSPCbe8oR+4zEBtdHwzM7A7M91F+suReqgzZrlClk4LRSSp882g== dependencies: chalk "^2.3.0" enhanced-resolve "^4.0.0" loader-utils "^1.0.2" - micromatch "^3.1.4" - semver "^5.0.1" + micromatch "^4.0.0" + semver "^6.0.0" tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: version "1.9.3" @@ -4600,33 +4734,6 @@ typescript@3.5, typescript@^3.5.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.1.tgz#ba72a6a600b2158139c5dd8850f700e231464202" integrity sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw== -uglify-es@^3.3.4: - version "3.3.9" - resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" - integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== - dependencies: - commander "~2.13.0" - source-map "~0.6.1" - -uglifyjs-webpack-plugin@^1.2.4: - version "1.3.0" - resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz#75f548160858163a08643e086d5fefe18a5d67de" - integrity sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw== - dependencies: - cacache "^10.0.4" - find-cache-dir "^1.0.0" - schema-utils "^0.4.5" - serialize-javascript "^1.4.0" - source-map "^0.6.1" - uglify-es "^3.3.4" - webpack-sources "^1.1.0" - worker-farm "^1.5.2" - -ultron@1.0.x: - version "1.0.2" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" - integrity sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po= - union-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" @@ -4637,10 +4744,10 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^0.4.3" -unique-filename@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3" - integrity sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM= +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== dependencies: unique-slug "^2.0.0" @@ -4801,34 +4908,24 @@ webpack-cli@^3.1.0: v8-compile-cache "^2.0.0" yargs "^12.0.1" -webpack-sources@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" - integrity sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw== +webpack-sources@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" + integrity sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA== dependencies: source-list-map "^2.0.0" source-map "~0.6.1" -webpack-sources@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.2.0.tgz#18181e0d013fce096faf6f8e6d41eeffffdceac2" - integrity sha512-9BZwxR85dNsjWz3blyxdOhTgtnQvv3OEs5xofI0wPYTwu5kaWxS08UuD1oI7WLBLpRO+ylf0ofnXLXWmGb2WMw== +webpack@^4.35.3: + version "4.35.3" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.35.3.tgz#66bc35ef215a7b75e8790f84d560013ffecf0ca3" + integrity sha512-xggQPwr9ILlXzz61lHzjvgoqGU08v5+Wnut19Uv3GaTtzN4xBTcwnobodrXE142EL1tOiS5WVEButooGzcQzTA== dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack@^4.17.1: - version "4.17.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.17.1.tgz#0f026e3d823f3fc604f811ed3ea8f0d9b267fb1e" - integrity sha512-vdPYogljzWPhFKDj3Gcp01Vqgu7K3IQlybc3XIdKSQHelK1C3eIQuysEUR7MxKJmdandZlQB/9BG2Jb1leJHaw== - dependencies: - "@webassemblyjs/ast" "1.5.13" - "@webassemblyjs/helper-module-context" "1.5.13" - "@webassemblyjs/wasm-edit" "1.5.13" - "@webassemblyjs/wasm-opt" "1.5.13" - "@webassemblyjs/wasm-parser" "1.5.13" - acorn "^5.6.2" - acorn-dynamic-import "^3.0.0" + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/wasm-edit" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + acorn "^6.2.0" ajv "^6.1.0" ajv-keywords "^3.1.0" chrome-trace-event "^1.0.0" @@ -4842,11 +4939,11 @@ webpack@^4.17.1: mkdirp "~0.5.0" neo-async "^2.5.0" node-libs-browser "^2.0.0" - schema-utils "^0.4.4" - tapable "^1.0.0" - uglifyjs-webpack-plugin "^1.2.4" + schema-utils "^1.0.0" + tapable "^1.1.0" + terser-webpack-plugin "^1.1.0" watchpack "^1.5.0" - webpack-sources "^1.0.1" + webpack-sources "^1.3.0" whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: version "1.0.3" @@ -4916,10 +5013,10 @@ wordwrap@~1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -worker-farm@^1.5.2: - version "1.6.0" - resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0" - integrity sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ== +worker-farm@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" + integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== dependencies: errno "~0.1.7" @@ -4936,14 +5033,6 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -ws@^1.0.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" - integrity sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w== - dependencies: - options ">=0.0.5" - ultron "1.0.x" - ws@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ws/-/ws-4.1.0.tgz#a979b5d7d4da68bf54efe0408967c324869a7289" @@ -4952,6 +5041,13 @@ ws@^4.0.0: async-limiter "~1.0.0" safe-buffer "~5.1.0" +ws@^5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + ws@^6.1.0: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"