Skip to content

Commit fcf92f6

Browse files
committed
deco - split between decorations (reading) and decorations data (providing)
1 parent 29cccbb commit fcf92f6

6 files changed

Lines changed: 119 additions & 98 deletions

File tree

src/vs/workbench/browser/labels.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ export class ResourceLabel extends IconLabel {
188188
this.options.fileKind !== FileKind.FILE
189189
);
190190
if (deco && this.options.fileDecorations.colors) {
191-
iconLabelOptions.extraClasses.push(deco.labelClasses);
191+
iconLabelOptions.extraClasses.push(deco.labelClassName);
192192
}
193193
if (deco && deco.letter && this.options.fileDecorations.badges) {
194194
iconLabelOptions.badge = {

src/vs/workbench/parts/markers/browser/markersFileDecorations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
99
import { IMarkerService } from 'vs/platform/markers/common/markers';
10-
import { IResourceDecorationsService, IDecorationsProvider, IResourceDecoration } from 'vs/workbench/services/decorations/browser/decorations';
10+
import { IResourceDecorationsService, IDecorationsProvider, IResourceDecorationData } from 'vs/workbench/services/decorations/browser/decorations';
1111
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
1212
import URI from 'vs/base/common/uri';
1313
import Event from 'vs/base/common/event';
@@ -30,7 +30,7 @@ class MarkersDecorationsProvider implements IDecorationsProvider {
3030
this.onDidChange = _markerService.onMarkerChanged;
3131
}
3232

33-
provideDecorations(resource: URI): IResourceDecoration {
33+
provideDecorations(resource: URI): IResourceDecorationData {
3434

3535
const markers = this._markerService.read({ resource })
3636
.sort((a, b) => Severity.compare(a.severity, b.severity));

src/vs/workbench/parts/scm/electron-browser/scmFileDecorations.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
'use strict';
77

88
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
9-
import { IResourceDecorationsService, IDecorationsProvider, IResourceDecoration } from 'vs/workbench/services/decorations/browser/decorations';
9+
import { IResourceDecorationsService, IDecorationsProvider, IResourceDecorationData } from 'vs/workbench/services/decorations/browser/decorations';
1010
import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
1111
import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource } from 'vs/workbench/services/scm/common/scm';
1212
import URI from 'vs/base/common/uri';
@@ -61,17 +61,16 @@ class SCMDecorationsProvider implements IDecorationsProvider {
6161
this._onDidChange.fire(uris);
6262
}
6363

64-
provideDecorations(uri: URI): IResourceDecoration {
64+
provideDecorations(uri: URI): IResourceDecorationData {
6565
const resource = this._data.get(uri.toString());
6666
if (!resource) {
6767
return undefined;
6868
}
6969
return {
7070
severity: Severity.Info,
71-
tooltip: localize('tooltip', "{0} - {1}", resource.decorations.tooltip, this._provider.label),
71+
tooltip: localize('tooltip', "{0}, {1}", resource.decorations.tooltip, this._provider.label),
7272
color: resource.decorations.color,
73-
letter: resource.decorations.tooltip.charAt(0),
74-
icon: { light: resource.decorations.icon, dark: resource.decorations.iconDark },
73+
letter: resource.decorations.tooltip.charAt(0)
7574
};
7675
}
7776
}

src/vs/workbench/services/decorations/browser/decorations.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,26 @@ import { IDisposable } from 'vs/base/common/lifecycle';
1313

1414
export const IResourceDecorationsService = createDecorator<IResourceDecorationsService>('IFileDecorationsService');
1515

16-
export interface IResourceDecoration {
16+
export interface IResourceDecorationData {
1717
readonly severity: Severity;
1818
readonly color?: ColorIdentifier;
1919
readonly letter?: string;
2020
readonly tooltip?: string;
21-
readonly icon?: { light: URI, dark: URI };
22-
readonly leafOnly?: boolean;
21+
}
2322

24-
labelClasses?: string;
25-
badgeClassName?: string;
23+
export interface IResourceDecoration {
24+
readonly _decoBrand: undefined;
25+
readonly severity: Severity;
26+
readonly letter?: string;
27+
readonly tooltip?: string;
28+
readonly labelClassName?: string;
29+
readonly badgeClassName?: string;
2630
}
2731

2832
export interface IDecorationsProvider {
2933
readonly label: string;
3034
readonly onDidChange: Event<URI[]>;
31-
provideDecorations(uri: URI): IResourceDecoration | Thenable<IResourceDecoration>;
35+
provideDecorations(uri: URI): IResourceDecorationData | Thenable<IResourceDecorationData>;
3236
}
3337

3438
export interface IResourceDecorationChangeEvent {

src/vs/workbench/services/decorations/browser/decorationsService.ts

Lines changed: 93 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import URI from 'vs/base/common/uri';
88
import Severity from 'vs/base/common/severity';
99
import Event, { Emitter, debounceEvent, any } from 'vs/base/common/event';
10-
import { IResourceDecorationsService, IResourceDecoration, IResourceDecorationChangeEvent, IDecorationsProvider } from './decorations';
10+
import { IResourceDecorationsService, IResourceDecoration, IResourceDecorationChangeEvent, IDecorationsProvider, IResourceDecorationData } from './decorations';
1111
import { TernarySearchTree } from 'vs/base/common/map';
1212
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
1313
import { isThenable } from 'vs/base/common/async';
@@ -17,6 +17,79 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
1717
import { IdGenerator } from 'vs/base/common/idGenerator';
1818
import { listActiveSelectionForeground, ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
1919

20+
21+
class DecorationColors {
22+
23+
private readonly _disposables: IDisposable[];
24+
private readonly _styleElement = createStyleSheet();
25+
private readonly _classNames = new IdGenerator('monaco-decoration-styles-');
26+
private readonly _classNames2ColorIds = new Map<string, [string, string]>();
27+
28+
constructor(
29+
private _themeService: IThemeService,
30+
) {
31+
this._disposables = [
32+
this._themeService.onThemeChange(this._onThemeChange, this),
33+
];
34+
}
35+
36+
dispose(): void {
37+
dispose(this._disposables);
38+
this._styleElement.innerHTML = '';
39+
}
40+
41+
makeResourceDecoration(decoration: IResourceDecorationData): IResourceDecoration {
42+
if (!decoration) {
43+
return undefined;
44+
}
45+
46+
let { severity, letter, tooltip } = decoration;
47+
let labelClassName, badgeClassName;
48+
49+
let tuple = this._classNames2ColorIds.get(decoration.color);
50+
51+
if (tuple) {
52+
// from cache
53+
labelClassName = tuple[0];
54+
badgeClassName = tuple[1];
55+
} else {
56+
// new css rules
57+
labelClassName = this._classNames.nextId();
58+
badgeClassName = this._classNames.nextId();
59+
this._classNames2ColorIds.set(decoration.color, [labelClassName, badgeClassName]);
60+
this._createCssRules(labelClassName, badgeClassName, decoration.color);
61+
}
62+
63+
return {
64+
_decoBrand: undefined,
65+
severity,
66+
letter,
67+
tooltip,
68+
labelClassName,
69+
badgeClassName
70+
};
71+
}
72+
73+
private _onThemeChange(): void {
74+
this._classNames2ColorIds.forEach((tuple, color) => {
75+
const [labelClassName, badgeClassName] = tuple;
76+
removeCSSRulesContainingSelector(labelClassName, this._styleElement);
77+
removeCSSRulesContainingSelector(badgeClassName, this._styleElement);
78+
this._createCssRules(labelClassName, badgeClassName, color);
79+
});
80+
}
81+
82+
private _createCssRules(labelClassName: string, badgeClassName: string, color: ColorIdentifier): void {
83+
const theme = this._themeService.getTheme();
84+
// label
85+
createCSSRule(`.${labelClassName}`, `color: ${theme.getColor(color)}`, this._styleElement);
86+
createCSSRule(`.selected .${labelClassName}`, `color: ${theme.getColor(listActiveSelectionForeground)}`, this._styleElement);
87+
88+
// badge
89+
createCSSRule(`.${badgeClassName}`, `background-color: ${theme.getColor(color)}; color: ${theme.getColor(listActiveSelectionForeground)};`, this._styleElement);
90+
}
91+
}
92+
2093
class FileDecorationChangeEvent implements IResourceDecorationChangeEvent {
2194

2295
private readonly _data = TernarySearchTree.forPaths<boolean>();
@@ -49,6 +122,7 @@ class DecorationProviderWrapper {
49122
private readonly _dispoable: IDisposable;
50123

51124
constructor(
125+
private readonly _decorationStyles: DecorationColors,
52126
private readonly _provider: IDecorationsProvider,
53127
private readonly _emitter: Emitter<URI | URI[]>
54128
) {
@@ -92,7 +166,7 @@ class DecorationProviderWrapper {
92166
const childTree = this._data.findSuperstr(key);
93167
if (childTree) {
94168
childTree.forEach(([, value]) => {
95-
if (value && !isThenable<void>(value) && !value.leafOnly) {
169+
if (value && !isThenable<void>(value)) {
96170
callback(value, true);
97171
}
98172
});
@@ -102,88 +176,27 @@ class DecorationProviderWrapper {
102176

103177
private _fetchData(uri: URI): IResourceDecoration {
104178

105-
const decoOrThenable = this._provider.provideDecorations(uri);
106-
if (!isThenable(decoOrThenable)) {
179+
const dataOrThenable = this._provider.provideDecorations(uri);
180+
if (!isThenable(dataOrThenable)) {
107181
// sync -> we have a result now
108-
this._data.set(uri.toString(), decoOrThenable || null);
109-
this._emitter.fire(uri);
110-
return decoOrThenable;
182+
return this._keepItem(uri, dataOrThenable);
111183

112184
} else {
113185
// async -> we have a result soon
114-
const request = Promise.resolve(decoOrThenable)
115-
.then(data => {
116-
this._data.set(uri.toString(), data || null);
117-
this._emitter.fire(uri);
118-
})
186+
const request = Promise.resolve(dataOrThenable)
187+
.then(data => this._keepItem(uri, data))
119188
.catch(_ => this._data.delete(uri.toString()));
120189

121190
this._data.set(uri.toString(), request);
122191
return undefined;
123192
}
124193
}
125-
}
126-
127-
class DecorationColors {
128-
129-
private readonly _disposables: IDisposable[];
130-
private readonly _styleElement = createStyleSheet();
131-
private readonly _classNames = new IdGenerator('monaco-decoration-styles-');
132-
private readonly _classNames2ColorIds = new Map<string, [string, string]>();
133-
134-
constructor(
135-
private _themeService: IThemeService,
136-
) {
137-
this._disposables = [
138-
this._themeService.onThemeChange(this._onThemeChange, this),
139-
];
140-
}
141-
142-
dispose(): void {
143-
dispose(this._disposables);
144-
this._styleElement.innerHTML = '';
145-
}
146-
147-
ensureCssStyles(decoration: IResourceDecoration): void {
148-
if (!decoration || !decoration.color) {
149-
return;
150-
}
151-
152-
const tuple = this._classNames2ColorIds.get(decoration.color);
153-
if (tuple) {
154-
// from cache
155-
decoration.labelClasses = tuple[0];
156-
decoration.badgeClassName = tuple[1];
157-
return;
158-
}
159-
160-
let labelClassName = this._classNames.nextId();
161-
let badgeClassName = this._classNames.nextId();
162-
163-
this._classNames2ColorIds.set(decoration.color, [labelClassName, badgeClassName]);
164-
decoration.labelClasses = labelClassName;
165-
decoration.badgeClassName = badgeClassName;
166-
167-
this._createCssRules(labelClassName, badgeClassName, decoration.color);
168-
}
169-
170-
private _onThemeChange(): void {
171-
this._classNames2ColorIds.forEach((tuple, color) => {
172-
const [labelClassName, badgeClassName] = tuple;
173-
removeCSSRulesContainingSelector(labelClassName, this._styleElement);
174-
removeCSSRulesContainingSelector(badgeClassName, this._styleElement);
175-
this._createCssRules(labelClassName, badgeClassName, color);
176-
});
177-
}
178194

179-
private _createCssRules(labelClassName: string, badgeClassName: string, color: ColorIdentifier): void {
180-
const theme = this._themeService.getTheme();
181-
// label
182-
createCSSRule(`.${labelClassName}`, `color: ${theme.getColor(color)}`, this._styleElement);
183-
createCSSRule(`.selected .${labelClassName}`, `color: ${theme.getColor(listActiveSelectionForeground)}`, this._styleElement);
184-
185-
// badge
186-
createCSSRule(`.${badgeClassName}`, `background-color: ${theme.getColor(color)}; color: ${theme.getColor(listActiveSelectionForeground)};`, this._styleElement);
195+
private _keepItem(uri: URI, data: IResourceDecorationData): IResourceDecoration {
196+
let deco = data ? this._decorationStyles.makeResourceDecoration(data) : null;
197+
this._data.set(uri.toString(), deco);
198+
this._emitter.fire(uri);
199+
return deco;
187200
}
188201
}
189202

@@ -216,7 +229,11 @@ export class FileDecorationsService implements IResourceDecorationsService {
216229

217230
registerDecortionsProvider(provider: IDecorationsProvider): IDisposable {
218231

219-
const wrapper = new DecorationProviderWrapper(provider, this._onDidChangeDecorationsDelayed);
232+
const wrapper = new DecorationProviderWrapper(
233+
this._decorationStyles,
234+
provider,
235+
this._onDidChangeDecorationsDelayed
236+
);
220237
const remove = this._data.push(wrapper);
221238
return {
222239
dispose: () => {
@@ -237,13 +254,13 @@ export class FileDecorationsService implements IResourceDecorationsService {
237254
if (isChild && top === candidate) {
238255
// only bubble up color
239256
top = {
257+
_decoBrand: undefined,
240258
severity: top.severity,
241-
color: top.color
259+
labelClassName: top.labelClassName
242260
};
243261
}
244262
});
245263
}
246-
this._decorationStyles.ensureCssStyles(top);
247264
return top;
248265
}
249266

src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import * as assert from 'assert';
99
import { FileDecorationsService } from 'vs/workbench/services/decorations/browser/decorationsService';
10-
import { IDecorationsProvider, IResourceDecoration } from 'vs/workbench/services/decorations/browser/decorations';
10+
import { IDecorationsProvider, IResourceDecorationData } from 'vs/workbench/services/decorations/browser/decorations';
1111
import URI from 'vs/base/common/uri';
1212
import Event, { toPromise } from 'vs/base/common/event';
1313
import Severity from 'vs/base/common/severity';
@@ -34,10 +34,11 @@ suite('DecorationsService', function () {
3434
readonly onDidChange: Event<URI[]> = Event.None;
3535
provideDecorations(uri: URI) {
3636
callCounter += 1;
37-
return new Promise<IResourceDecoration>(resolve => {
37+
return new Promise<IResourceDecorationData>(resolve => {
3838
setTimeout(() => resolve({
3939
severity: Severity.Info,
40-
color: 'someBlue'
40+
color: 'someBlue',
41+
letter: 'T'
4142
}));
4243
});
4344
}
@@ -52,7 +53,7 @@ suite('DecorationsService', function () {
5253
assert.equal(e.affectsResource(uri), true);
5354

5455
// sync result
55-
assert.deepEqual(service.getTopDecoration(uri, false), { severity: Severity.Info, color: 'someBlue' });
56+
assert.deepEqual(service.getTopDecoration(uri, false).letter, 'T');
5657
assert.equal(callCounter, 1);
5758
});
5859
});
@@ -67,12 +68,12 @@ suite('DecorationsService', function () {
6768
readonly onDidChange: Event<URI[]> = Event.None;
6869
provideDecorations(uri: URI) {
6970
callCounter += 1;
70-
return { severity: Severity.Info, color: 'someBlue' };
71+
return { severity: Severity.Info, color: 'someBlue', letter: 'Z' };
7172
}
7273
});
7374

7475
// trigger -> sync
75-
assert.deepEqual(service.getTopDecoration(uri, false), { severity: Severity.Info, color: 'someBlue' });
76+
assert.deepEqual(service.getTopDecoration(uri, false).letter, 'Z');
7677
assert.equal(callCounter, 1);
7778
});
7879

@@ -85,12 +86,12 @@ suite('DecorationsService', function () {
8586
readonly onDidChange: Event<URI[]> = Event.None;
8687
provideDecorations(uri: URI) {
8788
callCounter += 1;
88-
return { severity: Severity.Info, color: 'someBlue' };
89+
return { severity: Severity.Info, color: 'someBlue', letter: 'J' };
8990
}
9091
});
9192

9293
// trigger -> sync
93-
assert.deepEqual(service.getTopDecoration(uri, false), { severity: Severity.Info, color: 'someBlue' });
94+
assert.deepEqual(service.getTopDecoration(uri, false).letter, 'J');
9495
assert.equal(callCounter, 1);
9596

9697
// un-register -> ensure good event

0 commit comments

Comments
 (0)