Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/browser/contextkeys.ts
3294 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Event } from '../../base/common/event.js';
7
import { Disposable, DisposableStore, MutableDisposable } from '../../base/common/lifecycle.js';
8
import { IContextKeyService, IContextKey, setConstant as setConstantContextKey } from '../../platform/contextkey/common/contextkey.js';
9
import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext, ProductQualityContext, IsMobileContext, IsSimulationContext } from '../../platform/contextkey/common/contextkeys.js';
10
import { 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';
11
import { trackFocus, addDisposableListener, EventType, onDidRegisterWindow, getActiveWindow, isEditableElement } from '../../base/browser/dom.js';
12
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from '../services/editor/common/editorGroupsService.js';
13
import { IConfigurationService } from '../../platform/configuration/common/configuration.js';
14
import { IWorkbenchEnvironmentService } from '../services/environment/common/environmentService.js';
15
import { WorkbenchState, IWorkspaceContextService, isTemporaryWorkspace } from '../../platform/workspace/common/workspace.js';
16
import { IWorkbenchLayoutService, Parts, positionToString } from '../services/layout/browser/layoutService.js';
17
import { getRemoteName } from '../../platform/remote/common/remoteHosts.js';
18
import { getVirtualWorkspaceScheme } from '../../platform/workspace/common/virtualWorkspace.js';
19
import { IWorkingCopyService } from '../services/workingCopy/common/workingCopyService.js';
20
import { isNative } from '../../base/common/platform.js';
21
import { IPaneCompositePartService } from '../services/panecomposite/browser/panecomposite.js';
22
import { WebFileSystemAccess } from '../../platform/files/browser/webFileSystemAccess.js';
23
import { IProductService } from '../../platform/product/common/productService.js';
24
import { getTitleBarStyle } from '../../platform/window/common/window.js';
25
import { mainWindow } from '../../base/browser/window.js';
26
import { isFullscreen, onDidChangeFullscreen } from '../../base/browser/browser.js';
27
import { IEditorService } from '../services/editor/common/editorService.js';
28
29
export class WorkbenchContextKeysHandler extends Disposable {
30
private inputFocusedContext: IContextKey<boolean>;
31
32
private dirtyWorkingCopiesContext: IContextKey<boolean>;
33
34
private activeEditorGroupEmpty: IContextKey<boolean>;
35
private activeEditorGroupIndex: IContextKey<number>;
36
private activeEditorGroupLast: IContextKey<boolean>;
37
private activeEditorGroupLocked: IContextKey<boolean>;
38
private multipleEditorGroupsContext: IContextKey<boolean>;
39
40
private editorsVisibleContext: IContextKey<boolean>;
41
42
private splitEditorsVerticallyContext: IContextKey<boolean>;
43
44
private workbenchStateContext: IContextKey<string>;
45
private workspaceFolderCountContext: IContextKey<number>;
46
47
private openFolderWorkspaceSupportContext: IContextKey<boolean>;
48
private enterMultiRootWorkspaceSupportContext: IContextKey<boolean>;
49
private emptyWorkspaceSupportContext: IContextKey<boolean>;
50
51
private virtualWorkspaceContext: IContextKey<string>;
52
private temporaryWorkspaceContext: IContextKey<boolean>;
53
private inAutomationContext: IContextKey<boolean>;
54
55
private inZenModeContext: IContextKey<boolean>;
56
private isMainWindowFullscreenContext: IContextKey<boolean>;
57
private isAuxiliaryWindowFocusedContext: IContextKey<boolean>;
58
private isMainEditorCenteredLayoutContext: IContextKey<boolean>;
59
private sideBarVisibleContext: IContextKey<boolean>;
60
private mainEditorAreaVisibleContext: IContextKey<boolean>;
61
private panelPositionContext: IContextKey<string>;
62
private panelVisibleContext: IContextKey<boolean>;
63
private panelAlignmentContext: IContextKey<string>;
64
private panelMaximizedContext: IContextKey<boolean>;
65
private auxiliaryBarVisibleContext: IContextKey<boolean>;
66
private auxiliaryBarMaximizedContext: IContextKey<boolean>;
67
private editorTabsVisibleContext: IContextKey<boolean>;
68
private titleAreaVisibleContext: IContextKey<boolean>;
69
private titleBarStyleContext: IContextKey<string>;
70
71
constructor(
72
@IContextKeyService private readonly contextKeyService: IContextKeyService,
73
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
74
@IConfigurationService private readonly configurationService: IConfigurationService,
75
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
76
@IProductService private readonly productService: IProductService,
77
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
78
@IEditorService private readonly editorService: IEditorService,
79
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
80
@IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService,
81
@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService,
82
) {
83
super();
84
85
// Platform
86
IsMacContext.bindTo(this.contextKeyService);
87
IsLinuxContext.bindTo(this.contextKeyService);
88
IsWindowsContext.bindTo(this.contextKeyService);
89
90
IsWebContext.bindTo(this.contextKeyService);
91
IsMacNativeContext.bindTo(this.contextKeyService);
92
IsIOSContext.bindTo(this.contextKeyService);
93
IsMobileContext.bindTo(this.contextKeyService);
94
95
RemoteNameContext.bindTo(this.contextKeyService).set(getRemoteName(this.environmentService.remoteAuthority) || '');
96
97
this.virtualWorkspaceContext = VirtualWorkspaceContext.bindTo(this.contextKeyService);
98
this.temporaryWorkspaceContext = TemporaryWorkspaceContext.bindTo(this.contextKeyService);
99
this.updateWorkspaceContextKeys();
100
101
// Capabilities
102
HasWebFileSystemAccess.bindTo(this.contextKeyService).set(WebFileSystemAccess.supported(mainWindow));
103
104
// Development
105
const isDevelopment = !this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment;
106
IsDevelopmentContext.bindTo(this.contextKeyService).set(isDevelopment);
107
setConstantContextKey(IsDevelopmentContext.key, isDevelopment);
108
109
// Simulation mode
110
IsSimulationContext.bindTo(this.contextKeyService).set(!!this.environmentService.isSimulation);
111
112
// Product Service
113
ProductQualityContext.bindTo(this.contextKeyService).set(this.productService.quality || '');
114
EmbedderIdentifierContext.bindTo(this.contextKeyService).set(productService.embedderIdentifier);
115
116
// Automation
117
this.inAutomationContext = InAutomationContext.bindTo(this.contextKeyService);
118
this.inAutomationContext.set(!!this.environmentService.enableSmokeTestDriver);
119
120
// Editor Groups
121
this.activeEditorGroupEmpty = ActiveEditorGroupEmptyContext.bindTo(this.contextKeyService);
122
this.activeEditorGroupIndex = ActiveEditorGroupIndexContext.bindTo(this.contextKeyService);
123
this.activeEditorGroupLast = ActiveEditorGroupLastContext.bindTo(this.contextKeyService);
124
this.activeEditorGroupLocked = ActiveEditorGroupLockedContext.bindTo(this.contextKeyService);
125
this.multipleEditorGroupsContext = MultipleEditorGroupsContext.bindTo(this.contextKeyService);
126
127
// Editors
128
this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService);
129
130
// Working Copies
131
this.dirtyWorkingCopiesContext = DirtyWorkingCopiesContext.bindTo(this.contextKeyService);
132
this.dirtyWorkingCopiesContext.set(this.workingCopyService.hasDirty);
133
134
// Inputs
135
this.inputFocusedContext = InputFocusedContext.bindTo(this.contextKeyService);
136
137
// Workbench State
138
this.workbenchStateContext = WorkbenchStateContext.bindTo(this.contextKeyService);
139
this.updateWorkbenchStateContextKey();
140
141
// Workspace Folder Count
142
this.workspaceFolderCountContext = WorkspaceFolderCountContext.bindTo(this.contextKeyService);
143
this.updateWorkspaceFolderCountContextKey();
144
145
// Opening folder support: support for opening a folder workspace
146
// (e.g. "Open Folder...") is limited in web when not connected
147
// to a remote.
148
this.openFolderWorkspaceSupportContext = OpenFolderWorkspaceSupportContext.bindTo(this.contextKeyService);
149
this.openFolderWorkspaceSupportContext.set(isNative || typeof this.environmentService.remoteAuthority === 'string');
150
151
// Empty workspace support: empty workspaces require built-in file system
152
// providers to be available that allow to enter a workspace or open loose
153
// files. This condition is met:
154
// - desktop: always
155
// - web: only when connected to a remote
156
this.emptyWorkspaceSupportContext = EmptyWorkspaceSupportContext.bindTo(this.contextKeyService);
157
this.emptyWorkspaceSupportContext.set(isNative || typeof this.environmentService.remoteAuthority === 'string');
158
159
// Entering a multi root workspace support: support for entering a multi-root
160
// workspace (e.g. "Open Workspace from File...", "Duplicate Workspace", "Save Workspace")
161
// is driven by the ability to resolve a workspace configuration file (*.code-workspace)
162
// with a built-in file system provider.
163
// This condition is met:
164
// - desktop: always
165
// - web: only when connected to a remote
166
this.enterMultiRootWorkspaceSupportContext = EnterMultiRootWorkspaceSupportContext.bindTo(this.contextKeyService);
167
this.enterMultiRootWorkspaceSupportContext.set(isNative || typeof this.environmentService.remoteAuthority === 'string');
168
169
// Editor Layout
170
this.splitEditorsVerticallyContext = SplitEditorsVertically.bindTo(this.contextKeyService);
171
this.updateSplitEditorsVerticallyContext();
172
173
// Window
174
this.isMainWindowFullscreenContext = IsMainWindowFullscreenContext.bindTo(this.contextKeyService);
175
this.isAuxiliaryWindowFocusedContext = IsAuxiliaryWindowFocusedContext.bindTo(this.contextKeyService);
176
177
// Zen Mode
178
this.inZenModeContext = InEditorZenModeContext.bindTo(this.contextKeyService);
179
180
// Centered Layout (Main Editor)
181
this.isMainEditorCenteredLayoutContext = IsMainEditorCenteredLayoutContext.bindTo(this.contextKeyService);
182
183
// Editor Area
184
this.mainEditorAreaVisibleContext = MainEditorAreaVisibleContext.bindTo(this.contextKeyService);
185
this.editorTabsVisibleContext = EditorTabsVisibleContext.bindTo(this.contextKeyService);
186
187
// Sidebar
188
this.sideBarVisibleContext = SideBarVisibleContext.bindTo(this.contextKeyService);
189
190
// Title Bar
191
this.titleAreaVisibleContext = TitleBarVisibleContext.bindTo(this.contextKeyService);
192
this.titleBarStyleContext = TitleBarStyleContext.bindTo(this.contextKeyService);
193
this.updateTitleBarContextKeys();
194
195
// Panel
196
this.panelPositionContext = PanelPositionContext.bindTo(this.contextKeyService);
197
this.panelPositionContext.set(positionToString(this.layoutService.getPanelPosition()));
198
this.panelVisibleContext = PanelVisibleContext.bindTo(this.contextKeyService);
199
this.panelVisibleContext.set(this.layoutService.isVisible(Parts.PANEL_PART));
200
this.panelMaximizedContext = PanelMaximizedContext.bindTo(this.contextKeyService);
201
this.panelMaximizedContext.set(this.layoutService.isPanelMaximized());
202
this.panelAlignmentContext = PanelAlignmentContext.bindTo(this.contextKeyService);
203
this.panelAlignmentContext.set(this.layoutService.getPanelAlignment());
204
205
// Auxiliary Bar
206
this.auxiliaryBarVisibleContext = AuxiliaryBarVisibleContext.bindTo(this.contextKeyService);
207
this.auxiliaryBarVisibleContext.set(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART));
208
this.auxiliaryBarMaximizedContext = AuxiliaryBarMaximizedContext.bindTo(this.contextKeyService);
209
this.auxiliaryBarMaximizedContext.set(this.layoutService.isAuxiliaryBarMaximized());
210
211
this.registerListeners();
212
}
213
214
private registerListeners(): void {
215
this.editorGroupService.whenReady.then(() => {
216
this.updateEditorAreaContextKeys();
217
this.updateActiveEditorGroupContextKeys();
218
this.updateVisiblePanesContextKeys();
219
});
220
221
this._register(this.editorService.onDidActiveEditorChange(() => this.updateActiveEditorGroupContextKeys()));
222
this._register(this.editorService.onDidVisibleEditorsChange(() => this.updateVisiblePanesContextKeys()));
223
this._register(this.editorGroupService.onDidAddGroup(() => this.updateEditorGroupsContextKeys()));
224
this._register(this.editorGroupService.onDidRemoveGroup(() => this.updateEditorGroupsContextKeys()));
225
this._register(this.editorGroupService.onDidChangeGroupIndex(() => this.updateActiveEditorGroupContextKeys()));
226
this._register(this.editorGroupService.onDidChangeGroupLocked(() => this.updateActiveEditorGroupContextKeys()));
227
228
this._register(this.editorGroupService.onDidChangeEditorPartOptions(() => this.updateEditorAreaContextKeys()));
229
230
this._register(Event.runAndSubscribe(onDidRegisterWindow, ({ window, disposables }) => {
231
const onFocusDisposables = disposables.add(new MutableDisposable<DisposableStore>());
232
disposables.add(addDisposableListener(window, EventType.FOCUS_IN, () => {
233
onFocusDisposables.value = new DisposableStore();
234
this.updateInputContextKeys(window.document, onFocusDisposables.value);
235
}, true));
236
}, { window: mainWindow, disposables: this._store }));
237
238
this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateWorkbenchStateContextKey()));
239
this._register(this.contextService.onDidChangeWorkspaceFolders(() => {
240
this.updateWorkspaceFolderCountContextKey();
241
this.updateWorkspaceContextKeys();
242
}));
243
244
this._register(this.configurationService.onDidChangeConfiguration(e => {
245
if (e.affectsConfiguration('workbench.editor.openSideBySideDirection')) {
246
this.updateSplitEditorsVerticallyContext();
247
}
248
}));
249
250
this._register(this.layoutService.onDidChangeZenMode(enabled => this.inZenModeContext.set(enabled)));
251
this._register(this.layoutService.onDidChangeActiveContainer(() => this.isAuxiliaryWindowFocusedContext.set(this.layoutService.activeContainer !== this.layoutService.mainContainer)));
252
this._register(onDidChangeFullscreen(windowId => {
253
if (windowId === mainWindow.vscodeWindowId) {
254
this.isMainWindowFullscreenContext.set(isFullscreen(mainWindow));
255
}
256
}));
257
this._register(this.layoutService.onDidChangeMainEditorCenteredLayout(centered => this.isMainEditorCenteredLayoutContext.set(centered)));
258
this._register(this.layoutService.onDidChangePanelPosition(position => this.panelPositionContext.set(position)));
259
260
this._register(this.layoutService.onDidChangePanelAlignment(alignment => this.panelAlignmentContext.set(alignment)));
261
262
this._register(this.paneCompositeService.onDidPaneCompositeClose(() => this.updateSideBarContextKeys()));
263
this._register(this.paneCompositeService.onDidPaneCompositeOpen(() => this.updateSideBarContextKeys()));
264
265
this._register(this.layoutService.onDidChangePartVisibility(() => {
266
this.mainEditorAreaVisibleContext.set(this.layoutService.isVisible(Parts.EDITOR_PART, mainWindow));
267
this.panelVisibleContext.set(this.layoutService.isVisible(Parts.PANEL_PART));
268
this.panelMaximizedContext.set(this.layoutService.isPanelMaximized());
269
this.auxiliaryBarVisibleContext.set(this.layoutService.isVisible(Parts.AUXILIARYBAR_PART));
270
271
this.updateTitleBarContextKeys();
272
}));
273
274
this._register(this.layoutService.onDidChangeAuxiliaryBarMaximized(() => {
275
this.auxiliaryBarMaximizedContext.set(this.layoutService.isAuxiliaryBarMaximized());
276
}));
277
278
this._register(this.workingCopyService.onDidChangeDirty(workingCopy => this.dirtyWorkingCopiesContext.set(workingCopy.isDirty() || this.workingCopyService.hasDirty)));
279
}
280
281
private updateVisiblePanesContextKeys(): void {
282
const visibleEditorPanes = this.editorService.visibleEditorPanes;
283
if (visibleEditorPanes.length > 0) {
284
this.editorsVisibleContext.set(true);
285
} else {
286
this.editorsVisibleContext.reset();
287
}
288
}
289
290
// Context keys depending on the state of the editor group itself
291
private updateActiveEditorGroupContextKeys(): void {
292
if (!this.editorService.activeEditor) {
293
this.activeEditorGroupEmpty.set(true);
294
} else {
295
this.activeEditorGroupEmpty.reset();
296
}
297
298
const activeGroup = this.editorGroupService.activeGroup;
299
this.activeEditorGroupIndex.set(activeGroup.index + 1); // not zero-indexed
300
this.activeEditorGroupLocked.set(activeGroup.isLocked);
301
302
this.updateEditorGroupsContextKeys();
303
}
304
305
// Context keys depending on the state of other editor groups
306
private updateEditorGroupsContextKeys(): void {
307
const groupCount = this.editorGroupService.count;
308
if (groupCount > 1) {
309
this.multipleEditorGroupsContext.set(true);
310
} else {
311
this.multipleEditorGroupsContext.reset();
312
}
313
314
const activeGroup = this.editorGroupService.activeGroup;
315
this.activeEditorGroupLast.set(activeGroup.index === groupCount - 1);
316
}
317
318
private updateEditorAreaContextKeys(): void {
319
this.editorTabsVisibleContext.set(this.editorGroupService.partOptions.showTabs === 'multiple');
320
}
321
322
private updateInputContextKeys(ownerDocument: Document, disposables: DisposableStore): void {
323
324
function activeElementIsInput(): boolean {
325
return !!ownerDocument.activeElement && isEditableElement(ownerDocument.activeElement);
326
}
327
328
const isInputFocused = activeElementIsInput();
329
this.inputFocusedContext.set(isInputFocused);
330
331
if (isInputFocused) {
332
const tracker = disposables.add(trackFocus(ownerDocument.activeElement as HTMLElement));
333
Event.once(tracker.onDidBlur)(() => {
334
335
// Ensure we are only updating the context key if we are
336
// still in the same document that we are tracking. This
337
// fixes a race condition in multi-window setups where
338
// the blur event arrives in the inactive window overwriting
339
// the context key of the active window. This is because
340
// blur events from the focus tracker are emitted with a
341
// timeout of 0.
342
343
if (getActiveWindow().document === ownerDocument) {
344
this.inputFocusedContext.set(activeElementIsInput());
345
}
346
347
tracker.dispose();
348
}, undefined, disposables);
349
}
350
}
351
352
private updateWorkbenchStateContextKey(): void {
353
this.workbenchStateContext.set(this.getWorkbenchStateString());
354
}
355
356
private updateWorkspaceFolderCountContextKey(): void {
357
this.workspaceFolderCountContext.set(this.contextService.getWorkspace().folders.length);
358
}
359
360
private updateSplitEditorsVerticallyContext(): void {
361
const direction = preferredSideBySideGroupDirection(this.configurationService);
362
this.splitEditorsVerticallyContext.set(direction === GroupDirection.DOWN);
363
}
364
365
private getWorkbenchStateString(): string {
366
switch (this.contextService.getWorkbenchState()) {
367
case WorkbenchState.EMPTY: return 'empty';
368
case WorkbenchState.FOLDER: return 'folder';
369
case WorkbenchState.WORKSPACE: return 'workspace';
370
}
371
}
372
373
private updateSideBarContextKeys(): void {
374
this.sideBarVisibleContext.set(this.layoutService.isVisible(Parts.SIDEBAR_PART));
375
}
376
377
private updateTitleBarContextKeys(): void {
378
this.titleAreaVisibleContext.set(this.layoutService.isVisible(Parts.TITLEBAR_PART, mainWindow));
379
this.titleBarStyleContext.set(getTitleBarStyle(this.configurationService));
380
}
381
382
private updateWorkspaceContextKeys(): void {
383
this.virtualWorkspaceContext.set(getVirtualWorkspaceScheme(this.contextService.getWorkspace()) || '');
384
this.temporaryWorkspaceContext.set(isTemporaryWorkspace(this.contextService.getWorkspace()));
385
}
386
}
387
388