Skip to content

Commit 3f8225f

Browse files
authored
Merge pull request #131101 from microsoft/ben/grouplocking
Introduce locked editor groups and auto-lock setting
2 parents 77a4b65 + 4858eac commit 3f8225f

File tree

18 files changed

+667
-75
lines changed

18 files changed

+667
-75
lines changed

src/vs/platform/actions/common/actions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export class MenuId {
9696
static readonly EditorTitle = new MenuId('EditorTitle');
9797
static readonly EditorTitleRun = new MenuId('EditorTitleRun');
9898
static readonly EditorTitleContext = new MenuId('EditorTitleContext');
99+
static readonly EmptyEditorGroup = new MenuId('EmptyEditorGroup');
99100
static readonly EmptyEditorGroupContext = new MenuId('EmptyEditorGroupContext');
100101
static readonly ExplorerContext = new MenuId('ExplorerContext');
101102
static readonly ExtensionContext = new MenuId('ExtensionContext');

src/vs/workbench/browser/contextkeys.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Event } from 'vs/base/common/event';
88
import { Disposable } from 'vs/base/common/lifecycle';
99
import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
1010
import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, IsWebContext, IsMacNativeContext, IsDevelopmentContext, IsIOSContext } from 'vs/platform/contextkey/common/contextkeys';
11-
import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, EditorInputCapabilities, ActiveEditorCanRevertContext } from 'vs/workbench/common/editor';
11+
import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorReadonlyContext, EditorAreaVisibleContext, ActiveEditorAvailableEditorIdsContext, EditorInputCapabilities, ActiveEditorCanRevertContext, ActiveEditorGroupLockedContext } from 'vs/workbench/common/editor';
1212
import { trackFocus, addDisposableListener, EventType, WebFileSystemAccess } from 'vs/base/browser/dom';
1313
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
1414
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -54,6 +54,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
5454
private activeEditorGroupEmpty: IContextKey<boolean>;
5555
private activeEditorGroupIndex: IContextKey<number>;
5656
private activeEditorGroupLast: IContextKey<boolean>;
57+
private activeEditorGroupLocked: IContextKey<boolean>;
5758
private multipleEditorGroupsContext: IContextKey<boolean>;
5859

5960
private editorsVisibleContext: IContextKey<boolean>;
@@ -124,6 +125,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
124125
this.activeEditorGroupEmpty = ActiveEditorGroupEmptyContext.bindTo(this.contextKeyService);
125126
this.activeEditorGroupIndex = ActiveEditorGroupIndexContext.bindTo(this.contextKeyService);
126127
this.activeEditorGroupLast = ActiveEditorGroupLastContext.bindTo(this.contextKeyService);
128+
this.activeEditorGroupLocked = ActiveEditorGroupLockedContext.bindTo(this.contextKeyService);
127129
this.multipleEditorGroupsContext = MultipleEditorGroupsContext.bindTo(this.contextKeyService);
128130

129131
// Working Copies
@@ -205,6 +207,9 @@ export class WorkbenchContextKeysHandler extends Disposable {
205207
this._register(this.editorGroupService.onDidRemoveGroup(() => this.updateEditorContextKeys()));
206208
this._register(this.editorGroupService.onDidChangeGroupIndex(() => this.updateEditorContextKeys()));
207209

210+
this._register(this.editorGroupService.onDidChangeActiveGroup(() => this.updateEditorGroupContextKeys()));
211+
this._register(this.editorGroupService.onDidChangeGroupLocked(() => this.updateEditorGroupContextKeys()));
212+
208213
this._register(addDisposableListener(window, EventType.FOCUS_IN, () => this.updateInputContextKeys(), true));
209214

210215
this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateWorkbenchStateContextKey()));
@@ -237,7 +242,6 @@ export class WorkbenchContextKeysHandler extends Disposable {
237242
}
238243

239244
private updateEditorContextKeys(): void {
240-
const activeGroup = this.editorGroupService.activeGroup;
241245
const activeEditorPane = this.editorService.activeEditorPane;
242246
const visibleEditorPanes = this.editorService.visibleEditorPanes;
243247

@@ -256,15 +260,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
256260
this.activeEditorGroupEmpty.reset();
257261
}
258262

