Support ILink.dispose

When displaying hovers/overlays using the link provider API currently
it's not possible to know when unused links are no longer cached in
xterm.js which can result in leaked event listeners.
This commit is contained in:
Daniel Imms
2020-09-04 21:57:51 -07:00
parent ecfc7336c5
commit 50fc84f7d7
4 changed files with 62 additions and 0 deletions
+7
View File
@@ -125,6 +125,13 @@ export class Linkifier2 extends Disposable implements ILinkifier2 {
private _askForLink(position: IBufferCellPosition, useLineCache: boolean): void {
if (!this._activeProviderReplies || !useLineCache) {
this._activeProviderReplies?.forEach(reply => {
reply?.forEach(linkWithState => {
if (linkWithState.link.dispose) {
linkWithState.link.dispose();
}
});
});
this._activeProviderReplies = new Map();
this._activeLine = position.y;
}
+1
View File
@@ -275,6 +275,7 @@ interface ILink {
activate(event: MouseEvent, text: string): void;
hover?(event: MouseEvent, text: string): void;
leave?(event: MouseEvent, text: string): void;
dispose?(): void;
}
interface ILinkDecorations {
+49
View File
@@ -755,6 +755,55 @@ describe('API Integration Tests', function(): void {
await pollFor(page, `window.calls`, ['provide 1', 'hover 1-3', 'leave 1-3', 'hover 5-7', 'leave 5-7', 'provide 2', 'provide 1', 'hover 9-11']);
await page.evaluate(`window.disposable.dispose()`);
});
it('should dispose links when hovering away', async () => {
await openTerminal(page, { rendererType: 'dom' });
await writeSync(page, 'foo bar baz');
// Wait for renderer to catch up as links are cleared on render
await pollFor(page, `document.querySelector('.xterm-rows').textContent`, 'foo bar baz ');
await page.evaluate(`
window.calls = [];
window.disposable = window.term.registerLinkProvider({
provideLinks: (position, cb) => {
window.calls.push('provide ' + position);
if (position === 1) {
cb([{
range: { start: { x: 1, y: 1 }, end: { x: 3, y: 1 } },
text: '',
activate: () => window.calls.push('activate'),
dispose: () => window.calls.push('dispose 1-3'),
hover: () => window.calls.push('hover 1-3'),
leave: () => window.calls.push('leave 1-3')
}, {
range: { start: { x: 5, y: 1 }, end: { x: 7, y: 1 } },
text: '',
activate: () => window.calls.push('activate'),
dispose: () => window.calls.push('dispose 5-7'),
hover: () => window.calls.push('hover 5-7'),
leave: () => window.calls.push('leave 5-7')
}, {
range: { start: { x: 9, y: 1 }, end: { x: 11, y: 1 } },
text: '',
activate: () => window.calls.push('activate'),
dispose: () => window.calls.push('dispose 9-11'),
hover: () => window.calls.push('hover 9-11'),
leave: () => window.calls.push('leave 9-11')
}]);
}
}
});
`);
const dims = await getDimensions();
await moveMouseCell(page, dims, 2, 1);
await pollFor(page, `window.calls`, ['provide 1', 'hover 1-3']);
await moveMouseCell(page, dims, 6, 1);
await pollFor(page, `window.calls`, ['provide 1', 'hover 1-3', 'leave 1-3', 'hover 5-7']);
await moveMouseCell(page, dims, 6, 2);
await pollFor(page, `window.calls`, ['provide 1', 'hover 1-3', 'leave 1-3', 'hover 5-7', 'leave 5-7', 'dispose 1-3', 'dispose 5-7', 'dispose 9-11', 'provide 2']);
await moveMouseCell(page, dims, 10, 1);
await pollFor(page, `window.calls`, ['provide 1', 'hover 1-3', 'leave 1-3', 'hover 5-7', 'leave 5-7', 'dispose 1-3', 'dispose 5-7', 'dispose 9-11', 'provide 2', 'provide 1', 'hover 9-11']);
await page.evaluate(`window.disposable.dispose()`);
});
});
});
+5
View File
@@ -1158,6 +1158,11 @@ declare module 'xterm' {
* @param text The text of the link.
*/
leave?(event: MouseEvent, text: string): void;
/**
* Called when the link is released and no longer used by xterm.js.
*/
dispose?(): void;
}
/**