Skip to content

Commit ad10446

Browse files
author
Benjamin Pasero
committed
debt - fix the madness of context keys in workbench
1 parent 49e36ee commit ad10446

13 files changed

Lines changed: 293 additions & 226 deletions

File tree

src/vs/platform/contextkey/common/contextkeys.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,6 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
7-
import { isMacintosh, isLinux, isWindows } from 'vs/base/common/platform';
87

98
export const InputFocusedContextKey = 'inputFocus';
109
export const InputFocusedContext = new RawContextKey<boolean>(InputFocusedContextKey, false);
11-
12-
export const IsMacContext = new RawContextKey<boolean>('isMac', isMacintosh);
13-
export const IsLinuxContext = new RawContextKey<boolean>('isLinux', isLinux);
14-
export const IsWindowsContext = new RawContextKey<boolean>('isWindows', isWindows);
15-
16-
export const HasMacNativeTabsContext = new RawContextKey<boolean>('hasMacNativeTabs', false);
17-
18-
export const SupportsWorkspacesContext = new RawContextKey<boolean>('supportsWorkspaces', true);
19-
export const SupportsOpenFileFolderContext = new RawContextKey<boolean>('supportsOpenFileFolder', isMacintosh);
20-
21-
export const IsDevelopmentContext = new RawContextKey<boolean>('isDevelopment', false);