259-
const groupCount = this.editorGroupService.count;
260-
if (groupCount > 1) {
261-
this.multipleEditorGroupsContext.set(true);
262-
} else {
263-
this.multipleEditorGroupsContext.reset();
264-
}
265-
266-
this.activeEditorGroupIndex.set(activeGroup.index + 1); // not zero-indexed
267-
this.activeEditorGroupLast.set(activeGroup.index === groupCount - 1);
263+
this.updateEditorGroupContextKeys();
268264

269265
if (activeEditorPane) {
270266
this.activeEditorContext.set(activeEditorPane.getId());
@@ -282,6 +278,20 @@ export class WorkbenchContextKeysHandler extends Disposable {
282278
}
283279
}
284280

281+
private updateEditorGroupContextKeys(): void {
282+
const groupCount = this.editorGroupService.count;
283+
if (groupCount > 1) {
284+
this.multipleEditorGroupsContext.set(true);
285+
} else {
286+
this.multipleEditorGroupsContext.reset();
287+
}
288+
289+
const activeGroup = this.editorGroupService.activeGroup;
290+
this.activeEditorGroupIndex.set(activeGroup.index + 1); // not zero-indexed
291+
this.activeEditorGroupLast.set(activeGroup.index === groupCount - 1);
292+
this.activeEditorGroupLocked.set(activeGroup.isLocked);
293+
}
294+
285295
private updateInputContextKeys(): void {
286296

287297
function activeElementIsInput(): boolean {

src/vs/workbench/browser/parts/editor/editor.contribution.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri';
99
import { IEditorPaneRegistry, EditorPaneDescriptor } from 'vs/workbench/browser/editor';
1010
import {
1111
IEditorFactoryRegistry, TextCompareEditorActiveContext, ActiveEditorPinnedContext, EditorExtensions, EditorGroupEditorsCountContext,
12-
ActiveEditorStickyContext, ActiveEditorAvailableEditorIdsContext, MultipleEditorGroupsContext, ActiveEditorDirtyContext
12+
ActiveEditorStickyContext, ActiveEditorAvailableEditorIdsContext, MultipleEditorGroupsContext, ActiveEditorDirtyContext, ActiveEditorGroupLockedContext
1313
} from 'vs/workbench/common/editor';
1414
import { SideBySideEditorInput, SideBySideEditorInputSerializer } from 'vs/workbench/common/editor/sideBySideEditorInput';
1515
import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor';
@@ -42,7 +42,7 @@ import {
4242
CLOSE_EDITORS_AND_GROUP_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_EDITOR_GROUP_COMMAND_ID,
4343
CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_PINNED_EDITOR_COMMAND_ID, CLOSE_SAVED_EDITORS_COMMAND_ID, GOTO_NEXT_CHANGE, GOTO_PREVIOUS_CHANGE, KEEP_EDITOR_COMMAND_ID,
4444
PIN_EDITOR_COMMAND_ID, SHOW_EDITORS_IN_GROUP, SPLIT_EDITOR_DOWN, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE,
45-
TOGGLE_DIFF_SIDE_BY_SIDE, TOGGLE_KEEP_EDITORS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, setup as registerEditorCommands, REOPEN_WITH_COMMAND_ID
45+
TOGGLE_DIFF_SIDE_BY_SIDE, TOGGLE_KEEP_EDITORS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, setup as registerEditorCommands, REOPEN_WITH_COMMAND_ID, TOGGLE_LOCK_GROUP_COMMAND_ID, UNLOCK_GROUP_COMMAND_ID
4646
} from 'vs/workbench/browser/parts/editor/editorCommands';
4747
import { inQuickPickContext, getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess';
4848
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
@@ -292,12 +292,17 @@ if (isMacintosh) {
292292
});
293293
}
294294

295+
// Empty Editor Group Toolbar
296+
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroup, { command: { id: UNLOCK_GROUP_COMMAND_ID, title: localize('unlockGroupAction', "Unlock Group"), icon: Codicon.unlock }, group: 'navigation', order: 10, when: ActiveEditorGroupLockedContext });
297+
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroup, { command: { id: CLOSE_EDITOR_GROUP_COMMAND_ID, title: localize('closeGroupAction', "Close Group"), icon: Codicon.close }, group: 'navigation', order: 10, when: ActiveEditorGroupLockedContext.toNegated() });
298+
295299
// Empty Editor Group Context Menu
296300
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: SPLIT_EDITOR_UP, title: localize('splitUp', "Split Up") }, group: '2_split', order: 10 });
297301
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: SPLIT_EDITOR_DOWN, title: localize('splitDown', "Split Down") }, group: '2_split', order: 20 });
298302
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: SPLIT_EDITOR_LEFT, title: localize('splitLeft', "Split Left") }, group: '2_split', order: 30 });
299303
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: SPLIT_EDITOR_RIGHT, title: localize('splitRight', "Split Right") }, group: '2_split', order: 40 });
300-
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: CLOSE_EDITOR_GROUP_COMMAND_ID, title: localize('close', "Close") }, group: '3_close', order: 10, when: ContextKeyExpr.has('multipleEditorGroups') });
304+
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: TOGGLE_LOCK_GROUP_COMMAND_ID, title: localize('toggleLockGroup', "Lock Group"), toggled: ActiveEditorGroupLockedContext }, group: '3_lock', order: 10, when: ContextKeyExpr.has('multipleEditorGroups') });
305+
MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: CLOSE_EDITOR_GROUP_COMMAND_ID, title: localize('close', "Close") }, group: '4_close', order: 10, when: ContextKeyExpr.has('multipleEditorGroups') });
301306

