introduce the concept fo ContentProvider to the ResourceEditorInput

Instead of just letting the API layer know about content provider we must push it lower because only than we can handle uri's opaque.
This commit is contained in:
Johannes Rieken
2016-01-12 17:23:53 +01:00
parent 1b409e306c
commit 443a902d6a
4 changed files with 136 additions and 58 deletions

View File

@@ -24,8 +24,8 @@ import * as vscode from 'vscode';
import {WordHelper} from 'vs/editor/common/model/textModelWithTokensHelpers';
import {IFileService} from 'vs/platform/files/common/files';
import {IUntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
import {ResourceEditorInput} from 'vs/workbench/common/editor/resourceEditorInput';
import {asWinJsPromise} from 'vs/base/common/async';
import {IModeService} from 'vs/editor/common/services/modeService';
import * as weak from 'weak';
export interface IModelAddedData {
@@ -134,6 +134,7 @@ export class ExtHostModelService {
throw new Error(`scheme '${scheme}' already registered`);
}
this._documentContentProviders[scheme] = provider;
this._proxy.$registerTextContentProvider(scheme);
let subscription: IDisposable;
if (typeof provider.onDidChange === 'function') {
@@ -146,6 +147,7 @@ export class ExtHostModelService {
});
}
return new Disposable(() => {
this._proxy.$unregisterTextContentProvider(scheme);
delete this._documentContentProviders[scheme];
if (subscription) {
subscription.dispose();
@@ -166,8 +168,14 @@ export class ExtHostModelService {
});
}
$isDocumentReferenced(uri: URI): TPromise<boolean> {
return TPromise.as(this.getDocumentData(uri).isDocumentReferenced);
$getUnferencedDocuments(): TPromise<URI[]> {
const result: URI[] = [];
for (let key in this._documentData) {
if (!this._documentData[key].isDocumentReferenced) {
result.push(URI.parse(key));
}
}
return TPromise.as(result);
}
public _acceptModelAdd(initData: IModelAddedData): void {
@@ -449,7 +457,6 @@ export class ExtHostDocumentData extends MirrorModel2 {
@Remotable.MainContext('MainThreadDocuments')
export class MainThreadDocuments {
private _modelService: IModelService;
private _modeService: IModeService;
private _textFileService: ITextFileService;
private _editorService: IWorkbenchEditorService;
private _fileService: IFileService;
@@ -458,11 +465,11 @@ export class MainThreadDocuments {
private _modelToDisposeMap: { [modelUrl: string]: IDisposable; };
private _proxy: ExtHostModelService;
private _modelIsSynced: { [modelId: string]: boolean; };
private _resourceContentProvider: { [scheme: string]: IDisposable };
constructor(
@IThreadService threadService: IThreadService,
@IModelService modelService: IModelService,
@IModeService modeService: IModeService,
@IEventService eventService: IEventService,
@ITextFileService textFileService: ITextFileService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
@@ -470,7 +477,6 @@ export class MainThreadDocuments {
@IUntitledEditorService untitledEditorService: IUntitledEditorService
) {
this._modelService = modelService;
this._modeService = modeService;
this._textFileService = textFileService;
this._editorService = editorService;
this._fileService = fileService;
@@ -493,7 +499,11 @@ export class MainThreadDocuments {
this._proxy._acceptModelDirty(e.getAfter().resource);
}));
const handle = setInterval(() => this._runDocumentCleanup(), 30 * 1000);
this._toDispose.push({ dispose() { clearInterval(handle) } });
this._modelToDisposeMap = Object.create(null);
this._resourceContentProvider = Object.create(null);
}
public dispose(): void {
@@ -571,14 +581,12 @@ export class MainThreadDocuments {
let promise: TPromise<boolean>;
switch (uri.scheme) {
case 'file':
promise = this._handleFileScheme(uri);
break;
case 'untitled':
promise = this._handleUnititledScheme(uri);
break;
case 'file':
default:
promise = this._handleAnyScheme(uri);
promise = this._handleAsResourceInput(uri);
break;
}
@@ -591,7 +599,7 @@ export class MainThreadDocuments {
});
}
private _handleFileScheme(uri: URI): TPromise<boolean> {
private _handleAsResourceInput(uri: URI): TPromise<boolean> {
return this._editorService.resolveEditorModel({ resource: uri }).then(model => {
return !!model;
});
@@ -617,46 +625,46 @@ export class MainThreadDocuments {
// --- virtual document logic
private _handleAnyScheme(uri: URI): TPromise<boolean> {
if (this._modelService.getModel(uri)) {
return TPromise.as(true);
}
return this._proxy.$provideTextDocumentContent(uri).then(value => {
// create document from string
const firstLineText = value.substr(0, 1 + value.search(/\r?\n/));
const mode = this._modeService.getOrCreateModeByFilenameOrFirstLine(uri.fsPath, firstLineText);
const model = this._modelService.createModel(value, mode, uri);
// if neither the extension host nor an editor reference this
// document anymore we destroy the model to reclaim memory
const handle = setInterval(() => {
this._editorService.inputToType({ resource: uri }).then(input => {
if (!this._editorService.isVisible(input, true)) {
return this._proxy.$isDocumentReferenced(uri).then(referenced => {
if (!referenced) {
clearInterval(handle);
this._modelService.destroyModel(uri);
}
});
}
}, onUnexpectedError);
}, 30 * 1000);
return model;
}).then(() => {
return true;
$registerTextContentProvider(scheme: string): void {
this._resourceContentProvider[scheme] = ResourceEditorInput.registerResourceContentProvider(scheme, {
provideTextContent: (uri: URI): TPromise<string> => {
return this._proxy.$provideTextDocumentContent(uri);
}
});
}
$onVirtualDocumentChange(uri: URI, value: string): TPromise<any> {
$unregisterTextContentProvider(scheme: string): void {
const registration = this._resourceContentProvider[scheme];
if (registration) {
registration.dispose();
}
}
$onVirtualDocumentChange(uri: URI, value: string): void {
const model = this._modelService.getModel(uri);
if (model) {
model.setValue(value);
return;
}
}
private _runDocumentCleanup(): void {
this._proxy.$getUnferencedDocuments().then(resources => {
const toBeDisposed: URI[] = [];
const promises = resources.map(resource => {
return this._editorService.inputToType({ resource }).then(input => {
if (!this._editorService.isVisible(input, true)) {
toBeDisposed.push(resource);
}
});
});
return TPromise.join(promises).then(() => {
for (let resource of toBeDisposed) {
this._modelService.destroyModel(resource);
}
});
}, onUnexpectedError);
}
}