提交 92dd8208 编写于 作者: J Joao Moreno

wip: dirtydiff as a workbench concept

上级 19c20239
......@@ -40,6 +40,8 @@ import 'vs/workbench/parts/search/browser/search.contribution';
import 'vs/workbench/parts/search/browser/searchViewlet'; // can be packaged separately
import 'vs/workbench/parts/search/browser/openAnythingHandler'; // can be packaged separately
import 'vs/workbench/parts/scm/browser/scm.contribution';
import 'vs/workbench/parts/git/electron-browser/git.contribution';
import 'vs/workbench/parts/git/browser/gitQuickOpen';
import 'vs/workbench/parts/git/browser/gitActions.contribution';
......
......@@ -75,6 +75,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { TextFileService } from 'vs/workbench/services/textfile/electron-browser/textFileService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IDirtyDiffService } from 'vs/workbench/services/scm/common/dirtydiff';
import { DirtyDiffService } from 'vs/workbench/services/scm/common/dirtydiffService';
import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
import { ITextModelResolverService } from 'vs/platform/textmodelResolver/common/resolver';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
......@@ -456,6 +458,9 @@ export class Workbench implements IPartService {
// Text File Service
serviceCollection.set(ITextFileService, this.instantiationService.createInstance(TextFileService));
// DirtyDiff Service
serviceCollection.set(IDirtyDiffService, this.instantiationService.createInstance(DirtyDiffService));
// Backup Model Service
serviceCollection.set(IBackupModelService, this.instantiationService.createInstance(BackupModelService));
......
......@@ -8,14 +8,9 @@
import 'vs/css!./media/git.contribution';
import nls = require('vs/nls');
import async = require('vs/base/common/async');
import errors = require('vs/base/common/errors');
import paths = require('vs/base/common/paths');
import lifecycle = require('vs/base/common/lifecycle');
import winjs = require('vs/base/common/winjs.base');
import ext = require('vs/workbench/common/contributions');
import git = require('vs/workbench/parts/git/common/git');
import common = require('vs/editor/common/editorCommon');
import widget = require('vs/editor/browser/codeEditor');
import viewlet = require('vs/workbench/browser/viewlet');
import statusbar = require('vs/workbench/browser/parts/statusbar/statusbar');
import platform = require('vs/platform/platform');
......@@ -30,18 +25,10 @@ import quickopen = require('vs/workbench/browser/quickopen');
import 'vs/workbench/parts/git/browser/gitEditorContributions';
import { IActivityService, ProgressBadge, NumberBadge } from 'vs/workbench/services/activity/common/activityService';
import { IEventService } from 'vs/platform/event/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService } from 'vs/platform/message/common/message';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { RawText } from 'vs/editor/common/model/textModel';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import URI from 'vs/base/common/uri';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { Schemas } from 'vs/base/common/network';
import IGitService = git.IGitService;
......@@ -120,330 +107,6 @@ export class StatusUpdater implements ext.IWorkbenchContribution {
}
}
class DirtyDiffModelDecorator {
static ID = 'vs.git.editor.dirtyDiffDecorator';
static MODIFIED_DECORATION_OPTIONS: common.IModelDecorationOptions = {
linesDecorationsClassName: 'git-dirty-modified-diff-glyph',
isWholeLine: true,
overviewRuler: {
color: 'rgba(0, 122, 204, 0.6)',
darkColor: 'rgba(0, 122, 204, 0.6)',
position: common.OverviewRulerLane.Left
}
};
static ADDED_DECORATION_OPTIONS: common.IModelDecorationOptions = {
linesDecorationsClassName: 'git-dirty-added-diff-glyph',
isWholeLine: true,
overviewRuler: {
color: 'rgba(0, 122, 204, 0.6)',
darkColor: 'rgba(0, 122, 204, 0.6)',
position: common.OverviewRulerLane.Left
}
};
static DELETED_DECORATION_OPTIONS: common.IModelDecorationOptions = {
linesDecorationsClassName: 'git-dirty-deleted-diff-glyph',
isWholeLine: true,
overviewRuler: {
color: 'rgba(0, 122, 204, 0.6)',
darkColor: 'rgba(0, 122, 204, 0.6)',
position: common.OverviewRulerLane.Left
}
};
private modelService: IModelService;
private editorWorkerService: IEditorWorkerService;
private editorService: IWorkbenchEditorService;
private contextService: IWorkspaceContextService;
private gitService: IGitService;
private model: common.IModel;
private _originalContentsURI: URI;
private path: string;
private decorations: string[];
private delayer: async.ThrottledDelayer<void>;
private diffDelayer: async.ThrottledDelayer<void>;
private toDispose: lifecycle.IDisposable[];
constructor(model: common.IModel, path: string,
@IModelService modelService: IModelService,
@IEditorWorkerService editorWorkerService: IEditorWorkerService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IGitService gitService: IGitService
) {
this.modelService = modelService;
this.editorWorkerService = editorWorkerService;
this.editorService = editorService;
this.contextService = contextService;
this.gitService = gitService;
this.model = model;
this._originalContentsURI = model.uri.with({ scheme: Schemas.internal });
this.path = path;
this.decorations = [];
this.delayer = new async.ThrottledDelayer<void>(500);
this.diffDelayer = new async.ThrottledDelayer<void>(200);
this.toDispose = [];
this.toDispose.push(model.onDidChangeContent(() => this.triggerDiff()));
this.toDispose.push(this.gitService.addListener2(git.ServiceEvents.STATE_CHANGED, () => this.onChanges()));
this.toDispose.push(this.gitService.addListener2(git.ServiceEvents.OPERATION_END, e => {
if (e.operation.id !== git.ServiceOperations.BACKGROUND_FETCH) {
this.onChanges();
}
}));
this.onChanges();
}
private onChanges(): void {
if (!this.gitService) {
return;
}
if (this.gitService.getState() !== git.ServiceState.OK) {
return;
}
// go through all interesting models
this.trigger();
}
private trigger(): void {
this.delayer
.trigger(() => this.diffOriginalContents())
.done(null, errors.onUnexpectedError);
}
private diffOriginalContents(): winjs.TPromise<void> {
return this.getOriginalContents()
.then(contents => {
if (!this.model || this.model.isDisposed()) {
return; // disposed
}
if (!contents) {
// untracked file
this.modelService.destroyModel(this._originalContentsURI);
return this.triggerDiff();
}
let originalModel = this.modelService.getModel(this._originalContentsURI);
if (originalModel) {
let contentsRawText = RawText.fromStringWithModelOptions(contents, originalModel);
// return early if nothing has changed
if (originalModel.equals(contentsRawText)) {
return winjs.TPromise.as(null);
}
// we already have the original contents
originalModel.setValueFromRawText(contentsRawText);
} else {
// this is the first time we load the original contents
this.modelService.createModel(contents, null, this._originalContentsURI);
}
return this.triggerDiff();
});
}
private getOriginalContents(): winjs.TPromise<string> {
var gitModel = this.gitService.getModel();
var treeish = gitModel.getStatus().find(this.path, git.StatusType.INDEX) ? '~' : 'HEAD';
return this.gitService.buffer(this.path, treeish);
}
private triggerDiff(): winjs.Promise {
if (!this.diffDelayer) {
return winjs.TPromise.as(null);
}
return this.diffDelayer.trigger(() => {
if (!this.model || this.model.isDisposed()) {
return winjs.TPromise.as<any>([]); // disposed
}
return this.editorWorkerService.computeDirtyDiff(this._originalContentsURI, this.model.uri, true);
}).then((diff: common.IChange[]) => {
if (!this.model || this.model.isDisposed()) {
return; // disposed
}
return this.decorations = this.model.deltaDecorations(this.decorations, DirtyDiffModelDecorator.changesToDecorations(diff || []));
});
}
private static changesToDecorations(diff: common.IChange[]): common.IModelDeltaDecoration[] {
return diff.map((change) => {
var startLineNumber = change.modifiedStartLineNumber;
var endLineNumber = change.modifiedEndLineNumber || startLineNumber;
// Added
if (change.originalEndLineNumber === 0) {
return {
range: {
startLineNumber: startLineNumber, startColumn: 1,
endLineNumber: endLineNumber, endColumn: 1
},
options: DirtyDiffModelDecorator.ADDED_DECORATION_OPTIONS
};
}
// Removed
if (change.modifiedEndLineNumber === 0) {
return {
range: {
startLineNumber: startLineNumber, startColumn: 1,
endLineNumber: startLineNumber, endColumn: 1
},
options: DirtyDiffModelDecorator.DELETED_DECORATION_OPTIONS
};
}
// Modified
return {
range: {
startLineNumber: startLineNumber, startColumn: 1,
endLineNumber: endLineNumber, endColumn: 1
},
options: DirtyDiffModelDecorator.MODIFIED_DECORATION_OPTIONS
};
});
}
public dispose(): void {
this.modelService.destroyModel(this._originalContentsURI);
this.toDispose = lifecycle.dispose(this.toDispose);
if (this.model && !this.model.isDisposed()) {
this.model.deltaDecorations(this.decorations, []);
}
this.model = null;
this.decorations = null;
if (this.delayer) {
this.delayer.cancel();
this.delayer = null;
}
if (this.diffDelayer) {
this.diffDelayer.cancel();
this.diffDelayer = null;
}
}
}
export class DirtyDiffDecorator implements ext.IWorkbenchContribution {
private gitService: IGitService;
private messageService: IMessageService;
private editorService: IWorkbenchEditorService;
private eventService: IEventService;
private contextService: IWorkspaceContextService;
private instantiationService: IInstantiationService;
private models: common.IModel[];
private decorators: { [modelId: string]: DirtyDiffModelDecorator };
private toDispose: lifecycle.IDisposable[];
constructor(
@IGitService gitService: IGitService,
@IMessageService messageService: IMessageService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@IEventService eventService: IEventService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IInstantiationService instantiationService: IInstantiationService
) {
this.gitService = gitService;
this.messageService = messageService;
this.editorService = editorService;
this.eventService = eventService;
this.contextService = contextService;
this.instantiationService = instantiationService;
this.models = [];
this.decorators = Object.create(null);
this.toDispose = [];
this.toDispose.push(editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
this.toDispose.push(gitService.addListener2(git.ServiceEvents.DISPOSE, () => this.dispose()));
}
public getId(): string {
return 'git.DirtyDiffModelDecorator';
}
private onEditorsChanged(): void {
// HACK: This is the best current way of figuring out whether to draw these decorations
// or not. Needs context from the editor, to know whether it is a diff editor, in place editor
// etc.
const repositoryRoot = this.gitService.getModel().getRepositoryRoot();
// If there is no repository root, just wait until that changes
if (typeof repositoryRoot !== 'string') {
this.gitService.addOneTimeDisposableListener(git.ServiceEvents.STATE_CHANGED, () => this.onEditorsChanged());
this.models.forEach(m => this.onModelInvisible(m));
this.models = [];
return;
}
const models = this.editorService.getVisibleEditors()
// map to the editor controls
.map(e => e.getControl())
// only interested in code editor widgets
.filter(c => c instanceof widget.CodeEditor)
// map to models
.map(e => (<widget.CodeEditor>e).getModel())
// remove nulls and duplicates
.filter((m, i, a) => !!m && a.indexOf(m, i + 1) === -1)
// get the associated resource
.map(m => ({ model: m, resource: m.uri }))
// remove nulls
.filter(p => !!p.resource &&
// and invalid resources
(p.resource.scheme === 'file' && paths.isEqualOrParent(p.resource.fsPath, repositoryRoot))
)
// get paths
.map(p => ({ model: p.model, path: paths.normalize(paths.relative(repositoryRoot, p.resource.fsPath)) }))
// remove nulls and inside .git files
.filter(p => !!p.path && p.path.indexOf('.git/') === -1);
var newModels = models.filter(p => this.models.every(m => p.model !== m));
var oldModels = this.models.filter(m => models.every(p => p.model !== m));
newModels.forEach(p => this.onModelVisible(p.model, p.path));
oldModels.forEach(m => this.onModelInvisible(m));
this.models = models.map(p => p.model);
}
private onModelVisible(model: common.IModel, path: string): void {
this.decorators[model.id] = this.instantiationService.createInstance(DirtyDiffModelDecorator, model, path);
}
private onModelInvisible(model: common.IModel): void {
this.decorators[model.id].dispose();
delete this.decorators[model.id];
}
public dispose(): void {
this.toDispose = lifecycle.dispose(this.toDispose);
this.models.forEach(m => this.decorators[m.id].dispose());
this.models = null;
this.decorators = null;
}
}
export const VIEWLET_ID = 'workbench.view.git';
class OpenGitViewletAction extends viewlet.ToggleViewletAction {
......@@ -500,11 +163,6 @@ export function registerContributions(): void {
StatusUpdater
);
// Register DirtyDiffDecorator
(<ext.IWorkbenchContributionsRegistry>platform.Registry.as(ext.Extensions.Workbench)).registerWorkbenchContribution(
DirtyDiffDecorator
);
// Register Quick Open for git
(<quickopen.IQuickOpenRegistry>platform.Registry.as(quickopen.Extensions.Quickopen)).registerQuickOpenHandler(
new quickopen.QuickOpenHandlerDescriptor(
......
/*---------------------------------------------------------------------------------------------
* 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 { IModelService } from 'vs/editor/common/services/modelService';
import URI from 'vs/base/common/uri';
import { dispose } from 'vs/base/common/lifecycle';
import { onUnexpectedError } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { IModel } from 'vs/editor/common/editorCommon';
import { ITextModelResolverService, ITextModelContentProvider } from 'vs/platform/textmodelResolver/common/resolver';
import { IDirtyDiffTextDocumentProvider, IDirtyDiffService } from 'vs/workbench/services/scm/common/dirtydiff';
import { IGitService, StatusType, ServiceEvents, ServiceOperations, ServiceState } from 'vs/workbench/parts/git/common/git';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
export class GitContentProvider implements IWorkbenchContribution, ITextModelContentProvider, IDirtyDiffTextDocumentProvider {
constructor(
@ITextModelResolverService textModelResolverService: ITextModelResolverService,
@IModelService private modelService: IModelService,
@IDirtyDiffService private dirtyDiffService: IDirtyDiffService,
@IGitService private gitService: IGitService
) {
this.dirtyDiffService.registerDirtyDiffTextDocumentProvider(this);
textModelResolverService.registerTextModelContentProvider('git-index', this);
}
getDirtyDiffTextDocument(resource: URI): TPromise<URI> {
return TPromise.as(resource.with({ scheme: 'git-index' }));
}
provideTextContent(uri: URI): TPromise<IModel> {
if (uri.scheme !== 'git-index') {
return null;
}
const gitModel = this.gitService.getModel();
const path = uri.fsPath;
const treeish = gitModel.getStatus().find(path, StatusType.INDEX) ? '~' : 'HEAD';
return this.gitService.buffer(path, treeish)
.then(contents => this.modelService.createModel(contents, null, uri))
.then(model => {
const trigger = () => {
this.gitService.buffer(path, treeish).
then(contents => model.setValue(contents))
.done(null, onUnexpectedError);
};
const onChanges = () => {
if (this.gitService.getState() !== ServiceState.OK) {
return;
}
trigger();
};
const disposables = [
this.gitService.addListener2(ServiceEvents.STATE_CHANGED, onChanges),
this.gitService.addListener2(ServiceEvents.OPERATION_END, e => {
if (e.operation.id !== ServiceOperations.BACKGROUND_FETCH) {
onChanges();
}
})
];
model.onWillDispose(() => {
dispose(disposables);
});
return model;
});
}
getId(): string {
return 'git.contentprovider';
}
}
\ No newline at end of file
......@@ -10,15 +10,20 @@ import { IGitService } from 'vs/workbench/parts/git/common/git';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { Registry } from 'vs/platform/platform';
import { Extensions as WorkbenchExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actionRegistry';
import { CloneAction } from './gitActions';
import { GitContentProvider } from '../common/gitDirtyDiff';
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actionRegistry';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
registerContributions();
// Register Service
registerSingleton(IGitService, ElectronGitService);
const workbenchActionRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchExtensions.WorkbenchActions);
const category = localize('git', "Git");
workbenchActionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloneAction, CloneAction.ID, CloneAction.LABEL), 'Git: Clone', category);
\ No newline at end of file
Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions)
.registerWorkbenchAction(new SyncActionDescriptor(CloneAction, CloneAction.ID, CloneAction.LABEL), 'Git: Clone', category);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(GitContentProvider);
\ 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 { ThrottledDelayer } from 'vs/base/common/async';
import * as lifecycle from 'vs/base/common/lifecycle';
import * as winjs from 'vs/base/common/winjs.base';
import * as ext from 'vs/workbench/common/contributions';
import * as common from 'vs/editor/common/editorCommon';
import * as widget from 'vs/editor/browser/codeEditor';
import { IEventService } from 'vs/platform/event/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService } from 'vs/platform/message/common/message';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import URI from 'vs/base/common/uri';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IDirtyDiffService } from 'vs/workbench/services/scm/common/dirtydiff';
class DirtyDiffModelDecorator {
static MODIFIED_DECORATION_OPTIONS: common.IModelDecorationOptions = {
linesDecorationsClassName: 'git-dirty-modified-diff-glyph',
isWholeLine: true,
overviewRuler: {
color: 'rgba(0, 122, 204, 0.6)',
darkColor: 'rgba(0, 122, 204, 0.6)',
position: common.OverviewRulerLane.Left
}
};
static ADDED_DECORATION_OPTIONS: common.IModelDecorationOptions = {
linesDecorationsClassName: 'git-dirty-added-diff-glyph',
isWholeLine: true,
overviewRuler: {
color: 'rgba(0, 122, 204, 0.6)',
darkColor: 'rgba(0, 122, 204, 0.6)',
position: common.OverviewRulerLane.Left
}
};
static DELETED_DECORATION_OPTIONS: common.IModelDecorationOptions = {
linesDecorationsClassName: 'git-dirty-deleted-diff-glyph',
isWholeLine: true,
overviewRuler: {
color: 'rgba(0, 122, 204, 0.6)',
darkColor: 'rgba(0, 122, 204, 0.6)',
position: common.OverviewRulerLane.Left
}
};
private decorations: string[];
private diffDelayer: ThrottledDelayer<void>;
private toDispose: lifecycle.IDisposable[];
constructor(
private model: common.IModel,
private uri: URI,
@IDirtyDiffService private dirtyDiffService: IDirtyDiffService,
@IModelService private modelService: IModelService,
@IEditorWorkerService private editorWorkerService: IEditorWorkerService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IWorkspaceContextService private contextService: IWorkspaceContextService
) {
this.decorations = [];
this.diffDelayer = new ThrottledDelayer<void>(200);
this.toDispose = [];
this.triggerDiff();
this.toDispose.push(model.onDidChangeContent(() => this.triggerDiff()));
}
private triggerDiff(): winjs.Promise {
if (!this.diffDelayer) {
return winjs.TPromise.as(null);
}
return this.dirtyDiffService.getDirtyDiffTextDocument(this.uri).then(originalUri => {
return this.diffDelayer.trigger(() => {
if (!this.model || this.model.isDisposed()) {
return winjs.TPromise.as<any>([]); // disposed
}
return this.editorWorkerService.computeDirtyDiff(originalUri, this.model.uri, true);
}).then((diff: common.IChange[]) => {
if (!this.model || this.model.isDisposed()) {
return; // disposed
}
return this.decorations = this.model.deltaDecorations(this.decorations, DirtyDiffModelDecorator.changesToDecorations(diff || []));
});
});
}
private static changesToDecorations(diff: common.IChange[]): common.IModelDeltaDecoration[] {
return diff.map((change) => {
const startLineNumber = change.modifiedStartLineNumber;
const endLineNumber = change.modifiedEndLineNumber || startLineNumber;
// Added
if (change.originalEndLineNumber === 0) {
return {
range: {
startLineNumber: startLineNumber, startColumn: 1,
endLineNumber: endLineNumber, endColumn: 1
},
options: DirtyDiffModelDecorator.ADDED_DECORATION_OPTIONS
};
}
// Removed
if (change.modifiedEndLineNumber === 0) {
return {
range: {
startLineNumber: startLineNumber, startColumn: 1,
endLineNumber: startLineNumber, endColumn: 1
},
options: DirtyDiffModelDecorator.DELETED_DECORATION_OPTIONS
};
}
// Modified
return {
range: {
startLineNumber: startLineNumber, startColumn: 1,
endLineNumber: endLineNumber, endColumn: 1
},
options: DirtyDiffModelDecorator.MODIFIED_DECORATION_OPTIONS
};
});
}
dispose(): void {
this.toDispose = lifecycle.dispose(this.toDispose);
if (this.model && !this.model.isDisposed()) {
this.model.deltaDecorations(this.decorations, []);
}
this.model = null;
this.decorations = null;
if (this.diffDelayer) {
this.diffDelayer.cancel();
this.diffDelayer = null;
}
}
}
export class DirtyDiffDecorator implements ext.IWorkbenchContribution {
private models: common.IModel[] = [];
private decorators: { [modelId: string]: DirtyDiffModelDecorator } = Object.create(null);
private toDispose: lifecycle.IDisposable[] = [];
constructor(
@IMessageService private messageService: IMessageService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@IEventService private eventService: IEventService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IInstantiationService private instantiationService: IInstantiationService
) {
this.toDispose.push(editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
}
getId(): string {
return 'git.DirtyDiffModelDecorator';
}
private onEditorsChanged(): void {
// HACK: This is the best current way of figuring out whether to draw these decorations
// or not. Needs context from the editor, to know whether it is a diff editor, in place editor
// etc.
const models = this.editorService.getVisibleEditors()
// map to the editor controls
.map(e => e.getControl())
// only interested in code editor widgets
.filter(c => c instanceof widget.CodeEditor)
// map to models
.map(e => (<widget.CodeEditor>e).getModel())
// remove nulls and duplicates
.filter((m, i, a) => !!m && !!m.uri && a.indexOf(m, i + 1) === -1)
// get the associated resource
.map(m => ({ model: m, uri: m.uri }));
const newModels = models.filter(p => this.models.every(m => p.model !== m));
const oldModels = this.models.filter(m => models.every(p => p.model !== m));
newModels.forEach(({ model, uri }) => this.onModelVisible(model, uri));
oldModels.forEach(m => this.onModelInvisible(m));
this.models = models.map(p => p.model);
}
private onModelVisible(model: common.IModel, uri: URI): void {
this.decorators[model.id] = this.instantiationService.createInstance(DirtyDiffModelDecorator, model, uri);
}
private onModelInvisible(model: common.IModel): void {
this.decorators[model.id].dispose();
delete this.decorators[model.id];
}
dispose(): void {
this.toDispose = lifecycle.dispose(this.toDispose);
this.models.forEach(m => this.decorators[m.id].dispose());
this.models = null;
this.decorators = null;
}
}
/*---------------------------------------------------------------------------------------------
* 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 { Registry } from 'vs/platform/platform';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { DirtyDiffDecorator } from './dirtydiffDecorator';
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
.registerWorkbenchContribution(DirtyDiffDecorator);
\ 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 { TPromise } from 'vs/base/common/winjs.base';
import URI from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable } from 'vs/base/common/lifecycle';
export interface IDirtyDiffTextDocumentProvider {
getDirtyDiffTextDocument(resource: URI): TPromise<URI>;
}
export const IDirtyDiffService = createDecorator<IDirtyDiffService>('dirtyDiff');
export interface IDirtyDiffService {
_serviceBrand: any;
getDirtyDiffTextDocument(resource: URI): TPromise<URI>;
registerDirtyDiffTextDocumentProvider(provider: IDirtyDiffTextDocumentProvider): IDisposable;
}
\ 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 { TPromise } from 'vs/base/common/winjs.base';
import URI from 'vs/base/common/uri';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IDirtyDiffService, IDirtyDiffTextDocumentProvider } from './dirtydiff';
export class DirtyDiffService implements IDirtyDiffService {
_serviceBrand;
private providers: IDirtyDiffTextDocumentProvider[] = [];
getDirtyDiffTextDocument(resource: URI): TPromise<URI> {
// TODO@Joao: just take the first
const [provider] = this.providers;
if (!provider) {
return TPromise.as(null);
}
return provider.getDirtyDiffTextDocument(resource);
}
registerDirtyDiffTextDocumentProvider(provider: IDirtyDiffTextDocumentProvider): IDisposable {
this.providers = [provider, ...this.providers];
return toDisposable(() => {
const index = this.providers.indexOf(provider);
if (index < 0) {
return;
}
this.providers.splice(index, 1);
});
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册