diff --git a/src/Linkifier.test.ts b/src/Linkifier.test.ts index 78fc3f32..0e67403c 100644 --- a/src/Linkifier.test.ts +++ b/src/Linkifier.test.ts @@ -75,6 +75,23 @@ describe('Linkifier', () => { }, 0); } + function assertLinkifiesMultiLineLink(rowText: string, linkMatcherRegex: RegExp, links: {x1: number, y1: number, x2: number, y2: number}[], done: MochaDone): void { + addRow(rowText); + linkifier.registerLinkMatcher(linkMatcherRegex, () => {}); + linkifier.linkifyRows(); + // Allow linkify to happen + setTimeout(() => { + assert.equal(mouseZoneManager.zones.length, links.length); + links.forEach((l, i) => { + assert.equal(mouseZoneManager.zones[i].x1, l.x1 + 1); + assert.equal(mouseZoneManager.zones[i].x2, l.x2 + 1); + assert.equal(mouseZoneManager.zones[i].y1, l.y1 + 1); + assert.equal(mouseZoneManager.zones[i].y2, l.y2 + 1); + }); + done(); + }, 0); + } + describe('before attachToDom', () => { it('should allow link matcher registration', done => { assert.doesNotThrow(() => { @@ -118,6 +135,24 @@ describe('Linkifier', () => { // character (U+1F537) which caused the path to be duplicated. See #642. assertLinkifiesRow('echo \'🔷foo\'', /foo/, [{x: 8, length: 3}], done); }); + describe('multi-line links', () => { + it('should match links that start on line 1/2 of a wrapped line and end on the last character of line 1/2', done => { + terminal.cols = 4; + assertLinkifiesMultiLineLink('12345', /1234/, [{x1: 0, x2: 4, y1: 0, y2: 0}], done); + }); + it('should match links that start on line 1/2 of a wrapped line and wrap to line 2/2', done => { + terminal.cols = 4; + assertLinkifiesMultiLineLink('12345', /12345/, [{x1: 0, x2: 1, y1: 0, y2: 1}], done); + }); + it('should match links that start and end on line 2/2 of a wrapped line', done => { + terminal.cols = 4; + assertLinkifiesMultiLineLink('12345678', /5678/, [{x1: 0, x2: 4, y1: 1, y2: 1}], done); + }); + it('should match links that start on line 2/3 of a wrapped line and wrap to line 3/3', done => { + terminal.cols = 4; + assertLinkifiesMultiLineLink('123456789', /56789/, [{x1: 0, x2: 1, y1: 1, y2: 2}], done); + }); + }); }); describe('validationCallback', () => { diff --git a/src/Linkifier.ts b/src/Linkifier.ts index c7d64e9d..8552d8a5 100644 --- a/src/Linkifier.ts +++ b/src/Linkifier.ts @@ -232,13 +232,20 @@ export class Linkifier extends EventEmitter implements ILinkifier { * @param matcher The link matcher for the link. */ private _addLink(x: number, y: number, uri: string, matcher: ILinkMatcher): void { - const wrappedX = x % this._terminal.cols; - const wrappedY = y + Math.floor(x / this._terminal.cols); + const x1 = x % this._terminal.cols; + const y1 = y + Math.floor(x / this._terminal.cols); + let x2 = (x1 + uri.length) % this._terminal.cols; + let y2 = y1 + Math.floor((x1 + uri.length) / this._terminal.cols); + if (x2 === 0) { + x2 = this._terminal.cols; + y2--; + } + this._mouseZoneManager.add(new MouseZone( - wrappedX + 1, - wrappedY + 1, - (wrappedX + 1 + uri.length) % this._terminal.cols, - wrappedY + 1 + Math.floor((wrappedX + 1 + uri.length) / this._terminal.cols), + x1 + 1, + y1 + 1, + x2 + 1, + y2 + 1, e => { if (matcher.handler) { return matcher.handler(e, uri); @@ -246,17 +253,17 @@ export class Linkifier extends EventEmitter implements ILinkifier { window.open(uri, '_blank'); }, e => { - this.emit(LinkHoverEventTypes.HOVER, this._createLinkHoverEvent(wrappedX, wrappedY, uri)); + this.emit(LinkHoverEventTypes.HOVER, this._createLinkHoverEvent(x1, y1, x2, y2)); this._terminal.element.style.cursor = 'pointer'; }, e => { - this.emit(LinkHoverEventTypes.TOOLTIP, this._createLinkHoverEvent(wrappedX, wrappedY, uri)); + this.emit(LinkHoverEventTypes.TOOLTIP, this._createLinkHoverEvent(x1, y1, x2, y2)); if (matcher.hoverTooltipCallback) { matcher.hoverTooltipCallback(e, uri); } }, () => { - this.emit(LinkHoverEventTypes.LEAVE, this._createLinkHoverEvent(wrappedX, wrappedY, uri)); + this.emit(LinkHoverEventTypes.LEAVE, this._createLinkHoverEvent(x1, y1, x2, y2)); this._terminal.element.style.cursor = ''; if (matcher.hoverLeaveCallback) { matcher.hoverLeaveCallback(); @@ -271,13 +278,7 @@ export class Linkifier extends EventEmitter implements ILinkifier { )); } - private _createLinkHoverEvent(x: number, y: number, uri: string): ILinkHoverEvent { - return { - x1: x, - y1: y, - x2: (x + uri.length) % this._terminal.cols, - y2: y + Math.floor((x + uri.length) / this._terminal.cols), - cols: this._terminal.cols - }; + private _createLinkHoverEvent(x1: number, y1: number, x2: number, y2: number): ILinkHoverEvent { + return { x1, y1, x2, y2, cols: this._terminal.cols }; } }