未验证 提交 cce81b98 编写于 作者: J Joao Moreno

Merge branch 'joao/empty-views-api'

......@@ -1811,7 +1811,34 @@
72
]
}
}
},
"viewsWelcome": [
{
"view": "workbench.scm",
"contents": "%view.workbench.scm.disabled%",
"when": "!config.git.enabled"
},
{
"view": "workbench.scm",
"contents": "%view.workbench.scm.missing%",
"when": "config.git.enabled && git.missing"
},
{
"view": "workbench.scm",
"contents": "%view.workbench.scm.empty%",
"when": "config.git.enabled && !git.missing && workbenchState == empty"
},
{
"view": "workbench.scm",
"contents": "%view.workbench.scm.folder%",
"when": "config.git.enabled && !git.missing && workbenchState == folder"
},
{
"view": "workbench.scm",
"contents": "%view.workbench.scm.workspace%",
"when": "config.git.enabled && !git.missing && workbenchState == workspace"
}
]
},
"dependencies": {
"byline": "^5.0.0",
......
......@@ -149,5 +149,10 @@
"colors.untracked": "Color for untracked resources.",
"colors.ignored": "Color for ignored resources.",
"colors.conflict": "Color for resources with conflicts.",
"colors.submodule": "Color for submodule resources."
"colors.submodule": "Color for submodule resources.",
"view.workbench.scm.missing": "A valid git installation was not detected, more details can be found in the [git output](command:git.showOutput).\nPlease [install git](https://git-scm.com/), or learn more about how to use Git and source control in VS Code in [our docs](https://aka.ms/vscode-scm).\nIf you're using a different version control system, you can [search the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22) for additional extensions.",
"view.workbench.scm.disabled": "If you would like to use git features, please enable git in your [settings](command:workbench.action.openSettings?%5B%22git.enabled%22%5D).\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.empty": "In order to use git features, you can open a folder containing a git repository or clone from a URL.\n[Open Folder](command:vscode.openFolder)\n[Clone from URL](command:git.clone)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.folder": "The folder currently open doesn't have a git repository.\n[Initialize Repository](command:git.init)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Initialize Repository](command:git.init)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm)."
}
......@@ -175,6 +175,7 @@ export async function activate(context: ExtensionContext): Promise<GitExtension>
console.warn(err.message);
outputChannel.appendLine(err.message);
commands.executeCommand('setContext', 'git.missing', true);
warnAboutMissingGit();
return new GitExtensionImpl();
......
......@@ -71,7 +71,7 @@
display: none;
}
.monaco-workbench .pane > .pane-body > .empty-view {
.monaco-workbench .pane > .pane-body > .welcome-view {
width: 100%;
height: 100%;
padding: 0 20px 0 20px;
......@@ -79,12 +79,12 @@
box-sizing: border-box;
}
.monaco-workbench .pane > .pane-body:not(.empty) > .empty-view,
.monaco-workbench .pane > .pane-body.empty > :not(.empty-view) {
.monaco-workbench .pane > .pane-body:not(.welcome) > .welcome-view,
.monaco-workbench .pane > .pane-body.welcome > :not(.welcome-view) {
display: none;
}
.monaco-workbench .pane > .pane-body > .empty-view .monaco-button {
.monaco-workbench .pane > .pane-body > .welcome-view .monaco-button {
max-width: 260px;
margin-left: auto;
margin-right: auto;
......
......@@ -25,7 +25,7 @@ import { PaneView, IPaneViewOptions, IPaneOptions, Pane, DefaultPaneDndControlle
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewContainersRegistry, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry } from 'vs/workbench/common/views';
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewContainersRegistry, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor } from 'vs/workbench/common/views';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { assertIsDefined } from 'vs/base/common/types';
......@@ -60,6 +60,88 @@ export interface IViewPaneOptions extends IPaneOptions {
const viewsRegistry = Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry);
interface IItem {
readonly descriptor: IViewContentDescriptor;
visible: boolean;
}
class ViewWelcomeController {
private _onDidChange = new Emitter<void>();
readonly onDidChange = this._onDidChange.event;
private defaultItem: IItem | undefined;
private items: IItem[] = [];
get contents(): IViewContentDescriptor[] {
const visibleItems = this.items.filter(v => v.visible);
if (visibleItems.length === 0 && this.defaultItem) {
return [this.defaultItem.descriptor];
}
return visibleItems.map(v => v.descriptor);
}
private contextKeyService: IContextKeyService;
private disposables = new DisposableStore();
constructor(
private id: string,
@IContextKeyService contextKeyService: IContextKeyService,
) {
this.contextKeyService = contextKeyService.createScoped();
this.disposables.add(this.contextKeyService);
contextKeyService.onDidChangeContext(this.onDidChangeContext, this, this.disposables);
Event.filter(viewsRegistry.onDidChangeViewWelcomeContent, id => id === this.id)(this.onDidChangeViewWelcomeContent, this, this.disposables);
this.onDidChangeViewWelcomeContent();
}
private onDidChangeViewWelcomeContent(): void {
const descriptors = viewsRegistry.getViewWelcomeContent(this.id);
this.items = [];
for (const descriptor of descriptors) {
if (descriptor.when === 'default') {
this.defaultItem = { descriptor, visible: true };
} else {
const visible = descriptor.when ? this.contextKeyService.contextMatchesRules(descriptor.when) : true;
this.items.push({ descriptor, visible });
}
}
this._onDidChange.fire();
}
private onDidChangeContext(): void {
let didChange = false;
for (const item of this.items) {
if (!item.descriptor.when || item.descriptor.when === 'default') {
continue;
}
const visible = this.contextKeyService.contextMatchesRules(item.descriptor.when);
if (item.visible === visible) {
continue;
}
item.visible = visible;
didChange = true;
}
if (didChange) {
this._onDidChange.fire();
}
}
dispose(): void {
this.disposables.dispose();
}
}
export abstract class ViewPane extends Pane implements IView {
private static readonly AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions';
......@@ -76,8 +158,8 @@ export abstract class ViewPane extends Pane implements IView {
protected _onDidChangeTitleArea = this._register(new Emitter<void>());
readonly onDidChangeTitleArea: Event<void> = this._onDidChangeTitleArea.event;
protected _onDidChangeEmptyState = this._register(new Emitter<void>());
readonly onDidChangeEmptyState: Event<void> = this._onDidChangeEmptyState.event;
protected _onDidChangeViewWelcomeState = this._register(new Emitter<void>());
readonly onDidChangeViewWelcomeState: Event<void> = this._onDidChangeViewWelcomeState.event;
private focusedViewContextKey: IContextKey<string>;
......@@ -95,8 +177,9 @@ export abstract class ViewPane extends Pane implements IView {
protected twistiesContainer?: HTMLElement;
private bodyContainer!: HTMLElement;
private emptyViewContainer!: HTMLElement;
private emptyViewDisposable: IDisposable = Disposable.None;
private viewWelcomeContainer!: HTMLElement;
private viewWelcomeDisposable: IDisposable = Disposable.None;
private viewWelcomeController: ViewWelcomeController;
constructor(
options: IViewPaneOptions,
......@@ -119,6 +202,8 @@ export abstract class ViewPane extends Pane implements IView {
this.menuActions = this._register(instantiationService.createInstance(ViewMenuActions, this.id, options.titleMenuId || MenuId.ViewTitle, MenuId.ViewTitleContext));
this._register(this.menuActions.onDidChangeTitle(() => this.updateActions()));
this.viewWelcomeController = new ViewWelcomeController(this.id, contextKeyService);
}
setVisible(visible: boolean): void {
......@@ -206,18 +291,15 @@ export abstract class ViewPane extends Pane implements IView {
protected renderBody(container: HTMLElement): void {
this.bodyContainer = container;
this.emptyViewContainer = append(container, $('.empty-view', { tabIndex: 0 }));
this.viewWelcomeContainer = append(container, $('.welcome-view', { tabIndex: 0 }));
// we should update our empty state whenever
const onEmptyViewContentChange = Event.any(
// the registry changes
Event.map(Event.filter(viewsRegistry.onDidChangeEmptyViewContent, id => id === this.id), () => this.isEmpty()),
// or the view's empty state changes
Event.latch(Event.map(this.onDidChangeEmptyState, () => this.isEmpty()))
);
const onViewWelcomeChange = Event.any(this.viewWelcomeController.onDidChange, this.onDidChangeViewWelcomeState);
this._register(onViewWelcomeChange(this.updateViewWelcome, this));
this.updateViewWelcome();
}
this._register(onEmptyViewContentChange(this.updateEmptyState, this));
this.updateEmptyState(this.isEmpty());
protected layoutBody(height: number, width: number): void {
// noop
}
protected getProgressLocation(): string {
......@@ -286,26 +368,26 @@ export abstract class ViewPane extends Pane implements IView {
// Subclasses to implement for saving state
}
private updateEmptyState(isEmpty: boolean): void {
this.emptyViewDisposable.dispose();
private updateViewWelcome(): void {
this.viewWelcomeDisposable.dispose();
if (!isEmpty) {
removeClass(this.bodyContainer, 'empty');
this.emptyViewContainer.innerHTML = '';
if (!this.shouldShowWelcome()) {
removeClass(this.bodyContainer, 'welcome');
this.viewWelcomeContainer.innerHTML = '';
return;
}
const contents = viewsRegistry.getEmptyViewContent(this.id);
const contents = this.viewWelcomeController.contents;
if (contents.length === 0) {
removeClass(this.bodyContainer, 'empty');
this.emptyViewContainer.innerHTML = '';
removeClass(this.bodyContainer, 'welcome');
this.viewWelcomeContainer.innerHTML = '';
return;
}
const disposables = new DisposableStore();
addClass(this.bodyContainer, 'empty');
this.emptyViewContainer.innerHTML = '';
addClass(this.bodyContainer, 'welcome');
this.viewWelcomeContainer.innerHTML = '';
for (const { content } of contents) {
const lines = content.split('\n');
......@@ -317,7 +399,7 @@ export abstract class ViewPane extends Pane implements IView {
continue;
}
const p = append(this.emptyViewContainer, $('p'));
const p = append(this.viewWelcomeContainer, $('p'));
const linkedText = parseLinkedText(line);
for (const node of linkedText) {
......@@ -339,10 +421,10 @@ export abstract class ViewPane extends Pane implements IView {
}
}
this.emptyViewDisposable = disposables;
this.viewWelcomeDisposable = disposables;
}
isEmpty(): boolean {
shouldShowWelcome(): boolean {
return false;
}
}
......
......@@ -213,6 +213,7 @@ export interface IViewDescriptorCollection extends IDisposable {
export interface IViewContentDescriptor {
readonly content: string;
readonly when?: ContextKeyExpr | 'default';
}
export interface IViewsRegistry {
......@@ -235,9 +236,13 @@ export interface IViewsRegistry {
getViewContainer(id: string): ViewContainer | null;
readonly onDidChangeEmptyViewContent: Event<string>;
registerEmptyViewContent(id: string, viewContent: IViewContentDescriptor): IDisposable;
getEmptyViewContent(id: string): IViewContentDescriptor[];
readonly onDidChangeViewWelcomeContent: Event<string>;
registerViewWelcomeContent(id: string, viewContent: IViewContentDescriptor): IDisposable;
getViewWelcomeContent(id: string): IViewContentDescriptor[];
}
function compareViewContentDescriptors(a: IViewContentDescriptor, b: IViewContentDescriptor): number {
return a.content < b.content ? -1 : 1;
}
class ViewsRegistry extends Disposable implements IViewsRegistry {
......@@ -251,12 +256,12 @@ class ViewsRegistry extends Disposable implements IViewsRegistry {
private readonly _onDidChangeContainer: Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = this._register(new Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>());
readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = this._onDidChangeContainer.event;
private readonly _onDidChangeEmptyViewContent: Emitter<string> = this._register(new Emitter<string>());
readonly onDidChangeEmptyViewContent: Event<string> = this._onDidChangeEmptyViewContent.event;
private readonly _onDidChangeViewWelcomeContent: Emitter<string> = this._register(new Emitter<string>());
readonly onDidChangeViewWelcomeContent: Event<string> = this._onDidChangeViewWelcomeContent.event;
private _viewContainers: ViewContainer[] = [];
private _views: Map<ViewContainer, IViewDescriptor[]> = new Map<ViewContainer, IViewDescriptor[]>();
private _emptyViewContents = new SetMap<string, IViewContentDescriptor>();
private _viewWelcomeContents = new SetMap<string, IViewContentDescriptor>();
registerViews(views: IViewDescriptor[], viewContainer: ViewContainer): void {
this.addViews(views, viewContainer);
......@@ -306,19 +311,20 @@ class ViewsRegistry extends Disposable implements IViewsRegistry {
return null;
}
registerEmptyViewContent(id: string, viewContent: IViewContentDescriptor): IDisposable {
this._emptyViewContents.add(id, viewContent);
this._onDidChangeEmptyViewContent.fire(id);
registerViewWelcomeContent(id: string, viewContent: IViewContentDescriptor): IDisposable {
this._viewWelcomeContents.add(id, viewContent);
this._onDidChangeViewWelcomeContent.fire(id);
return toDisposable(() => {
this._emptyViewContents.delete(id, viewContent);
this._onDidChangeEmptyViewContent.fire(id);
this._viewWelcomeContents.delete(id, viewContent);
this._onDidChangeViewWelcomeContent.fire(id);
});
}
getEmptyViewContent(id: string): IViewContentDescriptor[] {
getViewWelcomeContent(id: string): IViewContentDescriptor[] {
const result: IViewContentDescriptor[] = [];
this._emptyViewContents.forEach(id, descriptor => result.push(descriptor));
result.sort(compareViewContentDescriptors);
this._viewWelcomeContents.forEach(id, descriptor => result.push(descriptor));
return result;
}
......
......@@ -14,7 +14,7 @@ import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } fro
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { VIEWLET_ID, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions';
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService';
import {
OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction,
......@@ -47,6 +47,7 @@ import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewConta
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
// Singletons
registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService);
......@@ -290,6 +291,30 @@ CommandsRegistry.registerCommand({
}
});
CommandsRegistry.registerCommand({
id: 'workbench.extensions.search',
description: {
description: localize('workbench.extensions.search.description', "Search for a specific extension"),
args: [
{
name: localize('workbench.extensions.search.arg.name', "Query to use in search"),
schema: { 'type': 'string' }
}
]
},
handler: async (accessor, query: string = '') => {
const viewletService = accessor.get(IViewletService);
const viewlet = await viewletService.openViewlet(VIEWLET_ID, true);
if (!viewlet) {
return;
}
(viewlet.getViewPaneContainer() as IExtensionsViewPaneContainer).search(query);
viewlet.focus();
}
});
// File menu registration
MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, {
......
......@@ -8,17 +8,6 @@
flex: 1;
}
.scm-viewlet .empty-message {
box-sizing: border-box;
height: 100%;
padding: 10px 22px 0 22px;
}
.scm-viewlet:not(.empty) .empty-message,
.scm-viewlet.empty .monaco-pane-view {
display: none;
}
.scm-viewlet .scm-status {
height: 100%;
position: relative;
......
......@@ -615,7 +615,7 @@ export class RepositoryPane extends ViewPane {
protected contextKeyService: IContextKeyService;
private commitTemplate = '';
isEmpty() { return true; }
shouldShowWelcome() { return true; }
constructor(
readonly repository: ISCMRepository,
......
......@@ -6,12 +6,11 @@
import 'vs/css!./media/scmViewlet';
import { localize } from 'vs/nls';
import { Event, Emitter } from 'vs/base/common/event';
import { append, $, toggleClass, addClasses } from 'vs/base/browser/dom';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { VIEWLET_ID, ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { MenuItemAction } from 'vs/platform/actions/common/actions';
......@@ -25,13 +24,16 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IViewsRegistry, Extensions, IViewDescriptorService } from 'vs/workbench/common/views';
import { IViewsRegistry, Extensions, IViewDescriptorService, IViewDescriptor } from 'vs/workbench/common/views';
import { Registry } from 'vs/platform/registry/common/platform';
import { RepositoryPane, RepositoryViewDescriptor } from 'vs/workbench/contrib/scm/browser/repositoryPane';
import { MainPaneDescriptor, MainPane, IViewModel } from 'vs/workbench/contrib/scm/browser/mainPane';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { ViewPaneContainer, IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import type { IAddedViewDescriptorRef, IViewDescriptorRef } from 'vs/workbench/browser/parts/views/views';
import { debounce } from 'vs/base/common/decorators';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { addClass } from 'vs/base/browser/dom';
export interface ISpliceEvent<T> {
index: number;
......@@ -39,12 +41,45 @@ export interface ISpliceEvent<T> {
elements: T[];
}
export class EmptyPane extends ViewPane {
static readonly ID = 'workbench.scm';
static readonly TITLE = localize('scm providers', "Source Control Providers");
constructor(
options: IViewPaneOptions,
@IKeybindingService keybindingService: IKeybindingService,
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@IInstantiationService instantiationService: IInstantiationService,
@IOpenerService openerService: IOpenerService,
@IThemeService themeService: IThemeService,
) {
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService);
}
shouldShowWelcome(): boolean {
return true;
}
}
export class EmptyPaneDescriptor implements IViewDescriptor {
readonly id = EmptyPane.ID;
readonly name = EmptyPane.TITLE;
readonly ctorDescriptor = new SyncDescriptor(EmptyPane);
readonly canToggleVisibility = true;
readonly hideByDefault = false;
readonly order = -1000;
readonly workspace = true;
readonly when = ContextKeyExpr.equals('scm.providerCount', 0);
}
export class SCMViewPaneContainer extends ViewPaneContainer implements IViewModel {
private static readonly STATE_KEY = 'workbench.scm.views.state';
private el!: HTMLElement;
private message: HTMLElement;
private menus: SCMMenus;
private _repositories: ISCMRepository[] = [];
......@@ -94,9 +129,14 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode
this.menus = instantiationService.createInstance(SCMMenus, undefined);
this._register(this.menus.onDidChangeTitle(this.updateTitleArea, this));
this.message = $('.empty-message', { tabIndex: 0 }, localize('no open repo', "No source control providers registered."));
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
viewsRegistry.registerViewWelcomeContent(EmptyPane.ID, {
content: localize('no open repo', "No source control providers registered."),
when: 'default'
});
viewsRegistry.registerViews([new EmptyPaneDescriptor()], this.viewContainer);
viewsRegistry.registerViews([new MainPaneDescriptor(this)], this.viewContainer);
this._register(configurationService.onDidChangeConfiguration(e => {
......@@ -113,11 +153,7 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode
create(parent: HTMLElement): void {
super.create(parent);
this.el = parent;
addClasses(parent, 'scm-viewlet', 'empty');
append(parent, this.message);
addClass(parent, 'scm-viewlet');
this._register(this.scmService.onDidAddRepository(this.onDidAddRepository, this));
this._register(this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this));
this.scmService.repositories.forEach(r => this.onDidAddRepository(r));
......@@ -156,9 +192,7 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode
}
private onDidChangeRepositories(): void {
const repositoryCount = this.repositories.length;
toggleClass(this.el, 'empty', repositoryCount === 0);
this.repositoryCountKey.set(repositoryCount);
this.repositoryCountKey.set(this.repositories.length);
}
private onDidShowView(e: IAddedViewDescriptorRef[]): void {
......@@ -187,23 +221,19 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode
}
focus(): void {
if (this.repositoryCountKey.get()! === 0) {
this.message.focus();
} else {
const repository = this.visibleRepositories[0];
const repository = this.visibleRepositories[0];
if (repository) {
const pane = this.panes
.filter(pane => pane instanceof RepositoryPane && pane.repository === repository)[0] as RepositoryPane | undefined;
if (repository) {
const pane = this.panes
.filter(pane => pane instanceof RepositoryPane && pane.repository === repository)[0] as RepositoryPane | undefined;
if (pane) {
pane.focus();
} else {
super.focus();
}
if (pane) {
pane.focus();
} else {
super.focus();
}
} else {
super.focus();
}
}
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { ViewsWelcomeContribution } from 'vs/workbench/contrib/welcome/common/viewsWelcomeContribution';
import { ViewsWelcomeExtensionPoint, viewsWelcomeExtensionPointDescriptor } from 'vs/workbench/contrib/welcome/common/viewsWelcomeExtensionPoint';
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
const extensionPoint = ExtensionsRegistry.registerExtensionPoint<ViewsWelcomeExtensionPoint>(viewsWelcomeExtensionPointDescriptor);
class WorkbenchConfigurationContribution {
constructor(
@IInstantiationService instantiationService: IInstantiationService,
) {
instantiationService.createInstance(ViewsWelcomeContribution, extensionPoint);
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(WorkbenchConfigurationContribution, LifecyclePhase.Eventually);
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
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 { ViewsWelcomeExtensionPoint, ViewWelcome, viewsWelcomeExtensionPointDescriptor } from './viewsWelcomeExtensionPoint';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as ViewContainerExtensions, IViewsRegistry } from 'vs/workbench/common/views';
import { localize } from 'vs/nls';
const viewsRegistry = Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry);
export class ViewsWelcomeContribution extends Disposable implements IWorkbenchContribution {
private viewWelcomeContents = new Map<ViewWelcome, IDisposable>();
constructor(extensionPoint: IExtensionPoint<ViewsWelcomeExtensionPoint>) {
super();
extensionPoint.setHandler((_, { added, removed }) => {
for (const contribution of removed) {
// Proposed API check
if (!contribution.description.enableProposedApi) {
continue;
}
for (const welcome of contribution.value) {
const disposable = this.viewWelcomeContents.get(welcome);
if (disposable) {
disposable.dispose();
}
}
}
for (const contribution of added) {
// Proposed API check
if (!contribution.description.enableProposedApi) {
contribution.collector.error(localize('proposedAPI.invalid', "The '{0}' contribution is a proposed API and is only available when running out of dev or with the following command line switch: --enable-proposed-api {1}", viewsWelcomeExtensionPointDescriptor.extensionPoint, contribution.description.identifier.value));
continue;
}
for (const welcome of contribution.value) {
const disposable = viewsRegistry.registerViewWelcomeContent(welcome.view, {
content: welcome.contents,
when: ContextKeyExpr.deserialize(welcome.when)
});
this.viewWelcomeContents.set(welcome, disposable);
}
}
});
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
export enum ViewsWelcomeExtensionPointFields {
view = 'view',
contents = 'contents',
when = 'when',
}
export interface ViewWelcome {
readonly [ViewsWelcomeExtensionPointFields.view]: string;
readonly [ViewsWelcomeExtensionPointFields.contents]: string;
readonly [ViewsWelcomeExtensionPointFields.when]: string;
}
export type ViewsWelcomeExtensionPoint = ViewWelcome[];
const viewsWelcomeExtensionPointSchema = Object.freeze<IConfigurationPropertySchema>({
type: 'array',
description: nls.localize('contributes.viewsWelcome', "Contributed views welcome content."),
items: {
type: 'object',
description: nls.localize('contributes.viewsWelcome.view', "Contributed welcome content for a specific view."),
required: [
ViewsWelcomeExtensionPointFields.view,
ViewsWelcomeExtensionPointFields.contents
],
properties: {
[ViewsWelcomeExtensionPointFields.view]: {
type: 'string',
description: nls.localize('contributes.viewsWelcome.view.view', "View identifier for this welcome content."),
},
[ViewsWelcomeExtensionPointFields.contents]: {
type: 'string',
description: nls.localize('contributes.viewsWelcome.view.contents', "Welcome content."),
},
[ViewsWelcomeExtensionPointFields.when]: {
type: 'string',
description: nls.localize('contributes.viewsWelcome.view.when', "When clause for this welcome content."),
},
}
}
});
export const viewsWelcomeExtensionPointDescriptor = {
extensionPoint: 'viewsWelcome',
jsonSchema: viewsWelcomeExtensionPointSchema
};
......@@ -272,6 +272,9 @@ import 'vs/workbench/contrib/userDataSync/browser/userDataSync.contribution';
// Code Actions
import 'vs/workbench/contrib/codeActions/common/codeActions.contribution';
// Welcome
import 'vs/workbench/contrib/welcome/common/viewsWelcome.contribution';
// Timeline
import 'vs/workbench/contrib/timeline/browser/timeline.contribution';
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册