Skip to content

Commit af13dbd

Browse files
committed
ContributableViews class
1 parent 2098851 commit af13dbd

3 files changed

Lines changed: 197 additions & 3 deletions

File tree

src/vs/platform/contextkey/browser/contextKeyService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
88
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
99
import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver';
10-
import { IContextKey, IContext, IContextKeyServiceTarget, IContextKeyService, SET_CONTEXT_COMMAND_ID, ContextKeyExpr, IContextKeyChangeEvent } from 'vs/platform/contextkey/common/contextkey';
10+
import { IContextKey, IContext, IContextKeyServiceTarget, IContextKeyService, SET_CONTEXT_COMMAND_ID, ContextKeyExpr, IContextKeyChangeEvent, IReadableSet } from 'vs/platform/contextkey/common/contextkey';
1111
import { IConfigurationService, IConfigurationChangeEvent, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
1212
import { Event, Emitter, debounceEvent } from 'vs/base/common/event';
1313

@@ -179,7 +179,7 @@ export class ContextKeyChangeEvent implements IContextKeyChangeEvent {
179179
this._keys = this._keys.concat(oneOrManyKeys);
180180
}
181181

182-
affectsSome(keys: Set<string>): boolean {
182+
affectsSome(keys: IReadableSet<string>): boolean {
183183
for (const key of this._keys) {
184184
if (keys.has(key)) {
185185
return true;

src/vs/platform/contextkey/common/contextkey.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,8 +554,12 @@ export interface IContextKeyServiceTarget {
554554

555555
export const IContextKeyService = createDecorator<IContextKeyService>('contextKeyService');
556556

557+
export interface IReadableSet<T> {
558+
has(value: T): boolean;
559+
}
560+
557561
export interface IContextKeyChangeEvent {
558-
affectsSome(keys: Set<string>): boolean;
562+
affectsSome(keys: IReadableSet<string>): boolean;
559563
}
560564

561565
export interface IContextKeyService {
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { IDisposable } from 'vs/base/common/lifecycle';
7+
import { ViewsRegistry, IViewDescriptor, ViewLocation } from 'vs/workbench/common/views';
8+
import { IContextKeyService, IContextKeyChangeEvent, IReadableSet } from 'vs/platform/contextkey/common/contextkey';
9+
import { Event, chain, filterEvent, Emitter } from 'vs/base/common/event';
10+
import { binarySearch, findFirst } from 'vs/base/common/arrays';
11+
import { ISplice } from 'vs/base/common/sequence';
12+
13+
function filterViewEvent(location: ViewLocation, event: Event<IViewDescriptor[]>): Event<IViewDescriptor[]> {
14+
return chain(event)
15+
.map(views => views.filter(view => view.location === location))
16+
.filter(views => views.length > 0)
17+
.event;
18+
}
19+
20+
class CounterSet<T> implements IReadableSet<T> {
21+
22+
private map = new Map<T, number>();
23+
24+
add(value: T): CounterSet<T> {
25+
this.map.set(value, (this.map.get(value) || 0) + 1);
26+
return this;
27+
}
28+
29+
delete(value: T): boolean {
30+
let counter = this.map.get(value) || 0;
31+
32+
if (counter === 0) {
33+
return false;
34+
}
35+
36+
counter--;
37+
38+
if (counter === 0) {
39+
this.map.delete(value);
40+
} else {
41+
this.map.set(value, counter);
42+
}
43+
44+
return true;
45+
}
46+
47+
has(value: T): boolean {
48+
return this.map.has(value);
49+
}
50+
}
51+
52+
export interface IView {
53+
collapsed: boolean;
54+
}
55+
56+
export interface IViewItem {
57+
viewDescriptor: IViewDescriptor;
58+
visible: boolean;
59+
}
60+
61+
function compareViewDescriptors(a: IViewDescriptor, b: IViewDescriptor): number {
62+
if (typeof a.order !== 'number') {
63+
return 1;
64+
} else if (typeof b.order !== 'number') {
65+
return -1;
66+
}
67+
68+
return a.order - b.order;
69+
}
70+
71+
function compareViewItems(a: IViewItem, b: IViewItem): number {
72+
return compareViewDescriptors(a.viewDescriptor, b.viewDescriptor);
73+
}
74+
75+
export class ContributableViews {
76+
77+
private contextKeys = new CounterSet<string>();
78+
private items: IViewItem[] = [];
79+
private disposables: IDisposable[] = [];
80+
81+
private _onDidSplice = new Emitter<ISplice<IViewDescriptor>>();
82+
readonly onDidSplice: Event<ISplice<IViewDescriptor>> = this._onDidSplice.event;
83+
84+
get viewDescriptors(): IViewDescriptor[] {
85+
return this.items
86+
.filter(i => i.visible)
87+
.map(i => i.viewDescriptor);
88+
}
89+
90+
constructor(
91+
location: ViewLocation,
92+
@IContextKeyService private contextKeyService: IContextKeyService
93+
) {
94+
const onRelevantViewsRegistered = filterViewEvent(location, ViewsRegistry.onViewsRegistered);
95+
onRelevantViewsRegistered(this.onViewsRegistered, this, this.disposables);
96+
97+
const onRelevantViewsDeregistered = filterViewEvent(location, ViewsRegistry.onViewsDeregistered);
98+
onRelevantViewsDeregistered(this.onViewsDeregistered, this, this.disposables);
99+
100+
const onRelevantContextChange = filterEvent(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys));
101+
onRelevantContextChange(this.onContextChanged, this);
102+
103+
this.onViewsRegistered(ViewsRegistry.getViews(location));
104+
}
105+
106+
private onViewsRegistered(viewDescriptors: IViewDescriptor[]): any {
107+
for (const viewDescriptor of viewDescriptors) {
108+
const item = {
109+
viewDescriptor,
110+
visible: this.canViewDescriptorBeVisible(viewDescriptor) // TODO: should read from some state?
111+
};
112+
113+
let index = binarySearch(this.items, item, compareViewItems);
114+
console.log('GOT INDEX', index, ~index);
115+
116+
// insert this new view descriptor at the end of same order view descriptors
117+
if (index < 0) {
118+
index = ~index;
119+
120+
while (index < this.items.length && compareViewItems(this.items[index], item) === 0) {
121+
index++;
122+
}
123+
}
124+
125+
this.items.splice(index, 0, item);
126+
127+
if (viewDescriptor.when) {
128+
for (const key of viewDescriptor.when.keys()) {
129+
this.contextKeys.add(key);
130+
}
131+
}
132+
133+
if (item.visible) {
134+
this._onDidSplice.fire({ start: index, deleteCount: 0, toInsert: [viewDescriptor] });
135+
}
136+
}
137+
}
138+
139+
private onViewsDeregistered(viewDescriptors: IViewDescriptor[]): any {
140+
for (const viewDescriptor of viewDescriptors) {
141+
const index = findFirst(this.items, i => i.viewDescriptor.id === viewDescriptor.id);
142+
143+
if (index === -1) {
144+
continue;
145+
}
146+
147+
const item = this.items[index];
148+
this.items.splice(index, 1);
149+
150+
if (viewDescriptor.when) {
151+
for (const key of viewDescriptor.when.keys()) {
152+
this.contextKeys.delete(key);
153+
}
154+
}
155+
156+
if (item.visible) {
157+
this._onDidSplice.fire({ start: index, deleteCount: 1, toInsert: [] });
158+
}
159+
}
160+
}
161+
162+
private onContextChanged(event: IContextKeyChangeEvent): any {
163+
let index = 0;
164+
165+
for (const item of this.items) {
166+
const visible = this.canViewDescriptorBeVisible(item.viewDescriptor);
167+
168+
if (item.visible === visible) {
169+
if (visible) {
170+
index++;
171+
}
172+
173+
continue;
174+
}
175+
176+
if (visible) { // show
177+
this._onDidSplice.fire({ start: index, deleteCount: 0, toInsert: [item.viewDescriptor] });
178+
index++;
179+
} else { // hide
180+
this._onDidSplice.fire({ start: index, deleteCount: 1, toInsert: [] });
181+
}
182+
183+
item.visible = visible;
184+
}
185+
}
186+
187+
private canViewDescriptorBeVisible(viewDescriptor: IViewDescriptor): boolean {
188+
return this.contextKeyService.contextMatchesRules(viewDescriptor.when);
189+
}
190+
}

0 commit comments

Comments
 (0)