support custom hover on status bar entries

This commit is contained in:
Martin Aeschlimann 2021-05-31 09:55:16 +02:00
parent f55a5243a4
commit 8d41153ffc
No known key found for this signature in database
GPG key ID: 2609A01E695523E3
7 changed files with 206 additions and 256 deletions

View file

@ -1,131 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isFunction, isString } from 'vs/base/common/types';
import * as dom from 'vs/base/browser/dom';
import { IIconLabelMarkdownString } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { domEvent } from 'vs/base/browser/event';
import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget';
import { localize } from 'vs/nls';
import { IMarkdownString } from 'vs/base/common/htmlContent';
export class CustomHoverProvider extends Disposable {
private readonly customHovers: Map<HTMLElement, IDisposable> = new Map();
constructor(private hoverDelegate: IHoverDelegate) {
super();
}
public setupHover(htmlElement: HTMLElement, tooltip: string | IIconLabelMarkdownString | undefined): void {
const previousCustomHover = this.customHovers.get(htmlElement);
if (previousCustomHover) {
previousCustomHover.dispose();
this.customHovers.delete(htmlElement);
}
if (tooltip) {
return this.setupCustomHover(this.hoverDelegate, htmlElement, tooltip);
}
}
private getTooltipForCustom(markdownTooltip: string | IIconLabelMarkdownString): (token: CancellationToken) => Promise<string | IMarkdownString | undefined> {
if (isString(markdownTooltip)) {
return async () => markdownTooltip;
} else if (isFunction(markdownTooltip.markdown)) {
return markdownTooltip.markdown;
} else {
const markdown = markdownTooltip.markdown;
return async () => markdown;
}
}
private setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, markdownTooltip: string | IIconLabelMarkdownString): void {
let tooltip = this.getTooltipForCustom(markdownTooltip);
let hoverOptions: IHoverDelegateOptions | undefined;
let mouseX: number | undefined;
let isHovering = false;
let tokenSource: CancellationTokenSource;
let hoverDisposable: IDisposable | undefined;
function mouseOver(this: HTMLElement, e: MouseEvent): void {
if (isHovering) {
return;
}
tokenSource = new CancellationTokenSource();
function mouseLeaveOrDown(this: HTMLElement, e: MouseEvent): void {
const isMouseDown = e.type === dom.EventType.MOUSE_DOWN;
if (isMouseDown) {
hoverDisposable?.dispose();
hoverDisposable = undefined;
}
if (isMouseDown || (<any>e).fromElement === htmlElement) {
isHovering = false;
hoverOptions = undefined;
tokenSource.dispose(true);
mouseLeaveDisposable.dispose();
mouseDownDisposable.dispose();
}
}
const mouseLeaveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_LEAVE, true)(mouseLeaveOrDown.bind(htmlElement));
const mouseDownDisposable = domEvent(htmlElement, dom.EventType.MOUSE_DOWN, true)(mouseLeaveOrDown.bind(htmlElement));
isHovering = true;
function mouseMove(this: HTMLElement, e: MouseEvent): void {
mouseX = e.x;
}
const mouseMoveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_MOVE, true)(mouseMove.bind(htmlElement));
setTimeout(async () => {
if (isHovering && tooltip) {
// Re-use the already computed hover options if they exist.
if (!hoverOptions) {
const target: IHoverDelegateTarget = {
targetElements: [this],
dispose: () => { }
};
hoverOptions = {
text: localize('iconLabel.loading', "Loading..."),
target,
hoverPosition: HoverPosition.BELOW
};
hoverDisposable = adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering);
const resolvedTooltip = (await tooltip(tokenSource.token)) ?? (!isString(markdownTooltip) ? markdownTooltip.markdownNotSupportedFallback : undefined);
if (resolvedTooltip) {
hoverOptions = {
text: resolvedTooltip,
target,
hoverPosition: HoverPosition.BELOW
};
// awaiting the tooltip could take a while. Make sure we're still hovering.
hoverDisposable = adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering);
} else if (hoverDisposable) {
hoverDisposable.dispose();
hoverDisposable = undefined;
}
}
}
mouseMoveDisposable.dispose();
}, hoverDelegate.delay);
}
const mouseOverDisposable = this._register(domEvent(htmlElement, dom.EventType.MOUSE_OVER, true)(mouseOver.bind(htmlElement)));
this.customHovers.set(htmlElement, mouseOverDisposable);
}
}
function adjustXAndShowCustomHover(hoverOptions: IHoverDelegateOptions | undefined, mouseX: number | undefined, hoverDelegate: IHoverDelegate, isHovering: boolean): IDisposable | undefined {
if (hoverOptions && isHovering) {
if (mouseX !== undefined) {
(<IHoverDelegateTarget>hoverOptions.target).x = mouseX + 10;
}
return hoverDelegate.showHover(hoverOptions);
}
return undefined;
}

