From 0e3518409e8d2ec20b2e3fbc24bef8ebb4b34990 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 9 Sep 2017 19:06:30 -0700 Subject: [PATCH] Fix issues with link state - Scrolling will now clear the link renderer - Refreshing a single line will now recalculate only that line Fixes #959 Fixes #960 --- src/Linkifier.ts | 31 +++++++++++++++++++++++-------- src/Terminal.ts | 8 ++++++-- src/input/Interfaces.ts | 2 +- src/input/MouseZoneManager.ts | 25 ++++++++++++++++++++++--- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/Linkifier.ts b/src/Linkifier.ts index 94795429..fa47271c 100644 --- a/src/Linkifier.ts +++ b/src/Linkifier.ts @@ -49,11 +49,16 @@ export class Linkifier extends EventEmitter implements ILinkifier { private _mouseZoneManager: IMouseZoneManager; private _rowsTimeoutId: number; private _nextLinkMatcherId = HYPERTEXT_LINK_MATCHER_ID; + private _rowsToLinkify: {start: number, end: number}; constructor( protected _terminal: IBufferAccessor & IElementAccessor ) { super(); + this._rowsToLinkify = { + start: null, + end: null + }; this.registerLinkMatcher(strictUrlRegex, null, { matchIndex: 1 }); } @@ -76,25 +81,35 @@ export class Linkifier extends EventEmitter implements ILinkifier { return; } - // Clear out any existing links - this._mouseZoneManager.clearAll(); + // Increase range to linkify + if (!this._rowsToLinkify.start) { + this._rowsToLinkify.start = start; + this._rowsToLinkify.end = end; + } else { + this._rowsToLinkify.start = this._rowsToLinkify.start < start ? this._rowsToLinkify.start : start; + this._rowsToLinkify.end = this._rowsToLinkify.end < end ? this._rowsToLinkify.end : end; + } + // Clear out any existing links on this row range + this._mouseZoneManager.clearAll(start, end); + + // Restart timer if (this._rowsTimeoutId) { clearTimeout(this._rowsTimeoutId); } - this._rowsTimeoutId = setTimeout(this._linkifyRows.bind(this, start, end), Linkifier.TIME_BEFORE_LINKIFY); + this._rowsTimeoutId = setTimeout(() => this._linkifyRows(), Linkifier.TIME_BEFORE_LINKIFY); } /** - * Linkifies - * @param start The row to start at. - * @param end The row to end at. + * Linkifies the rows requested. */ - private _linkifyRows(start: number, end: number): void { + private _linkifyRows(): void { this._rowsTimeoutId = null; - for (let i = start; i <= end; i++) { + for (let i = this._rowsToLinkify.start; i <= this._rowsToLinkify.end; i++) { this._linkifyRow(i); } + this._rowsToLinkify.start = null; + this._rowsToLinkify.end = null; } /** diff --git a/src/Terminal.ts b/src/Terminal.ts index e19887cb..817eed40 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -48,8 +48,11 @@ import { MouseZoneManager } from './input/MouseZoneManager'; import { initialize as initializeCharAtlas } from './renderer/CharAtlas'; import { IRenderer } from './renderer/Interfaces'; -// Declare for RequireJS in loadAddon +// Declares required for loadAddon +declare var exports: any; +declare var module: any; declare var define: any; +declare var require: any; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -588,6 +591,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT this.syncBellSound(); this._mouseZoneManager = new MouseZoneManager(this); + this.on('scroll', () => this._mouseZoneManager.clearAll()); this.linkifier.attachToDom(this._mouseZoneManager); // Create the container that will hold helpers like the textarea for @@ -1034,7 +1038,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT */ private queueLinkification(start: number, end: number): void { if (this.linkifier) { - this.linkifier.linkifyRows(0, this.rows); + this.linkifier.linkifyRows(start, end); } } diff --git a/src/input/Interfaces.ts b/src/input/Interfaces.ts index e3ed4eef..21514a53 100644 --- a/src/input/Interfaces.ts +++ b/src/input/Interfaces.ts @@ -5,7 +5,7 @@ export interface IMouseZoneManager { add(zone: IMouseZone): void; - clearAll(): void; + clearAll(start?: number, end?: number): void; } export interface IMouseZone { diff --git a/src/input/MouseZoneManager.ts b/src/input/MouseZoneManager.ts index d1bc8a75..f3ba90c1 100644 --- a/src/input/MouseZoneManager.ts +++ b/src/input/MouseZoneManager.ts @@ -44,9 +44,28 @@ export class MouseZoneManager implements IMouseZoneManager { } } - public clearAll(): void { - this._zones.length = 0; - this._deactivate(); + public clearAll(start?: number, end?: number): void { + // Exit if there's nothing to clear + if (this._zones.length === 0) { + return; + } + + // Iterate through zones and clear them out if they're within the range + for (let i = 0; i < this._zones.length; i++) { + const zone = this._zones[i]; + if (zone.y >= start && zone.y <= end) { + if (this._currentZone && this._currentZone === zone) { + this._currentZone.leaveCallback(); + this._currentZone = null; + } + this._zones.splice(i--, 1); + } + } + + // Deactivate the mouse zone manager if all the zones have been removed + if (this._zones.length === 0) { + this._deactivate(); + } } private _activate(): void {