From 99c8d615c15fa6b92514806f66b7aa24f36914c2 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 22 Sep 2020 12:04:53 +0200 Subject: [PATCH 1/3] groups for viewsWelcome --- extensions/git/package.json | 15 +++++++---- src/vs/workbench/common/views.ts | 25 +++++++++---------- .../contrib/debug/browser/welcomeView.ts | 19 ++++++++------ .../contrib/files/browser/explorerViewlet.ts | 14 ++++++++--- .../common/viewsWelcomeContribution.ts | 21 ++++++++++++++-- .../common/viewsWelcomeExtensionPoint.ts | 6 +++++ 6 files changed, 69 insertions(+), 31 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 6570b4a36c5..691c1deac71 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -2065,27 +2065,32 @@ { "view": "scm", "contents": "%view.workbench.scm.empty%", - "when": "config.git.enabled && git.state == initialized && workbenchState == empty" + "when": "config.git.enabled && git.state == initialized && workbenchState == empty", + "group": "2_open@1" }, { "view": "scm", "contents": "%view.workbench.scm.folder%", - "when": "config.git.enabled && git.state == initialized && workbenchState == folder" + "when": "config.git.enabled && git.state == initialized && workbenchState == folder", + "group": "5_scm@1" }, { "view": "scm", "contents": "%view.workbench.scm.workspace%", - "when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0" + "when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0", + "group": "5_scm@1" }, { "view": "scm", "contents": "%view.workbench.scm.emptyWorkspace%", - "when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount == 0" + "when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount == 0", + "group": "2_open@1" }, { "view": "explorer", "contents": "%view.workbench.cloneRepository%", - "when": "config.git.enabled && git.state == initialized" + "when": "config.git.enabled && git.state == initialized", + "group": "5_scm@1" } ] }, diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index f4b3e26f92f..0cd893fce42 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -278,16 +278,18 @@ export interface IViewContainerModel { move(from: string, to: string): void; } -export enum ViewContentPriority { - Normal = 0, - Low = 1, - Lowest = 2 +export enum ViewContentGroups { + Open = '2_open', + Debug = '4_debug', + SCM = '5_scm', + More = '9_more' } export interface IViewContentDescriptor { readonly content: string; readonly when?: ContextKeyExpression | 'default'; - readonly priority?: ViewContentPriority; + readonly group?: string; + readonly order?: number; /** * ordered preconditions for each button in the content @@ -323,15 +325,12 @@ export interface IViewsRegistry { } function compareViewContentDescriptors(a: IViewContentDescriptor, b: IViewContentDescriptor): number { - const aPriority = a.priority ?? ViewContentPriority.Normal; - const bPriority = b.priority ?? ViewContentPriority.Normal; - - if (aPriority !== bPriority) { - return aPriority - bPriority; + const aGroup = a.group ?? ViewContentGroups.More; + const bGroup = b.group ?? ViewContentGroups.More; + if (aGroup !== bGroup) { + return aGroup.localeCompare(bGroup); } - - // No priroity, keep views sorted in the order they got registered - return 0; + return (a.order ?? 5) - (b.order ?? 5); } class ViewsRegistry extends Disposable implements IViewsRegistry { diff --git a/src/vs/workbench/contrib/debug/browser/welcomeView.ts b/src/vs/workbench/contrib/debug/browser/welcomeView.ts index b0bad1df2f2..37f5d797560 100644 --- a/src/vs/workbench/contrib/debug/browser/welcomeView.ts +++ b/src/vs/workbench/contrib/debug/browser/welcomeView.ts @@ -15,7 +15,7 @@ import { IDebugService, CONTEXT_DEBUGGERS_AVAILABLE } from 'vs/workbench/contrib import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IViewDescriptorService, IViewsRegistry, Extensions, ViewContentPriority } from 'vs/workbench/common/views'; +import { IViewDescriptorService, IViewsRegistry, Extensions, ViewContentGroups } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; @@ -109,7 +109,8 @@ const viewsRegistry = Registry.as(Extensions.ViewsRegistry); viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { content: localize({ key: 'openAFileWhichCanBeDebugged', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID), - when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated()) + when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated()), + group: ViewContentGroups.Open }); let debugKeybindingLabel = ''; @@ -117,24 +118,28 @@ viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { content: localize({ key: 'runAndDebugAction', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID), preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR], - when: CONTEXT_DEBUGGERS_AVAILABLE + when: CONTEXT_DEBUGGERS_AVAILABLE, + group: ViewContentGroups.Debug }); viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { content: localize({ key: 'detectThenRunAndDebug', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "[Show](command:{0}) all automatic debug configurations.", SelectAndStartAction.ID), - priority: ViewContentPriority.Lowest, - when: CONTEXT_DEBUGGERS_AVAILABLE + when: CONTEXT_DEBUGGERS_AVAILABLE, + group: ViewContentGroups.Debug, + order: 10 }); viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { content: localize({ key: 'customizeRunAndDebug', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID), - when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, WorkbenchStateContext.notEqualsTo('empty')) + when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, WorkbenchStateContext.notEqualsTo('empty')), + group: ViewContentGroups.Debug }); viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, { content: localize({ key: 'customizeRunAndDebugOpenFolder', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID), - when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, WorkbenchStateContext.isEqualTo('empty')) + when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, WorkbenchStateContext.isEqualTo('empty')), + group: ViewContentGroups.Debug }); diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index 50567746fe9..40a69a74786 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -19,7 +19,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IViewsRegistry, IViewDescriptor, Extensions, ViewContainer, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views'; +import { IViewsRegistry, IViewDescriptor, Extensions, ViewContainer, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService, ViewContentGroups } from 'vs/workbench/common/views'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -273,18 +273,24 @@ const viewsRegistry = Registry.as(Extensions.ViewsRegistry); viewsRegistry.registerViewWelcomeContent(EmptyView.ID, { content: localize({ key: 'noWorkspaceHelp', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "You have not yet added a folder to the workspace.\n[Add Folder](command:{0})", AddRootFolderAction.ID), - when: WorkbenchStateContext.isEqualTo('workspace') + when: WorkbenchStateContext.isEqualTo('workspace'), + group: ViewContentGroups.Open, + order: 1 }); const commandId = isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID; viewsRegistry.registerViewWelcomeContent(EmptyView.ID, { content: localize({ key: 'remoteNoFolderHelp', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "Connected to remote.\n[Open Folder](command:{0})", commandId), - when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.notEqualsTo(''), IsWebContext.toNegated()) + when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.notEqualsTo(''), IsWebContext.toNegated()), + group: ViewContentGroups.Open, + order: 1 }); viewsRegistry.registerViewWelcomeContent(EmptyView.ID, { content: localize({ key: 'noFolderHelp', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, "You have not yet opened a folder.\n[Open Folder](command:{0})", commandId), - when: ContextKeyExpr.or(ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.isEqualTo('')), ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), IsWebContext)) + when: ContextKeyExpr.or(ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.isEqualTo('')), ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), IsWebContext)), + group: ViewContentGroups.Open, + order: 1 }); diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts index 3ee82a7b2c2..1bdecc10b6b 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts @@ -9,7 +9,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ViewsWelcomeExtensionPoint, ViewWelcome, ViewIdentifierMap } from './viewsWelcomeExtensionPoint'; import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions as ViewContainerExtensions, IViewsRegistry, ViewContentPriority } from 'vs/workbench/common/views'; +import { Extensions as ViewContainerExtensions, IViewsRegistry } from 'vs/workbench/common/views'; const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); @@ -34,10 +34,12 @@ export class ViewsWelcomeContribution extends Disposable implements IWorkbenchCo for (const contribution of added) { for (const welcome of contribution.value) { const id = ViewIdentifierMap[welcome.view] ?? welcome.view; + const { group, order } = parseGroupAndOrder(welcome); const disposable = viewsRegistry.registerViewWelcomeContent(id, { content: welcome.contents, when: ContextKeyExpr.deserialize(welcome.when), - priority: contribution.description.isBuiltin ? ViewContentPriority.Low : ViewContentPriority.Lowest + group, + order }); this.viewWelcomeContents.set(welcome, disposable); @@ -46,3 +48,18 @@ export class ViewsWelcomeContribution extends Disposable implements IWorkbenchCo }); } } + +function parseGroupAndOrder(welcome: ViewWelcome): { group: string | undefined, order: number | undefined } { + let group: string | undefined; + let order: number | undefined; + if (welcome.group) { + const idx = welcome.group.lastIndexOf('@'); + if (idx > 0) { + group = welcome.group.substr(0, idx); + order = Number(welcome.group.substr(idx + 1)) || undefined; + } else { + group = welcome.group; + } + } + return { group, order }; +} diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts index 68d5c4a6e64..24a86dafd5f 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts @@ -10,12 +10,14 @@ export enum ViewsWelcomeExtensionPointFields { view = 'view', contents = 'contents', when = 'when', + group = 'group', } export interface ViewWelcome { readonly [ViewsWelcomeExtensionPointFields.view]: string; readonly [ViewsWelcomeExtensionPointFields.contents]: string; readonly [ViewsWelcomeExtensionPointFields.when]: string; + readonly [ViewsWelcomeExtensionPointFields.group]: string; } export type ViewsWelcomeExtensionPoint = ViewWelcome[]; @@ -58,6 +60,10 @@ const viewsWelcomeExtensionPointSchema = Object.freeze Date: Wed, 23 Sep 2020 08:57:30 +0200 Subject: [PATCH 2/3] fix wording into -> to --- .../contrib/welcome/common/viewsWelcomeExtensionPoint.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts index 24a86dafd5f..6724d8a95ab 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint.ts @@ -62,7 +62,7 @@ const viewsWelcomeExtensionPointSchema = Object.freeze Date: Thu, 24 Sep 2020 17:02:54 +0200 Subject: [PATCH 3/3] add enableProposedApi check --- .../welcome/common/viewsWelcomeContribution.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts index 1bdecc10b6b..25049bac517 100644 --- a/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts +++ b/src/vs/workbench/contrib/welcome/common/viewsWelcomeContribution.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ViewsWelcomeExtensionPoint, ViewWelcome, ViewIdentifierMap } from './viewsWelcomeExtensionPoint'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as ViewContainerExtensions, IViewsRegistry } from 'vs/workbench/common/views'; @@ -34,7 +35,7 @@ export class ViewsWelcomeContribution extends Disposable implements IWorkbenchCo for (const contribution of added) { for (const welcome of contribution.value) { const id = ViewIdentifierMap[welcome.view] ?? welcome.view; - const { group, order } = parseGroupAndOrder(welcome); + const { group, order } = parseGroupAndOrder(welcome, contribution); const disposable = viewsRegistry.registerViewWelcomeContent(id, { content: welcome.contents, when: ContextKeyExpr.deserialize(welcome.when), @@ -49,10 +50,16 @@ export class ViewsWelcomeContribution extends Disposable implements IWorkbenchCo } } -function parseGroupAndOrder(welcome: ViewWelcome): { group: string | undefined, order: number | undefined } { +function parseGroupAndOrder(welcome: ViewWelcome, contribution: IExtensionPointUser): { group: string | undefined, order: number | undefined } { + let group: string | undefined; let order: number | undefined; if (welcome.group) { + if (!contribution.description.enableProposedApi) { + contribution.collector.warn(nls.localize('ViewsWelcomeExtensionPoint.proposedAPI', "The viewsWelcome contribution in '{0}' requires 'enableProposedApi' to be enabled.", contribution.description.identifier.value)); + return { group, order }; + } + const idx = welcome.group.lastIndexOf('@'); if (idx > 0) { group = welcome.group.substr(0, idx);