提交 dbf3ba74 编写于 作者: B Benjamin Pasero

fixes #6644

上级 8320b75a
......@@ -22,6 +22,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickOpenService';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { ITextModelResolverService, TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
import { IEditorInput, IEditorOptions, IEditorModel, Position, Direction, IEditor, IResourceInput, ITextEditorModel } from 'vs/platform/editor/common/editor';
import { IEventService } from 'vs/platform/event/common/event';
import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
......@@ -179,6 +180,7 @@ export function workbenchInstantiationService(): IInstantiationService {
instantiationService.stub(IMessageService, new TestMessageService());
instantiationService.stub(IUntitledEditorService, instantiationService.createInstance(UntitledEditorService));
instantiationService.stub(ITextFileService, <ITextFileService>instantiationService.createInstance(TestTextFileService));
instantiationService.stub(ITextModelResolverService, <ITextModelResolverService>instantiationService.createInstance(TextModelResolverService));
return instantiationService;
}
......
......@@ -19,12 +19,13 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IFileService } from 'vs/platform/files/common/files';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { ExtHostContext, MainThreadDocumentsShape, ExtHostDocumentsShape } from './extHost.protocol';
import { ITextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
export class MainThreadDocuments extends MainThreadDocumentsShape {
private _modelService: IModelService;
private _modeService: IModeService;
private _textModelResolverService: ITextModelResolverService;
private _textFileService: ITextFileService;
private _editorService: IWorkbenchEditorService;
private _fileService: IFileService;
......@@ -44,11 +45,13 @@ export class MainThreadDocuments extends MainThreadDocumentsShape {
@ITextFileService textFileService: ITextFileService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
@IFileService fileService: IFileService,
@ITextModelResolverService textModelResolverService: ITextModelResolverService,
@IUntitledEditorService untitledEditorService: IUntitledEditorService
) {
super();
this._modelService = modelService;
this._modeService = modeService;
this._textModelResolverService = textModelResolverService;
this._textFileService = textFileService;
this._editorService = editorService;
this._fileService = fileService;
......@@ -213,7 +216,7 @@ export class MainThreadDocuments extends MainThreadDocumentsShape {
// --- virtual document logic
$registerTextContentProvider(handle: number, scheme: string): void {
this._resourceContentProvider[handle] = ResourceEditorInput.registerResourceContentProvider(scheme, {
this._resourceContentProvider[handle] = this._textModelResolverService.registerTextModelContentProvider(scheme, {
provideTextContent: (uri: URI): TPromise<editorCommon.IModel> => {
return this._proxy.$provideTextDocumentContent(handle, uri).then(value => {
if (typeof value === 'string') {
......
......@@ -5,21 +5,10 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { sequence } from 'vs/base/common/async';
import { EditorModel, EditorInput } from 'vs/workbench/common/editor';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
import { IModel } from 'vs/editor/common/editorCommon';
import URI from 'vs/base/common/uri';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IDisposable } from 'vs/base/common/lifecycle';
/**
*
*/
export interface IResourceEditorContentProvider {
provideTextContent(resource: URI): TPromise<IModel>;
}
import { ITextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
/**
* A read-only text editor input whos contents are made of the provided resource that points to an existing
......@@ -27,92 +16,6 @@ export interface IResourceEditorContentProvider {
*/
export class ResourceEditorInput extends EditorInput {
// --- registry logic
// todo@joh,ben this should maybe be a service that is in charge of loading/resolving a uri from a scheme
private static loadingModels: { [uri: string]: TPromise<IModel> } = Object.create(null);
private static registry: { [scheme: string]: IResourceEditorContentProvider[] } = Object.create(null);
public static registerResourceContentProvider(scheme: string, provider: IResourceEditorContentProvider): IDisposable {
let array = ResourceEditorInput.registry[scheme];
if (!array) {
array = [provider];
ResourceEditorInput.registry[scheme] = array;
} else {
array.unshift(provider);
}
return {
dispose() {
let array = ResourceEditorInput.registry[scheme];
let idx = array.indexOf(provider);
if (idx >= 0) {
array.splice(idx, 1);
if (array.length === 0) {
delete ResourceEditorInput.registry[scheme];
}
}
}
};
}
private static getOrCreateModel(modelService: IModelService, resource: URI): TPromise<IModel> {
const model = modelService.getModel(resource);
if (model) {
return TPromise.as(model);
}
let loadingModel = ResourceEditorInput.loadingModels[resource.toString()];
if (!loadingModel) {
// make sure we have a provider this scheme
// the resource uses
const array = ResourceEditorInput.registry[resource.scheme];
if (!array) {
return TPromise.wrapError(`No model with uri '${resource}' nor a resolver for the scheme '${resource.scheme}'.`);
}
// load the model-content from the provider and cache
// the loading such that we don't create the same model
// twice
ResourceEditorInput.loadingModels[resource.toString()] = loadingModel = new TPromise<IModel>((resolve, reject) => {
let result: IModel;
let lastError: any;
sequence(array.map(provider => {
return () => {
if (!result) {
const contentPromise = provider.provideTextContent(resource);
if (!contentPromise) {
return TPromise.wrapError<any>(`No resolver for the scheme '${resource.scheme}' found.`);
}
return contentPromise.then(value => {
result = value;
}, err => {
lastError = err;
});
}
};
})).then(() => {
if (!result && lastError) {
reject(lastError);
} else {
resolve(result);
}
}, reject);
}, function () {
// no cancellation when caching promises
});
// remove the cached promise 'cos the model is now known to the model service (see above)
loadingModel.then(() => delete ResourceEditorInput.loadingModels[resource.toString()], () => delete ResourceEditorInput.loadingModels[resource.toString()]);
}
return loadingModel;
}
public static ID: string = 'workbench.editors.resourceEditorInput';
protected cachedModel: ResourceEditorModel;
......@@ -125,8 +28,7 @@ export class ResourceEditorInput extends EditorInput {
name: string,
description: string,
resource: URI,
@IModelService protected modelService: IModelService,
@IInstantiationService protected instantiationService: IInstantiationService
@ITextModelResolverService private textModelResolverService: ITextModelResolverService
) {
super();
......@@ -169,20 +71,16 @@ export class ResourceEditorInput extends EditorInput {
}
// Otherwise Create Model and handle dispose event
return ResourceEditorInput.getOrCreateModel(this.modelService, this.resource).then(() => {
let model = this.instantiationService.createInstance(ResourceEditorModel, this.resource);
return this.textModelResolverService.resolve(this.resource).then((model: ResourceEditorModel) => {
this.cachedModel = model;
const unbind = model.onDispose(() => {
this.cachedModel = null; // make sure we do not dispose model again
unbind.dispose();
this.dispose();
});
// Load it
return model.load().then((resolvedModel: ResourceEditorModel) => {
this.cachedModel = resolvedModel;
return this.cachedModel;
});
return this.cachedModel;
});
}
......
......@@ -67,6 +67,7 @@ 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 { ITextModelResolverService, TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IMessageService } from 'vs/platform/message/common/message';
......@@ -397,6 +398,9 @@ export class Workbench implements IPartService {
// Text File Service
serviceCollection.set(ITextFileService, this.instantiationService.createInstance(TextFileService));
// Text Model Resolver Service
serviceCollection.set(ITextModelResolverService, this.instantiationService.createInstance(TextModelResolverService));
// Configuration Editing
this.configurationEditingService = this.instantiationService.createInstance(ConfigurationEditingService);
serviceCollection.set(IConfigurationEditingService, this.configurationEditingService);
......
......@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import URI from 'vs/base/common/uri';
......@@ -14,6 +13,7 @@ import JSONContributionRegistry = require('vs/platform/jsonschemas/common/jsonCo
import { Registry } from 'vs/platform/platform';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { ITextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
let schemaRegistry = <JSONContributionRegistry.IJSONContributionRegistry>Registry.as(JSONContributionRegistry.Extensions.JSONContribution);
......@@ -21,13 +21,16 @@ export class WorkbenchContentProvider implements IWorkbenchContribution {
private modelService: IModelService;
private modeService: IModeService;
private textModelResolverService: ITextModelResolverService;
constructor(
@IModelService modelService: IModelService,
@ITextModelResolverService textModelResolverService: ITextModelResolverService,
@IModeService modeService: IModeService
) {
this.modelService = modelService;
this.modeService = modeService;
this.textModelResolverService = textModelResolverService;
this.start();
}
......@@ -37,7 +40,7 @@ export class WorkbenchContentProvider implements IWorkbenchContribution {
}
private start(): void {
ResourceEditorInput.registerResourceContentProvider('vscode', {
this.textModelResolverService.registerTextModelContentProvider('vscode', {
provideTextContent: (uri: URI): TPromise<IModel> => {
if (uri.scheme !== 'vscode') {
return null;
......@@ -57,4 +60,4 @@ export class WorkbenchContentProvider implements IWorkbenchContribution {
}
}
(<IWorkbenchContributionsRegistry>Registry.as(WorkbenchExtensions.Workbench)).registerWorkbenchContribution(WorkbenchContentProvider);
\ No newline at end of file
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(WorkbenchContentProvider);
\ No newline at end of file
......@@ -29,6 +29,7 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
import { ITextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
// A handler for save error happening with conflict resolution actions
export class SaveErrorHandler implements ISaveErrorHandler, IWorkbenchContribution {
......@@ -179,15 +180,16 @@ export class FileOnDiskEditorInput extends ResourceEditorInput {
fileResource: URI,
name: string,
description: string,
@IModelService modelService: IModelService,
@IModelService private modelService: IModelService,
@IModeService private modeService: IModeService,
@ITextModelResolverService textModelResolverService: ITextModelResolverService,
@IInstantiationService instantiationService: IInstantiationService,
@ITextFileService private textFileService: ITextFileService
) {
// We create a new resource URI here that is different from the file resource because we represent the state of
// the file as it is on disk and not as it is (potentially cached) in Code. That allows us to have a different
// model for the left-hand comparision compared to the conflicting one in Code to the right.
super(name, description, URI.from({ scheme: 'disk', path: fileResource.fsPath }), modelService, instantiationService);
super(name, description, URI.from({ scheme: 'disk', path: fileResource.fsPath }), textModelResolverService);
this.fileResource = fileResource;
}
......
/*---------------------------------------------------------------------------------------------
* 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, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IModel } from 'vs/editor/common/editorCommon';
import { ITextEditorModel } from 'vs/platform/editor/common/editor';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IModelService } from 'vs/editor/common/services/modelService';
import { sequence } from 'vs/base/common/async';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
export const ITextModelResolverService = createDecorator<ITextModelResolverService>('textModelResolverService');
export interface ITextModelResolverService {
_serviceBrand: any;
resolve(resource: URI): TPromise<ITextEditorModel>;
registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable;
}
export interface ITextModelContentProvider {
provideTextContent(resource: URI): TPromise<IModel>;
}
export class TextModelResolverService implements ITextModelResolverService {
public _serviceBrand: any;
private loadingTextModels: { [uri: string]: TPromise<IModel> } = Object.create(null);
private contentProviderRegistry: { [scheme: string]: ITextModelContentProvider[] } = Object.create(null);
constructor(
@IInstantiationService private instantiationService: IInstantiationService,
@IModelService private modelService: IModelService
) {
}
public resolve(resource: URI): TPromise<ITextEditorModel> {
return this.resolveTextModelContent(this.modelService, resource).then(() => this.instantiationService.createInstance(ResourceEditorModel, resource));
}
public registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable {
let array = this.contentProviderRegistry[scheme];
if (!array) {
array = [provider];
this.contentProviderRegistry[scheme] = array;
} else {
array.unshift(provider);
}
const registry = this.contentProviderRegistry;
return {
dispose() {
const array = registry[scheme];
const idx = array.indexOf(provider);
if (idx >= 0) {
array.splice(idx, 1);
if (array.length === 0) {
delete registry[scheme];
}
}
}
};
}
private resolveTextModelContent(modelService: IModelService, resource: URI): TPromise<IModel> {
const model = modelService.getModel(resource);
if (model) {
return TPromise.as(model);
}
let loadingTextModel = this.loadingTextModels[resource.toString()];
if (!loadingTextModel) {
// make sure we have a provider this scheme
// the resource uses
const contentProviders = this.contentProviderRegistry[resource.scheme];
if (!contentProviders) {
return TPromise.wrapError(`No model with uri '${resource}' nor a resolver for the scheme '${resource.scheme}'.`);
}
// load the model-content from the provider and cache
// the loading such that we don't create the same model
// twice
this.loadingTextModels[resource.toString()] = loadingTextModel = new TPromise<IModel>((resolve, reject) => {
let result: IModel;
let lastError: any;
sequence(contentProviders.map(provider => {
return () => {
if (!result) {
const contentPromise = provider.provideTextContent(resource);
if (!contentPromise) {
return TPromise.wrapError<any>(`No resolver for the scheme '${resource.scheme}' found.`);
}
return contentPromise.then(value => {
result = value;
}, err => {
lastError = err;
});
}
};
})).then(() => {
if (!result && lastError) {
reject(lastError);
} else {
resolve(result);
}
}, reject);
}, function () {
// no cancellation when caching promises
});
// remove the cached promise 'cos the model is now known to the model service (see above)
loadingTextModel.then(() => delete this.loadingTextModels[resource.toString()], () => delete this.loadingTextModels[resource.toString()]);
}
return loadingTextModel;
}
}
\ 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 * as assert from 'assert';
import { IModel } from 'vs/editor/common/editorCommon';
import { TPromise } from 'vs/base/common/winjs.base';
import URI from 'vs/base/common/uri';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { workbenchInstantiationService } from 'vs/test/utils/servicesTestUtils';
import { ITextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
class ServiceAccessor {
constructor(
@ITextModelResolverService public textModelResolverServie: ITextModelResolverService,
@IModelService public modelService: IModelService,
@IModeService public modeService: IModeService
) {
}
}
suite('Workbench - TextModelResolverService', () => {
let instantiationService: IInstantiationService;
let accessor: ServiceAccessor;
setup(() => {
instantiationService = workbenchInstantiationService();
accessor = instantiationService.createInstance(ServiceAccessor);
});
test('resolve', function (done) {
const dispose = accessor.textModelResolverServie.registerTextModelContentProvider('test', {
provideTextContent: function (resource: URI): TPromise<IModel> {
if (resource.scheme === 'test') {
let modelContent = 'Hello Test';
let mode = accessor.modeService.getOrCreateMode('json');
return TPromise.as(accessor.modelService.createModel(modelContent, mode, resource));
}
return TPromise.as(null);
}
});
let resource = URI.from({ scheme: 'test', authority: null, path: 'thePath' });
let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource);
input.resolve().then((model: ResourceEditorModel) => {
assert.ok(model);
assert.equal(model.getValue(), 'Hello Test');
dispose.dispose();
done();
});
});
});
\ 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 * as assert from 'assert';
import URI from 'vs/base/common/uri';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { workbenchInstantiationService } from 'vs/test/utils/servicesTestUtils';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
class ServiceAccessor {
constructor(
@IModelService public modelService: IModelService,
@IModeService public modeService: IModeService
) {
}
}
suite('Workbench - ResourceEditorInput', () => {
let instantiationService: IInstantiationService;
let accessor: ServiceAccessor;
setup(() => {
instantiationService = workbenchInstantiationService();
accessor = instantiationService.createInstance(ServiceAccessor);
});
test('simple', function (done) {
let resource = URI.from({ scheme: 'inMemory', authority: null, path: 'thePath' });
accessor.modelService.createModel('function test() {}', accessor.modeService.getOrCreateMode('text'), resource);
let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource);
input.resolve().then((model: ResourceEditorModel) => {
assert.ok(model);
assert.equal(model.getValue(), 'function test() {}');
done();
});
});
});
\ No newline at end of file
......@@ -10,7 +10,6 @@ import { TestInstantiationService } from 'vs/test/utils/instantiationTestUtils';
import URI from 'vs/base/common/uri';
import { StringEditorInput } from 'vs/workbench/common/editor/stringEditorInput';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
import { createMockModelService, TestEditorService } from 'vs/test/utils/servicesTestUtils';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
......@@ -109,17 +108,4 @@ suite('Workbench - StringEditorInput', () => {
assert.strictEqual(promiseEditorInput.matches(promiseEditorInput2), true);
assert.strictEqual(stringEditorInput.matches(stringEditorInput2), true);
});
test('ResourceEditorInput', function (done) {
let resource = URI.from({ scheme: 'inMemory', authority: null, path: 'thePath' });
modelService.createModel('function test() {}', modeService.getOrCreateMode('text'), resource);
let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource);
input.resolve().then((model: ResourceEditorModel) => {
assert.ok(model);
assert.equal(model.getValue(), 'function test() {}');
done();
});
});
});
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册