diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 3c6ebd5a1f1..a3207fa5aa6 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -1215,19 +1215,19 @@ export class CodeMenu { buttons.push(mnemonicButtonLabel(nls.localize({ key: 'copy', comment: ['&& denotes a mnemonic'] }, "&&Copy"))); // https://github.com/Microsoft/vscode/issues/37608 } - dialog.showMessageBox(lastActiveWindow && lastActiveWindow.win, { + const result = dialog.showMessageBox(lastActiveWindow && lastActiveWindow.win, { title: product.nameLong, type: 'info', message: product.nameLong, detail: `\n${detail}`, buttons, noLink: true - }, result => { - if (isWindows && result === 1) { - clipboard.writeText(detail); - } }); + if (isWindows && result === 1) { + clipboard.writeText(detail); + } + this.reportMenuActionTelemetry('showAboutDialog'); } diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 21e46fe7b39..1ef9ca6510b 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -1457,48 +1457,48 @@ export class WindowsManager implements IWindowsMainService { // Unresponsive if (error === WindowError.UNRESPONSIVE) { - dialog.showMessageBox(window.win, { + const result = dialog.showMessageBox(window.win, { title: product.nameLong, type: 'warning', buttons: [mnemonicButtonLabel(localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(localize({ key: 'wait', comment: ['&& denotes a mnemonic'] }, "&&Keep Waiting")), mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], message: localize('appStalled', "The window is no longer responding"), detail: localize('appStalledDetail', "You can reopen or close the window or keep waiting."), noLink: true - }, result => { - if (!window.win) { - return; // Return early if the window has been going down already - } - - if (result === 0) { - window.reload(); - } else if (result === 2) { - this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually - window.win.destroy(); // make sure to destroy the window as it is unresponsive - } }); + + if (!window.win) { + return; // Return early if the window has been going down already + } + + if (result === 0) { + window.reload(); + } else if (result === 2) { + this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually + window.win.destroy(); // make sure to destroy the window as it is unresponsive + } } // Crashed else { - dialog.showMessageBox(window.win, { + const result = dialog.showMessageBox(window.win, { title: product.nameLong, type: 'warning', buttons: [mnemonicButtonLabel(localize({ key: 'reopen', comment: ['&& denotes a mnemonic'] }, "&&Reopen")), mnemonicButtonLabel(localize({ key: 'close', comment: ['&& denotes a mnemonic'] }, "&&Close"))], message: localize('appCrashed', "The window has crashed"), detail: localize('appCrashedDetail', "We are sorry for the inconvenience! You can reopen the window to continue where you left off."), noLink: true - }, result => { - if (!window.win) { - return; // Return early if the window has been going down already - } - - if (result === 0) { - window.reload(); - } else if (result === 1) { - this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually - window.win.destroy(); // make sure to destroy the window as it has crashed - } }); + + if (!window.win) { + return; // Return early if the window has been going down already + } + + if (result === 0) { + window.reload(); + } else if (result === 1) { + this.onBeforeWindowClose(window); // 'close' event will not be fired on destroy(), so run it manually + window.win.destroy(); // make sure to destroy the window as it has crashed + } } } @@ -1654,21 +1654,20 @@ class FileDialog { // Show Dialog const focusedWindow = this.windowsMainService.getWindowById(options.windowId) || this.windowsMainService.getFocusedWindow(); - dialog.showOpenDialog(focusedWindow && focusedWindow.win, options.dialogOptions, paths => { - if (paths && paths.length > 0) { - if (isMacintosh) { - paths = paths.map(path => normalizeNFC(path)); // normalize paths returned from the OS - } - - // Remember path in storage for next time - this.stateService.setItem(FileDialog.workingDirPickerStorageKey, dirname(paths[0])); - - // Return - return clb(paths); + let paths = dialog.showOpenDialog(focusedWindow && focusedWindow.win, options.dialogOptions); + if (paths && paths.length > 0) { + if (isMacintosh) { + paths = paths.map(path => normalizeNFC(path)); // normalize paths returned from the OS } - return clb(void (0)); - }); + // Remember path in storage for next time + this.stateService.setItem(FileDialog.workingDirPickerStorageKey, dirname(paths[0])); + + // Return + return clb(paths); + } + + return clb(void (0)); } } diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index c316839a206..44dd3f9f46f 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -260,7 +260,7 @@ export class SimpleMessageService implements IMessageService { // No-op } - public confirmSync(confirmation: IConfirmation): boolean { + public confirm(confirmation: IConfirmation): boolean { let messageText = confirmation.message; if (confirmation.detail) { messageText = messageText + '\n\n' + confirmation.detail; @@ -269,8 +269,8 @@ export class SimpleMessageService implements IMessageService { return window.confirm(messageText); } - public confirm(confirmation: IConfirmation): TPromise { - return TPromise.as({ confirmed: this.confirmSync(confirmation) } as IConfirmationResult); + public confirmWithCheckbox(confirmation: IConfirmation): TPromise { + return TPromise.as({ confirmed: this.confirm(confirmation), checkboxChecked: false /* unsupported */ } as IConfirmationResult); } } diff --git a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts index e9468d0dd7b..82518d718f8 100644 --- a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts @@ -124,8 +124,8 @@ suite('AbstractKeybindingService', () => { let messageService: IMessageService = { _serviceBrand: undefined, hideAll: undefined, - confirmSync: undefined, confirm: undefined, + confirmWithCheckbox: undefined, show: (sev: Severity, message: any): () => void => { showMessageCalls.push({ sev: sev, diff --git a/src/vs/platform/message/common/message.ts b/src/vs/platform/message/common/message.ts index cfe7e448854..63ca9fe894b 100644 --- a/src/vs/platform/message/common/message.ts +++ b/src/vs/platform/message/common/message.ts @@ -62,12 +62,12 @@ export interface IMessageService { /** * Ask the user for confirmation. */ - confirmSync(confirmation: IConfirmation): boolean; + confirm(confirmation: IConfirmation): boolean; /** - * Ask the user for confirmation without blocking. + * Ask the user for confirmation with a checkbox. */ - confirm(confirmation: IConfirmation): TPromise; + confirmWithCheckbox(confirmation: IConfirmation): TPromise; } export const IChoiceService = createDecorator('choiceService'); diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 871f7fd8ed8..0c5c2b17f1c 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -193,8 +193,8 @@ export interface IWindowService { show(): TPromise; showMessageBoxSync(options: MessageBoxOptions): number; showMessageBox(options: MessageBoxOptions): TPromise; - showSaveDialog(options: SaveDialogOptions, callback?: (fileName: string) => void): string; - showOpenDialog(options: OpenDialogOptions, callback?: (fileNames: string[]) => void): string[]; + showSaveDialog(options: SaveDialogOptions): string; + showOpenDialog(options: OpenDialogOptions): string[]; } export type MenuBarVisibility = 'default' | 'visible' | 'toggle' | 'hidden'; diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index 5656370d8da..ec842feb186 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -135,7 +135,7 @@ export class WindowService implements IWindowService { }); } - showSaveDialog(options: Electron.SaveDialogOptions, callback?: (fileName: string) => void): string { + showSaveDialog(options: Electron.SaveDialogOptions): string { function normalizePath(path: string): string { if (path && isMacintosh) { @@ -145,14 +145,10 @@ export class WindowService implements IWindowService { return path; } - if (callback) { - return remote.dialog.showSaveDialog(remote.getCurrentWindow(), options, path => callback(normalizePath(path))); - } - return normalizePath(remote.dialog.showSaveDialog(remote.getCurrentWindow(), options)); // https://github.com/electron/electron/issues/4936 } - showOpenDialog(options: Electron.OpenDialogOptions, callback?: (fileNames: string[]) => void): string[] { + showOpenDialog(options: Electron.OpenDialogOptions): string[] { function normalizePaths(paths: string[]): string[] { if (paths && paths.length > 0 && isMacintosh) { @@ -162,10 +158,6 @@ export class WindowService implements IWindowService { return paths; } - if (callback) { - return remote.dialog.showOpenDialog(remote.getCurrentWindow(), options, paths => callback(normalizePaths(paths))); - } - return normalizePaths(remote.dialog.showOpenDialog(remote.getCurrentWindow(), options)); // https://github.com/electron/electron/issues/4936 } diff --git a/src/vs/workbench/api/electron-browser/mainThreadDialogs.ts b/src/vs/workbench/api/electron-browser/mainThreadDialogs.ts index 2e6768b97ed..6a4e7b7c164 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDialogs.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDialogs.ts @@ -31,10 +31,11 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape { return TPromise.wrapError(new Error('Not supported - Open-dialogs can only be opened on `file`-uris.')); } return new TPromise(resolve => { - this._windowService.showOpenDialog( - MainThreadDialogs._convertOpenOptions(options), - filenames => resolve(isFalsyOrEmpty(filenames) ? undefined : filenames) + const filenames = this._windowService.showOpenDialog( + MainThreadDialogs._convertOpenOptions(options) ); + + resolve(isFalsyOrEmpty(filenames) ? undefined : filenames); }); } @@ -44,10 +45,10 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape { return TPromise.wrapError(new Error('Not supported - Save-dialogs can only be opened on `file`-uris.')); } return new TPromise(resolve => { - this._windowService.showSaveDialog( - MainThreadDialogs._convertSaveOptions(options), - filename => resolve(!filename ? undefined : filename) + const filename = this._windowService.showSaveDialog( + MainThreadDialogs._convertSaveOptions(options) ); + resolve(!filename ? undefined : filename); }); } diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 959f0644dc2..97dac5b3dad 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -693,7 +693,7 @@ export class BaseDeleteFileAction extends BaseFileAction { message = nls.localize('dirtyMessageFileDelete', "You are deleting a file with unsaved changes. Do you want to continue?"); } - const res = this.messageService.confirmSync({ + const res = this.messageService.confirm({ message, type: 'warning', detail: nls.localize('dirtyWarning', "Your changes will be lost if you don't save them."), @@ -719,7 +719,7 @@ export class BaseDeleteFileAction extends BaseFileAction { // Confirm for moving to trash else if (this.useTrash) { - confirmPromise = this.messageService.confirm({ + confirmPromise = this.messageService.confirmWithCheckbox({ message: this.element.isDirectory ? nls.localize('confirmMoveTrashMessageFolder', "Are you sure you want to delete '{0}' and its contents?", this.element.name) : nls.localize('confirmMoveTrashMessageFile', "Are you sure you want to delete '{0}'?", this.element.name), detail: isWindows ? nls.localize('undoBin', "You can restore from the recycle bin.") : nls.localize('undoTrash', "You can restore from the trash."), primaryButton, @@ -732,7 +732,7 @@ export class BaseDeleteFileAction extends BaseFileAction { // Confirm for deleting permanently else { - confirmPromise = this.messageService.confirm({ + confirmPromise = this.messageService.confirmWithCheckbox({ message: this.element.isDirectory ? nls.localize('confirmDeleteMessageFolder', "Are you sure you want to permanently delete '{0}' and its contents?", this.element.name) : nls.localize('confirmDeleteMessageFile', "Are you sure you want to permanently delete '{0}'?", this.element.name), detail: nls.localize('irreversible', "This action is irreversible!"), primaryButton, @@ -861,7 +861,7 @@ export class ImportFileAction extends BaseFileAction { type: 'warning' }; - overwrite = this.messageService.confirmSync(confirm); + overwrite = this.messageService.confirm(confirm); } if (!overwrite) { diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts index 766fcc010ad..d41113e034d 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -915,7 +915,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { // If we are in no-workspace context, ask for confirmation to create a workspace let confirmed = true; if (this.contextService.getWorkbenchState() !== WorkbenchState.WORKSPACE) { - confirmed = this.messageService.confirmSync({ + confirmed = this.messageService.confirm({ message: folders.length > 1 ? nls.localize('dropFolders', "Do you want to add the folders to the workspace?") : nls.localize('dropFolder', "Do you want to add the folder to the workspace?"), type: 'question', primaryButton: folders.length > 1 ? nls.localize('addFolders', "&&Add Folders") : nls.localize('addFolder', "&&Add Folder") @@ -947,7 +947,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { // Handle confirm setting const confirmDragAndDrop = !isCopy && this.configurationService.getValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY); if (confirmDragAndDrop) { - confirmPromise = this.messageService.confirm({ + confirmPromise = this.messageService.confirmWithCheckbox({ message: nls.localize('confirmMove', "Are you sure you want to move '{0}'?", source.name), checkbox: { label: nls.localize('doNotAskAgain', "Do not ask me again") @@ -1039,7 +1039,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { }; // Move with overwrite if the user confirms - if (this.messageService.confirmSync(confirm)) { + if (this.messageService.confirm(confirm)) { const targetDirty = this.textFileService.getDirty().filter(d => resources.isEqualOrParent(d, targetResource, !isLinux /* ignorecase */)); // Make sure to revert all dirty in target first to be able to overwrite properly diff --git a/src/vs/workbench/parts/performance/electron-browser/startupProfiler.ts b/src/vs/workbench/parts/performance/electron-browser/startupProfiler.ts index f2cb7c7ee5b..242c7c0e1c8 100644 --- a/src/vs/workbench/parts/performance/electron-browser/startupProfiler.ts +++ b/src/vs/workbench/parts/performance/electron-browser/startupProfiler.ts @@ -59,7 +59,7 @@ class StartupProfiler implements IWorkbenchContribution { }).then(files => { const profileFiles = files.reduce((prev, cur) => `${prev}${join(dir, cur)}\n`, '\n'); - const primaryButton = this._messageService.confirmSync({ + const primaryButton = this._messageService.confirm({ type: 'info', message: localize('prof.message', "Successfully created profiles."), detail: localize('prof.detail', "Please create an issue and manually attach the following files:\n{0}", profileFiles), @@ -74,7 +74,7 @@ class StartupProfiler implements IWorkbenchContribution { action.run(`:warning: Make sure to **attach** these files from your *home*-directory: :warning:\n${files.map(file => `-\`${file}\``).join('\n')}`) ]).then(() => { // keep window stable until restart is selected - this._messageService.confirmSync({ + this._messageService.confirm({ type: 'info', message: localize('prof.thanks', "Thanks for helping us."), detail: localize('prof.detail.restart', "A final restart is required to continue to use '{0}'. Again, thank you for your contribution.", this._environmentService.appNameLong), diff --git a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts index 7d9233b0532..0c923fea0fb 100644 --- a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts +++ b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts @@ -138,7 +138,7 @@ export class SettingsChangeRelauncher implements IWorkbenchContribution { private doConfirm(message: string, detail: string, primaryButton: string, confirmed: () => void): void { this.windowService.isFocused().then(focused => { if (focused) { - const confirm = this.messageService.confirmSync({ + const confirm = this.messageService.confirm({ type: 'info', message, detail, diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index 12591d6c6b7..9d1e9a6ac19 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -390,7 +390,7 @@ export class SearchViewlet extends Viewlet { type: 'question' }; - if (this.messageService.confirmSync(confirmation)) { + if (this.messageService.confirm(confirmation)) { this.searchWidget.setReplaceAllActionState(false); this.viewModel.searchResult.replaceAll(progressRunner).then(() => { progressRunner.done(); diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index 55564f54c46..d3bf4705c50 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -1666,7 +1666,7 @@ class TaskService implements ITaskService { if (this._taskSystem instanceof TerminalTaskSystem) { return false; } - if (this._taskSystem.canAutoTerminate() || this.messageService.confirmSync({ + if (this._taskSystem.canAutoTerminate() || this.messageService.confirm({ message: nls.localize('TaskSystem.runningTask', 'There is a task running. Do you want to terminate it?'), primaryButton: nls.localize({ key: 'TaskSystem.terminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task"), type: 'question' @@ -1687,7 +1687,7 @@ class TaskService implements ITaskService { this.disposeTaskSystemListeners(); return false; // no veto } else if (code && code === TerminateResponseCode.ProcessNotFound) { - return !this.messageService.confirmSync({ + return !this.messageService.confirm({ message: nls.localize('TaskSystem.noProcess', 'The launched task doesn\'t exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.'), primaryButton: nls.localize({ key: 'TaskSystem.exitAnyways', comment: ['&& denotes a mnemonic'] }, "&&Exit Anyways"), type: 'info' diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts index d498d16a71f..4afb673317d 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts @@ -220,7 +220,7 @@ export class TerminalService extends AbstractTerminalService implements ITermina message = nls.localize('terminalService.terminalCloseConfirmationPlural', "There are {0} active terminal sessions, do you want to kill them?", this.terminalInstances.length); } - return !this._messageService.confirmSync({ + return !this._messageService.confirm({ message, type: 'warning', }); diff --git a/src/vs/workbench/services/message/browser/messageService.ts b/src/vs/workbench/services/message/browser/messageService.ts index 9fe4dd36645..77a2a3ceb86 100644 --- a/src/vs/workbench/services/message/browser/messageService.ts +++ b/src/vs/workbench/services/message/browser/messageService.ts @@ -136,7 +136,7 @@ export class WorkbenchMessageService implements IMessageService { } } - public confirmSync(confirmation: IConfirmation): boolean { + public confirm(confirmation: IConfirmation): boolean { let messageText = confirmation.message; if (confirmation.detail) { messageText = messageText + '\n\n' + confirmation.detail; @@ -145,8 +145,8 @@ export class WorkbenchMessageService implements IMessageService { return window.confirm(messageText); } - public confirm(confirmation: IConfirmation): TPromise { - return TPromise.as({ confirmed: this.confirmSync(confirmation) } as IConfirmationResult); + public confirmWithCheckbox(confirmation: IConfirmation): TPromise { + return TPromise.as({ confirmed: this.confirm(confirmation) } as IConfirmationResult); } public dispose(): void { diff --git a/src/vs/workbench/services/message/electron-browser/messageService.ts b/src/vs/workbench/services/message/electron-browser/messageService.ts index f3bb429559c..82db4f1c350 100644 --- a/src/vs/workbench/services/message/electron-browser/messageService.ts +++ b/src/vs/workbench/services/message/electron-browser/messageService.ts @@ -26,7 +26,7 @@ export class MessageService extends WorkbenchMessageService implements IChoiceSe super(container, telemetryService); } - public confirm(confirmation: IConfirmation): TPromise { + public confirmWithCheckbox(confirmation: IConfirmation): TPromise { const opts = this.getConfirmOptions(confirmation); return this.showMessageBox(opts).then(result => { @@ -37,7 +37,7 @@ export class MessageService extends WorkbenchMessageService implements IChoiceSe }); } - public confirmSync(confirmation: IConfirmation): boolean { + public confirm(confirmation: IConfirmation): boolean { const opts = this.getConfirmOptions(confirmation); const result = this.showMessageBoxSync(opts); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 3c78c660ced..2e6583d0baa 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -328,11 +328,11 @@ export class TestMessageService implements IMessageService { // No-op } - public confirmSync(confirmation: IConfirmation): boolean { + public confirm(confirmation: IConfirmation): boolean { return false; } - public confirm(confirmation: IConfirmation): Promise { + public confirmWithCheckbox(confirmation: IConfirmation): Promise { return TPromise.as({ confirmed: false }); } }