src/vs/workbench/browser/actions/layoutActions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
1919
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
2020
import { MenuBarVisibility } from 'vs/platform/windows/common/windows';
2121
import { isWindows, isLinux } from 'vs/base/common/platform';
22-
import { IsMacContext } from 'vs/platform/contextkey/common/contextkeys';
22+
import { IsMacContext } from 'vs/workbench/common/contextkeys';
2323
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
2424
import { InEditorZenModeContext } from 'vs/workbench/common/editor';
2525
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
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 'vs/base/common/event';
7+
import { Disposable } from 'vs/base/common/lifecycle';
8+
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
9+
import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys';
10+
import { IWindowConfiguration, IWindowService } from 'vs/platform/windows/common/windows';
11+
import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically } from 'vs/workbench/common/editor';
12+
import { IsMacContext, IsLinuxContext, IsWindowsContext, HasMacNativeTabsContext, IsDevelopmentContext, SupportsWorkspacesContext, SupportsOpenFileFolderContext, WorkbenchStateContext, WorkspaceFolderCountContext } from 'vs/workbench/common/contextkeys';
13+
import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom';
14+
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
15+
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
16+
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
17+
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
18+
import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
19+
import { EditorGroupsServiceImpl } from 'vs/workbench/browser/parts/editor/editor';
20+
21+
export class WorkbenchContextKeysHandler extends Disposable {
22+
private inputFocusedContext: IContextKey<boolean>;
23+
private activeEditorContext: IContextKey<string>;
24+
private editorsVisibleContext: IContextKey<boolean>;
25+
private textCompareEditorVisibleContext: IContextKey<boolean>;
26+
private textCompareEditorActiveContext: IContextKey<boolean>;
27+
private activeEditorGroupEmpty: IContextKey<boolean>;
28+
private multipleEditorGroupsContext: IContextKey<boolean>;
29+
private workbenchStateContext: IContextKey<string>;
30+
private workspaceFolderCountContext: IContextKey<number>;
31+
private splitEditorsVerticallyContext: IContextKey<boolean>;
32+
33+
constructor(
34+
@IContextKeyService private contextKeyService: IContextKeyService,
35+
@IWorkspaceContextService private contextService: IWorkspaceContextService,
36+
@IConfigurationService private configurationService: IConfigurationService,
37+
@IEnvironmentService private environmentService: IEnvironmentService,
38+
@IWindowService private windowService: IWindowService,
39+
@IEditorService private editorService: IEditorService,
40+
@IEditorGroupsService private editorGroupService: EditorGroupsServiceImpl
41+
) {
42+
super();
43+
44+
this.initContextKeys();
45+
this.registerListeners();
46+
}
47+
48+
private registerListeners(): void {
49+
this.editorGroupService.whenRestored.then(() => this.updateEditorContextKeys());
50+
51+
this._register(this.editorService.onDidActiveEditorChange(() => this.updateEditorContextKeys()));
52+
this._register(this.editorService.onDidVisibleEditorsChange(() => this.updateEditorContextKeys()));
53+
this._register(this.editorGroupService.onDidAddGroup(() => this.updateEditorContextKeys()));
54+
this._register(this.editorGroupService.onDidRemoveGroup(() => this.updateEditorContextKeys()));
55+
56+
this._register(addDisposableListener(window, EventType.FOCUS_IN, () => this.updateInputContextKeys(), true));
57+
58+
this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateWorkbenchStateContextKey()));
59+
this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.updateWorkspaceFolderCountContextKey()));
60+
61+
this._register(this.configurationService.onDidChangeConfiguration(e => {
62+
if (e.affectsConfiguration('workbench.editor.openSideBySideDirection')) {
63+
this.updateSplitEditorsVerticallyContext();
64+
}
65+
}));
66+
}
67+
68+
private initContextKeys(): void {
69+
70+
// Platform
71+
IsMacContext.bindTo(this.contextKeyService);
72+
IsLinuxContext.bindTo(this.contextKeyService);
73+
IsWindowsContext.bindTo(this.contextKeyService);
74+
75+
// macOS Native Tabs
76+
const windowConfig = this.configurationService.getValue<IWindowConfiguration>();
77+
HasMacNativeTabsContext.bindTo(this.contextKeyService).set(windowConfig && windowConfig.window && windowConfig.window.nativeTabs);
78+
79+
// Development
80+
IsDevelopmentContext.bindTo(this.contextKeyService).set(!this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment);
81+
82+
// File Pickers
83+
SupportsWorkspacesContext.bindTo(this.contextKeyService);
84+
SupportsOpenFileFolderContext.bindTo(this.contextKeyService).set(!!this.windowService.getConfiguration().remoteAuthority);
85+
86+
// Editors
87+
this.activeEditorContext = ActiveEditorContext.bindTo(this.contextKeyService);
88+
this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService);
89+
this.textCompareEditorVisibleContext = TextCompareEditorVisibleContext.bindTo(this.contextKeyService);
90+
this.textCompareEditorActiveContext = TextCompareEditorActiveContext.bindTo(this.contextKeyService);
91+
this.activeEditorGroupEmpty = ActiveEditorGroupEmptyContext.bindTo(this.contextKeyService);
92+
this.multipleEditorGroupsContext = MultipleEditorGroupsContext.bindTo(this.contextKeyService);
93+
94+
// Inputs
95+
this.inputFocusedContext = InputFocusedContext.bindTo(this.contextKeyService);
96+
97+
// Workbench State
98+
this.workbenchStateContext = WorkbenchStateContext.bindTo(this.contextKeyService);
99+
this.updateWorkbenchStateContextKey();
100+
101+
// Workspace Folder Count
102+
this.workspaceFolderCountContext = WorkspaceFolderCountContext.bindTo(this.contextKeyService);
103+
this.updateWorkspaceFolderCountContextKey();
104+
105+
// Editor Layout
106+
this.splitEditorsVerticallyContext = SplitEditorsVertically.bindTo(this.contextKeyService);
107+
this.updateSplitEditorsVerticallyContext();
108+
}
109+
110+
private updateEditorContextKeys(): void {
111+
const activeControl = this.editorService.activeControl;
112+
const visibleEditors = this.editorService.visibleControls;
113+
114+
this.textCompareEditorActiveContext.set(!!activeControl && activeControl.getId() === TEXT_DIFF_EDITOR_ID);
115+
this.textCompareEditorVisibleContext.set(visibleEditors.some(control => control.getId() === TEXT_DIFF_EDITOR_ID));
116+
117+
if (visibleEditors.length > 0) {
118+
this.editorsVisibleContext.set(true);
119+
} else {
120+
this.editorsVisibleContext.reset();
121+
}
122+
123+
if (!this.editorService.activeEditor) {
124+
this.activeEditorGroupEmpty.set(true);
125+
} else {
126+
this.activeEditorGroupEmpty.reset();
127+
}
128+
129+
if (this.editorGroupService.count > 1) {
130+
this.multipleEditorGroupsContext.set(true);
131+
} else {
132+
this.multipleEditorGroupsContext.reset();
133+
}
134+
135+
if (activeControl) {
136+
this.activeEditorContext.set(activeControl.getId());
137+
} else {
138+
this.activeEditorContext.reset();
139+
}
140+
}
141+
142+
private updateInputContextKeys(): void {
143+
144+
function activeElementIsInput(): boolean {
145+
return !!document.activeElement && (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA');
146+
}
147+
148+
const isInputFocused = activeElementIsInput();
149+
this.inputFocusedContext.set(isInputFocused);
150+
151+
if (isInputFocused) {
152+
const tracker = trackFocus(document.activeElement as HTMLElement);
153+
Event.once(tracker.onDidBlur)(() => {
154+
this.inputFocusedContext.set(activeElementIsInput());
155+
156+
tracker.dispose();
157+
});
158+
}
159+
}
160+
161+
private updateWorkbenchStateContextKey(): void {
162+
this.workbenchStateContext.set(this.getWorkbenchStateString());
163+
}
164+
165+
private updateWorkspaceFolderCountContextKey(): void {
166+
this.workspaceFolderCountContext.set(this.contextService.getWorkspace().folders.length);
167+
}
168+
169+
private updateSplitEditorsVerticallyContext(): void {
170+
const direction = preferredSideBySideGroupDirection(this.configurationService);
171+
this.splitEditorsVerticallyContext.set(direction === GroupDirection.DOWN);
172+
}
173+
174+
private getWorkbenchStateString(): string {
175+
switch (this.contextService.getWorkbenchState()) {
176+
case WorkbenchState.EMPTY: return 'empty';
177+
case WorkbenchState.FOLDER: return 'folder';
178+
case WorkbenchState.WORKSPACE: return 'workspace';
179+
}
180+
}
181+
}

