diff --git a/scripts/bundle.js b/scripts/bundle.js index bb9bfe13..e195e33d 100644 --- a/scripts/bundle.js +++ b/scripts/bundle.js @@ -29,8 +29,6 @@ bundleOne('monaco.contribution'); bundleOne('tsMode'); bundleOne('tsWorker'); -updateImports('monaco.contribution'); - function bundleOne(moduleId, exclude) { requirejs.optimize({ baseUrl: 'release/dev/', @@ -38,8 +36,7 @@ function bundleOne(moduleId, exclude) { out: 'release/min/' + moduleId + '.js', exclude: exclude, paths: { - 'vs/language/typescript': REPO_ROOT + '/release/dev', - 'vs/basic-languages': REPO_ROOT + '/node_modules/monaco-languages/release/dev' + 'vs/language/typescript': REPO_ROOT + '/release/dev' }, optimize: 'none' }, function(buildResponse) { @@ -56,10 +53,3 @@ function bundleOne(moduleId, exclude) { fs.writeFileSync(filePath, BUNDLED_FILE_HEADER + result.code); }) } - -function updateImports(moduleId) { - const filePath = path.join(REPO_ROOT, 'release/esm/' + moduleId + '.js'); - let fileContents = fs.readFileSync(filePath).toString(); - fileContents = fileContents.replace(/vs\/basic-languages\//g, "../../basic-languages/"); - fs.writeFileSync(filePath, fileContents); -} \ No newline at end of file diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index fed6a084..ccc50c85 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -134,15 +134,16 @@ export class DiagnostcsAdapter extends Adapter { } } }); - const redoDiagosticsCallback = () => { + + const recomputeDiagostics = () => { // redo diagnostics when options change for (const model of monaco.editor.getModels()) { onModelRemoved(model); onModelAdd(model); } }; - this._disposables.push(this._defaults.onDidChange(redoDiagosticsCallback)); - this._disposables.push(this._defaults.onDidExtraLibsChange(redoDiagosticsCallback)); + this._disposables.push(this._defaults.onDidChange(recomputeDiagostics)); + this._disposables.push(this._defaults.onDidExtraLibsChange(recomputeDiagostics)); monaco.editor.getModels().forEach(onModelAdd); } diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index 336be899..95fe8950 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -5,8 +5,6 @@ 'use strict'; import * as mode from './tsMode'; -import * as tsDefinitions from 'vs/basic-languages/typescript/typescript'; -import * as jsDefinitions from 'vs/basic-languages/javascript/javascript'; import Emitter = monaco.Emitter; import IEvent = monaco.IEvent; @@ -14,41 +12,45 @@ import IDisposable = monaco.IDisposable; // --- 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 { - private _onDidChange = new Emitter(); - private _onDidExtraLibsChange = new Emitter(); + private _onDidChange = new Emitter(); + private _onDidExtraLibsChange = new Emitter(); - private _extraLibs: { [path: string]: { content: string, version: number } }; + private _extraLibs: IExtraLibs; private _workerMaxIdleTime: number; private _eagerModelSync: boolean; private _compilerOptions: monaco.languages.typescript.CompilerOptions; private _diagnosticsOptions: monaco.languages.typescript.DiagnosticsOptions; - private _languageId: string; - private _eagerExtraLibSync: boolean = true; + private _onDidExtraLibsChangeTimeout: number; - constructor(languageId: string, 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._workerMaxIdleTime = 2 * 60 * 1000; this.setCompilerOptions(compilerOptions); this.setDiagnosticsOptions(diagnosticsOptions); - this._languageId = languageId; + this._onDidExtraLibsChangeTimeout = -1; } - get onDidChange(): IEvent { + get onDidChange(): IEvent { return this._onDidChange.event; } - get onDidExtraLibsChange(): IEvent { + get onDidExtraLibsChange(): IEvent { return this._onDidExtraLibsChange.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); + getExtraLibs(): IExtraLibs { + return this._extraLibs; } addExtraLib(content: string, filePath?: string): IDisposable { @@ -56,49 +58,49 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.typescript. filePath = `ts:extralib-${Date.now()}`; } - if (this._extraLibs[filePath]) { - if(this._extraLibs[filePath].content !== content) { - this._extraLibs[filePath].version++; - this._extraLibs[filePath].content = content; - } - } else { - this._extraLibs[filePath] = { - content: content, - version: 1, + if (this._extraLibs[filePath] && this._extraLibs[filePath].content === content) { + // no-op, there already exists an extra lib with this content + return { + dispose: () => { } }; } - if (this._eagerExtraLibSync) { - this.syncExtraLibs(); + + let myVersion = 1; + if (this._extraLibs[filePath]) { + myVersion = this._extraLibs[filePath].version + 1; } + this._extraLibs[filePath] = { + content: content, + version: myVersion, + }; + this._fireOnDidExtraLibsChangeSoon(); + return { dispose: () => { - if (delete this._extraLibs[filePath] && this._eagerExtraLibSync) { - this.syncExtraLibs(); + let extraLib = this._extraLibs[filePath]; + if (!extraLib) { + return; } + if (extraLib.version !== myVersion) { + return; + } + + delete this._extraLibs[filePath]; + this._fireOnDidExtraLibsChangeSoon(); } }; } - async syncExtraLibs(): Promise { - try { - let worker; - try { - // we don't care if the get language worker fails. - // This happens if addExtraLib is called before the worker has initialized. - // however, when the worker has finished downloading and initializes, - // it does so with the latest extraLibs so no sync issue can appear - worker = await getLanguageWorker(this._languageId); - } catch (ignored) { - return; - } - const client = await worker(""); - client.syncExtraLibs(this._extraLibs); - // let all listeners know that the extra libs have changed - this._onDidExtraLibsChange.fire(this); - } catch (error) { - console.error(error); + 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 { @@ -107,7 +109,7 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.typescript. setCompilerOptions(options: monaco.languages.typescript.CompilerOptions): void { this._compilerOptions = options || Object.create(null); - this._onDidChange.fire(this); + this._onDidChange.fire(undefined); } getDiagnosticsOptions(): monaco.languages.typescript.DiagnosticsOptions { @@ -116,7 +118,7 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.typescript. setDiagnosticsOptions(options: monaco.languages.typescript.DiagnosticsOptions): void { this._diagnosticsOptions = options || Object.create(null); - this._onDidChange.fire(this); + this._onDidChange.fire(undefined); } setMaximumWorkerIdleTime(value: number): void { @@ -138,10 +140,6 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.typescript. getEagerModelSync() { return this._eagerModelSync; } - - setEagerExtraLibSync(value: boolean) { - this._eagerExtraLibSync = value; - } } //#region enums copied from typescript to prevent loading the entire typescriptServices --- @@ -182,75 +180,20 @@ enum ModuleResolutionKind { } //#endregion -const languageDefaultOptions = { - javascript: { - compilerOptions: { allowNonTsExtensions: true, allowJs: true, target: ScriptTarget.Latest }, - diagnosticsOptions: { noSemanticValidation: true, noSyntaxValidation: false }, - }, - typescript: { - compilerOptions: { allowNonTsExtensions: true, target: ScriptTarget.Latest }, - diagnosticsOptions: { noSemanticValidation: false, noSyntaxValidation: false } - } -} +const typescriptDefaults = new LanguageServiceDefaultsImpl( + { allowNonTsExtensions: true, target: ScriptTarget.Latest }, + { noSemanticValidation: false, noSyntaxValidation: false }); -const languageDefaults: { [name: string]: LanguageServiceDefaultsImpl } = {}; - -/** - * Generate the LanguageServiceDefaults for a new langauage with the given name - * @param languageId Name of the language - * @param isTypescriptBased Whether the language inherits from a typescript base or a javascript one - */ -function setupLanguageServiceDefaults(languageId: string, isTypescriptBased: boolean) { - const languageOptions = isTypescriptBased ? languageDefaultOptions.typescript : languageDefaultOptions.javascript; - languageDefaults[languageId] = new LanguageServiceDefaultsImpl(languageId, languageOptions.compilerOptions, languageOptions.diagnosticsOptions); -} - -setupNamedLanguage({ - id: 'typescript', - extensions: ['.ts', '.tsx'], - aliases: ['TypeScript', 'ts', 'typescript'], - mimetypes: ['text/typescript'] -}, true, true); - -setupNamedLanguage({ - id: 'javascript', - extensions: ['.js', '.es6', '.jsx'], - firstLine: '^#!.*\\bnode', - filenames: ['jakefile'], - aliases: ['JavaScript', 'javascript', 'js'], - mimetypes: ['text/javascript'], -}, false, true); +const javascriptDefaults = new LanguageServiceDefaultsImpl( + { allowNonTsExtensions: true, allowJs: true, target: ScriptTarget.Latest }, + { noSemanticValidation: true, noSyntaxValidation: false }); function getTypeScriptWorker(): Promise { - return getLanguageWorker("typescript"); + return getMode().then(mode => mode.getTypeScriptWorker()); } function getJavaScriptWorker(): Promise { - return getLanguageWorker("javascript"); -} - -function getLanguageWorker(languageName: string): Promise { - return getMode().then(mode => mode.getNamedLanguageWorker(languageName)); -} - -function getLanguageDefaults(languageName: string): LanguageServiceDefaultsImpl { - return languageDefaults[languageName]; -} - -function setupNamedLanguage(languageDefinition: monaco.languages.ILanguageExtensionPoint, isTypescript: boolean, registerLanguage?: boolean): void { - if (registerLanguage) { - monaco.languages.register(languageDefinition); - - const langageConfig = isTypescript ? tsDefinitions : jsDefinitions; - monaco.languages.setMonarchTokensProvider(languageDefinition.id, langageConfig.language); - monaco.languages.setLanguageConfiguration(languageDefinition.id, langageConfig.conf); - } - - setupLanguageServiceDefaults(languageDefinition.id, isTypescript); - - monaco.languages.onLanguage(languageDefinition.id, () => { - return getMode().then(mode => mode.setupNamedLanguage(languageDefinition.id, isTypescript, languageDefaults[languageDefinition.id])); - }); + return getMode().then(mode => mode.getJavaScriptWorker()); } // Export API @@ -261,13 +204,10 @@ function createAPI(): typeof monaco.languages.typescript { NewLineKind: NewLineKind, ScriptTarget: ScriptTarget, ModuleResolutionKind: ModuleResolutionKind, - typescriptDefaults: getLanguageDefaults("typescript"), - javascriptDefaults: getLanguageDefaults("javascript"), - getLanguageDefaults: getLanguageDefaults, + typescriptDefaults: typescriptDefaults, + javascriptDefaults: javascriptDefaults, getTypeScriptWorker: getTypeScriptWorker, - getJavaScriptWorker: getJavaScriptWorker, - getLanguageWorker: getLanguageWorker, - setupNamedLanguage: setupNamedLanguage + getJavaScriptWorker: getJavaScriptWorker } } monaco.languages.typescript = createAPI(); @@ -277,3 +217,10 @@ monaco.languages.typescript = createAPI(); function getMode(): Promise { return import('./tsMode'); } + +monaco.languages.onLanguage('typescript', () => { + return getMode().then(mode => mode.setupTypeScript(typescriptDefaults)); +}); +monaco.languages.onLanguage('javascript', () => { + return getMode().then(mode => mode.setupJavaScript(javascriptDefaults)); +}); diff --git a/src/monaco.d.ts b/src/monaco.d.ts index cc696a82..2a3bab0c 100644 --- a/src/monaco.d.ts +++ b/src/monaco.d.ts @@ -164,17 +164,6 @@ declare module monaco.languages.typescript { * to the worker on start or restart. */ setEagerModelSync(value: boolean): void; - - /** - * Configure if the extra libs should be eagerly synced after each addExtraLibCall. - * This is true by default - */ - setEagerExtraLibSync(value: boolean): void; - - /** - * If EagerExtraLibSync is disabled, call this to trigger the changes. - */ - syncExtraLibs(): Promise; } export var typescriptDefaults: LanguageServiceDefaults; @@ -182,8 +171,4 @@ declare module monaco.languages.typescript { export var getTypeScriptWorker: () => Promise; export var getJavaScriptWorker: () => Promise; - - export var getLanguageWorker: (langaugeName: string) => Promise; - export var setupNamedLanguage: (languageDefinition: languages.ILanguageExtensionPoint, isTypescript: boolean, registerLanguage?: boolean) => void; - export var getLanguageDefaults: (languageName: string) => LanguageServiceDefaults; } diff --git a/src/tsMode.ts b/src/tsMode.ts index e2fdf643..ce9caeca 100644 --- a/src/tsMode.ts +++ b/src/tsMode.ts @@ -11,23 +11,40 @@ import * as languageFeatures from './languageFeatures'; import Uri = monaco.Uri; -let scriptWorkerMap: { [name: string]: (first: Uri, ...more: Uri[]) => Promise } = {}; +let javaScriptWorker: (first: Uri, ...more: Uri[]) => Promise; +let typeScriptWorker: (first: Uri, ...more: Uri[]) => Promise; -export function setupNamedLanguage(languageName: string, isTypescript: boolean, defaults: LanguageServiceDefaultsImpl): void { - scriptWorkerMap[languageName + "Worker"] = setupMode( +export function setupTypeScript(defaults: LanguageServiceDefaultsImpl): void { + typeScriptWorker = setupMode( defaults, - languageName + 'typescript' ); } -export function getNamedLanguageWorker(languageName: string): Promise<(first: Uri, ...more: Uri[]) => Promise> { - let workerName = languageName + "Worker"; +export function setupJavaScript(defaults: LanguageServiceDefaultsImpl): void { + javaScriptWorker = setupMode( + defaults, + 'javascript' + ); +} + +export function getJavaScriptWorker(): Promise<(first: Uri, ...more: Uri[]) => Promise> { return new Promise((resolve, reject) => { - if (!scriptWorkerMap[workerName]) { - return reject(languageName + " not registered!"); + if (!javaScriptWorker) { + return reject("JavaScript not registered!"); } - resolve(scriptWorkerMap[workerName]); + resolve(javaScriptWorker); + }); +} + +export function getTypeScriptWorker(): Promise<(first: Uri, ...more: Uri[]) => Promise> { + return new Promise((resolve, reject) => { + if (!typeScriptWorker) { + return reject("TypeScript not registered!"); + } + + resolve(typeScriptWorker); }); } diff --git a/src/tsWorker.ts b/src/tsWorker.ts index f5668fa1..fd2b280a 100644 --- a/src/tsWorker.ts +++ b/src/tsWorker.ts @@ -6,6 +6,7 @@ import * as ts from './lib/typescriptServices'; import { lib_dts, lib_es6_dts } from './lib/lib'; +import { IExtraLibs } from './monaco.contribution'; import IWorkerContext = monaco.worker.IWorkerContext; @@ -24,7 +25,7 @@ export class TypeScriptWorker implements ts.LanguageServiceHost { // --- model sync ----------------------- private _ctx: IWorkerContext; - private _extraLibs: { [path: string]: { content: string, version: number } } = Object.create(null); + private _extraLibs: IExtraLibs = Object.create(null); private _languageService = ts.createLanguageService(this); private _compilerOptions: ts.CompilerOptions; @@ -62,8 +63,8 @@ export class TypeScriptWorker implements ts.LanguageServiceHost { } else if (this.isDefaultLibFileName(fileName)) { // default lib is static return '1'; - } else if(fileName in this._extraLibs) { - return this._extraLibs[fileName].version.toString(); + } else if (fileName in this._extraLibs) { + return String(this._extraLibs[fileName].version); } } @@ -75,7 +76,7 @@ export class TypeScriptWorker implements ts.LanguageServiceHost { text = model.getValue(); } else if (fileName in this._extraLibs) { - // static extra lib + // extra lib text = this._extraLibs[fileName].content; } else if (fileName === DEFAULT_LIB.NAME) { @@ -199,14 +200,14 @@ export class TypeScriptWorker implements ts.LanguageServiceHost { return Promise.resolve(this._languageService.getEmitOutput(fileName)); } - syncExtraLibs(extraLibs: { [path: string]: { content: string, version: number } }) { + updateExtraLibs(extraLibs: IExtraLibs) { this._extraLibs = extraLibs; } } export interface ICreateData { compilerOptions: ts.CompilerOptions; - extraLibs: { [path: string]: { content: string, version: number } }; + extraLibs: IExtraLibs; } export function create(ctx: IWorkerContext, createData: ICreateData): TypeScriptWorker { diff --git a/src/tsconfig.esm.json b/src/tsconfig.esm.json index 546491e4..b95798a9 100644 --- a/src/tsconfig.esm.json +++ b/src/tsconfig.esm.json @@ -9,13 +9,7 @@ "es5", "es2015.collection", "es2015.promise" - ], - "baseUrl": "", - "paths": { - "vs/basic-languages/*": [ - "../node_modules/monaco-languages/release/esm/*" - ] - }, + ] }, "include": [ "**/*.ts" diff --git a/src/tsconfig.json b/src/tsconfig.json index 418cf371..f4ff52fe 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -9,13 +9,7 @@ "es5", "es2015.collection", "es2015.promise" - ], - "baseUrl": "", - "paths": { - "vs/basic-languages/*": [ - "../node_modules/monaco-languages/release/dev/*" - ] - }, + ] }, "include": [ "**/*.ts" diff --git a/src/workerManager.ts b/src/workerManager.ts index a99bc28a..666c9fc9 100644 --- a/src/workerManager.ts +++ b/src/workerManager.ts @@ -17,6 +17,8 @@ export class WorkerManager { private _idleCheckInterval: number; private _lastUsedTime: number; private _configChangeListener: IDisposable; + private _updateExtraLibsToken: number; + private _extraLibsChangeListener: IDisposable; private _worker: monaco.editor.MonacoWebWorker; private _client: Promise; @@ -28,6 +30,8 @@ export class WorkerManager { this._idleCheckInterval = setInterval(() => this._checkIfIdle(), 30 * 1000); this._lastUsedTime = 0; this._configChangeListener = this._defaults.onDidChange(() => this._stopWorker()); + this._updateExtraLibsToken = 0; + this._extraLibsChangeListener = this._defaults.onDidExtraLibsChange(() => this._updateExtraLibs()); } private _stopWorker(): void { @@ -41,9 +45,23 @@ export class WorkerManager { dispose(): void { clearInterval(this._idleCheckInterval); this._configChangeListener.dispose(); + this._extraLibsChangeListener.dispose(); this._stopWorker(); } + private async _updateExtraLibs(): Promise { + 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 { if (!this._worker) { return;