From 0103c689f7966c4909f51855b90053bdfe15d4cb Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 7 Feb 2020 11:16:41 -0800 Subject: [PATCH] Start registerLinkProvider tests Bugs fixed: - Not breaking on first link match for multiple providers - Hover event wasn't fired when on last character of link --- src/browser/Linkifier2.ts | 7 +-- test/api/Terminal.api.ts | 102 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/browser/Linkifier2.ts b/src/browser/Linkifier2.ts index 7e065c31..38bd12ac 100644 --- a/src/browser/Linkifier2.ts +++ b/src/browser/Linkifier2.ts @@ -117,6 +117,7 @@ export class Linkifier2 implements ILinkifier2 { const currentLink = providerReplies.get(j); if (currentLink) { this._handleNewLink(currentLink); + break; } } } @@ -165,7 +166,7 @@ export class Linkifier2 implements ILinkifier2 { return; } - // Show the tooltip if the we have a link at the position + // Trigger hover if the we have a link at the position if (this._linkAtPosition(link, position)) { this._currentLink = link; this._linkHover(this._element, link, this._lastMouseEvent); @@ -215,8 +216,8 @@ export class Linkifier2 implements ILinkifier2 { // If the start and end have the same y, then the position must be between start and end x // If not, then handle each case seperately, depending on which way it wraps - return ((sameLine && link.range.start.x <= position.x && link.range.end.x > position.x) || - (wrappedFromLeft && link.range.end.x > position.x) || + return ((sameLine && link.range.start.x <= position.x && link.range.end.x >= position.x) || + (wrappedFromLeft && link.range.end.x >= position.x) || (wrappedToRight && link.range.start.x <= position.x) || (wrappedFromLeft && wrappedToRight)) && link.range.start.y <= position.y && diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts index cdfab61f..6c4fb10c 100644 --- a/test/api/Terminal.api.ts +++ b/test/api/Terminal.api.ts @@ -524,6 +524,60 @@ describe('API Integration Tests', function(): void { await page.evaluate(`window.term.dispose()`); assert.equal(await page.evaluate(`window.term._core._isDisposed`), true); }); + + describe.only('registerLinkProvider', () => { + it('should fire provideLink when hovering cells', async () => { + await openTerminal({ rendererType: 'dom' }); + await page.evaluate(` + window.calls = []; + window.disposable = window.term.registerLinkProvider({ + provideLink: (position, cb) => { + calls.push(position); + cb(undefined); + } + }); + `); + const dims = await getDimensions(); + await moveMouseToCell(page, dims, 1, 1); + await moveMouseToCell(page, dims, 2, 2); + await moveMouseToCell(page, dims, 10, 4); + await pollFor(page, `window.calls`, [{ x: 1, y: 1 }, { x: 2, y: 2 }, { x: 10, y: 4 }]); + await page.evaluate(`window.disposable.dispose()`); + }); + + it('should fire hover and leave events on the link', async () => { + await openTerminal({ rendererType: 'dom' }); + await writeSync(page, 'foo bar baz'); + await page.evaluate(` + window.calls = []; + window.disposable = window.term.registerLinkProvider({ + provideLink: (position, cb) => { + window.calls.push('provide'); + if (position.x >= 5 && position.x <= 7 && position.y === 1) { + window.calls.push('match'); + cb({ + range: { start: { x: 5, y: 1 }, end: { x: 7, y: 1 } }, + text: 'bar', + activate: () => window.calls.push('activate'), + hover: () => window.calls.push('hover'), + leave: () => window.calls.push('leave') + }); + } + } + }); + `); + const dims = await getDimensions(); + await moveMouseToCell(page, dims, 5, 1); + await pollFor(page, `window.calls`, ['provide', 'match', 'hover']); + await moveMouseToCell(page, dims, 4, 1); + await pollFor(page, `window.calls`, ['provide', 'match', 'hover', 'leave', 'provide']); + await moveMouseToCell(page, dims, 7, 1); + await pollFor(page, `window.calls`, ['provide', 'match', 'hover', 'leave', 'provide', 'provide', 'match', 'hover']); + await moveMouseToCell(page, dims, 8, 1); + await pollFor(page, `window.calls`, ['provide', 'match', 'hover', 'leave', 'provide', 'provide', 'match', 'hover', 'leave', 'provide']); + await page.evaluate(`window.disposable.dispose()`); + }); + }); }); async function openTerminal(options: ITerminalOptions = {}): Promise { @@ -535,3 +589,51 @@ async function openTerminal(options: ITerminalOptions = {}): Promise { await page.waitForSelector('.xterm-text-layer'); } } + +interface IDimensions { + top: number; + left: number; + renderDimensions: IRenderDimensions; +} + +interface IRenderDimensions { + scaledCharWidth: number; + scaledCharHeight: number; + scaledCellWidth: number; + scaledCellHeight: number; + scaledCharLeft: number; + scaledCharTop: number; + scaledCanvasWidth: number; + scaledCanvasHeight: number; + canvasWidth: number; + canvasHeight: number; + actualCellWidth: number; + actualCellHeight: number; +} + +async function getDimensions(): Promise { + return await page.evaluate(` + (function() { + const rect = document.querySelector('.xterm-rows').getBoundingClientRect(); + return { + top: rect.top, + left: rect.left, + renderDimensions: window.term._core._renderService.dimensions + }; + })(); + `); +} + +async function getCellCoordinates(dimensions: IDimensions, col: number, row: number): Promise<{ x: number, y: number }> { + return { + x: dimensions.left + dimensions.renderDimensions.scaledCellWidth * (col - 0.5), + y: dimensions.top + dimensions.renderDimensions.scaledCellHeight * (row - 0.5) + }; +} + +async function moveMouseToCell(page: puppeteer.Page, dimensions: IDimensions, col: number, row: number) { + const coords = await getCellCoordinates(dimensions, col, row); + await page.mouse.move(coords.x, coords.y); + // Timeout is needed here otherwise the browser may drop events + await timeout(0); +}