diff --git a/demo/main.js b/demo/main.js index 7a114383..37bd8a7f 100644 --- a/demo/main.js +++ b/demo/main.js @@ -33,6 +33,8 @@ function setPadding() { term.fit(); } +createTerminal(); + addDomListener(paddingElement, 'change', setPadding); addDomListener(actionElements.findNext, 'keypress', function (e) { @@ -48,8 +50,6 @@ addDomListener(actionElements.findPrevious, 'keypress', function (e) { } }); -createTerminal(); - function createTerminal() { // Clean terminal while (terminalContainer.children.length) { @@ -76,11 +76,14 @@ function createTerminal() { term.fit(); term.focus(); - document.getElementById('dispose').addEventListener('click', () => { + const buttonHandler = () => { term.dispose(); term = null; window.term = null; - }); + socket = null; + document.getElementById('dispose').removeEventListener('click', buttonHandler); + }; + document.getElementById('dispose').addEventListener('click', buttonHandler); // fit is called within a setTimeout, cols and rows need this. setTimeout(function () { @@ -130,7 +133,7 @@ function runFakeTerminal() { term.writeln(''); term.prompt(); - term.on('key', function (key, ev) { + term._core.register(term.addDisposableListener('key', function (key, ev) { var printable = ( !ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey ); @@ -145,11 +148,11 @@ function runFakeTerminal() { } else if (printable) { term.write(key); } - }); + })); - term.on('paste', function (data, ev) { + term._core,register(term.addDisposableListener('paste', function (data, ev) { term.write(data); - }); + })); } function initOptions(term) { diff --git a/src/EscapeSequenceParser.ts b/src/EscapeSequenceParser.ts index 2849dbc1..8995b203 100644 --- a/src/EscapeSequenceParser.ts +++ b/src/EscapeSequenceParser.ts @@ -4,6 +4,7 @@ */ import { ParserState, ParserAction, IParsingState, IDcsHandler, IEscapeSequenceParser } from './Types'; +import { Disposable } from './common/Lifecycle'; /** * Returns an array filled with numbers between the low and high parameters (right exclusive). @@ -207,7 +208,7 @@ class DcsDummy implements IDcsHandler { * NOTE: The parameter element notation is currently not supported. * TODO: implement error recovery hook via error handler return values */ -export class EscapeSequenceParser implements IEscapeSequenceParser { +export class EscapeSequenceParser extends Disposable implements IEscapeSequenceParser { public initialState: number; public currentState: number; @@ -236,6 +237,8 @@ export class EscapeSequenceParser implements IEscapeSequenceParser { protected _errorHandlerFb: (state: IParsingState) => IParsingState; constructor(readonly TRANSITIONS: TransitionTable = VT500_TRANSITION_TABLE) { + super(); + this.initialState = ParserState.GROUND; this.currentState = this.initialState; this._osc = ''; @@ -260,6 +263,24 @@ export class EscapeSequenceParser implements IEscapeSequenceParser { this._errorHandler = this._errorHandlerFb; } + public dispose(): void { + this._printHandlerFb = null; + this._executeHandlerFb = null; + this._csiHandlerFb = null; + this._escHandlerFb = null; + this._oscHandlerFb = null; + this._dcsHandlerFb = null; + this._errorHandlerFb = null; + this._printHandler = null; + this._executeHandlers = null; + this._csiHandlers = null; + this._escHandlers = null; + this._oscHandlers = null; + this._dcsHandlers = null; + this._activeDcsHandler = null; + this._errorHandler = null; + } + setPrintHandler(callback: (data: string, start: number, end: number) => void): void { this._printHandler = callback; } diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 9df24c7d..089c2d0d 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -12,6 +12,7 @@ import { FLAGS } from './renderer/Types'; import { wcwidth } from './CharWidth'; import { EscapeSequenceParser } from './EscapeSequenceParser'; import { ICharset } from './core/Types'; +import { Disposable } from './common/Lifecycle'; /** * Map collect to glevel. Used in `selectCharset`. @@ -111,13 +112,17 @@ class DECRQSS implements IDcsHandler { * Refer to http://invisible-island.net/xterm/ctlseqs/ctlseqs.html to understand * each function's header comment. */ -export class InputHandler implements IInputHandler { +export class InputHandler extends Disposable implements IInputHandler { private _surrogateHigh: string; constructor( private _terminal: any, // TODO: reestablish IInputHandlingTerminal here private _parser: IEscapeSequenceParser = new EscapeSequenceParser()) { + super(); + + this.register(this._parser); + this._surrogateHigh = ''; /** @@ -285,6 +290,11 @@ export class InputHandler implements IInputHandler { this._parser.setDcsHandler('+q', new RequestTerminfo(this._terminal)); } + public dispose(): void { + super.dispose(); + this._terminal = null; + } + public parse(data: string): void { let buffer = this._terminal.buffer; const cursorStartX = buffer.x; diff --git a/src/Terminal.ts b/src/Terminal.ts index ca2e6dab..0e7bf210 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -300,6 +300,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II this._userScrolling = false; this._inputHandler = new InputHandler(this); + this.register(this._inputHandler); // Reuse renderer if the Terminal is being recreated via a reset call. this.renderer = this.renderer || null; this.selectionManager = this.selectionManager || null; diff --git a/src/Types.ts b/src/Types.ts index eed1e674..f7273eb6 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -468,7 +468,7 @@ export interface IDcsHandler { /** * EscapeSequenceParser interface. */ -export interface IEscapeSequenceParser { +export interface IEscapeSequenceParser extends IDisposable { /** * Reset the parser to its initial state (handlers are kept). */ diff --git a/src/addons/attach/attach.ts b/src/addons/attach/attach.ts index e94c0b46..98a0bfaa 100644 --- a/src/addons/attach/attach.ts +++ b/src/addons/attach/attach.ts @@ -90,7 +90,7 @@ export function attach(term: Terminal, socket: WebSocket, bidirectional: boolean addonTerminal._core.register(addSocketListener(socket, 'message', addonTerminal.__getMessage)); if (bidirectional) { - addonTerminal.on('data', addonTerminal.__sendData); + addonTerminal._core.register(addonTerminal.addDisposableListener('data', addonTerminal.__sendData)); } addonTerminal._core.register(addSocketListener(socket, 'close', () => detach(addonTerminal, socket))); diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index 76e7fba0..a56a270a 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -45,7 +45,7 @@ export class Terminal implements ITerminalApi { this._core.emit(type, data); } public addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable { - return this.addDisposableListener(type, handler); + return this._core.addDisposableListener(type, handler); } public resize(columns: number, rows: number): void { this._core.resize(columns, rows);