浏览代码

Merge pull request #30 from stefan-lacatus/extraLibOptimizations

Optimize how external libs are handled and allow for custom languages
Alexandru Dima 6 年之前
父节点
当前提交
f39458d794
共有 4 个文件被更改,包括 93 次插入24 次删除
  1. 4 2
      src/languageFeatures.ts
  2. 58 16
      src/monaco.contribution.ts
  3. 13 6
      src/tsWorker.ts
  4. 18 0
      src/workerManager.ts

+ 4 - 2
src/languageFeatures.ts

@@ -135,13 +135,15 @@ export class DiagnostcsAdapter extends Adapter {
 			}
 			}
 		});
 		});
 
 
-		this._disposables.push(this._defaults.onDidChange(() => {
+		const recomputeDiagostics = () => {
 			// redo diagnostics when options change
 			// redo diagnostics when options change
 			for (const model of monaco.editor.getModels()) {
 			for (const model of monaco.editor.getModels()) {
 				onModelRemoved(model);
 				onModelRemoved(model);
 				onModelAdd(model);
 				onModelAdd(model);
 			}
 			}
-		}));
+		};
+		this._disposables.push(this._defaults.onDidChange(recomputeDiagostics));
+		this._disposables.push(this._defaults.onDidExtraLibsChange(recomputeDiagostics));
 
 
 		monaco.editor.getModels().forEach(onModelAdd);
 		monaco.editor.getModels().forEach(onModelAdd);
 	}
 	}

+ 58 - 16
src/monaco.contribution.ts

@@ -12,32 +12,45 @@ import IDisposable = monaco.IDisposable;
 
 
 // --- TypeScript configuration and defaults ---------
 // --- TypeScript configuration and defaults ---------
 
 
+export interface IExtraLib {
+	content: string;
+	version: number;
+}
+
+export interface IExtraLibs {
+	[path: string]: IExtraLib;
+}
+
 export class LanguageServiceDefaultsImpl implements monaco.languages.typescript.LanguageServiceDefaults {
 export class LanguageServiceDefaultsImpl implements monaco.languages.typescript.LanguageServiceDefaults {
 
 
-	private _onDidChange = new Emitter<monaco.languages.typescript.LanguageServiceDefaults>();
-	private _extraLibs: { [path: string]: string };
+	private _onDidChange = new Emitter<void>();
+	private _onDidExtraLibsChange = new Emitter<void>();
+
+	private _extraLibs: IExtraLibs;
 	private _workerMaxIdleTime: number;
 	private _workerMaxIdleTime: number;
 	private _eagerModelSync: boolean;
 	private _eagerModelSync: boolean;
 	private _compilerOptions: monaco.languages.typescript.CompilerOptions;
 	private _compilerOptions: monaco.languages.typescript.CompilerOptions;
 	private _diagnosticsOptions: monaco.languages.typescript.DiagnosticsOptions;
 	private _diagnosticsOptions: monaco.languages.typescript.DiagnosticsOptions;
+	private _onDidExtraLibsChangeTimeout: number;
 
 
 	constructor(compilerOptions: monaco.languages.typescript.CompilerOptions, diagnosticsOptions: monaco.languages.typescript.DiagnosticsOptions) {
 	constructor(compilerOptions: monaco.languages.typescript.CompilerOptions, diagnosticsOptions: monaco.languages.typescript.DiagnosticsOptions) {
 		this._extraLibs = Object.create(null);
 		this._extraLibs = Object.create(null);
 		this._workerMaxIdleTime = 2 * 60 * 1000;
 		this._workerMaxIdleTime = 2 * 60 * 1000;
 		this.setCompilerOptions(compilerOptions);
 		this.setCompilerOptions(compilerOptions);
 		this.setDiagnosticsOptions(diagnosticsOptions);
 		this.setDiagnosticsOptions(diagnosticsOptions);
+		this._onDidExtraLibsChangeTimeout = -1;
 	}
 	}
 
 
-	get onDidChange(): IEvent<monaco.languages.typescript.LanguageServiceDefaults> {
+	get onDidChange(): IEvent<void> {
 		return this._onDidChange.event;
 		return this._onDidChange.event;
 	}
 	}
 
 
-	getExtraLibs(): { [path: string]: string; } {
-		const result = Object.create(null);
-		for (var key in this._extraLibs) {
-			result[key] = this._extraLibs[key];
-		}
-		return Object.freeze(result);
+	get onDidExtraLibsChange(): IEvent<void> {
+		return this._onDidExtraLibsChange.event;
+	}
+
+	getExtraLibs(): IExtraLibs {
+		return this._extraLibs;
 	}
 	}
 
 
 	addExtraLib(content: string, filePath?: string): IDisposable {
 	addExtraLib(content: string, filePath?: string): IDisposable {
@@ -45,29 +58,58 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.typescript.
 			filePath = `ts:extralib-${Math.random().toString(36).substring(2, 15)}`;
 			filePath = `ts:extralib-${Math.random().toString(36).substring(2, 15)}`;
 		}
 		}
 
 
+		if (this._extraLibs[filePath] && this._extraLibs[filePath].content === content) {
+			// no-op, there already exists an extra lib with this content
+			return {
+				dispose: () => { }
+			};
+		}
+
+		let myVersion = 1;
 		if (this._extraLibs[filePath]) {
 		if (this._extraLibs[filePath]) {
-			throw new Error(`${filePath} already a extra lib`);
+			myVersion = this._extraLibs[filePath].version + 1;
 		}
 		}
 
 
-		this._extraLibs[filePath] = content;
-		this._onDidChange.fire(this);
+		this._extraLibs[filePath] = {
+			content: content,
+			version: myVersion,
+		};
+		this._fireOnDidExtraLibsChangeSoon();
 
 
 		return {
 		return {
 			dispose: () => {
 			dispose: () => {
-				if (delete this._extraLibs[filePath]) {
-					this._onDidChange.fire(this);
+				let extraLib = this._extraLibs[filePath];
+				if (!extraLib) {
+					return;
+				}
+				if (extraLib.version !== myVersion) {
+					return;
 				}
 				}
+
+				delete this._extraLibs[filePath];
+				this._fireOnDidExtraLibsChangeSoon();
 			}
 			}
 		};
 		};
 	}
 	}
 
 
