diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 24106003124..8989321aead 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -28,7 +28,7 @@ import { CommandsRegistry, ICommand, ICommandEvent, ICommandHandler, ICommandSer import { IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationModel } from 'vs/platform/configuration/common/configuration'; import { Configuration, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService, IShowResult } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; import { IKeybindingEvent, IKeyboardEvent, KeybindingSource } from 'vs/platform/keybinding/common/keybinding'; @@ -183,8 +183,8 @@ export class SimpleDialogService implements IDialogService { return Promise.resolve(window.confirm(messageText)); } - public show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise { - return Promise.resolve(0); + public show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise { + return Promise.resolve({ choice: 0 }); } } diff --git a/src/vs/platform/dialogs/browser/dialogService.ts b/src/vs/platform/dialogs/browser/dialogService.ts index 0f1ef2dbcfb..517651193d1 100644 --- a/src/vs/platform/dialogs/browser/dialogService.ts +++ b/src/vs/platform/dialogs/browser/dialogService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IDialogService, IDialogOptions, IConfirmation, IConfirmationResult, DialogType } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService, IDialogOptions, IConfirmation, IConfirmationResult, DialogType, IShowResult } from 'vs/platform/dialogs/common/dialogs'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { ILogService } from 'vs/platform/log/common/log'; import Severity from 'vs/base/common/severity'; @@ -78,7 +78,7 @@ export class DialogService implements IDialogService { return (severity === Severity.Info) ? 'question' : (severity === Severity.Error) ? 'error' : (severity === Severity.Warning) ? 'warning' : 'none'; } - async show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise { + async show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise { this.logService.trace('DialogService#show', message); const dialogDisposables = new DisposableStore(); @@ -106,6 +106,8 @@ export class DialogService implements IDialogService { const result = await dialog.show(); dialogDisposables.dispose(); - return result.button; + return { + choice: result.button + }; } } diff --git a/src/vs/platform/dialogs/common/dialogs.ts b/src/vs/platform/dialogs/common/dialogs.ts index ab029f1944e..2081f736eb4 100644 --- a/src/vs/platform/dialogs/common/dialogs.ts +++ b/src/vs/platform/dialogs/common/dialogs.ts @@ -41,6 +41,22 @@ export interface IConfirmationResult { checkboxChecked?: boolean; } +export interface IShowResult { + + /** + * Selected choice index. If the user refused to choose, + * then a promise with index of `cancelId` option is returned. If there is no such + * option then promise with index `0` is returned. + */ + choice: number; + + /** + * This will only be defined if the confirmation was created + * with the checkbox option defined. + */ + checkboxChecked?: boolean; +} + export interface IPickAndOpenOptions { forceNewWindow?: boolean; defaultUri?: URI; @@ -151,7 +167,7 @@ export interface IDialogService { * then a promise with index of `cancelId` option is returned. If there is no such * option then promise with index `0` is returned. */ - show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise; + show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise; } export const IFileDialogService = createDecorator('fileDialogService'); diff --git a/src/vs/platform/dialogs/node/dialogIpc.ts b/src/vs/platform/dialogs/node/dialogIpc.ts index 30ee7ebd25c..00db1a7bf21 100644 --- a/src/vs/platform/dialogs/node/dialogIpc.ts +++ b/src/vs/platform/dialogs/node/dialogIpc.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService, IConfirmation, IConfirmationResult, IShowResult } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { Event } from 'vs/base/common/event'; @@ -31,11 +31,11 @@ export class DialogChannelClient implements IDialogService { constructor(private channel: IChannel) { } - show(severity: Severity, message: string, options: string[]): Promise { + show(severity: Severity, message: string, options: string[]): Promise { return this.channel.call('show', [severity, message, options]); } confirm(confirmation: IConfirmation): Promise { return this.channel.call('confirm', [confirmation]); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/api/browser/mainThreadMessageService.ts b/src/vs/workbench/api/browser/mainThreadMessageService.ts index 2c2507eca67..74c4109631f 100644 --- a/src/vs/workbench/api/browser/mainThreadMessageService.ts +++ b/src/vs/workbench/api/browser/mainThreadMessageService.ts @@ -97,7 +97,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape { }); } - private _showModalMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Promise { + private async _showModalMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Promise { let cancelId: number | undefined = undefined; const buttons = commands.map((command, index) => { @@ -118,7 +118,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape { cancelId = buttons.length - 1; } - return this._dialogService.show(severity, message, buttons, { cancelId }) - .then(result => result === commands.length ? undefined : commands[result].handle); + const { choice } = await this._dialogService.show(severity, message, buttons, { cancelId }); + return choice === commands.length ? undefined : commands[choice].handle; } } diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 83f0c3b53c6..43acbeb4f80 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -632,9 +632,9 @@ export class SimpleWindowsService implements IWindowsService { navigator.userAgent ); - const result = await this.dialogService.show(Severity.Info, this.productService.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail }); + const { choice } = await this.dialogService.show(Severity.Info, this.productService.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail }); - if (result === 0) { + if (choice === 0) { this.clipboardService.writeText(detail); } } diff --git a/src/vs/workbench/contrib/cli/node/cli.contribution.ts b/src/vs/workbench/contrib/cli/node/cli.contribution.ts index 943b4caefd0..612faac2eae 100644 --- a/src/vs/workbench/contrib/cli/node/cli.contribution.ts +++ b/src/vs/workbench/contrib/cli/node/cli.contribution.ts @@ -101,8 +101,8 @@ class InstallAction extends Action { return new Promise((resolve, reject) => { const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; - this.dialogService.show(Severity.Info, nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."), buttons, { cancelId: 1 }).then(choice => { - switch (choice) { + this.dialogService.show(Severity.Info, nls.localize('warnEscalation', "Code will now prompt with 'osascript' for Administrator privileges to install the shell command."), buttons, { cancelId: 1 }).then(result => { + switch (result.choice) { case 0 /* OK */: const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && ln -sf \'' + getSource() + '\' \'' + this.target + '\'\\" with administrator privileges"'; @@ -165,23 +165,22 @@ class UninstallAction extends Action { } private deleteSymlinkAsAdmin(): Promise { - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { const buttons = [nls.localize('ok', "OK"), nls.localize('cancel2', "Cancel")]; - this.dialogService.show(Severity.Info, nls.localize('warnEscalationUninstall', "Code will now prompt with 'osascript' for Administrator privileges to uninstall the shell command."), buttons, { cancelId: 1 }).then(choice => { - switch (choice) { - case 0 /* OK */: - const command = 'osascript -e "do shell script \\"rm \'' + this.target + '\'\\" with administrator privileges"'; + const { choice } = await this.dialogService.show(Severity.Info, nls.localize('warnEscalationUninstall', "Code will now prompt with 'osascript' for Administrator privileges to uninstall the shell command."), buttons, { cancelId: 1 }); + switch (choice) { + case 0 /* OK */: + const command = 'osascript -e "do shell script \\"rm \'' + this.target + '\'\\" with administrator privileges"'; - promisify(cp.exec)(command, {}) - .then(undefined, _ => Promise.reject(new Error(nls.localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", this.target)))) - .then(resolve, reject); - break; - case 1 /* Cancel */: - reject(new Error(nls.localize('aborted', "Aborted"))); - break; - } - }); + promisify(cp.exec)(command, {}) + .then(undefined, _ => Promise.reject(new Error(nls.localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", this.target)))) + .then(resolve, reject); + break; + case 1 /* Cancel */: + reject(new Error(nls.localize('aborted', "Aborted"))); + break; + } }); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 0758b523ae3..31ea15e6733 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -168,7 +168,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { } private registerListeners(): void { - this.toDispose.push(this.editor.onMouseDown((e: IEditorMouseEvent) => { + this.toDispose.push(this.editor.onMouseDown(async (e: IEditorMouseEvent) => { const data = e.target.detail as IMarginData; const model = this.editor.getModel(); if (!e.target.position || !model || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { @@ -213,18 +213,18 @@ export class DebugEditorContribution implements IDebugEditorContribution { logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition") ); - this.dialogService.show(severity.Info, disable ? disabling : enabling, [ + const { choice } = await this.dialogService.show(severity.Info, disable ? disabling : enabling, [ nls.localize('removeLogPoint', "Remove {0}", breakpointType), nls.localize('disableLogPoint', "{0} {1}", disable ? nls.localize('disable', "Disable") : nls.localize('enable', "Enable"), breakpointType), nls.localize('cancel', "Cancel") - ], { cancelId: 2 }).then(choice => { - if (choice === 0) { - breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId())); - } - if (choice === 1) { - breakpoints.forEach(bp => this.debugService.enableOrDisableBreakpoints(!disable, bp)); - } - }); + ], { cancelId: 2 }); + + if (choice === 0) { + breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId())); + } + if (choice === 1) { + breakpoints.forEach(bp => this.debugService.enableOrDisableBreakpoints(!disable, bp)); + } } else { breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId())); } diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 13898ad10bb..db7c61c54e6 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -670,16 +670,15 @@ export class DebugService implements IDebugService { return Promise.resolve(config); } - private showError(message: string, errorActions: ReadonlyArray = []): Promise { + private async showError(message: string, errorActions: ReadonlyArray = []): Promise { const configureAction = this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL); const actions = [...errorActions, configureAction]; - return this.dialogService.show(severity.Error, message, actions.map(a => a.label).concat(nls.localize('cancel', "Cancel")), { cancelId: actions.length }).then(choice => { - if (choice < actions.length) { - return actions[choice].run(); - } + const { choice } = await this.dialogService.show(severity.Error, message, actions.map(a => a.label).concat(nls.localize('cancel', "Cancel")), { cancelId: actions.length }); + if (choice < actions.length) { + return actions[choice].run(); + } - return undefined; - }); + return undefined; } //---- task management diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 6061e787c25..cf12d484239 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -630,7 +630,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { : localize('dropFolder', "Do you want to copy '{0}' or add '{0}' as a folder to the workspace?", basename(folders[0].uri)); } - const choice = await this.dialogService.show(Severity.Info, message, buttons); + const { choice } = await this.dialogService.show(Severity.Info, message, buttons); if (choice === buttons.length - 3) { return this.workspaceEditingService.addFolders(folders); } diff --git a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts index 857e42cd54c..a9fc289c823 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -416,9 +416,9 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { hideProgress(); progressReporter = null; - dialogService.show(Severity.Error, nls.localize('reconnectionPermanentFailure', "Cannot reconnect. Please reload the window."), [nls.localize('reloadWindow', "Reload Window"), nls.localize('cancel', "Cancel")], { cancelId: 1 }).then(choice => { + dialogService.show(Severity.Error, nls.localize('reconnectionPermanentFailure', "Cannot reconnect. Please reload the window."), [nls.localize('reloadWindow', "Reload Window"), nls.localize('cancel', "Cancel")], { cancelId: 1 }).then(result => { // Reload the window - if (choice === 0) { + if (result.choice === 0) { commandService.executeCommand(ReloadWindowAction.ID); } }); diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index c01181c70c4..396ec1c3a43 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -180,7 +180,7 @@ class OpenerValidatorContributions implements IWorkbenchContribution { if (isURLDomainTrusted(resource, trustedDomains)) { return true; } else { - const choice = await this._dialogService.show( + const { choice } = await this._dialogService.show( Severity.Info, localize( 'openExternalLinkAt', diff --git a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts index af8d88245d6..7e3f18bd489 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts @@ -9,7 +9,7 @@ import Severity from 'vs/base/common/severity'; import { isLinux, isWindows } from 'vs/base/common/platform'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { IDialogService, IConfirmation, IConfirmationResult, IDialogOptions } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService, IConfirmation, IConfirmationResult, IDialogOptions, IShowResult } from 'vs/platform/dialogs/common/dialogs'; import { DialogService as HTMLDialogService } from 'vs/platform/dialogs/browser/dialogService'; import { ILogService } from 'vs/platform/log/common/log'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -63,7 +63,7 @@ export class DialogService implements IDialogService { confirm(confirmation: IConfirmation): Promise { return this.impl.confirm(confirmation); } - show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions | undefined): Promise { + show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions | undefined): Promise { return this.impl.show(severity, message, buttons, options); } } @@ -129,7 +129,7 @@ class NativeDialogService implements IDialogService { return opts; } - async show(severity: Severity, message: string, buttons: string[], dialogOptions?: IDialogOptions): Promise { + async show(severity: Severity, message: string, buttons: string[], dialogOptions?: IDialogOptions): Promise { this.logService.trace('DialogService#show', message); const { options, buttonIndexMap } = this.massageMessageBoxOptions({ @@ -141,7 +141,7 @@ class NativeDialogService implements IDialogService { }); const result = await this.windowService.showMessageBox(options); - return buttonIndexMap[result.button]; + return { choice: buttonIndexMap[result.button] }; } private massageMessageBoxOptions(options: Electron.MessageBoxOptions): IMassagedMessageBoxOptions { diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index a608d90765f..c288bfa5a07 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -588,12 +588,12 @@ export abstract class TextFileService extends Disposable implements ITextFileSer nls.localize('cancel', "Cancel") ]; - const index = await this.dialogService.show(Severity.Warning, message, buttons, { + const { choice } = await this.dialogService.show(Severity.Warning, message, buttons, { cancelId: 2, detail: nls.localize('saveChangesDetail', "Your changes will be lost if you don't save them.") }); - switch (index) { + switch (choice) { case 0: return ConfirmResult.SAVE; case 1: return ConfirmResult.DONT_SAVE; default: return ConfirmResult.CANCEL; diff --git a/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts b/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts index 55c9404ea62..21fc21e1780 100644 --- a/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts @@ -108,9 +108,9 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { const detail = nls.localize('saveWorkspaceDetail', "Save your workspace if you plan to open it again."); const cancelId = buttons.indexOf(cancel); - const res = await this.dialogService.show(Severity.Warning, message, buttons.map(button => button.label), { detail, cancelId }); + const { choice } = await this.dialogService.show(Severity.Warning, message, buttons.map(button => button.label), { detail, cancelId }); - switch (buttons[res].result) { + switch (buttons[choice].result) { // Cancel: veto unload case ConfirmResult.CANCEL: diff --git a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts index 54dd9f1a9af..b3f8ec0258a 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts @@ -102,7 +102,7 @@ suite('ExtHostMessageService', function () { assert.equal(message, 'h'); assert.equal(buttons.length, 2); assert.equal(buttons[1], 'Cancel'); - return Promise.resolve(0); + return Promise.resolve({ choice: 0 }); } } as IDialogService); @@ -113,7 +113,7 @@ suite('ExtHostMessageService', function () { test('returns undefined when cancelled', async () => { const service = new MainThreadMessageService(null!, emptyNotificationService, emptyCommandService, new class extends mock() { show() { - return Promise.resolve(1); + return Promise.resolve({ choice: 1 }); } } as IDialogService); @@ -125,7 +125,7 @@ suite('ExtHostMessageService', function () { const service = new MainThreadMessageService(null!, emptyNotificationService, emptyCommandService, new class extends mock() { show(severity: Severity, message: string, buttons: string[]) { assert.equal(buttons.length, 1); - return Promise.resolve(0); + return Promise.resolve({ choice: 0 }); } } as IDialogService); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 689930dc68b..c25cc7e1a3e 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -50,7 +50,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService, MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { ITextBufferFactory, DefaultEndOfLine, EndOfLinePreference, IModelDecorationOptions, ITextModel, ITextSnapshot } from 'vs/editor/common/model'; import { Range } from 'vs/editor/common/core/range'; -import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, IShowResult } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -399,8 +399,8 @@ export class TestDialogService implements IDialogService { return Promise.resolve({ confirmed: false }); } - public show(_severity: Severity, _message: string, _buttons: string[], _options?: IDialogOptions): Promise { - return Promise.resolve(0); + public show(_severity: Severity, _message: string, _buttons: string[], _options?: IDialogOptions): Promise { + return Promise.resolve({ choice: 0 }); } }