diff --git a/src/browser/services/DecorationService.ts b/src/browser/services/DecorationService.ts index f29ec7ea..28785224 100644 --- a/src/browser/services/DecorationService.ts +++ b/src/browser/services/DecorationService.ts @@ -16,11 +16,7 @@ export class DecorationService extends Disposable implements IDecorationService private _screenElement: HTMLElement | undefined; private _renderService: IRenderService | undefined; - constructor( - @IBufferService private readonly _bufferService: IBufferService, - @IInstantiationService private readonly _instantiationService: IInstantiationService) { - super(); - } + constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService) { super(); } public attachToDom(screenElement: HTMLElement, renderService: IRenderService): void { this._renderService = renderService; @@ -28,7 +24,6 @@ export class DecorationService extends Disposable implements IDecorationService this._container = document.createElement('div'); this._container.classList.add('xterm-decoration-container'); screenElement.appendChild(this._container); - this.refresh(); this.register(this._renderService.onRenderedBufferChange(() => this.refresh())); this.register(this._renderService.onDimensionsChange(() => this.refresh(true))); } @@ -43,12 +38,12 @@ export class DecorationService extends Disposable implements IDecorationService return decoration; } - public refresh(recreate?: boolean): void { - if (!this._bufferService || !this._renderService) { + public refresh(shouldRecreate?: boolean): void { + if (!this._renderService) { return; } for (const decoration of this._decorations) { - decoration.render(this._renderService, recreate); + decoration.render(this._renderService, shouldRecreate); } } @@ -56,16 +51,16 @@ export class DecorationService extends Disposable implements IDecorationService for (const decoration of this._decorations) { decoration.dispose(); } - if (this._container) { - this._screenElement?.removeChild(this._container); + if (this._screenElement && this._container && this._screenElement.contains(this._container)) { + this._screenElement.removeChild(this._container); } } } export class Decoration extends Disposable implements IDecoration { - private static _nextId = 1; private readonly _marker: IMarker; private _element: HTMLElement | undefined; - private readonly _id: number = Decoration._nextId++; + private _altBufferActive: boolean = false; + public isDisposed: boolean = false; public get element(): HTMLElement | undefined { return this._element; } @@ -90,24 +85,28 @@ export class Decoration extends Disposable implements IDecoration { super(); this.x = options.x ?? 0; this._marker = options.marker; + this._marker.onDispose(() => this.dispose()); this.anchor = options.anchor || 'left'; this.width = options.width || 1; this.height = options.height || 1; + this.register(this._bufferService.buffers.onBufferActivate((event) => this._altBufferActive = event.activeBuffer === this._bufferService.buffers.alt)); } - public render(renderService: IRenderService, recreate?: boolean): void { - if (!this._element || recreate) { - this._createElement(renderService, recreate); + public render(renderService: IRenderService, shouldRecreate?: boolean): void { + if (!this._element || shouldRecreate) { + this._createElement(renderService, shouldRecreate); } if (this._container && this._element && !this._container.contains(this._element)) { this._container.append(this._element); } this._refreshStyle(renderService); - this._onRender.fire(this._element!); + if (this._element) { + this._onRender.fire(this._element); + } } - private _createElement(renderService: IRenderService, recreate?: boolean): void { - if (recreate && this._element) { + private _createElement(renderService: IRenderService, shouldRecreate?: boolean): void { + if (shouldRecreate && this._element && this._container.contains(this._element)) { this._container.removeChild(this._element); } this._element = document.createElement('div'); @@ -117,7 +116,8 @@ export class Decoration extends Disposable implements IDecoration { this._element.style.top = `${(this.marker.line - this._bufferService.buffers.active.ydisp) * renderService.dimensions.scaledCellHeight}px`; if (this.x && this.x > this._bufferService.cols) { - this._element!.style.display = 'none'; + // exceeded the container width, so hide + this._element.style.display = 'none'; } if (this.anchor === 'right') { this._element.style.right = this.x ? `${this.x * renderService.dimensions.scaledCellWidth}px` : ''; @@ -129,9 +129,13 @@ export class Decoration extends Disposable implements IDecoration { if (this.isDisposed) { return; } - this._container.removeChild(this._element!); + if (!this.marker.isDisposed) { + this.marker.dispose(); + } + if (this._element && this._container.contains(this._element)) { + this._container.removeChild(this._element); + } this.isDisposed = true; - this._marker.dispose(); // Emit before super.dispose such that dispose listeners get a change to react this._onDispose.fire(); super.dispose(); @@ -140,13 +144,16 @@ export class Decoration extends Disposable implements IDecoration { } private _refreshStyle(renderService: IRenderService): void { + if (!this._element) { + return; + } const line = this.marker.line - this._bufferService.buffers.active.ydisp; if (line < 0 || line > this._bufferService.rows) { // outside of viewport - this._element!.style.display = 'none'; + this._element.style.display = 'none'; } else { - this._element!.style.top = `${line * renderService.dimensions.scaledCellHeight}px`; - this._element!.style.display = 'block'; + this._element.style.top = `${line * renderService.dimensions.scaledCellHeight}px`; + this._element.style.display = this._altBufferActive ? 'none' : 'block'; } } } diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 88634048..7e6fbd3a 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -1292,6 +1292,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._resetBufferLine(j); } this._dirtyRowService.markDirty(0); + this._bufferService.buffer.clearMarkers(); break; case 3: // Clear scrollback (everything not in viewport)