提交 fcf92f6b 编写于 作者: J Johannes Rieken

deco - split between decorations (reading) and decorations data (providing)

上级 29cccbb9
......@@ -188,7 +188,7 @@ export class ResourceLabel extends IconLabel {
this.options.fileKind !== FileKind.FILE
);
if (deco && this.options.fileDecorations.colors) {
iconLabelOptions.extraClasses.push(deco.labelClasses);
iconLabelOptions.extraClasses.push(deco.labelClassName);
}
if (deco && deco.letter && this.options.fileDecorations.badges) {
iconLabelOptions.badge = {
......
......@@ -7,7 +7,7 @@
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IResourceDecorationsService, IDecorationsProvider, IResourceDecoration } from 'vs/workbench/services/decorations/browser/decorations';
import { IResourceDecorationsService, IDecorationsProvider, IResourceDecorationData } from 'vs/workbench/services/decorations/browser/decorations';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import Event from 'vs/base/common/event';
......@@ -30,7 +30,7 @@ class MarkersDecorationsProvider implements IDecorationsProvider {
this.onDidChange = _markerService.onMarkerChanged;
}
provideDecorations(resource: URI): IResourceDecoration {
provideDecorations(resource: URI): IResourceDecorationData {
const markers = this._markerService.read({ resource })
.sort((a, b) => Severity.compare(a.severity, b.severity));
......
......@@ -6,7 +6,7 @@
'use strict';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IResourceDecorationsService, IDecorationsProvider, IResourceDecoration } from 'vs/workbench/services/decorations/browser/decorations';
import { IResourceDecorationsService, IDecorationsProvider, IResourceDecorationData } from 'vs/workbench/services/decorations/browser/decorations';
import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource } from 'vs/workbench/services/scm/common/scm';
import URI from 'vs/base/common/uri';
......@@ -61,17 +61,16 @@ class SCMDecorationsProvider implements IDecorationsProvider {
this._onDidChange.fire(uris);
}
provideDecorations(uri: URI): IResourceDecoration {
provideDecorations(uri: URI): IResourceDecorationData {
const resource = this._data.get(uri.toString());
if (!resource) {
return undefined;
}
return {
severity: Severity.Info,
tooltip: localize('tooltip', "{0} - {1}", resource.decorations.tooltip, this._provider.label),
tooltip: localize('tooltip', "{0}, {1}", resource.decorations.tooltip, this._provider.label),
color: resource.decorations.color,
letter: resource.decorations.tooltip.charAt(0),
icon: { light: resource.decorations.icon, dark: resource.decorations.iconDark },
letter: resource.decorations.tooltip.charAt(0)
};
}
}
......
......@@ -13,22 +13,26 @@ import { IDisposable } from 'vs/base/common/lifecycle';
export const IResourceDecorationsService = createDecorator<IResourceDecorationsService>('IFileDecorationsService');
export interface IResourceDecoration {
export interface IResourceDecorationData {
readonly severity: Severity;
readonly color?: ColorIdentifier;
readonly letter?: string;
readonly tooltip?: string;
readonly icon?: { light: URI, dark: URI };
readonly leafOnly?: boolean;
}
labelClasses?: string;
badgeClassName?: string;
export interface IResourceDecoration {
readonly _decoBrand: undefined;
readonly severity: Severity;
readonly letter?: string;
readonly tooltip?: string;
readonly labelClassName?: string;
readonly badgeClassName?: string;
}
export interface IDecorationsProvider {
readonly label: string;
readonly onDidChange: Event<URI[]>;
provideDecorations(uri: URI): IResourceDecoration | Thenable<IResourceDecoration>;
provideDecorations(uri: URI): IResourceDecorationData | Thenable<IResourceDecorationData>;
}
export interface IResourceDecorationChangeEvent {
......
......@@ -7,7 +7,7 @@
import URI from 'vs/base/common/uri';
import Severity from 'vs/base/common/severity';
import Event, { Emitter, debounceEvent, any } from 'vs/base/common/event';
import { IResourceDecorationsService, IResourceDecoration, IResourceDecorationChangeEvent, IDecorationsProvider } from './decorations';
import { IResourceDecorationsService, IResourceDecoration, IResourceDecorationChangeEvent, IDecorationsProvider, IResourceDecorationData } from './decorations';
import { TernarySearchTree } from 'vs/base/common/map';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { isThenable } from 'vs/base/common/async';
......@@ -17,6 +17,79 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { listActiveSelectionForeground, ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
class DecorationColors {
private readonly _disposables: IDisposable[];
private readonly _styleElement = createStyleSheet();
private readonly _classNames = new IdGenerator('monaco-decoration-styles-');
private readonly _classNames2ColorIds = new Map<string, [string, string]>();
constructor(
private _themeService: IThemeService,
) {
this._disposables = [
this._themeService.onThemeChange(this._onThemeChange, this),
];
}
dispose(): void {
dispose(this._disposables);
this._styleElement.innerHTML = '';
}
makeResourceDecoration(decoration: IResourceDecorationData): IResourceDecoration {
if (!decoration) {
return undefined;
}
let { severity, letter, tooltip } = decoration;
let labelClassName, badgeClassName;
let tuple = this._classNames2ColorIds.get(decoration.color);
if (tuple) {
// from cache
labelClassName = tuple[0];
badgeClassName = tuple[1];
} else {
// new css rules
labelClassName = this._classNames.nextId();
badgeClassName = this._classNames.nextId();
this._classNames2ColorIds.set(decoration.color, [labelClassName, badgeClassName]);
this._createCssRules(labelClassName, badgeClassName, decoration.color);
}
return {
_decoBrand: undefined,
severity,
letter,
tooltip,
labelClassName,
badgeClassName
};
}
private _onThemeChange(): void {
this._classNames2ColorIds.forEach((tuple, color) => {
const [labelClassName, badgeClassName] = tuple;
removeCSSRulesContainingSelector(labelClassName, this._styleElement);
removeCSSRulesContainingSelector(badgeClassName, this._styleElement);
this._createCssRules(labelClassName, badgeClassName, color);
});
}
private _createCssRules(labelClassName: string, badgeClassName: string, color: ColorIdentifier): void {
const theme = this._themeService.getTheme();
// label
createCSSRule(`.${labelClassName}`, `color: ${theme.getColor(color)}`, this._styleElement);
createCSSRule(`.selected .${labelClassName}`, `color: ${theme.getColor(listActiveSelectionForeground)}`, this._styleElement);
// badge
createCSSRule(`.${badgeClassName}`, `background-color: ${theme.getColor(color)}; color: ${theme.getColor(listActiveSelectionForeground)};`, this._styleElement);
}
}
class FileDecorationChangeEvent implements IResourceDecorationChangeEvent {
private readonly _data = TernarySearchTree.forPaths<boolean>();
......@@ -49,6 +122,7 @@ class DecorationProviderWrapper {
private readonly _dispoable: IDisposable;
constructor(
private readonly _decorationStyles: DecorationColors,
private readonly _provider: IDecorationsProvider,
private readonly _emitter: Emitter<URI | URI[]>
) {
......@@ -92,7 +166,7 @@ class DecorationProviderWrapper {
const childTree = this._data.findSuperstr(key);
if (childTree) {
childTree.forEach(([, value]) => {
if (value && !isThenable<void>(value) && !value.leafOnly) {
if (value && !isThenable<void>(value)) {
callback(value, true);
}
});
......@@ -102,88 +176,27 @@ class DecorationProviderWrapper {
private _fetchData(uri: URI): IResourceDecoration {
const decoOrThenable = this._provider.provideDecorations(uri);
if (!isThenable(decoOrThenable)) {
const dataOrThenable = this._provider.provideDecorations(uri);
if (!isThenable(dataOrThenable)) {
// sync -> we have a result now
this._data.set(uri.toString(), decoOrThenable || null);
this._emitter.fire(uri);
return decoOrThenable;
return this._keepItem(uri, dataOrThenable);
} else {
// async -> we have a result soon
const request = Promise.resolve(decoOrThenable)
.then(data => {
this._data.set(uri.toString(), data || null);
this._emitter.fire(uri);
})
const request = Promise.resolve(dataOrThenable)
.then(data => this._keepItem(uri, data))
.catch(_ => this._data.delete(uri.toString()));
this._data.set(uri.toString(), request);
return undefined;
}
}
}
class DecorationColors {
private readonly _disposables: IDisposable[];
private readonly _styleElement = createStyleSheet();
private readonly _classNames = new IdGenerator('monaco-decoration-styles-');
private readonly _classNames2ColorIds = new Map<string, [string, string]>();
constructor(
private _themeService: IThemeService,
) {
this._disposables = [
this._themeService.onThemeChange(this._onThemeChange, this),
];
}
dispose(): void {
dispose(this._disposables);
this._styleElement.innerHTML = '';
}
ensureCssStyles(decoration: IResourceDecoration): void {
if (!decoration || !decoration.color) {
return;
}
const tuple = this._classNames2ColorIds.get(decoration.color);
if (tuple) {
// from cache
decoration.labelClasses = tuple[0];
decoration.badgeClassName = tuple[1];
return;
}
let labelClassName = this._classNames.nextId();
let badgeClassName = this._classNames.nextId();
this._classNames2ColorIds.set(decoration.color, [labelClassName, badgeClassName]);
decoration.labelClasses = labelClassName;
decoration.badgeClassName = badgeClassName;
this._createCssRules(labelClassName, badgeClassName, decoration.color);
}
private _onThemeChange(): void {
this._classNames2ColorIds.forEach((tuple, color) => {
const [labelClassName, badgeClassName] = tuple;
removeCSSRulesContainingSelector(labelClassName, this._styleElement);
removeCSSRulesContainingSelector(badgeClassName, this._styleElement);
this._createCssRules(labelClassName, badgeClassName, color);
});
}
private _createCssRules(labelClassName: string, badgeClassName: string, color: ColorIdentifier): void {
const theme = this._themeService.getTheme();
// label
createCSSRule(`.${labelClassName}`, `color: ${theme.getColor(color)}`, this._styleElement);
createCSSRule(`.selected .${labelClassName}`, `color: ${theme.getColor(listActiveSelectionForeground)}`, this._styleElement);
// badge
createCSSRule(`.${badgeClassName}`, `background-color: ${theme.getColor(color)}; color: ${theme.getColor(listActiveSelectionForeground)};`, this._styleElement);
private _keepItem(uri: URI, data: IResourceDecorationData): IResourceDecoration {
let deco = data ? this._decorationStyles.makeResourceDecoration(data) : null;
this._data.set(uri.toString(), deco);
this._emitter.fire(uri);
return deco;
}
}
......@@ -216,7 +229,11 @@ export class FileDecorationsService implements IResourceDecorationsService {
registerDecortionsProvider(provider: IDecorationsProvider): IDisposable {
const wrapper = new DecorationProviderWrapper(provider, this._onDidChangeDecorationsDelayed);
const wrapper = new DecorationProviderWrapper(
this._decorationStyles,
provider,
this._onDidChangeDecorationsDelayed
);
const remove = this._data.push(wrapper);
return {
dispose: () => {
......@@ -237,13 +254,13 @@ export class FileDecorationsService implements IResourceDecorationsService {
if (isChild && top === candidate) {
// only bubble up color
top = {
_decoBrand: undefined,
severity: top.severity,
color: top.color
labelClassName: top.labelClassName
};
}
});
}
this._decorationStyles.ensureCssStyles(top);
return top;
}
......
......@@ -7,7 +7,7 @@
import * as assert from 'assert';
import { FileDecorationsService } from 'vs/workbench/services/decorations/browser/decorationsService';
import { IDecorationsProvider, IResourceDecoration } from 'vs/workbench/services/decorations/browser/decorations';
import { IDecorationsProvider, IResourceDecorationData } from 'vs/workbench/services/decorations/browser/decorations';
import URI from 'vs/base/common/uri';
import Event, { toPromise } from 'vs/base/common/event';
import Severity from 'vs/base/common/severity';
......@@ -34,10 +34,11 @@ suite('DecorationsService', function () {
readonly onDidChange: Event<URI[]> = Event.None;
provideDecorations(uri: URI) {
callCounter += 1;
return new Promise<IResourceDecoration>(resolve => {
return new Promise<IResourceDecorationData>(resolve => {
setTimeout(() => resolve({
severity: Severity.Info,
color: 'someBlue'
color: 'someBlue',
letter: 'T'
}));
});
}
......@@ -52,7 +53,7 @@ suite('DecorationsService', function () {
assert.equal(e.affectsResource(uri), true);
// sync result
assert.deepEqual(service.getTopDecoration(uri, false), { severity: Severity.Info, color: 'someBlue' });
assert.deepEqual(service.getTopDecoration(uri, false).letter, 'T');
assert.equal(callCounter, 1);
});
});
......@@ -67,12 +68,12 @@ suite('DecorationsService', function () {
readonly onDidChange: Event<URI[]> = Event.None;
provideDecorations(uri: URI) {
callCounter += 1;
return { severity: Severity.Info, color: 'someBlue' };
return { severity: Severity.Info, color: 'someBlue', letter: 'Z' };
}
});
// trigger -> sync
assert.deepEqual(service.getTopDecoration(uri, false), { severity: Severity.Info, color: 'someBlue' });
assert.deepEqual(service.getTopDecoration(uri, false).letter, 'Z');
assert.equal(callCounter, 1);
});
......@@ -85,12 +86,12 @@ suite('DecorationsService', function () {
readonly onDidChange: Event<URI[]> = Event.None;
provideDecorations(uri: URI) {
callCounter += 1;
return { severity: Severity.Info, color: 'someBlue' };
return { severity: Severity.Info, color: 'someBlue', letter: 'J' };
}
});
// trigger -> sync
assert.deepEqual(service.getTopDecoration(uri, false), { severity: Severity.Info, color: 'someBlue' });
assert.deepEqual(service.getTopDecoration(uri, false).letter, 'J');
assert.equal(callCounter, 1);
// un-register -> ensure good event
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册