From 57a9948e8d3d6cc712a2848154f9816c21a05668 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 18 Jan 2018 13:25:11 -0800 Subject: [PATCH] Use Terminal typings in remaining addons Fixes #1220 --- src/addons/terminado/Intefaces.ts | 19 ++++ src/addons/terminado/terminado.test.ts | 2 +- src/addons/terminado/terminado.ts | 93 ++++++++++---------- src/addons/winptyCompat/Interfaces.ts | 10 +++ src/addons/winptyCompat/winptyCompat.test.ts | 2 +- src/addons/winptyCompat/winptyCompat.ts | 61 +++++++------ src/addons/zmodem/zmodem.test.ts | 2 +- src/addons/zmodem/zmodem.ts | 21 +++-- 8 files changed, 128 insertions(+), 82 deletions(-) create mode 100644 src/addons/terminado/Intefaces.ts create mode 100644 src/addons/winptyCompat/Interfaces.ts diff --git a/src/addons/terminado/Intefaces.ts b/src/addons/terminado/Intefaces.ts new file mode 100644 index 000000000..8f17b0cc7 --- /dev/null +++ b/src/addons/terminado/Intefaces.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + * + * Implements the attach method, that attaches the terminal to a WebSocket stream. + */ + +import { Terminal } from 'xterm'; + +export interface ITerminadoAddonTerminal extends Terminal { + __socket?: WebSocket; + __attachSocketBuffer?: string; + + __getMessage?(ev: MessageEvent): void; + __flushBuffer?(): void; + __pushToBuffer?(data: string): void; + __sendData?(data: string): void; + __setSize?(size: {rows: number, cols: number}): void; +} diff --git a/src/addons/terminado/terminado.test.ts b/src/addons/terminado/terminado.test.ts index e258bd7cb..2e4a53c5a 100644 --- a/src/addons/terminado/terminado.test.ts +++ b/src/addons/terminado/terminado.test.ts @@ -12,7 +12,7 @@ class MockTerminal {} describe('terminado addon', () => { describe('apply', () => { it('should do register the `terminadoAttach` and `terminadoDetach` methods', () => { - terminado.apply(MockTerminal); + terminado.apply(MockTerminal); assert.equal(typeof (MockTerminal).prototype.terminadoAttach, 'function'); assert.equal(typeof (MockTerminal).prototype.terminadoDetach, 'function'); }); diff --git a/src/addons/terminado/terminado.ts b/src/addons/terminado/terminado.ts index e703198fe..d5cf72d1b 100644 --- a/src/addons/terminado/terminado.ts +++ b/src/addons/terminado/terminado.ts @@ -6,106 +6,107 @@ * WebSocket stream. */ +/// + +import { Terminal } from 'xterm'; +import { ITerminadoAddonTerminal } from './Intefaces'; + /** * Attaches the given terminal to the given socket. * - * @param {Terminal} term - The terminal to be attached to the given socket. - * @param {WebSocket} socket - The socket to attach the current terminal. - * @param {boolean} bidirectional - Whether the terminal should send data - * to the socket as well. - * @param {boolean} buffered - Whether the rendering of incoming data - * should happen instantly or at a maximum - * frequency of 1 rendering per 10ms. + * @param term The terminal to be attached to the given socket. + * @param socket The socket to attach the current terminal. + * @param bidirectional Whether the terminal should send data to the socket as well. + * @param buffered Whether the rendering of incoming data should happen instantly or at a maximum + * frequency of 1 rendering per 10ms. */ -export function terminadoAttach(term: any, socket: WebSocket, bidirectional: boolean, buffered: boolean): void { +export function terminadoAttach(term: Terminal, socket: WebSocket, bidirectional: boolean, buffered: boolean): void { + const addonTerminal = term; bidirectional = (typeof bidirectional === 'undefined') ? true : bidirectional; - term.socket = socket; + addonTerminal.__socket = socket; - term._flushBuffer = () => { - term.write(term._attachSocketBuffer); - term._attachSocketBuffer = null; + addonTerminal.__flushBuffer = () => { + addonTerminal.write(addonTerminal.__attachSocketBuffer); + addonTerminal.__attachSocketBuffer = null; }; - term._pushToBuffer = (data) => { - if (term._attachSocketBuffer) { - term._attachSocketBuffer += data; + addonTerminal.__pushToBuffer = (data: string) => { + if (addonTerminal.__attachSocketBuffer) { + addonTerminal.__attachSocketBuffer += data; } else { - term._attachSocketBuffer = data; - setTimeout(term._flushBuffer, 10); + addonTerminal.__attachSocketBuffer = data; + setTimeout(addonTerminal.__flushBuffer, 10); } }; - term._getMessage = (ev: MessageEvent) => { + addonTerminal.__getMessage = (ev: MessageEvent) => { const data = JSON.parse(ev.data); if (data[0] === 'stdout') { if (buffered) { - term._pushToBuffer(data[1]); + addonTerminal.__pushToBuffer(data[1]); } else { - term.write(data[1]); + addonTerminal.write(data[1]); } } }; - term._sendData = (data: string) => { + addonTerminal.__sendData = (data: string) => { socket.send(JSON.stringify(['stdin', data])); }; - term._setSize = (size: {rows: number, cols: number}) => { + addonTerminal.__setSize = (size: {rows: number, cols: number}) => { socket.send(JSON.stringify(['set_size', size.rows, size.cols])); }; - socket.addEventListener('message', term._getMessage); + socket.addEventListener('message', addonTerminal.__getMessage); if (bidirectional) { - term.on('data', term._sendData); + addonTerminal.on('data', addonTerminal.__sendData); } - term.on('resize', term._setSize); + addonTerminal.on('resize', addonTerminal.__setSize); - socket.addEventListener('close', term.terminadoDetach.bind(term, socket)); - socket.addEventListener('error', term.terminadoDetach.bind(term, socket)); + socket.addEventListener('close', () => terminadoDetach(addonTerminal, socket)); + socket.addEventListener('error', () => terminadoDetach(addonTerminal, socket)); } /** * Detaches the given terminal from the given socket * - * @param {Xterm} term - The terminal to be detached from the given socket. - * @param {WebSocket} socket - The socket from which to detach the current - * terminal. + * @param term The terminal to be detached from the given socket. + * @param socket The socket from which to detach the current terminal. */ -export function terminadoDetach(term: any, socket: WebSocket): void { - term.off('data', term._sendData); +export function terminadoDetach(term: Terminal, socket: WebSocket): void { + const addonTerminal = term; + addonTerminal.off('data', addonTerminal.__sendData); - socket = (typeof socket === 'undefined') ? term.socket : socket; + socket = (typeof socket === 'undefined') ? addonTerminal.__socket : socket; if (socket) { - socket.removeEventListener('message', term._getMessage); + socket.removeEventListener('message', addonTerminal.__getMessage); } - delete term.socket; + delete addonTerminal.__socket; } -export function apply(terminalConstructor: any): void { +export function apply(terminalConstructor: typeof Terminal): void { /** * Attaches the current terminal to the given socket * - * @param {WebSocket} socket - The socket to attach the current terminal. - * @param {boolean} bidirectional - Whether the terminal should send data - * to the socket as well. - * @param {boolean} buffered - Whether the rendering of incoming data - * should happen instantly or at a maximum - * frequency of 1 rendering per 10ms. + * @param socket - The socket to attach the current terminal. + * @param bidirectional - Whether the terminal should send data to the socket as well. + * @param buffered - Whether the rendering of incoming data should happen instantly or at a + * maximum frequency of 1 rendering per 10ms. */ - terminalConstructor.prototype.terminadoAttach = function(socket: WebSocket, bidirectional: boolean, buffered: boolean): void { + (terminalConstructor.prototype).terminadoAttach = function (socket: WebSocket, bidirectional: boolean, buffered: boolean): void { return terminadoAttach(this, socket, bidirectional, buffered); }; /** * Detaches the current terminal from the given socket. * - * @param {WebSocket} socket - The socket from which to detach the current - * terminal. + * @param socket The socket from which to detach the current terminal. */ - terminalConstructor.prototype.terminadoDetach = function(socket: WebSocket): void { + (terminalConstructor.prototype).terminadoDetach = function (socket: WebSocket): void { return terminadoDetach(this, socket); }; } diff --git a/src/addons/winptyCompat/Interfaces.ts b/src/addons/winptyCompat/Interfaces.ts new file mode 100644 index 000000000..8e02c64b5 --- /dev/null +++ b/src/addons/winptyCompat/Interfaces.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { Terminal } from 'xterm'; + +export interface IWinptyCompatAddonTerminal extends Terminal { + buffer: any; +} diff --git a/src/addons/winptyCompat/winptyCompat.test.ts b/src/addons/winptyCompat/winptyCompat.test.ts index 73761fc90..0c9269edc 100644 --- a/src/addons/winptyCompat/winptyCompat.test.ts +++ b/src/addons/winptyCompat/winptyCompat.test.ts @@ -12,7 +12,7 @@ class MockTerminal {} describe('winptyCompat addon', () => { describe('apply', () => { it('should do register the `winptyCompatInit` method', () => { - winptyCompat.apply(MockTerminal); + winptyCompat.apply(MockTerminal); assert.equal(typeof (MockTerminal).prototype.winptyCompatInit, 'function'); }); }); diff --git a/src/addons/winptyCompat/winptyCompat.ts b/src/addons/winptyCompat/winptyCompat.ts index b594355bc..61524e125 100644 --- a/src/addons/winptyCompat/winptyCompat.ts +++ b/src/addons/winptyCompat/winptyCompat.ts @@ -3,36 +3,43 @@ * @license MIT */ -export function winptyCompatInit(terminal: any): void { - // Don't do anything when the platform is not Windows - const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0; - if (!isWindows) { - return; +/// + +import { Terminal } from 'xterm'; +import { IWinptyCompatAddonTerminal } from './Interfaces'; + +export function winptyCompatInit(terminal: Terminal): void { + const addonTerminal = terminal; + + // Don't do anything when the platform is not Windows + const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0; + if (!isWindows) { + return; + } + + // Winpty does not support wraparound mode which means that lines will never + // be marked as wrapped. This causes issues for things like copying a line + // retaining the wrapped new line characters or if consumers are listening + // in on the data stream. + // + // The workaround for this is to listen to every incoming line feed and mark + // the line as wrapped if the last character in the previous line is not a + // space. This is certainly not without its problems, but generally on + // Windows when text reaches the end of the terminal it's likely going to be + // wrapped. + addonTerminal.on('linefeed', () => { + const line = addonTerminal.buffer.lines.get(addonTerminal.buffer.ybase + addonTerminal.buffer.y - 1); + const lastChar = line[addonTerminal.cols - 1]; + + if (lastChar[3] !== 32 /* ' ' */) { + const nextLine = addonTerminal.buffer.lines.get(addonTerminal.buffer.ybase + addonTerminal.buffer.y); + (nextLine).isWrapped = true; } - - // Winpty does not support wraparound mode which means that lines will never - // be marked as wrapped. This causes issues for things like copying a line - // retaining the wrapped new line characters or if consumers are listening - // in on the data stream. - // - // The workaround for this is to listen to every incoming line feed and mark - // the line as wrapped if the last character in the previous line is not a - // space. This is certainly not without its problems, but generally on - // Windows when text reaches the end of the terminal it's likely going to be - // wrapped. - terminal.on('linefeed', () => { - const line = terminal.buffer.lines.get(terminal.buffer.ybase + terminal.buffer.y - 1); - const lastChar = line[terminal.cols - 1]; - - if (lastChar[3] !== 32 /* ' ' */) { - const nextLine = terminal.buffer.lines.get(terminal.buffer.ybase + terminal.buffer.y); - (nextLine).isWrapped = true; - } - }); + }); } -export function apply(terminalConstructor: any): void { - terminalConstructor.prototype.winptyCompatInit = function(): void { +export function apply(terminalConstructor: typeof Terminal): void { + (terminalConstructor.prototype).winptyCompatInit = function (): void { winptyCompatInit(this); }; } diff --git a/src/addons/zmodem/zmodem.test.ts b/src/addons/zmodem/zmodem.test.ts index 7646b7ccb..682e62c80 100644 --- a/src/addons/zmodem/zmodem.test.ts +++ b/src/addons/zmodem/zmodem.test.ts @@ -12,7 +12,7 @@ class MockTerminal {} describe('zmodem addon', () => { describe('apply', () => { it('should do register the `zmodemAttach` method and `zmodemBrowser` attribute', () => { - zmodem.apply(MockTerminal); + zmodem.apply(MockTerminal); assert.equal(typeof (MockTerminal).prototype.zmodemAttach, 'function'); assert.equal(typeof (MockTerminal).prototype.zmodemBrowser, 'object'); }); diff --git a/src/addons/zmodem/zmodem.ts b/src/addons/zmodem/zmodem.ts index 4713f111c..e8a7824d4 100644 --- a/src/addons/zmodem/zmodem.ts +++ b/src/addons/zmodem/zmodem.ts @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +/// + +import { Terminal } from 'xterm'; + /** * * Allow xterm.js to handle ZMODEM uploads and downloads. @@ -33,7 +42,7 @@ export interface IZModemOptions { noTerminalWriteOutsideSession?: boolean; } -export function zmodemAttach(term: any, ws: WebSocket, opts: IZModemOptions = {}): void { +export function zmodemAttach(term: Terminal, ws: WebSocket, opts: IZModemOptions = {}): void { const senderFunc = (octets: ArrayLike) => ws.send(new Uint8Array(octets)); let zsentry; @@ -51,8 +60,8 @@ export function zmodemAttach(term: any, ws: WebSocket, opts: IZModemOptions = {} } }, sender: senderFunc, - on_retract: () => term.emit('zmodemRetract'), - on_detect: (detection: any) => term.emit('zmodemDetect', detection) + on_retract: () => (term).emit('zmodemRetract'), + on_detect: (detection: any) => (term).emit('zmodemDetect', detection) }); function handleWSMessage(evt: MessageEvent): void { @@ -75,9 +84,9 @@ export function zmodemAttach(term: any, ws: WebSocket, opts: IZModemOptions = {} ws.addEventListener('message', handleWSMessage); } -export function apply(terminalConstructor: any): void { +export function apply(terminalConstructor: typeof Terminal): void { zmodem = (typeof window === 'object') ? (window).ZModem : {Browser: null}; // Nullify browser for tests - terminalConstructor.prototype.zmodemAttach = zmodemAttach.bind(this, this); - terminalConstructor.prototype.zmodemBrowser = zmodem.Browser; + (terminalConstructor.prototype).zmodemAttach = zmodemAttach.bind(this, this); + (terminalConstructor.prototype).zmodemBrowser = zmodem.Browser; }