src/vs/workbench/browser/parts/notifications/notificationsCommands.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,9 @@ export const TOGGLE_NOTIFICATION = 'notification.toggle';
3232
export const CLEAR_NOTIFICATION = 'notification.clear';
3333
export const CLEAR_ALL_NOTIFICATIONS = 'notifications.clearAll';
3434

35-
const notificationFocusedId = 'notificationFocus';
36-
export const NotificationFocusedContext = new RawContextKey<boolean>(notificationFocusedId, true);
37-
38-
const notificationsCenterVisibleId = 'notificationCenterVisible';
39-
export const NotificationsCenterVisibleContext = new RawContextKey<boolean>(notificationsCenterVisibleId, false);
40-
41-
const notificationsToastsVisibleId = 'notificationToastsVisible';
42-
export const NotificationsToastsVisibleContext = new RawContextKey<boolean>(notificationsToastsVisibleId, false);
35+
export const NotificationFocusedContext = new RawContextKey<boolean>('notificationFocus', true);
36+
export const NotificationsCenterVisibleContext = new RawContextKey<boolean>('notificationCenterVisible', false);
37+
export const NotificationsToastsVisibleContext = new RawContextKey<boolean>('notificationToastsVisible', false);
4338

4439
export interface INotificationsCenterController {
4540
readonly isVisible: boolean;

src/vs/workbench/browser/parts/quickinput/quickInput.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import Severity from 'vs/base/common/severity';
3232
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
3333
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
3434
import { ICommandAndKeybindingRule, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
35-
import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen';
35+
import { inQuickOpenContext, InQuickOpenContextKey } from 'vs/workbench/browser/parts/quickopen/quickopen';
3636
import { ActionBar, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
3737
import { Action } from 'vs/base/common/actions';
3838
import { URI } from 'vs/base/common/uri';
@@ -836,7 +836,7 @@ export class QuickInputService extends Component implements IQuickInputService {
836836
@IAccessibilityService private readonly accessibilityService: IAccessibilityService
837837
) {
838838
super(QuickInputService.ID, themeService, storageService);
839-
this.inQuickOpenContext = new RawContextKey<boolean>('inQuickOpen', false).bindTo(contextKeyService);
839+
this.inQuickOpenContext = InQuickOpenContextKey.bindTo(contextKeyService);
840840
this._register(this.quickOpenService.onShow(() => this.inQuickOpen('quickOpen', true)));
841841
this._register(this.quickOpenService.onHide(() => this.inQuickOpen('quickOpen', false)));
842842
this.registerKeyModsListeners();

src/vs/workbench/browser/parts/quickopen/quickopen.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ import { Action } from 'vs/base/common/actions';
88
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
99
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
1010
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
11-
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
11+
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
1212
import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands';
1313
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
1414

15-
export const inQuickOpenContext = ContextKeyExpr.has('inQuickOpen');
15+
const inQuickOpenKey = 'inQuickOpen';
16+
export const InQuickOpenContextKey = new RawContextKey<boolean>(inQuickOpenKey, false);
17+
export const inQuickOpenContext = ContextKeyExpr.has(inQuickOpenKey);
1618
export const defaultQuickOpenContextKey = 'inFilesPicker';
1719
export const defaultQuickOpenContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(defaultQuickOpenContextKey));
1820

src/vs/workbench/browser/parts/sidebar/sidebarPart.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
3333
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
3434
import { LayoutPriority } from 'vs/base/browser/ui/grid/gridview';
3535

36+
export const SidebarVisibleContext = new RawContextKey<boolean>('sidebarVisible', false);
3637
export const SidebarFocusContext = new RawContextKey<boolean>('sideBarFocus', false);
3738
export const ActiveViewletContext = new RawContextKey<string>('activeViewlet', '');
3839

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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 { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
7+
import { isMacintosh, isLinux, isWindows } from 'vs/base/common/platform';
8+
9+
export const IsMacContext = new RawContextKey<boolean>('isMac', isMacintosh);
10+
export const IsLinuxContext = new RawContextKey<boolean>('isLinux', isLinux);
11+
export const IsWindowsContext = new RawContextKey<boolean>('isWindows', isWindows);
12+
13+
export const HasMacNativeTabsContext = new RawContextKey<boolean>('hasMacNativeTabs', false);
14+
15+
export const SupportsWorkspacesContext = new RawContextKey<boolean>('supportsWorkspaces', true);
16+
export const SupportsOpenFileFolderContext = new RawContextKey<boolean>('supportsOpenFileFolder', isMacintosh);
17+
18+
export const IsDevelopmentContext = new RawContextKey<boolean>('isDevelopment', false);
19+
20+
export const WorkbenchStateContext = new RawContextKey<string>('workbenchState', undefined);
21+
22+
export const WorkspaceFolderCountContext = new RawContextKey<number>('workspaceFolderCount', 0);

src/vs/workbench/contrib/files/browser/fileActions.contribution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { ResourceContextKey } from 'vs/workbench/common/resources';
2323
import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService';
2424
import { URI } from 'vs/base/common/uri';
2525
import { Schemas } from 'vs/base/common/network';
26-
import { SupportsWorkspacesContext } from 'vs/platform/contextkey/common/contextkeys';
26+
import { SupportsWorkspacesContext } from 'vs/workbench/common/contextkeys';
2727
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
2828

2929
// Contribute Global Actions

src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest';
1212
import * as nls from 'vs/nls';
1313
import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
1414
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
15-
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
15+
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
16+
import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys';
1617
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
1718
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
1819
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
@@ -423,7 +424,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon
423424
dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/preferences-editor-inverse.svg`))
424425
}
425426
},
426-
when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.workspaceSettingsResource.toString()), new RawContextKey<string>('workbenchState', '').isEqualTo('workspace')),
427+
when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.workspaceSettingsResource.toString()), WorkbenchStateContext.isEqualTo('workspace')),
427428
group: 'navigation',
428429
order: 1
429430
});
@@ -478,7 +479,7 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
478479
title: { value: `${category}: ${OpenFolderSettingsAction.LABEL}`, original: 'Preferences: Open Folder Settings' },
479480
category: nls.localize('preferencesCategory', "Prefernces")
480481
},
481-
when: new RawContextKey<string>('workbenchState', '').isEqualTo('workspace')
482+
when: WorkbenchStateContext.isEqualTo('workspace')
482483
});
483484

484485
CommandsRegistry.registerCommand(OpenWorkspaceSettingsAction.ID, serviceAccessor => {
@@ -490,7 +491,7 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
490491
title: { value: `${category}: ${OpenWorkspaceSettingsAction.LABEL}`, original: 'Preferences: Open Workspace Settings' },
491492
category: nls.localize('preferencesCategory', "Prefernces")
492493
},
493-
when: new RawContextKey<string>('workbenchState', '').notEqualsTo('empty')
494+
when: WorkbenchStateContext.notEqualsTo('empty')
494495
});
495496

496497
KeybindingsRegistry.registerCommandAndKeybindingRule({

0 commit comments

Comments
 (0)