Unify into single ViewContainerModel

This commit is contained in:
Sandeep Somavarapu
2020-04-20 20:26:56 +02:00
parent 9275e7ed56
commit 116a3de416
15 changed files with 951 additions and 1011 deletions
@@ -488,9 +488,9 @@ export class ResetViewLocationsAction extends Action {
async run(): Promise<void> {
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
viewContainerRegistry.all.forEach(viewContainer => {
const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer);
const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer);
viewDescriptors.allViewDescriptors.forEach(viewDescriptor => {
viewContainerModel.allViewDescriptors.forEach(viewDescriptor => {
const defaultContainer = this.viewDescriptorService.getDefaultContainer(viewDescriptor.id);
const currentContainer = this.viewDescriptorService.getViewContainer(viewDescriptor.id);
@@ -583,7 +583,7 @@ export class MoveFocusedViewAction extends Action {
const currentContainer = this.viewDescriptorService.getViewContainer(focusedViewId)!;
const currentLocation = this.viewDescriptorService.getViewLocation(focusedViewId)!;
const isViewSolo = this.viewDescriptorService.getViewDescriptors(currentContainer).allViewDescriptors.length === 1;
const isViewSolo = this.viewDescriptorService.getViewContainerModel(currentContainer).allViewDescriptors.length === 1;
if (!(isViewSolo && currentLocation === ViewContainerLocation.Sidebar)) {
items.push({
@@ -26,7 +26,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { URI, UriComponents } from 'vs/base/common/uri';
import { ToggleCompositePinnedAction, ICompositeBarColors, ActivityAction, ICompositeActivity } from 'vs/workbench/browser/parts/compositeBarActions';
import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
import { IViewDescriptorService, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewDescriptorCollection, ViewContainerLocation } from 'vs/workbench/common/views';
import { IViewDescriptorService, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewContainerModel, ViewContainerLocation } from 'vs/workbench/common/views';
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IViewlet } from 'vs/workbench/common/viewlet';
import { isUndefinedOrNull, assertIsDefined, isString } from 'vs/base/common/types';
@@ -218,8 +218,8 @@ export class ActivitybarPart extends Part implements IActivityBarService {
if (viewletDescriptor) {
const viewContainer = this.getViewContainer(viewletDescriptor.id);
if (viewContainer?.hideIfEmpty) {
const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer);
if (viewDescriptors.activeViewDescriptors.length === 0) {
const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer);
if (viewContainerModel.activeViewDescriptors.length === 0) {
this.hideComposite(viewletDescriptor.id); // Update the composite bar by hiding
}
}
@@ -490,13 +490,12 @@ export class ActivitybarPart extends Part implements IActivityBarService {
for (const viewlet of viewlets) {
this.enableCompositeActions(viewlet);
const viewContainer = this.getViewContainer(viewlet.id)!;
const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer);
this.onDidChangeActiveViews(viewlet, viewDescriptors, viewContainer.hideIfEmpty);
const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer);
this.onDidChangeActiveViews(viewlet, viewContainerModel, viewContainer.hideIfEmpty);
const disposables = new DisposableStore();
disposables.add(viewDescriptors.onDidChangeActiveViews(() => this.onDidChangeActiveViews(viewlet, viewDescriptors, viewContainer.hideIfEmpty)));
disposables.add(viewDescriptors.onDidChangeViews(() => this.onDidUpdateViews(viewlet, viewDescriptors)));
disposables.add(viewDescriptors.onDidMove(() => this.onDidUpdateViews(viewlet, viewDescriptors)));
disposables.add(viewContainerModel.onDidChangeActiveViewDescriptors(() => this.onDidChangeActiveViews(viewlet, viewContainerModel, viewContainer.hideIfEmpty)));
disposables.add(viewContainerModel.onDidChangeContainerInfo(() => this.updateActivity(viewlet, viewContainerModel)));
this.viewletDisposables.set(viewlet.id, disposables);
}
@@ -512,12 +511,12 @@ export class ActivitybarPart extends Part implements IActivityBarService {
this.hideComposite(viewletId);
}
private updateActivity(viewlet: ViewletDescriptor, viewDescriptors: IViewDescriptorCollection): void {
const icon = viewDescriptors.getIcon();
private updateActivity(viewlet: ViewletDescriptor, viewContainerModel: IViewContainerModel): void {
const icon = viewContainerModel.icon;
const activity: IActivity = {
id: viewlet.id,
name: viewDescriptors.getTitle(),
name: viewContainerModel.title,
cssClass: isString(icon) ? icon : undefined,
iconUrl: icon instanceof URI ? icon : undefined,
keybindingId: viewlet.keybindingId
@@ -531,11 +530,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
}
}
private onDidUpdateViews(viewlet: ViewletDescriptor, viewDescriptors: IViewDescriptorCollection): void {
this.updateActivity(viewlet, viewDescriptors);
}
private onDidChangeActiveViews(viewlet: ViewletDescriptor, viewDescriptors: IViewDescriptorCollection, hideIfEmpty?: boolean): void {
private onDidChangeActiveViews(viewlet: ViewletDescriptor, viewDescriptors: IViewContainerModel, hideIfEmpty?: boolean): void {
if (viewDescriptors.activeViewDescriptors.length) {
this.updateActivity(viewlet, viewDescriptors);
this.compositeBar.addComposite(viewlet);
@@ -663,8 +658,8 @@ export class ActivitybarPart extends Part implements IActivityBarService {
if (viewlet) {
const views: { when: string | undefined }[] = [];
if (viewContainer) {
const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer);
for (const { when } of viewDescriptors.allViewDescriptors) {
const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer);
for (const { when } of viewContainerModel.allViewDescriptors) {
views.push({ when: when ? when.serialize() : undefined });
}
}
@@ -56,7 +56,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
}
// ... on a different composite bar
else {
const viewsToMove = this.viewDescriptorService.getViewDescriptors(currentContainer)!.allViewDescriptors;
const viewsToMove = this.viewDescriptorService.getViewContainerModel(currentContainer)!.allViewDescriptors;
if (viewsToMove.some(v => !v.canMoveView)) {
return;
}
@@ -119,7 +119,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
}
// ... to another composite location
const draggedViews = this.viewDescriptorService.getViewDescriptors(currentContainer)!.allViewDescriptors;
const draggedViews = this.viewDescriptorService.getViewContainerModel(currentContainer)!.allViewDescriptors;
// ... all views must be movable
return !draggedViews.some(v => !v.canMoveView);
@@ -33,7 +33,7 @@ import { IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/con
import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, IViewDescriptorCollection, ViewContainerLocation } from 'vs/workbench/common/views';
import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, IViewContainerModel, ViewContainerLocation } from 'vs/workbench/common/views';
import { MenuId } from 'vs/platform/actions/common/actions';
import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions';
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
@@ -178,9 +178,9 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
const result: IAction[] = [];
const container = this.getViewContainer(compositeId);
if (container) {
const viewDescriptors = this.viewDescriptorService.getViewDescriptors(container);
if (viewDescriptors.allViewDescriptors.length === 1) {
const viewMenuActions = this.instantiationService.createInstance(ViewMenuActions, viewDescriptors.allViewDescriptors[0].id, MenuId.ViewTitle, MenuId.ViewTitleContext);
const viewContainerModel = this.viewDescriptorService.getViewContainerModel(container);
if (viewContainerModel.allViewDescriptors.length === 1) {
const viewMenuActions = this.instantiationService.createInstance(ViewMenuActions, viewContainerModel.allViewDescriptors[0].id, MenuId.ViewTitle, MenuId.ViewTitleContext);
result.push(...viewMenuActions.getContextMenuActions());
viewMenuActions.dispose();
}
@@ -211,13 +211,13 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
for (const panel of panels) {
this.enableCompositeActions(panel);
const viewContainer = this.getViewContainer(panel.id)!;
const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer);
this.onDidChangeActiveViews(panel, viewDescriptors, viewContainer.hideIfEmpty);
const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer);
this.onDidChangeActiveViews(panel, viewContainerModel, viewContainer.hideIfEmpty);
const disposables = new DisposableStore();
disposables.add(viewDescriptors.onDidChangeActiveViews(() => this.onDidChangeActiveViews(panel, viewDescriptors, viewContainer.hideIfEmpty)));
disposables.add(viewDescriptors.onDidChangeViews(() => this.onDidUpdateViews(panel, viewDescriptors)));
disposables.add(viewDescriptors.onDidMove(() => this.onDidUpdateViews(panel, viewDescriptors)));
disposables.add(viewContainerModel.onDidChangeActiveViewDescriptors(() => this.onDidChangeActiveViews(panel, viewContainerModel, viewContainer.hideIfEmpty)));
disposables.add(viewContainerModel.onDidChangeAllViewDescriptors(() => this.onDidUpdateViews(panel, viewContainerModel)));
disposables.add(viewContainerModel.onDidMoveVisibleViewDescriptors(() => this.onDidUpdateViews(panel, viewContainerModel)));
this.panelDisposables.set(panel.id, disposables);
}
@@ -242,10 +242,10 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
}
}
private updateActivity(panel: PanelDescriptor, viewDescriptors: IViewDescriptorCollection): void {
private updateActivity(panel: PanelDescriptor, viewContainerModel: IViewContainerModel): void {
const activity: IActivity = {
id: panel.id,
name: viewDescriptors.getTitle(),
name: viewContainerModel.title,
keybindingId: panel.keybindingId
};
@@ -257,11 +257,11 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
}
}
private onDidUpdateViews(panel: PanelDescriptor, viewDescriptors: IViewDescriptorCollection): void {
private onDidUpdateViews(panel: PanelDescriptor, viewDescriptors: IViewContainerModel): void {
this.updateActivity(panel, viewDescriptors);
}
private onDidChangeActiveViews(panel: PanelDescriptor, viewDescriptors: IViewDescriptorCollection, hideIfEmpty?: boolean): void {
private onDidChangeActiveViews(panel: PanelDescriptor, viewDescriptors: IViewContainerModel, hideIfEmpty?: boolean): void {
if (viewDescriptors.activeViewDescriptors.length) {
this.updateActivity(panel, viewDescriptors);
this.compositeBar.addComposite(panel);
@@ -343,8 +343,8 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
if (panelDescriptor) {
const viewContainer = this.getViewContainer(panelDescriptor.id);
if (viewContainer?.hideIfEmpty) {
const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer);
if (viewDescriptors.activeViewDescriptors.length === 0 && this.compositeBar.getPinnedComposites().length > 1) {
const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer);
if (viewContainerModel.activeViewDescriptors.length === 0 && this.compositeBar.getPinnedComposites().length > 1) {
this.hideComposite(panelDescriptor.id); // Update the composite bar by hiding
}
}
@@ -610,10 +610,11 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
const compositeItems = this.compositeBar.getCompositeBarItems();
for (const compositeItem of compositeItems) {
const viewContainer = this.getViewContainer(compositeItem.id)!;
const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer);
state.push({ id: compositeItem.id, name: viewDescriptors.getTitle(), pinned: compositeItem.pinned, order: compositeItem.order, visible: compositeItem.visible });
const viewContainer = this.getViewContainer(compositeItem.id);
if (viewContainer) {
const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer);
state.push({ id: compositeItem.id, name: viewContainerModel.title, pinned: compositeItem.pinned, order: compositeItem.order, visible: compositeItem.visible });
}
}
this.cachedPanelsValue = JSON.stringify(state);
@@ -24,7 +24,7 @@ import { PaneView, IPaneViewOptions, IPaneOptions, Pane } from 'vs/base/browser/
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkbenchLayoutService, Position } 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, IViewContentDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef, IViewDescriptorCollection } from 'vs/workbench/common/views';
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewContainersRegistry, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef, IViewContainerModel } 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';
@@ -745,7 +745,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
private readonly visibleViewsCountFromCache: number | undefined;
private readonly visibleViewsStorageId: string;
protected readonly viewsDescriptors: IViewDescriptorCollection;
protected readonly viewContainerModel: IViewContainerModel;
private viewDisposables: IDisposable[] = [];
private readonly _onTitleAreaUpdate: Emitter<void> = this._register(new Emitter<void>());
@@ -806,7 +806,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
this.visibleViewsStorageId = `${id}.numberOfVisibleViews`;
this.visibleViewsCountFromCache = this.storageService.getNumber(this.visibleViewsStorageId, StorageScope.WORKSPACE, undefined);
this._register(toDisposable(() => this.viewDisposables = dispose(this.viewDisposables)));
this.viewsDescriptors = this.viewDescriptorService.getViewDescriptors(container);
this.viewContainerModel = this.viewDescriptorService.getViewContainerModel(container);
}
create(parent: HTMLElement): void {
@@ -837,7 +837,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
const container = viewContainerRegistry.get(dropData.id)!;
const viewsToMove = this.viewDescriptorService.getViewDescriptors(container).allViewDescriptors;
const viewsToMove = this.viewDescriptorService.getViewContainerModel(container).allViewDescriptors;
if (!viewsToMove.some(v => !v.canMoveView)) {
overlay = new ViewPaneDropOverlay(parent, undefined, this.themeService);
@@ -859,7 +859,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
const container = viewContainerRegistry.get(dropData.id)!;
const allViews = this.viewDescriptorService.getViewDescriptors(container).allViewDescriptors;
const allViews = this.viewDescriptorService.getViewContainerModel(container).allViewDescriptors;
if (!allViews.some(v => !v.canMoveView)) {
viewsToMove.push(...allViews);
}
@@ -882,11 +882,11 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
}));
this._register(this.onDidSashChange(() => this.saveViewSizes()));
this.viewsDescriptors.onDidAdd(added => this.onDidAddViewDescriptors(added));
this.viewsDescriptors.onDidRemove(removed => this.onDidRemoveViewDescriptors(removed));
const addedViews: IAddedViewDescriptorRef[] = this.viewsDescriptors.visibleViewDescriptors.map((viewDescriptor, index) => {
const size = this.viewsDescriptors.getSize(viewDescriptor.id);
const collapsed = this.viewsDescriptors.isCollapsed(viewDescriptor.id);
this.viewContainerModel.onDidAddVisibleViewDescriptors(added => this.onDidAddViewDescriptors(added));
this.viewContainerModel.onDidRemoveVisibleViewDescriptors(removed => this.onDidRemoveViewDescriptors(removed));
const addedViews: IAddedViewDescriptorRef[] = this.viewContainerModel.visibleViewDescriptors.map((viewDescriptor, index) => {
const size = this.viewContainerModel.getSize(viewDescriptor.id);
const collapsed = this.viewContainerModel.isCollapsed(viewDescriptor.id);
return ({ viewDescriptor, index, size, collapsed });
});
if (addedViews.length) {
@@ -904,7 +904,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
}
getTitle(): string {
const containerTitle = this.viewsDescriptors.getTitle();
const containerTitle = this.viewContainerModel.title;
if (this.isViewMergedWithContainer()) {
const paneItemTitle = this.paneItems[0].pane.title;
@@ -955,10 +955,10 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
}
}
const viewToggleActions = this.viewsDescriptors.activeViewDescriptors.map(viewDescriptor => (<IAction>{
const viewToggleActions = this.viewContainerModel.activeViewDescriptors.map(viewDescriptor => (<IAction>{
id: `${viewDescriptor.id}.toggleVisibility`,
label: viewDescriptor.name,
checked: this.viewsDescriptors.isVisible(viewDescriptor.id),
checked: this.viewContainerModel.isVisible(viewDescriptor.id),
enabled: viewDescriptor.canToggleVisibility,
run: () => this.toggleViewVisibility(viewDescriptor.id)
}));
@@ -1087,7 +1087,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
// Save size only when the layout has happened
if (this.didLayout) {
for (const view of this.panes) {
this.viewsDescriptors.setSize(view.id, this.getPaneSize(view));
this.viewContainerModel.setSize(view.id, this.getPaneSize(view));
}
}
}
@@ -1096,10 +1096,10 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
// Restore sizes only when the layout has happened
if (this.didLayout) {
let initialSizes;
for (let i = 0; i < this.viewsDescriptors.visibleViewDescriptors.length; i++) {
for (let i = 0; i < this.viewContainerModel.visibleViewDescriptors.length; i++) {
const pane = this.panes[i];
const viewDescriptor = this.viewsDescriptors.visibleViewDescriptors[i];
const size = this.viewsDescriptors.getSize(viewDescriptor.id);
const viewDescriptor = this.viewContainerModel.visibleViewDescriptors[i];
const size = this.viewContainerModel.getSize(viewDescriptor.id);
if (typeof size === 'number') {
this.resizePane(pane, size);
@@ -1114,8 +1114,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
private computeInitialSizes(): Map<string, number> {
const sizes: Map<string, number> = new Map<string, number>();
if (this.dimension) {
const totalWeight = this.viewsDescriptors.visibleViewDescriptors.reduce((totalWeight, { weight }) => totalWeight + (weight || 20), 0);
for (const viewDescriptor of this.viewsDescriptors.visibleViewDescriptors) {
const totalWeight = this.viewContainerModel.visibleViewDescriptors.reduce((totalWeight, { weight }) => totalWeight + (weight || 20), 0);
for (const viewDescriptor of this.viewContainerModel.visibleViewDescriptors) {
if (this.orientation === Orientation.VERTICAL) {
sizes.set(viewDescriptor.id, this.dimension.height * (viewDescriptor.weight || 20) / totalWeight);
} else {
@@ -1176,7 +1176,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
});
const collapseDisposable = Event.latch(Event.map(pane.onDidChange, () => !pane.isExpanded()))(collapsed => {
this.viewsDescriptors.setCollapsed(viewDescriptor.id, collapsed);
this.viewContainerModel.setCollapsed(viewDescriptor.id, collapsed);
});
this.viewDisposables.splice(index, 0, combinedDisposable(contextMenuDisposable, collapseDisposable));
@@ -1207,13 +1207,13 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
}
protected toggleViewVisibility(viewId: string): void {
const visible = !this.viewsDescriptors.isVisible(viewId);
const visible = !this.viewContainerModel.isVisible(viewId);
type ViewsToggleVisibilityClassification = {
viewId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
visible: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
this.telemetryService.publicLog2<{ viewId: String, visible: boolean }, ViewsToggleVisibilityClassification>('views.toggleVisibility', { viewId, visible });
this.viewsDescriptors.setVisible(viewId, visible);
this.viewContainerModel.setVisible(viewId, visible);
}
private addPane(pane: ViewPane, size: number, index = this.paneItems.length - 1): void {
@@ -1268,7 +1268,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
const container = viewContainerRegistry.get(dropData.id)!;
const viewsToMove = this.viewDescriptorService.getViewDescriptors(container).allViewDescriptors;
const viewsToMove = this.viewDescriptorService.getViewContainerModel(container).allViewDescriptors;
if (!viewsToMove.some(v => !v.canMoveView)) {
overlay = new ViewPaneDropOverlay(pane.dropTargetElement, this.orientation ?? Orientation.VERTICAL, this.themeService);
@@ -1290,7 +1290,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
const container = viewContainerRegistry.get(dropData.id)!;
const allViews = this.viewDescriptorService.getViewDescriptors(container).allViewDescriptors;
const allViews = this.viewDescriptorService.getViewContainerModel(container).allViewDescriptors;
if (allViews.length > 0 && !allViews.some(v => !v.canMoveView)) {
viewsToMove.push(...allViews);
@@ -1405,8 +1405,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
const fromIndex = firstIndex(this.paneItems, item => item.pane === from);
const toIndex = firstIndex(this.paneItems, item => item.pane === to);
const fromViewDescriptor = this.viewsDescriptors.visibleViewDescriptors[fromIndex];
const toViewDescriptor = this.viewsDescriptors.visibleViewDescriptors[toIndex];
const fromViewDescriptor = this.viewContainerModel.visibleViewDescriptors[fromIndex];
const toViewDescriptor = this.viewContainerModel.visibleViewDescriptors[toIndex];
if (fromIndex < 0 || fromIndex >= this.paneItems.length) {
return;
@@ -1421,7 +1421,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
assertIsDefined(this.paneview).movePane(from, to);
this.viewsDescriptors.move(fromViewDescriptor.id, toViewDescriptor.id);
this.viewContainerModel.move(fromViewDescriptor.id, toViewDescriptor.id);
this.updateTitleArea();
}
@@ -109,15 +109,15 @@ export class ViewsService extends Disposable implements IViewsService {
private onDidRegisterViewContainer(viewContainer: ViewContainer, viewContainerLocation: ViewContainerLocation): void {
this.registerViewletOrPanel(viewContainer, viewContainerLocation);
const viewDescriptorCollection = this.viewDescriptorService.getViewDescriptors(viewContainer);
this.onViewDescriptorsAdded(viewDescriptorCollection.allViewDescriptors, viewContainer);
this._register(viewDescriptorCollection.onDidChangeViews(({ added, removed }) => {
const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer);
this.onViewDescriptorsAdded(viewContainerModel.allViewDescriptors, viewContainer);
this._register(viewContainerModel.onDidChangeAllViewDescriptors(({ added, removed }) => {
this.onViewDescriptorsAdded(added, viewContainer);
this.onViewDescriptorsRemoved(removed);
}));
}
private onViewDescriptorsAdded(views: IViewDescriptor[], container: ViewContainer): void {
private onViewDescriptorsAdded(views: ReadonlyArray<IViewDescriptor>, container: ViewContainer): void {
const location = this.viewContainersRegistry.getViewContainerLocation(container);
if (location === undefined) {
return;
@@ -181,7 +181,7 @@ export class ViewsService extends Disposable implements IViewsService {
}
}
private onViewDescriptorsRemoved(views: IViewDescriptor[]): void {
private onViewDescriptorsRemoved(views: ReadonlyArray<IViewDescriptor>): void {
for (const view of views) {
const disposable = this.viewDisposable.get(view);
if (disposable) {
@@ -46,8 +46,8 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer {
this.onFilterChanged(newFilterValue);
}));
this._register(this.viewsDescriptors.onDidChangeActiveViews((viewDescriptors) => {
this.updateAllViews(viewDescriptors);
this._register(this.viewContainerModel.onDidChangeActiveViewDescriptors(() => {
this.updateAllViews(this.viewContainerModel.activeViewDescriptors);
}));
}
@@ -62,7 +62,7 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer {
}
this.allViews.get(filterOnValue)!.set(descriptor.id, descriptor);
if (this.filterValue && !this.filterValue.includes(filterOnValue)) {
this.viewsDescriptors.setVisible(descriptor.id, false);
this.viewContainerModel.setVisible(descriptor.id, false);
}
});
}
@@ -75,17 +75,17 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer {
private onFilterChanged(newFilterValue: string[]) {
if (this.allViews.size === 0) {
this.updateAllViews(this.viewsDescriptors.activeViewDescriptors);
this.updateAllViews(this.viewContainerModel.activeViewDescriptors);
}
this.getViewsNotForTarget(newFilterValue).forEach(item => this.viewsDescriptors.setVisible(item.id, false));
this.getViewsForTarget(newFilterValue).forEach(item => this.viewsDescriptors.setVisible(item.id, true));
this.getViewsNotForTarget(newFilterValue).forEach(item => this.viewContainerModel.setVisible(item.id, false));
this.getViewsForTarget(newFilterValue).forEach(item => this.viewContainerModel.setVisible(item.id, true));
}
getContextMenuActions(): IAction[] {
const result: IAction[] = Array.from(this.constantViewDescriptors.values()).map(viewDescriptor => (<IAction>{
id: `${viewDescriptor.id}.toggleVisibility`,
label: viewDescriptor.name,
checked: this.viewsDescriptors.isVisible(viewDescriptor.id),
checked: this.viewContainerModel.isVisible(viewDescriptor.id),
enabled: viewDescriptor.canToggleVisibility,
run: () => this.toggleViewVisibility(viewDescriptor.id)
}));
@@ -133,7 +133,7 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer {
}
// Check that allViews is ready
if (this.allViews.size === 0) {
this.updateAllViews(this.viewsDescriptors.activeViewDescriptors);
this.updateAllViews(this.viewContainerModel.activeViewDescriptors);
}
return panes;
}
+14 -13
View File
@@ -219,18 +219,22 @@ export interface IAddedViewDescriptorRef extends IViewDescriptorRef {
size?: number;
}
export interface IViewDescriptorCollection extends IDisposable {
export interface IViewContainerModel {
readonly allViewDescriptors: IViewDescriptor[];
readonly onDidChangeViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[] }>;
readonly title: string;
readonly icon: string | URI | undefined;
readonly onDidChangeContainerInfo: Event<{ title?: boolean, icon?: boolean }>;
readonly activeViewDescriptors: IViewDescriptor[];
readonly onDidChangeActiveViews: Event<ReadonlyArray<IViewDescriptor>>;
readonly allViewDescriptors: ReadonlyArray<IViewDescriptor>;
readonly onDidChangeAllViewDescriptors: Event<{ added: ReadonlyArray<IViewDescriptor>, removed: ReadonlyArray<IViewDescriptor> }>;
readonly visibleViewDescriptors: IViewDescriptor[];
readonly onDidAdd: Event<IAddedViewDescriptorRef[]>;
readonly onDidRemove: Event<IViewDescriptorRef[]>
readonly onDidMove: Event<{ from: IViewDescriptorRef; to: IViewDescriptorRef; }>
readonly activeViewDescriptors: ReadonlyArray<IViewDescriptor>;
readonly onDidChangeActiveViewDescriptors: Event<{ added: ReadonlyArray<IViewDescriptor>, removed: ReadonlyArray<IViewDescriptor> }>;
readonly visibleViewDescriptors: ReadonlyArray<IViewDescriptor>;
readonly onDidAddVisibleViewDescriptors: Event<IAddedViewDescriptorRef[]>;
readonly onDidRemoveVisibleViewDescriptors: Event<IViewDescriptorRef[]>
readonly onDidMoveVisibleViewDescriptors: Event<{ from: IViewDescriptorRef; to: IViewDescriptorRef; }>
isVisible(id: string): boolean;
setVisible(id: string, visible: boolean, size?: number): void;
@@ -241,9 +245,6 @@ export interface IViewDescriptorCollection extends IDisposable {
getSize(id: string): number | undefined;
setSize(id: string, size: number): void
getTitle(): string;
getIcon(): URI | string | undefined;
move(from: string, to: string): void;
}
@@ -484,7 +485,7 @@ export interface IViewDescriptorService {
moveViewsToContainer(views: IViewDescriptor[], viewContainer: ViewContainer): void;
getViewDescriptors(container: ViewContainer): IViewDescriptorCollection;
getViewContainerModel(container: ViewContainer): IViewContainerModel;
getViewDescriptor(viewId: string): IViewDescriptor | null;
@@ -96,9 +96,9 @@ export class ViewQuickAccessProvider extends PickerQuickAccessProvider<IViewQuic
const viewEntries: Array<IViewQuickPickItem> = [];
const getViewEntriesForViewlet = (viewlet: ViewletDescriptor, viewContainer: ViewContainer): IViewQuickPickItem[] => {
const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer);
const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer);
const result: IViewQuickPickItem[] = [];
for (const view of viewDescriptors.allViewDescriptors) {
for (const view of viewContainerModel.allViewDescriptors) {
if (this.contextKeyService.contextMatchesRules(view.when)) {
result.push({
label: view.name,
@@ -176,7 +176,7 @@ export class ViewQuickAccessProvider extends PickerQuickAccessProvider<IViewQuic
private includeViewContainer(container: ViewletDescriptor | IPanelIdentifier): boolean {
const viewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).get(container.id);
if (viewContainer?.hideIfEmpty) {
return this.viewDescriptorService.getViewDescriptors(viewContainer).activeViewDescriptors.length > 0;
return this.viewDescriptorService.getViewContainerModel(viewContainer).activeViewDescriptors.length > 0;
}
return true;
@@ -101,7 +101,7 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode
}
get onDidChangeVisibleRepositories(): Event<ISCMRepository[]> {
const modificationEvent = Event.debounce(Event.any(this.viewsDescriptors.onDidAdd, this.viewsDescriptors.onDidRemove), () => null, 0);
const modificationEvent = Event.debounce(Event.any(this.viewContainerModel.onDidAddVisibleViewDescriptors, this.viewContainerModel.onDidRemoveVisibleViewDescriptors), () => null, 0);
return Event.map(modificationEvent, () => this.visibleRepositories);
}
@@ -140,14 +140,14 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode
this._register(configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('scm.alwaysShowProviders') && configurationService.getValue<boolean>('scm.alwaysShowProviders')) {
this.viewsDescriptors.setVisible(MainPane.ID, true);
this.viewContainerModel.setVisible(MainPane.ID, true);
}
}));
this.repositoryCountKey = contextKeyService.createKey('scm.providerCount', 0);
this._register(this.viewsDescriptors.onDidAdd(this.onDidShowView, this));
this._register(this.viewsDescriptors.onDidRemove(this.onDidHideView, this));
this._register(this.viewContainerModel.onDidAddVisibleViewDescriptors(this.onDidShowView, this));
this._register(this.viewContainerModel.onDidRemoveVisibleViewDescriptors(this.onDidHideView, this));
}
create(parent: HTMLElement): void {
@@ -214,8 +214,8 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode
@debounce(0)
private afterOnDidHideView(): void {
if (this.repositoryCountKey.get()! > 0 && this.viewDescriptors.every(d => !this.viewsDescriptors.isVisible(d.id))) {
this.viewsDescriptors.setVisible(this.viewDescriptors[0].id, true);
if (this.repositoryCountKey.get()! > 0 && this.viewDescriptors.every(d => !this.viewContainerModel.isVisible(d.id))) {
this.viewContainerModel.setVisible(this.viewDescriptors[0].id, true);
}
}
@@ -282,9 +282,9 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode
}
setVisibleRepositories(repositories: ISCMRepository[]): void {
const visibleViewDescriptors = this.viewsDescriptors.visibleViewDescriptors;
const visibleViewDescriptors = this.viewContainerModel.visibleViewDescriptors;
const toSetVisible = this.viewsDescriptors.activeViewDescriptors
const toSetVisible = this.viewContainerModel.activeViewDescriptors
.filter((d): d is RepositoryViewDescriptor => d instanceof RepositoryViewDescriptor && repositories.indexOf(d.repository) > -1 && visibleViewDescriptors.indexOf(d) === -1);
const toSetInvisible = visibleViewDescriptors
@@ -302,11 +302,11 @@ export class SCMViewPaneContainer extends ViewPaneContainer implements IViewMode
}
}
this.viewsDescriptors.setVisible(viewDescriptor.id, false);
this.viewContainerModel.setVisible(viewDescriptor.id, false);
}
for (const viewDescriptor of toSetVisible) {
this.viewsDescriptors.setVisible(viewDescriptor.id, true, size);
this.viewContainerModel.setVisible(viewDescriptor.id, true, size);
}
}
}
@@ -704,7 +704,7 @@ export class TerminalService implements ITerminalService {
const location = this._viewDescriptorService.getViewLocation(TERMINAL_VIEW_ID);
if (location === ViewContainerLocation.Panel) {
const panel = this._viewDescriptorService.getViewContainer(TERMINAL_VIEW_ID);
if (panel && this._viewDescriptorService.getViewDescriptors(panel).activeViewDescriptors.length === 1) {
if (panel && this._viewDescriptorService.getViewContainerModel(panel).activeViewDescriptors.length === 1) {
this._layoutService.setPanelHidden(true);
}
}
@@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ViewContainerLocation, IViewDescriptorService, ViewContainer, IViewsRegistry, IViewContainersRegistry, IViewDescriptor, Extensions as ViewExtensions, IViewDescriptorCollection, IAddedViewDescriptorRef, IViewDescriptorRef } from 'vs/workbench/common/views';
import { IContextKey, RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ViewContainerLocation, IViewDescriptorService, ViewContainer, IViewsRegistry, IViewContainersRegistry, IViewDescriptor, Extensions as ViewExtensions, IViewContainerModel, IAddedViewDescriptorRef, IViewDescriptorRef } from 'vs/workbench/common/views';
import { IContextKey, RawContextKey, IContextKeyService, IReadableSet } from 'vs/platform/contextkey/common/contextkey';
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -17,68 +17,646 @@ import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { generateUuid } from 'vs/base/common/uuid';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { PersistentViewsModel, ViewDescriptorsModel } from 'vs/workbench/services/views/common/viewsModel';
import { URI } from 'vs/base/common/uri';
import { firstIndex, move } from 'vs/base/common/arrays';
import { isUndefined, isUndefinedOrNull } from 'vs/base/common/types';
import { values } from 'vs/base/common/map';
import { isEqual } from 'vs/base/common/resources';
class ViewDescriptorCollection extends Disposable implements IViewDescriptorCollection {
class CounterSet<T> implements IReadableSet<T> {
private readonly viewDescriptorsModel: ViewDescriptorsModel;
private readonly viewsModel: PersistentViewsModel;
private map = new Map<T, number>();
add(value: T): CounterSet<T> {
this.map.set(value, (this.map.get(value) || 0) + 1);
return this;
}
delete(value: T): boolean {
let counter = this.map.get(value) || 0;
if (counter === 0) {
return false;
}
counter--;
if (counter === 0) {
this.map.delete(value);
} else {
this.map.set(value, counter);
}
return true;
}
has(value: T): boolean {
return this.map.has(value);
}
}
interface IStoredWorkspaceViewState {
collapsed: boolean;
isHidden: boolean;
size?: number;
order?: number;
}
interface IStoredGlobalViewState {
id: string;
isHidden: boolean;
order?: number;
}
interface IViewDescriptorState {
visibleGlobal: boolean | undefined;
visibleWorkspace: boolean | undefined;
collapsed: boolean | undefined;
active: boolean
order?: number;
size?: number;
}
class ViewDescriptorsState extends Disposable {
private readonly workspaceViewsStateStorageId: string;
private readonly globalViewsStateStorageId: string;
private readonly state: Map<string, IViewDescriptorState>;
private _onDidChangeStoredState = this._register(new Emitter<{ id: string, visible: boolean }[]>());
readonly onDidChangeStoredState = this._onDidChangeStoredState.event;
constructor(
viewContainerStorageId: string,
@IStorageService private readonly storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
this.globalViewsStateStorageId = `${viewContainerStorageId}.hidden`;
this.workspaceViewsStateStorageId = viewContainerStorageId;
storageKeysSyncRegistryService.registerStorageKey({ key: this.globalViewsStateStorageId, version: 1 });
this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
this.state = this.initialize();
}
set(id: string, state: IViewDescriptorState): void {
this.state.set(id, state);
}
get(id: string): IViewDescriptorState | undefined {
return this.state.get(id);
}
updateState(viewDescriptors: ReadonlyArray<IViewDescriptor>): void {
this.updateWorkspaceState(viewDescriptors);
this.updateGlobalState(viewDescriptors);
}
private updateWorkspaceState(viewDescriptors: ReadonlyArray<IViewDescriptor>): void {
const storedViewsStates: { [id: string]: IStoredWorkspaceViewState; } = JSON.parse(this.storageService.get(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}'));
for (const viewDescriptor of viewDescriptors) {
const viewState = this.state.get(viewDescriptor.id);
if (viewState) {
storedViewsStates[viewDescriptor.id] = {
collapsed: !!viewState.collapsed,
isHidden: !viewState.visibleWorkspace,
size: viewState.size,
order: viewDescriptor.workspace && viewState ? viewState.order : undefined
};
}
}
if (Object.keys(storedViewsStates).length > 0) {
this.storageService.store(this.workspaceViewsStateStorageId, JSON.stringify(storedViewsStates), StorageScope.WORKSPACE);
} else {
this.storageService.remove(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE);
}
}
private updateGlobalState(viewDescriptors: ReadonlyArray<IViewDescriptor>): void {
const storedGlobalState = this.getStoredGlobalState();
for (const viewDescriptor of viewDescriptors) {
const state = this.state.get(viewDescriptor.id);
storedGlobalState.set(viewDescriptor.id, {
id: viewDescriptor.id,
isHidden: state && viewDescriptor.canToggleVisibility ? !state.visibleGlobal : false,
order: !viewDescriptor.workspace && state ? state.order : undefined
});
}
this.setStoredGlobalState(storedGlobalState);
}
private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void {
if (e.key === this.globalViewsStateStorageId && e.scope === StorageScope.GLOBAL
&& this.globalViewsStatesValue !== this.getStoredGlobalViewsStatesValue() /* This checks if current window changed the value or not */) {
this._globalViewsStatesValue = undefined;
const storedViewsVisibilityStates = this.getStoredGlobalState();
const changedStates: { id: string, visible: boolean }[] = [];
for (const [id, storedState] of storedViewsVisibilityStates) {
const state = this.state.get(id);
if (state) {
if (state.visibleGlobal !== !storedState.isHidden) {
changedStates.push({ id, visible: !storedState.isHidden });
}
}
}
if (changedStates.length) {
this._onDidChangeStoredState.fire(changedStates);
}
}
}
private initialize(): Map<string, IViewDescriptorState> {
const viewStates = new Map<string, IViewDescriptorState>();
const workspaceViewsStates = <{ [id: string]: IStoredWorkspaceViewState; }>JSON.parse(this.storageService.get(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}'));
for (const id of Object.keys(workspaceViewsStates)) {
const workspaceViewState = workspaceViewsStates[id];
viewStates.set(id, {
active: false,
visibleGlobal: undefined,
visibleWorkspace: isUndefined(workspaceViewState.isHidden) ? undefined : !workspaceViewState.isHidden,
collapsed: workspaceViewState.collapsed,
order: workspaceViewState.order,
size: workspaceViewState.size,
});
}
// Migrate to `viewletStateStorageId`
const value = this.storageService.get(this.globalViewsStateStorageId, StorageScope.WORKSPACE, '[]');
const { state: workspaceVisibilityStates } = this.parseStoredGlobalState(value);
if (workspaceVisibilityStates.size > 0) {
for (const { id, isHidden } of values(workspaceVisibilityStates)) {
let viewState = viewStates.get(id);
// Not migrated to `viewletStateStorageId`
if (viewState) {
if (isUndefined(viewState.visibleWorkspace)) {
viewState.visibleWorkspace = !isHidden;
}
} else {
viewStates.set(id, {
active: false,
collapsed: undefined,
visibleGlobal: undefined,
visibleWorkspace: !isHidden,
});
}
}
this.storageService.remove(this.globalViewsStateStorageId, StorageScope.WORKSPACE);
}
const { state, hasDuplicates } = this.parseStoredGlobalState(this.globalViewsStatesValue);
if (hasDuplicates) {
this.setStoredGlobalState(state);
}
for (const { id, isHidden, order } of values(state)) {
let viewState = viewStates.get(id);
if (viewState) {
viewState.visibleGlobal = !isHidden;
if (!isUndefined(order)) {
viewState.order = order;
}
} else {
viewStates.set(id, {
active: false,
visibleGlobal: !isHidden,
order,
collapsed: undefined,
visibleWorkspace: undefined,
});
}
}
return viewStates;
}
private getStoredGlobalState(): Map<string, IStoredGlobalViewState> {
return this.parseStoredGlobalState(this.globalViewsStatesValue).state;
}
private setStoredGlobalState(storedGlobalState: Map<string, IStoredGlobalViewState>): void {
this.globalViewsStatesValue = JSON.stringify(values(storedGlobalState));
}
private parseStoredGlobalState(value: string): { state: Map<string, IStoredGlobalViewState>, hasDuplicates: boolean } {
const storedValue = <Array<string | IStoredGlobalViewState>>JSON.parse(value);
let hasDuplicates = false;
const state = storedValue.reduce((result, storedState) => {
if (typeof storedState === 'string' /* migration */) {
hasDuplicates = hasDuplicates || result.has(storedState);
result.set(storedState, { id: storedState, isHidden: true });
} else {
hasDuplicates = hasDuplicates || result.has(storedState.id);
result.set(storedState.id, storedState);
}
return result;
}, new Map<string, IStoredGlobalViewState>());
return { state, hasDuplicates };
}
private _globalViewsStatesValue: string | undefined;
private get globalViewsStatesValue(): string {
if (!this._globalViewsStatesValue) {
this._globalViewsStatesValue = this.getStoredGlobalViewsStatesValue();
}
return this._globalViewsStatesValue;
}
private set globalViewsStatesValue(globalViewsStatesValue: string) {
if (this.globalViewsStatesValue !== globalViewsStatesValue) {
this._globalViewsStatesValue = globalViewsStatesValue;
this.setStoredGlobalViewsStatesValue(globalViewsStatesValue);
}
}
private getStoredGlobalViewsStatesValue(): string {
return this.storageService.get(this.globalViewsStateStorageId, StorageScope.GLOBAL, '[]');
}
private setStoredGlobalViewsStatesValue(value: string): void {
this.storageService.store(this.globalViewsStateStorageId, value, StorageScope.GLOBAL);
}
}
interface IViewDescriptorItem {
viewDescriptor: IViewDescriptor;
state: IViewDescriptorState;
}
class ViewContainerModel extends Disposable implements IViewContainerModel {
private readonly contextKeys = new CounterSet<string>();
private viewDescriptorItems: IViewDescriptorItem[] = [];
private viewDescriptorsState: ViewDescriptorsState;
// Container Info
private _title!: string;
get title(): string { return this._title; }
private _icon: URI | string | undefined;
get icon(): URI | string | undefined { return this._icon; }
private _onDidChangeContainerInfo = this._register(new Emitter<{ title?: boolean, icon?: boolean }>());
readonly onDidChangeContainerInfo = this._onDidChangeContainerInfo.event;
// All View Descriptors
get allViewDescriptors(): ReadonlyArray<IViewDescriptor> { return this.viewDescriptorItems.map(item => item.viewDescriptor); }
private _onDidChangeAllViewDescriptors = this._register(new Emitter<{ added: ReadonlyArray<IViewDescriptor>, removed: ReadonlyArray<IViewDescriptor> }>());
readonly onDidChangeAllViewDescriptors = this._onDidChangeAllViewDescriptors.event;
// Active View Descriptors
get activeViewDescriptors(): ReadonlyArray<IViewDescriptor> { return this.viewDescriptorItems.filter(item => item.state.active).map(item => item.viewDescriptor); }
private _onDidChangeActiveViewDescriptors = this._register(new Emitter<{ added: ReadonlyArray<IViewDescriptor>, removed: ReadonlyArray<IViewDescriptor> }>());
readonly onDidChangeActiveViewDescriptors = this._onDidChangeActiveViewDescriptors.event;
// Visible View Descriptors
get visibleViewDescriptors(): ReadonlyArray<IViewDescriptor> { return this.viewDescriptorItems.filter(item => this.isViewDescriptorVisible(item)).map(item => item.viewDescriptor); }
private _onDidAddVisibleViewDescriptors = this._register(new Emitter<IAddedViewDescriptorRef[]>());
readonly onDidAddVisibleViewDescriptors: Event<IAddedViewDescriptorRef[]> = this._onDidAddVisibleViewDescriptors.event;
private _onDidRemoveVisibleViewDescriptors = this._register(new Emitter<IViewDescriptorRef[]>());
readonly onDidRemoveVisibleViewDescriptors: Event<IViewDescriptorRef[]> = this._onDidRemoveVisibleViewDescriptors.event;
private _onDidMoveVisibleViewDescriptors = this._register(new Emitter<{ from: IViewDescriptorRef; to: IViewDescriptorRef; }>());
readonly onDidMoveVisibleViewDescriptors: Event<{ from: IViewDescriptorRef; to: IViewDescriptorRef; }> = this._onDidMoveVisibleViewDescriptors.event;
constructor(
private readonly container: ViewContainer,
@IInstantiationService instantiationService: IInstantiationService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
) {
super();
this.viewDescriptorsModel = instantiationService.createInstance(ViewDescriptorsModel);
this.viewsModel = instantiationService.createInstance(PersistentViewsModel, container.storageId || `${container.id}.state`, this.viewDescriptorsModel);
this._register(Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys))(() => this.onDidChangeContext()));
this.viewDescriptorsState = this._register(instantiationService.createInstance(ViewDescriptorsState, container.storageId || `${container.id}.state`));
this._register(this.viewDescriptorsState.onDidChangeStoredState(items => this.updateVisibility(items)));
this._register(Event.any(
this.onDidAddVisibleViewDescriptors,
this.onDidRemoveVisibleViewDescriptors,
this.onDidMoveVisibleViewDescriptors)
(() => {
this.viewDescriptorsState.updateState(this.allViewDescriptors);
this.updateContainerInfo();
}));
this.updateContainerInfo();
}
get allViewDescriptors(): IViewDescriptor[] { return this.viewDescriptorsModel.allViewDescriptors; }
get onDidChangeViews(): Event<{ added: IViewDescriptor[], removed: IViewDescriptor[] }> { return this.viewDescriptorsModel.onDidChangeViews; }
get activeViewDescriptors(): IViewDescriptor[] { return this.viewDescriptorsModel.activeViewDescriptors; }
get _onDidChangeActiveViews(): Event<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> { return this.viewDescriptorsModel.onDidChangeActiveViews; }
get onDidChangeActiveViews(): Event<ReadonlyArray<IViewDescriptor>> { return this.viewsModel.onDidChangeActiveViews; }
addViews(viewDescriptors: IViewDescriptor[]): void { return this.viewDescriptorsModel.addViews(viewDescriptors); }
removeViews(viewDescriptors: IViewDescriptor[]): void { return this.viewDescriptorsModel.removeViews(viewDescriptors); }
get visibleViewDescriptors(): IViewDescriptor[] { return this.viewsModel.visibleViewDescriptors; }
get onDidAdd(): Event<IAddedViewDescriptorRef[]> { return this.viewsModel.onDidAdd; }
get onDidRemove(): Event<IViewDescriptorRef[]> { return this.viewsModel.onDidRemove; }
get onDidMove(): Event<{ from: IViewDescriptorRef; to: IViewDescriptorRef; }> { return this.viewsModel.onDidMove; }
isVisible(id: string): boolean { return this.viewsModel.isVisible(id); }
setVisible(id: string, visible: boolean, size?: number): void { return this.viewsModel.setVisible(id, visible, size); }
isCollapsed(id: string): boolean { return this.viewsModel.isCollapsed(id); }
setCollapsed(id: string, collapsed: boolean): void { return this.viewsModel.setCollapsed(id, collapsed); }
private shouldUseContainerInfo(): boolean {
if (this.allViewDescriptors.length === 0) {
return true;
private updateContainerInfo(): void {
/* Use default container info if one of the visible view descriptors belongs to the current container by default */
const useDefaultContainerInfo = this.visibleViewDescriptors.some(v => Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).getViewContainer(v.id) === this.container);
const title = useDefaultContainerInfo ? this.container.name : this.visibleViewDescriptors[0]?.name || '';
let titleChanged: boolean = false;
if (this._title !== title) {
this._title = title;
titleChanged = true;
}
if (this.visibleViewDescriptors.length === 0) {
return true;
const icon = useDefaultContainerInfo ? this.container.icon : this.visibleViewDescriptors[0]?.containerIcon || 'codicon-window';
let iconChanged: boolean = false;
if (URI.isUri(icon) && URI.isUri(this._icon) ? isEqual(icon, this._icon) : this._icon !== icon) {
this._icon = icon;
iconChanged = true;
}
return this.allViewDescriptors.some(v => Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).getViewContainer(v.id) === this.container);
if (titleChanged || iconChanged) {
this._onDidChangeContainerInfo.fire({ title: titleChanged, icon: iconChanged });
}
}
getTitle(): string {
return this.shouldUseContainerInfo() ? this.container.name : this.visibleViewDescriptors[0].name;
isVisible(id: string): boolean {
const viewDescriptorItem = this.viewDescriptorItems.filter(v => v.viewDescriptor.id === id)[0];
if (!viewDescriptorItem) {
throw new Error(`Unknown view ${id}`);
}
return this.isViewDescriptorVisible(viewDescriptorItem);
}
getIcon(): URI | string | undefined {
return this.shouldUseContainerInfo() ? this.container.icon : this.visibleViewDescriptors[0]?.containerIcon || 'codicon-window';
setVisible(id: string, visible: boolean, size?: number): void {
this.updateVisibility([{ id, visible, size }]);
}
getSize(id: string): number | undefined { return this.viewsModel.getSize(id); }
setSize(id: string, size: number): void { return this.viewsModel.setSize(id, size); }
private updateVisibility(viewDescriptors: { id: string, visible: boolean, size?: number }[]): void {
const added: IAddedViewDescriptorRef[] = [];
const removed: IViewDescriptorRef[] = [];
move(from: string, to: string): void { return this.viewsModel.move(from, to); }
for (const { visibleIndex, viewDescriptorItem, visible, size } of viewDescriptors.map(({ id, visible, size }) => ({ ...this.find(id), visible, size }))) {
const viewDescriptor = viewDescriptorItem.viewDescriptor;
if (!viewDescriptor.canToggleVisibility) {
throw new Error(`Can't toggle this view's visibility`);
}
if (this.isViewDescriptorVisible(viewDescriptorItem) === visible) {
return;
}
if (viewDescriptor.workspace) {
viewDescriptorItem.state.visibleWorkspace = visible;
} else {
viewDescriptorItem.state.visibleGlobal = visible;
}
if (typeof viewDescriptorItem.state.size === 'number') {
viewDescriptorItem.state.size = size;
}
if (visible) {
added.push({ index: visibleIndex, viewDescriptor, size: viewDescriptorItem.state.size, collapsed: !!viewDescriptorItem.state.collapsed });
} else {
removed.push({ index: visibleIndex, viewDescriptor });
}
}
if (added.length) {
this._onDidAddVisibleViewDescriptors.fire(added);
}
if (removed.length) {
this._onDidRemoveVisibleViewDescriptors.fire(removed);
}
}
isCollapsed(id: string): boolean {
return !!this.find(id).viewDescriptorItem.state.collapsed;
}
setCollapsed(id: string, collapsed: boolean): void {
const { viewDescriptorItem } = this.find(id);
if (viewDescriptorItem.state.collapsed !== collapsed) {
viewDescriptorItem.state.collapsed = collapsed;
}
this.viewDescriptorsState.updateState(this.allViewDescriptors);
}
getSize(id: string): number | undefined {
return this.find(id).viewDescriptorItem.state.size;
}
setSize(id: string, size: number): void {
const { viewDescriptorItem } = this.find(id);
if (viewDescriptorItem.state.size !== size) {
viewDescriptorItem.state.size = size;
}
this.viewDescriptorsState.updateState(this.allViewDescriptors);
}
move(from: string, to: string): void {
const fromIndex = firstIndex(this.viewDescriptorItems, v => v.viewDescriptor.id === from);
const toIndex = firstIndex(this.viewDescriptorItems, v => v.viewDescriptor.id === to);
const fromViewDescriptor = this.viewDescriptorItems[fromIndex];
const toViewDescriptor = this.viewDescriptorItems[toIndex];
move(this.viewDescriptorItems, fromIndex, toIndex);
for (let index = 0; index < this.viewDescriptorItems.length; index++) {
this.viewDescriptorItems[index].state.order = index;
}
this._onDidMoveVisibleViewDescriptors.fire({
from: { index: fromIndex, viewDescriptor: fromViewDescriptor.viewDescriptor },
to: { index: toIndex, viewDescriptor: toViewDescriptor.viewDescriptor }
});
}
add(viewDescriptors: IViewDescriptor[]): void {
const addedItems: IViewDescriptorItem[] = [];
const addedActiveDescriptors: IViewDescriptor[] = [];
const addedVisibleItems: { index: number, viewDescriptor: IViewDescriptor, size?: number, collapsed: boolean; }[] = [];
for (const viewDescriptor of viewDescriptors) {
if (viewDescriptor.when) {
for (const key of viewDescriptor.when.keys()) {
this.contextKeys.add(key);
}
}
let state = this.viewDescriptorsState.get(viewDescriptor.id);
if (state) {
// set defaults if not set
if (viewDescriptor.workspace) {
state.visibleWorkspace = isUndefinedOrNull(state.visibleWorkspace) ? !viewDescriptor.hideByDefault : state.visibleWorkspace;
} else {
state.visibleGlobal = isUndefinedOrNull(state.visibleGlobal) ? !viewDescriptor.hideByDefault : state.visibleGlobal;
}
state.collapsed = isUndefinedOrNull(state.collapsed) ? !!viewDescriptor.collapsed : state.collapsed;
} else {
state = {
active: false,
visibleGlobal: !viewDescriptor.hideByDefault,
visibleWorkspace: !viewDescriptor.hideByDefault,
collapsed: !!viewDescriptor.collapsed,
};
}
this.viewDescriptorsState.set(viewDescriptor.id, state);
state.active = this.contextKeyService.contextMatchesRules(viewDescriptor.when);
addedItems.push({ viewDescriptor, state });
if (state.active) {
addedActiveDescriptors.push(viewDescriptor);
}
}
this.viewDescriptorItems.push(...addedItems);
this.viewDescriptorItems.sort(this.compareViewDescriptors.bind(this));
for (const viewDescriptorItem of addedItems) {
if (this.isViewDescriptorVisible(viewDescriptorItem)) {
const { visibleIndex } = this.find(viewDescriptorItem.viewDescriptor.id);
addedVisibleItems.push({ index: visibleIndex, viewDescriptor: viewDescriptorItem.viewDescriptor, size: viewDescriptorItem.state.size, collapsed: !!viewDescriptorItem.state.collapsed });
}
}
this._onDidChangeAllViewDescriptors.fire({ added: addedItems.map(({ viewDescriptor }) => viewDescriptor), removed: [] });
if (addedActiveDescriptors.length) {
this._onDidChangeActiveViewDescriptors.fire(({ added: addedActiveDescriptors, removed: [] }));
}
if (addedVisibleItems.length) {
this._onDidAddVisibleViewDescriptors.fire(addedVisibleItems);
}
}
remove(viewDescriptors: IViewDescriptor[]): void {
const removed: IViewDescriptor[] = [];
const removedItems: IViewDescriptorItem[] = [];
const removedActiveDescriptors: IViewDescriptor[] = [];
const removedVisibleItems: { index: number, viewDescriptor: IViewDescriptor; }[] = [];
for (const viewDescriptor of viewDescriptors) {
if (viewDescriptor.when) {
for (const key of viewDescriptor.when.keys()) {
this.contextKeys.delete(key);
}
}
const index = firstIndex(this.viewDescriptorItems, i => i.viewDescriptor.id === viewDescriptor.id);
if (index !== -1) {
removed.push(viewDescriptor);
const viewDescriptorItem = this.viewDescriptorItems[index];
if (viewDescriptorItem.state.active) {
removedActiveDescriptors.push(viewDescriptorItem.viewDescriptor);
}
if (this.isViewDescriptorVisible(viewDescriptorItem)) {
const { visibleIndex } = this.find(viewDescriptorItem.viewDescriptor.id);
removedVisibleItems.push({ index: visibleIndex, viewDescriptor: viewDescriptorItem.viewDescriptor });
}
removedItems.push(viewDescriptorItem);
}
}
removedItems.forEach(item => this.viewDescriptorItems.splice(this.viewDescriptorItems.indexOf(item), 1));
this._onDidChangeAllViewDescriptors.fire({ added: [], removed });
if (removedActiveDescriptors.length) {
this._onDidChangeActiveViewDescriptors.fire(({ added: [], removed: removedActiveDescriptors }));
}
if (removedVisibleItems.length) {
this._onDidRemoveVisibleViewDescriptors.fire(removedVisibleItems);
}
}
private onDidChangeContext(): void {
const addedActiveItems: { item: IViewDescriptorItem, wasVisible: boolean }[] = [];
const removedActiveItems: { item: IViewDescriptorItem, wasVisible: boolean }[] = [];
const removedVisibleItems: { index: number, viewDescriptor: IViewDescriptor; }[] = [];
const addedVisibleItems: { index: number, viewDescriptor: IViewDescriptor, size?: number, collapsed: boolean; }[] = [];
for (const item of this.viewDescriptorItems) {
const wasActive = item.state.active;
const wasVisible = this.isViewDescriptorVisible(item);
const isActive = this.contextKeyService.contextMatchesRules(item.viewDescriptor.when);
if (wasActive !== isActive) {
if (isActive) {
addedActiveItems.push({ item, wasVisible });
} else {
removedActiveItems.push({ item, wasVisible });
}
}
}
for (const { item, wasVisible } of removedActiveItems) {
if (wasVisible) {
const { visibleIndex } = this.find(item.viewDescriptor.id);
removedVisibleItems.push({ index: visibleIndex, viewDescriptor: item.viewDescriptor });
}
}
// Update the State
removedActiveItems.forEach(({ item }) => item.state.active = false);
addedActiveItems.forEach(({ item }) => item.state.active = true);
for (const { item, wasVisible } of addedActiveItems) {
if (wasVisible !== this.isViewDescriptorVisibleWhenActive(item)) {
const { visibleIndex } = this.find(item.viewDescriptor.id);
addedVisibleItems.push({ index: visibleIndex, viewDescriptor: item.viewDescriptor, size: item.state.size, collapsed: !!item.state.collapsed });
}
}
if (addedActiveItems.length || removedActiveItems.length) {
this._onDidChangeActiveViewDescriptors.fire(({ added: addedActiveItems.map(({ item }) => item.viewDescriptor), removed: removedActiveItems.map(({ item }) => item.viewDescriptor) }));
}
if (removedVisibleItems.length) {
this._onDidRemoveVisibleViewDescriptors.fire(removedVisibleItems);
}
if (addedVisibleItems.length) {
this._onDidAddVisibleViewDescriptors.fire(addedVisibleItems);
}
}
private isViewDescriptorVisible(viewDescriptorItem: IViewDescriptorItem): boolean {
if (!viewDescriptorItem.state.active) {
return false;
}
return this.isViewDescriptorVisibleWhenActive(viewDescriptorItem);
}
private isViewDescriptorVisibleWhenActive(viewDescriptorItem: IViewDescriptorItem): boolean {
if (viewDescriptorItem.viewDescriptor.workspace) {
return !!viewDescriptorItem.state.visibleWorkspace;
}
return !!viewDescriptorItem.state.visibleGlobal;
}
private find(id: string): { index: number, visibleIndex: number, viewDescriptorItem: IViewDescriptorItem; } {
for (let i = 0, visibleIndex = 0; i < this.viewDescriptorItems.length; i++) {
const viewDescriptorItem = this.viewDescriptorItems[i];
if (viewDescriptorItem.viewDescriptor.id === id) {
return { index: i, visibleIndex, viewDescriptorItem: viewDescriptorItem };
}
if (this.isViewDescriptorVisible(viewDescriptorItem)) {
visibleIndex++;
}
}
throw new Error(`view descriptor ${id} not found`);
}
private compareViewDescriptors(a: IViewDescriptorItem, b: IViewDescriptorItem): number {
if (a.viewDescriptor.id === b.viewDescriptor.id) {
return 0;
}
return (this.getViewOrder(a) - this.getViewOrder(b)) || this.getGroupOrderResult(a.viewDescriptor, b.viewDescriptor);
}
private getViewOrder(viewDescriptorItem: IViewDescriptorItem): number {
const viewOrder = typeof viewDescriptorItem.state.order === 'number' ? viewDescriptorItem.state.order : viewDescriptorItem.viewDescriptor.order;
return typeof viewOrder === 'number' ? viewOrder : Number.MAX_VALUE;
}
private getGroupOrderResult(a: IViewDescriptor, b: IViewDescriptor) {
if (!a.group || !b.group) {
return 0;
}
if (a.group === b.group) {
return 0;
}
return a.group < b.group ? -1 : 1;
}
}
interface ICachedViewContainerInfo {
@@ -99,7 +677,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
private readonly _onDidChangeLocation: Emitter<{ views: IViewDescriptor[], from: ViewContainerLocation, to: ViewContainerLocation }> = this._register(new Emitter<{ views: IViewDescriptor[], from: ViewContainerLocation, to: ViewContainerLocation }>());
readonly onDidChangeLocation: Event<{ views: IViewDescriptor[], from: ViewContainerLocation, to: ViewContainerLocation }> = this._onDidChangeLocation.event;
private readonly viewDescriptorCollections: Map<ViewContainer, { viewDescriptorCollection: ViewDescriptorCollection, disposable: IDisposable; }>;
private readonly viewContainerModels: Map<ViewContainer, { viewContainerModel: ViewContainerModel, disposable: IDisposable; }>;
private readonly activeViewContextKeys: Map<string, IContextKey<boolean>>;
private readonly movableViewContextKeys: Map<string, IContextKey<boolean>>;
private readonly defaultViewLocationContextKeys: Map<string, IContextKey<boolean>>;
@@ -136,7 +714,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
super();
storageKeysSyncRegistryService.registerStorageKey({ key: ViewDescriptorService.CACHED_VIEW_POSITIONS, version: 1 });
this.viewDescriptorCollections = new Map<ViewContainer, { viewDescriptorCollection: ViewDescriptorCollection, disposable: IDisposable; }>();
this.viewContainerModels = new Map<ViewContainer, { viewContainerModel: ViewContainerModel, disposable: IDisposable; }>();
this.activeViewContextKeys = new Map<string, IContextKey<boolean>>();
this.movableViewContextKeys = new Map<string, IContextKey<boolean>>();
this.defaultViewLocationContextKeys = new Map<string, IContextKey<boolean>>();
@@ -160,8 +738,8 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer }) => this.onDidRegisterViewContainer(viewContainer)));
this._register(this.viewContainersRegistry.onDidDeregister(({ viewContainer }) => this.onDidDeregisterViewContainer(viewContainer)));
this._register(toDisposable(() => {
this.viewDescriptorCollections.forEach(({ disposable }) => disposable.dispose());
this.viewDescriptorCollections.clear();
this.viewContainerModels.forEach(({ disposable }) => disposable.dispose());
this.viewContainerModels.clear();
}));
this._register(this.storageService.onDidChangeStorage((e) => { this.onDidStorageChange(e); }));
@@ -176,7 +754,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
const containerData = groupedViews.get(containerId)!;
// The container has not been registered yet
if (!viewContainer || !this.viewDescriptorCollections.has(viewContainer)) {
if (!viewContainer || !this.viewContainerModels.has(viewContainer)) {
if (containerData.cachedContainerInfo && this.shouldGenerateContainer(containerData.cachedContainerInfo)) {
const containerInfo = containerData.cachedContainerInfo;
@@ -198,7 +776,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
const viewContainer = this.viewContainersRegistry.get(viewContainerId);
// The container has not been registered yet
if (!viewContainer || !this.viewDescriptorCollections.has(viewContainer)) {
if (!viewContainer || !this.viewContainerModels.has(viewContainer)) {
continue;
}
@@ -321,8 +899,8 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
return this.viewsRegistry.getViewContainer(viewId) ?? null;
}
getViewDescriptors(container: ViewContainer): ViewDescriptorCollection {
return this.getOrRegisterViewDescriptorCollection(container);
getViewContainerModel(container: ViewContainer): ViewContainerModel {
return this.getOrRegisterViewContainerModel(container);
}
moveViewToLocation(view: IViewDescriptor, location: ViewContainerLocation): void {
@@ -456,8 +1034,8 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
// If a value is not present in the cache, it must be reset to default
this.viewContainersRegistry.all.forEach(viewContainer => {
const viewDescriptorCollection = this.getViewDescriptors(viewContainer);
viewDescriptorCollection.allViewDescriptors.forEach(viewDescriptor => {
const viewContainerModel = this.getViewContainerModel(viewContainer);
viewContainerModel.allViewDescriptors.forEach(viewDescriptor => {
if (!newCachedPositions.has(viewDescriptor.id)) {
const currentContainer = this.getViewContainer(viewDescriptor.id);
const defaultContainer = this.getDefaultContainer(viewDescriptor.id);
@@ -492,8 +1070,8 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
private saveViewPositionsToCache(): void {
this.viewContainersRegistry.all.forEach(viewContainer => {
const viewDescriptorCollection = this.getViewDescriptors(viewContainer);
viewDescriptorCollection.allViewDescriptors.forEach(viewDescriptor => {
const viewContainerModel = this.getViewContainerModel(viewContainer);
viewContainerModel.allViewDescriptors.forEach(viewDescriptor => {
const containerLocation = this.getViewContainerLocation(viewContainer);
this.cachedViewInfo.set(viewDescriptor.id, {
containerId: viewContainer.id,
@@ -540,20 +1118,20 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
}
private onDidRegisterViewContainer(viewContainer: ViewContainer): void {
this.getOrRegisterViewDescriptorCollection(viewContainer);
this.getOrRegisterViewContainerModel(viewContainer);
}
private getOrRegisterViewDescriptorCollection(viewContainer: ViewContainer): ViewDescriptorCollection {
let viewDescriptorCollection = this.viewDescriptorCollections.get(viewContainer)?.viewDescriptorCollection;
private getOrRegisterViewContainerModel(viewContainer: ViewContainer): ViewContainerModel {
let viewContainerModel = this.viewContainerModels.get(viewContainer)?.viewContainerModel;
if (!viewDescriptorCollection) {
if (!viewContainerModel) {
const disposables = new DisposableStore();
viewDescriptorCollection = disposables.add(this.instantiationService.createInstance(ViewDescriptorCollection, viewContainer));
viewContainerModel = disposables.add(this.instantiationService.createInstance(ViewContainerModel, viewContainer));
this.onDidChangeActiveViews({ added: viewDescriptorCollection.activeViewDescriptors, removed: [] });
viewDescriptorCollection._onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed), this, disposables);
this.onDidChangeActiveViews({ added: viewContainerModel.activeViewDescriptors, removed: [] });
viewContainerModel.onDidChangeActiveViewDescriptors(changed => this.onDidChangeActiveViews(changed), this, disposables);
this.viewDescriptorCollections.set(viewContainer, { viewDescriptorCollection, disposable: disposables });
this.viewContainerModels.set(viewContainer, { viewContainerModel: viewContainerModel, disposable: disposables });
const viewsToRegister = this.getViewsByContainer(viewContainer);
if (viewsToRegister.length) {
@@ -562,18 +1140,18 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
}
}
return viewDescriptorCollection;
return viewContainerModel;
}
private onDidDeregisterViewContainer(viewContainer: ViewContainer): void {
const viewDescriptorCollectionItem = this.viewDescriptorCollections.get(viewContainer);
if (viewDescriptorCollectionItem) {
viewDescriptorCollectionItem.disposable.dispose();
this.viewDescriptorCollections.delete(viewContainer);
const viewContainerModelItem = this.viewContainerModels.get(viewContainer);
if (viewContainerModelItem) {
viewContainerModelItem.disposable.dispose();
this.viewContainerModels.delete(viewContainer);
}
}
private onDidChangeActiveViews({ added, removed }: { added: IViewDescriptor[], removed: IViewDescriptor[]; }): void {
private onDidChangeActiveViews({ added, removed }: { added: ReadonlyArray<IViewDescriptor>, removed: ReadonlyArray<IViewDescriptor>; }): void {
added.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(true));
removed.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(false));
}
@@ -586,7 +1164,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
this.getOrCreateDefaultViewLocationContextKey(view).set(this.getDefaultContainer(view.id) === container);
});
this.getViewDescriptors(container).addViews(views);
this.getViewContainerModel(container).add(views);
}
private removeViews(container: ViewContainer, views: IViewDescriptor[]): void {
@@ -594,7 +1172,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
views.forEach(view => this.getOrCreateDefaultViewLocationContextKey(view).set(false));
// Remove the views
this.getViewDescriptors(container).removeViews(views);
this.getViewContainerModel(container).remove(views);
}
private getOrCreateActiveViewContextKey(viewDescriptor: IViewDescriptor): IContextKey<boolean> {
@@ -1,648 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { IViewDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef } from 'vs/workbench/common/views';
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { Event, Emitter } from 'vs/base/common/event';
import { firstIndex, move } from 'vs/base/common/arrays';
import { isUndefinedOrNull, isUndefined } from 'vs/base/common/types';
import { values } from 'vs/base/common/map';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IContextKeyChangeEvent, IContextKeyService, IReadableSet } from 'vs/platform/contextkey/common/contextkey';
class CounterSet<T> implements IReadableSet<T> {
private map = new Map<T, number>();
add(value: T): CounterSet<T> {
this.map.set(value, (this.map.get(value) || 0) + 1);
return this;
}
delete(value: T): boolean {
let counter = this.map.get(value) || 0;
if (counter === 0) {
return false;
}
counter--;
if (counter === 0) {
this.map.delete(value);
} else {
this.map.set(value, counter);
}
return true;
}
has(value: T): boolean {
return this.map.has(value);
}
}
interface IViewItem {
viewDescriptor: IViewDescriptor;
active: boolean;
}
export class ViewDescriptorsModel extends Disposable {
private contextKeys = new CounterSet<string>();
private items: IViewItem[] = [];
private _onDidChangeViews: Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._register(new Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }>());
readonly onDidChangeViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._onDidChangeViews.event;
private _onDidChangeActiveViews: Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._register(new Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }>());
readonly onDidChangeActiveViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._onDidChangeActiveViews.event;
get activeViewDescriptors(): IViewDescriptor[] {
return this.items
.filter(i => i.active)
.map(i => i.viewDescriptor);
}
get allViewDescriptors(): IViewDescriptor[] {
return this.items.map(i => i.viewDescriptor);
}
constructor(
@IContextKeyService private readonly contextKeyService: IContextKeyService,
) {
super();
this._register(Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys))(this.onContextChanged, this));
}
addViews(viewDescriptors: IViewDescriptor[]): void {
const added: IViewDescriptor[] = [];
for (const viewDescriptor of viewDescriptors) {
const item = {
viewDescriptor,
active: this.isViewDescriptorActive(viewDescriptor) // TODO: should read from some state?
};
this.items.push(item);
if (viewDescriptor.when) {
for (const key of viewDescriptor.when.keys()) {
this.contextKeys.add(key);
}
}
if (item.active) {
added.push(viewDescriptor);
}
}
this._onDidChangeViews.fire({ added: viewDescriptors, removed: [] });
if (added.length) {
this._onDidChangeActiveViews.fire({ added, removed: [] });
}
}
removeViews(viewDescriptors: IViewDescriptor[]): void {
const removed: IViewDescriptor[] = [];
for (const viewDescriptor of viewDescriptors) {
const index = firstIndex(this.items, i => i.viewDescriptor.id === viewDescriptor.id);
if (index === -1) {
continue;
}
const item = this.items[index];
this.items.splice(index, 1);
if (viewDescriptor.when) {
for (const key of viewDescriptor.when.keys()) {
this.contextKeys.delete(key);
}
}
if (item.active) {
removed.push(viewDescriptor);
}
}
this._onDidChangeViews.fire({ added: [], removed: viewDescriptors });
if (removed.length) {
this._onDidChangeActiveViews.fire({ added: [], removed });
}
}
private onContextChanged(event: IContextKeyChangeEvent): void {
const removed: IViewDescriptor[] = [];
const added: IViewDescriptor[] = [];
for (const item of this.items) {
const active = this.isViewDescriptorActive(item.viewDescriptor);
if (item.active !== active) {
if (active) {
added.push(item.viewDescriptor);
} else {
removed.push(item.viewDescriptor);
}
}
item.active = active;
}
if (added.length || removed.length) {
this._onDidChangeActiveViews.fire({ added, removed });
}
}
private isViewDescriptorActive(viewDescriptor: IViewDescriptor): boolean {
return !viewDescriptor.when || this.contextKeyService.contextMatchesRules(viewDescriptor.when);
}
}
export interface IViewState {
visibleGlobal: boolean | undefined;
visibleWorkspace: boolean | undefined;
collapsed: boolean | undefined;
order?: number;
size?: number;
}
export class ViewsModel extends Disposable {
private _viewDescriptors: IViewDescriptor[] = [];
get viewDescriptors(): ReadonlyArray<IViewDescriptor> {
return this._viewDescriptors;
}
get visibleViewDescriptors(): IViewDescriptor[] {
return this.viewDescriptors.filter(v => this.isViewDescriptorVisible(v)).sort((a, b) => {
const aIndex = this.viewStates.get(a.id)?.order;
const bIndex = this.viewStates.get(b.id)?.order;
if (aIndex === undefined) {
return 1;
}
if (bIndex === undefined) {
return -1;
}
return aIndex - bIndex;
});
}
private _onDidAdd = this._register(new Emitter<IAddedViewDescriptorRef[]>());
readonly onDidAdd: Event<IAddedViewDescriptorRef[]> = this._onDidAdd.event;
private _onDidRemove = this._register(new Emitter<IViewDescriptorRef[]>());
readonly onDidRemove: Event<IViewDescriptorRef[]> = this._onDidRemove.event;
private _onDidMove = this._register(new Emitter<{ from: IViewDescriptorRef; to: IViewDescriptorRef; }>());
readonly onDidMove: Event<{ from: IViewDescriptorRef; to: IViewDescriptorRef; }> = this._onDidMove.event;
private _onDidChangeViewState = this._register(new Emitter<IViewDescriptorRef>());
protected readonly onDidChangeViewState: Event<IViewDescriptorRef> = this._onDidChangeViewState.event;
private _onDidChangeActiveViews = this._register(new Emitter<ReadonlyArray<IViewDescriptor>>());
readonly onDidChangeActiveViews: Event<ReadonlyArray<IViewDescriptor>> = this._onDidChangeActiveViews.event;
constructor(
viewDescriptorCollection: ViewDescriptorsModel,
protected viewStates = new Map<string, IViewState>(),
) {
super();
this._register(viewDescriptorCollection.onDidChangeActiveViews(() => this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors)));
this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors);
}
isVisible(id: string): boolean {
const viewDescriptor = this.viewDescriptors.filter(v => v.id === id)[0];
if (!viewDescriptor) {
throw new Error(`Unknown view ${id}`);
}
return this.isViewDescriptorVisible(viewDescriptor);
}
setVisible(id: string, visible: boolean, size?: number): void {
this.doSetVisible([{ id, visible, size }]);
}
protected doSetVisible(viewDescriptors: { id: string, visible: boolean, size?: number }[]): void {
const added: IAddedViewDescriptorRef[] = [];
const removed: IViewDescriptorRef[] = [];
for (const { visibleIndex, viewDescriptor, state, visible, size } of viewDescriptors.map(({ id, visible, size }) => ({ ...this.find(id), visible, size }))) {
if (!viewDescriptor.canToggleVisibility) {
throw new Error(`Can't toggle this view's visibility`);
}
if (this.isViewDescriptorVisible(viewDescriptor) === visible) {
return;
}
if (viewDescriptor.workspace) {
state.visibleWorkspace = visible;
} else {
state.visibleGlobal = visible;
}
if (typeof size === 'number') {
state.size = size;
}
if (visible) {
added.push({ index: visibleIndex, viewDescriptor, size: state.size, collapsed: !!state.collapsed });
} else {
removed.push({ index: visibleIndex, viewDescriptor });
}
}
if (added.length) {
this._onDidAdd.fire(added);
}
if (removed.length) {
this._onDidRemove.fire(removed);
}
}
isCollapsed(id: string): boolean {
const state = this.viewStates.get(id);
if (!state) {
throw new Error(`Unknown view ${id}`);
}
return !!state.collapsed;
}
setCollapsed(id: string, collapsed: boolean): void {
const { index, state, viewDescriptor } = this.find(id);
if (state.collapsed !== collapsed) {
state.collapsed = collapsed;
this._onDidChangeViewState.fire({ viewDescriptor, index });
}
}
getSize(id: string): number | undefined {
const state = this.viewStates.get(id);
if (!state) {
throw new Error(`Unknown view ${id}`);
}
return state.size;
}
setSize(id: string, size: number): void {
const { index, state, viewDescriptor } = this.find(id);
if (state.size !== size) {
state.size = size;
this._onDidChangeViewState.fire({ viewDescriptor, index });
}
}
move(from: string, to: string): void {
const fromIndex = firstIndex(this.viewDescriptors, v => v.id === from);
const toIndex = firstIndex(this.viewDescriptors, v => v.id === to);
const fromViewDescriptor = this.viewDescriptors[fromIndex];
const toViewDescriptor = this.viewDescriptors[toIndex];
move(this._viewDescriptors, fromIndex, toIndex);
for (let index = 0; index < this.viewDescriptors.length; index++) {
const state = this.viewStates.get(this.viewDescriptors[index].id)!;
state.order = index;
}
this._onDidMove.fire({
from: { index: fromIndex, viewDescriptor: fromViewDescriptor },
to: { index: toIndex, viewDescriptor: toViewDescriptor }
});
}
private isViewDescriptorVisible(viewDescriptor: IViewDescriptor): boolean {
const viewState = this.viewStates.get(viewDescriptor.id);
if (!viewState) {
throw new Error(`Unknown view ${viewDescriptor.id}`);
}
return viewDescriptor.workspace ? !!viewState.visibleWorkspace : !!viewState.visibleGlobal;
}
private find(id: string): { index: number, visibleIndex: number, viewDescriptor: IViewDescriptor, state: IViewState; } {
for (let i = 0, visibleIndex = 0; i < this.viewDescriptors.length; i++) {
const viewDescriptor = this.viewDescriptors[i];
const state = this.viewStates.get(viewDescriptor.id);
if (!state) {
throw new Error(`View state for ${id} not found`);
}
if (viewDescriptor.id === id) {
return { index: i, visibleIndex, viewDescriptor, state };
}
if (viewDescriptor.workspace ? state.visibleWorkspace : state.visibleGlobal) {
visibleIndex++;
}
}
throw new Error(`view descriptor ${id} not found`);
}
private compareViewDescriptors(a: IViewDescriptor, b: IViewDescriptor): number {
if (a.id === b.id) {
return 0;
}
return (this.getViewOrder(a) - this.getViewOrder(b)) || this.getGroupOrderResult(a, b);
}
private getGroupOrderResult(a: IViewDescriptor, b: IViewDescriptor) {
if (!a.group || !b.group) {
return 0;
}
if (a.group === b.group) {
return 0;
}
return a.group < b.group ? -1 : 1;
}
private getViewOrder(viewDescriptor: IViewDescriptor): number {
const viewState = this.viewStates.get(viewDescriptor.id);
const viewOrder = viewState && typeof viewState.order === 'number' ? viewState.order : viewDescriptor.order;
return typeof viewOrder === 'number' ? viewOrder : Number.MAX_VALUE;
}
private onDidChangeViewDescriptors(viewDescriptors: IViewDescriptor[]): void {
for (const viewDescriptor of viewDescriptors) {
const viewState = this.viewStates.get(viewDescriptor.id);
if (viewState) {
// set defaults if not set
if (viewDescriptor.workspace) {
viewState.visibleWorkspace = isUndefinedOrNull(viewState.visibleWorkspace) ? !viewDescriptor.hideByDefault : viewState.visibleWorkspace;
} else {
viewState.visibleGlobal = isUndefinedOrNull(viewState.visibleGlobal) ? !viewDescriptor.hideByDefault : viewState.visibleGlobal;
}
viewState.collapsed = isUndefinedOrNull(viewState.collapsed) ? !!viewDescriptor.collapsed : viewState.collapsed;
} else {
this.viewStates.set(viewDescriptor.id, {
visibleGlobal: !viewDescriptor.hideByDefault,
visibleWorkspace: !viewDescriptor.hideByDefault,
collapsed: !!viewDescriptor.collapsed
});
}
}
viewDescriptors = viewDescriptors.sort(this.compareViewDescriptors.bind(this));
const toRemove: { index: number, viewDescriptor: IViewDescriptor; }[] = [];
for (let index = 0; index < this._viewDescriptors.length; index++) {
const previousViewDescriptor = this._viewDescriptors[index];
if (this.isViewDescriptorVisible(previousViewDescriptor) && viewDescriptors.every(viewDescriptor => viewDescriptor.id !== previousViewDescriptor.id)) {
const { visibleIndex } = this.find(previousViewDescriptor.id);
toRemove.push({ index: visibleIndex, viewDescriptor: previousViewDescriptor });
}
}
const previous = this._viewDescriptors;
this._viewDescriptors = viewDescriptors.slice(0);
const toAdd: { index: number, viewDescriptor: IViewDescriptor, size?: number, collapsed: boolean; }[] = [];
for (let i = 0; i < this._viewDescriptors.length; i++) {
const viewDescriptor = this._viewDescriptors[i];
if (this.isViewDescriptorVisible(viewDescriptor) && previous.every(previousViewDescriptor => previousViewDescriptor.id !== viewDescriptor.id)) {
const { visibleIndex, state } = this.find(viewDescriptor.id);
toAdd.push({ index: visibleIndex, viewDescriptor, size: state.size, collapsed: !!state.collapsed });
}
}
if (toRemove.length) {
this._onDidRemove.fire(toRemove);
}
if (toAdd.length) {
this._onDidAdd.fire(toAdd);
}
this._onDidChangeActiveViews.fire(this.viewDescriptors);
}
}
interface IStoredWorkspaceViewState {
collapsed: boolean;
isHidden: boolean;
size?: number;
order?: number;
}
interface IStoredGlobalViewState {
id: string;
isHidden: boolean;
order?: number;
}
export class PersistentViewsModel extends ViewsModel {
private readonly workspaceViewsStateStorageId: string;
private readonly globalViewsStateStorageId: string;
private storageService: IStorageService;
constructor(
viewletStateStorageId: string,
viewDescriptorCollection: ViewDescriptorsModel,
@IStorageService storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
const globalViewsStateStorageId = `${viewletStateStorageId}.hidden`;
storageKeysSyncRegistryService.registerStorageKey({ key: globalViewsStateStorageId, version: 1 });
const viewStates = PersistentViewsModel.loadViewsStates(viewletStateStorageId, globalViewsStateStorageId, storageService);
super(viewDescriptorCollection, viewStates);
this.storageService = storageService;
this.workspaceViewsStateStorageId = viewletStateStorageId;
this.globalViewsStateStorageId = globalViewsStateStorageId;
this._register(Event.any(
this.onDidAdd,
this.onDidRemove,
Event.map(this.onDidMove, ({ from, to }) => [from, to]),
Event.map(this.onDidChangeViewState, viewDescriptorRef => [viewDescriptorRef]))
(viewDescriptorRefs => this.saveViewsStates()));
this._globalViewsStatesValue = this.getStoredGlobalViewsStatesValue();
this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
}
private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void {
if (e.key === this.globalViewsStateStorageId && e.scope === StorageScope.GLOBAL
&& this.globalViewsStatesValue !== this.getStoredGlobalViewsStatesValue() /* This checks if current window changed the value or not */) {
this._globalViewsStatesValue = undefined;
const storedViewsVisibilityStates = PersistentViewsModel.loadGlobalViewsState(this.globalViewsStateStorageId, this.storageService, StorageScope.GLOBAL);
const changedViews: { id: string, visible: boolean }[] = [];
for (const [id, state] of storedViewsVisibilityStates) {
const viewState = this.viewStates.get(id);
if (viewState) {
if (viewState.visibleGlobal !== !state.isHidden) {
changedViews.push({ id, visible: !state.isHidden });
}
}
}
if (changedViews.length) {
this.doSetVisible(changedViews);
}
}
}
private saveViewsStates(): void {
this.saveWorkspaceViewsStates();
this.saveGlobalViewsStates();
}
private saveWorkspaceViewsStates(): void {
const storedViewsStates: { [id: string]: IStoredWorkspaceViewState; } = JSON.parse(this.storageService.get(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}'));
for (const viewDescriptor of this.viewDescriptors) {
const viewState = this.viewStates.get(viewDescriptor.id);
if (viewState) {
storedViewsStates[viewDescriptor.id] = {
collapsed: !!viewState.collapsed,
isHidden: !viewState.visibleWorkspace,
size: viewState.size,
order: viewDescriptor.workspace && viewState ? viewState.order : undefined
};
}
}
if (Object.keys(storedViewsStates).length > 0) {
this.storageService.store(this.workspaceViewsStateStorageId, JSON.stringify(storedViewsStates), StorageScope.WORKSPACE);
} else {
this.storageService.remove(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE);
}
}
private saveGlobalViewsStates(): void {
const storedViewsVisibilityStates = PersistentViewsModel.loadGlobalViewsState(this.globalViewsStateStorageId, this.storageService, StorageScope.GLOBAL);
for (const viewDescriptor of this.viewDescriptors) {
const viewState = this.viewStates.get(viewDescriptor.id);
storedViewsVisibilityStates.set(viewDescriptor.id, {
id: viewDescriptor.id,
isHidden: viewState && viewDescriptor.canToggleVisibility ? !viewState.visibleGlobal : false,
order: !viewDescriptor.workspace && viewState ? viewState.order : undefined
});
}
this.globalViewsStatesValue = JSON.stringify(values(storedViewsVisibilityStates));
}
private _globalViewsStatesValue: string | undefined;
private get globalViewsStatesValue(): string {
if (!this._globalViewsStatesValue) {
this._globalViewsStatesValue = this.getStoredGlobalViewsStatesValue();
}
return this._globalViewsStatesValue;
}
private set globalViewsStatesValue(globalViewsStatesValue: string) {
if (this.globalViewsStatesValue !== globalViewsStatesValue) {
this._globalViewsStatesValue = globalViewsStatesValue;
this.setStoredGlobalViewsStatesValue(globalViewsStatesValue);
}
}
private getStoredGlobalViewsStatesValue(): string {
return this.storageService.get(this.globalViewsStateStorageId, StorageScope.GLOBAL, '[]');
}
private setStoredGlobalViewsStatesValue(value: string): void {
this.storageService.store(this.globalViewsStateStorageId, value, StorageScope.GLOBAL);
}
private static loadViewsStates(workspaceViewsStateStorageId: string, globalViewsStateStorageId: string, storageService: IStorageService): Map<string, IViewState> {
const viewStates = new Map<string, IViewState>();
const workspaceViewsStates = <{ [id: string]: IStoredWorkspaceViewState; }>JSON.parse(storageService.get(workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}'));
for (const id of Object.keys(workspaceViewsStates)) {
const workspaceViewState = workspaceViewsStates[id];
viewStates.set(id, {
visibleGlobal: undefined,
visibleWorkspace: isUndefined(workspaceViewState.isHidden) ? undefined : !workspaceViewState.isHidden,
collapsed: workspaceViewState.collapsed,
order: workspaceViewState.order,
size: workspaceViewState.size
});
}
// Migrate to `viewletStateStorageId`
const workspaceVisibilityStates = this.loadGlobalViewsState(globalViewsStateStorageId, storageService, StorageScope.WORKSPACE);
if (workspaceVisibilityStates.size > 0) {
for (const { id, isHidden } of values(workspaceVisibilityStates)) {
let viewState = viewStates.get(id);
// Not migrated to `viewletStateStorageId`
if (viewState) {
if (isUndefined(viewState.visibleWorkspace)) {
viewState.visibleWorkspace = !isHidden;
}
} else {
viewStates.set(id, {
collapsed: undefined,
visibleGlobal: undefined,
visibleWorkspace: !isHidden,
});
}
}
storageService.remove(globalViewsStateStorageId, StorageScope.WORKSPACE);
}
const globalViewsStates = this.loadGlobalViewsState(globalViewsStateStorageId, storageService, StorageScope.GLOBAL);
for (const { id, isHidden, order } of values(globalViewsStates)) {
let viewState = viewStates.get(id);
if (viewState) {
viewState.visibleGlobal = !isHidden;
if (!isUndefined(order)) {
viewState.order = order;
}
} else {
viewStates.set(id, {
visibleGlobal: !isHidden,
order,
collapsed: undefined,
visibleWorkspace: undefined,
});
}
}
return viewStates;
}
private static loadGlobalViewsState(globalViewsStateStorageId: string, storageService: IStorageService, scope: StorageScope): Map<string, IStoredGlobalViewState> {
const storedValue = <Array<string | IStoredGlobalViewState>>JSON.parse(storageService.get(globalViewsStateStorageId, scope, '[]'));
let hasDuplicates = false;
const storedGlobalViewsState = storedValue.reduce((result, storedState) => {
if (typeof storedState === 'string' /* migration */) {
hasDuplicates = hasDuplicates || result.has(storedState);
result.set(storedState, { id: storedState, isHidden: true });
} else {
hasDuplicates = hasDuplicates || result.has(storedState.id);
result.set(storedState.id, storedState);
}
return result;
}, new Map<string, IStoredGlobalViewState>());
if (hasDuplicates) {
storageService.store(globalViewsStateStorageId, JSON.stringify(values(storedGlobalViewsState)), scope);
}
return storedGlobalViewsState;
}
}
@@ -11,6 +11,8 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ViewDescriptorService } from 'vs/workbench/services/views/browser/viewDescriptorService';
import { assertIsDefined } from 'vs/base/common/types';
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
const ViewsRegistry = Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry);
const sidebarContainer = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: 'testSidebar', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
@@ -22,6 +24,7 @@ suite('ViewDescriptorService', () => {
setup(() => {
const instantiationService: TestInstantiationService = <TestInstantiationService>workbenchInstantiationService();
instantiationService.stub(IContextKeyService, instantiationService.createInstance(ContextKeyService));
viewDescriptorService = instantiationService.createInstance(ViewDescriptorService);
});
@@ -31,8 +34,8 @@ suite('ViewDescriptorService', () => {
});
test('Empty Containers', function () {
const sidebarViews = viewDescriptorService.getViewDescriptors(sidebarContainer);
const panelViews = viewDescriptorService.getViewDescriptors(panelContainer);
const sidebarViews = viewDescriptorService.getViewContainerModel(sidebarContainer);
const panelViews = viewDescriptorService.getViewContainerModel(panelContainer);
assert.equal(sidebarViews.allViewDescriptors.length, 0, 'The sidebar container should have no views yet.');
assert.equal(panelViews.allViewDescriptors.length, 0, 'The panel container should have no views yet.');
});
@@ -64,8 +67,8 @@ suite('ViewDescriptorService', () => {
ViewsRegistry.registerViews(viewDescriptors.slice(2), panelContainer);
let sidebarViews = viewDescriptorService.getViewDescriptors(sidebarContainer);
let panelViews = viewDescriptorService.getViewDescriptors(panelContainer);
let sidebarViews = viewDescriptorService.getViewContainerModel(sidebarContainer);
let panelViews = viewDescriptorService.getViewContainerModel(panelContainer);
assert.equal(sidebarViews.activeViewDescriptors.length, 2, 'Sidebar should have 2 views');
assert.equal(panelViews.activeViewDescriptors.length, 1, 'Panel should have 1 view');
@@ -74,8 +77,8 @@ suite('ViewDescriptorService', () => {
ViewsRegistry.deregisterViews(viewDescriptors.slice(2), panelContainer);
sidebarViews = viewDescriptorService.getViewDescriptors(sidebarContainer);
panelViews = viewDescriptorService.getViewDescriptors(panelContainer);
sidebarViews = viewDescriptorService.getViewContainerModel(sidebarContainer);
panelViews = viewDescriptorService.getViewContainerModel(panelContainer);
assert.equal(sidebarViews.activeViewDescriptors.length, 0, 'Sidebar should have no views');
assert.equal(panelViews.activeViewDescriptors.length, 0, 'Panel should have no views');
@@ -109,8 +112,8 @@ suite('ViewDescriptorService', () => {
viewDescriptorService.moveViewsToContainer(viewDescriptors.slice(2), sidebarContainer);
viewDescriptorService.moveViewsToContainer(viewDescriptors.slice(0, 2), panelContainer);
let sidebarViews = viewDescriptorService.getViewDescriptors(sidebarContainer);
let panelViews = viewDescriptorService.getViewDescriptors(panelContainer);
let sidebarViews = viewDescriptorService.getViewContainerModel(sidebarContainer);
let panelViews = viewDescriptorService.getViewContainerModel(panelContainer);
assert.equal(sidebarViews.activeViewDescriptors.length, 1, 'Sidebar should have 2 views');
assert.equal(panelViews.activeViewDescriptors.length, 2, 'Panel should have 1 view');
@@ -148,8 +151,8 @@ suite('ViewDescriptorService', () => {
viewDescriptorService.moveViewToLocation(viewDescriptors[0], ViewContainerLocation.Panel);
viewDescriptorService.moveViewToLocation(viewDescriptors[2], ViewContainerLocation.Sidebar);
let sidebarViews = viewDescriptorService.getViewDescriptors(sidebarContainer);
let panelViews = viewDescriptorService.getViewDescriptors(panelContainer);
let sidebarViews = viewDescriptorService.getViewContainerModel(sidebarContainer);
let panelViews = viewDescriptorService.getViewContainerModel(panelContainer);
assert.equal(sidebarViews.activeViewDescriptors.length, 1, 'Sidebar container should have 1 view');
assert.equal(panelViews.activeViewDescriptors.length, 0, 'Panel container should have no views');
@@ -169,8 +172,8 @@ suite('ViewDescriptorService', () => {
viewDescriptorService.moveViewToLocation(viewDescriptors[0], ViewContainerLocation.Sidebar);
viewDescriptorService.moveViewToLocation(viewDescriptors[2], ViewContainerLocation.Panel);
sidebarViews = viewDescriptorService.getViewDescriptors(sidebarContainer);
panelViews = viewDescriptorService.getViewDescriptors(panelContainer);
sidebarViews = viewDescriptorService.getViewContainerModel(sidebarContainer);
panelViews = viewDescriptorService.getViewContainerModel(panelContainer);
assert.equal(sidebarViews.activeViewDescriptors.length, 1, 'Sidebar should have 2 views');
assert.equal(panelViews.activeViewDescriptors.length, 0, 'Panel should have 1 view');
@@ -5,25 +5,31 @@
import * as assert from 'assert';
import * as sinon from 'sinon';
import { IViewDescriptor } from 'vs/workbench/common/views';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IViewsRegistry, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewContainerModel, IViewDescriptorService, ViewContainer } from 'vs/workbench/common/views';
import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
import { move } from 'vs/base/common/arrays';
import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
import { IViewState, ViewDescriptorsModel, ViewsModel } from 'vs/workbench/services/views/common/viewsModel';
import { ViewDescriptorService } from 'vs/workbench/services/views/browser/viewDescriptorService';
import { Registry } from 'vs/platform/registry/common/platform';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
const ViewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
const ViewsRegistry = Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry);
class ViewDescriptorSequence {
readonly elements: IViewDescriptor[];
private disposables: IDisposable[] = [];
constructor(model: ViewsModel) {
constructor(model: IViewContainerModel) {
this.elements = [...model.visibleViewDescriptors];
model.onDidAdd(added => added.forEach(({ viewDescriptor, index }) => this.elements.splice(index, 0, viewDescriptor)), null, this.disposables);
model.onDidRemove(removed => removed.sort((a, b) => b.index - a.index).forEach(({ index }) => this.elements.splice(index, 1)), null, this.disposables);
model.onDidMove(({ from, to }) => move(this.elements, from.index, to.index), null, this.disposables);
model.onDidAddVisibleViewDescriptors(added => added.forEach(({ viewDescriptor, index }) => this.elements.splice(index, 0, viewDescriptor)), null, this.disposables);
model.onDidRemoveVisibleViewDescriptors(removed => removed.sort((a, b) => b.index - a.index).forEach(({ index }) => this.elements.splice(index, 1)), null, this.disposables);
model.onDidMoveVisibleViewDescriptors(({ from, to }) => move(this.elements, from.index, to.index), null, this.disposables);
}
dispose() {
@@ -31,29 +37,42 @@ class ViewDescriptorSequence {
}
}
suite('ContributableViewsModel', () => {
suite('ViewContainerModel', () => {
let container: ViewContainer;
let disposableStore: DisposableStore;
let contextKeyService: IContextKeyService;
let viewDescriptorService: IViewDescriptorService;
let storageService: IStorageService;
setup(() => {
disposableStore = new DisposableStore();
const instantiationService: TestInstantiationService = <TestInstantiationService>workbenchInstantiationService();
contextKeyService = instantiationService.createInstance(ContextKeyService);
instantiationService.stub(IContextKeyService, contextKeyService);
storageService = instantiationService.get(IStorageService);
viewDescriptorService = instantiationService.createInstance(ViewDescriptorService);
});
teardown(() => {
disposableStore.dispose();
ViewsRegistry.deregisterViews(ViewsRegistry.getViews(container), container);
ViewContainerRegistry.deregisterViewContainer(container);
});
test('empty model', function () {
const viewsDescriptorsModel = new ViewDescriptorsModel(contextKeyService);
const model = new ViewsModel(viewsDescriptorsModel);
assert.equal(model.visibleViewDescriptors.length, 0);
container = ViewContainerRegistry.registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
assert.equal(testObject.visibleViewDescriptors.length, 0);
});
test('register/unregister', () => {
const viewsDescriptorsModel = new ViewDescriptorsModel(contextKeyService);
const model = new ViewsModel(viewsDescriptorsModel);
const seq = new ViewDescriptorSequence(model);
container = ViewContainerRegistry.registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const target = disposableStore.add(new ViewDescriptorSequence(testObject));
assert.equal(model.visibleViewDescriptors.length, 0);
assert.equal(seq.elements.length, 0);
assert.equal(testObject.visibleViewDescriptors.length, 0);
assert.equal(target.elements.length, 0);
const viewDescriptor: IViewDescriptor = {
id: 'view1',
@@ -61,26 +80,25 @@ suite('ContributableViewsModel', () => {
name: 'Test View 1'
};
viewsDescriptorsModel.addViews([viewDescriptor]);
ViewsRegistry.registerViews([viewDescriptor], container);
assert.equal(model.visibleViewDescriptors.length, 1);
assert.equal(seq.elements.length, 1);
assert.deepEqual(model.visibleViewDescriptors[0], viewDescriptor);
assert.deepEqual(seq.elements[0], viewDescriptor);
assert.equal(testObject.visibleViewDescriptors.length, 1);
assert.equal(target.elements.length, 1);
assert.deepEqual(testObject.visibleViewDescriptors[0], viewDescriptor);
assert.deepEqual(target.elements[0], viewDescriptor);
viewsDescriptorsModel.removeViews([viewDescriptor]);
ViewsRegistry.deregisterViews([viewDescriptor], container);
assert.equal(model.visibleViewDescriptors.length, 0);
assert.equal(seq.elements.length, 0);
assert.equal(testObject.visibleViewDescriptors.length, 0);
assert.equal(target.elements.length, 0);
});
test('when contexts', async function () {
const viewsDescriptorsModel = new ViewDescriptorsModel(contextKeyService);
const model = new ViewsModel(viewsDescriptorsModel);
const seq = new ViewDescriptorSequence(model);
assert.equal(model.visibleViewDescriptors.length, 0);
assert.equal(seq.elements.length, 0);
container = ViewContainerRegistry.registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const target = disposableStore.add(new ViewDescriptorSequence(testObject));
assert.equal(testObject.visibleViewDescriptors.length, 0);
assert.equal(target.elements.length, 0);
const viewDescriptor: IViewDescriptor = {
id: 'view1',
@@ -89,169 +107,164 @@ suite('ContributableViewsModel', () => {
when: ContextKeyExpr.equals('showview1', true)
};
viewsDescriptorsModel.addViews([viewDescriptor]);
assert.equal(model.visibleViewDescriptors.length, 0, 'view should not appear since context isnt in');
assert.equal(seq.elements.length, 0);
ViewsRegistry.registerViews([viewDescriptor], container);
assert.equal(testObject.visibleViewDescriptors.length, 0, 'view should not appear since context isnt in');
assert.equal(target.elements.length, 0);
const key = contextKeyService.createKey('showview1', false);
assert.equal(model.visibleViewDescriptors.length, 0, 'view should still not appear since showview1 isnt true');
assert.equal(seq.elements.length, 0);
assert.equal(testObject.visibleViewDescriptors.length, 0, 'view should still not appear since showview1 isnt true');
assert.equal(target.elements.length, 0);
key.set(true);
await new Promise(c => setTimeout(c, 30));
assert.equal(model.visibleViewDescriptors.length, 1, 'view should appear');
assert.equal(seq.elements.length, 1);
assert.deepEqual(model.visibleViewDescriptors[0], viewDescriptor);
assert.equal(seq.elements[0], viewDescriptor);
assert.equal(testObject.visibleViewDescriptors.length, 1, 'view should appear');
assert.equal(target.elements.length, 1);
assert.deepEqual(testObject.visibleViewDescriptors[0], viewDescriptor);
assert.equal(target.elements[0], viewDescriptor);
key.set(false);
await new Promise(c => setTimeout(c, 30));
assert.equal(model.visibleViewDescriptors.length, 0, 'view should disappear');
assert.equal(seq.elements.length, 0);
assert.equal(testObject.visibleViewDescriptors.length, 0, 'view should disappear');
assert.equal(target.elements.length, 0);
viewsDescriptorsModel.removeViews([viewDescriptor]);
assert.equal(model.visibleViewDescriptors.length, 0, 'view should not be there anymore');
assert.equal(seq.elements.length, 0);
ViewsRegistry.deregisterViews([viewDescriptor], container);
assert.equal(testObject.visibleViewDescriptors.length, 0, 'view should not be there anymore');
assert.equal(target.elements.length, 0);
key.set(true);
await new Promise(c => setTimeout(c, 30));
assert.equal(model.visibleViewDescriptors.length, 0, 'view should not be there anymore');
assert.equal(seq.elements.length, 0);
assert.equal(testObject.visibleViewDescriptors.length, 0, 'view should not be there anymore');
assert.equal(target.elements.length, 0);
});
test('when contexts - multiple', async function () {
const viewsDescriptorsModel = new ViewDescriptorsModel(contextKeyService);
const model = new ViewsModel(viewsDescriptorsModel);
const seq = new ViewDescriptorSequence(model);
container = ViewContainerRegistry.registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const target = disposableStore.add(new ViewDescriptorSequence(testObject));
const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1' };
const view2: IViewDescriptor = { id: 'view2', ctorDescriptor: null!, name: 'Test View 2', when: ContextKeyExpr.equals('showview2', true) };
viewsDescriptorsModel.addViews([view1, view2]);
assert.deepEqual(model.visibleViewDescriptors, [view1], 'only view1 should be visible');
assert.deepEqual(seq.elements, [view1], 'only view1 should be visible');
ViewsRegistry.registerViews([view1, view2], container);
assert.deepEqual(testObject.visibleViewDescriptors, [view1], 'only view1 should be visible');
assert.deepEqual(target.elements, [view1], 'only view1 should be visible');
const key = contextKeyService.createKey('showview2', false);
assert.deepEqual(model.visibleViewDescriptors, [view1], 'still only view1 should be visible');
assert.deepEqual(seq.elements, [view1], 'still only view1 should be visible');
assert.deepEqual(testObject.visibleViewDescriptors, [view1], 'still only view1 should be visible');
assert.deepEqual(target.elements, [view1], 'still only view1 should be visible');
key.set(true);
await new Promise(c => setTimeout(c, 30));
assert.deepEqual(model.visibleViewDescriptors, [view1, view2], 'both views should be visible');
assert.deepEqual(seq.elements, [view1, view2], 'both views should be visible');
assert.deepEqual(testObject.visibleViewDescriptors, [view1, view2], 'both views should be visible');
assert.deepEqual(target.elements, [view1, view2], 'both views should be visible');
viewsDescriptorsModel.removeViews([view1, view2]);
ViewsRegistry.deregisterViews([view1, view2], container);
});
test('when contexts - multiple 2', async function () {
const viewsDescriptorsModel = new ViewDescriptorsModel(contextKeyService);
const model = new ViewsModel(viewsDescriptorsModel);
const seq = new ViewDescriptorSequence(model);
container = ViewContainerRegistry.registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const target = disposableStore.add(new ViewDescriptorSequence(testObject));
const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1', when: ContextKeyExpr.equals('showview1', true) };
const view2: IViewDescriptor = { id: 'view2', ctorDescriptor: null!, name: 'Test View 2' };
viewsDescriptorsModel.addViews([view1, view2]);
assert.deepEqual(model.visibleViewDescriptors, [view2], 'only view2 should be visible');
assert.deepEqual(seq.elements, [view2], 'only view2 should be visible');
ViewsRegistry.registerViews([view1, view2], container);
assert.deepEqual(testObject.visibleViewDescriptors, [view2], 'only view2 should be visible');
assert.deepEqual(target.elements, [view2], 'only view2 should be visible');
const key = contextKeyService.createKey('showview1', false);
assert.deepEqual(model.visibleViewDescriptors, [view2], 'still only view2 should be visible');
assert.deepEqual(seq.elements, [view2], 'still only view2 should be visible');
assert.deepEqual(testObject.visibleViewDescriptors, [view2], 'still only view2 should be visible');
assert.deepEqual(target.elements, [view2], 'still only view2 should be visible');
key.set(true);
await new Promise(c => setTimeout(c, 30));
assert.deepEqual(model.visibleViewDescriptors, [view1, view2], 'both views should be visible');
assert.deepEqual(seq.elements, [view1, view2], 'both views should be visible');
assert.deepEqual(testObject.visibleViewDescriptors, [view1, view2], 'both views should be visible');
assert.deepEqual(target.elements, [view1, view2], 'both views should be visible');
viewsDescriptorsModel.removeViews([view1, view2]);
ViewsRegistry.deregisterViews([view1, view2], container);
});
test('setVisible', () => {
const viewsDescriptorsModel = new ViewDescriptorsModel(contextKeyService);
const model = new ViewsModel(viewsDescriptorsModel);
const seq = new ViewDescriptorSequence(model);
container = ViewContainerRegistry.registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const target = disposableStore.add(new ViewDescriptorSequence(testObject));
const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1', canToggleVisibility: true };
const view2: IViewDescriptor = { id: 'view2', ctorDescriptor: null!, name: 'Test View 2', canToggleVisibility: true };
const view3: IViewDescriptor = { id: 'view3', ctorDescriptor: null!, name: 'Test View 3', canToggleVisibility: true };
viewsDescriptorsModel.addViews([view1, view2, view3]);
assert.deepEqual(model.visibleViewDescriptors, [view1, view2, view3]);
assert.deepEqual(seq.elements, [view1, view2, view3]);
ViewsRegistry.registerViews([view1, view2, view3], container);
assert.deepEqual(testObject.visibleViewDescriptors, [view1, view2, view3]);
assert.deepEqual(target.elements, [view1, view2, view3]);
model.setVisible('view2', true);
assert.deepEqual(model.visibleViewDescriptors, [view1, view2, view3], 'nothing should happen');
assert.deepEqual(seq.elements, [view1, view2, view3]);
testObject.setVisible('view2', true);
assert.deepEqual(testObject.visibleViewDescriptors, [view1, view2, view3], 'nothing should happen');
assert.deepEqual(target.elements, [view1, view2, view3]);
model.setVisible('view2', false);
assert.deepEqual(model.visibleViewDescriptors, [view1, view3], 'view2 should hide');
assert.deepEqual(seq.elements, [view1, view3]);
testObject.setVisible('view2', false);
assert.deepEqual(testObject.visibleViewDescriptors, [view1, view3], 'view2 should hide');
assert.deepEqual(target.elements, [view1, view3]);
model.setVisible('view1', false);
assert.deepEqual(model.visibleViewDescriptors, [view3], 'view1 should hide');
assert.deepEqual(seq.elements, [view3]);
testObject.setVisible('view1', false);
assert.deepEqual(testObject.visibleViewDescriptors, [view3], 'view1 should hide');
assert.deepEqual(target.elements, [view3]);
model.setVisible('view3', false);
assert.deepEqual(model.visibleViewDescriptors, [], 'view3 shoud hide');
assert.deepEqual(seq.elements, []);
testObject.setVisible('view3', false);
assert.deepEqual(testObject.visibleViewDescriptors, [], 'view3 shoud hide');
assert.deepEqual(target.elements, []);
model.setVisible('view1', true);
assert.deepEqual(model.visibleViewDescriptors, [view1], 'view1 should show');
assert.deepEqual(seq.elements, [view1]);
testObject.setVisible('view1', true);
assert.deepEqual(testObject.visibleViewDescriptors, [view1], 'view1 should show');
assert.deepEqual(target.elements, [view1]);
model.setVisible('view3', true);
assert.deepEqual(model.visibleViewDescriptors, [view1, view3], 'view3 should show');
assert.deepEqual(seq.elements, [view1, view3]);
testObject.setVisible('view3', true);
assert.deepEqual(testObject.visibleViewDescriptors, [view1, view3], 'view3 should show');
assert.deepEqual(target.elements, [view1, view3]);
model.setVisible('view2', true);
assert.deepEqual(model.visibleViewDescriptors, [view1, view2, view3], 'view2 should show');
assert.deepEqual(seq.elements, [view1, view2, view3]);
testObject.setVisible('view2', true);
assert.deepEqual(testObject.visibleViewDescriptors, [view1, view2, view3], 'view2 should show');
assert.deepEqual(target.elements, [view1, view2, view3]);
viewsDescriptorsModel.removeViews([view1, view2, view3]);
assert.deepEqual(model.visibleViewDescriptors, []);
assert.deepEqual(seq.elements, []);
ViewsRegistry.deregisterViews([view1, view2, view3], container);
assert.deepEqual(testObject.visibleViewDescriptors, []);
assert.deepEqual(target.elements, []);
});
test('move', () => {
const viewsDescriptorsModel = new ViewDescriptorsModel(contextKeyService);
const model = new ViewsModel(viewsDescriptorsModel);
const seq = new ViewDescriptorSequence(model);
container = ViewContainerRegistry.registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const target = disposableStore.add(new ViewDescriptorSequence(testObject));
const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1' };
const view2: IViewDescriptor = { id: 'view2', ctorDescriptor: null!, name: 'Test View 2' };
const view3: IViewDescriptor = { id: 'view3', ctorDescriptor: null!, name: 'Test View 3' };
viewsDescriptorsModel.addViews([view1, view2, view3]);
assert.deepEqual(model.visibleViewDescriptors, [view1, view2, view3], 'model views should be OK');
assert.deepEqual(seq.elements, [view1, view2, view3], 'sql views should be OK');
ViewsRegistry.registerViews([view1, view2, view3], container);
assert.deepEqual(testObject.visibleViewDescriptors, [view1, view2, view3], 'model views should be OK');
assert.deepEqual(target.elements, [view1, view2, view3], 'sql views should be OK');
model.move('view3', 'view1');
assert.deepEqual(model.visibleViewDescriptors, [view3, view1, view2], 'view3 should go to the front');
assert.deepEqual(seq.elements, [view3, view1, view2]);
testObject.move('view3', 'view1');
assert.deepEqual(testObject.visibleViewDescriptors, [view3, view1, view2], 'view3 should go to the front');
assert.deepEqual(target.elements, [view3, view1, view2]);
model.move('view1', 'view2');
assert.deepEqual(model.visibleViewDescriptors, [view3, view2, view1], 'view1 should go to the end');
assert.deepEqual(seq.elements, [view3, view2, view1]);
testObject.move('view1', 'view2');
assert.deepEqual(testObject.visibleViewDescriptors, [view3, view2, view1], 'view1 should go to the end');
assert.deepEqual(target.elements, [view3, view2, view1]);
model.move('view1', 'view3');
assert.deepEqual(model.visibleViewDescriptors, [view1, view3, view2], 'view1 should go to the front');
assert.deepEqual(seq.elements, [view1, view3, view2]);
testObject.move('view1', 'view3');
assert.deepEqual(testObject.visibleViewDescriptors, [view1, view3, view2], 'view1 should go to the front');
assert.deepEqual(target.elements, [view1, view3, view2]);
model.move('view2', 'view3');
assert.deepEqual(model.visibleViewDescriptors, [view1, view2, view3], 'view2 should go to the middle');
assert.deepEqual(seq.elements, [view1, view2, view3]);
testObject.move('view2', 'view3');
assert.deepEqual(testObject.visibleViewDescriptors, [view1, view2, view3], 'view2 should go to the middle');
assert.deepEqual(target.elements, [view1, view2, view3]);
});
test('view states', async function () {
const viewStates = new Map<string, IViewState>();
viewStates.set('view1', { visibleGlobal: false, collapsed: false, visibleWorkspace: undefined });
const viewsDescriptorsModel = new ViewDescriptorsModel(contextKeyService);
const model = new ViewsModel(viewsDescriptorsModel, viewStates);
const seq = new ViewDescriptorSequence(model);
storageService.store(`${container.id}.state.hidden`, JSON.stringify([{ id: 'view1', isHidden: true }]), StorageScope.GLOBAL);
container = ViewContainerRegistry.registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const target = disposableStore.add(new ViewDescriptorSequence(testObject));
assert.equal(model.visibleViewDescriptors.length, 0);
assert.equal(seq.elements.length, 0);
assert.equal(testObject.visibleViewDescriptors.length, 0);
assert.equal(target.elements.length, 0);
const viewDescriptor: IViewDescriptor = {
id: 'view1',
@@ -259,20 +272,19 @@ suite('ContributableViewsModel', () => {
name: 'Test View 1'
};
viewsDescriptorsModel.addViews([viewDescriptor]);
assert.equal(model.visibleViewDescriptors.length, 0, 'view should not appear since it was set not visible in view state');
assert.equal(seq.elements.length, 0);
ViewsRegistry.registerViews([viewDescriptor], container);
assert.equal(testObject.visibleViewDescriptors.length, 0, 'view should not appear since it was set not visible in view state');
assert.equal(target.elements.length, 0);
});
test('view states and when contexts', async function () {
const viewStates = new Map<string, IViewState>();
viewStates.set('view1', { visibleGlobal: false, collapsed: false, visibleWorkspace: undefined });
const viewsDescriptorsModel = new ViewDescriptorsModel(contextKeyService);
const model = new ViewsModel(viewsDescriptorsModel, viewStates);
const seq = new ViewDescriptorSequence(model);
storageService.store(`${container.id}.state.hidden`, JSON.stringify([{ id: 'view1', isHidden: true }]), StorageScope.GLOBAL);
container = ViewContainerRegistry.registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const target = disposableStore.add(new ViewDescriptorSequence(testObject));
assert.equal(model.visibleViewDescriptors.length, 0);
assert.equal(seq.elements.length, 0);
assert.equal(testObject.visibleViewDescriptors.length, 0);
assert.equal(target.elements.length, 0);
const viewDescriptor: IViewDescriptor = {
id: 'view1',
@@ -281,29 +293,28 @@ suite('ContributableViewsModel', () => {
when: ContextKeyExpr.equals('showview1', true)
};
viewsDescriptorsModel.addViews([viewDescriptor]);
assert.equal(model.visibleViewDescriptors.length, 0, 'view should not appear since context isnt in');
assert.equal(seq.elements.length, 0);
ViewsRegistry.registerViews([viewDescriptor], container);
assert.equal(testObject.visibleViewDescriptors.length, 0, 'view should not appear since context isnt in');
assert.equal(target.elements.length, 0);
const key = contextKeyService.createKey('showview1', false);
assert.equal(model.visibleViewDescriptors.length, 0, 'view should still not appear since showview1 isnt true');
assert.equal(seq.elements.length, 0);
assert.equal(testObject.visibleViewDescriptors.length, 0, 'view should still not appear since showview1 isnt true');
assert.equal(target.elements.length, 0);
key.set(true);
await new Promise(c => setTimeout(c, 30));
assert.equal(model.visibleViewDescriptors.length, 0, 'view should still not appear since it was set not visible in view state');
assert.equal(seq.elements.length, 0);
assert.equal(testObject.visibleViewDescriptors.length, 0, 'view should still not appear since it was set not visible in view state');
assert.equal(target.elements.length, 0);
});
test('view states and when contexts multiple views', async function () {
const viewStates = new Map<string, IViewState>();
viewStates.set('view1', { visibleGlobal: false, collapsed: false, visibleWorkspace: undefined });
const viewsDescriptorsModel = new ViewDescriptorsModel(contextKeyService);
const model = new ViewsModel(viewsDescriptorsModel, viewStates);
const seq = new ViewDescriptorSequence(model);
storageService.store(`${container.id}.state.hidden`, JSON.stringify([{ id: 'view1', isHidden: true }]), StorageScope.GLOBAL);
container = ViewContainerRegistry.registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const target = disposableStore.add(new ViewDescriptorSequence(testObject));
assert.equal(model.visibleViewDescriptors.length, 0);
assert.equal(seq.elements.length, 0);
assert.equal(testObject.visibleViewDescriptors.length, 0);
assert.equal(target.elements.length, 0);
const view1: IViewDescriptor = {
id: 'view1',
@@ -323,30 +334,29 @@ suite('ContributableViewsModel', () => {
when: ContextKeyExpr.equals('showview', true)
};
viewsDescriptorsModel.addViews([view1, view2, view3]);
assert.deepEqual(model.visibleViewDescriptors, [view2], 'Only view2 should be visible');
assert.deepEqual(seq.elements, [view2]);
ViewsRegistry.registerViews([view1, view2, view3], container);
assert.deepEqual(testObject.visibleViewDescriptors, [view2], 'Only view2 should be visible');
assert.deepEqual(target.elements, [view2]);
const key = contextKeyService.createKey('showview', false);
assert.deepEqual(model.visibleViewDescriptors, [view2], 'Only view2 should be visible');
assert.deepEqual(seq.elements, [view2]);
assert.deepEqual(testObject.visibleViewDescriptors, [view2], 'Only view2 should be visible');
assert.deepEqual(target.elements, [view2]);
key.set(true);
await new Promise(c => setTimeout(c, 30));
assert.deepEqual(model.visibleViewDescriptors, [view2, view3], 'view3 should be visible');
assert.deepEqual(seq.elements, [view2, view3]);
assert.deepEqual(testObject.visibleViewDescriptors, [view2, view3], 'view3 should be visible');
assert.deepEqual(target.elements, [view2, view3]);
key.set(false);
await new Promise(c => setTimeout(c, 30));
assert.deepEqual(model.visibleViewDescriptors, [view2], 'Only view2 should be visible');
assert.deepEqual(seq.elements, [view2]);
assert.deepEqual(testObject.visibleViewDescriptors, [view2], 'Only view2 should be visible');
assert.deepEqual(target.elements, [view2]);
});
test('remove event is not triggered if view was hidden and removed', async function () {
const viewsDescriptorsModel = new ViewDescriptorsModel(contextKeyService);
const model = new ViewsModel(viewsDescriptorsModel);
const seq = new ViewDescriptorSequence(model);
container = ViewContainerRegistry.registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
const testObject = viewDescriptorService.getViewContainerModel(container);
const target = disposableStore.add(new ViewDescriptorSequence(testObject));
const viewDescriptor: IViewDescriptor = {
id: 'view1',
ctorDescriptor: null!,
@@ -355,21 +365,21 @@ suite('ContributableViewsModel', () => {
canToggleVisibility: true
};
viewsDescriptorsModel.addViews([viewDescriptor]);
ViewsRegistry.registerViews([viewDescriptor], container);
const key = contextKeyService.createKey('showview1', true);
await new Promise(c => setTimeout(c, 30));
assert.equal(model.visibleViewDescriptors.length, 1, 'view should appear after context is set');
assert.equal(seq.elements.length, 1);
assert.equal(testObject.visibleViewDescriptors.length, 1, 'view should appear after context is set');
assert.equal(target.elements.length, 1);
model.setVisible('view1', false);
assert.equal(model.visibleViewDescriptors.length, 0, 'view should disappear after setting visibility to false');
assert.equal(seq.elements.length, 0);
testObject.setVisible('view1', false);
assert.equal(testObject.visibleViewDescriptors.length, 0, 'view should disappear after setting visibility to false');
assert.equal(target.elements.length, 0);
const target = sinon.spy(model.onDidRemove);
const targetEvent = sinon.spy(testObject.onDidRemoveVisibleViewDescriptors);
key.set(false);
await new Promise(c => setTimeout(c, 30));
assert.ok(!target.called, 'remove event should not be called since it is already hidden');
assert.ok(!targetEvent.called, 'remove event should not be called since it is already hidden');
});
});