View file

@ -10,13 +10,10 @@ import { IMatch } from 'vs/base/common/filters';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Range } from 'vs/base/common/range';
import { equals } from 'vs/base/common/objects';
import { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { isFunction, isString } from 'vs/base/common/types';
import { domEvent } from 'vs/base/browser/event';
import { localize } from 'vs/nls';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget';
import { CancellationToken } from 'vs/base/common/cancellation';
import { setupCustomHover, setupNativeHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover';
export interface IIconLabelCreationOptions {
supportHighlights?: boolean;
@ -91,17 +88,17 @@ class FastLabelNode {
export class IconLabel extends Disposable {
private domNode: FastLabelNode;
private readonly domNode: FastLabelNode;
private nameNode: Label | LabelWithHighlights;
private readonly nameNode: Label | LabelWithHighlights;
private descriptionContainer: FastLabelNode;
private readonly descriptionContainer: FastLabelNode;
private descriptionNode: FastLabelNode | HighlightedLabel | undefined;
private descriptionNodeFactory: () => FastLabelNode | HighlightedLabel;
private readonly descriptionNodeFactory: () => FastLabelNode | HighlightedLabel;
private labelContainer: HTMLElement;
private readonly labelContainer: HTMLElement;
private hoverDelegate: IHoverDelegate | undefined = undefined;
private readonly hoverDelegate: IHoverDelegate | undefined;
private readonly customHovers: Map<HTMLElement, IDisposable> = new Map();
constructor(container: HTMLElement, options?: IIconLabelCreationOptions) {
@ -126,9 +123,7 @@ export class IconLabel extends Disposable {
this.descriptionNodeFactory = () => this._register(new FastLabelNode(dom.append(this.descriptionContainer.element, dom.$('span.label-description'))));
}
if (options?.hoverDelegate) {
this.hoverDelegate = options.hoverDelegate;
}
this.hoverDelegate = options?.hoverDelegate;
}
get element(): HTMLElement {
@ -185,116 +180,21 @@ export class IconLabel extends Disposable {
}
if (!this.hoverDelegate) {
return this.setupNativeHover(htmlElement, tooltip);
setupNativeHover(htmlElement, tooltip);
} else {
return this.setupCustomHover(this.hoverDelegate, htmlElement, tooltip);
const hoverDisposable = setupCustomHover(this.hoverDelegate, htmlElement, tooltip);
if (hoverDisposable) {
this.customHovers.set(htmlElement, hoverDisposable);
}
}
}
private static adjustXAndShowCustomHover(hoverOptions: IHoverDelegateOptions | undefined, mouseX: number | undefined, hoverDelegate: IHoverDelegate, isHovering: boolean): IDisposable | undefined {
if (hoverOptions && isHovering) {
if (mouseX !== undefined) {
(<IHoverDelegateTarget>hoverOptions.target).x = mouseX + 10;
}
return hoverDelegate.showHover(hoverOptions);
public override dispose() {
super.dispose();
for (const disposable of this.customHovers.values()) {
disposable.dispose();
}
return undefined;
}
private getTooltipForCustom(markdownTooltip: string | IIconLabelMarkdownString): (token: CancellationToken) => Promise<string | IMarkdownString | undefined> {
if (isString(markdownTooltip)) {
return async () => markdownTooltip;
} else if (isFunction(markdownTooltip.markdown)) {
return markdownTooltip.markdown;
} else {
const markdown = markdownTooltip.markdown;
return async () => markdown;
}
}
private setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, markdownTooltip: string | IIconLabelMarkdownString): void {
htmlElement.setAttribute('title', '');
htmlElement.removeAttribute('title');
let tooltip = this.getTooltipForCustom(markdownTooltip);
let hoverOptions: IHoverDelegateOptions | undefined;
let mouseX: number | undefined;
let isHovering = false;
let tokenSource: CancellationTokenSource;
let hoverDisposable: IDisposable | undefined;
function mouseOver(this: HTMLElement, e: MouseEvent): void {
if (isHovering) {
return;
}
tokenSource = new CancellationTokenSource();
function mouseLeaveOrDown(this: HTMLElement, e: MouseEvent): void {
const isMouseDown = e.type === dom.EventType.MOUSE_DOWN;
if (isMouseDown) {
hoverDisposable?.dispose();
hoverDisposable = undefined;
}
if (isMouseDown || (<any>e).fromElement === htmlElement) {
isHovering = false;
hoverOptions = undefined;
tokenSource.dispose(true);
mouseLeaveDisposable.dispose();
mouseDownDisposable.dispose();
}
}
const mouseLeaveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_LEAVE, true)(mouseLeaveOrDown.bind(htmlElement));
const mouseDownDisposable = domEvent(htmlElement, dom.EventType.MOUSE_DOWN, true)(mouseLeaveOrDown.bind(htmlElement));
isHovering = true;
function mouseMove(this: HTMLElement, e: MouseEvent): void {
mouseX = e.x;
}
const mouseMoveDisposable = domEvent(htmlElement, dom.EventType.MOUSE_MOVE, true)(mouseMove.bind(htmlElement));
setTimeout(async () => {
if (isHovering && tooltip) {
// Re-use the already computed hover options if they exist.
if (!hoverOptions) {
const target: IHoverDelegateTarget = {
targetElements: [this],
dispose: () => { }
};
hoverOptions = {
text: localize('iconLabel.loading', "Loading..."),
target,
hoverPosition: HoverPosition.BELOW
};
hoverDisposable = IconLabel.adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering);
const resolvedTooltip = (await tooltip(tokenSource.token)) ?? (!isString(markdownTooltip) ? markdownTooltip.markdownNotSupportedFallback : undefined);
if (resolvedTooltip) {
hoverOptions = {
text: resolvedTooltip,
target,
hoverPosition: HoverPosition.BELOW
};
// awaiting the tooltip could take a while. Make sure we're still hovering.
hoverDisposable = IconLabel.adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering);
} else if (hoverDisposable) {
hoverDisposable.dispose();
hoverDisposable = undefined;
}
}
}
mouseMoveDisposable.dispose();
}, hoverDelegate.delay);
}
const mouseOverDisposable = this._register(domEvent(htmlElement, dom.EventType.MOUSE_OVER, true)(mouseOver.bind(htmlElement)));
this.customHovers.set(htmlElement, mouseOverDisposable);
}
private setupNativeHover(htmlElement: HTMLElement, tooltip: string | IIconLabelMarkdownString | undefined): void {
let stringTooltip: string = '';
if (isString(tooltip)) {
stringTooltip = tooltip;
} else if (tooltip?.markdownNotSupportedFallback) {
stringTooltip = tooltip.markdownNotSupportedFallback;
}
htmlElement.title = stringTooltip;
this.customHovers.clear();
}
}

View file

@ -0,0 +1,129 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isFunction, isString } from 'vs/base/common/types';
import * as dom from 'vs/base/browser/dom';
import { IIconLabelMarkdownString } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { IDisposable } from 'vs/base/common/lifecycle';
import { DomEmitter } from 'vs/base/browser/event';
import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget';
import { localize } from 'vs/nls';
import { IMarkdownString } from 'vs/base/common/htmlContent';
export function setupNativeHover(htmlElement: HTMLElement, tooltip: string | IIconLabelMarkdownString | undefined): void {
if (isString(tooltip)) {
htmlElement.title = tooltip;
} else if (tooltip?.markdownNotSupportedFallback) {
htmlElement.title = tooltip.markdownNotSupportedFallback;
} else {
htmlElement.removeAttribute('title');
}
}
export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, markdownTooltip: string | IIconLabelMarkdownString | undefined): IDisposable | undefined {
if (!markdownTooltip) {
return undefined;
}
const tooltip = getTooltipForCustom(markdownTooltip);
let hoverOptions: IHoverDelegateOptions | undefined;
let mouseX: number | undefined;
let isHovering = false;
let tokenSource: CancellationTokenSource;
let hoverDisposable: IDisposable | undefined;
const mouseOverDomEmitter = new DomEmitter(htmlElement, dom.EventType.MOUSE_OVER, true);
mouseOverDomEmitter.event((e: MouseEvent) => {
if (isHovering) {
return;
}
tokenSource = new CancellationTokenSource();
function mouseLeaveOrDown(e: MouseEvent): void {
const isMouseDown = e.type === dom.EventType.MOUSE_DOWN;
if (isMouseDown) {
hoverDisposable?.dispose();
hoverDisposable = undefined;
}
if (isMouseDown || (<any>e).fromElement === htmlElement) {
isHovering = false;
hoverOptions = undefined;
tokenSource.dispose(true);
mouseLeaveDomEmitter.dispose();
mouseDownDomEmitter.dispose();
}
}
const mouseLeaveDomEmitter = new DomEmitter(htmlElement, dom.EventType.MOUSE_LEAVE, true);
mouseLeaveDomEmitter.event(mouseLeaveOrDown);
const mouseDownDomEmitter = new DomEmitter(htmlElement, dom.EventType.MOUSE_DOWN, true);
mouseDownDomEmitter.event(mouseLeaveOrDown);
isHovering = true;
function mouseMove(e: MouseEvent): void {
mouseX = e.x;
}
const mouseMoveDomEmitter = new DomEmitter(htmlElement, dom.EventType.MOUSE_MOVE, true);
mouseMoveDomEmitter.event(mouseMove);
setTimeout(async () => {
if (isHovering && tooltip) {
// Re-use the already computed hover options if they exist.
if (!hoverOptions) {
const target: IHoverDelegateTarget = {
targetElements: [htmlElement],
dispose: () => { }
};
hoverOptions = {
text: localize('iconLabel.loading', "Loading..."),
target,
hoverPosition: HoverPosition.BELOW
};
hoverDisposable = adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering);
const resolvedTooltip = (await tooltip(tokenSource.token)) ?? (!isString(markdownTooltip) ? markdownTooltip.markdownNotSupportedFallback : undefined);
if (resolvedTooltip) {
hoverOptions = {
text: resolvedTooltip,
target,
hoverPosition: HoverPosition.BELOW
};
// awaiting the tooltip could take a while. Make sure we're still hovering.
hoverDisposable = adjustXAndShowCustomHover(hoverOptions, mouseX, hoverDelegate, isHovering);
} else if (hoverDisposable) {
hoverDisposable.dispose();
hoverDisposable = undefined;
}
}
}
mouseMoveDomEmitter.dispose();
}, hoverDelegate.delay);
});
return mouseOverDomEmitter;
}
function getTooltipForCustom(markdownTooltip: string | IIconLabelMarkdownString): (token: CancellationToken) => Promise<string | IMarkdownString | undefined> {
if (isString(markdownTooltip)) {
return async () => markdownTooltip;
} else if (isFunction(markdownTooltip.markdown)) {
return markdownTooltip.markdown;
} else {
const markdown = markdownTooltip.markdown;
return async () => markdown;
}
}
function adjustXAndShowCustomHover(hoverOptions: IHoverDelegateOptions | undefined, mouseX: number | undefined, hoverDelegate: IHoverDelegate, isHovering: boolean): IDisposable | undefined {
if (hoverOptions && isHovering) {
if (mouseX !== undefined) {
(<IHoverDelegateTarget>hoverOptions.target).x = mouseX + 10;
}
return hoverDelegate.showHover(hoverOptions);
}
return undefined;
}