302307
// Editor Title Context Menu
303308
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_EDITOR_COMMAND_ID, title: localize('close', "Close") }, group: '1_close', order: 10 });
@@ -320,6 +325,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: SHOW_EDITORS_IN
320325
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeAll', "Close All") }, group: '5_close', order: 10 });
321326
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 });
322327
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('toggleKeepEditors', "Keep Editors Open"), toggled: ContextKeyExpr.not('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 });
328+
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_LOCK_GROUP_COMMAND_ID, title: localize('lockGroup', "Lock Group"), toggled: ActiveEditorGroupLockedContext }, group: '8_lock', order: 10, when: ContextKeyExpr.has('multipleEditorGroups') });
323329

324330
interface IEditorToolItem { id: string; title: string; icon?: { dark?: URI; light?: URI; } | ThemeIcon; }
325331

@@ -442,6 +448,17 @@ appendEditorToolItem(
442448
}
443449
);
444450

451+
// Unlock Group: only when group is locked
452+
appendEditorToolItem(
453+
{
454+
id: UNLOCK_GROUP_COMMAND_ID,
455+
title: localize('unlockEditorGroup', "Unlock Group"),
456+
icon: Codicon.unlock
457+
},
458+
ActiveEditorGroupLockedContext,
459+
1000000 - 1, // left to close action
460+
);
461+
445462
const previousChangeIcon = registerIcon('diff-editor-previous-change', Codicon.arrowUp, localize('previousChangeIcon', 'Icon for the previous change action in the diff editor.'));
446463
const nextChangeIcon = registerIcon('diff-editor-next-change', Codicon.arrowDown, localize('nextChangeIcon', 'Icon for the next change action in the diff editor.'));
447464
const toggleWhitespace = registerIcon('diff-editor-toggle-whitespace', Codicon.whitespace, localize('toggleWhitespace', 'Icon for the toggle whitespace action in the diff editor.'));

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,16 @@ export function getEditorPartOptions(configurationService: IConfigurationService
5454

5555
const config = configurationService.getValue<IWorkbenchEditorConfiguration>();
5656
if (config?.workbench?.editor) {
57+
58+
// Assign all primitive configuration over
5759
Object.assign(options, config.workbench.editor);
60+
61+
// Special handle array types and convert to Set
62+
if (Array.isArray(config.workbench.editor.experimentalAutoLockGroups)) {
63+
options.experimentalAutoLockGroups = new Set(config.workbench.editor.experimentalAutoLockGroups);
64+
} else {
65+
options.experimentalAutoLockGroups = undefined;
66+
}
5867
}
5968

6069
return options;

src/vs/workbench/browser/parts/editor/editorCommands.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, Grou
2222
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
2323
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
2424
import { CommandsRegistry, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands';
25-
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
25+
import { MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
26+
import { CATEGORIES } from 'vs/workbench/common/actions';
2627
import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess';
2728
import { IOpenerService } from 'vs/platform/opener/common/opener';
2829
import { EditorResolution, ITextEditorOptions } from 'vs/platform/editor/common/editor';
@@ -41,6 +42,9 @@ export const MOVE_ACTIVE_EDITOR_COMMAND_ID = 'moveActiveEditor';
4142
export const LAYOUT_EDITOR_GROUPS_COMMAND_ID = 'layoutEditorGroups';
4243
export const KEEP_EDITOR_COMMAND_ID = 'workbench.action.keepEditor';
4344
export const TOGGLE_KEEP_EDITORS_COMMAND_ID = 'workbench.action.toggleKeepEditors';
45+
export const TOGGLE_LOCK_GROUP_COMMAND_ID = 'workbench.action.experimentalToggleEditorGroupLock';
46+
export const LOCK_GROUP_COMMAND_ID = 'workbench.action.experimentalLockEditorGroup';
47+
export const UNLOCK_GROUP_COMMAND_ID = 'workbench.action.experimentalUnlockEditorGroup';
4448
export const SHOW_EDITORS_IN_GROUP = 'workbench.action.showEditorsInGroup';
4549
export const REOPEN_WITH_COMMAND_ID = 'workbench.action.reopenWithEditor';
4650

@@ -958,6 +962,60 @@ function registerOtherEditorCommands(): void {
958962
}
959963
});
960964

965+
function setEditorGroupLock(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext, locked?: boolean): void {
966+
const editorGroupService = accessor.get(IEditorGroupsService);
967+
968+
const { group } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
969+
if (group) {
970+
group.lock(locked ?? !group.isLocked);
971+
}
972+
}
973+
974+
registerAction2(class extends Action2 {
975+
constructor() {
976+
super({
977+
id: TOGGLE_LOCK_GROUP_COMMAND_ID,
978+
title: localize('toggleEditorGroupLock', "Toggle Editor Group Lock"),
979+
category: CATEGORIES.View,
980+
precondition: ContextKeyExpr.has('multipleEditorGroups'),
981+
f1: true
982+
});
983+
}
984+
async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise<void> {
985+
setEditorGroupLock(accessor, resourceOrContext, context);
986+
}
987+
});
988+
989+
registerAction2(class extends Action2 {
990+
constructor() {
991+
super({
992+
id: LOCK_GROUP_COMMAND_ID,
993+
title: localize('lockEditorGroup', "Lock Editor Group"),
994+
category: CATEGORIES.View,
995+
precondition: ContextKeyExpr.has('multipleEditorGroups'),
996+
f1: true
997+
});
998+
}
999+
async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise<void> {
1000+
setEditorGroupLock(accessor, resourceOrContext, context, true);
1001+
}
1002+
});
1003+
1004+
registerAction2(class extends Action2 {
1005+
constructor() {
1006+
super({
1007+
id: UNLOCK_GROUP_COMMAND_ID,
1008+
title: localize('unlockEditorGroup', "Unlock Editor Group"),
1009+
precondition: ContextKeyExpr.has('multipleEditorGroups'),
1010+
category: CATEGORIES.View,
1011+
f1: true
1012+
});
1013+
}
1014+
async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise<void> {
1015+
setEditorGroupLock(accessor, resourceOrContext, context, false);
1016+
}
1017+
});
1018+
9611019
KeybindingsRegistry.registerCommandAndKeybindingRule({
9621020
id: PIN_EDITOR_COMMAND_ID,
9631021
weight: KeybindingWeight.WorkbenchContrib,

0 commit comments

Comments
 (0)