Skip to content

Commit 2ef6c25

Browse files
authored
Merge pull request microsoft#107215 from microsoft/aeschli/welcomeViewGroups
groups for viewsWelcome
2 parents 64be1b4 + bb99748 commit 2ef6c25

6 files changed

Lines changed: 77 additions & 32 deletions

File tree

extensions/git/package.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2083,27 +2083,32 @@
20832083
{
20842084
"view": "scm",
20852085
"contents": "%view.workbench.scm.empty%",
2086-
"when": "config.git.enabled && git.state == initialized && workbenchState == empty"
2086+
"when": "config.git.enabled && git.state == initialized && workbenchState == empty",
2087+
"group": "2_open@1"
20872088
},
20882089
{
20892090
"view": "scm",
20902091
"contents": "%view.workbench.scm.folder%",
2091-
"when": "config.git.enabled && git.state == initialized && workbenchState == folder"
2092+
"when": "config.git.enabled && git.state == initialized && workbenchState == folder",
2093+
"group": "5_scm@1"
20922094
},
20932095
{
20942096
"view": "scm",
20952097
"contents": "%view.workbench.scm.workspace%",
2096-
"when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0"
2098+
"when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0",
2099+
"group": "5_scm@1"
20972100
},
20982101
{
20992102
"view": "scm",
21002103
"contents": "%view.workbench.scm.emptyWorkspace%",
2101-
"when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount == 0"
2104+
"when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount == 0",
2105+
"group": "2_open@1"
21022106
},
21032107
{
21042108
"view": "explorer",
21052109
"contents": "%view.workbench.cloneRepository%",
2106-
"when": "config.git.enabled && git.state == initialized"
2110+
"when": "config.git.enabled && git.state == initialized",
2111+
"group": "5_scm@1"
21072112
}
21082113
]
21092114
},

src/vs/workbench/common/views.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -278,16 +278,18 @@ export interface IViewContainerModel {
278278
move(from: string, to: string): void;
279279
}
280280

