Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StatusBarItem - proposed API for async hover #238297

Merged
merged 14 commits into from
Jan 21, 2025
3 changes: 3 additions & 0 deletions src/vs/platform/extensions/common/extensionsApiProposals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,9 @@ const _allApiProposals = {
speech: {
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.speech.d.ts',
},
statusBarItemTooltip: {
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.statusBarItemTooltip.d.ts',
},
tabInputMultiDiff: {
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tabInputMultiDiff.d.ts',
},
Expand Down
24 changes: 18 additions & 6 deletions src/vs/workbench/api/browser/mainThreadStatusBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { MainThreadStatusBarShape, MainContext, ExtHostContext, StatusBarItemDto } from '../common/extHost.protocol.js';
import { MainThreadStatusBarShape, MainContext, ExtHostContext, StatusBarItemDto, ExtHostStatusBarShape } from '../common/extHost.protocol.js';
import { ThemeColor } from '../../../base/common/themables.js';
import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js';
import { DisposableStore, toDisposable } from '../../../base/common/lifecycle.js';
Expand All @@ -12,29 +12,32 @@ import { IAccessibilityInformation } from '../../../platform/accessibility/commo
import { IMarkdownString } from '../../../base/common/htmlContent.js';
import { IExtensionStatusBarItemService, StatusBarUpdateKind } from './statusBarExtensionPoint.js';
import { IStatusbarEntry, StatusbarAlignment } from '../../services/statusbar/browser/statusbar.js';
import { IManagedHoverTooltipMarkdownString } from '../../../base/browser/ui/hover/hover.js';
import { CancellationToken } from '../../../base/common/cancellation.js';

@extHostNamedCustomer(MainContext.MainThreadStatusBar)
export class MainThreadStatusBar implements MainThreadStatusBarShape {

private readonly _proxy: ExtHostStatusBarShape;
private readonly _store = new DisposableStore();

constructor(
extHostContext: IExtHostContext,
@IExtensionStatusBarItemService private readonly statusbarService: IExtensionStatusBarItemService
) {
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostStatusBar);
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostStatusBar);

// once, at startup read existing items and send them over
const entries: StatusBarItemDto[] = [];
for (const [entryId, item] of statusbarService.getEntries()) {
entries.push(asDto(entryId, item));
}

proxy.$acceptStaticEntries(entries);
this._proxy.$acceptStaticEntries(entries);

this._store.add(statusbarService.onDidChange(e => {
if (e.added) {
proxy.$acceptStaticEntries([asDto(e.added[0], e.added[1])]);
this._proxy.$acceptStaticEntries([asDto(e.added[0], e.added[1])]);
}
}));

Expand All @@ -56,8 +59,17 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
this._store.dispose();
}

