src/vs/base/browser/ui/iconLabel/iconLabelHover.ts/triggerShowHover
export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, content: IHoverContent, options?: IUpdatableHoverOptions): ICustomHover {
	let hoverPreparation: IDisposable | undefined;

	let hoverWidget: UpdatableHoverWidget | undefined;

	const hideHover = (disposeWidget: boolean, disposePreparation: boolean) => {
		if (disposeWidget) {
			hoverWidget?.dispose();
			hoverWidget = undefined;
		}
		if (disposePreparation) {
			hoverPreparation?.dispose();
			hoverPreparation = undefined;
		}
		hoverDelegate.onDidHideHover?.();
	};

	const triggerShowHover = (delay: number, focus?: boolean, target?: IHoverDelegateTarget) => {
		return new TimeoutTimer(async () => {
			if (!hoverWidget || hoverWidget.isDisposed) {
				hoverWidget = new UpdatableHoverWidget(hoverDelegate, target || htmlElement, delay > 0);
				await hoverWidget.update(content, focus, options);
			}
		}, delay);
	};

	const onMouseOver = () => {
		if (hoverPreparation) {
			return;
		}

		const toDispose: DisposableStore = new DisposableStore();

		const onMouseLeave = (e: MouseEvent) => hideHover(false, (<any>e).fromElement === htmlElement);
		toDispose.add(dom.addDisposableListener(htmlElement, dom.EventType.MOUSE_LEAVE, onMouseLeave, true));

		const onMouseDown = () => hideHover(true, true);
		toDispose.add(dom.addDisposableListener(htmlElement, dom.EventType.MOUSE_DOWN, onMouseDown, true));

		const target: IHoverDelegateTarget = {
			targetElements: [htmlElement],
			dispose: () => { }
		};
		if (hoverDelegate.placement === undefined || hoverDelegate.placement === 'mouse') {
			// track the mouse position
			const onMouseMove = (e: MouseEvent) => {
				target.x = e.x + 10;
				if ((e.target instanceof HTMLElement) && e.target.classList.contains('action-label')) {
					hideHover(true, true);
				}
			};
			toDispose.add(dom.addDisposableListener(htmlElement, dom.EventType.MOUSE_MOVE, onMouseMove, true));
		}
		toDispose.add(triggerShowHover(hoverDelegate.delay, false, target));

		hoverPreparation = toDispose;
	};
	const mouseOverDomEmitter = dom.addDisposableListener(htmlElement, dom.EventType.MOUSE_OVER, onMouseOver, true);
	const hover: ICustomHover = {
		show: focus => {
			hideHover(false, true); // terminate a ongoing mouse over preparation
			triggerShowHover(0, focus); // show hover immediately
		},
		hide: () => {
			hideHover(true, true);
		},
		update: async (newContent, hoverOptions) => {
			content = newContent;
			await hoverWidget?.update(content, undefined, hoverOptions);
		},
		dispose: () => {
			mouseOverDomEmitter.dispose();
			hideHover(true, true);
		}
	};
	return hover;
}