Merge pull request #30 from stefan-lacatus/extraLibOptimizations

Optimize how external libs are handled and allow for custom languages
This commit is contained in:
Alexandru Dima 2019-03-04 15:14:07 +01:00 committed by GitHub
commit f39458d794
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 25 deletions

View file

@ -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);
} }

View file

@ -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 _onDidChange = new Emitter<void>();
private _extraLibs: { [path: string]: string }; 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; } { get onDidExtraLibsChange(): IEvent<void> {
const result = Object.create(null); return this._onDidExtraLibsChange.event;
for (var key in this._extraLibs) { }
result[key] = this._extraLibs[key];
} getExtraLibs(): IExtraLibs {
return Object.freeze(result); 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]) { if (this._extraLibs[filePath] && this._extraLibs[filePath].content === content) {
throw new Error(`${filePath} already a extra lib`); // no-op, there already exists an extra lib with this content
return {
dispose: () => { }
};
} }
this._extraLibs[filePath] = content; let myVersion = 1;
this._onDidChange.fire(this); if (this._extraLibs[filePath]) {
myVersion = this._extraLibs[filePath].version + 1;
}
this._extraLibs[filePath] = {
content: content,
version: myVersion,
};
this._fireOnDidExtraLibsChangeSoon();
return { return {
dispose: () => { dispose: () => {
if (delete this._extraLibs[filePath]) { let extraLib = this._extraLibs[filePath];
this._onDidChange.fire(this); 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 {

View file

@ -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) { } else if (this.isDefaultLibFileName(fileName)) {
// extra lib and default lib are static // 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 // extra lib
text = this._extraLibs[fileName]; 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 {

View file

@ -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;