$setEntry(entryId: string, id: string, extensionId: string | undefined, name: string, text: string, tooltip: IMarkdownString | string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: ThemeColor | undefined, alignLeft: boolean, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): void {
const kind = this.statusbarService.setOrUpdateEntry(entryId, id, extensionId, name, text, tooltip, command, color, backgroundColor, alignLeft, priority, accessibilityInformation);
$setEntry(entryId: string, id: string, extensionId: string | undefined, name: string, text: string, tooltip: IMarkdownString | string | undefined, hasTooltipProvider: boolean, command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: ThemeColor | undefined, alignLeft: boolean, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): void {
const tooltipOrTooltipProvider = hasTooltipProvider
? {
markdown: (cancellation: CancellationToken) => {
return this._proxy.$provideTooltip(entryId, cancellation);
},
markdownNotSupportedFallback: undefined
} satisfies IManagedHoverTooltipMarkdownString
: tooltip;

const kind = this.statusbarService.setOrUpdateEntry(entryId, id, extensionId, name, text, tooltipOrTooltipProvider, command, color, backgroundColor, alignLeft, priority, accessibilityInformation);
if (kind === StatusBarUpdateKind.DidDefine) {
this._store.add(toDisposable(() => this.statusbarService.unsetEntry(entryId)));
}
Expand Down
10 changes: 6 additions & 4 deletions src/vs/workbench/api/browser/statusBarExtensionPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment,
import { ThemeColor } from '../../../base/common/themables.js';
import { Command } from '../../../editor/common/languages.js';
import { IAccessibilityInformation, isAccessibilityInformation } from '../../../platform/accessibility/common/accessibility.js';
import { IMarkdownString } from '../../../base/common/htmlContent.js';
import { IMarkdownString, isMarkdownString } from '../../../base/common/htmlContent.js';
import { getCodiconAriaLabel } from '../../../base/common/iconLabels.js';
import { hash } from '../../../base/common/hash.js';
import { Event, Emitter } from '../../../base/common/event.js';
Expand All @@ -22,6 +22,7 @@ import { Iterable } from '../../../base/common/iterator.js';
import { ExtensionIdentifier } from '../../../platform/extensions/common/extensions.js';
import { asStatusBarItemIdentifier } from '../common/extHostTypes.js';
import { STATUS_BAR_ERROR_ITEM_BACKGROUND, STATUS_BAR_WARNING_ITEM_BACKGROUND } from '../../common/theme.js';
import { IManagedHoverTooltipMarkdownString } from '../../../base/browser/ui/hover/hover.js';


// --- service
Expand Down Expand Up @@ -49,7 +50,7 @@ export interface IExtensionStatusBarItemService {

onDidChange: Event<IExtensionStatusBarItemChangeEvent>;

setOrUpdateEntry(id: string, statusId: string, extensionId: string | undefined, name: string, text: string, tooltip: IMarkdownString | string | undefined, command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: ThemeColor | undefined, alignLeft: boolean, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): StatusBarUpdateKind;
setOrUpdateEntry(id: string, statusId: string, extensionId: string | undefined, name: string, text: string, tooltip: IMarkdownString | string | undefined | IManagedHoverTooltipMarkdownString, command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: ThemeColor | undefined, alignLeft: boolean, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): StatusBarUpdateKind;

unsetEntry(id: string): void;

Expand All @@ -75,7 +76,8 @@ class ExtensionStatusBarItemService implements IExtensionStatusBarItemService {
}

setOrUpdateEntry(entryId: string,
id: string, extensionId: string | undefined, name: string, text: string, tooltip: IMarkdownString | string | undefined,
id: string, extensionId: string | undefined, name: string, text: string,
tooltip: IMarkdownString | string | undefined | IManagedHoverTooltipMarkdownString,
command: Command | undefined, color: string | ThemeColor | undefined, backgroundColor: ThemeColor | undefined,
alignLeft: boolean, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined
): StatusBarUpdateKind {
Expand All @@ -87,7 +89,7 @@ class ExtensionStatusBarItemService implements IExtensionStatusBarItemService {
role = accessibilityInformation.role;
} else {
ariaLabel = getCodiconAriaLabel(text);
if (tooltip) {
if (typeof tooltip === 'string' || isMarkdownString(tooltip)) {
const tooltipString = typeof tooltip === 'string' ? tooltip : tooltip.value;
ariaLabel += `, ${tooltipString}`;
}
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ export interface MainThreadQuickOpenShape extends IDisposable {
}

export interface MainThreadStatusBarShape extends IDisposable {
$setEntry(id: string, statusId: string, extensionId: string | undefined, statusName: string, text: string, tooltip: IMarkdownString | string | undefined, command: ICommandDto | undefined, color: string | ThemeColor | undefined, backgroundColor: string | ThemeColor | undefined, alignLeft: boolean, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): void;
$setEntry(id: string, statusId: string, extensionId: string | undefined, statusName: string, text: string, tooltip: IMarkdownString | string | undefined, hasTooltipProvider: boolean, command: ICommandDto | undefined, color: string | ThemeColor | undefined, backgroundColor: string | ThemeColor | undefined, alignLeft: boolean, priority: number | undefined, accessibilityInformation: IAccessibilityInformation | undefined): void;
$disposeEntry(id: string): void;
}

Expand All @@ -693,6 +693,7 @@ export type StatusBarItemDto = {

export interface ExtHostStatusBarShape {
$acceptStaticEntries(added?: StatusBarItemDto[]): void;
$provideTooltip(entryId: string, cancellation: CancellationToken): Promise<string | IMarkdownString | undefined>;
}

export interface MainThreadStorageShape extends IDisposable {
Expand Down
43 changes: 38 additions & 5 deletions src/vs/workbench/api/common/extHostStatusBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { DisposableStore } from '../../../base/common/lifecycle.js';
import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
import { MarkdownString } from './extHostTypeConverters.js';
import { isNumber } from '../../../base/common/types.js';
import { IMarkdownString } from '../../../base/common/htmlContent.js';


export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
Expand Down Expand Up @@ -43,6 +44,7 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {

private _text: string = '';
private _tooltip?: string | vscode.MarkdownString;
private _tooltip2?: (token: vscode.CancellationToken) => Promise<string | vscode.MarkdownString | undefined>;
private _name?: string;
private _color?: string | ThemeColor;
private _backgroundColor?: ThemeColor;
Expand All @@ -57,9 +59,9 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
private _timeoutHandle: any;
private _accessibilityInformation?: vscode.AccessibilityInformation;

constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, staticItems: ReadonlyMap<string, StatusBarItemDto>, extension: IExtensionDescription, id?: string, alignment?: ExtHostStatusBarAlignment, priority?: number);
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, staticItems: ReadonlyMap<string, StatusBarItemDto>, extension: IExtensionDescription | undefined, id: string, alignment?: ExtHostStatusBarAlignment, priority?: number);
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, staticItems: ReadonlyMap<string, StatusBarItemDto>, extension?: IExtensionDescription, id?: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) {
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, staticItems: Map<string, StatusBarItemDto>, entries: Map<string, ExtHostStatusBarEntry>, extension: IExtensionDescription, id?: string, alignment?: ExtHostStatusBarAlignment, priority?: number);
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, staticItems: Map<string, StatusBarItemDto>, entries: Map<string, ExtHostStatusBarEntry>, extension: IExtensionDescription | undefined, id: string, alignment?: ExtHostStatusBarAlignment, priority?: number);
constructor(proxy: MainThreadStatusBarShape, commands: CommandsConverter, private staticItems: Map<string, StatusBarItemDto>, private entries: Map<string, ExtHostStatusBarEntry>, extension?: IExtensionDescription, id?: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) {
lszomoru marked this conversation as resolved.
Show resolved Hide resolved
this.#proxy = proxy;
this.#commands = commands;

Expand Down Expand Up @@ -113,6 +115,10 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
return this._id ?? this._extension!.identifier.value;
}

public get entryId(): string {
return this._entryId;
}

public get alignment(): vscode.StatusBarAlignment {
return this._alignment;
}
Expand All @@ -133,6 +139,10 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
return this._tooltip;
}

public get tooltip2(): ((token: vscode.CancellationToken) => Promise<string | vscode.MarkdownString | undefined>) | undefined {
return this._tooltip2;
lszomoru marked this conversation as resolved.
Show resolved Hide resolved
}

public get color(): string | ThemeColor | undefined {
return this._color;
}
Expand Down Expand Up @@ -164,6 +174,11 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
this.update();
}

lszomoru marked this conversation as resolved.
Show resolved Hide resolved
public set tooltip2(tooltip: any) {
this._tooltip2 = tooltip;
lszomoru marked this conversation as resolved.
Show resolved Hide resolved
this.update();
}

public set color(color: string | ThemeColor | undefined) {
this._color = color;
this.update();
Expand Down Expand Up @@ -259,9 +274,10 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {
}

const tooltip = MarkdownString.fromStrict(this._tooltip);
const hasTooltipProvider = typeof this._tooltip2 === 'function';

// Set to status bar
this.#proxy.$setEntry(this._entryId, id, this._extension?.identifier.value, name, this._text, tooltip, this._command?.internal, color,
this.#proxy.$setEntry(this._entryId, id, this._extension?.identifier.value, name, this._text, tooltip, hasTooltipProvider, this._command?.internal, color,
this._backgroundColor, this._alignment === ExtHostStatusBarAlignment.Left,
this._priority, this._accessibilityInformation);

Expand All @@ -272,6 +288,10 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem {

public dispose(): void {
this.hide();

this.staticItems.delete(this._entryId);
lszomoru marked this conversation as resolved.
Show resolved Hide resolved
this.entries.delete(this._entryId);
lszomoru marked this conversation as resolved.
Show resolved Hide resolved

this._disposed = true;
}
}
Expand Down Expand Up @@ -320,6 +340,7 @@ export class ExtHostStatusBar implements ExtHostStatusBarShape {
private readonly _proxy: MainThreadStatusBarShape;
private readonly _commands: CommandsConverter;
private readonly _statusMessage: StatusBarMessage;
private readonly _entries = new Map<string, ExtHostStatusBarEntry>();
private readonly _existingItems = new Map<string, StatusBarItemDto>();

constructor(mainContext: IMainContext, commands: CommandsConverter) {
Expand All @@ -334,10 +355,22 @@ export class ExtHostStatusBar implements ExtHostStatusBarShape {
}
}

async $provideTooltip(entryId: string, cancellation: vscode.CancellationToken): Promise<string | IMarkdownString | undefined> {
const entry = this._entries.get(entryId);
if (!entry) {
return undefined;
}

return MarkdownString.fromStrict(await entry.tooltip2?.(cancellation));
lszomoru marked this conversation as resolved.
Show resolved Hide resolved
}

createStatusBarEntry(extension: IExtensionDescription | undefined, id: string, alignment?: ExtHostStatusBarAlignment, priority?: number): vscode.StatusBarItem;
createStatusBarEntry(extension: IExtensionDescription, id?: string, alignment?: ExtHostStatusBarAlignment, priority?: number): vscode.StatusBarItem;
createStatusBarEntry(extension: IExtensionDescription, id: string, alignment?: ExtHostStatusBarAlignment, priority?: number): vscode.StatusBarItem {
return new ExtHostStatusBarEntry(this._proxy, this._commands, this._existingItems, extension, id, alignment, priority);
const entry = new ExtHostStatusBarEntry(this._proxy, this._commands, this._existingItems, this._entries, extension, id, alignment, priority);
this._entries.set(entry.entryId, entry);
lszomoru marked this conversation as resolved.
Show resolved Hide resolved

return entry;
}

setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): Disposable {
Expand Down
13 changes: 13 additions & 0 deletions src/vscode-dts/vscode.proposed.statusBarItemTooltip.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

declare module 'vscode' {

// https://github.com/microsoft/vscode/issues/234339

export interface StatusBarItem {
tooltip2?: (token: CancellationToken) => ProviderResult<string | MarkdownString>;
lszomoru marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading