Path: blob/main/src/vs/workbench/browser/contextkeys.ts
3294 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Event } from '../../base/common/event.js';6import { Disposable, DisposableStore, MutableDisposable } from '../../base/common/lifecycle.js';7import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from '../../platform/contextkey/common/contextkey.js';8import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext, IsSimulationContext } from '../../platform/contextkey/common/contextkeys.js';9import { SplitEditorsVertically, InEditorZenModeContext, AuxiliaryBarVisibleContext, SideBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelVisibleContext, EmbedderIdentifierContext, EditorTabsVisibleContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, DirtyWorkingCopiesContext, EmptyWorkspaceSupportContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, IsMainWindowFullscreenContext, OpenFolderWorkspaceSupportContext, RemoteNameContext, VirtualWorkspaceContext, WorkbenchStateContext, WorkspaceFolderCountContext, PanelPositionContext, TemporaryWorkspaceContext, TitleBarVisibleContext, TitleBarStyleContext, IsAuxiliaryWindowFocusedContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorGroupLockedContext, MultipleEditorGroupsContext, EditorsVisibleContext, AuxiliaryBarMaximizedContext, InAutomationContext } from '../common/contextkeys.js';10import { trackFocus, addDisposableListener, EventType, onDidRegisterWindow, getActiveWindow, isEditableElement } from '../../base/browser/dom.js';11import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from '../services/editor/common/editorGroupsService.js';12import { IConfigurationService } from '../../platform/configuration/common/configuration.js';13import { IWorkbenchEnvironmentService } from '../services/environment/common/environmentService.js';14import { WorkbenchState, IWorkspaceContextService, isTemporaryWorkspace } from '../../platform/workspace/common/workspace.js';15import { IWorkbenchLayoutService, Parts, positionToString } from '../services/layout/browser/layoutService.js';16import { getRemoteName } from '../../platform/remote/common/remoteHosts.js';17import { getVirtualWorkspaceScheme } from '../../platform/workspace/common/virtualWorkspace.js';18import { IWorkingCopyService } from '../services/workingCopy/common/workingCopyService.js';19import { isNative } from '../../base/common/platform.js';20import { IPaneCompositePartService } from '../services/panecomposite/browser/panecomposite.js';21import { WebFileSystemAccess } from '../../platform/files/browser/webFileSystemAccess.js';22import { IProductService } from '../../platform/product/common/productService.js';23import { getTitleBarStyle } from '../../platform/window/common/window.js';24import { mainWindow } from '../../base/browser/window.js';25import { isFullscreen, onDidChangeFullscreen } from '../../base/browser/browser.js';26import { IEditorService } from '../services/editor/common/editorService.js';2728export class WorkbenchContextKeysHandler extends Disposable {29private inputFocusedContext: IContextKey<boolean>;3031private dirtyWorkingCopiesContext: IContextKey<boolean>;3233private activeEditorGroupEmpty: IContextKey<boolean>;34private activeEditorGroupIndex: IContextKey<number>;35private activeEditorGroupLast: IContextKey<boolean>;36private activeEditorGroupLocked: IContextKey<boolean>;37private multipleEditorGroupsContext: IContextKey<boolean>;3839private editorsVisibleContext: IContextKey<boolean>;4041private splitEditorsVerticallyContext: IContextKey<boolean>;4243private workbenchStateContext: IContextKey<string>;44private workspaceFolderCountContext: IContextKey<number>;4546private openFolderWorkspaceSupportContext: IContextKey<boolean>;47private enterMultiRootWorkspaceSupportContext: IContextKey<boolean>;48private emptyWorkspaceSupportContext: IContextKey<boolean>;4950private virtualWorkspaceContext: IContextKey<string>;51private temporaryWorkspaceContext: IContextKey<boolean>;52private inAutomationContext: IContextKey<boolean>;5354private inZenModeContext: IContextKey<boolean>;55private isMainWindowFullscreenContext: IContextKey<boolean>;56private isAuxiliaryWindowFocusedContext: IContextKey<boolean>;57private isMainEditorCenteredLayoutContext: IContextKey<boolean>;58private sideBarVisibleContext: IContextKey<boolean>;59private mainEditorAreaVisibleContext: IContextKey<boolean>;60private panelPositionContext: IContextKey<string>;61private panelVisibleContext: IContextKey<boolean>;62private panelAlignmentContext: IContextKey<string>;63private panelMaximizedContext: IContextKey<boolean>;64private auxiliaryBarVisibleContext: IContextKey<boolean>;65private auxiliaryBarMaximizedContext: IContextKey<boolean>;66private editorTabsVisibleContext: IContextKey<boolean>;67private titleAreaVisibleContext: IContextKey<boolean>;68private titleBarStyleContext: IContextKey<string>;6970constructor(71@IContextKeyService private readonly contextKeyService: IContextKeyService,72@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,73@IConfigurationService private readonly configurationService: IConfigurationService,74@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,75@IProductService private readonly productService: IProductService,76@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,77@IEditorService private readonly editorService: IEditorService,78@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,79@IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService,80@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService,81) {82super();8384// Platform85IsMacContext.bindTo(this.contextKeyService);86IsLinuxContext.bindTo(this.contextKeyService);87IsWindowsContext.bindTo(this.contextKeyService);8889IsWebContext.bindTo(this.contextKeyService);90IsMacNativeContext.bindTo(this.contextKeyService);91IsIOSContext.bindTo(this.contextKeyService);92IsMobileContext.bindTo(this.contextKeyService);9394RemoteNameContext.bindTo(this.contextKeyService).set(getRemoteName(this.environmentService.remoteAuthority) || '');9596this.virtualWorkspaceContext = VirtualWorkspaceContext.bindTo(this.contextKeyService);97this.temporaryWorkspaceContext = TemporaryWorkspaceContext.bindTo(this.contextKeyService);98this.updateWorkspaceContextKeys();99100// Capabilities101HasWebFileSystemAccess.bindTo(this.contextKeyService).set(WebFileSystemAccess.supported(mainWindow));102103// Development104const isDevelopment = !this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment;105IsDevelopmentContext.bindTo(this.contextKeyService).set(isDevelopment);106setConstantContextKey(IsDevelopmentContext.key, isDevelopment);107108// Simulation mode109IsSimulationContext.bindTo(this.contextKeyService).set(!!this.environmentService.isSimulation);110111// Product Service112ProductQualityContext.bindTo(this.contextKeyService).set(this.productService.quality || '');113EmbedderIdentifierContext.bindTo(this.contextKeyService).set(productService.embedderIdentifier);114115// Automation116this.inAutomationContext = InAutomationContext.bindTo(this.contextKeyService);117this.inAutomationContext.set(!!this.environmentService.enableSmokeTestDriver);118119// Editor Groups120this.activeEditorGroupEmpty = ActiveEditorGroupEmptyContext.bindTo(this.contextKeyService);121this.activeEditorGroupIndex = ActiveEditorGroupIndexContext.bindTo(this.contextKeyService);122this.activeEditorGroupLast = ActiveEditorGroupLastContext.bindTo(this.contextKeyService);123this.activeEditorGroupLocked = ActiveEditorGroupLockedContext.bindTo(this.contextKeyService);124this.multipleEditorGroupsContext = MultipleEditorGroupsContext.bindTo(this.contextKeyService);125126// Editors127this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService);128129// Working Copies130this.dirtyWorkingCopiesContext = DirtyWorkingCopiesContext.bindTo(this.contextKeyService);131this.dirtyWorkingCopiesContext.set(this.workingCopyService.hasDirty);132133// Inputs134this.inputFocusedContext = InputFocusedContext.bindTo(this.contextKeyService);135136// Workbench State137this.workbenchStateContext = WorkbenchStateContext.bindTo(this.contextKeyService);138this.updateWorkbenchStateContextKey();139140// Workspace Folder Count141this.workspaceFolderCountContext = WorkspaceFolderCountContext.bindTo(this.contextKeyService);142this.updateWorkspaceFolderCountContextKey();143144// Opening folder support: support for opening a folder workspace145// (e.g. "Open Folder...") is limited in web when not connected146// to a remote.147this.openFolderWorkspaceSupportContext = OpenFolderWorkspaceSupportContext.bindTo(this.contextKeyService);148this.openFolderWorkspaceSupportContext.set(isNative || typeof this.environmentService.remoteAuthority === 'string');149150// Empty workspace support: empty workspaces require built-in file system151// providers to be available that allow to enter a workspace or open loose152// files. This condition is met:153// - desktop: always154// - web: only when connected to a remote155this.emptyWorkspaceSupportContext = EmptyWorkspaceSupportContext.bindTo(this.contextKeyService);156this.emptyWorkspaceSupportContext.set(isNative || typeof this.environmentService.remoteAuthority === 'string');157158// Entering a multi root workspace support: support for entering a multi-root159// workspace (e.g. "Open Workspace from File...", "Duplicate Workspace", "Save Workspace")160// is driven by the ability to resolve a workspace configuration file (*.code-workspace)161// with a built-in file system provider.162// This condition is met:163// - desktop: always164// - web: only when connected to a remote165this.enterMultiRootWorkspaceSupportContext = EnterMultiRootWorkspaceSupportContext.bindTo(this.contextKeyService);166this.enterMultiRootWorkspaceSupportContext.set(isNative || typeof this.environmentService.remoteAuthority === 'string');167168// Editor Layout169this.splitEditorsVerticallyContext = SplitEditorsVertically.bindTo(this.contextKeyService);170this.updateSplitEditorsVerticallyContext();171172// Window173this.isMainWindowFullscreenContext = IsMainWindowFullscreenContext.bindTo(this.contextKeyService);174this.isAuxiliaryWindowFocusedContext = IsAuxiliaryWindowFocusedContext.bindTo(this.contextKeyService);175176// Zen Mode177this.inZenModeContext = InEditorZenModeContext.bindTo(this.contextKeyService);178179// Centered Layout (Main Editor)180this.isMainEditorCenteredLayoutContext = IsMainEditorCenteredLayoutContext.bindTo(this.contextKeyService);181182// Editor Area183this.mainEditorAreaVisibleContext = MainEditorAreaVisibleContext.bindTo(this.contextKeyService);184this.editorTabsVisibleContext = EditorTabsVisibleContext.bindTo(this.contextKeyService);185186// Sidebar187this.sideBarVisibleContext = SideBarVisibleContext.bindTo(this.contextKeyService);188189// Title Bar190this.titleAreaVisibleContext = TitleBarVisibleContext.bindTo(this.contextKeyService);191this.titleBarStyleContext = TitleBarStyleContext.bindTo(this.contextKeyService);192this.updateTitleBarContextKeys();193194// Panel195this.panelPositionContext = PanelPositionContext.bindTo(this.contextKeyService);196this.panelPositionContext.set(positionToString(this.layoutService.getPanelPosition()));197this.panelVisibleContext = PanelVisibleContext.bindTo(this.contextKeyService);198this.panelVisibleContext.set(this.layoutService.isVisible(Parts.PANEL_PART));199this.panelMaximizedContext = PanelMaximizedContext.bindTo(this.contextKeyService);200this.panelMaximizedContext.set(this.layoutService.isPanelMaximized());201this.panelAlignmentContext = PanelAlignmentContext.bindTo(this.contextKeyService);202this.panelAlignmentContext.set(this.layoutService.getPanelAlignment());203204// Auxiliary Bar205this.auxiliaryBarVisibleContext = AuxiliaryBarVisibleContext.bindTo(this.contextKeyService);206this.auxiliaryBarVisibleContext.set(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART));207this.auxiliaryBarMaximizedContext = AuxiliaryBarMaximizedContext.bindTo(this.contextKeyService);208this.auxiliaryBarMaximizedContext.set(this.layoutService.isAuxiliaryBarMaximized());209210this.registerListeners();211}212213private registerListeners(): void {214this.editorGroupService.whenReady.then(() => {215this.updateEditorAreaContextKeys();216this.updateActiveEditorGroupContextKeys();217this.updateVisiblePanesContextKeys();218});219220this._register(this.editorService.onDidActiveEditorChange(() => this.updateActiveEditorGroupContextKeys()));221this._register(this.editorService.onDidVisibleEditorsChange(() => this.updateVisiblePanesContextKeys()));222this._register(this.editorGroupService.onDidAddGroup(() => this.updateEditorGroupsContextKeys()));223this._register(this.editorGroupService.onDidRemoveGroup(() => this.updateEditorGroupsContextKeys()));224this._register(this.editorGroupService.onDidChangeGroupIndex(() => this.updateActiveEditorGroupContextKeys()));225this._register(this.editorGroupService.onDidChangeGroupLocked(() => this.updateActiveEditorGroupContextKeys()));226227this._register(this.editorGroupService.onDidChangeEditorPartOptions(() => this.updateEditorAreaContextKeys()));228229this._register(Event.runAndSubscribe(onDidRegisterWindow, ({ window, disposables }) => {230const onFocusDisposables = disposables.add(new MutableDisposable<DisposableStore>());231disposables.add(addDisposableListener(window, EventType.FOCUS_IN, () => {232onFocusDisposables.value = new DisposableStore();233this.updateInputContextKeys(window.document, onFocusDisposables.value);234}, true));235}, { window: mainWindow, disposables: this._store }));236237this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateWorkbenchStateContextKey()));238this._register(this.contextService.onDidChangeWorkspaceFolders(() => {239this.updateWorkspaceFolderCountContextKey();240this.updateWorkspaceContextKeys();241}));242243this._register(this.configurationService.onDidChangeConfiguration(e => {244if (e.affectsConfiguration('workbench.editor.openSideBySideDirection')) {245this.updateSplitEditorsVerticallyContext();246}247}));248249this._register(this.layoutService.onDidChangeZenMode(enabled => this.inZenModeContext.set(enabled)));250this._register(this.layoutService.onDidChangeActiveContainer(() => this.isAuxiliaryWindowFocusedContext.set(this.layoutService.activeContainer !== this.layoutService.mainContainer)));251this._register(onDidChangeFullscreen(windowId => {252if (windowId === mainWindow.vscodeWindowId) {253this.isMainWindowFullscreenContext.set(isFullscreen(mainWindow));254}255}));256this._register(this.layoutService.onDidChangeMainEditorCenteredLayout(centered => this.isMainEditorCenteredLayoutContext.set(centered)));257this._register(this.layoutService.onDidChangePanelPosition(position => this.panelPositionContext.set(position)));258259this._register(this.layoutService.onDidChangePanelAlignment(alignment => this.panelAlignmentContext.set(alignment)));260261this._register(this.paneCompositeService.onDidPaneCompositeClose(() => this.updateSideBarContextKeys()));262this._register(this.paneCompositeService.onDidPaneCompositeOpen(() => this.updateSideBarContextKeys()));263264this._register(this.layoutService.onDidChangePartVisibility(() => {265this.mainEditorAreaVisibleContext.set(this.layoutService.isVisible(Parts.EDITOR_PART, mainWindow));266this.panelVisibleContext.set(this.layoutService.isVisible(Parts.PANEL_PART));267this.panelMaximizedContext.set(this.layoutService.isPanelMaximized());268this.auxiliaryBarVisibleContext.set(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART));269270this.updateTitleBarContextKeys();271}));272273this._register(this.layoutService.onDidChangeAuxiliaryBarMaximized(() => {274this.auxiliaryBarMaximizedContext.set(this.layoutService.isAuxiliaryBarMaximized());275}));276277this._register(this.workingCopyService.onDidChangeDirty(workingCopy => this.dirtyWorkingCopiesContext.set(workingCopy.isDirty() || this.workingCopyService.hasDirty)));278}279280private updateVisiblePanesContextKeys(): void {281const visibleEditorPanes = this.editorService.visibleEditorPanes;282if (visibleEditorPanes.length > 0) {283this.editorsVisibleContext.set(true);284} else {285this.editorsVisibleContext.reset();286}287}288289// Context keys depending on the state of the editor group itself290private updateActiveEditorGroupContextKeys(): void {291if (!this.editorService.activeEditor) {292this.activeEditorGroupEmpty.set(true);293} else {294this.activeEditorGroupEmpty.reset();295}296297const activeGroup = this.editorGroupService.activeGroup;298this.activeEditorGroupIndex.set(activeGroup.index + 1); // not zero-indexed299this.activeEditorGroupLocked.set(activeGroup.isLocked);300301this.updateEditorGroupsContextKeys();302}303304// Context keys depending on the state of other editor groups305private updateEditorGroupsContextKeys(): void {306const groupCount = this.editorGroupService.count;307if (groupCount > 1) {308this.multipleEditorGroupsContext.set(true);309} else {310this.multipleEditorGroupsContext.reset();311}312313const activeGroup = this.editorGroupService.activeGroup;314this.activeEditorGroupLast.set(activeGroup.index === groupCount - 1);315}316317private updateEditorAreaContextKeys(): void {318this.editorTabsVisibleContext.set(this.editorGroupService.partOptions.showTabs === 'multiple');319}320321private updateInputContextKeys(ownerDocument: Document, disposables: DisposableStore): void {322323function activeElementIsInput(): boolean {324return !!ownerDocument.activeElement && isEditableElement(ownerDocument.activeElement);325}326327const isInputFocused = activeElementIsInput();328this.inputFocusedContext.set(isInputFocused);329330if (isInputFocused) {331const tracker = disposables.add(trackFocus(ownerDocument.activeElement as HTMLElement));332Event.once(tracker.onDidBlur)(() => {333334// Ensure we are only updating the context key if we are335// still in the same document that we are tracking. This336// fixes a race condition in multi-window setups where337// the blur event arrives in the inactive window overwriting338// the context key of the active window. This is because339// blur events from the focus tracker are emitted with a340// timeout of 0.341342if (getActiveWindow().document === ownerDocument) {343this.inputFocusedContext.set(activeElementIsInput());344}345346tracker.dispose();347}, undefined, disposables);348}349}350351private updateWorkbenchStateContextKey(): void {352this.workbenchStateContext.set(this.getWorkbenchStateString());353}354355private updateWorkspaceFolderCountContextKey(): void {356this.workspaceFolderCountContext.set(this.contextService.getWorkspace().folders.length);357}358359private updateSplitEditorsVerticallyContext(): void {360const direction = preferredSideBySideGroupDirection(this.configurationService);361this.splitEditorsVerticallyContext.set(direction === GroupDirection.DOWN);362}363364private getWorkbenchStateString(): string {365switch (this.contextService.getWorkbenchState()) {366case WorkbenchState.EMPTY: return 'empty';367case WorkbenchState.FOLDER: return 'folder';368case WorkbenchState.WORKSPACE: return 'workspace';369}370}371372private updateSideBarContextKeys(): void {373this.sideBarVisibleContext.set(this.layoutService.isVisible(Parts.SIDEBAR_PART));374}375376private updateTitleBarContextKeys(): void {377this.titleAreaVisibleContext.set(this.layoutService.isVisible(Parts.TITLEBAR_PART, mainWindow));378this.titleBarStyleContext.set(getTitleBarStyle(this.configurationService));379}380381private updateWorkspaceContextKeys(): void {382this.virtualWorkspaceContext.set(getVirtualWorkspaceScheme(this.contextService.getWorkspace()) || '');383this.temporaryWorkspaceContext.set(isTemporaryWorkspace(this.contextService.getWorkspace()));384}385}386387388