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/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/demo/client.ts b/demo/client.ts index eb8b1053..89ae226d 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -223,7 +223,6 @@ function initOptions(term: TerminalType): void { // Internal only options 'cancelEvents', 'convertEol', - 'debug', 'handler', 'screenKeys', 'termName', @@ -238,6 +237,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/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/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/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/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/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/fixtures/escape_sequence_files/t0300-vttest1.in b/fixtures/escape_sequence_files/t0300-vttest1.in new file mode 100644 index 00000000..dff924d4 --- /dev/null +++ b/fixtures/escape_sequence_files/t0300-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/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/package.json b/package.json index f26c010f..68467a89 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\"", @@ -36,20 +36,20 @@ "@types/webpack": "^4.4.11", "@types/ws": "^6.0.1", "chai": "3.5.0", - "express": "4.13.4", - "express-ws": "2.0.0-rc.1", + "express": "^4.17.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", - "tslint": "^5.9.1", + "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/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/InputHandler.test.ts b/src/InputHandler.test.ts index b8f94d5b..38645a0b 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -13,70 +13,81 @@ 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, MockDirtyRowService, MockOptionsService, MockLogService } from 'common/TestUtils.test'; +import { IBufferService } from 'common/services/Services'; +import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; +import { clone } from 'common/Clone'; + +function getCursor(term: TestTerminal): number[] { + return [ + term.buffer.x, + term.buffer.y + ]; +} describe('InputHandler', () => { describe('save and restore cursor', () => { const terminal = new MockInputHandlingTerminal(); - 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(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // 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 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', () => { @@ -84,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(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // Set bracketed paste mode inputHandler.setMode(Params.fromArray([2004]), collect); assert.equal(terminal.bracketedPasteMode, true); @@ -94,194 +105,198 @@ 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(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // 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 = term.buffer.lines.get(0); - expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '1234567890'); + const line1: IBufferLine = bufferService.buffer.lines.get(0); + expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890'); // insert one char from params = [0] - 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'); + expect(line1.translateToString(false)).equals(Array(bufferService.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'); + expect(line1.translateToString(false)).equals(Array(bufferService.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'); + expect(line1.translateToString(false)).equals(Array(bufferService.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')); + expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' '); + expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a')); }); it('deleteChars', function(): void { const term = new Terminal(); - const inputHandler = new InputHandler(term, new MockCoreService()); + const bufferService = new MockBufferService(80, 30); + 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(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 = term.buffer.lines.get(0); - expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '1234567890'); + const line1: IBufferLine = bufferService.buffer.lines.get(0); + expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890'); // delete one char from params = [0] - 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'); + 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] - 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'); + 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] - 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'); + 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] - 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')); + expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' '); + expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a')); }); it('eraseInLine', function(): void { const term = new Terminal(); - const inputHandler = new InputHandler(term, new MockCoreService()); + const bufferService = new MockBufferService(80, 30); + 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(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 - 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(bufferService.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(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // 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 < bufferService.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([ '', '', '', @@ -292,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 { @@ -342,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(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); const container = new Uint32Array(10); container[0] = 0x200B; inputHandler.print(container, 0, 1); @@ -351,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(), 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'); - 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); }); }); @@ -697,4 +714,483 @@ describe('InputHandler', () => { }); }); }); + describe('cursor positioning', () => { + let term: TestTerminal; + beforeEach(() => { + term = new TestTerminal({cols: 10, rows: 10}); + }); + it('cursor forward (CUF)', () => { + term.writeSync('\x1b[C'); + assert.deepEqual(getCursor(term), [1, 0]); + term.writeSync('\x1b[1C'); + assert.deepEqual(getCursor(term), [2, 0]); + term.writeSync('\x1b[4C'); + assert.deepEqual(getCursor(term), [6, 0]); + term.writeSync('\x1b[100C'); + assert.deepEqual(getCursor(term), [9, 0]); + // should not change y + term.buffer.x = 8; + term.buffer.y = 4; + term.writeSync('\x1b[C'); + assert.deepEqual(getCursor(term), [9, 4]); + }); + it('cursor backward (CUB)', () => { + term.writeSync('\x1b[D'); + assert.deepEqual(getCursor(term), [0, 0]); + term.writeSync('\x1b[1D'); + assert.deepEqual(getCursor(term), [0, 0]); + // place cursor at end of first line + term.writeSync('\x1b[100C'); + term.writeSync('\x1b[D'); + assert.deepEqual(getCursor(term), [8, 0]); + term.writeSync('\x1b[1D'); + assert.deepEqual(getCursor(term), [7, 0]); + term.writeSync('\x1b[4D'); + assert.deepEqual(getCursor(term), [3, 0]); + term.writeSync('\x1b[100D'); + assert.deepEqual(getCursor(term), [0, 0]); + // should not change y + term.buffer.x = 4; + term.buffer.y = 4; + term.writeSync('\x1b[D'); + assert.deepEqual(getCursor(term), [3, 4]); + }); + it('cursor down (CUD)', () => { + term.writeSync('\x1b[B'); + assert.deepEqual(getCursor(term), [0, 1]); + term.writeSync('\x1b[1B'); + assert.deepEqual(getCursor(term), [0, 2]); + term.writeSync('\x1b[4B'); + assert.deepEqual(getCursor(term), [0, 6]); + term.writeSync('\x1b[100B'); + assert.deepEqual(getCursor(term), [0, 9]); + // should not change x + term.buffer.x = 8; + term.buffer.y = 0; + term.writeSync('\x1b[B'); + assert.deepEqual(getCursor(term), [8, 1]); + }); + it('cursor up (CUU)', () => { + term.writeSync('\x1b[A'); + assert.deepEqual(getCursor(term), [0, 0]); + term.writeSync('\x1b[1A'); + assert.deepEqual(getCursor(term), [0, 0]); + // place cursor at beginning of last row + term.writeSync('\x1b[100B'); + term.writeSync('\x1b[A'); + assert.deepEqual(getCursor(term), [0, 8]); + term.writeSync('\x1b[1A'); + assert.deepEqual(getCursor(term), [0, 7]); + term.writeSync('\x1b[4A'); + assert.deepEqual(getCursor(term), [0, 3]); + term.writeSync('\x1b[100A'); + assert.deepEqual(getCursor(term), [0, 0]); + // should not change x + term.buffer.x = 8; + term.buffer.y = 9; + term.writeSync('\x1b[A'); + assert.deepEqual(getCursor(term), [8, 8]); + }); + it('cursor next line (CNL)', () => { + term.writeSync('\x1b[E'); + assert.deepEqual(getCursor(term), [0, 1]); + term.writeSync('\x1b[1E'); + assert.deepEqual(getCursor(term), [0, 2]); + term.writeSync('\x1b[4E'); + assert.deepEqual(getCursor(term), [0, 6]); + term.writeSync('\x1b[100E'); + assert.deepEqual(getCursor(term), [0, 9]); + // should reset x to zero + term.buffer.x = 8; + term.buffer.y = 0; + term.writeSync('\x1b[E'); + assert.deepEqual(getCursor(term), [0, 1]); + }); + it('cursor previous line (CPL)', () => { + term.writeSync('\x1b[F'); + assert.deepEqual(getCursor(term), [0, 0]); + term.writeSync('\x1b[1F'); + assert.deepEqual(getCursor(term), [0, 0]); + // place cursor at beginning of last row + term.writeSync('\x1b[100E'); + term.writeSync('\x1b[F'); + assert.deepEqual(getCursor(term), [0, 8]); + term.writeSync('\x1b[1F'); + assert.deepEqual(getCursor(term), [0, 7]); + term.writeSync('\x1b[4F'); + assert.deepEqual(getCursor(term), [0, 3]); + term.writeSync('\x1b[100F'); + assert.deepEqual(getCursor(term), [0, 0]); + // should reset x to zero + term.buffer.x = 8; + term.buffer.y = 9; + term.writeSync('\x1b[F'); + assert.deepEqual(getCursor(term), [0, 8]); + }); + it('cursor character absolute (CHA)', () => { + term.writeSync('\x1b[G'); + assert.deepEqual(getCursor(term), [0, 0]); + term.writeSync('\x1b[1G'); + assert.deepEqual(getCursor(term), [0, 0]); + term.writeSync('\x1b[2G'); + assert.deepEqual(getCursor(term), [1, 0]); + term.writeSync('\x1b[5G'); + assert.deepEqual(getCursor(term), [4, 0]); + term.writeSync('\x1b[100G'); + assert.deepEqual(getCursor(term), [9, 0]); + }); + it('cursor position (CUP)', () => { + term.buffer.x = 5; + term.buffer.y = 5; + term.writeSync('\x1b[H'); + assert.deepEqual(getCursor(term), [0, 0]); + term.buffer.x = 5; + term.buffer.y = 5; + term.writeSync('\x1b[1H'); + assert.deepEqual(getCursor(term), [0, 0]); + term.buffer.x = 5; + term.buffer.y = 5; + term.writeSync('\x1b[1;1H'); + assert.deepEqual(getCursor(term), [0, 0]); + term.buffer.x = 5; + term.buffer.y = 5; + term.writeSync('\x1b[8H'); + assert.deepEqual(getCursor(term), [0, 7]); + term.buffer.x = 5; + term.buffer.y = 5; + term.writeSync('\x1b[;8H'); + assert.deepEqual(getCursor(term), [7, 0]); + term.buffer.x = 5; + term.buffer.y = 5; + term.writeSync('\x1b[100;100H'); + assert.deepEqual(getCursor(term), [9, 9]); + }); + it('horizontal position absolute (HPA)', () => { + term.writeSync('\x1b[`'); + assert.deepEqual(getCursor(term), [0, 0]); + term.writeSync('\x1b[1`'); + assert.deepEqual(getCursor(term), [0, 0]); + term.writeSync('\x1b[2`'); + assert.deepEqual(getCursor(term), [1, 0]); + term.writeSync('\x1b[5`'); + assert.deepEqual(getCursor(term), [4, 0]); + term.writeSync('\x1b[100`'); + assert.deepEqual(getCursor(term), [9, 0]); + }); + it('horizontal position relative (HPR)', () => { + term.writeSync('\x1b[a'); + assert.deepEqual(getCursor(term), [1, 0]); + term.writeSync('\x1b[1a'); + assert.deepEqual(getCursor(term), [2, 0]); + term.writeSync('\x1b[4a'); + assert.deepEqual(getCursor(term), [6, 0]); + term.writeSync('\x1b[100a'); + assert.deepEqual(getCursor(term), [9, 0]); + // should not change y + term.buffer.x = 8; + term.buffer.y = 4; + term.writeSync('\x1b[a'); + assert.deepEqual(getCursor(term), [9, 4]); + }); + it('vertical position absolute (VPA)', () => { + term.writeSync('\x1b[d'); + assert.deepEqual(getCursor(term), [0, 0]); + term.writeSync('\x1b[1d'); + assert.deepEqual(getCursor(term), [0, 0]); + term.writeSync('\x1b[2d'); + assert.deepEqual(getCursor(term), [0, 1]); + term.writeSync('\x1b[5d'); + assert.deepEqual(getCursor(term), [0, 4]); + term.writeSync('\x1b[100d'); + assert.deepEqual(getCursor(term), [0, 9]); + // should not change x + term.buffer.x = 8; + term.buffer.y = 4; + term.writeSync('\x1b[d'); + assert.deepEqual(getCursor(term), [8, 0]); + }); + it('vertical position relative (VPR)', () => { + term.writeSync('\x1b[e'); + assert.deepEqual(getCursor(term), [0, 1]); + term.writeSync('\x1b[1e'); + assert.deepEqual(getCursor(term), [0, 2]); + term.writeSync('\x1b[4e'); + assert.deepEqual(getCursor(term), [0, 6]); + term.writeSync('\x1b[100e'); + assert.deepEqual(getCursor(term), [0, 9]); + // should not change x + term.buffer.x = 8; + term.buffer.y = 4; + term.writeSync('\x1b[e'); + assert.deepEqual(getCursor(term), [8, 5]); + }); + describe('should clamp cursor into addressible range', () => { + it('CUF', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[C'); + assert.deepEqual(getCursor(term), [9, 9]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[C'); + assert.deepEqual(getCursor(term), [1, 0]); + }); + it('CUB', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[D'); + assert.deepEqual(getCursor(term), [8, 9]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[D'); + assert.deepEqual(getCursor(term), [0, 0]); + }); + it('CUD', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[B'); + assert.deepEqual(getCursor(term), [9, 9]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[B'); + assert.deepEqual(getCursor(term), [0, 1]); + }); + it('CUU', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[A'); + assert.deepEqual(getCursor(term), [9, 8]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[A'); + assert.deepEqual(getCursor(term), [0, 0]); + }); + it('CNL', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[E'); + assert.deepEqual(getCursor(term), [0, 9]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[E'); + assert.deepEqual(getCursor(term), [0, 1]); + }); + it('CPL', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[F'); + assert.deepEqual(getCursor(term), [0, 8]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[F'); + assert.deepEqual(getCursor(term), [0, 0]); + }); + it('CHA', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[5G'); + assert.deepEqual(getCursor(term), [4, 9]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[5G'); + assert.deepEqual(getCursor(term), [4, 0]); + }); + it('CUP', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[5;5H'); + assert.deepEqual(getCursor(term), [4, 4]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[5;5H'); + assert.deepEqual(getCursor(term), [4, 4]); + }); + it('HPA', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[5`'); + assert.deepEqual(getCursor(term), [4, 9]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[5`'); + assert.deepEqual(getCursor(term), [4, 0]); + }); + it('HPR', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[a'); + assert.deepEqual(getCursor(term), [9, 9]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[a'); + assert.deepEqual(getCursor(term), [1, 0]); + }); + it('VPA', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[5d'); + assert.deepEqual(getCursor(term), [9, 4]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[5d'); + assert.deepEqual(getCursor(term), [0, 4]); + }); + it('VPR', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[e'); + assert.deepEqual(getCursor(term), [9, 9]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[e'); + assert.deepEqual(getCursor(term), [0, 1]); + }); + it('DCH', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[P'); + assert.deepEqual(getCursor(term), [9, 9]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[P'); + assert.deepEqual(getCursor(term), [0, 0]); + }); + it('DCH - should delete last cell', () => { + term.writeSync('0123456789\x1b[P'); + assert.equal(term.buffer.lines.get(0).translateToString(false), '012345678 '); + }); + it('ECH', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[X'); + assert.deepEqual(getCursor(term), [9, 9]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[X'); + assert.deepEqual(getCursor(term), [0, 0]); + }); + it('ECH - should delete last cell', () => { + term.writeSync('0123456789\x1b[X'); + assert.equal(term.buffer.lines.get(0).translateToString(false), '012345678 '); + }); + it('ICH', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[@'); + assert.deepEqual(getCursor(term), [9, 9]); + term.buffer.x = -10000; + term.buffer.y = -10000; + term.writeSync('\x1b[@'); + assert.deepEqual(getCursor(term), [0, 0]); + }); + it('ICH - should delete last cell', () => { + term.writeSync('0123456789\x1b[@'); + assert.equal(term.buffer.lines.get(0).translateToString(false), '012345678 '); + }); + }); + }); + describe('DECSTBM - scroll margins', () => { + let term: TestTerminal; + beforeEach(() => { + term = new TestTerminal({cols: 10, rows: 10}); + }); + it('should default to whole viewport', () => { + term.writeSync('\x1b[r'); + assert.equal(term.buffer.scrollTop, 0); + assert.equal(term.buffer.scrollBottom, 9); + term.writeSync('\x1b[3;7r'); + assert.equal(term.buffer.scrollTop, 2); + assert.equal(term.buffer.scrollBottom, 6); + term.writeSync('\x1b[0;0r'); + assert.equal(term.buffer.scrollTop, 0); + assert.equal(term.buffer.scrollBottom, 9); + }); + it('should clamp bottom', () => { + term.writeSync('\x1b[3;1000r'); + assert.equal(term.buffer.scrollTop, 2); + assert.equal(term.buffer.scrollBottom, 9); + }); + it('should only apply for top < bottom', () => { + term.writeSync('\x1b[7;2r'); + assert.equal(term.buffer.scrollTop, 0); + assert.equal(term.buffer.scrollBottom, 9); + }); + it('should home cursor', () => { + term.buffer.x = 10000; + term.buffer.y = 10000; + term.writeSync('\x1b[2;7r'); + assert.deepEqual(getCursor(term), [0, 0]); + }); + }); + describe('scroll margins', () => { + let term: TestTerminal; + beforeEach(() => { + term = new TestTerminal({cols: 10, rows: 10}); + }); + 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', () => { + term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Sm'); + assert.deepEqual(getLines(term), ['m', '3', '', '', '4', '5', '6', '7', '8', '9']); + }); + it('scrollDown', () => { + term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Tm'); + assert.deepEqual(getLines(term), ['m', '', '', '1', '4', '5', '6', '7', '8', '9']); + }); + it('insertLines - out of margins', () => { + term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r'); + assert.equal(term.buffer.scrollTop, 2); + assert.equal(term.buffer.scrollBottom, 5); + term.writeSync('\x1b[2Lm'); + assert.deepEqual(getLines(term), ['m', '1', '2', '3', '4', '5', '6', '7', '8', '9']); + term.writeSync('\x1b[2H\x1b[2Ln'); + assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', '6', '7', '8', '9']); + // skip below scrollbottom + term.writeSync('\x1b[7H\x1b[2Lo'); + assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', '7', '8', '9']); + term.writeSync('\x1b[8H\x1b[2Lp'); + assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', '9']); + term.writeSync('\x1b[100H\x1b[2Lq'); + assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', 'q']); + }); + it('insertLines - within margins', () => { + term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r'); + assert.equal(term.buffer.scrollTop, 2); + assert.equal(term.buffer.scrollBottom, 5); + term.writeSync('\x1b[3H\x1b[2Lm'); + assert.deepEqual(getLines(term), ['0', '1', 'm', '', '2', '3', '6', '7', '8', '9']); + term.writeSync('\x1b[6H\x1b[2Ln'); + assert.deepEqual(getLines(term), ['0', '1', 'm', '', '2', 'n', '6', '7', '8', '9']); + }); + it('deleteLines - out of margins', () => { + term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r'); + assert.equal(term.buffer.scrollTop, 2); + assert.equal(term.buffer.scrollBottom, 5); + term.writeSync('\x1b[2Mm'); + assert.deepEqual(getLines(term), ['m', '1', '2', '3', '4', '5', '6', '7', '8', '9']); + term.writeSync('\x1b[2H\x1b[2Mn'); + assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', '6', '7', '8', '9']); + // skip below scrollbottom + term.writeSync('\x1b[7H\x1b[2Mo'); + assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', '7', '8', '9']); + term.writeSync('\x1b[8H\x1b[2Mp'); + assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', '9']); + term.writeSync('\x1b[100H\x1b[2Mq'); + assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', 'q']); + }); + it('deleteLines - within margins', () => { + term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r'); + assert.equal(term.buffer.scrollTop, 2); + assert.equal(term.buffer.scrollBottom, 5); + term.writeSync('\x1b[6H\x1b[2Mm'); + assert.deepEqual(getLines(term), ['0', '1', '2', '3', '4', 'm', '6', '7', '8', '9']); + term.writeSync('\x1b[3H\x1b[2Mn'); + assert.deepEqual(getLines(term), ['0', '1', 'n', 'm', '', '', '6', '7', '8', '9']); + }); + }); }); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 577029f8..cc767a16 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -15,11 +15,11 @@ import { StringToUtf32, stringFromCodePoint, utf32ToString, Utf8ToUtf32 } from ' import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams } 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 { IAttributeData, IDisposable } from 'common/Types'; -import { ICoreService } from 'common/services/Services'; +import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService } from 'common/services/Services'; import { ISelectionService } from 'browser/services/Services'; /** @@ -41,7 +41,12 @@ 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 _bufferService: IBufferService, + private _coreService: ICoreService, + private _logService: ILogService, + private _optionsService: IOptionsService + ) { } hook(collect: string, params: IParams, flag: number): void { this._data = new Uint32Array(0); @@ -57,25 +62,25 @@ 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._terminal.buffer.scrollTop + 1) + - ';' + (this._terminal.buffer.scrollBottom + 1) + 'r'; - return this._terminal.handler(`${C0.ESC}P1$r${pt}${C0.ESC}\\`); + const pt = '' + (this._bufferService.buffer.scrollTop + 1) + + ';' + (this._bufferService.buffer.scrollBottom + 1) + 'r'; + 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}\\`); + this._logService.error('Unknown DCS $q %s', data); + this._coreService.triggerDataEvent(`${C0.ESC}P0$r${C0.ESC}\\`); } } } @@ -123,9 +128,13 @@ export class InputHandler extends Disposable implements IInputHandler { public get onScroll(): IEvent { return this._onScroll.event; } constructor( - protected _terminal: IInputHandlingTerminal, - private _coreService: ICoreService, - 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(); @@ -135,16 +144,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 }); }); /** @@ -280,24 +289,24 @@ 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 */ 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._terminal)); + this._parser.setDcsHandler('$q', new DECRQSS(this._bufferService, this._coreService, this._logService, this._optionsService)); } public dispose(): void { super.dispose(); - this._terminal = null; } // TODO: When InputHandler moves into common, browser dependencies need to move out @@ -306,52 +315,36 @@ 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._terminal.buffer; + let buffer = this._bufferService.buffer; 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('parsing data', data); if (this._parseBuffer.length < data.length) { this._parseBuffer = new Uint32Array(data.length); } 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(); } } public parseUtf8(data: Uint8Array): void { - // Ensure the terminal is not disposed - if (!this._terminal) { - return; - } - - let buffer = this._terminal.buffer; + let buffer = this._bufferService.buffer; 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('parsing data', data); if (this._parseBuffer.length < data.length) { this._parseBuffer = new Uint32Array(data.length); } 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(); } @@ -360,16 +353,16 @@ 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; + 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]; @@ -430,12 +423,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? } } @@ -478,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); } /** @@ -509,9 +502,9 @@ 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) { + if (this._optionsService.options.convertEol) { buffer.x = 0; } buffer.y++; @@ -520,7 +513,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--; } @@ -532,7 +525,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; } /** @@ -540,8 +533,9 @@ export class InputHandler extends Disposable implements IInputHandler { * Backspace (Ctrl-H). */ public backspace(): void { - if (this._terminal.buffer.x > 0) { - this._terminal.buffer.x--; + this._restrictCursor(); + if (this._bufferService.buffer.x > 0) { + this._bufferService.buffer.x--; } } @@ -550,10 +544,13 @@ export class InputHandler extends Disposable implements IInputHandler { * Horizontal Tab (HT) (Ctrl-I). */ public tab(): void { - const originalX = this._terminal.buffer.x; - this._terminal.buffer.x = this._terminal.buffer.nextStop(); - if (this._terminal.options.screenReaderMode) { - this._terminal.onA11yTabEmitter.fire(this._terminal.buffer.x - originalX); + if (this._bufferService.buffer.x >= this._bufferService.cols) { + return; + } + const originalX = this._bufferService.buffer.x; + this._bufferService.buffer.x = this._bufferService.buffer.nextStop(); + if (this._optionsService.options.screenReaderMode) { + this._terminal.onA11yTabEmitter.fire(this._bufferService.buffer.x - originalX); } } @@ -576,16 +573,37 @@ export class InputHandler extends Disposable implements IInputHandler { } /** - * CSI Ps @ - * Insert Ps (Blank) Character(s) (default = 1) (ICH). + * Restrict cursor to viewport size / scroll margin (origin mode). */ - public insertChars(params: IParams): void { - 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); + private _restrictCursor(): void { + 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._bufferService.rows - 1, Math.max(0, this._bufferService.buffer.y)); + } + + /** + * Set absolute cursor position. + */ + private _setCursor(x: number, y: number): void { + if (this._terminal.originMode) { + this._bufferService.buffer.x = x; + this._bufferService.buffer.y = this._bufferService.buffer.scrollTop + y; + } else { + this._bufferService.buffer.x = x; + this._bufferService.buffer.y = y; + } + 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 + this._restrictCursor(); + this._setCursor(this._bufferService.buffer.x + x, this._bufferService.buffer.y + y); } /** @@ -593,11 +611,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Cursor Up Ps Times (default = 1) (CUU). */ public cursorUp(params: IParams): void { - const param = params.params[0] || 1; - this._terminal.buffer.y -= param; - if (this._terminal.buffer.y < 0) { - this._terminal.buffer.y = 0; - } + this._moveCursor(0, -(params.params[0] || 1)); } /** @@ -605,15 +619,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Cursor Down Ps Times (default = 1) (CUD). */ public cursorDown(params: IParams): void { - const param = params.params[0] || 1; - 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._moveCursor(0, params.params[0] || 1); } /** @@ -621,11 +627,7 @@ export class InputHandler extends Disposable implements IInputHandler { * Cursor Forward Ps Times (default = 1) (CUF). */ public cursorForward(params: IParams): void { - const param = params.params[0] || 1; - this._terminal.buffer.x += param; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } + this._moveCursor(params.params[0] || 1, 0); } /** @@ -633,55 +635,35 @@ export class InputHandler extends Disposable implements IInputHandler { * Cursor Backward Ps Times (default = 1) (CUB). */ public cursorBackward(params: IParams): void { - const param = params.params[0] || 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._terminal.buffer.x -= param; - if (this._terminal.buffer.x < 0) { - this._terminal.buffer.x = 0; - } + this._moveCursor(-(params.params[0] || 1), 0); } /** * 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: IParams): void { - const param = params.params[0] || 1; - 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._moveCursor(0, params.params[0] || 1); + this._bufferService.buffer.x = 0; } - /** * 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: IParams): void { - const param = params.params[0] || 1; - this._terminal.buffer.y -= param; - if (this._terminal.buffer.y < 0) { - this._terminal.buffer.y = 0; - } - this._terminal.buffer.x = 0; + this._moveCursor(0, -(params.params[0] || 1)); + this._bufferService.buffer.x = 0; } - /** * CSI Ps G * Cursor Character Absolute [column] (default = [row,1]) (CHA). */ public cursorCharAbsolute(params: IParams): void { - const param = params.params[0] || 1; - this._terminal.buffer.x = param - 1; + this._setCursor((params.params[0] || 1) - 1, this._bufferService.buffer.y); } /** @@ -689,23 +671,73 @@ export class InputHandler extends Disposable implements IInputHandler { * Cursor Position [row;column] (default = [1,1]) (CUP). */ public cursorPosition(params: IParams): void { - let row: number = params.params[0] - 1; - let col: number = (params.length >= 2) ? params.params[1] - 1 : 0; + this._setCursor( + // col + (params.length >= 2) ? (params.params[1] || 1) - 1 : 0, + // row + (params.params[0] || 1) - 1); + } - if (row < 0) { - row = 0; - } else if (row >= this._terminal.rows) { - row = this._terminal.rows - 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._bufferService.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._bufferService.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._bufferService.buffer.tabs[this._bufferService.buffer.x]; + } else if (param === 3) { + this._bufferService.buffer.tabs = {}; } - - if (col < 0) { - col = 0; - } else if (col >= this._terminal.cols) { - col = this._terminal.cols - 1; - } - - this._terminal.buffer.x = col; - this._terminal.buffer.y = row; } /** @@ -713,12 +745,33 @@ 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._bufferService.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(); } } + /** + * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). + */ + public cursorBackwardTab(params: IParams): void { + if (this._bufferService.buffer.x >= this._bufferService.cols) { + return; + } + let param = params.params[0] || 1; + + // make buffer local for faster access + const buffer = this._bufferService.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. @@ -727,11 +780,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; @@ -744,7 +797,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._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y); + line.fill(this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData())); + line.isWrapped = false; } /** @@ -760,46 +815,47 @@ 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: - j = this._terminal.buffer.y; - this._terminal.updateRange(j); - this._eraseInBufferLine(j++, this._terminal.buffer.x, this._terminal.cols, this._terminal.buffer.x === 0); - for (; j < this._terminal.rows; j++) { + j = this._bufferService.buffer.y; + 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._terminal.buffer.y; - this._terminal.updateRange(j); + j = this._bufferService.buffer.y; + this._dirtyRowService.markDirty(j); // Deleted front part of line and everything before. This line will no longer be wrapped. - this._eraseInBufferLine(j, 0, this._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._bufferService.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); } - this._terminal.updateRange(0); + this._dirtyRowService.markDirty(0); break; case 2: - j = this._terminal.rows; - this._terminal.updateRange(j - 1); + j = this._bufferService.rows; + 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) - const scrollBackSize = this._terminal.buffer.lines.length - this._terminal.rows; + const scrollBackSize = this._bufferService.buffer.lines.length - this._bufferService.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); } @@ -819,18 +875,19 @@ export class InputHandler extends Disposable implements IInputHandler { * Ps = 2 -> Selective Erase All. */ public eraseInLine(params: IParams): void { + 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._bufferService.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._bufferService.cols); break; } - this._terminal.updateRange(this._terminal.buffer.y); + this._dirtyRowService.markDirty(this._bufferService.buffer.y); } /** @@ -838,15 +895,20 @@ 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 - const buffer = this._terminal.buffer; + const buffer = this._bufferService.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; - 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 @@ -854,9 +916,8 @@ 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? } /** @@ -864,16 +925,21 @@ 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 - const buffer = this._terminal.buffer; + const buffer = this._bufferService.buffer; + + if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) { + return; + } 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 @@ -881,9 +947,25 @@ 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? + } + + /** + * CSI Ps @ + * Insert Ps (Blank) Character(s) (default = 1) (ICH). + */ + public insertChars(params: IParams): void { + this._restrictCursor(); + const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase); + if (line) { + line.insertCells( + this._bufferService.buffer.x, + params.params[0] || 1, + this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()) + ); + this._dirtyRowService.markDirty(this._bufferService.buffer.y); + } } /** @@ -891,12 +973,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._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase); + if (line) { + line.deleteCells( + this._bufferService.buffer.x, + params.params[0] || 1, + this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()) + ); + this._dirtyRowService.markDirty(this._bufferService.buffer.y); + } } /** @@ -906,15 +992,13 @@ 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); 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); } /** @@ -925,15 +1009,13 @@ 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); 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); } } @@ -942,50 +1024,15 @@ 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); - } - - /** - * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). - */ - public cursorBackwardTab(params: IParams): void { - 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). - */ - public charPosAbsolute(params: IParams): void { - const param = params.params[0] || 1; - this._terminal.buffer.x = param - 1; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } - } - - /** - * CSI Pm a Character Position Relative - * [columns] (default = [row,col+1]) (HPR) - * reuse CSI Ps C ? - */ - public hPositionRelative(params: IParams): void { - const param = params.params[0] || 1; - this._terminal.buffer.x += param; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; + this._restrictCursor(); + const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase); + if (line) { + line.replaceCells( + this._bufferService.buffer.x, + this._bufferService.buffer.x + (params.params[0] || 1), + this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()) + ); + this._dirtyRowService.markDirty(this._bufferService.buffer.y); } } @@ -1090,72 +1137,6 @@ export class InputHandler extends Disposable implements IInputHandler { } } - /** - * CSI Pm d Vertical Position Absolute (VPA) - * [row] (default = [1,column]) - */ - public linePosAbsolute(params: IParams): void { - const param = params.params[0] || 1; - this._terminal.buffer.y = param - 1; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - } - - /** - * CSI Pm e Vertical Position Relative (VPR) - * [rows] (default = [row+1,column]) - * reuse CSI Ps B ? - */ - public vPositionRelative(params: IParams): void { - const param = params.params[0] || 1; - 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--; - } - } - - /** - * CSI Ps ; Ps f - * Horizontal and Vertical Position [row;column] (default = - * [1,1]) (HVP). - */ - public hVPosition(params: IParams): void { - const row = params.params[0] || 1; - const col = (params.length > 1) ? params.params[1] || 1 : 1; - - this._terminal.buffer.y = row - 1; - if (this._terminal.buffer.y >= this._terminal.rows) { - this._terminal.buffer.y = this._terminal.rows - 1; - } - - this._terminal.buffer.x = col - 1; - if (this._terminal.buffer.x >= this._terminal.cols) { - this._terminal.buffer.x = this._terminal.cols - 1; - } - } - - /** - * 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). @@ -1271,11 +1252,14 @@ export class InputHandler extends Disposable implements IInputHandler { // set VT100 mode here break; case 3: // 132 col mode - this._terminal.savedCols = this._terminal.cols; - this._terminal.resize(132, this._terminal.rows); + // TODO: move DECCOLM into compat addon + this._terminal.savedCols = this._bufferService.cols; + this._terminal.resize(132, this._bufferService.rows); + this._terminal.reset(); break; case 6: this._terminal.originMode = true; + this._setCursor(0, 0); break; case 7: this._terminal.wraparoundMode = true; @@ -1284,7 +1268,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(); @@ -1312,7 +1296,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 @@ -1349,8 +1333,8 @@ 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._terminal.refresh(0, this._terminal.rows - 1); + this._bufferService.buffers.activateAltBuffer(this._terminal.eraseAttrData()); + this._terminal.refresh(0, this._bufferService.rows - 1); if (this._terminal.viewport) { this._terminal.viewport.syncScrollArea(); } @@ -1467,13 +1451,18 @@ export class InputHandler extends Disposable implements IInputHandler { this._coreService.decPrivateModes.applicationCursorKeys = false; break; case 3: - if (this._terminal.cols === 132 && this._terminal.savedCols) { - this._terminal.resize(this._terminal.savedCols, this._terminal.rows); + // 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._bufferService.cols === 132 && this._terminal.savedCols) { + this._terminal.resize(this._terminal.savedCols, this._bufferService.rows); } delete this._terminal.savedCols; + this._terminal.reset(); break; case 6: this._terminal.originMode = false; + this._setCursor(0, 0); break; case 7: this._terminal.wraparoundMode = false; @@ -1482,7 +1471,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(); @@ -1526,11 +1515,11 @@ 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(); } - this._terminal.refresh(0, this._terminal.rows - 1); + this._terminal.refresh(0, this._bufferService.rows - 1); if (this._terminal.viewport) { this._terminal.viewport.syncScrollArea(); } @@ -1773,7 +1762,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); } } } @@ -1810,8 +1799,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; } @@ -1821,8 +1810,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: @@ -1860,10 +1849,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._bufferService.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]; // ?? @@ -1886,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; } } @@ -1913,10 +1902,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._bufferService.rows || bottom === 0) { + bottom = this._bufferService.rows; + } + + if (bottom > top) { + this._bufferService.buffer.scrollTop = top - 1; + this._bufferService.buffer.scrollBottom = bottom - 1; + this._setCursor(0, 0); + } } @@ -1926,10 +1924,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._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; } @@ -1939,10 +1938,15 @@ 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._bufferService.buffer.savedCharset) { + this._terminal.charset = this._bufferService.buffer.savedCharset; + } + this._restrictCursor(); } @@ -1962,7 +1966,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(); } @@ -1972,7 +1976,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(); @@ -1985,7 +1989,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(); @@ -2038,7 +2042,13 @@ export class InputHandler extends Disposable implements IInputHandler { * Moves the cursor down one line in the same column. */ public index(): void { - this._terminal.index(); // TODO: save to move from terminal? + this._restrictCursor(); + this._bufferService.buffer.y++; + if (this._bufferService.buffer.y > this._bufferService.buffer.scrollBottom) { + this._bufferService.buffer.y--; + this._terminal.scroll(); + } + this._restrictCursor(); } /** @@ -2049,7 +2059,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._bufferService.buffer.tabs[this._bufferService.buffer.x] = true; } /** @@ -2060,7 +2070,20 @@ export class InputHandler extends Disposable implements IInputHandler { * the page scrolls down. */ public reverseIndex(): void { - this._terminal.reverseIndex(); // TODO: save to move from terminal? + this._restrictCursor(); + 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' + // 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._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom); + } else { + buffer.y--; + this._restrictCursor(); // quickfix to not run out of bounds + } } /** @@ -2086,4 +2109,31 @@ 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. + * + * TODO: move DECALN into compat addon + */ + 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._bufferService.buffer; + + this._setCursor(0, 0); + for (let yOffset = 0; yOffset < this._bufferService.rows; ++yOffset) { + const row = buffer.y + buffer.ybase + yOffset; + buffer.lines.get(row).fill(cell); + buffer.lines.get(row).isWrapped = false; + } + this._dirtyRowService.markAllDirty(); + this._setCursor(0, 0); + } } diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index f50b32a7..b83692e7 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -4,19 +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 = { @@ -750,10 +749,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(); } @@ -1019,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/Terminal.ts b/src/Terminal.ts index 1e51f439..7a068c84 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -21,21 +21,21 @@ * http://linux.die.net/man/7/urxvt */ -import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, IMouseZoneManager } from './Types'; +import { IInputHandlingTerminal, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, CustomKeyEventHandler } from './Types'; import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; -import { CompositionHelper } from './CompositionHelper'; -import { Viewport } from './Viewport'; -import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './Clipboard'; +import { CompositionHelper } from 'browser/input/CompositionHelper'; +import { Viewport } from 'browser/Viewport'; +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 { SelectionService } from './browser/services/SelectionService'; +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 './MouseZoneManager'; +import { MouseZoneManager } from 'browser/MouseZoneManager'; import { AccessibilityManager } from './AccessibilityManager'; import { ITheme, IMarker, IDisposable, ISelectionPosition } from 'xterm'; import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; @@ -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, 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'; @@ -58,6 +58,10 @@ 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'; +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; @@ -87,7 +91,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; @@ -110,6 +113,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp // common services private _bufferService: IBufferService; private _coreService: ICoreService; + private _dirtyRowService: IDirtyRowService; + private _instantiationService: IInstantiationService; + private _logService: ILogService; public optionsService: IOptionsService; // browser services @@ -146,8 +152,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; @@ -237,10 +241,18 @@ 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 = this._instantiationService.createInstance(DirtyRowService); + this._instantiationService.setService(IDirtyRowService, this._dirtyRowService); + this._logService = this._instantiationService.createInstance(LogService); + this._instantiationService.setService(ILogService, this._logService); this._setupOptionsListeners(); this._setup(); @@ -297,14 +309,12 @@ 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._dirtyRowService, this._logService, this.optionsService); this._inputHandler.onCursorMove(() => this._onCursorMove.fire()); this._inputHandler.onLineFeed(() => this._onLineFeed.fire()); this.register(this._inputHandler); - this._selectionService = this._selectionService || null; - this.linkifier = this.linkifier || new Linkifier(this); - this._mouseZoneManager = this._mouseZoneManager || null; + this.linkifier = this.linkifier || new Linkifier(this._bufferService, this._logService); if (this.options.windowsMode) { this._windowsMode = applyWindowsMode(this); @@ -384,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) { @@ -534,8 +544,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 @@ -577,11 +585,12 @@ 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'); - this._compositionHelper = new CompositionHelper(this.textarea, this._compositionView, this, 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 @@ -593,19 +602,21 @@ 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._mouseZoneManager = new MouseZoneManager(this, this._mouseService); - this.register(this._mouseZoneManager); - this.register(this.onScroll(() => this._mouseZoneManager.clearAll())); - this.linkifier.attachToDom(this._mouseZoneManager); - - this.viewport = new Viewport(this, this._viewportElement, this._viewportScrollArea, this._renderService.dimensions, this._charSizeService); + this.viewport = this._instantiationService.createInstance(Viewport, + (amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent), + this._viewportElement, + this._viewportScrollArea + ); this.viewport.onThemeChange(this._colorManager.colors); this.register(this.viewport); @@ -615,11 +626,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))); @@ -637,6 +648,11 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp })); this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this._selectionService.refresh())); + this._mouseZoneManager = this._instantiationService.createInstance(MouseZoneManager, this.element, this.screenElement); + this.register(this._mouseZoneManager); + this.register(this.onScroll(() => this._mouseZoneManager.clearAll())); + this.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) { @@ -644,12 +660,12 @@ 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 // 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 @@ -669,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}"`); } } @@ -1129,8 +1145,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); } @@ -1251,19 +1266,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; @@ -1338,19 +1343,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; @@ -1675,24 +1670,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. * @@ -1728,29 +1705,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. */ @@ -1810,46 +1764,12 @@ 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. - */ - 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--; - } - } - - /** - * ESC c Full Reset (RIS). + * 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 { /** @@ -1891,14 +1811,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/Terminal2.test.ts b/src/Terminal2.test.ts index c8164c54..cbd790d5 100644 --- a/src/Terminal2.test.ts +++ b/src/Terminal2.test.ts @@ -1,45 +1,106 @@ /** - * 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'; +import { IDisposable } from 'xterm'; -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 = [ + 't0070-DECSTBM_LF.in', // lineFeed not working correctly + 't0071-DECSTBM_IND.in', + 't0072-DECSTBM_NEL.in', + 't0075-DECSTBM_CUU_CUD.in', + 't0076-DECSTBM_IL_DL.in', // not working due to lineFeed + 't0077-DECSTBM_quirks.in', + 't0084-CBT.in', + 't0101-NLM.in', + 't0103-reverse_wrap.in', + 't0504-vim.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; + let slaveEnd: any; + let term: Terminal; + let customHandler: IDisposable | undefined; + + 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._master.end(); + ptyTerm._master.destroy(); + }); + + 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 = ''; + const OSC_CODE = 12345; + await new Promise(resolve => { + customHandler = term.addOscHandler(OSC_CODE, () => { + // 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]${OSC_CODE};\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 +112,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 +125,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,81 +133,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, '..')}); - // 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 - ]; - // These are failing on macOS only - if (os.platform() === 'darwin') { - skip.push(3, 7, 11, 67); - } - for (let i = 0; i < files.length; i++) { - if (skip.indexOf(i) >= 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]); - } - }); -} diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index 62ae609e..0ee948d5 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -4,15 +4,15 @@ */ import { IRenderer, IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types'; -import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminal, IBrowser, ITerminalOptions, ILinkifier, ILinkMatcherOptions } from './Types'; +import { IInputHandlingTerminal, ICompositionHelper, ITerminal, IBrowser, ITerminalOptions } 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'; import { Terminal } from './Terminal'; import { AttributeData } from 'common/buffer/AttributeData'; -import { IColorManager, IColorSet } 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'; @@ -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 { @@ -295,21 +297,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 { @@ -329,6 +322,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/Types.d.ts b/src/Types.d.ts index ee6815da..f96ba07d 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, IViewport } 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 @@ -58,32 +55,16 @@ export interface IInputHandlingTerminal { bell(): void; focus(): void; - updateRange(y: number): void; scroll(isWrapped?: boolean): void; setgLevel(g: number): void; eraseAttrData(): IAttributeData; 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; - tabSet(): void; handleTitle(title: string): void; - index(): void; - reverseIndex(): 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 { @@ -169,27 +150,7 @@ export interface IInputHandler { ESC | ESC } ESC ~ */ setgLevel(level: number): 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; + /** ESC # 8 */ screenAlignmentPattern(): void; } export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor { @@ -214,7 +175,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; } @@ -284,58 +244,12 @@ export interface ITerminalOptions extends IPublicTerminalOptions { [key: string]: any; cancelEvents?: boolean; convertEol?: boolean; - debug?: boolean; handler?: (data: string) => void; screenKeys?: boolean; termName?: string; useFlowControl?: boolean; } -export interface ILinkifier { - onLinkHover: IEvent; - onLinkLeave: IEvent; - onLinkTooltip: IEvent; - - attachToDom(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; @@ -346,20 +260,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/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(); } diff --git a/src/Linkifier.test.ts b/src/browser/Linkifier.test.ts similarity index 55% rename from src/Linkifier.test.ts rename to src/browser/Linkifier.test.ts index 9e6588af..128f786d 100644 --- a/src/Linkifier.test.ts +++ b/src/browser/Linkifier.test.ts @@ -4,22 +4,22 @@ */ import { assert } from 'chai'; -import { IMouseZoneManager, IMouseZone, ILinkMatcher, ITerminal } from './Types'; +import { IMouseZoneManager, IMouseZone, IRegisteredLinkMatcher } from 'browser/Types'; import { IBufferLine } from 'common/Types'; -import { Linkifier } from './Linkifier'; -import { MockBuffer, MockTerminal, TestTerminal } from './TestUtils.test'; -import { CircularList } from 'common/CircularList'; +import { Linkifier } from 'browser/Linkifier'; import { BufferLine } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; +import { MockLogService, MockBufferService } from 'common/TestUtils.test'; +import { IBufferService } from 'common/services/Services'; class TestLinkifier extends Linkifier { - constructor(terminal: ITerminal) { - super(terminal); + 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 get linkMatchers(): IRegisteredLinkMatcher[] { return this._linkMatchers; } + public linkifyRows(): void { super.linkifyRows(0, this._bufferService.buffer.lines.length - 1); } } class TestMouseZoneManager implements IMouseZoneManager { @@ -36,18 +36,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(); }); @@ -60,13 +55,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(() => { @@ -74,8 +68,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); @@ -110,7 +104,7 @@ describe('Linkifier', () => { describe('after attachToDom', () => { beforeEach(() => { - linkifier.attachToDom(mouseZoneManager); + linkifier.attachToDom({} as any, mouseZoneManager); }); describe('link matcher', () => { @@ -143,19 +137,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); }); }); @@ -163,6 +161,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) => { @@ -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(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); - }); - }); - }); }); diff --git a/src/Linkifier.ts b/src/browser/Linkifier.ts similarity index 76% rename from src/Linkifier.ts rename to src/browser/Linkifier.ts index ded6f87a..a5454ac5 100644 --- a/src/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -3,11 +3,11 @@ * @license MIT */ -import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, ITerminal, IMouseZoneManager } from './Types'; +import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, IMouseZoneManager, IMouseZone, IRegisteredLinkMatcher } 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'; /** * Limit of the unwrapping line expansion (overscan) at the top and bottom @@ -27,12 +27,14 @@ export class Linkifier implements ILinkifier { */ protected static _timeBeforeLatency = 200; - protected _linkMatchers: ILinkMatcher[] = []; + protected _linkMatchers: IRegisteredLinkMatcher[] = []; - private _mouseZoneManager: IMouseZoneManager; - private _rowsTimeoutId: number; + private _mouseZoneManager: IMouseZoneManager | undefined; + private _element: HTMLElement | undefined; + + 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; } @@ -42,11 +44,12 @@ export class Linkifier implements ILinkifier { public get onLinkTooltip(): IEvent { return this._onLinkTooltip.event; } constructor( - protected _terminal: ITerminal + protected readonly _bufferService: IBufferService, + private readonly _logService: ILogService ) { this._rowsToLinkify = { - start: null, - end: null + start: undefined, + end: undefined }; } @@ -54,7 +57,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; } @@ -70,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 { @@ -92,8 +96,13 @@ export class Linkifier implements ILinkifier { * Linkifies the rows requested. */ private _linkifyRows(): void { - this._rowsTimeoutId = null; - const buffer = this._terminal.buffer; + 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; @@ -102,7 +111,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). @@ -114,8 +123,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(); @@ -124,8 +133,8 @@ export class Linkifier implements ILinkifier { } } - this._rowsToLinkify.start = null; - this._rowsToLinkify.end = null; + this._rowsToLinkify.start = undefined; + this._rowsToLinkify.end = undefined; } /** @@ -142,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, @@ -163,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; @@ -210,11 +219,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) { - console.log({match, matcher}); - throw new Error('match found without corresponding matchIndex'); - } + this._logService.debug('match found without corresponding matchIndex', match, matcher); break; } @@ -230,19 +235,20 @@ 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 attr = line.getFg(bufferIndex[1]); - let fg: number | undefined; - if (attr) { - fg = (attr >> 9) & 0x1ff; + const line = this._bufferService.buffer.lines.get(bufferIndex[0]); + 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 @@ -250,11 +256,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,14 +273,18 @@ 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._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--; } @@ -291,7 +301,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)); @@ -301,7 +311,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(); } @@ -315,7 +325,22 @@ 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 }; + private _createLinkHoverEvent(x1: number, y1: number, x2: number, y2: number, fg: number | undefined): ILinkifierEvent { + 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/browser/MouseZoneManager.ts similarity index 73% rename from src/MouseZoneManager.ts rename to src/browser/MouseZoneManager.ts index de724b88..7eb7c5f8 100644 --- a/src/MouseZoneManager.ts +++ b/src/browser/MouseZoneManager.ts @@ -3,10 +3,11 @@ * @license MIT */ -import { ITerminal, IMouseZoneManager, IMouseZone } 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; @@ -26,18 +27,21 @@ 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 _terminal: ITerminal, - private _mouseService: IMouseService + private readonly _element: HTMLElement, + private readonly _screenElement: HTMLElement, + @IBufferService private readonly _bufferService: IBufferService, + @IMouseService private readonly _mouseService: IMouseService, + @ISelectionService 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); @@ -64,9 +68,9 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { } // Clear all if start/end weren't set - if (!end) { + if (!start || !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 @@ -77,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); } @@ -92,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); } } @@ -129,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); } @@ -151,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); @@ -161,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) { @@ -184,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); } @@ -195,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); @@ -204,10 +208,15 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { } } - private _findZoneEventAt(e: MouseEvent): IMouseZone { - const coords = this._mouseService.getCoords(e, this._terminal.screenElement, this._terminal.cols, this._terminal.rows); + private _getSelectionLength(): number { + const selectionText = this._selectionService.selectionText; + return selectionText ? selectionText.length : 0; + } + + 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]; @@ -227,21 +236,6 @@ 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 - ) { + return undefined; } } diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index d89dc391..60861112 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/Types.d.ts b/src/browser/Types.d.ts index ef725ba6..c9167316 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,103 @@ export interface IColorSet { selection: IColor; 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; + +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 IRegisteredLinkMatcher extends ILinkMatcher { + priority: number; +} + +export interface ILinkifierEvent { + x1: number; + y1: number; + x2: number; + y2: number; + cols: number; + fg: number | undefined; +} + +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/Viewport.ts b/src/browser/Viewport.ts similarity index 82% rename from src/Viewport.ts rename to src/browser/Viewport.ts index cd5a282c..9625588d 100644 --- a/src/Viewport.ts +++ b/src/browser/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; @@ -22,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 @@ -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, + @IBufferService private readonly _bufferService: IBufferService, + @ICharSizeService private readonly _charSizeService: ICharSizeService, + @IRenderService 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/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 89% rename from src/CompositionHelper.ts rename to src/browser/input/CompositionHelper.ts index 010585f8..e6994ea3 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 _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; - 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/CharSizeService.ts b/src/browser/services/CharSizeService.ts index 42920bfa..0749a420 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..b0f8c358 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..ecf20e28 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.test.ts b/src/browser/services/SelectionService.test.ts index fc9cafdd..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 '../../../out/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/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 6a9498a1..e4c703eb 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..863f3a9e 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..1772c750 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 ) { } 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": [ diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index e3a6c9b9..ec36be2f 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, IDirtyRowService } from 'common/services/Services'; import { IEvent, EventEmitter } from 'common/EventEmitter'; import { clone } from 'common/Clone'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; @@ -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; @@ -36,7 +38,26 @@ export class MockCoreService implements ICoreService { triggerDataEvent(data: string, wasUserInput?: boolean): void {} } +export class MockDirtyRowService implements IDirtyRowService { + serviceBrand: any; + 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 { + serviceBrand: any; + 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 { + serviceBrand: any; options: ITerminalOptions = clone(DEFAULT_OPTIONS); onOptionChange: IEvent = new EventEmitter().event; constructor(testOptions?: IPartialTerminalOptions) { 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/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[]; diff --git a/src/common/parser/EscapeSequenceParser.ts b/src/common/parser/EscapeSequenceParser.ts index 364ba803..1d2b7e1e 100644 --- a/src/common/parser/EscapeSequenceParser.ts +++ b/src/common/parser/EscapeSequenceParser.ts @@ -466,10 +466,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; @@ -488,10 +488,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; @@ -504,6 +500,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) and : (0x3a) @@ -529,10 +526,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 = ''; @@ -541,7 +538,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); @@ -569,6 +565,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP params.reset(); params.addParam(0); // ZDM collect = ''; + this.precedingCodepoint = 0; break; case ParserAction.OSC_START: osc = ''; @@ -584,7 +581,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 @@ -616,6 +612,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP params.reset(); params.addParam(0); // ZDM collect = ''; + this.precedingCodepoint = 0; break; } currentState = transition & TableAccess.TRANSITION_STATE_MASK; diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 6ff08061..c7b6afce 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; @@ -18,7 +20,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..674cfedb 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(); @@ -23,8 +25,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 new file mode 100644 index 00000000..0f2f14f5 --- /dev/null +++ b/src/common/services/DirtyRowService.ts @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IBufferService, IDirtyRowService } from 'common/services/Services'; + +export class DirtyRowService implements IDirtyRowService { + serviceBrand: any; + + private _start!: number; + private _end!: number; + + public get start(): number { return this._start; } + public get end(): number { return this._end; } + + constructor( + @IBufferService 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/InstantiationService.ts b/src/common/services/InstantiationService.ts new file mode 100644 index 00000000..abce8ac5 --- /dev/null +++ b/src/common/services/InstantiationService.ts @@ -0,0 +1,77 @@ +/** + * 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'; + +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); + + 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); + } + + 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/LogService.ts b/src/common/services/LogService.ts new file mode 100644 index 00000000..6740ad4a --- /dev/null +++ b/src/common/services/LogService.ts @@ -0,0 +1,82 @@ +/** + * 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 +}; + +const LOG_PREFIX = 'xterm.js: '; + +export class LogService implements ILogService { + serviceBrand: any; + + private _logLevel!: LogLevel; + + constructor( + @IOptionsService 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: string, ...optionalParams: any[]): void { + if (this._logLevel <= LogLevel.DEBUG) { + console.log.call(console, LOG_PREFIX + message, ...optionalParams); + } + } + + info(message: string, ...optionalParams: any[]): void { + if (this._logLevel <= LogLevel.INFO) { + console.info.call(console, LOG_PREFIX + message, ...optionalParams); + } + } + + warn(message: string, ...optionalParams: any[]): void { + if (this._logLevel <= LogLevel.WARN) { + console.warn.call(console, LOG_PREFIX + message, ...optionalParams); + } + } + + error(message: string, ...optionalParams: any[]): void { + if (this._logLevel <= LogLevel.ERROR) { + console.error.call(console, LOG_PREFIX + message, ...optionalParams); + } + } +} diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index ab041488..9a5d2151 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, @@ -44,7 +45,6 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ convertEol: false, termName: 'xterm', screenKeys: false, - debug: false, cancelEvents: false, useFlowControl: false, wordSeparator: ' ()[]{}\'"' @@ -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/ServiceRegistry.ts b/src/common/services/ServiceRegistry.ts new file mode 100644 index 00000000..450af492 --- /dev/null +++ b/src/common/services/ServiceRegistry.ts @@ -0,0 +1,49 @@ +/** + * 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'; + +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.d.ts deleted file mode 100644 index f46f3d76..00000000 --- a/src/common/services/Services.d.ts +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright (c) 2019 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { IEvent } from 'common/EventEmitter'; -import { IBuffer, IBufferSet } from 'common/buffer/Types'; -import { IDecPrivateModes } from 'common/Types'; - -export interface IBufferService { - readonly cols: number; - readonly rows: number; - readonly buffer: IBuffer; - readonly buffers: IBufferSet; - - // TODO: Move resize event here - - resize(cols: number, rows: number): void; - reset(): void; -} - -export interface ICoreService { - readonly decPrivateModes: IDecPrivateModes; - - readonly onData: IEvent; - readonly onUserInput: IEvent; - - reset(): void; - - /** - * Triggers the onData event in the public API. - * @param data The data that is being emitted. - * @param wasFromUser Whether the data originated from the user (as opposed to - * resulting from parsing incoming data). When true this will also: - * - Scroll to the bottom of the buffer.s - * - Fire the `onUserInput` event (so selection can be cleared). - */ - triggerDataEvent(data: string, wasUserInput?: boolean): void; -} - -export interface IOptionsService { - readonly options: ITerminalOptions; - - readonly onOptionChange: IEvent; - - setOption(key: string, value: T): void; - getOption(key: string): T | undefined; -} - -export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'; - -export type RendererType = 'dom' | 'canvas'; - -export interface IPartialTerminalOptions { - allowTransparency?: boolean; - bellSound?: string; - bellStyle?: 'none' /*| 'visual'*/ | 'sound' /*| 'both'*/; - cols?: number; - cursorBlink?: boolean; - cursorStyle?: 'block' | 'underline' | 'bar'; - disableStdin?: boolean; - drawBoldTextInBrightColors?: boolean; - fontSize?: number; - fontFamily?: string; - fontWeight?: FontWeight; - fontWeightBold?: FontWeight; - letterSpacing?: number; - lineHeight?: number; - macOptionIsMeta?: boolean; - macOptionClickForcesSelection?: boolean; - rendererType?: RendererType; - rightClickSelectsWord?: boolean; - rows?: number; - screenReaderMode?: boolean; - scrollback?: number; - tabStopWidth?: number; - theme?: ITheme; - windowsMode?: boolean; - wordSeparator?: string; -} - -export interface ITerminalOptions { - allowTransparency: boolean; - bellSound: string; - bellStyle: 'none' /*| 'visual'*/ | 'sound' /*| 'both'*/; - cols: number; - cursorBlink: boolean; - cursorStyle: 'block' | 'underline' | 'bar'; - disableStdin: boolean; - drawBoldTextInBrightColors: boolean; - fontSize: number; - fontFamily: string; - fontWeight: FontWeight; - fontWeightBold: FontWeight; - letterSpacing: number; - lineHeight: number; - macOptionIsMeta: boolean; - macOptionClickForcesSelection: boolean; - rendererType: RendererType; - rightClickSelectsWord: boolean; - rows: number; - screenReaderMode: boolean; - scrollback: number; - tabStopWidth: number; - theme: ITheme; - windowsMode: boolean; - wordSeparator: string; - - [key: string]: any; - cancelEvents: boolean; - convertEol: boolean; - debug: boolean; - screenKeys: boolean; - termName: string; - useFlowControl: boolean; -} - -export interface ITheme { - foreground?: string; - background?: string; - cursor?: string; - cursorAccent?: string; - selection?: string; - black?: string; - red?: string; - green?: string; - yellow?: string; - blue?: string; - magenta?: string; - cyan?: string; - white?: string; - brightBlack?: string; - brightRed?: string; - brightGreen?: string; - brightYellow?: string; - brightBlue?: string; - brightMagenta?: string; - brightCyan?: string; - brightWhite?: string; -} diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts new file mode 100644 index 00000000..1af55e9a --- /dev/null +++ b/src/common/services/Services.ts @@ -0,0 +1,230 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +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 { + serviceBrand: any; + + readonly cols: number; + readonly rows: number; + readonly buffer: IBuffer; + readonly buffers: IBufferSet; + + // TODO: Move resize event here + + resize(cols: number, rows: number): void; + reset(): void; +} + +export const ICoreService = createDecorator('CoreService'); +export interface ICoreService { + serviceBrand: any; + + readonly decPrivateModes: IDecPrivateModes; + + readonly onData: IEvent; + readonly onUserInput: IEvent; + + reset(): void; + + /** + * Triggers the onData event in the public API. + * @param data The data that is being emitted. + * @param wasFromUser Whether the data originated from the user (as opposed to + * resulting from parsing incoming data). When true this will also: + * - Scroll to the bottom of the buffer.s + * - Fire the `onUserInput` event (so selection can be cleared). + */ + triggerDataEvent(data: string, wasUserInput?: boolean): void; +} + +export const IDirtyRowService = createDecorator('DirtyRowService'); +export interface IDirtyRowService { + serviceBrand: any; + + readonly start: number; + readonly end: number; + + clearRange(): void; + markDirty(y: number): void; + markRangeDirty(y1: number, y2: number): void; + markAllDirty(): void; +} + +export interface IServiceIdentifier { + (...args: any[]): void; + 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: 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; + error(message: any, ...optionalParams: any[]): void; +} + +export const IOptionsService = createDecorator('OptionsService'); +export interface IOptionsService { + serviceBrand: any; + + readonly options: ITerminalOptions; + + readonly onOptionChange: IEvent; + + setOption(key: string, value: T): void; + getOption(key: string): T | undefined; +} + +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 { + allowTransparency?: boolean; + bellSound?: string; + bellStyle?: 'none' /*| 'visual'*/ | 'sound' /*| 'both'*/; + cols?: number; + cursorBlink?: boolean; + cursorStyle?: 'block' | 'underline' | 'bar'; + disableStdin?: boolean; + drawBoldTextInBrightColors?: boolean; + fontSize?: number; + fontFamily?: string; + fontWeight?: FontWeight; + fontWeightBold?: FontWeight; + letterSpacing?: number; + lineHeight?: number; + logLevel?: LogLevel; + macOptionIsMeta?: boolean; + macOptionClickForcesSelection?: boolean; + rendererType?: RendererType; + rightClickSelectsWord?: boolean; + rows?: number; + screenReaderMode?: boolean; + scrollback?: number; + tabStopWidth?: number; + theme?: ITheme; + windowsMode?: boolean; + wordSeparator?: string; +} + +export interface ITerminalOptions { + allowTransparency: boolean; + bellSound: string; + bellStyle: 'none' /*| 'visual'*/ | 'sound' /*| 'both'*/; + cols: number; + cursorBlink: boolean; + cursorStyle: 'block' | 'underline' | 'bar'; + disableStdin: boolean; + drawBoldTextInBrightColors: boolean; + fontSize: number; + fontFamily: string; + fontWeight: FontWeight; + fontWeightBold: FontWeight; + letterSpacing: number; + lineHeight: number; + logLevel: LogLevel; + macOptionIsMeta: boolean; + macOptionClickForcesSelection: boolean; + rendererType: RendererType; + rightClickSelectsWord: boolean; + rows: number; + screenReaderMode: boolean; + scrollback: number; + tabStopWidth: number; + theme: ITheme; + windowsMode: boolean; + wordSeparator: string; + + [key: string]: any; + cancelEvents: boolean; + convertEol: boolean; + screenKeys: boolean; + termName: string; + useFlowControl: boolean; +} + +export interface ITheme { + foreground?: string; + background?: string; + cursor?: string; + cursorAccent?: string; + selection?: string; + black?: string; + red?: string; + green?: string; + yellow?: string; + blue?: string; + magenta?: string; + cyan?: string; + white?: string; + brightBlack?: string; + brightRed?: string; + brightGreen?: string; + brightYellow?: string; + brightBlue?: string; + brightMagenta?: string; + brightCyan?: string; + brightWhite?: string; +} 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/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/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/Renderer.ts b/src/renderer/Renderer.ts index a6e8fca7..2ed61b76 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 '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(bufferService); this._renderLayers = [ new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency), diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index a6b6e868..22b50349 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'; @@ -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 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/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')`); diff --git a/tslint.json b/tslint.json index a18111bf..cd790c14 100644 --- a/tslint.json +++ b/tslint.json @@ -20,6 +20,10 @@ true, "spaces" ], + "import-blacklist": [ + true, + [".*\\/out\\/.*"] + ], "interface-name": [ true, "always-prefix" @@ -71,12 +75,6 @@ "variable-declaration": "nospace" } ], - "variable-name": [ - true, - "ban-keywords", - "check-format", - "allow-leading-underscore" - ], "whitespace": [ true, "check-branch", @@ -99,6 +97,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": { diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index c915a4ba..d9e28b26 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. */ @@ -676,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. @@ -715,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. @@ -732,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. diff --git a/yarn.lock b/yarn.lock index 1ac3af13..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" @@ -330,20 +332,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" - -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" + mime-types "~2.1.24" + negotiator "0.6.2" acorn-globals@^4.1.0: version "4.1.0" @@ -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" @@ -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" @@ -618,21 +604,42 @@ 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" 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" @@ -657,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" @@ -760,23 +774,29 @@ 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= -cacache@^10.0.4: - version "10.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" - integrity sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA== +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@^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: @@ -818,17 +838,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" @@ -877,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" @@ -1065,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" @@ -1117,12 +1126,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== @@ -1132,10 +1143,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" @@ -1293,13 +1304,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" @@ -1385,7 +1389,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= @@ -1503,6 +1507,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" @@ -1574,7 +1583,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= @@ -1626,10 +1635,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" @@ -1683,43 +1692,48 @@ 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.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" @@ -1737,9 +1751,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" @@ -1821,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" @@ -1838,24 +1857,34 @@ 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= +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: - debug "~2.2.0" + 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" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + 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: - 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" @@ -1905,7 +1934,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= @@ -1922,10 +1951,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" @@ -2026,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== @@ -2050,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= @@ -2078,13 +2124,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" @@ -2175,13 +2214,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" @@ -2210,7 +2263,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== @@ -2224,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== @@ -2277,6 +2330,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" @@ -2321,10 +2379,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" @@ -2454,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" @@ -2495,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" @@ -2527,17 +2595,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== @@ -2612,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" @@ -2694,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" @@ -2720,15 +2799,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" @@ -2748,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== @@ -2766,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" @@ -2877,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" @@ -2885,22 +2965,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" @@ -2959,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" @@ -2970,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" @@ -3031,11 +3123,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" @@ -3082,10 +3169,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" @@ -3306,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" @@ -3442,10 +3524,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" @@ -3508,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" @@ -3520,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" @@ -3555,13 +3649,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" @@ -3594,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== @@ -3662,10 +3756,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" @@ -3697,10 +3791,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" @@ -3871,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" @@ -3907,7 +4018,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== @@ -3929,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: @@ -3942,70 +4054,54 @@ 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== -send@0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.13.1.tgz#a30d5f4c82c8a9bae9ad00a1d9b1bdbe6f199ed7" - integrity sha1-ow1fTILIqbrprQCh2bG9vm8Zntc= +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" + 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" + range-parser "~1.2.1" + statuses "~1.5.0" -send@0.13.2: - version "0.13.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.13.2.tgz#765e7607c8055452bba6f0b052595350986036de" - integrity sha1-dl52B8gFVFK7pvCwUllTUJhgNt4= +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" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== dependencies: - debug "~2.2.0" - depd "~1.1.0" - 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" - on-finished "~2.3.0" - range-parser "~1.0.3" - statuses "~1.2.1" - -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= - dependencies: - 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" @@ -4042,6 +4138,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" @@ -4135,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" @@ -4178,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" @@ -4198,16 +4307,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" @@ -4323,11 +4427,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" @@ -4352,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" @@ -4365,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" @@ -4422,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" @@ -4432,6 +4568,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" @@ -4459,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" @@ -4503,25 +4644,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== @@ -4569,13 +4711,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" @@ -4592,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" @@ -4629,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" @@ -4643,7 +4758,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= @@ -4712,10 +4827,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" @@ -4727,10 +4842,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" @@ -4793,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" @@ -4834,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" @@ -4908,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" @@ -4928,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" @@ -4944,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"