Path: blob/main/src/vs/workbench/common/contextkeys.ts
3291 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 { DisposableStore } from '../../base/common/lifecycle.js';6import { URI } from '../../base/common/uri.js';7import { localize } from '../../nls.js';8import { IContextKeyService, IContextKey, RawContextKey } from '../../platform/contextkey/common/contextkey.js';9import { basename, dirname, extname, isEqual } from '../../base/common/resources.js';10import { ILanguageService } from '../../editor/common/languages/language.js';11import { IFileService } from '../../platform/files/common/files.js';12import { IModelService } from '../../editor/common/services/model.js';13import { Schemas } from '../../base/common/network.js';14import { EditorInput } from './editor/editorInput.js';15import { IEditorResolverService } from '../services/editor/common/editorResolverService.js';16import { DEFAULT_EDITOR_ASSOCIATION } from './editor.js';17import { DiffEditorInput } from './editor/diffEditorInput.js';1819//#region < --- Workbench --- >2021export const WorkbenchStateContext = new RawContextKey<string>('workbenchState', undefined, { type: 'string', description: localize('workbenchState', "The kind of workspace opened in the window, either 'empty' (no workspace), 'folder' (single folder) or 'workspace' (multi-root workspace)") });22export const WorkspaceFolderCountContext = new RawContextKey<number>('workspaceFolderCount', 0, localize('workspaceFolderCount', "The number of root folders in the workspace"));2324export const OpenFolderWorkspaceSupportContext = new RawContextKey<boolean>('openFolderWorkspaceSupport', true, true);25export const EnterMultiRootWorkspaceSupportContext = new RawContextKey<boolean>('enterMultiRootWorkspaceSupport', true, true);26export const EmptyWorkspaceSupportContext = new RawContextKey<boolean>('emptyWorkspaceSupport', true, true);2728export const DirtyWorkingCopiesContext = new RawContextKey<boolean>('dirtyWorkingCopies', false, localize('dirtyWorkingCopies', "Whether there are any working copies with unsaved changes"));2930export const RemoteNameContext = new RawContextKey<string>('remoteName', '', localize('remoteName', "The name of the remote the window is connected to or an empty string if not connected to any remote"));3132export const VirtualWorkspaceContext = new RawContextKey<string>('virtualWorkspace', '', localize('virtualWorkspace', "The scheme of the current workspace is from a virtual file system or an empty string."));33export const TemporaryWorkspaceContext = new RawContextKey<boolean>('temporaryWorkspace', false, localize('temporaryWorkspace', "The scheme of the current workspace is from a temporary file system."));3435export const HasWebFileSystemAccess = new RawContextKey<boolean>('hasWebFileSystemAccess', false, true); // Support for FileSystemAccess web APIs (https://wicg.github.io/file-system-access)3637export const EmbedderIdentifierContext = new RawContextKey<string | undefined>('embedderIdentifier', undefined, localize('embedderIdentifier', 'The identifier of the embedder according to the product service, if one is defined'));3839export const InAutomationContext = new RawContextKey<boolean>('inAutomation', false, localize('inAutomation', "Whether VS Code is running under automation/smoke test"));4041//#endregion4243//#region < --- Window --- >4445export const IsMainWindowFullscreenContext = new RawContextKey<boolean>('isFullscreen', false, localize('isFullscreen', "Whether the main window is in fullscreen mode"));46export const IsAuxiliaryWindowFocusedContext = new RawContextKey<boolean>('isAuxiliaryWindowFocusedContext', false, localize('isAuxiliaryWindowFocusedContext', "Whether an auxiliary window is focused"));4748export const IsWindowAlwaysOnTopContext = new RawContextKey<boolean>('isWindowAlwaysOnTop', false, localize('isWindowAlwaysOnTop', "Whether the window is always on top"));4950export const IsAuxiliaryWindowContext = new RawContextKey<boolean>('isAuxiliaryWindow', false, localize('isAuxiliaryWindow', "Window is an auxiliary window"));515253//#endregion545556//#region < --- Editor --- >5758// Editor State Context Keys59export const ActiveEditorDirtyContext = new RawContextKey<boolean>('activeEditorIsDirty', false, localize('activeEditorIsDirty', "Whether the active editor has unsaved changes"));60export const ActiveEditorPinnedContext = new RawContextKey<boolean>('activeEditorIsNotPreview', false, localize('activeEditorIsNotPreview', "Whether the active editor is not in preview mode"));61export const ActiveEditorFirstInGroupContext = new RawContextKey<boolean>('activeEditorIsFirstInGroup', false, localize('activeEditorIsFirstInGroup', "Whether the active editor is the first one in its group"));62export const ActiveEditorLastInGroupContext = new RawContextKey<boolean>('activeEditorIsLastInGroup', false, localize('activeEditorIsLastInGroup', "Whether the active editor is the last one in its group"));63export const ActiveEditorStickyContext = new RawContextKey<boolean>('activeEditorIsPinned', false, localize('activeEditorIsPinned', "Whether the active editor is pinned"));64export const ActiveEditorReadonlyContext = new RawContextKey<boolean>('activeEditorIsReadonly', false, localize('activeEditorIsReadonly', "Whether the active editor is read-only"));65export const ActiveCompareEditorCanSwapContext = new RawContextKey<boolean>('activeCompareEditorCanSwap', false, localize('activeCompareEditorCanSwap', "Whether the active compare editor can swap sides"));66export const ActiveEditorCanToggleReadonlyContext = new RawContextKey<boolean>('activeEditorCanToggleReadonly', true, localize('activeEditorCanToggleReadonly', "Whether the active editor can toggle between being read-only or writeable"));67export const ActiveEditorCanRevertContext = new RawContextKey<boolean>('activeEditorCanRevert', false, localize('activeEditorCanRevert', "Whether the active editor can revert"));68export const ActiveEditorCanSplitInGroupContext = new RawContextKey<boolean>('activeEditorCanSplitInGroup', true);6970// Editor Kind Context Keys71export const ActiveEditorContext = new RawContextKey<string | null>('activeEditor', null, { type: 'string', description: localize('activeEditor', "The identifier of the active editor") });72export const ActiveEditorAvailableEditorIdsContext = new RawContextKey<string>('activeEditorAvailableEditorIds', '', localize('activeEditorAvailableEditorIds', "The available editor identifiers that are usable for the active editor"));73export const TextCompareEditorVisibleContext = new RawContextKey<boolean>('textCompareEditorVisible', false, localize('textCompareEditorVisible', "Whether a text compare editor is visible"));74export const TextCompareEditorActiveContext = new RawContextKey<boolean>('textCompareEditorActive', false, localize('textCompareEditorActive', "Whether a text compare editor is active"));75export const SideBySideEditorActiveContext = new RawContextKey<boolean>('sideBySideEditorActive', false, localize('sideBySideEditorActive', "Whether a side by side editor is active"));7677// Editor Group Context Keys78export const EditorGroupEditorsCountContext = new RawContextKey<number>('groupEditorsCount', 0, localize('groupEditorsCount', "The number of opened editor groups"));79export const ActiveEditorGroupEmptyContext = new RawContextKey<boolean>('activeEditorGroupEmpty', false, localize('activeEditorGroupEmpty', "Whether the active editor group is empty"));80export const ActiveEditorGroupIndexContext = new RawContextKey<number>('activeEditorGroupIndex', 0, localize('activeEditorGroupIndex', "The index of the active editor group"));81export const ActiveEditorGroupLastContext = new RawContextKey<boolean>('activeEditorGroupLast', false, localize('activeEditorGroupLast', "Whether the active editor group is the last group"));82export const ActiveEditorGroupLockedContext = new RawContextKey<boolean>('activeEditorGroupLocked', false, localize('activeEditorGroupLocked', "Whether the active editor group is locked"));83export const MultipleEditorGroupsContext = new RawContextKey<boolean>('multipleEditorGroups', false, localize('multipleEditorGroups', "Whether there are multiple editor groups opened"));84export const SingleEditorGroupsContext = MultipleEditorGroupsContext.toNegated();85export const MultipleEditorsSelectedInGroupContext = new RawContextKey<boolean>('multipleEditorsSelectedInGroup', false, localize('multipleEditorsSelectedInGroup', "Whether multiple editors have been selected in an editor group"));86export const TwoEditorsSelectedInGroupContext = new RawContextKey<boolean>('twoEditorsSelectedInGroup', false, localize('twoEditorsSelectedInGroup', "Whether exactly two editors have been selected in an editor group"));87export const SelectedEditorsInGroupFileOrUntitledResourceContextKey = new RawContextKey<boolean>('SelectedEditorsInGroupFileOrUntitledResourceContextKey', true, localize('SelectedEditorsInGroupFileOrUntitledResourceContextKey', "Whether all selected editors in a group have a file or untitled resource associated"));8889// Editor Part Context Keys90export const EditorPartMultipleEditorGroupsContext = new RawContextKey<boolean>('editorPartMultipleEditorGroups', false, localize('editorPartMultipleEditorGroups', "Whether there are multiple editor groups opened in an editor part"));91export const EditorPartSingleEditorGroupsContext = EditorPartMultipleEditorGroupsContext.toNegated();92export const EditorPartMaximizedEditorGroupContext = new RawContextKey<boolean>('editorPartMaximizedEditorGroup', false, localize('editorPartEditorGroupMaximized', "Editor Part has a maximized group"));9394// Editor Layout Context Keys95export const EditorsVisibleContext = new RawContextKey<boolean>('editorIsOpen', false, localize('editorIsOpen', "Whether an editor is open"));96export const InEditorZenModeContext = new RawContextKey<boolean>('inZenMode', false, localize('inZenMode', "Whether Zen mode is enabled"));97export const IsMainEditorCenteredLayoutContext = new RawContextKey<boolean>('isCenteredLayout', false, localize('isMainEditorCenteredLayout', "Whether centered layout is enabled for the main editor"));98export const SplitEditorsVertically = new RawContextKey<boolean>('splitEditorsVertically', false, localize('splitEditorsVertically', "Whether editors split vertically"));99export const MainEditorAreaVisibleContext = new RawContextKey<boolean>('mainEditorAreaVisible', true, localize('mainEditorAreaVisible', "Whether the editor area in the main window is visible"));100export const EditorTabsVisibleContext = new RawContextKey<boolean>('editorTabsVisible', true, localize('editorTabsVisible', "Whether editor tabs are visible"));101102//#endregion103104105//#region < --- Side Bar --- >106107export const SideBarVisibleContext = new RawContextKey<boolean>('sideBarVisible', false, localize('sideBarVisible', "Whether the sidebar is visible"));108export const SidebarFocusContext = new RawContextKey<boolean>('sideBarFocus', false, localize('sideBarFocus', "Whether the sidebar has keyboard focus"));109export const ActiveViewletContext = new RawContextKey<string>('activeViewlet', '', localize('activeViewlet', "The identifier of the active viewlet"));110111//#endregion112113114//#region < --- Status Bar --- >115116export const StatusBarFocused = new RawContextKey<boolean>('statusBarFocused', false, localize('statusBarFocused', "Whether the status bar has keyboard focus"));117118//#endregion119120//#region < --- Title Bar --- >121122export const TitleBarStyleContext = new RawContextKey<string>('titleBarStyle', 'custom', localize('titleBarStyle', "Style of the window title bar"));123export const TitleBarVisibleContext = new RawContextKey<boolean>('titleBarVisible', false, localize('titleBarVisible', "Whether the title bar is visible"));124export const IsCompactTitleBarContext = new RawContextKey<boolean>('isCompactTitleBar', false, localize('isCompactTitleBar', "Title bar is in compact mode"));125126//#endregion127128129//#region < --- Banner --- >130131export const BannerFocused = new RawContextKey<boolean>('bannerFocused', false, localize('bannerFocused', "Whether the banner has keyboard focus"));132133//#endregion134135136//#region < --- Notifications --- >137138export const NotificationFocusedContext = new RawContextKey<boolean>('notificationFocus', true, localize('notificationFocus', "Whether a notification has keyboard focus"));139export const NotificationsCenterVisibleContext = new RawContextKey<boolean>('notificationCenterVisible', false, localize('notificationCenterVisible', "Whether the notifications center is visible"));140export const NotificationsToastsVisibleContext = new RawContextKey<boolean>('notificationToastsVisible', false, localize('notificationToastsVisible', "Whether a notification toast is visible"));141142//#endregion143144145//#region < --- Auxiliary Bar --- >146147export const ActiveAuxiliaryContext = new RawContextKey<string>('activeAuxiliary', '', localize('activeAuxiliary', "The identifier of the active auxiliary panel"));148export const AuxiliaryBarFocusContext = new RawContextKey<boolean>('auxiliaryBarFocus', false, localize('auxiliaryBarFocus', "Whether the auxiliary bar has keyboard focus"));149export const AuxiliaryBarVisibleContext = new RawContextKey<boolean>('auxiliaryBarVisible', false, localize('auxiliaryBarVisible', "Whether the auxiliary bar is visible"));150export const AuxiliaryBarMaximizedContext = new RawContextKey<boolean>('auxiliaryBarMaximized', false, localize('auxiliaryBarMaximized', "Whether the auxiliary bar is maximized"));151152//#endregion153154155//#region < --- Panel --- >156157export const ActivePanelContext = new RawContextKey<string>('activePanel', '', localize('activePanel', "The identifier of the active panel"));158export const PanelFocusContext = new RawContextKey<boolean>('panelFocus', false, localize('panelFocus', "Whether the panel has keyboard focus"));159export const PanelPositionContext = new RawContextKey<string>('panelPosition', 'bottom', localize('panelPosition', "The position of the panel, always 'bottom'"));160export const PanelAlignmentContext = new RawContextKey<string>('panelAlignment', 'center', localize('panelAlignment', "The alignment of the panel, either 'center', 'left', 'right' or 'justify'"));161export const PanelVisibleContext = new RawContextKey<boolean>('panelVisible', false, localize('panelVisible', "Whether the panel is visible"));162export const PanelMaximizedContext = new RawContextKey<boolean>('panelMaximized', false, localize('panelMaximized', "Whether the panel is maximized"));163164//#endregion165166167//#region < --- Views --- >168169export const FocusedViewContext = new RawContextKey<string>('focusedView', '', localize('focusedView', "The identifier of the view that has keyboard focus"));170export function getVisbileViewContextKey(viewId: string): string { return `view.${viewId}.visible`; }171172//#endregion173174175//#region < --- Resources --- >176177export class ResourceContextKey {178179// NOTE: DO NOT CHANGE THE DEFAULT VALUE TO ANYTHING BUT180// UNDEFINED! IT IS IMPORTANT THAT DEFAULTS ARE INHERITED181// FROM THE PARENT CONTEXT AND ONLY UNDEFINED DOES THIS182183static readonly Scheme = new RawContextKey<string>('resourceScheme', undefined, { type: 'string', description: localize('resourceScheme', "The scheme of the resource") });184static readonly Filename = new RawContextKey<string>('resourceFilename', undefined, { type: 'string', description: localize('resourceFilename', "The file name of the resource") });185static readonly Dirname = new RawContextKey<string>('resourceDirname', undefined, { type: 'string', description: localize('resourceDirname', "The folder name the resource is contained in") });186static readonly Path = new RawContextKey<string>('resourcePath', undefined, { type: 'string', description: localize('resourcePath', "The full path of the resource") });187static readonly LangId = new RawContextKey<string>('resourceLangId', undefined, { type: 'string', description: localize('resourceLangId', "The language identifier of the resource") });188static readonly Resource = new RawContextKey<string>('resource', undefined, { type: 'URI', description: localize('resource', "The full value of the resource including scheme and path") });189static readonly Extension = new RawContextKey<string>('resourceExtname', undefined, { type: 'string', description: localize('resourceExtname', "The extension name of the resource") });190static readonly HasResource = new RawContextKey<boolean>('resourceSet', undefined, { type: 'boolean', description: localize('resourceSet', "Whether a resource is present or not") });191static readonly IsFileSystemResource = new RawContextKey<boolean>('isFileSystemResource', undefined, { type: 'boolean', description: localize('isFileSystemResource', "Whether the resource is backed by a file system provider") });192193private readonly _disposables = new DisposableStore();194195private _value: URI | undefined;196private readonly _resourceKey: IContextKey<string | null>;197private readonly _schemeKey: IContextKey<string | null>;198private readonly _filenameKey: IContextKey<string | null>;199private readonly _dirnameKey: IContextKey<string | null>;200private readonly _pathKey: IContextKey<string | null>;201private readonly _langIdKey: IContextKey<string | null>;202private readonly _extensionKey: IContextKey<string | null>;203private readonly _hasResource: IContextKey<boolean>;204private readonly _isFileSystemResource: IContextKey<boolean>;205206constructor(207@IContextKeyService private readonly _contextKeyService: IContextKeyService,208@IFileService private readonly _fileService: IFileService,209@ILanguageService private readonly _languageService: ILanguageService,210@IModelService private readonly _modelService: IModelService211) {212this._schemeKey = ResourceContextKey.Scheme.bindTo(this._contextKeyService);213this._filenameKey = ResourceContextKey.Filename.bindTo(this._contextKeyService);214this._dirnameKey = ResourceContextKey.Dirname.bindTo(this._contextKeyService);215this._pathKey = ResourceContextKey.Path.bindTo(this._contextKeyService);216this._langIdKey = ResourceContextKey.LangId.bindTo(this._contextKeyService);217this._resourceKey = ResourceContextKey.Resource.bindTo(this._contextKeyService);218this._extensionKey = ResourceContextKey.Extension.bindTo(this._contextKeyService);219this._hasResource = ResourceContextKey.HasResource.bindTo(this._contextKeyService);220this._isFileSystemResource = ResourceContextKey.IsFileSystemResource.bindTo(this._contextKeyService);221222this._disposables.add(_fileService.onDidChangeFileSystemProviderRegistrations(() => {223const resource = this.get();224this._isFileSystemResource.set(Boolean(resource && _fileService.hasProvider(resource)));225}));226227this._disposables.add(_modelService.onModelAdded(model => {228if (isEqual(model.uri, this.get())) {229this._setLangId();230}231}));232this._disposables.add(_modelService.onModelLanguageChanged(e => {233if (isEqual(e.model.uri, this.get())) {234this._setLangId();235}236}));237}238239dispose(): void {240this._disposables.dispose();241}242243private _setLangId(): void {244const value = this.get();245if (!value) {246this._langIdKey.set(null);247return;248}249const langId = this._modelService.getModel(value)?.getLanguageId() ?? this._languageService.guessLanguageIdByFilepathOrFirstLine(value);250this._langIdKey.set(langId);251}252253set(value: URI | null | undefined) {254value = value ?? undefined;255if (isEqual(this._value, value)) {256return;257}258this._value = value;259this._contextKeyService.bufferChangeEvents(() => {260this._resourceKey.set(value ? value.toString() : null);261this._schemeKey.set(value ? value.scheme : null);262this._filenameKey.set(value ? basename(value) : null);263this._dirnameKey.set(value ? this.uriToPath(dirname(value)) : null);264this._pathKey.set(value ? this.uriToPath(value) : null);265this._setLangId();266this._extensionKey.set(value ? extname(value) : null);267this._hasResource.set(Boolean(value));268this._isFileSystemResource.set(value ? this._fileService.hasProvider(value) : false);269});270}271272private uriToPath(uri: URI): string {273if (uri.scheme === Schemas.file) {274return uri.fsPath;275}276277return uri.path;278}279280reset(): void {281this._value = undefined;282this._contextKeyService.bufferChangeEvents(() => {283this._resourceKey.reset();284this._schemeKey.reset();285this._filenameKey.reset();286this._dirnameKey.reset();287this._pathKey.reset();288this._langIdKey.reset();289this._extensionKey.reset();290this._hasResource.reset();291this._isFileSystemResource.reset();292});293}294295get(): URI | undefined {296return this._value;297}298}299300//#endregion301302export function applyAvailableEditorIds(contextKey: IContextKey<string>, editor: EditorInput | undefined | null, editorResolverService: IEditorResolverService): void {303if (!editor) {304contextKey.set('');305return;306}307308const editors = getAvailableEditorIds(editor, editorResolverService);309contextKey.set(editors.join(','));310}311312function getAvailableEditorIds(editor: EditorInput, editorResolverService: IEditorResolverService): string[] {313// Non text editor untitled files cannot be easily serialized between314// extensions so instead we disable this context key to prevent common315// commands that act on the active editor.316if (editor.resource?.scheme === Schemas.untitled && editor.editorId !== DEFAULT_EDITOR_ASSOCIATION.id) {317return [];318}319320// Diff editors. The original and modified resources of a diff editor321// *should* be the same, but calculate the set intersection just to be safe.322if (editor instanceof DiffEditorInput) {323const original = getAvailableEditorIds(editor.original, editorResolverService);324const modified = new Set(getAvailableEditorIds(editor.modified, editorResolverService));325return original.filter(editor => modified.has(editor));326}327328// Normal editors.329if (editor.resource) {330return editorResolverService.getEditors(editor.resource).map(editor => editor.id);331}332333return [];334}335336337