View file

@ -89,6 +89,16 @@ export function isMarkdownString(thing: any): thing is IMarkdownString {
return false;
}
export function markdownStringEqual(a: IMarkdownString, b: IMarkdownString): boolean {
if (a === b) {
return true;
} else if (!a || !b) {
return false;
} else {
return a.value === b.value && a.isTrusted === b.isTrusted && a.supportThemeIcons === b.supportThemeIcons;
}
}
export function escapeMarkdownSyntaxTokens(text: string): string {
// escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
return text.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&');

View file

@ -43,6 +43,11 @@ import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { hash } from 'vs/base/common/hash';
import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover';
import { IHoverService } from 'vs/workbench/services/hover/browser/hover';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { isMarkdownString, markdownStringEqual } from 'vs/base/common/htmlContent';
import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate';
interface IStatusbarEntryPriority {
@ -426,6 +431,8 @@ export class StatusbarPart extends Part implements IStatusbarService {
private leftItemsContainer: HTMLElement | undefined;
private rightItemsContainer: HTMLElement | undefined;
private hoverDelegate: IHoverDelegate;
constructor(
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@ -434,10 +441,17 @@ export class StatusbarPart extends Part implements IStatusbarService {
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IHoverService hoverService: IHoverService,
@IConfigurationService configurationService: IConfigurationService
) {
super(Parts.STATUSBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
this.registerListeners();
this.hoverDelegate = {
showHover: (options: IHoverDelegateOptions) => hoverService.showHover(options),
delay: <number>configurationService.getValue('workbench.hover.delay')
};
}
private registerListeners(): void {
@ -489,7 +503,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
// Create item
const itemContainer = this.doCreateStatusItem(id, alignment, ...coalesce([entry.showBeak ? 'has-beak' : undefined]));
const item = this.instantiationService.createInstance(StatusbarEntryItem, itemContainer, entry);
const item = this.instantiationService.createInstance(StatusbarEntryItem, itemContainer, entry, this.hoverDelegate);
// Append to parent
this.appendOneStatusbarEntry(itemContainer, alignment, priority);
@ -821,6 +835,8 @@ class StatusbarEntryItem extends Disposable {
readonly labelContainer: HTMLElement;
private readonly label: StatusBarCodiconLabel;
private customHover: IDisposable | undefined;
private entry: IStatusbarEntry | undefined = undefined;
get name(): string { return assertIsDefined(this.entry).name; }
@ -833,6 +849,7 @@ class StatusbarEntryItem extends Disposable {
constructor(
private container: HTMLElement,
entry: IStatusbarEntry,
private readonly customHoverDelegate: IHoverDelegate,
@ICommandService private readonly commandService: ICommandService,
@INotificationService private readonly notificationService: INotificationService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@ -881,8 +898,15 @@ class StatusbarEntryItem extends Disposable {
}
// Update: Tooltip (on the container, because label can be disabled)
if (!this.entry || entry.tooltip !== this.entry.tooltip) {
if (entry.tooltip) {
if (!this.entry || !isEqualTooltip(this.entry, entry)) {
if (this.customHover) {
this.customHover.dispose();
this.customHover = undefined;
}
if (isMarkdownString(entry.tooltip)) {
this.container.removeAttribute('title');
this.customHover = setupCustomHover(this.customHoverDelegate, this.container, { markdown: entry.tooltip, markdownNotSupportedFallback: undefined });
} else if (entry.tooltip) {
this.container.title = entry.tooltip;
} else {
this.container.title = '';
@ -993,9 +1017,24 @@ class StatusbarEntryItem extends Disposable {
dispose(this.backgroundListener);
dispose(this.commandMouseListener);
dispose(this.commandKeyboardListener);
if (this.customHover) {
this.customHover.dispose();
}
}
}
function isEqualTooltip(e1: IStatusbarEntry, e2: IStatusbarEntry) {
const t1 = e1.tooltip;
const t2 = e2.tooltip;
if (t1 === undefined) {
return t2 === undefined;
}
if (isMarkdownString(t1)) {
return isMarkdownString(t2) && markdownStringEqual(t1, t2);
}
return t1 === t2;
}
registerThemingParticipant((theme, collector) => {
if (theme.type !== ColorScheme.HIGH_CONTRAST) {
const statusBarItemHoverBackground = theme.getColor(STATUS_BAR_ITEM_HOVER_BACKGROUND);

View file

@ -18,6 +18,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { isString } from 'vs/base/common/types';
const $ = dom.$;
type TargetRect = {
@ -66,7 +67,7 @@ export class HoverWidget extends Widget {
) {
super();
this._linkHandler = options.linkHandler || this._openerService.open;
this._linkHandler = options.linkHandler || (url => this._openerService.open(url, { allowCommands: (!isString(options.text) && options.text.isTrusted) }));
this._target = 'targetElements' in options.target ? options.target : new ElementHoverTarget(options.target);

View file

@ -8,6 +8,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { Event } from 'vs/base/common/event';
import { Command } from 'vs/editor/common/modes';
import { IMarkdownString } from 'vs/base/common/htmlContent';
export const IStatusbarService = createDecorator<IStatusbarService>('statusbarService');
@ -48,7 +49,7 @@ export interface IStatusbarEntry {
/**
* An optional tooltip text to show when you hover over the entry
*/
readonly tooltip?: string;
readonly tooltip?: string | IMarkdownString;
/**
* An optional color to use for the entry
@ -74,6 +75,7 @@ export interface IStatusbarEntry {
* Will enable a spinning icon in front of the text to indicate progress.
*/
readonly showProgress?: boolean;
}
export interface IStatusbarService {