Path: blob/main/src/vs/workbench/api/browser/mainThreadMessageService.ts
3296 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 * as nls from '../../../nls.js';6import Severity from '../../../base/common/severity.js';7import { IAction, toAction } from '../../../base/common/actions.js';8import { MainThreadMessageServiceShape, MainContext, MainThreadMessageOptions } from '../common/extHost.protocol.js';9import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js';10import { IDialogService, IPromptButton } from '../../../platform/dialogs/common/dialogs.js';11import { INotificationService, INotificationSource, NotificationPriority } from '../../../platform/notification/common/notification.js';12import { Event } from '../../../base/common/event.js';13import { ICommandService } from '../../../platform/commands/common/commands.js';14import { IExtensionService } from '../../services/extensions/common/extensions.js';15import { IDisposable } from '../../../base/common/lifecycle.js';1617@extHostNamedCustomer(MainContext.MainThreadMessageService)18export class MainThreadMessageService implements MainThreadMessageServiceShape {1920private extensionsListener: IDisposable;2122private static readonly URGENT_NOTIFICATION_SOURCES = [23'vscode.github-authentication',24'vscode.microsoft-authentication'25];2627constructor(28extHostContext: IExtHostContext,29@INotificationService private readonly _notificationService: INotificationService,30@ICommandService private readonly _commandService: ICommandService,31@IDialogService private readonly _dialogService: IDialogService,32@IExtensionService extensionService: IExtensionService33) {34this.extensionsListener = extensionService.onDidChangeExtensions(e => {35for (const extension of e.removed) {36this._notificationService.removeFilter(extension.identifier.value);37}38});39}4041dispose(): void {42this.extensionsListener.dispose();43}4445$showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number }[]): Promise<number | undefined> {46if (options.modal) {47return this._showModalMessage(severity, message, options.detail, commands, options.useCustom);48} else {49return this._showMessage(severity, message, commands, options);50}51}5253private _showMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number }[], options: MainThreadMessageOptions): Promise<number | undefined> {5455return new Promise<number | undefined>(resolve => {5657const primaryActions: IAction[] = commands.map(command => toAction({58id: `_extension_message_handle_${command.handle}`,59label: command.title,60enabled: true,61run: () => {62resolve(command.handle);63return Promise.resolve();64}65}));6667let source: string | INotificationSource | undefined;68let sourceIsUrgent = false;69if (options.source) {70source = {71label: options.source.label,72id: options.source.identifier.value73};74sourceIsUrgent = MainThreadMessageService.URGENT_NOTIFICATION_SOURCES.includes(source.id);75}7677if (!source) {78source = nls.localize('defaultSource', "Extension");79}8081const secondaryActions: IAction[] = [];82if (options.source) {83secondaryActions.push(toAction({84id: options.source.identifier.value,85label: nls.localize('manageExtension', "Manage Extension"),86run: () => {87return this._commandService.executeCommand('_extensions.manage', options.source!.identifier.value);88}89}));90}9192const messageHandle = this._notificationService.notify({93severity,94message,95actions: { primary: primaryActions, secondary: secondaryActions },96source,97priority: sourceIsUrgent ? NotificationPriority.URGENT : NotificationPriority.DEFAULT,98sticky: sourceIsUrgent99});100101// if promise has not been resolved yet, now is the time to ensure a return value102// otherwise if already resolved it means the user clicked one of the buttons103Event.once(messageHandle.onDidClose)(() => {104resolve(undefined);105});106});107}108109private async _showModalMessage(severity: Severity, message: string, detail: string | undefined, commands: { title: string; isCloseAffordance: boolean; handle: number }[], useCustom?: boolean): Promise<number | undefined> {110const buttons: IPromptButton<number>[] = [];111let cancelButton: IPromptButton<number | undefined> | undefined = undefined;112113for (const command of commands) {114const button: IPromptButton<number> = {115label: command.title,116run: () => command.handle117};118119if (command.isCloseAffordance) {120cancelButton = button;121} else {122buttons.push(button);123}124}125126if (!cancelButton) {127if (buttons.length > 0) {128cancelButton = {129label: nls.localize('cancel', "Cancel"),130run: () => undefined131};132} else {133cancelButton = {134label: nls.localize({ key: 'ok', comment: ['&& denotes a mnemonic'] }, "&&OK"),135run: () => undefined136};137}138}139140const { result } = await this._dialogService.prompt({141type: severity,142message,143detail,144buttons,145cancelButton,146custom: useCustom147});148149return result;150}151}152153154