Path: blob/main/src/vs/platform/hover/browser/hover.ts
3294 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { createDecorator } from '../../instantiation/common/instantiation.js';6import { Disposable, DisposableStore } from '../../../base/common/lifecycle.js';7import { IHoverDelegate, IHoverDelegateOptions } from '../../../base/browser/ui/hover/hoverDelegate.js';8import { IConfigurationService } from '../../configuration/common/configuration.js';9import { addStandardDisposableListener, isHTMLElement } from '../../../base/browser/dom.js';10import { KeyCode } from '../../../base/common/keyCodes.js';11import type { IHoverDelegate2, IHoverOptions, IHoverWidget, IManagedHoverContentOrFactory } from '../../../base/browser/ui/hover/hover.js';1213export const IHoverService = createDecorator<IHoverService>('hoverService');1415export interface IHoverService extends IHoverDelegate2 {16readonly _serviceBrand: undefined;17}1819export interface IHoverDelayOptions {20readonly instantHover?: boolean;21readonly dynamicDelay?: (content?: IManagedHoverContentOrFactory) => number | undefined;22}2324export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate {2526private lastHoverHideTime = 0;27private timeLimit = 200;2829private _delay: number;30get delay(): number | ((content: IManagedHoverContentOrFactory) => number) {31if (this.isInstantlyHovering()) {32return 0; // show instantly when a hover was recently shown33}3435if (this.hoverOptions?.dynamicDelay) {36return content => this.hoverOptions?.dynamicDelay?.(content) ?? this._delay;37}3839return this._delay;40}4142private readonly hoverDisposables = this._register(new DisposableStore());4344constructor(45public readonly placement: 'mouse' | 'element',46private readonly hoverOptions: IHoverDelayOptions | undefined,47private overrideOptions: Partial<IHoverOptions> | ((options: IHoverDelegateOptions, focus?: boolean) => Partial<IHoverOptions>) = {},48@IConfigurationService private readonly configurationService: IConfigurationService,49@IHoverService private readonly hoverService: IHoverService,50) {51super();5253this._delay = this.configurationService.getValue<number>('workbench.hover.delay');54this._register(this.configurationService.onDidChangeConfiguration(e => {55if (e.affectsConfiguration('workbench.hover.delay')) {56this._delay = this.configurationService.getValue<number>('workbench.hover.delay');57}58}));59}6061showHover(options: IHoverDelegateOptions, focus?: boolean): IHoverWidget | undefined {62const overrideOptions = typeof this.overrideOptions === 'function' ? this.overrideOptions(options, focus) : this.overrideOptions;6364// close hover on escape65this.hoverDisposables.clear();66const targets = isHTMLElement(options.target) ? [options.target] : options.target.targetElements;67for (const target of targets) {68this.hoverDisposables.add(addStandardDisposableListener(target, 'keydown', (e) => {69if (e.equals(KeyCode.Escape)) {70this.hoverService.hideHover();71}72}));73}7475const id = isHTMLElement(options.content)76? undefined77: typeof options.content === 'string'78? options.content.toString()79: options.content.value;8081return this.hoverService.showInstantHover({82...options,83...overrideOptions,84persistence: {85hideOnKeyDown: true,86...overrideOptions.persistence87},88id,89appearance: {90...options.appearance,91compact: true,92skipFadeInAnimation: this.isInstantlyHovering(),93...overrideOptions.appearance94}95}, focus);96}9798private isInstantlyHovering(): boolean {99return !!this.hoverOptions?.instantHover && Date.now() - this.lastHoverHideTime < this.timeLimit;100}101102setInstantHoverTimeLimit(timeLimit: number): void {103if (!this.hoverOptions?.instantHover) {104throw new Error('Instant hover is not enabled');105}106this.timeLimit = timeLimit;107}108109onDidHideHover(): void {110this.hoverDisposables.clear();111if (this.hoverOptions?.instantHover) {112this.lastHoverHideTime = Date.now();113}114}115}116117// TODO@benibenj remove this, only temp fix for contextviews118export const nativeHoverDelegate: IHoverDelegate = {119showHover: function (): IHoverWidget | undefined {120throw new Error('Native hover function not implemented.');121},122delay: 0,123showNativeHover: true124};125126127