From 0edaded92a01525d5f5c9ecf95ae5bbce40b35fa Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 1 Sep 2022 23:46:53 -0700 Subject: [PATCH] debt - use new `EditorContent` menu for "Open Workspace" (#158741) (#159844) * debt - use new `EditorContent` menu for "Open Workspace" (#158741) * Update src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts Co-authored-by: John Murray Co-authored-by: John Murray --- src/vs/platform/workspace/common/workspace.ts | 2 +- src/vs/workbench/browser/codeeditor.ts | 101 +----------------- src/vs/workbench/browser/contextkeys.ts | 13 ++- .../parts/editor/editor.contribution.ts | 3 +- src/vs/workbench/common/contextkeys.ts | 4 +- .../browser/workspaces.contribution.ts | 46 +++++++- 6 files changed, 58 insertions(+), 111 deletions(-) diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 555c522c741b4..f6c876aee1ce1 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -406,7 +406,7 @@ export function toWorkspaceFolder(resource: URI): WorkspaceFolder { } export const WORKSPACE_EXTENSION = 'code-workspace'; -const WORKSPACE_SUFFIX = `.${WORKSPACE_EXTENSION}`; +export const WORKSPACE_SUFFIX = `.${WORKSPACE_EXTENSION}`; export const WORKSPACE_FILTER = [{ name: localize('codeWorkspace', "Code Workspace"), extensions: [WORKSPACE_EXTENSION] }]; export const UNTITLED_WORKSPACE_NAME = 'workspace.json'; diff --git a/src/vs/workbench/browser/codeeditor.ts b/src/vs/workbench/browser/codeeditor.ts index 3c4353efa9117..00be0866e3e5e 100644 --- a/src/vs/workbench/browser/codeeditor.ts +++ b/src/vs/workbench/browser/codeeditor.ts @@ -12,13 +12,9 @@ import { $, append, clearNode } from 'vs/base/browser/dom'; import { attachStylerCallback } from 'vs/platform/theme/common/styler'; import { buttonBackground, buttonForeground, editorBackground, editorForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { hasWorkspaceFileExtension, isTemporaryWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; -import { localize } from 'vs/nls'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { isEqual } from 'vs/base/common/resources'; -import { IFileService } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IRange } from 'vs/editor/common/core/range'; @@ -253,98 +249,3 @@ export class FloatingClickMenu extends Disposable implements IEditorContribution renderMenuAsFloatingClickBtn(); } } - -export class OpenWorkspaceButtonContribution extends Disposable implements IEditorContribution { - - static get(editor: ICodeEditor): OpenWorkspaceButtonContribution | null { - return editor.getContribution(OpenWorkspaceButtonContribution.ID); - } - - public static readonly ID = 'editor.contrib.openWorkspaceButton'; - - private openWorkspaceButton: FloatingClickWidget | undefined; - - constructor( - private editor: ICodeEditor, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IHostService private readonly hostService: IHostService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IFileService private readonly fileService: IFileService - ) { - super(); - - this.update(); - this.registerListeners(); - } - - private registerListeners(): void { - this._register(this.editor.onDidChangeModel(e => this.update())); - } - - private update(): void { - if (!this.shouldShowButton(this.editor)) { - this.disposeOpenWorkspaceWidgetRenderer(); - return; - } - - this.createOpenWorkspaceWidgetRenderer(); - } - - private shouldShowButton(editor: ICodeEditor): boolean { - const model = editor.getModel(); - if (!model) { - return false; // we need a model - } - - if (!hasWorkspaceFileExtension(model.uri)) { - return false; // we need a workspace file - } - - if (!this.fileService.hasProvider(model.uri)) { - return false; // needs to be backed by a file service - } - - if (isTemporaryWorkspace(this.contextService.getWorkspace())) { - return false; // unsupported in temporary workspaces - } - - if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { - const workspaceConfiguration = this.contextService.getWorkspace().configuration; - if (workspaceConfiguration && isEqual(workspaceConfiguration, model.uri)) { - return false; // already inside workspace - } - } - - if (editor.getOption(EditorOption.inDiffEditor)) { - // in diff editor - return false; - } - - return true; - } - - private createOpenWorkspaceWidgetRenderer(): void { - if (!this.openWorkspaceButton) { - this.openWorkspaceButton = this.instantiationService.createInstance(FloatingClickWidget, this.editor, localize('openWorkspace', "Open Workspace"), null); - this._register(this.openWorkspaceButton.onClick(() => { - const model = this.editor.getModel(); - if (model) { - this.hostService.openWindow([{ workspaceUri: model.uri }]); - } - })); - - this.openWorkspaceButton.render(); - } - } - - private disposeOpenWorkspaceWidgetRenderer(): void { - dispose(this.openWorkspaceButton); - this.openWorkspaceButton = undefined; - } - - override dispose(): void { - this.disposeOpenWorkspaceWidgetRenderer(); - - super.dispose(); - } -} diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 3d287e577818c..2ac8feeba1f87 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -7,14 +7,14 @@ import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext } from 'vs/platform/contextkey/common/contextkeys'; -import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext } from 'vs/workbench/common/contextkeys'; +import { SplitEditorsVertically, InEditorZenModeContext, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, EditorTabsVisibleContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext } from 'vs/workbench/common/contextkeys'; import { TEXT_DIFF_EDITOR_ID, EditorInputCapabilities, SIDE_BY_SIDE_EDITOR_ID, DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { WorkbenchState, IWorkspaceContextService, isTemporaryWorkspace } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchLayoutService, Parts, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; import { getVirtualWorkspaceScheme } from 'vs/platform/workspace/common/virtualWorkspace'; @@ -59,6 +59,7 @@ export class WorkbenchContextKeysHandler extends Disposable { private emptyWorkspaceSupportContext: IContextKey; private virtualWorkspaceContext: IContextKey; + private temporaryWorkspaceContext: IContextKey; private inZenModeContext: IContextKey; private isFullscreenContext: IContextKey; @@ -99,7 +100,8 @@ export class WorkbenchContextKeysHandler extends Disposable { RemoteNameContext.bindTo(this.contextKeyService).set(getRemoteName(this.environmentService.remoteAuthority) || ''); this.virtualWorkspaceContext = VirtualWorkspaceContext.bindTo(this.contextKeyService); - this.updateVirtualWorkspaceContextKey(); + this.temporaryWorkspaceContext = TemporaryWorkspaceContext.bindTo(this.contextKeyService); + this.updateWorkspaceContextKeys(); // Capabilities HasWebFileSystemAccess.bindTo(this.contextKeyService).set(WebFileSystemAccess.supported(window)); @@ -225,7 +227,7 @@ export class WorkbenchContextKeysHandler extends Disposable { this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateWorkbenchStateContextKey())); this._register(this.contextService.onDidChangeWorkspaceFolders(() => { this.updateWorkspaceFolderCountContextKey(); - this.updateVirtualWorkspaceContextKey(); + this.updateWorkspaceContextKeys(); })); this._register(this.configurationService.onDidChangeConfiguration(e => { @@ -363,7 +365,8 @@ export class WorkbenchContextKeysHandler extends Disposable { this.sideBarVisibleContext.set(this.layoutService.isVisible(Parts.SIDEBAR_PART)); } - private updateVirtualWorkspaceContextKey(): void { + private updateWorkspaceContextKeys(): void { this.virtualWorkspaceContext.set(getVirtualWorkspaceScheme(this.contextService.getWorkspace()) || ''); + this.temporaryWorkspaceContext.set(isTemporaryWorkspace(this.contextService.getWorkspace())); } } diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 01f9373d5ffd1..8651d5bfbe279 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -54,7 +54,7 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { isMacintosh } from 'vs/base/common/platform'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { FloatingClickMenu, OpenWorkspaceButtonContribution } from 'vs/workbench/browser/codeeditor'; +import { FloatingClickMenu } from 'vs/workbench/browser/codeeditor'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { EditorAutoSave } from 'vs/workbench/browser/parts/editor/editorAutoSave'; @@ -128,7 +128,6 @@ Registry.as(WorkbenchExtensions.Workbench).regi Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DynamicEditorConfigurations, 'DynamicEditorConfigurations', LifecyclePhase.Ready); registerEditorContribution(FloatingClickMenu.ID, FloatingClickMenu); -registerEditorContribution(OpenWorkspaceButtonContribution.ID, OpenWorkspaceButtonContribution); //#endregion diff --git a/src/vs/workbench/common/contextkeys.ts b/src/vs/workbench/common/contextkeys.ts index 907cfa0dda22d..7739005ed0aad 100644 --- a/src/vs/workbench/common/contextkeys.ts +++ b/src/vs/workbench/common/contextkeys.ts @@ -24,7 +24,9 @@ export const EmptyWorkspaceSupportContext = new RawContextKey('emptyWor export const DirtyWorkingCopiesContext = new RawContextKey('dirtyWorkingCopies', false, localize('dirtyWorkingCopies', "Whether there are any working copies with unsaved changes")); export const RemoteNameContext = new RawContextKey('remoteName', '', localize('remoteName', "The name of the remote the window is connected to or an empty string if not connected to any remote")); -export const VirtualWorkspaceContext = new RawContextKey('virtualWorkspace', '', localize('virtualWorkspace', "The scheme of the current workspace if is from a virtual file system or an empty string.")); + +export const VirtualWorkspaceContext = new RawContextKey('virtualWorkspace', '', localize('virtualWorkspace', "The scheme of the current workspace is from a virtual file system or an empty string.")); +export const TemporaryWorkspaceContext = new RawContextKey('temporaryWorkspace', false, localize('temporaryWorkspace', "The scheme of the current workspace is from a temporary file system.")); export const IsFullscreenContext = new RawContextKey('isFullscreen', false, localize('isFullscreen', "Whether the window is in fullscreen mode")); diff --git a/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts b/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts index df64ab61f3427..c274839d5bd13 100644 --- a/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts +++ b/src/vs/workbench/contrib/workspaces/browser/workspaces.contribution.ts @@ -7,16 +7,21 @@ import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { hasWorkspaceFileExtension, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { hasWorkspaceFileExtension, IWorkspaceContextService, WorkbenchState, WORKSPACE_SUFFIX } from 'vs/platform/workspace/common/workspace'; import { Disposable } from 'vs/base/common/lifecycle'; import { IFileService } from 'vs/platform/files/common/files'; import { INeverShowAgainOptions, INotificationService, NeverShowAgainScope, Severity } from 'vs/platform/notification/common/notification'; import { URI } from 'vs/base/common/uri'; -import { joinPath } from 'vs/base/common/resources'; +import { isEqual, joinPath } from 'vs/base/common/resources'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { isVirtualWorkspace } from 'vs/platform/workspace/common/virtualWorkspace'; +import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { ActiveEditorContext, ResourceContextKey, TemporaryWorkspaceContext } from 'vs/workbench/common/contextkeys'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { TEXT_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; /** * A workbench contribution that will look for `.code-workspace` files in the root of the @@ -90,3 +95,40 @@ export class WorkspacesFinderContribution extends Disposable implements IWorkben } Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkspacesFinderContribution, 'WorkspacesFinderContribution', LifecyclePhase.Eventually); + +// Render "Open Workspace" button in *.code-workspace files + +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'workbench.action.openWorkspaceFromEditor', + title: { original: 'Open Workspace', value: localize('openWorkspace', "Open Workspace") }, + f1: false, + menu: { + id: MenuId.EditorContent, + when: ContextKeyExpr.and( + ResourceContextKey.Extension.isEqualTo(WORKSPACE_SUFFIX), + ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID), + TemporaryWorkspaceContext.toNegated() + ) + } + }); + } + + async run(accessor: ServicesAccessor, uri: URI): Promise { + const hostService = accessor.get(IHostService); + const contextService = accessor.get(IWorkspaceContextService); + const notificationService = accessor.get(INotificationService); + + if (contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { + const workspaceConfiguration = contextService.getWorkspace().configuration; + if (workspaceConfiguration && isEqual(workspaceConfiguration, uri)) { + notificationService.info(localize('alreadyOpen', "This workspace is already open.")); + + return; // workspace already opened + } + } + + return hostService.openWindow([{ workspaceUri: uri }]); + } +});