提交 7f9ff2b1 编写于 作者: J Johannes Rieken

add FileDecorationsService and use it with markers

上级 a57c8ec7
......@@ -98,6 +98,8 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/workspaceEditingService';
import URI from 'vs/base/common/uri';
import { FileDecorationsService } from 'vs/workbench/services/fileDecorations/browser/fileDecorationsService';
import { IFileDecorationsService } from 'vs/workbench/services/fileDecorations/browser/fileDecorations';
export const MessagesVisibleContext = new RawContextKey<boolean>('globalMessageVisible', false);
export const EditorsVisibleContext = new RawContextKey<boolean>('editorIsOpen', false);
......@@ -583,6 +585,9 @@ export class Workbench implements IPartService {
// Text File Service
serviceCollection.set(ITextFileService, new SyncDescriptor(TextFileService));
// File Decorations
serviceCollection.set(IFileDecorationsService, new SyncDescriptor(FileDecorationsService));
// SCM Service
serviceCollection.set(ISCMService, new SyncDescriptor(SCMService));
......
......@@ -45,6 +45,7 @@ import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/th
import { isLinux } from 'vs/base/common/platform';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { IFileDecorationsService } from 'vs/workbench/services/fileDecorations/browser/fileDecorations';
export interface IExplorerViewOptions extends IViewletViewOptions {
viewletState: FileViewletState;
......@@ -98,7 +99,8 @@ export class ExplorerView extends ViewsViewletPanel {
@IContextKeyService contextKeyService: IContextKeyService,
@IConfigurationService private configurationService: IConfigurationService,
@IWorkbenchThemeService private themeService: IWorkbenchThemeService,
@IEnvironmentService private environmentService: IEnvironmentService
@IEnvironmentService private environmentService: IEnvironmentService,
@IFileDecorationsService private fileDecorationsService: IFileDecorationsService
) {
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService);
......@@ -166,6 +168,7 @@ export class ExplorerView extends ViewsViewletPanel {
this.disposables.push(this.themeService.onDidFileIconThemeChange(onFileIconThemeChange));
this.disposables.push(this.contextService.onDidChangeWorkspaceFolders(e => this.refreshFromEvent(e.added)));
this.disposables.push(this.contextService.onDidChangeWorkbenchState(e => this.refreshFromEvent()));
this.disposables.push(this.fileDecorationsService.onDidChangeFileDecoration(this.onDidChangeFileDecorations, this));
onFileIconThemeChange(this.themeService.getFileIconTheme());
}
......@@ -681,6 +684,19 @@ export class ExplorerView extends ViewsViewletPanel {
}));
}
private onDidChangeFileDecorations(uris: URI[]): void {
let seen = new Set<FileStat>();
let stack = uris.map(uri => this.model.findClosest(uri));
while (stack.length > 0) {
let stat = stack.shift();
if (stat && !seen.has(stat)) {
this.explorerViewer.refresh(stat, false);
stack.push(stat.parent);
seen.add(stat);
}
}
}
private refreshFromEvent(newRoots: IWorkspaceFolder[] = []): void {
if (this.isVisible()) {
this.explorerRefreshDelayer.trigger(() => {
......
......@@ -58,6 +58,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { getPathLabel } from 'vs/base/common/labels';
import { extractResources } from 'vs/base/browser/dnd';
import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IFileDecorationsService } from 'vs/workbench/services/fileDecorations/browser/fileDecorations';
export class FileDataSource implements IDataSource {
constructor(
......@@ -290,7 +291,8 @@ export class FileRenderer implements IRenderer {
state: FileViewletState,
@IContextViewService private contextViewService: IContextViewService,
@IInstantiationService private instantiationService: IInstantiationService,
@IThemeService private themeService: IThemeService
@IThemeService private themeService: IThemeService,
@IFileDecorationsService private decorationsService: IFileDecorationsService
) {
this.state = state;
}
......@@ -324,6 +326,9 @@ export class FileRenderer implements IRenderer {
extraClasses.push('nonexistent-root');
}
templateData.label.setFile(stat.resource, { hidePath: true, fileKind: stat.isRoot ? FileKind.ROOT_FOLDER : stat.isDirectory ? FileKind.FOLDER : FileKind.FILE, extraClasses });
let top = this.decorationsService.getTopDecoration(stat.resource, stat.isDirectory);
templateData.label.element.style.color = top ? top.color.toString() : '';
}
// Input Box
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions';
import { IMarkerService, IMarker } from 'vs/platform/markers/common/markers';
import { IFileDecorationsService, DecorationType, IFileDecorationData } from 'vs/workbench/services/fileDecorations/browser/fileDecorations';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { Registry } from 'vs/platform/registry/common/platform';
import Severity from 'vs/base/common/severity';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { editorErrorForeground, editorWarningForeground } from 'vs/editor/common/view/editorColorRegistry';
class MarkersFileDecorations implements IWorkbenchContribution {
private readonly _disposables: IDisposable[];
private readonly _type: DecorationType;
constructor(
@IMarkerService private _markerService: IMarkerService,
@IFileDecorationsService private _decorationsService: IFileDecorationsService,
@IThemeService private _themeService: IThemeService
) {
//
this._disposables = [
this._markerService.onMarkerChanged(this._onDidChangeMarker, this),
this._type = this._decorationsService.registerDecorationType(localize('errorAndWarnings', "Errors & Warnings"))
];
}
dispose(): void {
dispose(this._disposables);
}
getId(): string {
return 'markers.MarkersFileDecorations';
}
private _onDidChangeMarker(resources: URI[]): void {
for (const resource of resources) {
const markers = this._markerService.read({ resource });
if (!isFalsyOrEmpty(markers)) {
const data = markers.map(this._toFileDecorationData, this);
this._decorationsService.setFileDecorations(this._type, resource, data);
} else {
this._decorationsService.unsetFileDecorations(this._type, resource);
}
}
}
private _toFileDecorationData(marker: IMarker): IFileDecorationData {
const { message, severity } = marker;
const color = this._themeService.getTheme().getColor(severity === Severity.Error ? editorErrorForeground : editorWarningForeground);
return { message, severity, color };
}
}
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(MarkersFileDecorations);
......@@ -17,6 +17,8 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { MarkersPanel } from 'vs/workbench/parts/markers/browser/markersPanel';
import './markersFileDecorations';
export function registerContributions(): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
......@@ -66,4 +68,4 @@ export function registerContributions(): void {
// Retaining old action to show errors and warnings, so that custom bindings to this action for existing users works.
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleErrorsAndWarningsAction, ToggleErrorsAndWarningsAction.ID, ToggleErrorsAndWarningsAction.LABEL), 'Show Errors and Warnings');
}
\ No newline at end of file
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Color } from 'vs/base/common/color';
import URI from 'vs/base/common/uri';
import Event from 'vs/base/common/event';
import Severity from 'vs/base/common/severity';
export const IFileDecorationsService = createDecorator<IFileDecorationsService>('IFileDecorationsService');
export interface IFileDecoration {
readonly type: DecorationType;
readonly message: string;
readonly color: Color;
readonly severity: Severity;
}
export abstract class DecorationType {
readonly label: string;
protected constructor(label: string) {
this.label = label;
}
dispose(): void {
//
}
}
export interface IFileDecorationData {
message: string;
color: Color;
severity: Severity;
}
export interface IFileDecorationsService {
readonly _serviceBrand: any;
readonly onDidChangeFileDecoration: Event<URI[]>;
registerDecorationType(label: string): DecorationType;
setFileDecorations(type: DecorationType, target: URI, data: IFileDecorationData[]): void;
unsetFileDecorations(type: DecorationType, target: URI): void;
getDecorations(uri: URI, includeChildren: boolean): IFileDecoration[];
getTopDecoration(uri: URI, includeChildren: boolean): IFileDecoration;
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import Severity from 'vs/base/common/severity';
import Event, { Emitter, debounceEvent } from 'vs/base/common/event';
import { IFileDecorationsService, IFileDecoration, DecorationType, IFileDecorationData } from 'vs/workbench/services/fileDecorations/browser/fileDecorations';
import { TernarySearchTree } from 'vs/base/common/map';
import { mergeSort, isFalsyOrEmpty } from 'vs/base/common/arrays';
export class FileDecorationsService implements IFileDecorationsService {
readonly _serviceBrand;
private readonly _onDidChangeFileDecoration = new Emitter<URI>();
private readonly _types = new Map<DecorationType, TernarySearchTree<IFileDecoration[]>>();
readonly onDidChangeFileDecoration: Event<URI[]> = debounceEvent<URI, URI[]>(
this._onDidChangeFileDecoration.event,
(last, current) => {
if (!last) {
last = [];
}
last.push(current);
return last;
}
);
registerDecorationType(label: string): DecorationType {
const outer = this;
const type = new class extends DecorationType {
constructor() {
super(label);
}
dispose() {
outer._types.delete(type);
}
};
this._types.set(type, TernarySearchTree.forPaths<IFileDecoration[]>());
return type;
}
setFileDecorations(type: DecorationType, target: URI, data: IFileDecorationData[]): void {
let decorations = mergeSort(data.map(data => ({ type, ...data })), FileDecorationsService._compareFileDecorationsBySeverity);
this._types.get(type).set(target.toString(), decorations);
this._onDidChangeFileDecoration.fire(target);
}
unsetFileDecorations(type: DecorationType, target: URI): void {
this._types.get(type).delete(target.toString());
this._onDidChangeFileDecoration.fire(target);
}
getDecorations(uri: URI, includeChildren: boolean): IFileDecoration[] {
let ret: IFileDecoration[] = [];
this._someFileDecoration(uri, includeChildren, decoration => {
ret.push(decoration);
return false;
});
return ret;
}
getTopDecoration(uri: URI, includeChildren: boolean): IFileDecoration {
let top: IFileDecoration;
this._someFileDecoration(uri, includeChildren, decoration => {
// top is the most severe one,
// stop as soon as an error is found
if (!top || FileDecorationsService._compareFileDecorationsBySeverity(top, decoration) > 0) {
top = decoration;
}
return top.severity === Severity.Error;
});
return top;
}
private _someFileDecoration(uri: URI, includeChildren: boolean, callback: (a: IFileDecoration) => boolean): void {
let key = uri.toString();
let done = false;
this._types.forEach(tree => {
if (done) {
return;
}
if (includeChildren) {
let newTree = tree.findSuperstr(key);
if (newTree) {
newTree.forEach(([, data]) => done = done || data.some(callback));
}
} else {
let list = tree.get(key);
if (!isFalsyOrEmpty(list)) {
done = list.some(callback);
}
}
});
}
private static _compareFileDecorationsBySeverity(a: IFileDecoration, b: IFileDecoration): number {
return Severity.compare(a.severity, b.severity);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册