use weak references to track TextDocument that were given out to consumers
This commit is contained in:
parent
d7dbecc91d
commit
2e95c110f4
|
@ -29,6 +29,7 @@
|
|||
"vscode-debugprotocol": "^1.2.1",
|
||||
"vscode-textmate": "^1.0.9",
|
||||
"native-keymap": "^0.1.2",
|
||||
"weak": "^1.0.1",
|
||||
"winreg": "0.0.12",
|
||||
"yauzl": "^2.3.1"
|
||||
},
|
||||
|
|
29
src/typings/weak.d.ts
vendored
Normal file
29
src/typings/weak.d.ts
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare namespace weak {
|
||||
interface WeakRef {
|
||||
// tagging
|
||||
}
|
||||
}
|
||||
|
||||
interface WeakFunction {
|
||||
<T>(obj: T, callback?: () => any): T & weak.WeakRef;
|
||||
(obj: any, callback?: () => any): any & weak.WeakRef;
|
||||
|
||||
get(ref: weak.WeakRef): any;
|
||||
get<T>(ref: weak.WeakRef): T;
|
||||
|
||||
isDead(ref: weak.WeakRef): boolean;
|
||||
isNearDeath(ref: weak.WeakRef): boolean;
|
||||
isWeakRef(obj: any): boolean;
|
||||
}
|
||||
|
||||
declare const weak: WeakFunction;
|
||||
|
||||
declare module 'weak' {
|
||||
export = weak;
|
||||
}
|
|
@ -244,16 +244,24 @@ export class ExtHostAPIImplementation {
|
|||
return pluginHostFileSystemEvent.createFileSystemWatcher(pattern, ignoreCreate, ignoreChange, ignoreDelete);
|
||||
},
|
||||
get textDocuments() {
|
||||
return pluginHostDocuments.getDocuments();
|
||||
return pluginHostDocuments.getAllDocumentData().map(data => data.document);
|
||||
},
|
||||
set textDocuments(value) {
|
||||
throw errors.readonly();
|
||||
},
|
||||
// createTextDocument(text: string, fileName?: string, language?: string): Thenable<vscode.TextDocument> {
|
||||
// return pluginHostDocuments.createDocument(text, fileName, language);
|
||||
// },
|
||||
openTextDocument(uriOrFileName:vscode.Uri | string) {
|
||||
return pluginHostDocuments.openDocument(uriOrFileName);
|
||||
openTextDocument(uriOrFileName: vscode.Uri | string) {
|
||||
let uri: URI;
|
||||
if (typeof uriOrFileName === 'string') {
|
||||
uri = URI.file(uriOrFileName);
|
||||
} else if (uriOrFileName instanceof URI) {
|
||||
uri = <URI>uriOrFileName;
|
||||
} else {
|
||||
throw new Error('illegal argument - uriOrFileName');
|
||||
}
|
||||
return pluginHostDocuments.ensureDocumentData(uri).then(() => {
|
||||
const data = pluginHostDocuments.getDocumentData(uri);
|
||||
return data && data.document;
|
||||
});
|
||||
},
|
||||
registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider) {
|
||||
return pluginHostDocuments.registerTextDocumentContentProvider(scheme, provider);
|
||||
|
|
|
@ -30,6 +30,7 @@ import {IEditorInput, IResourceInput} from 'vs/platform/editor/common/editor';
|
|||
import {IMode} from 'vs/editor/common/modes';
|
||||
import {IModeService} from 'vs/editor/common/services/modeService';
|
||||
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
|
||||
import * as weak from 'weak';
|
||||
|
||||
export interface IModelAddedData {
|
||||
url: URI;
|
||||
|
@ -92,38 +93,29 @@ export class ExtHostModelService {
|
|||
this._documentContentProviders = Object.create(null);
|
||||
}
|
||||
|
||||
public getDocuments(): vscode.TextDocument[] {
|
||||
let r: vscode.TextDocument[] = [];
|
||||
public getAllDocumentData(): ExtHostDocumentData[] {
|
||||
const result: ExtHostDocumentData[] = [];
|
||||
for (let key in this._documentData) {
|
||||
r.push(this._documentData[key].document);
|
||||
result.push(this._documentData[key]);
|
||||
}
|
||||
return r;
|
||||
return result;
|
||||
}
|
||||
|
||||
public getDocument(resource: vscode.Uri): vscode.TextDocument {
|
||||
public getDocumentData(resource: vscode.Uri): ExtHostDocumentData {
|
||||
if (!resource) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
const data = this._documentData[resource.toString()];
|
||||
if (data) {
|
||||
return data.document;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
public openDocument(uriOrFileName: vscode.Uri | string): TPromise<vscode.TextDocument> {
|
||||
|
||||
let uri: URI;
|
||||
if (typeof uriOrFileName === 'string') {
|
||||
uri = URI.file(uriOrFileName);
|
||||
} else if (uriOrFileName instanceof URI) {
|
||||
uri = <URI>uriOrFileName;
|
||||
} else {
|
||||
throw new Error('illegal argument - uriOrFileName');
|
||||
}
|
||||
public ensureDocumentData(uri: URI): TPromise<ExtHostDocumentData> {
|
||||
|
||||
let cached = this._documentData[uri.toString()];
|
||||
if (cached) {
|
||||
return TPromise.as(cached.document);
|
||||
return TPromise.as(cached);
|
||||
}
|
||||
|
||||
let promise = this._documentLoader[uri.toString()];
|
||||
|
@ -138,7 +130,7 @@ export class ExtHostModelService {
|
|||
this._documentLoader[uri.toString()] = promise;
|
||||
}
|
||||
|
||||
return promise.then(data => data.document);
|
||||
return promise;
|
||||
}
|
||||
|
||||
public registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider): vscode.Disposable {
|
||||
|
@ -231,7 +223,7 @@ export class ExtHostDocumentData extends MirrorModel2 {
|
|||
private _languageId: string;
|
||||
private _isDirty: boolean;
|
||||
private _textLines: vscode.TextLine[];
|
||||
private _document: vscode.TextDocument;
|
||||
private _documentRef: weak.WeakRef & vscode.TextDocument;
|
||||
|
||||
constructor(proxy: MainThreadDocuments, uri: URI, lines: string[], eol: string,
|
||||
languageId: string, versionId: number, isDirty: boolean) {
|
||||
|
@ -250,27 +242,36 @@ export class ExtHostDocumentData extends MirrorModel2 {
|
|||
}
|
||||
|
||||
get document(): vscode.TextDocument {
|
||||
if (!this._document) {
|
||||
const document = this;
|
||||
this._document = {
|
||||
get uri() { return document._uri },
|
||||
get fileName() { return document._uri.fsPath },
|
||||
get isUntitled() { return document._uri.scheme !== 'file' },
|
||||
get languageId() { return document._languageId },
|
||||
get version() { return document._versionId },
|
||||
get isDirty() { return document._isDirty },
|
||||
save() { return document._proxy._trySaveDocument(document._uri) },
|
||||
getText(range?) { return range ? document._getTextInRange(range) : document.getText() },
|
||||
get lineCount() { return document._lines.length },
|
||||
lineAt(lineOrPos) { return document.lineAt(lineOrPos) },
|
||||
offsetAt(pos) { return document.offsetAt(pos) },
|
||||
positionAt(offset) { return document.positionAt(offset) },
|
||||
validateRange(ran) { return document.validateRange(ran) },
|
||||
validatePosition(pos) { return document.validatePosition(pos) },
|
||||
getWordRangeAtPosition(pos){ return document.getWordRangeAtPosition(pos)}
|
||||
}
|
||||
// dereferences or creates the actual document for this
|
||||
// document data. keeps a weak reference only such that
|
||||
// we later when a document isn't needed anymore
|
||||
|
||||
if (!this.isDocumentReferenced) {
|
||||
const data = this;
|
||||
const doc = {
|
||||
get uri() { return data._uri },
|
||||
get fileName() { return data._uri.fsPath },
|
||||
get isUntitled() { return data._uri.scheme !== 'file' },
|
||||
get languageId() { return data._languageId },
|
||||
get version() { return data._versionId },
|
||||
get isDirty() { return data._isDirty },
|
||||
save() { return data._proxy._trySaveDocument(data._uri) },
|
||||
getText(range?) { return range ? data._getTextInRange(range) : data.getText() },
|
||||
get lineCount() { return data._lines.length },
|
||||
lineAt(lineOrPos) { return data.lineAt(lineOrPos) },
|
||||
offsetAt(pos) { return data.offsetAt(pos) },
|
||||
positionAt(offset) { return data.positionAt(offset) },
|
||||
validateRange(ran) { return data.validateRange(ran) },
|
||||
validatePosition(pos) { return data.validatePosition(pos) },
|
||||
getWordRangeAtPosition(pos) { return data.getWordRangeAtPosition(pos) }
|
||||
};
|
||||
this._documentRef = weak(doc);
|
||||
}
|
||||
return this._document;
|
||||
return weak.get(this._documentRef);
|
||||
}
|
||||
|
||||
get isDocumentReferenced(): boolean {
|
||||
return this._documentRef && !weak.isDead(this._documentRef);
|
||||
}
|
||||
|
||||
_acceptLanguageId(newLanguageId:string): void {
|
||||
|
|
|
@ -9,7 +9,7 @@ import Event, {Emitter} from 'vs/base/common/event';
|
|||
import {IDisposable, disposeAll} from 'vs/base/common/lifecycle';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {Remotable, IThreadService} from 'vs/platform/thread/common/thread';
|
||||
import {ExtHostModelService} from 'vs/workbench/api/common/extHostDocuments';
|
||||
import {ExtHostModelService, ExtHostDocumentData} from 'vs/workbench/api/common/extHostDocuments';
|
||||
import {Selection, Range, Position, EditorOptions} from './extHostTypes';
|
||||
import {ISingleEditOperation, ISelection, IRange, IInternalIndentationOptions, IEditor, EditorType, ICommonCodeEditor, ICommonDiffEditor, IDecorationRenderOptions, IRangeWithMessage} from 'vs/editor/common/editorCommon';
|
||||
import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService';
|
||||
|
@ -94,7 +94,7 @@ export class ExtHostEditors {
|
|||
// --- called from main thread
|
||||
|
||||
_acceptTextEditorAdd(data:ITextEditorAddData): void {
|
||||
let document = this._modelService.getDocument(data.document);
|
||||
let document = this._modelService.getDocumentData(data.document);
|
||||
let newEditor = new ExtHostTextEditor(this._proxy, data.id, document, data.selections.map(TypeConverters.toSelection), data.options);
|
||||
this._editors[data.id] = newEditor;
|
||||
}
|
||||
|
@ -268,20 +268,20 @@ class ExtHostTextEditor implements vscode.TextEditor {
|
|||
private _proxy: MainThreadEditors;
|
||||
private _id: string;
|
||||
|
||||
private _document: vscode.TextDocument;
|
||||
private _documentData: ExtHostDocumentData;
|
||||
private _selections: Selection[];
|
||||
private _options: TextEditorOptions;
|
||||
|
||||
constructor(proxy: MainThreadEditors, id: string, document: TextDocument, selections: Selection[], options: EditorOptions) {
|
||||
constructor(proxy: MainThreadEditors, id: string, document: ExtHostDocumentData, selections: Selection[], options: EditorOptions) {
|
||||
this._proxy = proxy;
|
||||
this._id = id;
|
||||
this._document = document;
|
||||
this._documentData = document;
|
||||
this._selections = selections;
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._document = null;
|
||||
this._documentData = null;
|
||||
}
|
||||
|
||||
@deprecated('TextEditor.show') show(column: vscode.ViewColumn) {
|
||||
|
@ -295,7 +295,7 @@ class ExtHostTextEditor implements vscode.TextEditor {
|
|||
// ---- the document
|
||||
|
||||
get document(): vscode.TextDocument {
|
||||
return this._document;
|
||||
return this._documentData.document;
|
||||
}
|
||||
|
||||
set document(value) {
|
||||
|
@ -379,7 +379,7 @@ class ExtHostTextEditor implements vscode.TextEditor {
|
|||
// ---- editing
|
||||
|
||||
edit(callback:(edit:TextEditorEdit)=>void): Thenable<boolean> {
|
||||
let edit = new TextEditorEdit(this._document);
|
||||
let edit = new TextEditorEdit(this._documentData.document);
|
||||
callback(edit);
|
||||
return this._applyEdit(edit);
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ class OutlineAdapter implements IOutlineSupport {
|
|||
}
|
||||
|
||||
getOutline(resource: URI): TPromise<IOutlineEntry[]> {
|
||||
let doc = this._documents.getDocument(resource);
|
||||
let doc = this._documents.getDocumentData(resource).document;
|
||||
return asWinJsPromise(token => this._provider.provideDocumentSymbols(doc, token)).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(TypeConverters.SymbolInformation.toOutlineEntry);
|
||||
|
@ -76,7 +76,7 @@ class CodeLensAdapter implements modes.ICodeLensSupport {
|
|||
}
|
||||
|
||||
findCodeLensSymbols(resource: URI): TPromise<modes.ICodeLensSymbol[]> {
|
||||
const doc = this._documents.getDocument(resource);
|
||||
const doc = this._documents.getDocumentData(resource).document;
|
||||
const version = doc.version;
|
||||
const key = resource.toString();
|
||||
|
||||
|
@ -180,7 +180,7 @@ class DeclarationAdapter implements modes.IDeclarationSupport {
|
|||
}
|
||||
|
||||
findDeclaration(resource: URI, position: IPosition): TPromise<modes.IReference[]> {
|
||||
let doc = this._documents.getDocument(resource);
|
||||
let doc = this._documents.getDocumentData(resource).document;
|
||||
let pos = TypeConverters.toPosition(position);
|
||||
return asWinJsPromise(token => this._provider.provideDefinition(doc, pos, token)).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
|
@ -214,7 +214,7 @@ class ExtraInfoAdapter implements modes.IExtraInfoSupport {
|
|||
|
||||
computeInfo(resource: URI, position: IPosition): TPromise<modes.IComputeExtraInfoResult> {
|
||||
|
||||
let doc = this._documents.getDocument(resource);
|
||||
let doc = this._documents.getDocumentData(resource).document;
|
||||
let pos = TypeConverters.toPosition(position);
|
||||
|
||||
return asWinJsPromise(token => this._provider.provideHover(doc, pos, token)).then(value => {
|
||||
|
@ -245,7 +245,7 @@ class OccurrencesAdapter implements modes.IOccurrencesSupport {
|
|||
|
||||
findOccurrences(resource: URI, position: IPosition): TPromise<modes.IOccurence[]> {
|
||||
|
||||
let doc = this._documents.getDocument(resource);
|
||||
let doc = this._documents.getDocumentData(resource).document;
|
||||
let pos = TypeConverters.toPosition(position);
|
||||
|
||||
return asWinJsPromise(token => this._provider.provideDocumentHighlights(doc, pos, token)).then(value => {
|
||||
|
@ -278,7 +278,7 @@ class ReferenceAdapter implements modes.IReferenceSupport {
|
|||
}
|
||||
|
||||
findReferences(resource: URI, position: IPosition, includeDeclaration: boolean): TPromise<modes.IReference[]> {
|
||||
let doc = this._documents.getDocument(resource);
|
||||
let doc = this._documents.getDocumentData(resource).document;
|
||||
let pos = TypeConverters.toPosition(position);
|
||||
|
||||
return asWinJsPromise(token => this._provider.provideReferences(doc, pos, { includeDeclaration }, token)).then(value => {
|
||||
|
@ -312,7 +312,7 @@ class QuickFixAdapter implements modes.IQuickFixSupport {
|
|||
|
||||
getQuickFixes(resource: URI, range: IRange, markers?: IMarker[]): TPromise<modes.IQuickFix[]> {
|
||||
|
||||
const doc = this._documents.getDocument(resource);
|
||||
const doc = this._documents.getDocumentData(resource).document;
|
||||
const ran = TypeConverters.toRange(range);
|
||||
const diagnostics = markers.map(marker => {
|
||||
const diag = new Diagnostic(TypeConverters.toRange(marker), marker.message);
|
||||
|
@ -355,7 +355,7 @@ class DocumentFormattingAdapter implements modes.IFormattingSupport {
|
|||
|
||||
formatDocument(resource: URI, options: modes.IFormattingOptions): TPromise<ISingleEditOperation[]> {
|
||||
|
||||
let doc = this._documents.getDocument(resource);
|
||||
let doc = this._documents.getDocumentData(resource).document;
|
||||
|
||||
return asWinJsPromise(token => this._provider.provideDocumentFormattingEdits(doc, <any>options, token)).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
|
@ -377,7 +377,7 @@ class RangeFormattingAdapter implements modes.IFormattingSupport {
|
|||
|
||||
formatRange(resource: URI, range: IRange, options: modes.IFormattingOptions): TPromise<ISingleEditOperation[]> {
|
||||
|
||||
let doc = this._documents.getDocument(resource);
|
||||
let doc = this._documents.getDocumentData(resource).document;
|
||||
let ran = TypeConverters.toRange(range);
|
||||
|
||||
return asWinJsPromise(token => this._provider.provideDocumentRangeFormattingEdits(doc, ran, <any>options, token)).then(value => {
|
||||
|
@ -402,7 +402,7 @@ class OnTypeFormattingAdapter implements modes.IFormattingSupport {
|
|||
|
||||
formatAfterKeystroke(resource: URI, position: IPosition, ch: string, options: modes.IFormattingOptions): TPromise<ISingleEditOperation[]> {
|
||||
|
||||
let doc = this._documents.getDocument(resource);
|
||||
let doc = this._documents.getDocumentData(resource).document;
|
||||
let pos = TypeConverters.toPosition(position);
|
||||
|
||||
return asWinJsPromise(token => this._provider.provideOnTypeFormattingEdits(doc, pos, ch, <any> options, token)).then(value => {
|
||||
|
@ -442,7 +442,7 @@ class RenameAdapter implements modes.IRenameSupport {
|
|||
|
||||
rename(resource: URI, position: IPosition, newName: string): TPromise<modes.IRenameResult> {
|
||||
|
||||
let doc = this._documents.getDocument(resource);
|
||||
let doc = this._documents.getDocumentData(resource).document;
|
||||
let pos = TypeConverters.toPosition(position);
|
||||
|
||||
return asWinJsPromise(token => this._provider.provideRenameEdits(doc, pos, newName, token)).then(value => {
|
||||
|
@ -497,7 +497,7 @@ class SuggestAdapter implements modes.ISuggestSupport {
|
|||
|
||||
suggest(resource: URI, position: IPosition): TPromise<modes.ISuggestResult[]> {
|
||||
|
||||
const doc = this._documents.getDocument(resource);
|
||||
const doc = this._documents.getDocumentData(resource).document;
|
||||
const pos = TypeConverters.toPosition(position);
|
||||
const ran = doc.getWordRangeAtPosition(pos);
|
||||
|
||||
|
@ -596,7 +596,7 @@ class ParameterHintsAdapter implements modes.IParameterHintsSupport {
|
|||
|
||||
getParameterHints(resource: URI, position: IPosition, triggerCharacter?: string): TPromise<modes.IParameterHints> {
|
||||
|
||||
const doc = this._documents.getDocument(resource);
|
||||
const doc = this._documents.getDocumentData(resource).document;
|
||||
const pos = TypeConverters.toPosition(position);
|
||||
|
||||
return asWinJsPromise(token => this._provider.provideSignatureHelp(doc, pos, token)).then(value => {
|
||||
|
|
Loading…
Reference in a new issue