+	private _fireOnDidExtraLibsChangeSoon(): void {
+		if (this._onDidExtraLibsChangeTimeout !== -1) {
+			// already scheduled
+			return;
+		}
+		this._onDidExtraLibsChangeTimeout = setTimeout(() => {
+			this._onDidExtraLibsChangeTimeout = -1;
+			this._onDidExtraLibsChange.fire(undefined);
+		}, 0);
+	}
+
 	getCompilerOptions(): monaco.languages.typescript.CompilerOptions {
 	getCompilerOptions(): monaco.languages.typescript.CompilerOptions {
 		return this._compilerOptions;
 		return this._compilerOptions;
 	}
 	}
 
 
 	setCompilerOptions(options: monaco.languages.typescript.CompilerOptions): void {
 	setCompilerOptions(options: monaco.languages.typescript.CompilerOptions): void {
 		this._compilerOptions = options || Object.create(null);
 		this._compilerOptions = options || Object.create(null);
-		this._onDidChange.fire(this);
+		this._onDidChange.fire(undefined);
 	}
 	}
 
 
 	getDiagnosticsOptions(): monaco.languages.typescript.DiagnosticsOptions {
 	getDiagnosticsOptions(): monaco.languages.typescript.DiagnosticsOptions {
@@ -76,7 +118,7 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.typescript.
 
 
 	setDiagnosticsOptions(options: monaco.languages.typescript.DiagnosticsOptions): void {
 	setDiagnosticsOptions(options: monaco.languages.typescript.DiagnosticsOptions): void {
 		this._diagnosticsOptions = options || Object.create(null);
 		this._diagnosticsOptions = options || Object.create(null);
-		this._onDidChange.fire(this);
+		this._onDidChange.fire(undefined);
 	}
 	}
 
 
 	setMaximumWorkerIdleTime(value: number): void {
 	setMaximumWorkerIdleTime(value: number): void {

+ 13 - 6
src/tsWorker.ts

@@ -6,6 +6,7 @@
 
 
 import * as ts from './lib/typescriptServices';
 import * as ts from './lib/typescriptServices';
 import { lib_dts, lib_es6_dts } from './lib/lib';
 import { lib_dts, lib_es6_dts } from './lib/lib';
+import { IExtraLibs } from './monaco.contribution';
 
 
 import IWorkerContext = monaco.worker.IWorkerContext;
 import IWorkerContext = monaco.worker.IWorkerContext;
 
 
@@ -24,7 +25,7 @@ export class TypeScriptWorker implements ts.LanguageServiceHost {
 	// --- model sync -----------------------
 	// --- model sync -----------------------
 
 
 	private _ctx: IWorkerContext;
 	private _ctx: IWorkerContext;
-	private _extraLibs: { [fileName: string]: string } = Object.create(null);
+	private _extraLibs: IExtraLibs = Object.create(null);
 	private _languageService = ts.createLanguageService(this);
 	private _languageService = ts.createLanguageService(this);
 	private _compilerOptions: ts.CompilerOptions;
 	private _compilerOptions: ts.CompilerOptions;
 
 
@@ -59,9 +60,11 @@ export class TypeScriptWorker implements ts.LanguageServiceHost {
 		let model = this._getModel(fileName);
 		let model = this._getModel(fileName);
 		if (model) {
 		if (model) {
 			return model.version.toString();
 			return model.version.toString();
-		} else if (this.isDefaultLibFileName(fileName) || fileName in this._extraLibs) {
-			// extra lib and default lib are static
+		} else if (this.isDefaultLibFileName(fileName)) {
+			// default lib is static
 			return '1';
 			return '1';
+		} else if (fileName in this._extraLibs) {
+			return String(this._extraLibs[fileName].version);
 		}
 		}
 	}
 	}
 
 
@@ -73,8 +76,8 @@ export class TypeScriptWorker implements ts.LanguageServiceHost {
 			text = model.getValue();
 			text = model.getValue();
 
 
 		} else if (fileName in this._extraLibs) {
 		} else if (fileName in this._extraLibs) {
-			// static extra lib
-			text = this._extraLibs[fileName];
+			// extra lib
+			text = this._extraLibs[fileName].content;
 
 
 		} else if (fileName === DEFAULT_LIB.NAME) {
 		} else if (fileName === DEFAULT_LIB.NAME) {
 			text = DEFAULT_LIB.CONTENTS;
 			text = DEFAULT_LIB.CONTENTS;
@@ -196,11 +199,15 @@ export class TypeScriptWorker implements ts.LanguageServiceHost {
 	getEmitOutput(fileName: string): Promise<ts.EmitOutput> {
 	getEmitOutput(fileName: string): Promise<ts.EmitOutput> {
 		return Promise.resolve(this._languageService.getEmitOutput(fileName));
 		return Promise.resolve(this._languageService.getEmitOutput(fileName));
 	}
 	}
+
+	updateExtraLibs(extraLibs: IExtraLibs) {
+		this._extraLibs = extraLibs;
+	}
 }
 }
 
 
 export interface ICreateData {
 export interface ICreateData {
 	compilerOptions: ts.CompilerOptions;
 	compilerOptions: ts.CompilerOptions;
-	extraLibs: { [path: string]: string };
+	extraLibs: IExtraLibs;
 }
 }
 
 
 export function create(ctx: IWorkerContext, createData: ICreateData): TypeScriptWorker {
 export function create(ctx: IWorkerContext, createData: ICreateData): TypeScriptWorker {

+ 18 - 0
src/workerManager.ts

@@ -17,6 +17,8 @@ export class WorkerManager {
 	private _idleCheckInterval: number;
 	private _idleCheckInterval: number;
 	private _lastUsedTime: number;
 	private _lastUsedTime: number;
 	private _configChangeListener: IDisposable;
 	private _configChangeListener: IDisposable;
+	private _updateExtraLibsToken: number;
+	private _extraLibsChangeListener: IDisposable;
 
 
 	private _worker: monaco.editor.MonacoWebWorker<TypeScriptWorker>;
 	private _worker: monaco.editor.MonacoWebWorker<TypeScriptWorker>;
 	private _client: Promise<TypeScriptWorker>;
 	private _client: Promise<TypeScriptWorker>;
@@ -28,6 +30,8 @@ export class WorkerManager {
 		this._idleCheckInterval = setInterval(() => this._checkIfIdle(), 30 * 1000);
 		this._idleCheckInterval = setInterval(() => this._checkIfIdle(), 30 * 1000);
 		this._lastUsedTime = 0;
 		this._lastUsedTime = 0;
 		this._configChangeListener = this._defaults.onDidChange(() => this._stopWorker());
 		this._configChangeListener = this._defaults.onDidChange(() => this._stopWorker());
+		this._updateExtraLibsToken = 0;
+		this._extraLibsChangeListener = this._defaults.onDidExtraLibsChange(() => this._updateExtraLibs());
 	}
 	}
 
 
 	private _stopWorker(): void {
 	private _stopWorker(): void {
@@ -41,9 +45,23 @@ export class WorkerManager {
 	dispose(): void {
 	dispose(): void {
 		clearInterval(this._idleCheckInterval);
 		clearInterval(this._idleCheckInterval);
 		this._configChangeListener.dispose();
 		this._configChangeListener.dispose();
+		this._extraLibsChangeListener.dispose();
 		this._stopWorker();
 		this._stopWorker();
 	}
 	}
 
 
+	private async _updateExtraLibs(): Promise<void> {
+		if (!this._worker) {
+			return;
+		}
+		const myToken = ++this._updateExtraLibsToken;
+		const proxy = await this._worker.getProxy();
+		if (this._updateExtraLibsToken !== myToken) {
+			// avoid multiple calls
+			return;
+		}
+		proxy.updateExtraLibs(this._defaults.getExtraLibs());
+	}
+
 	private _checkIfIdle(): void {
 	private _checkIfIdle(): void {
 		if (!this._worker) {
 		if (!this._worker) {
 			return;
 			return;