diff --git a/src/browser/Linkifier2.ts b/src/browser/Linkifier2.ts index 647b9882..39f750cc 100644 --- a/src/browser/Linkifier2.ts +++ b/src/browser/Linkifier2.ts @@ -8,7 +8,8 @@ import { IDisposable } from 'common/Types'; import { IMouseService, IRenderService } from './services/Services'; import { IBufferService } from 'common/services/Services'; import { EventEmitter, IEvent } from 'common/EventEmitter'; -import { Disposable } from 'common/Lifecycle'; +import { Disposable, getDisposeArrayDisposable } from 'common/Lifecycle'; +import { addDisposableDomListener } from 'browser/Lifecycle'; interface ILinkState { decorations: ILinkDecorations; @@ -36,6 +37,7 @@ export class Linkifier2 extends Disposable implements ILinkifier2 { @IBufferService private readonly _bufferService: IBufferService ) { super(); + this.register(getDisposeArrayDisposable(this._linkCacheDisposables)); } public registerLinkProvider(linkProvider: ILinkProvider): IDisposable { @@ -57,12 +59,12 @@ export class Linkifier2 extends Disposable implements ILinkifier2 { this._mouseService = mouseService; this._renderService = renderService; - this._element.addEventListener('mouseleave', () => { + this.register(addDisposableDomListener(this._element, 'mouseleave', () => { this._isMouseOut = true; this._clearCurrentLink(); - }); - this._element.addEventListener('mousemove', this._onMouseMove.bind(this)); - this._element.addEventListener('click', this._onClick.bind(this)); + })); + this.register(addDisposableDomListener(this._element, 'mousemove', this._onMouseMove.bind(this))); + this.register(addDisposableDomListener(this._element, 'click', this._onClick.bind(this))); } private _onMouseMove(event: MouseEvent): void { diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index c38e9bb4..a1e5adf5 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -417,7 +417,7 @@ export class Terminal extends CoreTerminal implements ITerminal { this._colorManager.setTheme(this._theme); const renderer = this._createRenderer(); - this._renderService = this._instantiationService.createInstance(RenderService, renderer, this.rows, this.screenElement); + this._renderService = this.register(this._instantiationService.createInstance(RenderService, renderer, this.rows, this.screenElement)); this._instantiationService.setService(IRenderService, this._renderService); this.register(this._renderService.onRenderedBufferChange(e => this._onRender.fire(e))); this.onResize(e => this._renderService!.resize(e.cols, e.rows)); diff --git a/src/browser/renderer/BaseRenderLayer.ts b/src/browser/renderer/BaseRenderLayer.ts index 837f18e4..7d9fb5b8 100644 --- a/src/browser/renderer/BaseRenderLayer.ts +++ b/src/browser/renderer/BaseRenderLayer.ts @@ -60,7 +60,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { } public dispose(): void { - this._container.removeChild(this._canvas); + this._canvas.parentElement?.removeChild(this._canvas); this._charAtlas?.dispose(); } diff --git a/src/browser/renderer/Renderer.ts b/src/browser/renderer/Renderer.ts index 5142e4cb..b9f577fe 100644 --- a/src/browser/renderer/Renderer.ts +++ b/src/browser/renderer/Renderer.ts @@ -71,8 +71,8 @@ export class Renderer extends Disposable implements IRenderer { } public dispose(): void { - super.dispose(); this._renderLayers.forEach(l => l.dispose()); + super.dispose(); removeTerminalFromCache(this._id); } diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index d6b30524..df4cf443 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -52,7 +52,7 @@ export class RenderService extends Disposable implements IRenderService { screenElement: HTMLElement, @IOptionsService optionsService: IOptionsService, @ICharSizeService charSizeService: ICharSizeService, - @IBufferService private readonly _bufferService: IBufferService + @IBufferService bufferService: IBufferService ) { super(); this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end)); @@ -62,7 +62,7 @@ export class RenderService extends Disposable implements IRenderService { this._screenDprMonitor.setListener(() => this.onDevicePixelRatioChange()); this.register(this._screenDprMonitor); - this.register(this._bufferService.onResize(e => this._fullRefresh())); + this.register(bufferService.onResize(e => this._fullRefresh())); this.register(optionsService.onOptionChange(() => this._renderer.onOptionsChanged())); this.register(charSizeService.onCharSizeChange(() => this.onCharSizeChanged())); diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 65768c51..beb00764 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -82,7 +82,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { this._instantiationService = new InstantiationService(); this.optionsService = new OptionsService(options); this._instantiationService.setService(IOptionsService, this.optionsService); - this._bufferService = this._instantiationService.createInstance(BufferService); + this._bufferService = this.register(this._instantiationService.createInstance(BufferService)); this._instantiationService.setService(IBufferService, this._bufferService); this._logService = this._instantiationService.createInstance(LogService); this._instantiationService.setService(ILogService, this._logService); diff --git a/src/common/Lifecycle.ts b/src/common/Lifecycle.ts index 910862c4..b6148ab0 100644 --- a/src/common/Lifecycle.ts +++ b/src/common/Lifecycle.ts @@ -47,3 +47,18 @@ export abstract class Disposable implements IDisposable { } } } + +/** + * Dispose of all disposables in an array and set its length to 0. + */ +export function disposeArray(disposables: IDisposable[]): void { + disposables.forEach(d => d.dispose()); + disposables.length = 0; +} + +/** + * Creates a disposable that will dispose of an array of disposables when disposed. + */ +export function getDisposeArrayDisposable(array: IDisposable[]): IDisposable { + return { dispose: () => disposeArray(array) }; +} diff --git a/src/common/buffer/BufferSet.ts b/src/common/buffer/BufferSet.ts index 26fc1d09..b9dc7995 100644 --- a/src/common/buffer/BufferSet.ts +++ b/src/common/buffer/BufferSet.ts @@ -8,18 +8,19 @@ import { IAttributeData } from 'common/Types'; import { Buffer } from 'common/buffer/Buffer'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { IOptionsService, IBufferService } from 'common/services/Services'; +import { Disposable } from 'common/Lifecycle'; /** * The BufferSet represents the set of two buffers used by xterm terminals (normal and alt) and * provides also utilities for working with them. */ -export class BufferSet implements IBufferSet { +export class BufferSet extends Disposable implements IBufferSet { private _normal: Buffer; private _alt: Buffer; private _activeBuffer: Buffer; - private _onBufferActivate = new EventEmitter<{activeBuffer: IBuffer, inactiveBuffer: IBuffer}>(); + private _onBufferActivate = this.register(new EventEmitter<{activeBuffer: IBuffer, inactiveBuffer: IBuffer}>()); public get onBufferActivate(): IEvent<{activeBuffer: IBuffer, inactiveBuffer: IBuffer}> { return this._onBufferActivate.event; } /** @@ -30,6 +31,8 @@ export class BufferSet implements IBufferSet { optionsService: IOptionsService, bufferService: IBufferService ) { + super(); + this._normal = new Buffer(true, optionsService, bufferService); this._normal.fillViewportRows(); diff --git a/src/common/buffer/Types.d.ts b/src/common/buffer/Types.d.ts index c271f33c..752b1a26 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, ICharset } from 'common/Types'; +import { IAttributeData, ICircularList, IBufferLine, ICellData, IMarker, ICharset, IDisposable } from 'common/Types'; import { IEvent } from 'common/EventEmitter'; // BufferIndex denotes a position in the buffer: [rowIndex, colIndex] @@ -47,7 +47,7 @@ export interface IBuffer { addMarker(y: number): IMarker; } -export interface IBufferSet { +export interface IBufferSet extends IDisposable { alt: IBuffer; normal: IBuffer; active: IBuffer; diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 8de44e43..301146e1 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -7,11 +7,12 @@ import { IBufferService, IOptionsService } from 'common/services/Services'; import { BufferSet } from 'common/buffer/BufferSet'; import { IBufferSet, IBuffer } from 'common/buffer/Types'; import { EventEmitter, IEvent } from 'common/EventEmitter'; +import { Disposable } from 'common/Lifecycle'; export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars export const MINIMUM_ROWS = 1; -export class BufferService implements IBufferService { +export class BufferService extends Disposable implements IBufferService { public serviceBrand: any; public cols: number; @@ -28,11 +29,17 @@ export class BufferService implements IBufferService { constructor( @IOptionsService private _optionsService: IOptionsService ) { + super(); this.cols = Math.max(_optionsService.options.cols, MINIMUM_COLS); this.rows = Math.max(_optionsService.options.rows, MINIMUM_ROWS); this.buffers = new BufferSet(_optionsService, this); } + public dispose(): void { + super.dispose(); + this.buffers.dispose(); + } + public resize(cols: number, rows: number): void { this.cols = cols; this.rows = rows; @@ -42,6 +49,7 @@ export class BufferService implements IBufferService { } public reset(): void { + this.buffers.dispose(); this.buffers = new BufferSet(this._optionsService, this); this.isUserScrolling = false; }