From 4929acf926a938505653ae20eff6fe18577a6044 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 23 May 2019 11:49:18 +0200 Subject: [PATCH] debt - async/await in workbench --- .../browser/actions/navigationActions.ts | 6 +- .../browser/actions/workspaceActions.ts | 50 ++-- .../browser/actions/workspaceCommands.ts | 35 ++- src/vs/workbench/browser/dnd.ts | 120 +++++----- .../parts/activitybar/activitybarActions.ts | 11 +- .../workbench/browser/parts/compositeBar.ts | 5 +- .../browser/parts/editor/binaryEditor.ts | 57 ++--- .../browser/parts/editor/editorActions.ts | 57 ++--- .../browser/parts/editor/editorCommands.ts | 14 +- .../browser/parts/editor/editorControl.ts | 23 +- .../browser/parts/editor/editorGroupView.ts | 219 +++++++++--------- .../browser/parts/editor/editorStatus.ts | 71 +++--- .../browser/parts/editor/resourceViewer.ts | 9 +- .../browser/parts/editor/sideBySideEditor.ts | 25 +- .../browser/parts/editor/textDiffEditor.ts | 83 ++++--- .../browser/parts/editor/textEditor.ts | 13 +- .../parts/editor/textResourceEditor.ts | 66 +++--- .../notifications/notificationsActions.ts | 10 +- .../notifications/notificationsToasts.ts | 19 +- .../parts/quickopen/quickOpenController.ts | 146 ++++++------ .../browser/parts/quickopen/quickopen.ts | 12 +- .../browser/parts/sidebar/sidebarPart.ts | 15 +- .../browser/parts/statusbar/statusbarPart.ts | 8 +- .../browser/parts/titlebar/titlebarPart.ts | 15 +- .../browser/parts/views/customView.ts | 27 +-- src/vs/workbench/browser/parts/views/views.ts | 18 +- src/vs/workbench/browser/web.main.ts | 31 ++- src/vs/workbench/browser/workbench.ts | 86 +++---- src/vs/workbench/common/actions.ts | 50 ++-- src/vs/workbench/common/contributions.ts | 4 +- .../common/editor/binaryEditorModel.ts | 17 +- .../common/editor/diffEditorInput.ts | 43 ++-- .../common/editor/diffEditorModel.ts | 8 +- .../common/editor/resourceEditorInput.ts | 32 +-- .../common/editor/resourceEditorModel.ts | 1 + .../common/editor/textDiffEditorModel.ts | 10 +- .../common/editor/untitledEditorModel.ts | 68 +++--- .../electron-browser/actions/helpActions.ts | 9 +- .../electron-browser/actions/windowActions.ts | 104 +++++---- src/vs/workbench/electron-browser/main.ts | 134 ++++++----- src/vs/workbench/electron-browser/window.ts | 111 ++++----- 41 files changed, 900 insertions(+), 942 deletions(-) diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts index 183f228b237..621f41f00f6 100644 --- a/src/vs/workbench/browser/actions/navigationActions.ts +++ b/src/vs/workbench/browser/actions/navigationActions.ts @@ -73,7 +73,7 @@ abstract class BaseNavigationAction extends Action { return this.panelService.openPanel(activePanelId, true)!; } - protected navigateToSidebar(): Promise { + protected async navigateToSidebar(): Promise { if (!this.layoutService.isVisible(Parts.SIDEBAR_PART)) { return Promise.resolve(false); } @@ -84,8 +84,8 @@ abstract class BaseNavigationAction extends Action { } const activeViewletId = activeViewlet.getId(); - return this.viewletService.openViewlet(activeViewletId, true) - .then(value => value === null ? false : value); + const value = await this.viewletService.openViewlet(activeViewletId, true); + return value === null ? false : value; } protected navigateAcrossEditorGroup(direction: GroupDirection): boolean { diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 24c7555c9c4..ceacc925f41 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -160,21 +160,18 @@ export class GlobalRemoveRootFolderAction extends Action { super(id, label); } - run(): Promise { + async run(): Promise { const state = this.contextService.getWorkbenchState(); // Workspace / Folder if (state === WorkbenchState.WORKSPACE || state === WorkbenchState.FOLDER) { - return this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID).then(folder => { - if (folder) { - return this.workspaceEditingService.removeFolders([folder.uri]).then(() => true); - } - - return true; - }); + const folder = await this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID); + if (folder) { + await this.workspaceEditingService.removeFolders([folder.uri]); + } } - return Promise.resolve(true); + return true; } } @@ -193,20 +190,18 @@ export class SaveWorkspaceAsAction extends Action { super(id, label); } - run(): Promise { - return this.workspaceEditingService.pickNewWorkspacePath().then((configPathUri): Promise | void => { - if (configPathUri) { - switch (this.contextService.getWorkbenchState()) { - case WorkbenchState.EMPTY: - case WorkbenchState.FOLDER: - const folders = this.contextService.getWorkspace().folders.map(folder => ({ uri: folder.uri })); - return this.workspaceEditingService.createAndEnterWorkspace(folders, configPathUri); - - case WorkbenchState.WORKSPACE: - return this.workspaceEditingService.saveAndEnterWorkspace(configPathUri); - } + async run(): Promise { + const configPathUri = await this.workspaceEditingService.pickNewWorkspacePath(); + if (configPathUri) { + switch (this.contextService.getWorkbenchState()) { + case WorkbenchState.EMPTY: + case WorkbenchState.FOLDER: + const folders = this.contextService.getWorkspace().folders.map(folder => ({ uri: folder.uri })); + return this.workspaceEditingService.createAndEnterWorkspace(folders, configPathUri); + case WorkbenchState.WORKSPACE: + return this.workspaceEditingService.saveAndEnterWorkspace(configPathUri); } - }); + } } } @@ -296,14 +291,13 @@ export class DuplicateWorkspaceInNewWindowAction extends Action { super(id, label); } - run(): Promise { + async run(): Promise { const folders = this.workspaceContextService.getWorkspace().folders; const remoteAuthority = this.environmentService.configuration.remoteAuthority; - return this.workspacesService.createUntitledWorkspace(folders, remoteAuthority).then(newWorkspace => { - return this.workspaceEditingService.copyWorkspaceSettings(newWorkspace).then(() => { - return this.windowService.openWindow([{ workspaceUri: newWorkspace.configPath }], { forceNewWindow: true }); - }); - }); + const newWorkspace = await this.workspacesService.createUntitledWorkspace(folders, remoteAuthority); + await this.workspaceEditingService.copyWorkspaceSettings(newWorkspace); + + return this.windowService.openWindow([{ workspaceUri: newWorkspace.configPath }], { forceNewWindow: true }); } } diff --git a/src/vs/workbench/browser/actions/workspaceCommands.ts b/src/vs/workbench/browser/actions/workspaceCommands.ts index 4f4fd99e365..f8ea5408e58 100644 --- a/src/vs/workbench/browser/actions/workspaceCommands.ts +++ b/src/vs/workbench/browser/actions/workspaceCommands.ts @@ -54,30 +54,28 @@ CommandsRegistry.registerCommand({ CommandsRegistry.registerCommand({ id: ADD_ROOT_FOLDER_COMMAND_ID, - handler: (accessor) => { + handler: async (accessor) => { const viewletService = accessor.get(IViewletService); const workspaceEditingService = accessor.get(IWorkspaceEditingService); const dialogsService = accessor.get(IFileDialogService); - return dialogsService.showOpenDialog({ + const folders = await dialogsService.showOpenDialog({ openLabel: mnemonicButtonLabel(nls.localize({ key: 'add', comment: ['&& denotes a mnemonic'] }, "&&Add")), title: nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace"), canSelectFolders: true, canSelectMany: true, defaultUri: dialogsService.defaultFolderPath() - }).then((folders): Promise | null => { - if (!folders || !folders.length) { - return null; - } - - // Add and show Files Explorer viewlet - return workspaceEditingService.addFolders(folders.map(folder => ({ uri: resources.removeTrailingPathSeparator(folder) }))) - .then(() => viewletService.openViewlet(viewletService.getDefaultViewletId(), true)) - .then(() => undefined); }); + + if (!folders || !folders.length) { + return; + } + + await workspaceEditingService.addFolders(folders.map(folder => ({ uri: resources.removeTrailingPathSeparator(folder) }))); + await viewletService.openViewlet(viewletService.getDefaultViewletId(), true); } }); -CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, function (accessor, args?: [IPickOptions, CancellationToken]) { +CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, async function (accessor, args?: [IPickOptions, CancellationToken]) { const quickInputService = accessor.get(IQuickInputService); const labelService = accessor.get(ILabelService); const contextService = accessor.get(IWorkspaceContextService); @@ -86,7 +84,7 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, function (acc const folders = contextService.getWorkspace().folders; if (!folders.length) { - return undefined; + return; } const folderPicks: IQuickPickItem[] = folders.map(folder => { @@ -113,12 +111,11 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, function (acc } const token: CancellationToken = (args ? args[1] : undefined) || CancellationToken.None; + const pick = await quickInputService.pick(folderPicks, options, token); - return quickInputService.pick(folderPicks, options, token).then(pick => { - if (!pick) { - return undefined; - } - + if (pick) { return folders[folderPicks.indexOf(pick)]; - }); + } + + return; }); diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 870076ae23d..a7b7ec91d2e 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -170,53 +170,52 @@ export class ResourcesDropHandler { ) { } - handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup | undefined, afterDrop: (targetGroup: IEditorGroup | undefined) => void, targetIndex?: number): void { + async handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup | undefined, afterDrop: (targetGroup: IEditorGroup | undefined) => void, targetIndex?: number): Promise { const untitledOrFileResources = extractResources(event).filter(r => this.fileService.canHandleResource(r.resource) || r.resource.scheme === Schemas.untitled); if (!untitledOrFileResources.length) { return; } // Make the window active to handle the drop properly within - this.windowService.focusWindow().then(() => { + await this.windowService.focusWindow(); - // Check for special things being dropped - return this.doHandleDrop(untitledOrFileResources).then(isWorkspaceOpening => { - if (isWorkspaceOpening) { - return undefined; // return early if the drop operation resulted in this window changing to a workspace - } + // Check for special things being dropped + const isWorkspaceOpening = await this.doHandleDrop(untitledOrFileResources); - // Add external ones to recently open list unless dropped resource is a workspace - const recents: IRecentFile[] = untitledOrFileResources.filter(d => d.isExternal && d.resource.scheme === Schemas.file).map(d => ({ fileUri: d.resource })); - if (recents.length) { - this.windowsService.addRecentlyOpened(recents); - } + if (isWorkspaceOpening) { + return; // return early if the drop operation resulted in this window changing to a workspace + } - const editors: IResourceEditor[] = untitledOrFileResources.map(untitledOrFileResource => ({ - resource: untitledOrFileResource.resource, - options: { - pinned: true, - index: targetIndex, - viewState: (untitledOrFileResource as IDraggedEditor).viewState - } - })); + // Add external ones to recently open list unless dropped resource is a workspace + const recents: IRecentFile[] = untitledOrFileResources.filter(d => d.isExternal && d.resource.scheme === Schemas.file).map(d => ({ fileUri: d.resource })); + if (recents.length) { + this.windowsService.addRecentlyOpened(recents); + } - // Open in Editor - const targetGroup = resolveTargetGroup(); - return this.editorService.openEditors(editors, targetGroup).then(() => { + const editors: IResourceEditor[] = untitledOrFileResources.map(untitledOrFileResource => ({ + resource: untitledOrFileResource.resource, + options: { + pinned: true, + index: targetIndex, + viewState: (untitledOrFileResource as IDraggedEditor).viewState + } + })); - // Finish with provided function - afterDrop(targetGroup); - }); - }); - }); + // Open in Editor + const targetGroup = resolveTargetGroup(); + await this.editorService.openEditors(editors, targetGroup); + + // Finish with provided function + afterDrop(targetGroup); } - private doHandleDrop(untitledOrFileResources: Array): Promise { + private async doHandleDrop(untitledOrFileResources: Array): Promise { // Check for dirty editors being dropped const resourcesWithBackups: IDraggedEditor[] = untitledOrFileResources.filter(resource => !resource.isExternal && !!(resource as IDraggedEditor).backupResource); if (resourcesWithBackups.length > 0) { - return Promise.all(resourcesWithBackups.map(resourceWithBackup => this.handleDirtyEditorDrop(resourceWithBackup))).then(() => false); + await Promise.all(resourcesWithBackups.map(resourceWithBackup => this.handleDirtyEditorDrop(resourceWithBackup))); + return false; } // Check for workspace file being dropped if we are allowed to do so @@ -227,10 +226,10 @@ export class ResourcesDropHandler { } } - return Promise.resolve(false); + return false; } - private handleDirtyEditorDrop(droppedDirtyEditor: IDraggedEditor): Promise { + private async handleDirtyEditorDrop(droppedDirtyEditor: IDraggedEditor): Promise { // Untitled: always ensure that we open a new untitled for each file we drop if (droppedDirtyEditor.resource.scheme === Schemas.untitled) { @@ -239,15 +238,18 @@ export class ResourcesDropHandler { // Return early if the resource is already dirty in target or opened already if (this.textFileService.isDirty(droppedDirtyEditor.resource) || this.editorService.isOpen({ resource: droppedDirtyEditor.resource })) { - return Promise.resolve(false); + return false; } // Resolve the contents of the dropped dirty resource from source - return this.backupFileService.resolveBackupContent(droppedDirtyEditor.backupResource!).then(content => { + try { + const content = await this.backupFileService.resolveBackupContent((droppedDirtyEditor.backupResource!)); + await this.backupFileService.backupResource(droppedDirtyEditor.resource, content.value.create(this.getDefaultEOL()).createSnapshot(true)); + } catch (e) { + // Ignore error + } - // Set the contents of to the resource to the target - return this.backupFileService.backupResource(droppedDirtyEditor.resource, content.value.create(this.getDefaultEOL()).createSnapshot(true)); - }).then(() => false, () => false /* ignore any error */); + return false; } private getDefaultEOL(): DefaultEndOfLine { @@ -259,44 +261,50 @@ export class ResourcesDropHandler { return DefaultEndOfLine.LF; } - private handleWorkspaceFileDrop(fileOnDiskResources: URI[]): Promise { + private async handleWorkspaceFileDrop(fileOnDiskResources: URI[]): Promise { const urisToOpen: IURIToOpen[] = []; const folderURIs: IWorkspaceFolderCreationData[] = []; - return Promise.all(fileOnDiskResources.map(fileOnDiskResource => { + await Promise.all(fileOnDiskResources.map(async fileOnDiskResource => { // Check for Workspace if (hasWorkspaceFileExtension(fileOnDiskResource)) { urisToOpen.push({ workspaceUri: fileOnDiskResource }); - return undefined; + return; } // Check for Folder - return this.fileService.resolve(fileOnDiskResource).then(stat => { + try { + const stat = await this.fileService.resolve(fileOnDiskResource); if (stat.isDirectory) { urisToOpen.push({ folderUri: stat.resource }); folderURIs.push({ uri: stat.resource }); } - }, error => undefined); - })).then(_ => { - - // Return early if no external resource is a folder or workspace - if (urisToOpen.length === 0) { - return false; + } catch (error) { + // Ignore error } + })); - // Pass focus to window - this.windowService.focusWindow(); + // Return early if no external resource is a folder or workspace + if (urisToOpen.length === 0) { + return false; + } - // Open in separate windows if we drop workspaces or just one folder - if (urisToOpen.length > folderURIs.length || folderURIs.length === 1) { - return this.windowService.openWindow(urisToOpen, { forceReuseWindow: true }).then(_ => true); - } + // Pass focus to window + this.windowService.focusWindow(); - // folders.length > 1: Multiple folders: Create new workspace with folders and open - return this.workspaceEditingService.createAndEnterWorkspace(folderURIs).then(_ => true); - }); + // Open in separate windows if we drop workspaces or just one folder + if (urisToOpen.length > folderURIs.length || folderURIs.length === 1) { + await this.windowService.openWindow(urisToOpen, { forceReuseWindow: true }); + } + + // folders.length > 1: Multiple folders: Create new workspace with folders and open + else { + await this.workspaceEditingService.createAndEnterWorkspace(folderURIs); + } + + return true; } } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 4e4f5523191..329aa05869c 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -42,15 +42,15 @@ export class ViewletActivityAction extends ActivityAction { super(activity); } - run(event: any): Promise { + async run(event: any): Promise { if (event instanceof MouseEvent && event.button === 2) { - return Promise.resolve(false); // do not run on right click + return false; // do not run on right click } // prevent accident trigger on a doubleclick (to help nervous people) const now = Date.now(); if (now > this.lastRun /* https://github.com/Microsoft/vscode/issues/25830 */ && now - this.lastRun < ViewletActivityAction.preventDoubleClickDelay) { - return Promise.resolve(true); + return true; } this.lastRun = now; @@ -61,11 +61,12 @@ export class ViewletActivityAction extends ActivityAction { if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.activity.id) { this.logAction('hide'); this.layoutService.setSideBarHidden(true); - return Promise.resolve(); + return true; } this.logAction('show'); - return this.viewletService.openViewlet(this.activity.id, true).then(() => this.activate()); + await this.viewletService.openViewlet(this.activity.id, true); + return this.activate(); } private logAction(action: string) { diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 8e9b5bd0a4f..af6c4549411 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -204,12 +204,13 @@ export class CompositeBar extends Widget implements ICompositeBar { return toDisposable(() => this.model.removeActivity(compositeId, activity)); } - pin(compositeId: string, open?: boolean): void { + async pin(compositeId: string, open?: boolean): Promise { if (this.model.setPinned(compositeId, true)) { this.updateCompositeSwitcher(); if (open) { - this.options.openComposite(compositeId).then(() => this.activateComposite(compositeId)); // Activate after opening + await this.options.openComposite(compositeId); + this.activateComposite(compositeId); // Activate after opening } } } diff --git a/src/vs/workbench/browser/parts/editor/binaryEditor.ts b/src/vs/workbench/browser/parts/editor/binaryEditor.ts index 76839dbde39..e4d4e9694a1 100644 --- a/src/vs/workbench/browser/parts/editor/binaryEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryEditor.ts @@ -74,44 +74,33 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { parent.appendChild(this.scrollbar.getDomNode()); } - setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { - return super.setInput(input, options, token).then(() => { - return input.resolve().then(model => { - - // Check for cancellation - if (token.isCancellationRequested) { - return undefined; - } - - // Assert Model instance - if (!(model instanceof BinaryEditorModel)) { - return Promise.reject(new Error('Unable to open file as binary')); - } - - // Render Input - this.resourceViewerContext = ResourceViewer.show( - { name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() }, - this.textFileService, - this.binaryContainer, - this.scrollbar, - { - openInternalClb: _ => this.handleOpenInternalCallback(input, options), - openExternalClb: this.environmentService.configuration.remoteAuthority ? undefined : resource => this.callbacks.openExternal(resource), - metadataClb: meta => this.handleMetadataChanged(meta) - } - ); - - return undefined; - }); + async setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { + await super.setInput(input, options, token); + const model = await input.resolve(); + + // Check for cancellation + if (token.isCancellationRequested) { + return; + } + + // Assert Model instance + if (!(model instanceof BinaryEditorModel)) { + throw new Error('Unable to open file as binary'); + } + + // Render Input + this.resourceViewerContext = ResourceViewer.show({ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() }, this.textFileService, this.binaryContainer, this.scrollbar, { + openInternalClb: () => this.handleOpenInternalCallback(input, options), + openExternalClb: this.environmentService.configuration.remoteAuthority ? undefined : resource => this.callbacks.openExternal(resource), + metadataClb: meta => this.handleMetadataChanged(meta) }); } - private handleOpenInternalCallback(input: EditorInput, options: EditorOptions) { - this.callbacks.openInternal(input, options).then(() => { + private async handleOpenInternalCallback(input: EditorInput, options: EditorOptions): Promise { + await this.callbacks.openInternal(input, options); - // Signal to listeners that the binary editor has been opened in-place - this._onDidOpenInPlace.fire(); - }); + // Signal to listeners that the binary editor has been opened in-place + this._onDidOpenInPlace.fire(); } private handleMetadataChanged(meta: string | undefined): void { diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 1d6d3e15151..7f7a5a58e53 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -539,23 +539,27 @@ export class RevertAndCloseEditorAction extends Action { super(id, label); } - run(): Promise { + async run(): Promise { const activeControl = this.editorService.activeControl; if (activeControl) { const editor = activeControl.input; const group = activeControl.group; // first try a normal revert where the contents of the editor are restored - return editor.revert().then(() => group.closeEditor(editor), error => { + try { + await editor.revert(); + } catch (error) { // if that fails, since we are about to close the editor, we accept that // the editor cannot be reverted and instead do a soft revert that just // enables us to close the editor. With this, a user can always close a // dirty editor even when reverting fails. - return editor.revert({ soft: true }).then(() => group.closeEditor(editor)); - }); + await editor.revert({ soft: true }); + } + + group.closeEditor(editor); } - return Promise.resolve(false); + return true; } } @@ -618,7 +622,7 @@ export abstract class BaseCloseAllAction extends Action { return groupsToClose; } - run(): Promise { + async run(): Promise { // Just close all if there are no or one dirty editor if (this.textFileService.getDirty().length < 2) { @@ -626,26 +630,23 @@ export abstract class BaseCloseAllAction extends Action { } // Otherwise ask for combined confirmation - return this.textFileService.confirmSave().then(confirm => { - if (confirm === ConfirmResult.CANCEL) { - return undefined; - } - - let saveOrRevertPromise: Promise; - if (confirm === ConfirmResult.DONT_SAVE) { - saveOrRevertPromise = this.textFileService.revertAll(undefined, { soft: true }).then(() => true); - } else { - saveOrRevertPromise = this.textFileService.saveAll(true).then(res => res.results.every(r => !!r.success)); - } + const confirm = await this.textFileService.confirmSave(); + if (confirm === ConfirmResult.CANCEL) { + return; + } - return saveOrRevertPromise.then(success => { - if (success) { - return this.doCloseAll(); - } + let saveOrRevert: boolean; + if (confirm === ConfirmResult.DONT_SAVE) { + await this.textFileService.revertAll(undefined, { soft: true }); + saveOrRevert = true; + } else { + const res = await this.textFileService.saveAll(true); + saveOrRevert = res.results.every(r => !!r.success); + } - return undefined; - }); - }); + if (saveOrRevert) { + return this.doCloseAll(); + } } protected abstract doCloseAll(): Promise; @@ -684,10 +685,10 @@ export class CloseAllEditorGroupsAction extends BaseCloseAllAction { super(id, label, undefined, textFileService, editorGroupService); } - protected doCloseAll(): Promise { - return Promise.all(this.groupsToClose.map(g => g.closeAllEditors())).then(() => { - this.groupsToClose.forEach(group => this.editorGroupService.removeGroup(group)); - }); + protected async doCloseAll(): Promise { + await Promise.all(this.groupsToClose.map(group => group.closeAllEditors())); + + this.groupsToClose.forEach(group => this.editorGroupService.removeGroup(group)); } } diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index d13f6cfbc16..ae8928e6b49 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -670,19 +670,17 @@ function registerCloseEditorCommands() { } }); - CommandsRegistry.registerCommand(CLOSE_EDITORS_AND_GROUP_COMMAND_ID, (accessor: ServicesAccessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { + CommandsRegistry.registerCommand(CLOSE_EDITORS_AND_GROUP_COMMAND_ID, async (accessor: ServicesAccessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { const editorGroupService = accessor.get(IEditorGroupsService); const { group } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context)); if (group) { - return group.closeAllEditors().then(() => { - if (group.count === 0 && editorGroupService.getGroup(group.id) /* could be gone by now */) { - editorGroupService.removeGroup(group); // only remove group if it is now empty - } - }); - } + await group.closeAllEditors(); - return undefined; + if (group.count === 0 && editorGroupService.getGroup(group.id) /* could be gone by now */) { + editorGroupService.removeGroup(group); // only remove group if it is now empty + } + } }); } diff --git a/src/vs/workbench/browser/parts/editor/editorControl.ts b/src/vs/workbench/browser/parts/editor/editorControl.ts index e3ce5c5a1fe..7b03b2a8de4 100644 --- a/src/vs/workbench/browser/parts/editor/editorControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorControl.ts @@ -58,7 +58,7 @@ export class EditorControl extends Disposable { return this._activeControl as IVisibleEditor | null; } - openEditor(editor: EditorInput, options?: EditorOptions): Promise { + async openEditor(editor: EditorInput, options?: EditorOptions): Promise { // Editor control const descriptor = Registry.as(EditorExtensions.Editors).getEditor(editor); @@ -68,7 +68,8 @@ export class EditorControl extends Disposable { const control = this.doShowEditorControl(descriptor); // Set input - return this.doSetInput(control, editor, withUndefinedAsNull(options)).then((editorChanged => (({ control, editorChanged })))); + const editorChanged = await this.doSetInput(control, editor, withUndefinedAsNull(options)); + return { control, editorChanged }; } private doShowEditorControl(descriptor: IEditorDescriptor): BaseEditor { @@ -150,7 +151,7 @@ export class EditorControl extends Disposable { this._onDidSizeConstraintsChange.fire(undefined); } - private doSetInput(control: BaseEditor, editor: EditorInput, options: EditorOptions | null): Promise { + private async doSetInput(control: BaseEditor, editor: EditorInput, options: EditorOptions | null): Promise { // If the input did not change, return early and only apply the options // unless the options instruct us to force open it even if it is the same @@ -167,7 +168,7 @@ export class EditorControl extends Disposable { control.focus(); } - return Promise.resolve(false); + return false; } // Show progress while setting input after a certain timeout. If the workbench is opening @@ -176,7 +177,8 @@ export class EditorControl extends Disposable { // Call into editor control const editorWillChange = !inputMatches; - return control.setInput(editor, options, operation.token).then(() => { + try { + await control.setInput(editor, options, operation.token); // Focus (unless prevented or another operation is running) if (operation.isCurrent()) { @@ -186,17 +188,10 @@ export class EditorControl extends Disposable { } } - // Operation done - operation.stop(); - return editorWillChange; - }, e => { - - // Operation done + } finally { operation.stop(); - - return Promise.reject(e); - }); + } } private doHideActiveEditorControl(): void { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index fa7d3c0dd69..8f4d5491d6e 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -400,7 +400,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } } - private restoreEditors(from: IEditorGroupView | ISerializedEditorGroup): Promise { + private async restoreEditors(from: IEditorGroupView | ISerializedEditorGroup): Promise { if (this._group.count === 0) { return Promise.resolve(); // nothing to show } @@ -424,16 +424,15 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const activeElement = document.activeElement; // Show active editor - return this.doShowEditor(activeEditor, true, options).then(() => { - - // Set focused now if this is the active group and focus has - // not changed meanwhile. This prevents focus from being - // stolen accidentally on startup when the user already - // clicked somewhere. - if (this.accessor.activeGroup === this && activeElement === document.activeElement) { - this.focus(); - } - }); + await this.doShowEditor(activeEditor, true, options); + + // Set focused now if this is the active group and focus has + // not changed meanwhile. This prevents focus from being + // stolen accidentally on startup when the user already + // clicked somewhere. + if (this.accessor.activeGroup === this && activeElement === document.activeElement) { + this.focus(); + } } //#region event handling @@ -815,34 +814,33 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return this.doShowEditor(editor, !!openEditorOptions.active, options); } - private doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): Promise { + private async doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): Promise { // Show in editor control if the active editor changed - let openEditorPromise: Promise; + let openEditor: IEditor | null = null; if (active) { - openEditorPromise = this.editorControl.openEditor(editor, options).then(result => { + try { + const result = await this.editorControl.openEditor(editor, options); // Editor change event if (result.editorChanged) { this._onDidGroupChange.fire({ kind: GroupChangeKind.EDITOR_ACTIVE, editor }); } - return result.control; - }, error => { + openEditor = result.control; + } catch (error) { // Handle errors but do not bubble them up this.doHandleOpenEditorError(error, editor, options); - - return null; // error: return NULL as result to signal this - }); + } } else { - openEditorPromise = Promise.resolve(null); // inactive: return NULL as result to signal this + openEditor = null; // inactive: return NULL as result to signal this } // Show in title control after editor control because some actions depend on it this.titleAreaControl.openEditor(editor); - return openEditorPromise; + return openEditor; } private doHandleOpenEditorError(error: Error, editor: EditorInput, options?: EditorOptions): void { @@ -878,37 +876,33 @@ export class EditorGroupView extends Themable implements IEditorGroupView { //#region openEditors() - openEditors(editors: { editor: EditorInput, options?: EditorOptions }[]): Promise { + async openEditors(editors: { editor: EditorInput, options?: EditorOptions }[]): Promise { if (!editors.length) { - return Promise.resolve(null); + return null; } // Do not modify original array editors = editors.slice(0); - let result: IEditor | null; - // Use the first editor as active editor const { editor, options } = editors.shift()!; - return this.openEditor(editor, options).then(activeEditor => { - result = activeEditor; // this can be NULL if the opening failed - - const startingIndex = this.getIndexOfEditor(editor) + 1; - - // Open the other ones inactive - return Promise.all(editors.map(({ editor, options }, index) => { - const adjustedEditorOptions = options || new EditorOptions(); - adjustedEditorOptions.inactive = true; - adjustedEditorOptions.pinned = true; - adjustedEditorOptions.index = startingIndex + index; - - return this.openEditor(editor, adjustedEditorOptions).then(activeEditor => { - if (!result) { - result = activeEditor; // only take if the first editor opening failed - } - }); - })).then(() => result); - }); + let firstEditor = await this.openEditor(editor, options); + + // Open the other ones inactive + const startingIndex = this.getIndexOfEditor(editor) + 1; + await Promise.all(editors.map(async ({ editor, options }, index) => { + const adjustedEditorOptions = options || new EditorOptions(); + adjustedEditorOptions.inactive = true; + adjustedEditorOptions.pinned = true; + adjustedEditorOptions.index = startingIndex + index; + + const openedEditor = await this.openEditor(editor, adjustedEditorOptions); + if (!firstEditor) { + firstEditor = openedEditor; // only take if the first editor opening failed + } + })); + + return firstEditor; } //#endregion @@ -989,20 +983,19 @@ export class EditorGroupView extends Themable implements IEditorGroupView { //#region closeEditor() - closeEditor(editor: EditorInput | undefined = this.activeEditor || undefined, options?: ICloseEditorOptions): Promise { + async closeEditor(editor: EditorInput | undefined = this.activeEditor || undefined, options?: ICloseEditorOptions): Promise { if (!editor) { - return Promise.resolve(); + return; } // Check for dirty and veto - return this.handleDirty([editor]).then(veto => { - if (veto) { - return; - } + const veto = await this.handleDirty([editor]); + if (veto) { + return; + } - // Do close - this.doCloseEditor(editor, options && options.preserveFocus ? false : undefined); - }); + // Do close + this.doCloseEditor(editor, options && options.preserveFocus ? false : undefined); } private doCloseEditor(editor: EditorInput, focusNext = (this.accessor.activeGroup === this), fromError?: boolean): void { @@ -1107,7 +1100,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this._group.closeEditor(editor); } - private handleDirty(editors: EditorInput[]): Promise { + private async handleDirty(editors: EditorInput[]): Promise { if (!editors.length) { return Promise.resolve(false); // no veto } @@ -1122,22 +1115,21 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.mapEditorToPendingConfirmation.set(editor, handleDirtyPromise); } - return handleDirtyPromise.then(veto => { + const veto = await handleDirtyPromise; - // Make sure to remove from our map of cached pending confirmations - this.mapEditorToPendingConfirmation.delete(editor); + // Make sure to remove from our map of cached pending confirmations + this.mapEditorToPendingConfirmation.delete(editor); - // Return for the first veto we got - if (veto) { - return veto; - } + // Return for the first veto we got + if (veto) { + return veto; + } - // Otherwise continue with the remainders - return this.handleDirty(editors); - }); + // Otherwise continue with the remainders + return this.handleDirty(editors); } - private doHandleDirty(editor: EditorInput): Promise { + private async doHandleDirty(editor: EditorInput): Promise { if ( !editor.isDirty() || // editor must be dirty this.accessor.groups.some(groupView => groupView !== this && groupView.group.contains(editor, true /* support side by side */)) || // editor is opened in other group @@ -1147,59 +1139,65 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } // Switch to editor that we want to handle and confirm to save/revert - return this.openEditor(editor).then(() => editor.confirmSave().then(res => { - - // It could be that the editor saved meanwhile, so we check again - // to see if anything needs to happen before closing for good. - // This can happen for example if autoSave: onFocusChange is configured - // so that the save happens when the dialog opens. - if (!editor.isDirty()) { - return res === ConfirmResult.CANCEL ? true : false; - } + await this.openEditor(editor); + + const res = await editor.confirmSave(); + + // It could be that the editor saved meanwhile, so we check again + // to see if anything needs to happen before closing for good. + // This can happen for example if autoSave: onFocusChange is configured + // so that the save happens when the dialog opens. + if (!editor.isDirty()) { + return res === ConfirmResult.CANCEL ? true : false; + } - // Otherwise, handle accordingly - switch (res) { - case ConfirmResult.SAVE: - return editor.save().then(ok => !ok); + // Otherwise, handle accordingly + switch (res) { + case ConfirmResult.SAVE: + const result = await editor.save(); - case ConfirmResult.DONT_SAVE: + return !result; + case ConfirmResult.DONT_SAVE: + + try { // first try a normal revert where the contents of the editor are restored - return editor.revert().then(ok => !ok, error => { + const result = await editor.revert(); - // if that fails, since we are about to close the editor, we accept that - // the editor cannot be reverted and instead do a soft revert that just - // enables us to close the editor. With this, a user can always close a - // dirty editor even when reverting fails. - return editor.revert({ soft: true }).then(ok => !ok); - }); + return !result; + } catch (error) { + // if that fails, since we are about to close the editor, we accept that + // the editor cannot be reverted and instead do a soft revert that just + // enables us to close the editor. With this, a user can always close a + // dirty editor even when reverting fails. + const result = await editor.revert({ soft: true }); - case ConfirmResult.CANCEL: - return true; // veto - } - })); + return !result; + } + case ConfirmResult.CANCEL: + return true; // veto + } } //#endregion //#region closeEditors() - closeEditors(args: EditorInput[] | ICloseEditorsFilter, options?: ICloseEditorOptions): Promise { + async closeEditors(args: EditorInput[] | ICloseEditorsFilter, options?: ICloseEditorOptions): Promise { if (this.isEmpty()) { - return Promise.resolve(); + return; } const editors = this.getEditorsToClose(args); // Check for dirty and veto - return this.handleDirty(editors.slice(0)).then(veto => { - if (veto) { - return; - } + const veto = await this.handleDirty(editors.slice(0)); + if (veto) { + return; + } - // Do close - this.doCloseEditors(editors, options); - }); + // Do close + this.doCloseEditors(editors, options); } private getEditorsToClose(editors: EditorInput[] | ICloseEditorsFilter): EditorInput[] { @@ -1257,7 +1255,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { //#region closeAllEditors() - closeAllEditors(): Promise { + async closeAllEditors(): Promise { if (this.isEmpty()) { // If the group is empty and the request is to close all editors, we still close @@ -1267,19 +1265,18 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.accessor.removeGroup(this); } - return Promise.resolve(); + return; } // Check for dirty and veto const editors = this._group.getEditors(true); - return this.handleDirty(editors.slice(0)).then(veto => { - if (veto) { - return; - } + const veto = await this.handleDirty(editors.slice(0)); + if (veto) { + return; + } - // Do close - this.doCloseAllEditors(); - }); + // Do close + this.doCloseAllEditors(); } private doCloseAllEditors(): void { @@ -1302,7 +1299,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { //#region replaceEditors() - replaceEditors(editors: EditorReplacement[]): Promise { + async replaceEditors(editors: EditorReplacement[]): Promise { // Extract active vs. inactive replacements let activeReplacement: EditorReplacement | undefined; @@ -1360,10 +1357,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.titleAreaControl.closeEditor(activeReplacement.editor); } - return openEditorResult.then(() => undefined); + await openEditorResult; } - - return Promise.resolve(); } //#endregion diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 48bbdff9a7f..9c89adfcc22 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -855,8 +855,8 @@ export class ShowLanguageExtensionsAction extends Action { this.enabled = galleryService.isEnabled(); } - run(): Promise { - return this.commandService.executeCommand('workbench.extensions.action.showExtensionsForLanguage', this.fileExtension).then(() => undefined); + async run(): Promise { + await this.commandService.executeCommand('workbench.extensions.action.showExtensionsForLanguage', this.fileExtension); } } @@ -880,7 +880,7 @@ export class ChangeModeAction extends Action { super(actionId, actionLabel); } - run(): Promise { + async run(): Promise { const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget); if (!activeTextEditorWidget) { return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); @@ -1030,31 +1030,30 @@ export class ChangeModeAction extends Action { }; }); - setTimeout(() => { - this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || base) }).then(language => { - if (language) { - const fileAssociationsConfig = this.configurationService.inspect<{}>(FILES_ASSOCIATIONS_CONFIG); + setTimeout(async () => { + const language = await this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || base) }); + if (language) { + const fileAssociationsConfig = this.configurationService.inspect<{}>(FILES_ASSOCIATIONS_CONFIG); - let associationKey: string; - if (extension && base[0] !== '.') { - associationKey = `*${extension}`; // only use "*.ext" if the file path is in the form of . - } else { - associationKey = base; // otherwise use the basename (e.g. .gitignore, Dockerfile) - } + let associationKey: string; + if (extension && base[0] !== '.') { + associationKey = `*${extension}`; // only use "*.ext" if the file path is in the form of . + } else { + associationKey = base; // otherwise use the basename (e.g. .gitignore, Dockerfile) + } - // If the association is already being made in the workspace, make sure to target workspace settings - let target = ConfigurationTarget.USER; - if (fileAssociationsConfig.workspace && !!fileAssociationsConfig.workspace[associationKey]) { - target = ConfigurationTarget.WORKSPACE; - } + // If the association is already being made in the workspace, make sure to target workspace settings + let target = ConfigurationTarget.USER; + if (fileAssociationsConfig.workspace && !!fileAssociationsConfig.workspace[associationKey]) { + target = ConfigurationTarget.WORKSPACE; + } - // Make sure to write into the value of the target and not the merged value from USER and WORKSPACE config - const currentAssociations = deepClone((target === ConfigurationTarget.WORKSPACE) ? fileAssociationsConfig.workspace : fileAssociationsConfig.user) || Object.create(null); - currentAssociations[associationKey] = language.id; + // Make sure to write into the value of the target and not the merged value from USER and WORKSPACE config + const currentAssociations = deepClone((target === ConfigurationTarget.WORKSPACE) ? fileAssociationsConfig.workspace : fileAssociationsConfig.user) || Object.create(null); + currentAssociations[associationKey] = language.id; - this.configurationService.updateValue(FILES_ASSOCIATIONS_CONFIG, currentAssociations, target); - } - }); + this.configurationService.updateValue(FILES_ASSOCIATIONS_CONFIG, currentAssociations, target); + } }, 50 /* quick open is sensitive to being opened so soon after another */); } } @@ -1077,7 +1076,7 @@ class ChangeIndentationAction extends Action { super(actionId, actionLabel); } - run(): Promise { + async run(): Promise { const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget); if (!activeTextEditorWidget) { return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); @@ -1109,7 +1108,8 @@ class ChangeIndentationAction extends Action { picks.splice(3, 0, { type: 'separator', label: nls.localize('indentConvert', "convert file") }); picks.unshift({ type: 'separator', label: nls.localize('indentView', "change view") }); - return this.quickInputService.pick(picks, { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true }).then(action => action && action.run()); + const action = await this.quickInputService.pick(picks, { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true }); + return action && action.run(); } } @@ -1127,7 +1127,7 @@ export class ChangeEOLAction extends Action { super(actionId, actionLabel); } - run(): Promise { + async run(): Promise { const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget); if (!activeTextEditorWidget) { return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); @@ -1137,7 +1137,7 @@ export class ChangeEOLAction extends Action { return this.quickInputService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]); } - const textModel = activeTextEditorWidget.getModel(); + let textModel = activeTextEditorWidget.getModel(); const EOLOptions: IChangeEOLEntry[] = [ { label: nlsEOLLF, eol: EndOfLineSequence.LF }, @@ -1146,15 +1146,14 @@ export class ChangeEOLAction extends Action { const selectedIndex = (textModel && textModel.getEOL() === '\n') ? 0 : 1; - return this.quickInputService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), activeItem: EOLOptions[selectedIndex] }).then(eol => { - if (eol) { - const activeCodeEditor = getCodeEditor(this.editorService.activeTextEditorWidget); - if (activeCodeEditor && activeCodeEditor.hasModel() && isWritableCodeEditor(activeCodeEditor)) { - const textModel = activeCodeEditor.getModel(); - textModel.pushEOL(eol.eol); - } + const eol = await this.quickInputService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), activeItem: EOLOptions[selectedIndex] }); + if (eol) { + const activeCodeEditor = getCodeEditor(this.editorService.activeTextEditorWidget); + if (activeCodeEditor && activeCodeEditor.hasModel() && isWritableCodeEditor(activeCodeEditor)) { + textModel = activeCodeEditor.getModel(); + textModel.pushEOL(eol.eol); } - }); + } } } diff --git a/src/vs/workbench/browser/parts/editor/resourceViewer.ts b/src/vs/workbench/browser/parts/editor/resourceViewer.ts index 999c8064c6b..131cf402e57 100644 --- a/src/vs/workbench/browser/parts/editor/resourceViewer.ts +++ b/src/vs/workbench/browser/parts/editor/resourceViewer.ts @@ -567,16 +567,15 @@ class InlineImageView { return context; } - private static imageSrc(descriptor: IResourceDescriptor, textFileService: ITextFileService): Promise { + private static async imageSrc(descriptor: IResourceDescriptor, textFileService: ITextFileService): Promise { if (descriptor.resource.scheme === Schemas.data) { return Promise.resolve(descriptor.resource.toString(true /* skip encoding */)); } - return textFileService.read(descriptor.resource, { encoding: 'base64' }).then(data => { - const mime = getMime(descriptor); + const data = await textFileService.read(descriptor.resource, { encoding: 'base64' }); + const mime = getMime(descriptor); - return `data:${mime};base64,${data.value}`; - }); + return `data:${mime};base64,${data.value}`; } } diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index 54a09e72921..639c4ada7d8 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -93,10 +93,11 @@ export class SideBySideEditor extends BaseEditor { this.updateStyles(); } - setInput(newInput: EditorInput, options: EditorOptions, token: CancellationToken): Promise { + async setInput(newInput: EditorInput, options: EditorOptions, token: CancellationToken): Promise { const oldInput = this.input as SideBySideEditorInput; - return super.setInput(newInput, options, token) - .then(() => this.updateInput(oldInput, newInput as SideBySideEditorInput, options, token)); + await super.setInput(newInput, options, token); + + return this.updateInput(oldInput, (newInput as SideBySideEditorInput), options, token); } setOptions(options: EditorOptions): void { @@ -158,7 +159,7 @@ export class SideBySideEditor extends BaseEditor { return this.detailsEditor; } - private updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Promise { + private async updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Promise { if (!newInput.matches(oldInput)) { if (oldInput) { this.disposeEditors(); @@ -166,14 +167,15 @@ export class SideBySideEditor extends BaseEditor { return this.setNewInput(newInput, options, token); } + if (!this.detailsEditor || !this.masterEditor) { return Promise.resolve(); } - return Promise.all([ + await Promise.all([ this.detailsEditor.setInput(newInput.details, null, token), - this.masterEditor.setInput(newInput.master, options, token)] - ).then(() => undefined); + this.masterEditor.setInput(newInput.master, options, token) + ]); } private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions, token: CancellationToken): Promise { @@ -196,7 +198,7 @@ export class SideBySideEditor extends BaseEditor { return editor; } - private onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions, token: CancellationToken): Promise { + private async onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions, token: CancellationToken): Promise { this.detailsEditor = details; this.masterEditor = master; @@ -207,7 +209,12 @@ export class SideBySideEditor extends BaseEditor { this.onDidCreateEditors.fire(undefined); - return Promise.all([this.detailsEditor.setInput(detailsInput, null, token), this.masterEditor.setInput(masterInput, options, token)]).then(() => this.focus()); + await Promise.all([ + this.detailsEditor.setInput(detailsInput, null, token), + this.masterEditor.setInput(masterInput, options, token)] + ); + + return this.focus(); } updateStyles(): void { diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index e63f5649844..3c7045c94a3 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -72,7 +72,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { return this.instantiationService.createInstance(DiffEditorWidget, parent, configuration); } - setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { + async setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { // Dispose previous diff navigator this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables); @@ -81,56 +81,55 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor { this.saveTextDiffEditorViewState(this.input); // Set input and resolve - return super.setInput(input, options, token).then(() => { - return input.resolve().then(resolvedModel => { + await super.setInput(input, options, token); - // Check for cancellation - if (token.isCancellationRequested) { - return undefined; - } + try { + const resolvedModel = await input.resolve(); - // Assert Model Instance - if (!(resolvedModel instanceof TextDiffEditorModel) && this.openAsBinary(input, options)) { - return undefined; - } + // Check for cancellation + if (token.isCancellationRequested) { + return undefined; + } - // Set Editor Model - const diffEditor = this.getControl(); - const resolvedDiffEditorModel = resolvedModel; - diffEditor.setModel(resolvedDiffEditorModel.textDiffEditorModel); + // Assert Model Instance + if (!(resolvedModel instanceof TextDiffEditorModel) && this.openAsBinary(input, options)) { + return undefined; + } - // Apply Options from TextOptions - let optionsGotApplied = false; - if (options && types.isFunction((options).apply)) { - optionsGotApplied = (options).apply(diffEditor, ScrollType.Immediate); - } + // Set Editor Model + const diffEditor = this.getControl(); + const resolvedDiffEditorModel = resolvedModel; + diffEditor.setModel(resolvedDiffEditorModel.textDiffEditorModel); - // Otherwise restore View State - let hasPreviousViewState = false; - if (!optionsGotApplied) { - hasPreviousViewState = this.restoreTextDiffEditorViewState(input); - } + // Apply Options from TextOptions + let optionsGotApplied = false; + if (options && types.isFunction((options).apply)) { + optionsGotApplied = (options).apply(diffEditor, ScrollType.Immediate); + } - // Diff navigator - this.diffNavigator = new DiffNavigator(diffEditor, { - alwaysRevealFirst: !optionsGotApplied && !hasPreviousViewState // only reveal first change if we had no options or viewstate - }); - this.diffNavigatorDisposables.push(this.diffNavigator); + // Otherwise restore View State + let hasPreviousViewState = false; + if (!optionsGotApplied) { + hasPreviousViewState = this.restoreTextDiffEditorViewState(input); + } - // Readonly flag - diffEditor.updateOptions({ readOnly: resolvedDiffEditorModel.isReadonly() }); - return undefined; - }, error => { + // Diff navigator + this.diffNavigator = new DiffNavigator(diffEditor, { + alwaysRevealFirst: !optionsGotApplied && !hasPreviousViewState // only reveal first change if we had no options or viewstate + }); + this.diffNavigatorDisposables.push(this.diffNavigator); - // In case we tried to open a file and the response indicates that this is not a text file, fallback to binary diff. - if (this.isFileBinaryError(error) && this.openAsBinary(input, options)) { - return null; - } + // Readonly flag + diffEditor.updateOptions({ readOnly: resolvedDiffEditorModel.isReadonly() }); + } catch (error) { - // Otherwise make sure the error bubbles up - return Promise.reject(error); - }); - }); + // In case we tried to open a file and the response indicates that this is not a text file, fallback to binary diff. + if (this.isFileBinaryError(error) && this.openAsBinary(input, options)) { + return; + } + + throw error; + } } setOptions(options: EditorOptions): void { diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index 0ddbcc0130e..55229cb7201 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -191,14 +191,13 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor { return this.instantiationService.createInstance(CodeEditorWidget, parent, configuration, {}); } - setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { - return super.setInput(input, options, token).then(() => { + async setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { + await super.setInput(input, options, token); - // Update editor options after having set the input. We do this because there can be - // editor input specific options (e.g. an ARIA label depending on the input showing) - this.updateEditorConfiguration(); - this._editorContainer.setAttribute('aria-label', this.computeAriaLabel()); - }); + // Update editor options after having set the input. We do this because there can be + // editor input specific options (e.g. an ARIA label depending on the input showing) + this.updateEditorConfiguration(); + this._editorContainer.setAttribute('aria-label', this.computeAriaLabel()); } protected setEditorVisible(visible: boolean, group: IEditorGroup): void { diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index 7c8160aa080..4a428b5cea0 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { TextEditorOptions, EditorModel, EditorInput, EditorOptions } from 'vs/workbench/common/editor'; +import { TextEditorOptions, EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; @@ -54,45 +54,41 @@ export class AbstractTextResourceEditor extends BaseTextEditor { return nls.localize('textEditor', "Text Editor"); } - setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { + async setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { // Remember view settings if input changes this.saveTextResourceEditorViewState(this.input); // Set input and resolve - return super.setInput(input, options, token).then(() => { - return input.resolve().then((resolvedModel: EditorModel) => { - - // Check for cancellation - if (token.isCancellationRequested) { - return undefined; - } - - // Assert Model instance - if (!(resolvedModel instanceof BaseTextEditorModel)) { - return Promise.reject(new Error('Unable to open file as text')); - } - - // Set Editor Model - const textEditor = this.getControl(); - const textEditorModel = resolvedModel.textEditorModel; - textEditor.setModel(textEditorModel); - - // Apply Options from TextOptions - let optionsGotApplied = false; - const textOptions = options; - if (textOptions && types.isFunction(textOptions.apply)) { - optionsGotApplied = textOptions.apply(textEditor, ScrollType.Immediate); - } - - // Otherwise restore View State - if (!optionsGotApplied) { - this.restoreTextResourceEditorViewState(input); - } - - return undefined; - }); - }); + await super.setInput(input, options, token); + const resolvedModel = await input.resolve(); + + // Check for cancellation + if (token.isCancellationRequested) { + return undefined; + } + + // Assert Model instance + if (!(resolvedModel instanceof BaseTextEditorModel)) { + return Promise.reject(new Error('Unable to open file as text')); + } + + // Set Editor Model + const textEditor = this.getControl(); + const textEditorModel = resolvedModel.textEditorModel; + textEditor.setModel(textEditorModel); + + // Apply Options from TextOptions + let optionsGotApplied = false; + const textOptions = options; + if (textOptions && types.isFunction(textOptions.apply)) { + optionsGotApplied = textOptions.apply(textEditor, ScrollType.Immediate); + } + + // Otherwise restore View State + if (!optionsGotApplied) { + this.restoreTextResourceEditorViewState(input); + } } private restoreTextResourceEditorViewState(input: EditorInput) { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsActions.ts b/src/vs/workbench/browser/parts/notifications/notificationsActions.ts index f2136d88031..f9eb000bb49 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsActions.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsActions.ts @@ -160,7 +160,7 @@ export class NotificationActionRunner extends ActionRunner { super(); } - protected runAction(action: IAction, context: INotificationViewItem): Promise { + protected async runAction(action: IAction, context: INotificationViewItem): Promise { /* __GDPR__ "workbenchActionExecuted" : { @@ -171,8 +171,10 @@ export class NotificationActionRunner extends ActionRunner { this.telemetryService.publicLog('workbenchActionExecuted', { id: action.id, from: 'message' }); // Run and make sure to notify on any error again - super.runAction(action, context).then(undefined, error => this.notificationService.error(error)); - - return Promise.resolve(); + try { + await super.runAction(action, context); + } catch (error) { + this.notificationService.error(error); + } } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 1b82648bc91..707ce0440ea 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -93,18 +93,17 @@ export class NotificationsToasts extends Themable { }); } - private onCanShowNotifications(): Promise { + private async onCanShowNotifications(): Promise { // Wait for the running phase to ensure we can draw notifications properly - return this.lifecycleService.when(LifecyclePhase.Ready).then(() => { - - // Push notificiations out until either workbench is restored - // or some time has ellapsed to reduce pressure on the startup - return Promise.race([ - this.lifecycleService.when(LifecyclePhase.Restored), - timeout(2000) - ]); - }); + await this.lifecycleService.when(LifecyclePhase.Ready); + + // Push notificiations out until either workbench is restored + // or some time has ellapsed to reduce pressure on the startup + return Promise.race([ + this.lifecycleService.when(LifecyclePhase.Restored), + timeout(2000) + ]); } private onDidNotificationChange(e: INotificationChangeEvent): void { diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index 6683de3ce6c..c44249707e9 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -259,8 +259,7 @@ export class QuickOpenController extends Component implements IQuickOpenService // Pass to handlers for (let prefix in this.mapResolvedHandlersToPrefix) { - const promise = this.mapResolvedHandlersToPrefix[prefix]; - promise.then(handler => { + this.mapResolvedHandlersToPrefix[prefix].then(handler => { this.handlerOnOpenCalled[prefix] = false; handler.onClose(reason === HideReason.CANCELED); // Don't check if onOpen was called to preserve old behaviour for now @@ -429,7 +428,7 @@ export class QuickOpenController extends Component implements IQuickOpenService }); } - private handleDefaultHandler(handler: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise { + private async handleDefaultHandler(handler: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise { // Fill in history results if matching and we are configured to search in history let matchingHistoryEntries: QuickOpenEntry[]; @@ -444,47 +443,41 @@ export class QuickOpenController extends Component implements IQuickOpenService } // Resolve - return this.resolveHandler(handler).then(resolvedHandler => { - const quickOpenModel = new QuickOpenModel(matchingHistoryEntries, this.actionProvider); - - let inputSet = false; - - // If we have matching entries from history we want to show them directly and not wait for the other results to come in - // This also applies when we used to have entries from a previous run and now there are no more history results matching - const previousInput = this.quickOpenWidget.getInput(); - const wasShowingHistory = previousInput && previousInput.entries && previousInput.entries.some(e => e instanceof EditorHistoryEntry || e instanceof EditorHistoryEntryGroup); - if (wasShowingHistory || matchingHistoryEntries.length > 0) { - let responseDelay: Promise; - if (resolvedHandler.hasShortResponseTime()) { - responseDelay = timeout(QuickOpenController.MAX_SHORT_RESPONSE_TIME); - } else { - responseDelay = Promise.resolve(); - } + const resolvedHandler = await this.resolveHandler(handler); - responseDelay.then(() => { - if (!token.isCancellationRequested && !inputSet) { - this.quickOpenWidget.setInput(quickOpenModel, { autoFocusFirstEntry: true }); - inputSet = true; - } - }); + const quickOpenModel = new QuickOpenModel(matchingHistoryEntries, this.actionProvider); + + let inputSet = false; + + // If we have matching entries from history we want to show them directly and not wait for the other results to come in + // This also applies when we used to have entries from a previous run and now there are no more history results matching + const previousInput = this.quickOpenWidget.getInput(); + const wasShowingHistory = previousInput && previousInput.entries && previousInput.entries.some(e => e instanceof EditorHistoryEntry || e instanceof EditorHistoryEntryGroup); + if (wasShowingHistory || matchingHistoryEntries.length > 0) { + if (resolvedHandler.hasShortResponseTime()) { + await timeout(QuickOpenController.MAX_SHORT_RESPONSE_TIME); } - // Get results - return resolvedHandler.getResults(value, token).then(result => { - if (!token.isCancellationRequested) { + if (!token.isCancellationRequested && !inputSet) { + this.quickOpenWidget.setInput(quickOpenModel, { autoFocusFirstEntry: true }); + inputSet = true; + } + } - // now is the time to show the input if we did not have set it before - if (!inputSet) { - this.quickOpenWidget.setInput(quickOpenModel, { autoFocusFirstEntry: true }); - inputSet = true; - } + // Get results + const result = await resolvedHandler.getResults(value, token); + if (!token.isCancellationRequested) { - // merge history and default handler results - const handlerResults = (result && result.entries) || []; - this.mergeResults(quickOpenModel, handlerResults, types.withNullAsUndefined(resolvedHandler.getGroupLabel())); - } - }); - }); + // now is the time to show the input if we did not have set it before + if (!inputSet) { + this.quickOpenWidget.setInput(quickOpenModel, { autoFocusFirstEntry: true }); + inputSet = true; + } + + // merge history and default handler results + const handlerResults = (result && result.entries) || []; + this.mergeResults(quickOpenModel, handlerResults, types.withNullAsUndefined(resolvedHandler.getGroupLabel())); + } } private mergeResults(quickOpenModel: QuickOpenModel, handlerResults: QuickOpenEntry[], groupLabel: string | undefined): void { @@ -516,46 +509,44 @@ export class QuickOpenController extends Component implements IQuickOpenService } } - private handleSpecificHandler(handlerDescriptor: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise { - return this.resolveHandler(handlerDescriptor).then((resolvedHandler: QuickOpenHandler) => { + private async handleSpecificHandler(handlerDescriptor: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise { + const resolvedHandler = await this.resolveHandler(handlerDescriptor); - // Remove handler prefix from search value - value = value.substr(handlerDescriptor.prefix.length); + // Remove handler prefix from search value + value = value.substr(handlerDescriptor.prefix.length); - // Return early if the handler can not run in the current environment and inform the user - const canRun = resolvedHandler.canRun(); - if (types.isUndefinedOrNull(canRun) || (typeof canRun === 'boolean' && !canRun) || typeof canRun === 'string') { - const placeHolderLabel = (typeof canRun === 'string') ? canRun : nls.localize('canNotRunPlaceholder', "This quick open handler can not be used in the current context"); + // Return early if the handler can not run in the current environment and inform the user + const canRun = resolvedHandler.canRun(); + if (types.isUndefinedOrNull(canRun) || (typeof canRun === 'boolean' && !canRun) || typeof canRun === 'string') { + const placeHolderLabel = (typeof canRun === 'string') ? canRun : nls.localize('canNotRunPlaceholder', "This quick open handler can not be used in the current context"); - const model = new QuickOpenModel([new PlaceholderQuickOpenEntry(placeHolderLabel)], this.actionProvider); - this.showModel(model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel())); + const model = new QuickOpenModel([new PlaceholderQuickOpenEntry(placeHolderLabel)], this.actionProvider); + this.showModel(model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel())); - return Promise.resolve(undefined); - } + return Promise.resolve(undefined); + } - // Support extra class from handler - const extraClass = resolvedHandler.getClass(); - if (extraClass) { - this.quickOpenWidget.setExtraClass(extraClass); - } + // Support extra class from handler + const extraClass = resolvedHandler.getClass(); + if (extraClass) { + this.quickOpenWidget.setExtraClass(extraClass); + } - // When handlers change, clear the result list first before loading the new results - if (this.previousActiveHandlerDescriptor !== handlerDescriptor) { - this.clearModel(); - } + // When handlers change, clear the result list first before loading the new results + if (this.previousActiveHandlerDescriptor !== handlerDescriptor) { + this.clearModel(); + } - // Receive Results from Handler and apply - return resolvedHandler.getResults(value, token).then(result => { - if (!token.isCancellationRequested) { - if (!result || !result.entries.length) { - const model = new QuickOpenModel([new PlaceholderQuickOpenEntry(resolvedHandler.getEmptyLabel(value))]); - this.showModel(model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel())); - } else { - this.showModel(result, resolvedHandler.getAutoFocus(value, { model: result, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel())); - } - } - }); - }); + // Receive Results from Handler and apply + const result = await resolvedHandler.getResults(value, token); + if (!token.isCancellationRequested) { + if (!result || !result.entries.length) { + const model = new QuickOpenModel([new PlaceholderQuickOpenEntry(resolvedHandler.getEmptyLabel(value))]); + this.showModel(model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel())); + } else { + this.showModel(result, resolvedHandler.getAutoFocus(value, { model: result, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel())); + } + } } private showModel(model: IModel, autoFocus?: IAutoFocus, ariaLabel?: string): void { @@ -837,7 +828,7 @@ export class RemoveFromEditorHistoryAction extends Action { super(id, label); } - run(): Promise { + async run(): Promise { interface IHistoryPickEntry extends IQuickPickItem { input: IEditorInput | IResourceInput; } @@ -854,11 +845,10 @@ export class RemoveFromEditorHistoryAction extends Action { }; }); - return this.quickInputService.pick(picks, { placeHolder: nls.localize('pickHistory', "Select an editor entry to remove from history"), matchOnDescription: true }).then(pick => { - if (pick) { - this.historyService.remove(pick.input); - } - }); + const pick = await this.quickInputService.pick(picks, { placeHolder: nls.localize('pickHistory', "Select an editor entry to remove from history"), matchOnDescription: true }); + if (pick) { + this.historyService.remove(pick.input); + } } } diff --git a/src/vs/workbench/browser/parts/quickopen/quickopen.ts b/src/vs/workbench/browser/parts/quickopen/quickopen.ts index 808a6e8bb4e..a1e64954456 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickopen.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickopen.ts @@ -23,12 +23,10 @@ export const QUICKOPEN_ACION_LABEL = nls.localize('quickOpen', "Go to File..."); CommandsRegistry.registerCommand({ id: QUICKOPEN_ACTION_ID, - handler: function (accessor: ServicesAccessor, prefix: string | null = null) { + handler: async function (accessor: ServicesAccessor, prefix: string | null = null) { const quickOpenService = accessor.get(IQuickOpenService); - return quickOpenService.show(typeof prefix === 'string' ? prefix : undefined).then(() => { - return undefined; - }); + await quickOpenService.show(typeof prefix === 'string' ? prefix : undefined); }, description: { description: `Quick open`, @@ -42,12 +40,10 @@ CommandsRegistry.registerCommand({ }); export const QUICKOPEN_FOCUS_SECONDARY_ACTION_ID = 'workbench.action.quickOpenPreviousEditor'; -CommandsRegistry.registerCommand(QUICKOPEN_FOCUS_SECONDARY_ACTION_ID, function (accessor: ServicesAccessor, prefix: string | null = null) { +CommandsRegistry.registerCommand(QUICKOPEN_FOCUS_SECONDARY_ACTION_ID, async function (accessor: ServicesAccessor, prefix: string | null = null) { const quickOpenService = accessor.get(IQuickOpenService); - return quickOpenService.show(undefined, { autoFocus: { autoFocusSecondEntry: true } }).then(() => { - return undefined; - }); + await quickOpenService.show(undefined, { autoFocus: { autoFocusSecondEntry: true } }); }); export class BaseQuickOpenNavigateAction extends Action { diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 8ac0298db71..ffbee03d079 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -189,19 +189,18 @@ export class SidebarPart extends CompositePart implements IViewletServi this.hideActiveComposite(); } - openViewlet(id: string | undefined, focus?: boolean): Promise { + async openViewlet(id: string | undefined, focus?: boolean): Promise { if (typeof id === 'string' && this.getViewlet(id)) { return Promise.resolve(this.doOpenViewlet(id, focus)); } - return this.extensionService.whenInstalledExtensionsRegistered() - .then(() => { - if (typeof id === 'string' && this.getViewlet(id)) { - return this.doOpenViewlet(id, focus); - } + await this.extensionService.whenInstalledExtensionsRegistered(); - return null; - }); + if (typeof id === 'string' && this.getViewlet(id)) { + return this.doOpenViewlet(id, focus); + } + + return null; } getViewlets(): ViewletDescriptor[] { diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 5311262f6e4..1070575c2be 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -382,7 +382,7 @@ class StatusBarEntryItem extends Disposable { } } - private executeCommand(id: string, args?: unknown[]) { + private async executeCommand(id: string, args?: unknown[]): Promise { args = args || []; // Maintain old behaviour of always focusing the editor here @@ -398,7 +398,11 @@ class StatusBarEntryItem extends Disposable { } */ this.telemetryService.publicLog('workbenchActionExecuted', { id, from: 'status bar' }); - this.commandService.executeCommand(id, ...args).then(undefined, err => this.notificationService.error(toErrorMessage(err))); + try { + await this.commandService.executeCommand(id, ...args); + } catch (error) { + this.notificationService.error(toErrorMessage(error)); + } } dispose(): void { diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index d96209b11f3..fa0315de8f5 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -391,14 +391,13 @@ export class TitlebarPart extends Part implements ITitleService { const restoreIconContainer = append(this.windowControls, $('div.window-icon-bg')); this.maxRestoreControl = append(restoreIconContainer, $('div.window-icon')); addClass(this.maxRestoreControl, 'window-max-restore'); - this._register(addDisposableListener(this.maxRestoreControl, EventType.CLICK, e => { - this.windowService.isMaximized().then((maximized) => { - if (maximized) { - return this.windowService.unmaximizeWindow(); - } - - return this.windowService.maximizeWindow(); - }); + this._register(addDisposableListener(this.maxRestoreControl, EventType.CLICK, async e => { + const maximized = await this.windowService.isMaximized(); + if (maximized) { + return this.windowService.unmaximizeWindow(); + } + + return this.windowService.maximizeWindow(); })); // Close diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 8c4e0f8337c..de4b7c1512b 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -251,15 +251,13 @@ export class CustomTreeView extends Disposable implements ITreeView { set dataProvider(dataProvider: ITreeViewDataProvider | null) { if (dataProvider) { this._dataProvider = new class implements ITreeViewDataProvider { - getChildren(node: ITreeItem): Promise { + async getChildren(node: ITreeItem): Promise { if (node && node.children) { return Promise.resolve(node.children); } - const promise = node instanceof Root ? dataProvider.getChildren() : dataProvider.getChildren(node); - return promise.then(children => { - node.children = children; - return children; - }); + const children = await (node instanceof Root ? dataProvider.getChildren() : dataProvider.getChildren(node)); + node.children = children; + return children; } }; this.updateMessage(); @@ -524,19 +522,16 @@ export class CustomTreeView extends Disposable implements ITreeView { } private refreshing: boolean = false; - private doRefresh(elements: ITreeItem[]): Promise { + private async doRefresh(elements: ITreeItem[]): Promise { if (this.tree) { this.refreshing = true; - return Promise.all(elements.map(e => this.tree.refresh(e))) - .then(() => { - this.refreshing = false; - this.updateContentAreas(); - if (this.focused) { - this.focus(); - } - }); + await Promise.all(elements.map(e => this.tree.refresh(e))); + this.refreshing = false; + this.updateContentAreas(); + if (this.focused) { + this.focus(); + } } - return Promise.resolve(undefined); } private updateContentAreas(): void { diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 64ef1a1f105..ddf297c0264 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -618,21 +618,19 @@ export class ViewsService extends Disposable implements IViewsService { return viewDescriptorCollectionItem ? viewDescriptorCollectionItem.viewDescriptorCollection : null; } - openView(id: string, focus: boolean): Promise { + async openView(id: string, focus: boolean): Promise { const viewContainer = Registry.as(ViewExtensions.ViewsRegistry).getViewContainer(id); if (viewContainer) { const viewletDescriptor = this.viewletService.getViewlet(viewContainer.id); if (viewletDescriptor) { - return this.viewletService.openViewlet(viewletDescriptor.id, focus) - .then((viewlet: IViewsViewlet) => { - if (viewlet && viewlet.openView) { - return viewlet.openView(id, focus); - } - return null; - }); + const viewlet = await this.viewletService.openViewlet(viewletDescriptor.id, focus) as IViewsViewlet | null; + if (viewlet && viewlet.openView) { + return viewlet.openView(id, focus); + } } } - return Promise.resolve(null); + + return null; } private onDidRegisterViewContainer(viewContainer: ViewContainer): void { @@ -669,7 +667,7 @@ export class ViewsService extends Disposable implements IViewsService { }; const when = ContextKeyExpr.has(`${viewDescriptor.id}.active`); - disposables.push(CommandsRegistry.registerCommand(command.id, () => this.openView(viewDescriptor.id, true).then(() => null))); + disposables.push(CommandsRegistry.registerCommand(command.id, () => this.openView(viewDescriptor.id, true))); disposables.push(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command, diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index c4867584fa2..c2ad7bd6fec 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -26,28 +26,27 @@ class CodeRendererMain extends Disposable { private workbench: Workbench; - open(): Promise { + async open(): Promise { const services = this.initServices(); - return domContentLoaded().then(() => { - mark('willStartWorkbench'); + await domContentLoaded(); + mark('willStartWorkbench'); - // Create Workbench - this.workbench = new Workbench( - document.body, - services.serviceCollection, - services.logService - ); + // Create Workbench + this.workbench = new Workbench( + document.body, + services.serviceCollection, + services.logService + ); - // Layout - this._register(addDisposableListener(window, EventType.RESIZE, () => this.workbench.layout())); + // Layout + this._register(addDisposableListener(window, EventType.RESIZE, () => this.workbench.layout())); - // Workbench Lifecycle - this._register(this.workbench.onShutdown(() => this.dispose())); + // Workbench Lifecycle + this._register(this.workbench.onShutdown(() => this.dispose())); - // Startup - this.workbench.startup(); - }); + // Startup + this.workbench.startup(); } private initServices(): { serviceCollection: ServiceCollection, logService: ILogService } { diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 0aab890a5c2..8c79c0e728a 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -129,7 +129,7 @@ export class Workbench extends Layout { // Services const instantiationService = this.initServices(this.serviceCollection); - instantiationService.invokeFunction(accessor => { + instantiationService.invokeFunction(async accessor => { const lifecycleService = accessor.get(ILifecycleService); const storageService = accessor.get(IStorageService); const configurationService = accessor.get(IConfigurationService); @@ -156,7 +156,11 @@ export class Workbench extends Layout { this.layout(); // Restore - this.restoreWorkbench(accessor.get(IEditorService), accessor.get(IEditorGroupsService), accessor.get(IViewletService), accessor.get(IPanelService), accessor.get(ILogService), lifecycleService).then(undefined, error => onUnexpectedError(error)); + try { + await this.restoreWorkbench(accessor.get(IEditorService), accessor.get(IEditorGroupsService), accessor.get(IViewletService), accessor.get(IPanelService), accessor.get(ILogService), lifecycleService); + } catch (error) { + onUnexpectedError(error); + } }); return instantiationService; @@ -331,7 +335,7 @@ export class Workbench extends Layout { registerNotificationCommands(notificationsCenter, notificationsToasts); } - private restoreWorkbench( + private async restoreWorkbench( editorService: IEditorService, editorGroupService: IEditorGroupsService, viewletService: IViewletService, @@ -342,36 +346,39 @@ export class Workbench extends Layout { const restorePromises: Promise[] = []; // Restore editors - mark('willRestoreEditors'); - restorePromises.push(editorGroupService.whenRestored.then(() => { + restorePromises.push((async () => { + mark('willRestoreEditors'); - function openEditors(editors: IResourceEditor[], editorService: IEditorService) { - if (editors.length) { - return editorService.openEditors(editors); - } + // first ensure the editor part is restored + await editorGroupService.whenRestored; - return Promise.resolve(undefined); + // then see for editors to open as instructed + let editors: IResourceEditor[]; + if (Array.isArray(this.state.editor.editorsToOpen)) { + editors = this.state.editor.editorsToOpen; + } else { + editors = await this.state.editor.editorsToOpen; } - if (Array.isArray(this.state.editor.editorsToOpen)) { - return openEditors(this.state.editor.editorsToOpen, editorService); + if (editors.length) { + await editorService.openEditors(editors); } - return this.state.editor.editorsToOpen.then(editors => openEditors(editors, editorService)); - }).then(() => mark('didRestoreEditors'))); + mark('didRestoreEditors'); + })()); // Restore Sidebar if (this.state.sideBar.viewletToRestore) { - mark('willRestoreViewlet'); - restorePromises.push(viewletService.openViewlet(this.state.sideBar.viewletToRestore) - .then(viewlet => { - if (!viewlet) { - return viewletService.openViewlet(viewletService.getDefaultViewletId()); // fallback to default viewlet as needed - } - - return viewlet; - }) - .then(() => mark('didRestoreViewlet'))); + restorePromises.push((async () => { + mark('willRestoreViewlet'); + + const viewlet = await viewletService.openViewlet(this.state.sideBar.viewletToRestore); + if (!viewlet) { + await viewletService.openViewlet(viewletService.getDefaultViewletId()); // fallback to default viewlet as needed + } + + mark('didRestoreViewlet'); + })()); } // Restore Panel @@ -394,23 +401,24 @@ export class Workbench extends Layout { // Emit a warning after 10s if restore does not complete const restoreTimeoutHandle = setTimeout(() => logService.warn('Workbench did not finish loading in 10 seconds, that might be a problem that should be reported.'), 10000); - return Promise.all(restorePromises) - .then(() => clearTimeout(restoreTimeoutHandle)) - .catch(error => onUnexpectedError(error)) - .finally(() => { + try { + await Promise.all(restorePromises); - // Set lifecycle phase to `Restored` - lifecycleService.phase = LifecyclePhase.Restored; + clearTimeout(restoreTimeoutHandle); + } catch (error) { + onUnexpectedError(error); + } finally { - // Set lifecycle phase to `Eventually` after a short delay and when idle (min 2.5sec, max 5sec) - setTimeout(() => { - this._register(runWhenIdle(() => { - lifecycleService.phase = LifecyclePhase.Eventually; - }, 2500)); - }, 2500); + // Set lifecycle phase to `Restored` + lifecycleService.phase = LifecyclePhase.Restored; - // Telemetry: startup metrics - mark('didStartWorkbench'); - }); + // Set lifecycle phase to `Eventually` after a short delay and when idle (min 2.5sec, max 5sec) + setTimeout(() => { + this._register(runWhenIdle(() => lifecycleService.phase = LifecyclePhase.Eventually, 2500)); + }, 2500); + + // Telemetry: startup metrics + mark('didStartWorkbench'); + } } } diff --git a/src/vs/workbench/common/actions.ts b/src/vs/workbench/common/actions.ts index 8db73d85924..af3e7f2e497 100644 --- a/src/vs/workbench/common/actions.ts +++ b/src/vs/workbench/common/actions.ts @@ -82,46 +82,40 @@ Registry.add(Extensions.WorkbenchActions, new class implements IWorkbenchActionR } private createCommandHandler(descriptor: SyncActionDescriptor): ICommandHandler { - return (accessor, args) => { + return async (accessor, args) => { const notificationService = accessor.get(INotificationService); const instantiationService = accessor.get(IInstantiationService); const lifecycleService = accessor.get(ILifecycleService); - Promise.resolve(this.triggerAndDisposeAction(instantiationService, lifecycleService, descriptor, args)).then(undefined, err => { - notificationService.error(err); - }); + try { + await this.triggerAndDisposeAction(instantiationService, lifecycleService, descriptor, args); + } catch (error) { + notificationService.error(error); + } }; } - private triggerAndDisposeAction(instantiationService: IInstantiationService, lifecycleService: ILifecycleService, descriptor: SyncActionDescriptor, args: any): Promise { + private async triggerAndDisposeAction(instantiationService: IInstantiationService, lifecycleService: ILifecycleService, descriptor: SyncActionDescriptor, args: any): Promise { // run action when workbench is created - return lifecycleService.when(LifecyclePhase.Ready).then(() => { - const actionInstance = instantiationService.createInstance(descriptor.syncDescriptor); - try { - actionInstance.label = descriptor.label || actionInstance.label; - - // don't run the action when not enabled - if (!actionInstance.enabled) { - actionInstance.dispose(); - - return undefined; - } + await lifecycleService.when(LifecyclePhase.Ready); - const from = args && args.from || 'keybinding'; + const actionInstance = instantiationService.createInstance(descriptor.syncDescriptor); + actionInstance.label = descriptor.label || actionInstance.label; - return Promise.resolve(actionInstance.run(undefined, { from })).then(() => { - actionInstance.dispose(); - }, err => { - actionInstance.dispose(); + // don't run the action when not enabled + if (!actionInstance.enabled) { + actionInstance.dispose(); - return Promise.reject(err); - }); - } catch (err) { - actionInstance.dispose(); + return; + } - return Promise.reject(err); - } - }); + // otherwise run and dispose + try { + const from = args && args.from || 'keybinding'; + await actionInstance.run(undefined, { from }); + } finally { + actionInstance.dispose(); + } } }); diff --git a/src/vs/workbench/common/contributions.ts b/src/vs/workbench/common/contributions.ts index 61acada214e..cc28bacaf7c 100644 --- a/src/vs/workbench/common/contributions.ts +++ b/src/vs/workbench/common/contributions.ts @@ -80,9 +80,7 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry // Otherwise wait for phase to be reached else { - lifecycleService.when(phase).then(() => { - this.doInstantiateByPhase(instantiationService, phase); - }); + lifecycleService.when(phase).then(() => this.doInstantiateByPhase(instantiationService, phase)); } } diff --git a/src/vs/workbench/common/editor/binaryEditorModel.ts b/src/vs/workbench/common/editor/binaryEditorModel.ts index fe07e754bc9..48c1cf5582c 100644 --- a/src/vs/workbench/common/editor/binaryEditorModel.ts +++ b/src/vs/workbench/common/editor/binaryEditorModel.ts @@ -72,20 +72,17 @@ export class BinaryEditorModel extends EditorModel { return this.etag; } - load(): Promise { + async load(): Promise { // Make sure to resolve up to date stat for file resources if (this.fileService.canHandleResource(this.resource)) { - return this.fileService.resolve(this.resource, { resolveMetadata: true }).then(stat => { - this.etag = stat.etag; - if (typeof stat.size === 'number') { - this.size = stat.size; - } - - return this; - }); + const stat = await this.fileService.resolve(this.resource, { resolveMetadata: true }); + this.etag = stat.etag; + if (typeof stat.size === 'number') { + this.size = stat.size; + } } - return Promise.resolve(this); + return this; } } diff --git a/src/vs/workbench/common/editor/diffEditorInput.ts b/src/vs/workbench/common/editor/diffEditorInput.ts index b962443bd7e..bd4aea9a15b 100644 --- a/src/vs/workbench/common/editor/diffEditorInput.ts +++ b/src/vs/workbench/common/editor/diffEditorInput.ts @@ -34,45 +34,44 @@ export class DiffEditorInput extends SideBySideEditorInput { return this.master; } - resolve(): Promise { + async resolve(): Promise { // Create Model - we never reuse our cached model if refresh is true because we cannot // decide for the inputs within if the cached model can be reused or not. There may be // inputs that need to be loaded again and thus we always recreate the model and dispose // the previous one - if any. - return this.createModel().then(resolvedModel => { - if (this.cachedModel) { - this.cachedModel.dispose(); - } + const resolvedModel = await this.createModel(); + if (this.cachedModel) { + this.cachedModel.dispose(); + } - this.cachedModel = resolvedModel; + this.cachedModel = resolvedModel; - return this.cachedModel; - }); + return this.cachedModel; } getPreferredEditorId(candidates: string[]): string { return this.forceOpenAsBinary ? BINARY_DIFF_EDITOR_ID : TEXT_DIFF_EDITOR_ID; } - private createModel(): Promise { + private async createModel(): Promise { // Join resolve call over two inputs and build diff editor model - return Promise.all([ + const models = await Promise.all([ this.originalInput.resolve(), this.modifiedInput.resolve() - ]).then(models => { - const originalEditorModel = models[0]; - const modifiedEditorModel = models[1]; - - // If both are text models, return textdiffeditor model - if (modifiedEditorModel instanceof BaseTextEditorModel && originalEditorModel instanceof BaseTextEditorModel) { - return new TextDiffEditorModel(originalEditorModel, modifiedEditorModel); - } - - // Otherwise return normal diff model - return new DiffEditorModel(originalEditorModel, modifiedEditorModel); - }); + ]); + + const originalEditorModel = models[0]; + const modifiedEditorModel = models[1]; + + // If both are text models, return textdiffeditor model + if (modifiedEditorModel instanceof BaseTextEditorModel && originalEditorModel instanceof BaseTextEditorModel) { + return new TextDiffEditorModel(originalEditorModel, modifiedEditorModel); + } + + // Otherwise return normal diff model + return new DiffEditorModel(originalEditorModel, modifiedEditorModel); } dispose(): void { diff --git a/src/vs/workbench/common/editor/diffEditorModel.ts b/src/vs/workbench/common/editor/diffEditorModel.ts index 544d9901316..2f3b4818e2b 100644 --- a/src/vs/workbench/common/editor/diffEditorModel.ts +++ b/src/vs/workbench/common/editor/diffEditorModel.ts @@ -37,11 +37,13 @@ export class DiffEditorModel extends EditorModel { return this._modifiedModel; } - load(): Promise { - return Promise.all([ + async load(): Promise { + await Promise.all([ this._originalModel ? this._originalModel.load() : Promise.resolve(undefined), this._modifiedModel ? this._modifiedModel.load() : Promise.resolve(undefined), - ]).then(() => this); + ]); + + return this; } isResolved(): boolean { diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index 6ff63c0a617..b6691ee28f3 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -76,31 +76,31 @@ export class ResourceEditorInput extends EditorInput implements IModeSupport { this.preferredMode = mode; } - resolve(): Promise { + async resolve(): Promise { if (!this.modelReference) { this.modelReference = this.textModelResolverService.createModelReference(this.resource); } - return this.modelReference.then(ref => { - const model = ref.object; + const ref = await this.modelReference; - // Ensure the resolved model is of expected type - if (!(model instanceof ResourceEditorModel)) { - ref.dispose(); - this.modelReference = null; + const model = ref.object; - return Promise.reject(new Error(`Unexpected model for ResourceInput: ${this.resource}`)); - } + // Ensure the resolved model is of expected type + if (!(model instanceof ResourceEditorModel)) { + ref.dispose(); + this.modelReference = null; + + return Promise.reject(new Error(`Unexpected model for ResourceInput: ${this.resource}`)); + } - this.cachedModel = model; + this.cachedModel = model; - // Set mode if we have a preferred mode configured - if (this.preferredMode) { - model.setMode(this.preferredMode); - } + // Set mode if we have a preferred mode configured + if (this.preferredMode) { + model.setMode(this.preferredMode); + } - return model; - }); + return model; } matches(otherInput: unknown): boolean { diff --git a/src/vs/workbench/common/editor/resourceEditorModel.ts b/src/vs/workbench/common/editor/resourceEditorModel.ts index ae5d8ff2e90..e7ddc5dc07a 100644 --- a/src/vs/workbench/common/editor/resourceEditorModel.ts +++ b/src/vs/workbench/common/editor/resourceEditorModel.ts @@ -26,6 +26,7 @@ export class ResourceEditorModel extends BaseTextEditorModel { } dispose(): void { + // TODO@Joao: force this class to dispose the underlying model if (this.textEditorModelHandle) { this.modelService.destroyModel(this.textEditorModelHandle); diff --git a/src/vs/workbench/common/editor/textDiffEditorModel.ts b/src/vs/workbench/common/editor/textDiffEditorModel.ts index 4a8ec42d1db..1f48d92db29 100644 --- a/src/vs/workbench/common/editor/textDiffEditorModel.ts +++ b/src/vs/workbench/common/editor/textDiffEditorModel.ts @@ -33,12 +33,12 @@ export class TextDiffEditorModel extends DiffEditorModel { return this._modifiedModel; } - load(): Promise { - return super.load().then(() => { - this.updateTextDiffEditorModel(); + async load(): Promise { + await super.load(); - return this; - }); + this.updateTextDiffEditorModel(); + + return this; } private updateTextDiffEditorModel(): void { diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index a658a8f3793..dee5ab87281 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -11,7 +11,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { Event, Emitter } from 'vs/base/common/event'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { ITextBufferFactory } from 'vs/editor/common/model'; import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; @@ -133,52 +133,48 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin return Promise.resolve(); } - load(): Promise { + async load(): Promise { // Check for backups first - return this.backupFileService.loadBackupResource(this.resource).then(backupResource => { - if (backupResource) { - return this.backupFileService.resolveBackupContent(backupResource); - } - - return Promise.resolve(undefined); - }).then(backup => { - const hasBackup = !!backup; + let backup: IResolvedBackup | undefined = undefined; + const backupResource = await this.backupFileService.loadBackupResource(this.resource); + if (backupResource) { + backup = await this.backupFileService.resolveBackupContent(backupResource); + } - // untitled associated to file path are dirty right away as well as untitled with content - this.setDirty(this._hasAssociatedFilePath || hasBackup || !!this.initialValue); + // untitled associated to file path are dirty right away as well as untitled with content + this.setDirty(this._hasAssociatedFilePath || !!backup || !!this.initialValue); - let untitledContents: ITextBufferFactory; - if (backup) { - untitledContents = backup.value; - } else { - untitledContents = createTextBufferFactory(this.initialValue || ''); - } + let untitledContents: ITextBufferFactory; + if (backup) { + untitledContents = backup.value; + } else { + untitledContents = createTextBufferFactory(this.initialValue || ''); + } - // Create text editor model if not yet done - if (!this.textEditorModel) { - this.createTextEditorModel(untitledContents, this.resource, this.preferredMode); - } + // Create text editor model if not yet done + if (!this.textEditorModel) { + this.createTextEditorModel(untitledContents, this.resource, this.preferredMode); + } - // Otherwise update - else { - this.updateTextEditorModel(untitledContents, this.preferredMode); - } + // Otherwise update + else { + this.updateTextEditorModel(untitledContents, this.preferredMode); + } - // Encoding - this.configuredEncoding = this.configurationService.getValue(this.resource, 'files.encoding'); + // Encoding + this.configuredEncoding = this.configurationService.getValue(this.resource, 'files.encoding'); - // We know for a fact there is a text editor model here - const textEditorModel = this.textEditorModel!; + // We know for a fact there is a text editor model here + const textEditorModel = this.textEditorModel!; - // Listen to content changes - this._register(textEditorModel.onDidChangeContent(() => this.onModelContentChanged())); + // Listen to content changes + this._register(textEditorModel.onDidChangeContent(() => this.onModelContentChanged())); - // Listen to mode changes - this._register(textEditorModel.onDidChangeLanguage(() => this.onConfigurationChange())); // mode change can have impact on config + // Listen to mode changes + this._register(textEditorModel.onDidChangeLanguage(() => this.onConfigurationChange())); // mode change can have impact on config - return this as UntitledEditorModel & IResolvedTextEditorModel; - }); + return this as UntitledEditorModel & IResolvedTextEditorModel; } private onModelContentChanged(): void { diff --git a/src/vs/workbench/electron-browser/actions/helpActions.ts b/src/vs/workbench/electron-browser/actions/helpActions.ts index 75849bbca5a..3c5d242f92f 100644 --- a/src/vs/workbench/electron-browser/actions/helpActions.ts +++ b/src/vs/workbench/electron-browser/actions/helpActions.ts @@ -113,11 +113,10 @@ export class OpenNewsletterSignupUrlAction extends Action { this.telemetryService = telemetryService; } - run(): Promise { - this.telemetryService.getTelemetryInfo().then(info => { - window.open(`${OpenNewsletterSignupUrlAction.URL}?machineId=${encodeURIComponent(info.machineId)}`); - }); - return Promise.resolve(); + async run(): Promise { + const info = await this.telemetryService.getTelemetryInfo(); + + window.open(`${OpenNewsletterSignupUrlAction.URL}?machineId=${encodeURIComponent(info.machineId)}`); } } diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts index 223fda7d7a7..ce3d1279119 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -86,7 +86,7 @@ export abstract class BaseZoomAction extends Action { super(id, label); } - protected setConfiguredZoomLevel(level: number): void { + protected async setConfiguredZoomLevel(level: number): Promise { level = Math.round(level); // when reaching smallest zoom, prevent fractional zoom levels const applyZoom = () => { @@ -98,7 +98,9 @@ export abstract class BaseZoomAction extends Action { browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false); }; - this.configurationService.updateValue(BaseZoomAction.SETTING_KEY, level).then(() => applyZoom()); + await this.configurationService.updateValue(BaseZoomAction.SETTING_KEY, level); + + applyZoom(); } } @@ -175,8 +177,10 @@ export class ReloadWindowAction extends Action { super(id, label); } - run(): Promise { - return this.windowService.reloadWindow().then(() => true); + async run(): Promise { + await this.windowService.reloadWindow(); + + return true; } } @@ -193,8 +197,10 @@ export class ReloadWindowWithExtensionsDisabledAction extends Action { super(id, label); } - run(): Promise { - return this.windowService.reloadWindow({ _: [], 'disable-extensions': true }).then(() => true); + async run(): Promise { + await this.windowService.reloadWindow({ _: [], 'disable-extensions': true }); + + return true; } } @@ -221,41 +227,38 @@ export abstract class BaseSwitchWindow extends Action { protected abstract isQuickNavigate(): boolean; - run(): Promise { + async run(): Promise { const currentWindowId = this.windowService.windowId; - return this.windowsService.getWindows().then(windows => { - const placeHolder = nls.localize('switchWindowPlaceHolder', "Select a window to switch to"); - const picks = windows.map(win => { - const resource = win.filename ? URI.file(win.filename) : win.folderUri ? win.folderUri : win.workspace ? win.workspace.configPath : undefined; - const fileKind = win.filename ? FileKind.FILE : win.workspace ? FileKind.ROOT_FOLDER : win.folderUri ? FileKind.FOLDER : FileKind.FILE; - return { - payload: win.id, - label: win.title, - iconClasses: getIconClasses(this.modelService, this.modeService, resource, fileKind), - description: (currentWindowId === win.id) ? nls.localize('current', "Current Window") : undefined, - buttons: (!this.isQuickNavigate() && currentWindowId !== win.id) ? [this.closeWindowAction] : undefined - }; - }); - - const autoFocusIndex = (picks.indexOf(picks.filter(pick => pick.payload === currentWindowId)[0]) + 1) % picks.length; - - return this.quickInputService.pick(picks, { - contextKey: 'inWindowsPicker', - activeItem: picks[autoFocusIndex], - placeHolder, - quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined, - onDidTriggerItemButton: context => { - this.windowsService.closeWindow(context.item.payload).then(() => { - context.removeItem(); - }); - } - }); - }).then(pick => { - if (pick) { - this.windowsService.focusWindow(pick.payload); + const windows = await this.windowsService.getWindows(); + const placeHolder = nls.localize('switchWindowPlaceHolder', "Select a window to switch to"); + const picks = windows.map(win => { + const resource = win.filename ? URI.file(win.filename) : win.folderUri ? win.folderUri : win.workspace ? win.workspace.configPath : undefined; + const fileKind = win.filename ? FileKind.FILE : win.workspace ? FileKind.ROOT_FOLDER : win.folderUri ? FileKind.FOLDER : FileKind.FILE; + return { + payload: win.id, + label: win.title, + iconClasses: getIconClasses(this.modelService, this.modeService, resource, fileKind), + description: (currentWindowId === win.id) ? nls.localize('current', "Current Window") : undefined, + buttons: (!this.isQuickNavigate() && currentWindowId !== win.id) ? [this.closeWindowAction] : undefined + }; + }); + const autoFocusIndex = (picks.indexOf(picks.filter(pick => pick.payload === currentWindowId)[0]) + 1) % picks.length; + + const pick = await this.quickInputService.pick(picks, { + contextKey: 'inWindowsPicker', + activeItem: picks[autoFocusIndex], + placeHolder, + quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined, + onDidTriggerItemButton: async context => { + await this.windowsService.closeWindow(context.item.payload); + context.removeItem(); } }); + + if (pick) { + this.windowsService.focusWindow(pick.payload); + } } } @@ -331,12 +334,13 @@ export abstract class BaseOpenRecentAction extends Action { protected abstract isQuickNavigate(): boolean; - run(): Promise { - return this.windowService.getRecentlyOpened() - .then(({ workspaces, files }) => this.openRecent(workspaces, files)); + async run(): Promise { + const { workspaces, files } = await this.windowService.getRecentlyOpened(); + + this.openRecent(workspaces, files); } - private openRecent(recentWorkspaces: Array, recentFiles: IRecentFile[]): void { + private async openRecent(recentWorkspaces: Array, recentFiles: IRecentFile[]): Promise { const toPick = (recent: IRecent, labelService: ILabelService, buttons: IQuickInputButton[] | undefined) => { let uriToOpen: IURIToOpen | undefined; @@ -376,26 +380,26 @@ export abstract class BaseOpenRecentAction extends Action { const firstEntry = recentWorkspaces[0]; let autoFocusSecondEntry: boolean = firstEntry && this.contextService.isCurrentWorkspace(isRecentWorkspace(firstEntry) ? firstEntry.workspace : firstEntry.folderUri); - let keyMods: IKeyMods; + let keyMods: IKeyMods | undefined; const workspaceSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('workspaces', "workspaces") }; const fileSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('files', "files") }; const picks = [workspaceSeparator, ...workspacePicks, fileSeparator, ...filePicks]; - this.quickInputService.pick(picks, { + const pick = await this.quickInputService.pick(picks, { contextKey: inRecentFilesPickerContextKey, activeItem: [...workspacePicks, ...filePicks][autoFocusSecondEntry ? 1 : 0], placeHolder: isMacintosh ? nls.localize('openRecentPlaceHolderMac', "Select to open (hold Cmd-key to open in new window)") : nls.localize('openRecentPlaceHolder', "Select to open (hold Ctrl-key to open in new window)"), matchOnDescription: true, onKeyMods: mods => keyMods = mods, quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined, - onDidTriggerItemButton: context => { - this.windowsService.removeFromRecentlyOpened([context.item.resource]).then(() => context.removeItem()); - } - }).then((pick): Promise | void => { - if (pick) { - const forceNewWindow = keyMods.ctrlCmd; - return this.windowService.openWindow([pick.uriToOpen], { forceNewWindow }); + onDidTriggerItemButton: async context => { + await this.windowsService.removeFromRecentlyOpened([context.item.resource]); + context.removeItem(); } }); + + if (pick) { + return this.windowService.openWindow([pick.uriToOpen], { forceNewWindow: keyMods && keyMods.ctrlCmd }); + } } } diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index c35f3e95a25..40b009039a2 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -106,42 +106,39 @@ class CodeRendererMain extends Disposable { } } - open(): Promise { - return this.initServices().then(services => { + async open(): Promise { + const services = await this.initServices(); + await domContentLoaded(); + mark('willStartWorkbench'); - return domContentLoaded().then(() => { - mark('willStartWorkbench'); + // Create Workbench + this.workbench = new Workbench(document.body, services.serviceCollection, services.logService); - // Create Workbench - this.workbench = new Workbench(document.body, services.serviceCollection, services.logService); + // Layout + this._register(addDisposableListener(window, EventType.RESIZE, e => this.onWindowResize(e, true))); - // Layout - this._register(addDisposableListener(window, EventType.RESIZE, e => this.onWindowResize(e, true))); + // Workbench Lifecycle + this._register(this.workbench.onShutdown(() => this.dispose())); + this._register(this.workbench.onWillShutdown(event => event.join(services.storageService.close()))); - // Workbench Lifecycle - this._register(this.workbench.onShutdown(() => this.dispose())); - this._register(this.workbench.onWillShutdown(event => event.join(services.storageService.close()))); + // Startup + const instantiationService = this.workbench.startup(); - // Startup - const instantiationService = this.workbench.startup(); + // Window + this._register(instantiationService.createInstance(ElectronWindow)); - // Window - this._register(instantiationService.createInstance(ElectronWindow)); - - // Driver - if (this.configuration.driver) { - instantiationService.invokeFunction(accessor => registerWindowDriver(accessor).then(disposable => this._register(disposable))); - } + // Driver + if (this.configuration.driver) { + instantiationService.invokeFunction(async accessor => this._register(await registerWindowDriver(accessor))); + } - // Config Exporter - if (this.configuration['export-default-configuration']) { - instantiationService.createInstance(DefaultConfigurationExportHelper); - } + // Config Exporter + if (this.configuration['export-default-configuration']) { + instantiationService.createInstance(DefaultConfigurationExportHelper); + } - // Logging - services.logService.trace('workbench configuration', JSON.stringify(this.configuration)); - }); - }); + // Logging + services.logService.trace('workbench configuration', JSON.stringify(this.configuration)); } private onWindowResize(e: Event, retry: boolean): void { @@ -162,7 +159,7 @@ class CodeRendererMain extends Disposable { } } - private initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: StorageService }> { + private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: StorageService }> { const serviceCollection = new ServiceCollection(); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -203,7 +200,9 @@ class CodeRendererMain extends Disposable { fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider); } - return this.resolveWorkspaceInitializationPayload(environmentService).then(payload => Promise.all([ + const payload = await this.resolveWorkspaceInitializationPayload(environmentService); + + const services = await Promise.all([ this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, logService).then(service => { // Workspace @@ -222,10 +221,12 @@ class CodeRendererMain extends Disposable { return service; }) - ]).then(services => ({ serviceCollection, logService, storageService: services[1] }))); + ]); + + return { serviceCollection, logService, storageService: services[1] }; } - private resolveWorkspaceInitializationPayload(environmentService: IWorkbenchEnvironmentService): Promise { + private async resolveWorkspaceInitializationPayload(environmentService: IWorkbenchEnvironmentService): Promise { // Multi-root workspace if (this.configuration.workspace) { @@ -233,32 +234,29 @@ class CodeRendererMain extends Disposable { } // Single-folder workspace - let workspaceInitializationPayload: Promise = Promise.resolve(undefined); + let workspaceInitializationPayload: IWorkspaceInitializationPayload | undefined; if (this.configuration.folderUri) { - workspaceInitializationPayload = this.resolveSingleFolderWorkspaceInitializationPayload(this.configuration.folderUri); + workspaceInitializationPayload = await this.resolveSingleFolderWorkspaceInitializationPayload(this.configuration.folderUri); } - return workspaceInitializationPayload.then(payload => { - - // Fallback to empty workspace if we have no payload yet. - if (!payload) { - let id: string; - if (this.configuration.backupPath) { - id = basename(this.configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID - } else if (environmentService.isExtensionDevelopment) { - id = 'ext-dev'; // extension development window never stores backups and is a singleton - } else { - return Promise.reject(new Error('Unexpected window configuration without backupPath')); - } - - payload = { id }; + // Fallback to empty workspace if we have no payload yet. + if (!workspaceInitializationPayload) { + let id: string; + if (this.configuration.backupPath) { + id = basename(this.configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID + } else if (environmentService.isExtensionDevelopment) { + id = 'ext-dev'; // extension development window never stores backups and is a singleton + } else { + throw new Error('Unexpected window configuration without backupPath'); } - return payload; - }); + workspaceInitializationPayload = { id }; + } + + return workspaceInitializationPayload; } - private resolveSingleFolderWorkspaceInitializationPayload(folderUri: ISingleFolderWorkspaceIdentifier): Promise { + private async resolveSingleFolderWorkspaceInitializationPayload(folderUri: ISingleFolderWorkspaceIdentifier): Promise { // Return early the folder is not local if (folderUri.scheme !== Schemas.file) { @@ -285,40 +283,54 @@ class CodeRendererMain extends Disposable { } // For local: ensure path is absolute and exists - const sanitizedFolderPath = sanitizeFilePath(folderUri.fsPath, process.env['VSCODE_CWD'] || process.cwd()); - return stat(sanitizedFolderPath).then(stat => { + try { + const sanitizedFolderPath = sanitizeFilePath(folderUri.fsPath, process.env['VSCODE_CWD'] || process.cwd()); + const fileStat = await stat(sanitizedFolderPath); + const sanitizedFolderUri = URI.file(sanitizedFolderPath); return { - id: computeLocalDiskFolderId(sanitizedFolderUri, stat), + id: computeLocalDiskFolderId(sanitizedFolderUri, fileStat), folder: sanitizedFolderUri }; - }, error => onUnexpectedError(error)); + } catch (error) { + onUnexpectedError(error); + } + + return; } - private createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise { + private async createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise { const configurationFileService = new ConfigurationFileService(); configurationFileService.fileService = fileService; const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService); - return workspaceService.initialize(payload).then(() => workspaceService, error => { + try { + await workspaceService.initialize(payload); + + return workspaceService; + } catch (error) { onUnexpectedError(error); logService.error(error); return workspaceService; - }); + } } - private createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, logService: ILogService, mainProcessService: IMainProcessService): Promise { + private async createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, logService: ILogService, mainProcessService: IMainProcessService): Promise { const globalStorageDatabase = new GlobalStorageDatabaseChannelClient(mainProcessService.getChannel('storage')); const storageService = new StorageService(globalStorageDatabase, logService, environmentService); - return storageService.initialize(payload).then(() => storageService, error => { + try { + await storageService.initialize(payload); + + return storageService; + } catch (error) { onUnexpectedError(error); logService.error(error); return storageService; - }); + } } private createLogService(mainProcessService: IMainProcessService, environmentService: IWorkbenchEnvironmentService): ILogService { diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index c3d153bcb44..7a52a1b49de 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -117,7 +117,7 @@ export class ElectronWindow extends Disposable { }); // Support runAction event - ipc.on('vscode:runAction', (event: Event, request: IRunActionInWindowRequest) => { + ipc.on('vscode:runAction', async (event: Event, request: IRunActionInWindowRequest) => { const args: unknown[] = request.args || []; // If we run an action from the touchbar, we fill in the currently active resource @@ -134,7 +134,9 @@ export class ElectronWindow extends Disposable { args.push({ from: request.from }); // TODO@telemetry this is a bit weird to send this to every action? } - this.commandService.executeCommand(request.id, ...args).then(_ => { + try { + await this.commandService.executeCommand(request.id, ...args); + /* __GDPR__ "commandExecuted" : { "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, @@ -142,9 +144,9 @@ export class ElectronWindow extends Disposable { } */ this.telemetryService.publicLog('commandExecuted', { id: request.id, from: request.from }); - }, err => { - this.notificationService.error(err); - }); + } catch (error) { + this.notificationService.error(error); + } }); // Support runKeybinding event @@ -173,34 +175,30 @@ export class ElectronWindow extends Disposable { }); // Fullscreen Events - ipc.on('vscode:enterFullScreen', () => { - this.lifecycleService.when(LifecyclePhase.Ready).then(() => { - browser.setFullscreen(true); - }); + ipc.on('vscode:enterFullScreen', async () => { + await this.lifecycleService.when(LifecyclePhase.Ready); + browser.setFullscreen(true); }); - ipc.on('vscode:leaveFullScreen', () => { - this.lifecycleService.when(LifecyclePhase.Ready).then(() => { - browser.setFullscreen(false); - }); + ipc.on('vscode:leaveFullScreen', async () => { + await this.lifecycleService.when(LifecyclePhase.Ready); + browser.setFullscreen(false); }); // High Contrast Events - ipc.on('vscode:enterHighContrast', () => { + ipc.on('vscode:enterHighContrast', async () => { const windowConfig = this.configurationService.getValue('window'); if (windowConfig && windowConfig.autoDetectHighContrast) { - this.lifecycleService.when(LifecyclePhase.Ready).then(() => { - this.themeService.setColorTheme(VS_HC_THEME, undefined); - }); + await this.lifecycleService.when(LifecyclePhase.Ready); + this.themeService.setColorTheme(VS_HC_THEME, undefined); } }); - ipc.on('vscode:leaveHighContrast', () => { + ipc.on('vscode:leaveHighContrast', async () => { const windowConfig = this.configurationService.getValue('window'); if (windowConfig && windowConfig.autoDetectHighContrast) { - this.lifecycleService.when(LifecyclePhase.Ready).then(() => { - this.themeService.restoreColorTheme(); - }); + await this.lifecycleService.when(LifecyclePhase.Ready); + this.themeService.restoreColorTheme(); } }); @@ -310,32 +308,28 @@ export class ElectronWindow extends Disposable { }; // Emit event when vscode is ready - this.lifecycleService.when(LifecyclePhase.Ready).then(() => { - ipc.send('vscode:workbenchReady', this.windowService.windowId); - }); + this.lifecycleService.when(LifecyclePhase.Ready).then(() => ipc.send('vscode:workbenchReady', this.windowService.windowId)); // Integrity warning this.integrityService.isPure().then(res => this.titleService.updateProperties({ isPure: res.isPure })); // Root warning - this.lifecycleService.when(LifecyclePhase.Restored).then(() => { - let isAdminPromise: Promise; + this.lifecycleService.when(LifecyclePhase.Restored).then(async () => { + let isAdmin: boolean; if (isWindows) { - isAdminPromise = import('native-is-elevated').then(isElevated => isElevated()); + const isElevated = await import('native-is-elevated'); + isAdmin = isElevated(); } else { - isAdminPromise = Promise.resolve(isRootUser()); + isAdmin = isRootUser(); } - return isAdminPromise.then(isAdmin => { + // Update title + this.titleService.updateProperties({ isAdmin }); - // Update title - this.titleService.updateProperties({ isAdmin }); - - // Show warning message (unix only) - if (isAdmin && !isWindows) { - this.notificationService.warn(nls.localize('runningAsRoot', "It is not recommended to run {0} as root user.", product.nameShort)); - } - }); + // Show warning message (unix only) + if (isAdmin && !isWindows) { + this.notificationService.warn(nls.localize('runningAsRoot', "It is not recommended to run {0} as root user.", product.nameShort)); + } }); // Touchbar menu (if enabled) @@ -408,7 +402,7 @@ export class ElectronWindow extends Disposable { } } - private setupCrashReporter(): void { + private async setupCrashReporter(): Promise { // base options with product info const options = { @@ -422,18 +416,14 @@ export class ElectronWindow extends Disposable { }; // mixin telemetry info - this.telemetryService.getTelemetryInfo() - .then(info => { - assign(options.extra, { - vscode_sessionId: info.sessionId - }); + const info = await this.telemetryService.getTelemetryInfo(); + assign(options.extra, { vscode_sessionId: info.sessionId }); - // start crash reporter right here - crashReporter.start(deepClone(options)); + // start crash reporter right here + crashReporter.start(deepClone(options)); - // start crash reporter in the main process - return this.windowsService.startCrashReporter(options); - }); + // start crash reporter in the main process + return this.windowsService.startCrashReporter(options); } private onAddFoldersRequest(request: IAddFoldersRequest): void { @@ -525,22 +515,21 @@ export class ElectronWindow extends Disposable { }); } - private openResources(resources: Array, diffMode: boolean): void { - this.lifecycleService.when(LifecyclePhase.Ready).then((): Promise => { + private async openResources(resources: Array, diffMode: boolean): Promise { + await this.lifecycleService.when(LifecyclePhase.Ready); - // In diffMode we open 2 resources as diff - if (diffMode && resources.length === 2) { - return this.editorService.openEditor({ leftResource: resources[0].resource!, rightResource: resources[1].resource!, options: { pinned: true } }); - } + // In diffMode we open 2 resources as diff + if (diffMode && resources.length === 2) { + return this.editorService.openEditor({ leftResource: resources[0].resource!, rightResource: resources[1].resource!, options: { pinned: true } }); + } - // For one file, just put it into the current active editor - if (resources.length === 1) { - return this.editorService.openEditor(resources[0]); - } + // For one file, just put it into the current active editor + if (resources.length === 1) { + return this.editorService.openEditor(resources[0]); + } - // Otherwise open all - return this.editorService.openEditors(resources); - }); + // Otherwise open all + return this.editorService.openEditors(resources); } dispose(): void { -- GitLab