281-
export enum ViewContentPriority {
282-
Normal = 0,
283-
Low = 1,
284-
Lowest = 2
281+
export enum ViewContentGroups {
282+
Open = '2_open',
283+
Debug = '4_debug',
284+
SCM = '5_scm',
285+
More = '9_more'
285286
}
286287

287288
export interface IViewContentDescriptor {
288289
readonly content: string;
289290
readonly when?: ContextKeyExpression | 'default';
290-
readonly priority?: ViewContentPriority;
291+
readonly group?: string;
292+
readonly order?: number;
291293

292294
/**
293295
* ordered preconditions for each button in the content
@@ -323,15 +325,12 @@ export interface IViewsRegistry {
323325
}
324326

325327
function compareViewContentDescriptors(a: IViewContentDescriptor, b: IViewContentDescriptor): number {
326-
const aPriority = a.priority ?? ViewContentPriority.Normal;
327-
const bPriority = b.priority ?? ViewContentPriority.Normal;
328-
329-
if (aPriority !== bPriority) {
330-
return aPriority - bPriority;
328+
const aGroup = a.group ?? ViewContentGroups.More;
329+
const bGroup = b.group ?? ViewContentGroups.More;
330+
if (aGroup !== bGroup) {
331+
return aGroup.localeCompare(bGroup);
331332
}
332-
333-
// No priroity, keep views sorted in the order they got registered
334-
return 0;
333+
return (a.order ?? 5) - (b.order ?? 5);
335334
}
336335

337336
class ViewsRegistry extends Disposable implements IViewsRegistry {

src/vs/workbench/contrib/debug/browser/welcomeView.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { IDebugService, CONTEXT_DEBUGGERS_AVAILABLE } from 'vs/workbench/contrib
1515
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
1616
import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
1717
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
18-
import { IViewDescriptorService, IViewsRegistry, Extensions, ViewContentPriority } from 'vs/workbench/common/views';
18+
import { IViewDescriptorService, IViewsRegistry, Extensions, ViewContentGroups } from 'vs/workbench/common/views';
1919
import { Registry } from 'vs/platform/registry/common/platform';
2020
import { IOpenerService } from 'vs/platform/opener/common/opener';
2121
import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys';
@@ -109,32 +109,37 @@ const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
109109
viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, {
110110
content: localize({ key: 'openAFileWhichCanBeDebugged', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
111111
"[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID),
112-
when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated())
112+
when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated()),
113+
group: ViewContentGroups.Open
113114
});
114115

115116
let debugKeybindingLabel = '';
116117
viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, {
117118
content: localize({ key: 'runAndDebugAction', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
118119
"[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID),
119120
preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR],
120-
when: CONTEXT_DEBUGGERS_AVAILABLE
121+
when: CONTEXT_DEBUGGERS_AVAILABLE,
122+
group: ViewContentGroups.Debug
121123
});
122124

123125
viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, {
124126
content: localize({ key: 'detectThenRunAndDebug', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
125127
"[Show](command:{0}) all automatic debug configurations.", SelectAndStartAction.ID),
126-
priority: ViewContentPriority.Lowest,
127-
when: CONTEXT_DEBUGGERS_AVAILABLE
128+
when: CONTEXT_DEBUGGERS_AVAILABLE,
129+
group: ViewContentGroups.Debug,
130+
order: 10
128131
});
129132

130133
viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, {
131134
content: localize({ key: 'customizeRunAndDebug', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
132135
"To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID),
133-
when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, WorkbenchStateContext.notEqualsTo('empty'))
136+
when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, WorkbenchStateContext.notEqualsTo('empty')),
137+
group: ViewContentGroups.Debug
134138
});
135139

136140
viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, {
137141
content: localize({ key: 'customizeRunAndDebugOpenFolder', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
138142
"To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID),
139-
when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, WorkbenchStateContext.isEqualTo('empty'))
143+
when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, WorkbenchStateContext.isEqualTo('empty')),
144+
group: ViewContentGroups.Debug
140145
});

src/vs/workbench/contrib/files/browser/explorerViewlet.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
1919
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
2020
import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
2121
import { IThemeService } from 'vs/platform/theme/common/themeService';
22-
import { IViewsRegistry, IViewDescriptor, Extensions, ViewContainer, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views';
22+
import { IViewsRegistry, IViewDescriptor, Extensions, ViewContainer, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService, ViewContentGroups } from 'vs/workbench/common/views';
2323
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
2424
import { Disposable } from 'vs/base/common/lifecycle';
2525
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
@@ -273,18 +273,24 @@ const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
273273
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
274274
content: localize({ key: 'noWorkspaceHelp', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
275275
"You have not yet added a folder to the workspace.\n[Add Folder](command:{0})", AddRootFolderAction.ID),
276-
when: WorkbenchStateContext.isEqualTo('workspace')
276+
when: WorkbenchStateContext.isEqualTo('workspace'),
277+
group: ViewContentGroups.Open,
278+
order: 1
277279
});
278280

279281
const commandId = isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID;
280282
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
281283
content: localize({ key: 'remoteNoFolderHelp', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
282284
"Connected to remote.\n[Open Folder](command:{0})", commandId),
283-
when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.notEqualsTo(''), IsWebContext.toNegated())
285+
when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.notEqualsTo(''), IsWebContext.toNegated()),
286+
group: ViewContentGroups.Open,
287+
order: 1
284288
});
285289

286290
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
287291
content: localize({ key: 'noFolderHelp', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] },
288292
"You have not yet opened a folder.\n[Open Folder](command:{0})", commandId),
289-
when: ContextKeyExpr.or(ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.isEqualTo('')), ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), IsWebContext))
293+
when: ContextKeyExpr.or(ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.isEqualTo('')), ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), IsWebContext)),
294+
group: ViewContentGroups.Open,
295+
order: 1
290296
});

src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import * as nls from 'vs/nls';
67
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
78
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
89
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
9-
import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry';
10+
import { IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
1011
import { ViewsWelcomeExtensionPoint, ViewWelcome, ViewIdentifierMap } from './viewsWelcomeExtensionPoint';
1112
import { Registry } from 'vs/platform/registry/common/platform';
12-
import { Extensions as ViewContainerExtensions, IViewsRegistry, ViewContentPriority } from 'vs/workbench/common/views';
13+
import { Extensions as ViewContainerExtensions, IViewsRegistry } from 'vs/workbench/common/views';
1314

1415
const viewsRegistry = Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry);
1516

@@ -34,10 +35,12 @@ export class ViewsWelcomeContribution extends Disposable implements IWorkbenchCo
3435
for (const contribution of added) {
3536
for (const welcome of contribution.value) {
3637
const id = ViewIdentifierMap[welcome.view] ?? welcome.view;
38+
const { group, order } = parseGroupAndOrder(welcome, contribution);
3739
const disposable = viewsRegistry.registerViewWelcomeContent(id, {
3840
content: welcome.contents,
3941
when: ContextKeyExpr.deserialize(welcome.when),
40-
priority: contribution.description.isBuiltin ? ViewContentPriority.Low : ViewContentPriority.Lowest
42+
group,
43+
order
4144
});
4245

4346
this.viewWelcomeContents.set(welcome, disposable);
@@ -46,3 +49,24 @@ export class ViewsWelcomeContribution extends Disposable implements IWorkbenchCo
4649
});
4750
}
4851
}
52+
53+
function parseGroupAndOrder(welcome: ViewWelcome, contribution: IExtensionPointUser<ViewsWelcomeExtensionPoint>): { group: string | undefined, order: number | undefined } {
54+
55+
let group: string | undefined;
56+
let order: number | undefined;
57+
if (welcome.group) {
58+
if (!contribution.description.enableProposedApi) {
59+
contribution.collector.warn(nls.localize('ViewsWelcomeExtensionPoint.proposedAPI', "The viewsWelcome contribution in '{0}' requires 'enableProposedApi' to be enabled.", contribution.description.identifier.value));
60+
return { group, order };
61+
}
62+
63+
const idx = welcome.group.lastIndexOf('@');
64+
if (idx > 0) {
65+
group = welcome.group.substr(0, idx);
66+
order = Number(welcome.group.substr(idx + 1)) || undefined;
67+
} else {
68+
group = welcome.group;
69+
}
70+
}
71+
return { group, order };
72+
}

src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ export enum ViewsWelcomeExtensionPointFields {
1010
view = 'view',
1111
contents = 'contents',
1212
when = 'when',
13+
group = 'group',
1314
}
1415

1516
export interface ViewWelcome {
1617
readonly [ViewsWelcomeExtensionPointFields.view]: string;
1718
readonly [ViewsWelcomeExtensionPointFields.contents]: string;
1819
readonly [ViewsWelcomeExtensionPointFields.when]: string;
20+
readonly [ViewsWelcomeExtensionPointFields.group]: string;
1921
}
2022

2123
export type ViewsWelcomeExtensionPoint = ViewWelcome[];
@@ -58,6 +60,10 @@ const viewsWelcomeExtensionPointSchema = Object.freeze<IConfigurationPropertySch
5860
type: 'string',
5961
description: nls.localize('contributes.viewsWelcome.view.when', "Condition when the welcome content should be displayed."),
6062
},
63+
[ViewsWelcomeExtensionPointFields.group]: {
64+
type: 'string',
65+
description: nls.localize('contributes.viewsWelcome.view.group', "Group to which this welcome content belongs."),
66+
},
6167
}
6268
}
6369
});

0 commit comments

Comments
 (0)