From d3e29a25c906036c141ed016455e60ea96fe81f3 Mon Sep 17 00:00:00 2001 From: Sebastian Pahnke Date: Wed, 2 Sep 2020 13:21:52 +0200 Subject: [PATCH 01/37] Format signature and parameter documentation as Markdown --- src/languageFeatures.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index 7ebed60..b3fe20c 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -478,13 +478,17 @@ export class SignatureHelpAdapter extends Adapter implements monaco.languages.Si parameters: [] }; - signature.documentation = displayPartsToString(item.documentation); + signature.documentation = { + value: displayPartsToString(item.documentation) + }; signature.label += displayPartsToString(item.prefixDisplayParts); item.parameters.forEach((p, i, a) => { const label = displayPartsToString(p.displayParts); const parameter: monaco.languages.ParameterInformation = { label: label, - documentation: displayPartsToString(p.documentation) + documentation: { + value: displayPartsToString(p.documentation) + } }; signature.label += label; signature.parameters.push(parameter); From f7c5803a40f7181948762587d2a353291b2ccb8f Mon Sep 17 00:00:00 2001 From: Orta Date: Fri, 21 Aug 2020 07:43:59 -0400 Subject: [PATCH 02/37] Add support for creating a custom webworker subclass --- src/monaco.contribution.ts | 19 +++- src/monaco.d.ts | 4 + src/tsWorker.ts | 21 +++- src/workerManager.ts | 3 +- test/custom-worker.html | 211 +++++++++++++++++++++++++++++++++++++ test/custom-worker.js | 9 ++ 6 files changed, 262 insertions(+), 5 deletions(-) create mode 100644 test/custom-worker.html create mode 100644 test/custom-worker.js diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index c038d4b..a26b7e2 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -31,13 +31,15 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.typescript. private _eagerModelSync: boolean; private _compilerOptions!: monaco.languages.typescript.CompilerOptions; private _diagnosticsOptions!: monaco.languages.typescript.DiagnosticsOptions; + private _workerOptions!: monaco.languages.typescript.WorkerOptions; 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, workerOptions: monaco.languages.typescript.WorkerOptions) { this._extraLibs = Object.create(null); this._eagerModelSync = false; this.setCompilerOptions(compilerOptions); this.setDiagnosticsOptions(diagnosticsOptions); + this.setWorkerOptions(workerOptions) this._onDidExtraLibsChangeTimeout = -1; } @@ -49,6 +51,10 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.typescript. return this._onDidExtraLibsChange.event; } + get workerOptions(): monaco.languages.typescript.WorkerOptions { + return this._workerOptions + } + getExtraLibs(): IExtraLibs { return this._extraLibs; } @@ -142,6 +148,11 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.typescript. this._onDidChange.fire(undefined); } + setWorkerOptions(options: monaco.languages.typescript.WorkerOptions): void { + this._workerOptions = options || Object.create(null); + this._onDidChange.fire(undefined); + } + setMaximumWorkerIdleTime(value: number): void { } @@ -202,11 +213,13 @@ enum ModuleResolutionKind { const typescriptDefaults = new LanguageServiceDefaultsImpl( { allowNonTsExtensions: true, target: ScriptTarget.Latest }, - { noSemanticValidation: false, noSyntaxValidation: false }); + { noSemanticValidation: false, noSyntaxValidation: false }, + {}); const javascriptDefaults = new LanguageServiceDefaultsImpl( { allowNonTsExtensions: true, allowJs: true, target: ScriptTarget.Latest }, - { noSemanticValidation: true, noSyntaxValidation: false }); + { noSemanticValidation: true, noSyntaxValidation: false }, + {}); function getTypeScriptWorker(): Promise<(...uris: monaco.Uri[]) => Promise> { return getMode().then(mode => mode.getTypeScriptWorker()); diff --git a/src/monaco.d.ts b/src/monaco.d.ts index 1e99b41..698ce73 100644 --- a/src/monaco.d.ts +++ b/src/monaco.d.ts @@ -137,6 +137,10 @@ declare module monaco.languages.typescript { diagnosticCodesToIgnore?: number[]; } + export interface WorkerOptions { + customWorkerPath?: string; + } + interface IExtraLib { content: string; version: number; diff --git a/src/tsWorker.ts b/src/tsWorker.ts index b47227f..8aea097 100644 --- a/src/tsWorker.ts +++ b/src/tsWorker.ts @@ -275,8 +275,27 @@ export class TypeScriptWorker implements ts.LanguageServiceHost, monaco.language export interface ICreateData { compilerOptions: ts.CompilerOptions; extraLibs: IExtraLibs; + customWorkerPath?: string } export function create(ctx: IWorkerContext, createData: ICreateData): TypeScriptWorker { - return new TypeScriptWorker(ctx, createData); + let TSWorkerClass = TypeScriptWorker + if (createData.customWorkerPath) { + + // @ts-ignore - This is available in a webworker + if (typeof importScripts === "undefined") { + console.warn("Monaco is not using webworker workers, and that is needed to support the customWorkerPath flag") + } else { + // @ts-ignore - This is available in a webworker + importScripts(createData.customWorkerPath) + // @ts-ignore - This should come from + if(!self.extendTSWorkerFactory) { + throw new Error(`The script at ${createData.customWorkerPath} does not add extendTSWorkerFactory to self`) + } + // @ts-ignore - The throw validates this + TSWorkerClass = self.extendTSWorkerFactory(TypeScriptWorker) + } + } + + return new TSWorkerClass(ctx, createData); } diff --git a/src/workerManager.ts b/src/workerManager.ts index ff0b560..fe6e245 100644 --- a/src/workerManager.ts +++ b/src/workerManager.ts @@ -72,7 +72,8 @@ export class WorkerManager { // passed in to the create() method createData: { compilerOptions: this._defaults.getCompilerOptions(), - extraLibs: this._defaults.getExtraLibs() + extraLibs: this._defaults.getExtraLibs(), + customWorkerPath: this._defaults.workerOptions.customWorkerPath } }); diff --git a/test/custom-worker.html b/test/custom-worker.html new file mode 100644 index 0000000..fcaceaf --- /dev/null +++ b/test/custom-worker.html @@ -0,0 +1,211 @@ + + + + + + + + + +

Monaco Editor TypeScript test page

+ +
+

Compiler settings

+
+ + + + + + + + + + diff --git a/test/custom-worker.js b/test/custom-worker.js new file mode 100644 index 0000000..ad3c8df --- /dev/null +++ b/test/custom-worker.js @@ -0,0 +1,9 @@ +/// + +console.log("worker") + +self.extendTSWorkerFactory = (TypeScriptWorker) => { + return class MonacoTSWorker extends TypeScriptWorker { + + } +} From 8a795381d8da4c15dbaa52329e2472f702ca4d80 Mon Sep 17 00:00:00 2001 From: Orta Date: Fri, 21 Aug 2020 08:04:24 -0400 Subject: [PATCH 03/37] Make a useful sample --- test/custom-worker.html | 35 ++++++++++++++++++++--------------- test/custom-worker.js | 9 +++++++-- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/test/custom-worker.html b/test/custom-worker.html index fcaceaf..259a890 100644 --- a/test/custom-worker.html +++ b/test/custom-worker.html @@ -1,3 +1,11 @@ + + @@ -10,9 +18,8 @@

Monaco Editor TypeScript test page

-

Compiler settings

-
- +

Custom webworker

+ diff --git a/test/custom-worker.js b/test/custom-worker.js index 498b567..ccc7e0f 100644 --- a/test/custom-worker.js +++ b/test/custom-worker.js @@ -1,4 +1,19 @@ -self.customTSWorkerFactory = (TypeScriptWorker) => { +// This example uses @typescript/vfs to create a virtual TS program +// which can do work on a bg thread. + +importScripts("https://unpkg.com/@typescript/vfs@1.3.0/dist/vfs.globals.js") + +/** + * + * @param {import("../src/tsWorker").TypeScriptWorker} TypeScriptWorker + * @param {import("typescript")} ts + * @param {Record} libFileMap + * + */ +const worker = (TypeScriptWorker, ts, libFileMap) => { + /** @type { import("@typescript/vfs") } */ + const tsvfs = globalThis.tsvfs + return class MonacoTSWorker extends TypeScriptWorker { // Adds a custom function to the webworker @@ -8,5 +23,44 @@ self.customTSWorkerFactory = (TypeScriptWorker) => { return (firstDTS && firstDTS.text) || "" } + async printAST(fileName) { + console.log("Creating virtual TS project") + const compilerOptions = this.getCompilationSettings() + const fsMap = new Map() + for (const key of Object.keys(libFileMap)) { + fsMap.set(key, "/" + libFileMap[key]) + } + + const thisCode = await this.getScriptText(fileName) + fsMap.set("index.ts", thisCode) + + console.log("Starting up TS program") + const system = tsvfs.createSystem(fsMap) + const host = tsvfs.createVirtualCompilerHost(system, compilerOptions, ts) + + const program = ts.createProgram({ + rootNames: [...fsMap.keys()], + options: compilerOptions, + host: host.compilerHost, + }) + + // Now I can look at the AST for the .ts file too + const mainSrcFile = program.getSourceFile("index.ts") + let miniAST = "SourceFile" + + const recurse = (parent, depth) => { + if (depth > 5) return + ts.forEachChild(parent, node => { + const spaces = " ".repeat(depth + 1) + miniAST += `\n${spaces}${ts.SyntaxKind[node.kind]}` + recurse(node, depth + 1) + }) + } + recurse(mainSrcFile, 0) + return miniAST + } + } } + +self.customTSWorkerFactory = worker From 8bbaa32e7a7e3a93851296de909b73f026086866 Mon Sep 17 00:00:00 2001 From: Orta Date: Mon, 31 Aug 2020 13:38:06 -0400 Subject: [PATCH 07/37] Tightens the public API --- src/monaco.d.ts | 1 + src/tsWorker.ts | 16 +++++++++++----- test/custom-worker.js | 16 +++++----------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/monaco.d.ts b/src/monaco.d.ts index 698ce73..d63c60d 100644 --- a/src/monaco.d.ts +++ b/src/monaco.d.ts @@ -138,6 +138,7 @@ declare module monaco.languages.typescript { } export interface WorkerOptions { + /** A full HTTP path to a JavaScript file which adds a function `customTSWorkerFactory` to the self inside a web-worker */ customWorkerPath?: string; } diff --git a/src/tsWorker.ts b/src/tsWorker.ts index d5837ca..92feae6 100644 --- a/src/tsWorker.ts +++ b/src/tsWorker.ts @@ -37,7 +37,7 @@ export class TypeScriptWorker implements ts.LanguageServiceHost, monaco.language return models.concat(Object.keys(this._extraLibs)); } - _getModel(fileName: string): monaco.worker.IMirrorModel | null { + private _getModel(fileName: string): monaco.worker.IMirrorModel | null { let models = this._ctx.getMirrorModels(); for (let i = 0; i < models.length; i++) { if (models[i].uri.toString() === fileName) { @@ -279,22 +279,28 @@ export interface ICreateData { customWorkerPath?: string } +/** The shape of the factory */ +export interface CustomTSWebWorkerFactory { + (TSWorkerClass: typeof TypeScriptWorker, ts: typeof import("typescript"), libs: Record): typeof TypeScriptWorker +} + export function create(ctx: IWorkerContext, createData: ICreateData): TypeScriptWorker { let TSWorkerClass = TypeScriptWorker if (createData.customWorkerPath) { - // @ts-ignore - This is available in a webworker if (typeof importScripts === "undefined") { console.warn("Monaco is not using webworkers for background tasks, and that is needed to support the customWorkerPath flag") } else { // @ts-ignore - This is available in a webworker importScripts(createData.customWorkerPath) + // @ts-ignore - This should come from the above eval - if (!self.customTSWorkerFactory) { + const workerFactoryFunc: CustomTSWebWorkerFactory | undefined = self.customTSWorkerFactory + if (!workerFactoryFunc) { throw new Error(`The script at ${createData.customWorkerPath} does not add customTSWorkerFactory to self`) } - // @ts-ignore - The throw validates this - TSWorkerClass = self.customTSWorkerFactory(TypeScriptWorker, ts, libFileMap) + + TSWorkerClass = workerFactoryFunc(TypeScriptWorker, ts, libFileMap) } } diff --git a/test/custom-worker.js b/test/custom-worker.js index ccc7e0f..3eeacd1 100644 --- a/test/custom-worker.js +++ b/test/custom-worker.js @@ -1,19 +1,14 @@ // This example uses @typescript/vfs to create a virtual TS program // which can do work on a bg thread. +// This version of the vfs edits the global scope (in the case of a webworker, this is 'self') importScripts("https://unpkg.com/@typescript/vfs@1.3.0/dist/vfs.globals.js") -/** - * - * @param {import("../src/tsWorker").TypeScriptWorker} TypeScriptWorker - * @param {import("typescript")} ts - * @param {Record} libFileMap - * - */ -const worker = (TypeScriptWorker, ts, libFileMap) => { - /** @type { import("@typescript/vfs") } */ - const tsvfs = globalThis.tsvfs +/** @type { import("@typescript/vfs") } */ +const tsvfs = globalThis.tsvfs +/** @type {import("../src/tsWorker").CustomTSWebWorkerFactory }*/ +const worker = (TypeScriptWorker, ts, libFileMap) => { return class MonacoTSWorker extends TypeScriptWorker { // Adds a custom function to the webworker @@ -59,7 +54,6 @@ const worker = (TypeScriptWorker, ts, libFileMap) => { recurse(mainSrcFile, 0) return miniAST } - } } From be18981f9bb1c023724073b164d85ae87f401a58 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 7 Sep 2020 17:49:49 +0200 Subject: [PATCH 08/37] update deps --- package-lock.json | 30 +++++++++++++++--------------- package.json | 8 ++++---- scripts/bundle.js | 6 +++--- src/tsWorker.ts | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1778064..c76ee4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@typescript/vfs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.2.0.tgz", - "integrity": "sha512-3YhBC+iyngEHjEedSAWk9rbJHoBwa2cd4h/tzb2TXmZc2CUclTl3x5AQRKNoRqm7t+X9PGTc2q2/Dpray/O4mA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.3.0.tgz", + "integrity": "sha512-Bd1LdvQpm0uU2eclcCfO8H8oAGAfEJiKn0acKy/xeZV4sARwXx9MHBMuDX0XDPLmI2JpIm+mFV9Ers65xnoaQg==", "dev": true, "requires": { "debug": "^4.1.1" @@ -47,9 +47,9 @@ "dev": true }, "monaco-plugin-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/monaco-plugin-helpers/-/monaco-plugin-helpers-1.0.2.tgz", - "integrity": "sha512-7kUx8dtd5qVNVgUARBRhnM8oftPglYwlINfigC4yGUiuzqtIN22u1tly8umiOCIPR0eFiBLjt6aN23oZh2QJgg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/monaco-plugin-helpers/-/monaco-plugin-helpers-1.0.3.tgz", + "integrity": "sha512-6AYI3ONAy8ki74qG2JqtFrLdiJHQlgeO5l4Rwr0OMyIpGXhc94y5rZuFxOtgGkxgSrZfHSwOt/MulUNZ/mOQOw==", "dev": true, "requires": { "typescript": "^2.7.2" @@ -82,9 +82,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -92,9 +92,9 @@ } }, "terser": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz", - "integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.0.tgz", + "integrity": "sha512-XTT3D3AwxC54KywJijmY2mxZ8nJiEjBHVYzq8l9OaYuRFWeQNBwvipuzzYEP4e+/AVcd1hqG/CqgsdIRyT45Fg==", "dev": true, "requires": { "commander": "^2.20.0", @@ -103,9 +103,9 @@ } }, "typescript": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", - "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", "dev": true } } diff --git a/package.json b/package.json index 2aa0b2f..555fc91 100644 --- a/package.json +++ b/package.json @@ -20,12 +20,12 @@ "url": "https://github.com/pgherveou/monaco-typescript/issues" }, "devDependencies": { - "@typescript/vfs": "^1.2.0", + "@typescript/vfs": "^1.3.0", "monaco-editor-core": "^0.20.0", "monaco-languages": "^1.10.0", - "monaco-plugin-helpers": "^1.0.2", + "monaco-plugin-helpers": "^1.0.3", "requirejs": "^2.3.6", - "terser": "^4.6.3", - "typescript": "^4.0.2" + "terser": "^5.3.0", + "typescript": "^3.8.2" } } diff --git a/scripts/bundle.js b/scripts/bundle.js index 524c625..58eb20a 100644 --- a/scripts/bundle.js +++ b/scripts/bundle.js @@ -39,13 +39,13 @@ function bundleOne(moduleId, exclude) { 'vs/language/typescript': REPO_ROOT + '/release/dev' }, optimize: 'none' - }, function(buildResponse) { + }, async function(buildResponse) { const filePath = path.join(REPO_ROOT, 'release/min/' + moduleId + '.js'); const fileContents = fs.readFileSync(filePath).toString(); console.log(); console.log(`Minifying ${filePath}...`); - const result = terser.minify(fileContents); - console.log(`Done.`); + const result = await terser.minify(fileContents); + console.log(`Done minifying ${filePath}.`); fs.writeFileSync(filePath, BUNDLED_FILE_HEADER + result.code); }) } diff --git a/src/tsWorker.ts b/src/tsWorker.ts index 92feae6..360633b 100644 --- a/src/tsWorker.ts +++ b/src/tsWorker.ts @@ -281,7 +281,7 @@ export interface ICreateData { /** The shape of the factory */ export interface CustomTSWebWorkerFactory { - (TSWorkerClass: typeof TypeScriptWorker, ts: typeof import("typescript"), libs: Record): typeof TypeScriptWorker + (TSWorkerClass: typeof TypeScriptWorker, tsc: typeof ts, libs: Record): typeof TypeScriptWorker } export function create(ctx: IWorkerContext, createData: ICreateData): TypeScriptWorker { From 07bac428a1c8836d878c1ada0b8c66eae0b9e500 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 7 Sep 2020 17:58:25 +0200 Subject: [PATCH 09/37] update to typescript@4.0.2 --- package-lock.json | 6 +++--- package.json | 2 +- scripts/importTypescript.js | 6 ------ 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index c76ee4b..b4cc3ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -103,9 +103,9 @@ } }, "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", + "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", "dev": true } } diff --git a/package.json b/package.json index 555fc91..11e1a77 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,6 @@ "monaco-plugin-helpers": "^1.0.3", "requirejs": "^2.3.6", "terser": "^5.3.0", - "typescript": "^3.8.2" + "typescript": "^4.0.2" } } diff --git a/scripts/importTypescript.js b/scripts/importTypescript.js index da05901..ed74e78 100644 --- a/scripts/importTypescript.js +++ b/scripts/importTypescript.js @@ -61,12 +61,6 @@ export const typescriptVersion = "${typeScriptDependencyVersion}";\n` process.exit(1); } - // Make sure process.args don't get called in the browser, this - // should only happen in TS 2.6.2 - const beforeProcess = `ts.perfLogger.logInfoEvent("Starting TypeScript v" + ts.versionMajorMinor + " with command line: " + JSON.stringify(process.argv));` - const afterProcess = `// MONACOCHANGE\n ts.perfLogger.logInfoEvent("Starting TypeScript v" + ts.versionMajorMinor + " with command line: " + JSON.stringify([]));\n// END MONACOCHANGE` - tsServices = tsServices.replace(beforeProcess, afterProcess); - var tsServices_amd = generatedNote + tsServices + ` // MONACOCHANGE From 168d87efc1fd9a9f8c9519f2447201c1740f0c16 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 7 Sep 2020 18:03:57 +0200 Subject: [PATCH 10/37] Add prettier --- .prettierignore | 2 ++ .prettierrc | 7 +++++++ package.json | 6 +++++- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 .prettierignore create mode 100644 .prettierrc diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..45537e5 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +/release/ +/src/lib/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..e1b5c6f --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "arrowParens": "always", + "singleQuote": true, + "trailingComma": "none", + "semi": true, + "useTabs": true +} diff --git a/package.json b/package.json index 11e1a77..5c22d28 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "compile": "mrmdir ./release && npm run compile-amd && npm run compile-esm", "watch": "tsc -p ./src --watch", "prepublishOnly": "npm run compile && node ./scripts/bundle && mcopy ./src/monaco.d.ts ./release/monaco.d.ts", - "import-typescript": "node ./scripts/importTypescript" + "import-typescript": "node ./scripts/importTypescript", + "prettier": "prettier --write ." }, "author": "Microsoft Corporation", "license": "MIT", @@ -21,9 +22,12 @@ }, "devDependencies": { "@typescript/vfs": "^1.3.0", + "husky": "^4.3.0", "monaco-editor-core": "^0.20.0", "monaco-languages": "^1.10.0", "monaco-plugin-helpers": "^1.0.3", + "prettier": "^2.1.1", + "pretty-quick": "^3.0.0", "requirejs": "^2.3.6", "terser": "^5.3.0", "typescript": "^4.0.2" From afbf66e977c33e340e94ac516973958e911da9b3 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 7 Sep 2020 18:08:18 +0200 Subject: [PATCH 11/37] Run prettier --- .github/workflows/ci.yml | 4 +- .vscode/settings.json | 2 +- LICENSE.md | 42 +- README.md | 79 +- package-lock.json | 220 ++-- package.json | 71 +- scripts/bundle.js | 37 +- scripts/importTypescript.js | 105 +- src/languageFeatures.ts | 2030 +++++++++++++++++++---------------- src/monaco.contribution.ts | 542 +++++----- src/monaco.d.ts | 855 ++++++++------- src/ts.worker.ts | 8 +- src/tsMode.ts | 181 ++-- src/tsWorker.ts | 740 +++++++------ src/tsconfig.esm.json | 36 +- src/tsconfig.json | 36 +- src/workerManager.ts | 227 ++-- test/custom-worker.html | 421 ++++---- test/custom-worker.js | 101 +- test/index.html | 439 ++++---- 20 files changed, 3358 insertions(+), 2818 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d786245..ceb8433 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,8 +1,8 @@ -name: "CI" +name: 'CI' on: [pull_request] jobs: build: - name: "Builds and Compiles" + name: 'Builds and Compiles' runs-on: ubuntu-latest steps: diff --git a/.vscode/settings.json b/.vscode/settings.json index 9974d57..a5826cb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,4 +5,4 @@ "**/node_modules": true, "**/release": true } -} \ No newline at end of file +} diff --git a/LICENSE.md b/LICENSE.md index f8a94f6..5ae193c 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,21 +1,21 @@ -The MIT License (MIT) - -Copyright (c) Microsoft Corporation - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +The MIT License (MIT) + +Copyright (c) Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 2b3e1ac..4055879 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,39 @@ -# Monaco TypeScript - -Simple TypeScript and JavaScript language support for the Monaco Editor. - -![typescript](https://cloud.githubusercontent.com/assets/5047891/15926623/5262fe08-2e3d-11e6-9b90-1d43fda07178.gif) - -*Note* that this project focuses on single-file scenarios and that things like project-isolation, cross-file-features like Rename etc. are *outside* the scope of this project and not supported. - -## Issues - -Please file issues concerning `monaco-typescript` in the [`monaco-editor` repository](https://github.com/Microsoft/monaco-editor/issues). - -## Installing - -This npm module is bundled and distributed in the [monaco-editor](https://www.npmjs.com/package/monaco-editor) npm module. - - -## Development - -* `git clone https://github.com/Microsoft/monaco-typescript` -* `cd monaco-typescript` -* `npm install .` -* `npm run compile` -* `npm run watch` -* open `$/monaco-typescript/test/index.html` in your favorite browser. - -## Updating TypeScript - -* change typescript's version in `package.json`. -* execute `npm install .` -* execute `npm run import-typescript` -* adopt new APIs - -## Code of Conduct - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. - - -## License -[MIT](https://github.com/Microsoft/monaco-typescript/blob/master/LICENSE.md) +# Monaco TypeScript + +Simple TypeScript and JavaScript language support for the Monaco Editor. + +![typescript](https://cloud.githubusercontent.com/assets/5047891/15926623/5262fe08-2e3d-11e6-9b90-1d43fda07178.gif) + +_Note_ that this project focuses on single-file scenarios and that things like project-isolation, cross-file-features like Rename etc. are _outside_ the scope of this project and not supported. + +## Issues + +Please file issues concerning `monaco-typescript` in the [`monaco-editor` repository](https://github.com/Microsoft/monaco-editor/issues). + +## Installing + +This npm module is bundled and distributed in the [monaco-editor](https://www.npmjs.com/package/monaco-editor) npm module. + +## Development + +- `git clone https://github.com/Microsoft/monaco-typescript` +- `cd monaco-typescript` +- `npm install .` +- `npm run compile` +- `npm run watch` +- open `$/monaco-typescript/test/index.html` in your favorite browser. + +## Updating TypeScript + +- change typescript's version in `package.json`. +- execute `npm install .` +- execute `npm run import-typescript` +- adopt new APIs + +## Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## License + +[MIT](https://github.com/Microsoft/monaco-typescript/blob/master/LICENSE.md) diff --git a/package-lock.json b/package-lock.json index b4cc3ad..ad97fa0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,112 +1,112 @@ { - "name": "@pgherveou/monaco-typescript", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@typescript/vfs": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.3.0.tgz", - "integrity": "sha512-Bd1LdvQpm0uU2eclcCfO8H8oAGAfEJiKn0acKy/xeZV4sARwXx9MHBMuDX0XDPLmI2JpIm+mFV9Ers65xnoaQg==", - "dev": true, - "requires": { - "debug": "^4.1.1" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "monaco-editor-core": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/monaco-editor-core/-/monaco-editor-core-0.20.0.tgz", - "integrity": "sha512-4mdmfEejTvRZzrEIn70jqqNl3g15vnkRdTkJ8uMK4jiljntlwhiSc5vknZOLt1QM8za16C3tDrSl2mTL9ma2Sg==", - "dev": true - }, - "monaco-languages": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/monaco-languages/-/monaco-languages-1.10.0.tgz", - "integrity": "sha512-ARAws17Xh0K4WsZYkJY6CqHn9EYdYN8CjzK6w/jgXIwU0owzCdUWxzu+FNJ/LeDLcKxL/YK3phcwGFj9IqX2yw==", - "dev": true - }, - "monaco-plugin-helpers": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/monaco-plugin-helpers/-/monaco-plugin-helpers-1.0.3.tgz", - "integrity": "sha512-6AYI3ONAy8ki74qG2JqtFrLdiJHQlgeO5l4Rwr0OMyIpGXhc94y5rZuFxOtgGkxgSrZfHSwOt/MulUNZ/mOQOw==", - "dev": true, - "requires": { - "typescript": "^2.7.2" - }, - "dependencies": { - "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", - "dev": true - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "requirejs": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", - "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "terser": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.0.tgz", - "integrity": "sha512-XTT3D3AwxC54KywJijmY2mxZ8nJiEjBHVYzq8l9OaYuRFWeQNBwvipuzzYEP4e+/AVcd1hqG/CqgsdIRyT45Fg==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - } - }, - "typescript": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", - "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", - "dev": true - } - } + "name": "monaco-typescript", + "version": "3.7.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@typescript/vfs": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.3.0.tgz", + "integrity": "sha512-Bd1LdvQpm0uU2eclcCfO8H8oAGAfEJiKn0acKy/xeZV4sARwXx9MHBMuDX0XDPLmI2JpIm+mFV9Ers65xnoaQg==", + "dev": true, + "requires": { + "debug": "^4.1.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "monaco-editor-core": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/monaco-editor-core/-/monaco-editor-core-0.20.0.tgz", + "integrity": "sha512-4mdmfEejTvRZzrEIn70jqqNl3g15vnkRdTkJ8uMK4jiljntlwhiSc5vknZOLt1QM8za16C3tDrSl2mTL9ma2Sg==", + "dev": true + }, + "monaco-languages": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/monaco-languages/-/monaco-languages-1.10.0.tgz", + "integrity": "sha512-ARAws17Xh0K4WsZYkJY6CqHn9EYdYN8CjzK6w/jgXIwU0owzCdUWxzu+FNJ/LeDLcKxL/YK3phcwGFj9IqX2yw==", + "dev": true + }, + "monaco-plugin-helpers": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/monaco-plugin-helpers/-/monaco-plugin-helpers-1.0.3.tgz", + "integrity": "sha512-6AYI3ONAy8ki74qG2JqtFrLdiJHQlgeO5l4Rwr0OMyIpGXhc94y5rZuFxOtgGkxgSrZfHSwOt/MulUNZ/mOQOw==", + "dev": true, + "requires": { + "typescript": "^2.7.2" + }, + "dependencies": { + "typescript": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "dev": true + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "requirejs": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", + "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "terser": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.0.tgz", + "integrity": "sha512-XTT3D3AwxC54KywJijmY2mxZ8nJiEjBHVYzq8l9OaYuRFWeQNBwvipuzzYEP4e+/AVcd1hqG/CqgsdIRyT45Fg==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } + }, + "typescript": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", + "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", + "dev": true + } + } } diff --git a/package.json b/package.json index 5c22d28..70dcfd7 100644 --- a/package.json +++ b/package.json @@ -1,35 +1,40 @@ { - "name": "@pgherveou/monaco-typescript", - "version": "3.10.0", - "description": "TypeScript and JavaScript language support for Monaco Editor", - "scripts": { - "compile-amd": "mcopy ./src/lib/typescriptServices-amd.js ./release/dev/lib/typescriptServices.js && tsc -p ./src/tsconfig.json", - "compile-esm": "mcopy ./src/lib/typescriptServices.js ./release/esm/lib/typescriptServices.js && tsc -p ./src/tsconfig.esm.json", - "compile": "mrmdir ./release && npm run compile-amd && npm run compile-esm", - "watch": "tsc -p ./src --watch", - "prepublishOnly": "npm run compile && node ./scripts/bundle && mcopy ./src/monaco.d.ts ./release/monaco.d.ts", - "import-typescript": "node ./scripts/importTypescript", - "prettier": "prettier --write ." - }, - "author": "Microsoft Corporation", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/pgherveou/monaco-typescript" - }, - "bugs": { - "url": "https://github.com/pgherveou/monaco-typescript/issues" - }, - "devDependencies": { - "@typescript/vfs": "^1.3.0", - "husky": "^4.3.0", - "monaco-editor-core": "^0.20.0", - "monaco-languages": "^1.10.0", - "monaco-plugin-helpers": "^1.0.3", - "prettier": "^2.1.1", - "pretty-quick": "^3.0.0", - "requirejs": "^2.3.6", - "terser": "^5.3.0", - "typescript": "^4.0.2" - } + "name": "monaco-typescript", + "version": "3.7.0", + "description": "TypeScript and JavaScript language support for Monaco Editor", + "scripts": { + "compile-amd": "mcopy ./src/lib/typescriptServices-amd.js ./release/dev/lib/typescriptServices.js && tsc -p ./src/tsconfig.json", + "compile-esm": "mcopy ./src/lib/typescriptServices.js ./release/esm/lib/typescriptServices.js && tsc -p ./src/tsconfig.esm.json", + "compile": "mrmdir ./release && npm run compile-amd && npm run compile-esm", + "watch": "tsc -p ./src --watch", + "prepublishOnly": "npm run compile && node ./scripts/bundle && mcopy ./src/monaco.d.ts ./release/monaco.d.ts", + "import-typescript": "node ./scripts/importTypescript", + "prettier": "prettier --write ." + }, + "author": "Microsoft Corporation", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/monaco-typescript" + }, + "bugs": { + "url": "https://github.com/Microsoft/monaco-typescript/issues" + }, + "devDependencies": { + "@typescript/vfs": "^1.3.0", + "husky": "^4.3.0", + "monaco-editor-core": "^0.20.0", + "monaco-languages": "^1.10.0", + "monaco-plugin-helpers": "^1.0.3", + "prettier": "^2.1.1", + "pretty-quick": "^3.0.0", + "requirejs": "^2.3.6", + "terser": "^5.3.0", + "typescript": "^4.0.2" + }, + "husky": { + "hooks": { + "pre-commit": "pretty-quick --staged" + } + } } diff --git a/scripts/bundle.js b/scripts/bundle.js index 58eb20a..b15988c 100644 --- a/scripts/bundle.js +++ b/scripts/bundle.js @@ -30,22 +30,25 @@ bundleOne('tsMode'); bundleOne('tsWorker'); function bundleOne(moduleId, exclude) { - requirejs.optimize({ - baseUrl: 'release/dev/', - name: 'vs/language/typescript/' + moduleId, - out: 'release/min/' + moduleId + '.js', - exclude: exclude, - paths: { - 'vs/language/typescript': REPO_ROOT + '/release/dev' + requirejs.optimize( + { + baseUrl: 'release/dev/', + name: 'vs/language/typescript/' + moduleId, + out: 'release/min/' + moduleId + '.js', + exclude: exclude, + paths: { + 'vs/language/typescript': REPO_ROOT + '/release/dev' + }, + optimize: 'none' }, - optimize: 'none' - }, async function(buildResponse) { - const filePath = path.join(REPO_ROOT, 'release/min/' + moduleId + '.js'); - const fileContents = fs.readFileSync(filePath).toString(); - console.log(); - console.log(`Minifying ${filePath}...`); - const result = await terser.minify(fileContents); - console.log(`Done minifying ${filePath}.`); - fs.writeFileSync(filePath, BUNDLED_FILE_HEADER + result.code); - }) + async function (buildResponse) { + const filePath = path.join(REPO_ROOT, 'release/min/' + moduleId + '.js'); + const fileContents = fs.readFileSync(filePath).toString(); + console.log(); + console.log(`Minifying ${filePath}...`); + const result = await terser.minify(fileContents); + console.log(`Done minifying ${filePath}.`); + fs.writeFileSync(filePath, BUNDLED_FILE_HEADER + result.code); + } + ); } diff --git a/scripts/importTypescript.js b/scripts/importTypescript.js index ed74e78..a36c217 100644 --- a/scripts/importTypescript.js +++ b/scripts/importTypescript.js @@ -12,7 +12,10 @@ const generatedNote = `// // `; -const TYPESCRIPT_LIB_SOURCE = path.join(__dirname, '../node_modules/typescript/lib'); +const TYPESCRIPT_LIB_SOURCE = path.join( + __dirname, + '../node_modules/typescript/lib' +); const TYPESCRIPT_LIB_DESTINATION = path.join(__dirname, '../src/lib'); (function () { @@ -23,8 +26,11 @@ const TYPESCRIPT_LIB_DESTINATION = path.join(__dirname, '../src/lib'); } importLibs(); - const npmLsOutput = JSON.parse(child_process.execSync("npm ls typescript --depth=0 --json=true").toString()); - const typeScriptDependencyVersion = npmLsOutput.dependencies.typescript.version; + const npmLsOutput = JSON.parse( + child_process.execSync('npm ls typescript --depth=0 --json=true').toString() + ); + const typeScriptDependencyVersion = + npmLsOutput.dependencies.typescript.version; fs.writeFileSync( path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServicesMetadata.ts'), @@ -32,36 +38,55 @@ const TYPESCRIPT_LIB_DESTINATION = path.join(__dirname, '../src/lib'); export const typescriptVersion = "${typeScriptDependencyVersion}";\n` ); - var tsServices = fs.readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescriptServices.js')).toString(); + var tsServices = fs + .readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescriptServices.js')) + .toString(); // Ensure we never run into the node system... // (this also removes require calls that trick webpack into shimming those modules...) - tsServices = ( - tsServices.replace(/\n ts\.sys =([^]*)\n \}\)\(\);/m, `\n // MONACOCHANGE\n ts.sys = undefined;\n // END MONACOCHANGE`) + tsServices = tsServices.replace( + /\n ts\.sys =([^]*)\n \}\)\(\);/m, + `\n // MONACOCHANGE\n ts.sys = undefined;\n // END MONACOCHANGE` ); // Eliminate more require() calls... - tsServices = tsServices.replace(/^( +)etwModule = require\(.*$/m, '$1// MONACOCHANGE\n$1etwModule = undefined;\n$1// END MONACOCHANGE'); - tsServices = tsServices.replace(/^( +)var result = ts\.sys\.require\(.*$/m, '$1// MONACOCHANGE\n$1var result = undefined;\n$1// END MONACOCHANGE'); + tsServices = tsServices.replace( + /^( +)etwModule = require\(.*$/m, + '$1// MONACOCHANGE\n$1etwModule = undefined;\n$1// END MONACOCHANGE' + ); + tsServices = tsServices.replace( + /^( +)var result = ts\.sys\.require\(.*$/m, + '$1// MONACOCHANGE\n$1var result = undefined;\n$1// END MONACOCHANGE' + ); // Flag any new require calls (outside comments) so they can be corrected preemptively. // To avoid missing cases (or using an even more complex regex), temporarily remove comments // about require() and then check for lines actually calling require(). // \/[*/] matches the start of a comment (single or multi-line). // ^\s+\*[^/] matches (presumably) a later line of a multi-line comment. - const tsServicesNoCommentedRequire = tsServices.replace(/(\/[*/]|^\s+\*[^/]).*\brequire\(.*/gm, ''); - const linesWithRequire = tsServicesNoCommentedRequire.match(/^.*?\brequire\(.*$/gm) + const tsServicesNoCommentedRequire = tsServices.replace( + /(\/[*/]|^\s+\*[^/]).*\brequire\(.*/gm, + '' + ); + const linesWithRequire = tsServicesNoCommentedRequire.match( + /^.*?\brequire\(.*$/gm + ); // Allow error messages to include references to require() in their strings - const runtimeRequires = linesWithRequire && linesWithRequire.filter(l => !l.includes(": diag(")) + const runtimeRequires = + linesWithRequire && linesWithRequire.filter((l) => !l.includes(': diag(')); if (runtimeRequires && runtimeRequires.length && linesWithRequire) { - console.error('Found new require() calls on the following lines. These should be removed to avoid breaking webpack builds.\n'); + console.error( + 'Found new require() calls on the following lines. These should be removed to avoid breaking webpack builds.\n' + ); console.error(linesWithRequire.join('\n')); process.exit(1); } - var tsServices_amd = generatedNote + tsServices + + var tsServices_amd = + generatedNote + + tsServices + ` // MONACOCHANGE // Defining the entire module name because r.js has an issue and cannot bundle this file @@ -69,9 +94,14 @@ export const typescriptVersion = "${typeScriptDependencyVersion}";\n` define("vs/language/typescript/lib/typescriptServices", [], function() { return ts; }); // END MONACOCHANGE `; - fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices-amd.js'), stripSourceMaps(tsServices_amd)); + fs.writeFileSync( + path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices-amd.js'), + stripSourceMaps(tsServices_amd) + ); - var tsServices_esm = generatedNote + tsServices + + var tsServices_esm = + generatedNote + + tsServices + ` // MONACOCHANGE export var createClassifier = ts.createClassifier; @@ -85,17 +115,23 @@ export var ScriptTarget = ts.ScriptTarget; export var TokenClass = ts.TokenClass; // END MONACOCHANGE `; - fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices.js'), stripSourceMaps(tsServices_esm)); + fs.writeFileSync( + path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices.js'), + stripSourceMaps(tsServices_esm) + ); - var dtsServices = fs.readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescriptServices.d.ts')).toString(); - dtsServices += - ` + var dtsServices = fs + .readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescriptServices.d.ts')) + .toString(); + dtsServices += ` // MONACOCHANGE export = ts; // END MONACOCHANGE `; - fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices.d.ts'), generatedNote + dtsServices); - + fs.writeFileSync( + path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices.d.ts'), + generatedNote + dtsServices + ); })(); function importLibs() { @@ -112,9 +148,7 @@ ${generatedNote} /** Contains all the lib files */ export const libFileMap: Record = {} -` -; - +`; var strIndexResult = `/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. @@ -123,10 +157,10 @@ ${generatedNote} /** Contains all the lib files */ export const libFileSet: Record = {} -` -; - - var dtsFiles = fs.readdirSync(TYPESCRIPT_LIB_SOURCE).filter(f => f.includes("lib.")); +`; + var dtsFiles = fs + .readdirSync(TYPESCRIPT_LIB_SOURCE) + .filter((f) => f.includes('lib.')); while (dtsFiles.length > 0) { var name = dtsFiles.shift(); var output = readLibFile(name).replace(/\r\n/g, '\n'); @@ -134,8 +168,14 @@ export const libFileSet: Record = {} strIndexResult += `libFileSet['${name}'] = true;\n`; } - fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'lib.ts'), strLibResult); - fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'lib.index.ts'), strIndexResult); + fs.writeFileSync( + path.join(TYPESCRIPT_LIB_DESTINATION, 'lib.ts'), + strLibResult + ); + fs.writeFileSync( + path.join(TYPESCRIPT_LIB_DESTINATION, 'lib.index.ts'), + strIndexResult + ); } /** @@ -153,7 +193,10 @@ function escapeText(text) { var _backslash = '\\'.charCodeAt(0); var _doubleQuote = '"'.charCodeAt(0); - var startPos = 0, chrCode, replaceWith = null, resultPieces = []; + var startPos = 0, + chrCode, + replaceWith = null, + resultPieces = []; for (var i = 0, len = text.length; i < len; i++) { chrCode = text.charCodeAt(i); diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index b3fe20c..909525f 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -1,902 +1,1128 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import { LanguageServiceDefaultsImpl } from './monaco.contribution'; -import * as ts from './lib/typescriptServices'; -import { TypeScriptWorker } from './tsWorker'; -import { libFileSet } from "./lib/lib.index" - -import Uri = monaco.Uri; -import Position = monaco.Position; -import Range = monaco.Range; -import CancellationToken = monaco.CancellationToken; -import IDisposable = monaco.IDisposable; - -//#region utils copied from typescript to prevent loading the entire typescriptServices --- - -enum IndentStyle { - None = 0, - Block = 1, - Smart = 2 -} - -export function flattenDiagnosticMessageText(diag: string | ts.DiagnosticMessageChain | undefined, newLine: string, indent = 0): string { - if (typeof diag === "string") { - return diag; - } - else if (diag === undefined) { - return ""; - } - let result = ""; - if (indent) { - result += newLine; - - for (let i = 0; i < indent; i++) { - result += " "; - } - } - result += diag.messageText; - indent++; - if (diag.next) { - for (const kid of diag.next) { - result += flattenDiagnosticMessageText(kid, newLine, indent); - } - } - return result; -} - -function displayPartsToString(displayParts: ts.SymbolDisplayPart[] | undefined): string { - if (displayParts) { - return displayParts.map((displayPart) => displayPart.text).join(""); - } - return ""; -} - -//#endregion - -export abstract class Adapter { - - constructor(protected _worker: (...uris: Uri[]) => Promise) { - } - - // protected _positionToOffset(model: monaco.editor.ITextModel, position: monaco.IPosition): number { - // return model.getOffsetAt(position); - // } - - // protected _offsetToPosition(model: monaco.editor.ITextModel, offset: number): monaco.IPosition { - // return model.getPositionAt(offset); - // } - - protected _textSpanToRange(model: monaco.editor.ITextModel, span: ts.TextSpan): monaco.IRange { - let p1 = model.getPositionAt(span.start); - let p2 = model.getPositionAt(span.start + span.length); - let { lineNumber: startLineNumber, column: startColumn } = p1; - let { lineNumber: endLineNumber, column: endColumn } = p2; - return { startLineNumber, startColumn, endLineNumber, endColumn }; - } -} - -// --- lib files - -export class LibFiles { - - private _libFiles: Record; - private _hasFetchedLibFiles: boolean; - private _fetchLibFilesPromise: Promise | null; - - constructor( - private readonly _worker: (...uris: Uri[]) => Promise - ) { - this._libFiles = {}; - this._hasFetchedLibFiles = false; - this._fetchLibFilesPromise = null; - } - - public isLibFile(uri: Uri | null): boolean { - if (!uri) { - return false; - } - if (uri.path.indexOf("/lib.") === 0) { - return !!libFileSet[uri.path.slice(1)]; - } - return false; - } - - public getOrCreateModel(uri: Uri): monaco.editor.ITextModel | null { - const model = monaco.editor.getModel(uri); - if (model) { - return model; - } - if (this.isLibFile(uri) && this._hasFetchedLibFiles) { - return monaco.editor.createModel(this._libFiles[uri.path.slice(1)], "javascript", uri); - } - return null; - } - - private _containsLibFile(uris: (Uri | null)[]): boolean { - for (let uri of uris) { - if (this.isLibFile(uri)) { - return true; - } - } - return false; - } - - public async fetchLibFilesIfNecessary(uris: (Uri | null)[]): Promise { - if (!this._containsLibFile(uris)) { - // no lib files necessary - return; - } - await this._fetchLibFiles(); - } - - private _fetchLibFiles(): Promise { - if (!this._fetchLibFilesPromise) { - this._fetchLibFilesPromise = ( - this._worker() - .then(w => w.getLibFiles()) - .then((libFiles) => { - this._hasFetchedLibFiles = true; - this._libFiles = libFiles; - }) - ); - } - return this._fetchLibFilesPromise; - } -} - -// --- diagnostics --- --- - -enum DiagnosticCategory { - Warning = 0, - Error = 1, - Suggestion = 2, - Message = 3 -} - -export class DiagnosticsAdapter extends Adapter { - - private _disposables: IDisposable[] = []; - private _listener: { [uri: string]: IDisposable } = Object.create(null); - - constructor( - private readonly _libFiles: LibFiles, - private _defaults: LanguageServiceDefaultsImpl, - private _selector: string, - worker: (...uris: Uri[]) => Promise - ) { - super(worker); - - const onModelAdd = (model: monaco.editor.IModel): void => { - if (model.getModeId() !== _selector) { - return; - } - - let handle: number; - const changeSubscription = model.onDidChangeContent(() => { - clearTimeout(handle); - handle = setTimeout(() => this._doValidate(model), 500); - }); - - this._listener[model.uri.toString()] = { - dispose() { - changeSubscription.dispose(); - clearTimeout(handle); - } - }; - - this._doValidate(model); - }; - - const onModelRemoved = (model: monaco.editor.IModel): void => { - monaco.editor.setModelMarkers(model, this._selector, []); - const key = model.uri.toString(); - if (this._listener[key]) { - this._listener[key].dispose(); - delete this._listener[key]; - } - }; - - this._disposables.push(monaco.editor.onDidCreateModel(onModelAdd)); - this._disposables.push(monaco.editor.onWillDisposeModel(onModelRemoved)); - this._disposables.push(monaco.editor.onDidChangeModelLanguage(event => { - onModelRemoved(event.model); - onModelAdd(event.model); - })); - - this._disposables.push({ - dispose() { - for (const model of monaco.editor.getModels()) { - onModelRemoved(model); - } - } - }); - - const recomputeDiagostics = () => { - // redo diagnostics when options change - for (const model of monaco.editor.getModels()) { - onModelRemoved(model); - onModelAdd(model); - } - }; - this._disposables.push(this._defaults.onDidChange(recomputeDiagostics)); - this._disposables.push(this._defaults.onDidExtraLibsChange(recomputeDiagostics)); - - monaco.editor.getModels().forEach(onModelAdd); - } - - public dispose(): void { - this._disposables.forEach(d => d && d.dispose()); - this._disposables = []; - } - - private async _doValidate(model: monaco.editor.ITextModel): Promise { - const worker = await this._worker(model.uri); - - if (model.isDisposed()) { - // model was disposed in the meantime - return; - } - - const promises: Promise[] = []; - const { noSyntaxValidation, noSemanticValidation, noSuggestionDiagnostics } = this._defaults.getDiagnosticsOptions(); - if (!noSyntaxValidation) { - promises.push(worker.getSyntacticDiagnostics(model.uri.toString())); - } - if (!noSemanticValidation) { - promises.push(worker.getSemanticDiagnostics(model.uri.toString())); - } - if (!noSuggestionDiagnostics) { - promises.push(worker.getSuggestionDiagnostics(model.uri.toString())); - } - - const allDiagnostics = await Promise.all(promises); - - if (!allDiagnostics || model.isDisposed()) { - // model was disposed in the meantime - return; - } - - const diagnostics = allDiagnostics - .reduce((p, c) => c.concat(p), []) - .filter(d => (this._defaults.getDiagnosticsOptions().diagnosticCodesToIgnore || []).indexOf(d.code) === -1); - - // Fetch lib files if necessary - const relatedUris = diagnostics - .map(d => d.relatedInformation || []) - .reduce((p, c) => c.concat(p), []) - .map(relatedInformation => relatedInformation.file ? monaco.Uri.parse(relatedInformation.file.fileName) : null); - - await this._libFiles.fetchLibFilesIfNecessary(relatedUris); - - if (model.isDisposed()) { - // model was disposed in the meantime - return; - } - - monaco.editor.setModelMarkers(model, this._selector, diagnostics.map(d => this._convertDiagnostics(model, d))); - } - - private _convertDiagnostics(model: monaco.editor.ITextModel, diag: ts.Diagnostic): monaco.editor.IMarkerData { - const diagStart = diag.start || 0; - const diagLength = diag.length || 1; - const { lineNumber: startLineNumber, column: startColumn } = model.getPositionAt(diagStart); - const { lineNumber: endLineNumber, column: endColumn } = model.getPositionAt(diagStart + diagLength); - - return { - severity: this._tsDiagnosticCategoryToMarkerSeverity(diag.category), - startLineNumber, - startColumn, - endLineNumber, - endColumn, - message: flattenDiagnosticMessageText(diag.messageText, '\n'), - code: diag.code.toString(), - tags: diag.reportsUnnecessary ? [monaco.MarkerTag.Unnecessary] : [], - relatedInformation: this._convertRelatedInformation(model, diag.relatedInformation), - }; - } - - private _convertRelatedInformation(model: monaco.editor.ITextModel, relatedInformation?: ts.DiagnosticRelatedInformation[]): monaco.editor.IRelatedInformation[] | undefined { - if (!relatedInformation) { - return; - } - - const result: monaco.editor.IRelatedInformation[] = []; - relatedInformation.forEach((info) => { - let relatedResource: monaco.editor.ITextModel | null = model; - if (info.file) { - const relatedResourceUri = monaco.Uri.parse(info.file.fileName); - relatedResource = this._libFiles.getOrCreateModel(relatedResourceUri); - } - - if (!relatedResource) { - return; - } - const infoStart = info.start || 0; - const infoLength = info.length || 1; - const { lineNumber: startLineNumber, column: startColumn } = relatedResource.getPositionAt(infoStart); - const { lineNumber: endLineNumber, column: endColumn } = relatedResource.getPositionAt(infoStart + infoLength); - - result.push({ - resource: relatedResource.uri, - startLineNumber, - startColumn, - endLineNumber, - endColumn, - message: flattenDiagnosticMessageText(info.messageText, '\n') - }); - }); - return result; - } - - private _tsDiagnosticCategoryToMarkerSeverity(category: ts.DiagnosticCategory): monaco.MarkerSeverity { - switch (category) { - case DiagnosticCategory.Error: return monaco.MarkerSeverity.Error - case DiagnosticCategory.Message: return monaco.MarkerSeverity.Info - case DiagnosticCategory.Warning: return monaco.MarkerSeverity.Warning - case DiagnosticCategory.Suggestion: return monaco.MarkerSeverity.Hint - } - return monaco.MarkerSeverity.Info; - } -} - -// --- suggest ------ - -interface MyCompletionItem extends monaco.languages.CompletionItem { - label: string; - uri: Uri; - position: Position; -} - -export class SuggestAdapter extends Adapter implements monaco.languages.CompletionItemProvider { - - public get triggerCharacters(): string[] { - return ['.']; - } - - public async provideCompletionItems(model: monaco.editor.ITextModel, position: Position, _context: monaco.languages.CompletionContext, token: CancellationToken): Promise { - const wordInfo = model.getWordUntilPosition(position); - const wordRange = new Range(position.lineNumber, wordInfo.startColumn, position.lineNumber, wordInfo.endColumn); - const resource = model.uri; - const offset = model.getOffsetAt(position); - - const worker = await this._worker(resource); - const info = await worker.getCompletionsAtPosition(resource.toString(), offset); - - if (!info || model.isDisposed()) { - return; - } - - const suggestions: MyCompletionItem[] = info.entries.map(entry => { - let range = wordRange; - if (entry.replacementSpan) { - const p1 = model.getPositionAt(entry.replacementSpan.start); - const p2 = model.getPositionAt(entry.replacementSpan.start + entry.replacementSpan.length); - range = new Range(p1.lineNumber, p1.column, p2.lineNumber, p2.column); - } - - return { - uri: resource, - position: position, - range: range, - label: entry.name, - insertText: entry.name, - sortText: entry.sortText, - kind: SuggestAdapter.convertKind(entry.kind) - }; - }); - - return { - suggestions - }; - } - - public async resolveCompletionItem(model: monaco.editor.ITextModel, _position: Position, item: monaco.languages.CompletionItem, token: CancellationToken): Promise { - const myItem = item; - const resource = myItem.uri; - const position = myItem.position; - const offset = model.getOffsetAt(position); - - const worker = await this._worker(resource); - const details = await worker.getCompletionEntryDetails(resource.toString(), offset, myItem.label); - if (!details || model.isDisposed()) { - return myItem; - } - return { - uri: resource, - position: position, - label: details.name, - kind: SuggestAdapter.convertKind(details.kind), - detail: displayPartsToString(details.displayParts), - documentation: { - value: displayPartsToString(details.documentation) - } - }; - } - - private static convertKind(kind: string): monaco.languages.CompletionItemKind { - switch (kind) { - case Kind.primitiveType: - case Kind.keyword: - return monaco.languages.CompletionItemKind.Keyword; - case Kind.variable: - case Kind.localVariable: - return monaco.languages.CompletionItemKind.Variable; - case Kind.memberVariable: - case Kind.memberGetAccessor: - case Kind.memberSetAccessor: - return monaco.languages.CompletionItemKind.Field; - case Kind.function: - case Kind.memberFunction: - case Kind.constructSignature: - case Kind.callSignature: - case Kind.indexSignature: - return monaco.languages.CompletionItemKind.Function; - case Kind.enum: - return monaco.languages.CompletionItemKind.Enum; - case Kind.module: - return monaco.languages.CompletionItemKind.Module; - case Kind.class: - return monaco.languages.CompletionItemKind.Class; - case Kind.interface: - return monaco.languages.CompletionItemKind.Interface; - case Kind.warning: - return monaco.languages.CompletionItemKind.File; - } - - return monaco.languages.CompletionItemKind.Property; - } -} - -export class SignatureHelpAdapter extends Adapter implements monaco.languages.SignatureHelpProvider { - - public signatureHelpTriggerCharacters = ['(', ',']; - - public async provideSignatureHelp(model: monaco.editor.ITextModel, position: Position, token: CancellationToken): Promise { - const resource = model.uri; - const offset = model.getOffsetAt(position); - const worker = await this._worker(resource); - const info = await worker.getSignatureHelpItems(resource.toString(), offset); - - if (!info || model.isDisposed()) { - return; - } - - const ret: monaco.languages.SignatureHelp = { - activeSignature: info.selectedItemIndex, - activeParameter: info.argumentIndex, - signatures: [] - }; - - info.items.forEach(item => { - - const signature: monaco.languages.SignatureInformation = { - label: '', - parameters: [] - }; - - signature.documentation = { - value: displayPartsToString(item.documentation) - }; - signature.label += displayPartsToString(item.prefixDisplayParts); - item.parameters.forEach((p, i, a) => { - const label = displayPartsToString(p.displayParts); - const parameter: monaco.languages.ParameterInformation = { - label: label, - documentation: { - value: displayPartsToString(p.documentation) - } - }; - signature.label += label; - signature.parameters.push(parameter); - if (i < a.length - 1) { - signature.label += displayPartsToString(item.separatorDisplayParts); - } - }); - signature.label += displayPartsToString(item.suffixDisplayParts); - ret.signatures.push(signature); - }); - - return { - value: ret, - dispose() { } - }; - } -} - -// --- hover ------ - -export class QuickInfoAdapter extends Adapter implements monaco.languages.HoverProvider { - - public async provideHover(model: monaco.editor.ITextModel, position: Position, token: CancellationToken): Promise { - const resource = model.uri; - const offset = model.getOffsetAt(position); - const worker = await this._worker(resource); - const info = await worker.getQuickInfoAtPosition(resource.toString(), offset); - - if (!info || model.isDisposed()) { - return; - } - - const documentation = displayPartsToString(info.documentation); - const tags = info.tags ? info.tags.map(tag => { - const label = `*@${tag.name}*`; - if (!tag.text) { - return label; - } - return label + (tag.text.match(/\r\n|\n/g) ? ' \n' + tag.text : ` - ${tag.text}`); - }).join(' \n\n') : ''; - const contents = displayPartsToString(info.displayParts); - return { - range: this._textSpanToRange(model, info.textSpan), - contents: [{ - value: '```js\n' + contents + '\n```\n' - }, { - value: documentation + (tags ? '\n\n' + tags : '') - }] - }; - } -} - -// --- occurrences ------ - -export class OccurrencesAdapter extends Adapter implements monaco.languages.DocumentHighlightProvider { - - public async provideDocumentHighlights(model: monaco.editor.ITextModel, position: Position, token: CancellationToken): Promise { - const resource = model.uri; - const offset = model.getOffsetAt(position) - const worker = await this._worker(resource); - const entries = await worker.getOccurrencesAtPosition(resource.toString(), offset); - - if (!entries || model.isDisposed()) { - return; - } - - return entries.map(entry => { - return { - range: this._textSpanToRange(model, entry.textSpan), - kind: entry.isWriteAccess ? monaco.languages.DocumentHighlightKind.Write : monaco.languages.DocumentHighlightKind.Text - }; - }); - } -} - -// --- definition ------ - -export class DefinitionAdapter extends Adapter { - - constructor( - private readonly _libFiles: LibFiles, - worker: (...uris: Uri[]) => Promise - ) { - super(worker); - } - - public async provideDefinition(model: monaco.editor.ITextModel, position: Position, token: CancellationToken): Promise { - const resource = model.uri; - const offset = model.getOffsetAt(position); - const worker = await this._worker(resource); - const entries = await worker.getDefinitionAtPosition(resource.toString(), offset); - - if (!entries || model.isDisposed()) { - return; - } - - // Fetch lib files if necessary - await this._libFiles.fetchLibFilesIfNecessary(entries.map(entry => Uri.parse(entry.fileName))); - - if (model.isDisposed()) { - return; - } - - const result: monaco.languages.Location[] = []; - for (let entry of entries) { - const uri = Uri.parse(entry.fileName); - const refModel = this._libFiles.getOrCreateModel(uri); - if (refModel) { - result.push({ - uri: uri, - range: this._textSpanToRange(refModel, entry.textSpan) - }); - } - } - return result; - } -} - -// --- references ------ - -export class ReferenceAdapter extends Adapter implements monaco.languages.ReferenceProvider { - - constructor( - private readonly _libFiles: LibFiles, - worker: (...uris: Uri[]) => Promise - ) { - super(worker); - } - - public async provideReferences(model: monaco.editor.ITextModel, position: Position, context: monaco.languages.ReferenceContext, token: CancellationToken): Promise { - const resource = model.uri; - const offset = model.getOffsetAt(position); - const worker = await this._worker(resource); - const entries = await worker.getReferencesAtPosition(resource.toString(), offset); - - if (!entries || model.isDisposed()) { - return; - } - - // Fetch lib files if necessary - await this._libFiles.fetchLibFilesIfNecessary(entries.map(entry => Uri.parse(entry.fileName))); - - if (model.isDisposed()) { - return; - } - - const result: monaco.languages.Location[] = []; - for (let entry of entries) { - const uri = Uri.parse(entry.fileName); - const refModel = this._libFiles.getOrCreateModel(uri); - if (refModel) { - result.push({ - uri: uri, - range: this._textSpanToRange(refModel, entry.textSpan) - }); - } - } - return result; - } -} - -// --- outline ------ - -export class OutlineAdapter extends Adapter implements monaco.languages.DocumentSymbolProvider { - - public async provideDocumentSymbols(model: monaco.editor.ITextModel, token: CancellationToken): Promise { - const resource = model.uri; - const worker = await this._worker(resource); - const items = await worker.getNavigationBarItems(resource.toString()); - - if (!items || model.isDisposed()) { - return; - } - - const convert = (bucket: monaco.languages.DocumentSymbol[], item: ts.NavigationBarItem, containerLabel?: string): void => { - let result: monaco.languages.DocumentSymbol = { - name: item.text, - detail: '', - kind: (outlineTypeTable[item.kind] || monaco.languages.SymbolKind.Variable), - range: this._textSpanToRange(model, item.spans[0]), - selectionRange: this._textSpanToRange(model, item.spans[0]), - tags: [], - containerName: containerLabel - }; - - if (item.childItems && item.childItems.length > 0) { - for (let child of item.childItems) { - convert(bucket, child, result.name); - } - } - - bucket.push(result); - } - - let result: monaco.languages.DocumentSymbol[] = []; - items.forEach(item => convert(result, item)); - return result; - } -} - -export class Kind { - public static unknown: string = ''; - public static keyword: string = 'keyword'; - public static script: string = 'script'; - public static module: string = 'module'; - public static class: string = 'class'; - public static interface: string = 'interface'; - public static type: string = 'type'; - public static enum: string = 'enum'; - public static variable: string = 'var'; - public static localVariable: string = 'local var'; - public static function: string = 'function'; - public static localFunction: string = 'local function'; - public static memberFunction: string = 'method'; - public static memberGetAccessor: string = 'getter'; - public static memberSetAccessor: string = 'setter'; - public static memberVariable: string = 'property'; - public static constructorImplementation: string = 'constructor'; - public static callSignature: string = 'call'; - public static indexSignature: string = 'index'; - public static constructSignature: string = 'construct'; - public static parameter: string = 'parameter'; - public static typeParameter: string = 'type parameter'; - public static primitiveType: string = 'primitive type'; - public static label: string = 'label'; - public static alias: string = 'alias'; - public static const: string = 'const'; - public static let: string = 'let'; - public static warning: string = 'warning'; -} - -let outlineTypeTable: { [kind: string]: monaco.languages.SymbolKind } = Object.create(null); -outlineTypeTable[Kind.module] = monaco.languages.SymbolKind.Module; -outlineTypeTable[Kind.class] = monaco.languages.SymbolKind.Class; -outlineTypeTable[Kind.enum] = monaco.languages.SymbolKind.Enum; -outlineTypeTable[Kind.interface] = monaco.languages.SymbolKind.Interface; -outlineTypeTable[Kind.memberFunction] = monaco.languages.SymbolKind.Method; -outlineTypeTable[Kind.memberVariable] = monaco.languages.SymbolKind.Property; -outlineTypeTable[Kind.memberGetAccessor] = monaco.languages.SymbolKind.Property; -outlineTypeTable[Kind.memberSetAccessor] = monaco.languages.SymbolKind.Property; -outlineTypeTable[Kind.variable] = monaco.languages.SymbolKind.Variable; -outlineTypeTable[Kind.const] = monaco.languages.SymbolKind.Variable; -outlineTypeTable[Kind.localVariable] = monaco.languages.SymbolKind.Variable; -outlineTypeTable[Kind.variable] = monaco.languages.SymbolKind.Variable; -outlineTypeTable[Kind.function] = monaco.languages.SymbolKind.Function; -outlineTypeTable[Kind.localFunction] = monaco.languages.SymbolKind.Function; - -// --- formatting ---- - -export abstract class FormatHelper extends Adapter { - protected static _convertOptions(options: monaco.languages.FormattingOptions): ts.FormatCodeOptions { - return { - ConvertTabsToSpaces: options.insertSpaces, - TabSize: options.tabSize, - IndentSize: options.tabSize, - IndentStyle: IndentStyle.Smart, - NewLineCharacter: '\n', - InsertSpaceAfterCommaDelimiter: true, - InsertSpaceAfterSemicolonInForStatements: true, - InsertSpaceBeforeAndAfterBinaryOperators: true, - InsertSpaceAfterKeywordsInControlFlowStatements: true, - InsertSpaceAfterFunctionKeywordForAnonymousFunctions: true, - InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, - InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, - InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, - PlaceOpenBraceOnNewLineForControlBlocks: false, - PlaceOpenBraceOnNewLineForFunctions: false - }; - } - - protected _convertTextChanges(model: monaco.editor.ITextModel, change: ts.TextChange): monaco.languages.TextEdit { - return { - text: change.newText, - range: this._textSpanToRange(model, change.span) - }; - } -} - -export class FormatAdapter extends FormatHelper implements monaco.languages.DocumentRangeFormattingEditProvider { - - public async provideDocumentRangeFormattingEdits(model: monaco.editor.ITextModel, range: Range, options: monaco.languages.FormattingOptions, token: CancellationToken): Promise { - const resource = model.uri; - const startOffset = model.getOffsetAt({ lineNumber: range.startLineNumber, column: range.startColumn }); - const endOffset = model.getOffsetAt({ lineNumber: range.endLineNumber, column: range.endColumn }); - const worker = await this._worker(resource); - const edits = await worker.getFormattingEditsForRange(resource.toString(), startOffset, endOffset, FormatHelper._convertOptions(options)); - - if (!edits || model.isDisposed()) { - return; - } - - return edits.map(edit => this._convertTextChanges(model, edit)); - } -} - -export class FormatOnTypeAdapter extends FormatHelper implements monaco.languages.OnTypeFormattingEditProvider { - - get autoFormatTriggerCharacters() { - return [';', '}', '\n']; - } - - public async provideOnTypeFormattingEdits(model: monaco.editor.ITextModel, position: Position, ch: string, options: monaco.languages.FormattingOptions, token: CancellationToken): Promise { - const resource = model.uri; - const offset = model.getOffsetAt(position); - const worker = await this._worker(resource); - const edits = await worker.getFormattingEditsAfterKeystroke(resource.toString(), offset, ch, FormatHelper._convertOptions(options)); - - if (!edits || model.isDisposed()) { - return; - } - - return edits.map(edit => this._convertTextChanges(model, edit)); - } -} - -// --- code actions ------ - -export class CodeActionAdaptor extends FormatHelper implements monaco.languages.CodeActionProvider { - - public async provideCodeActions(model: monaco.editor.ITextModel, range: Range, context: monaco.languages.CodeActionContext, token: CancellationToken): Promise { - const resource = model.uri; - const start = model.getOffsetAt({ lineNumber: range.startLineNumber, column: range.startColumn }); - const end = model.getOffsetAt({ lineNumber: range.endLineNumber, column: range.endColumn }); - const formatOptions = FormatHelper._convertOptions(model.getOptions()); - const errorCodes = context.markers.filter(m => m.code).map(m => m.code).map(Number); - const worker = await this._worker(resource); - const codeFixes = await worker.getCodeFixesAtPosition(resource.toString(), start, end, errorCodes, formatOptions); - - if (!codeFixes || model.isDisposed()) { - return { actions: [], dispose: () => { } }; - } - - const actions = codeFixes.filter(fix => { - // Removes any 'make a new file'-type code fix - return fix.changes.filter(change => change.isNewFile).length === 0; - }).map(fix => { - return this._tsCodeFixActionToMonacoCodeAction(model, context, fix); - }); - - return { - actions: actions, - dispose: () => { } - }; - } - - - private _tsCodeFixActionToMonacoCodeAction(model: monaco.editor.ITextModel, context: monaco.languages.CodeActionContext, codeFix: ts.CodeFixAction): monaco.languages.CodeAction { - const edits: monaco.languages.WorkspaceTextEdit[] = []; - for (const change of codeFix.changes) { - for (const textChange of change.textChanges) { - edits.push({ - resource: model.uri, - edit: { - range: this._textSpanToRange(model, textChange.span), - text: textChange.newText - } - }); - } - } - - const action: monaco.languages.CodeAction = { - title: codeFix.description, - edit: { edits: edits }, - diagnostics: context.markers, - kind: "quickfix" - }; - - return action; - } -} -// --- rename ---- - -export class RenameAdapter extends Adapter implements monaco.languages.RenameProvider { - - public async provideRenameEdits(model: monaco.editor.ITextModel, position: Position, newName: string, token: CancellationToken): Promise { - const resource = model.uri; - const fileName = resource.toString(); - const offset = model.getOffsetAt(position); - const worker = await this._worker(resource); - - const renameInfo = await worker.getRenameInfo(fileName, offset, { allowRenameOfImportPath: false }); - if (renameInfo.canRename === false) { // use explicit comparison so that the discriminated union gets resolved properly - return { - edits: [], - rejectReason: renameInfo.localizedErrorMessage - }; - } - if (renameInfo.fileToRename !== undefined) { - throw new Error("Renaming files is not supported."); - } - - const renameLocations = await worker.findRenameLocations(fileName, offset, /*strings*/ false, /*comments*/ false, /*prefixAndSuffix*/ false); - - if (!renameLocations || model.isDisposed()) { - return; - } - - const edits: monaco.languages.WorkspaceTextEdit[] = []; - for (const renameLocation of renameLocations) { - edits.push({ - resource: monaco.Uri.parse(renameLocation.fileName), - edit: { - range: this._textSpanToRange(model, renameLocation.textSpan), - text: newName - } - }); - } - - return { edits }; - } -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { LanguageServiceDefaultsImpl } from './monaco.contribution'; +import * as ts from './lib/typescriptServices'; +import { TypeScriptWorker } from './tsWorker'; +import { libFileSet } from './lib/lib.index'; + +import Uri = monaco.Uri; +import Position = monaco.Position; +import Range = monaco.Range; +import CancellationToken = monaco.CancellationToken; +import IDisposable = monaco.IDisposable; + +//#region utils copied from typescript to prevent loading the entire typescriptServices --- + +enum IndentStyle { + None = 0, + Block = 1, + Smart = 2 +} + +export function flattenDiagnosticMessageText( + diag: string | ts.DiagnosticMessageChain | undefined, + newLine: string, + indent = 0 +): string { + if (typeof diag === 'string') { + return diag; + } else if (diag === undefined) { + return ''; + } + let result = ''; + if (indent) { + result += newLine; + + for (let i = 0; i < indent; i++) { + result += ' '; + } + } + result += diag.messageText; + indent++; + if (diag.next) { + for (const kid of diag.next) { + result += flattenDiagnosticMessageText(kid, newLine, indent); + } + } + return result; +} + +function displayPartsToString( + displayParts: ts.SymbolDisplayPart[] | undefined +): string { + if (displayParts) { + return displayParts.map((displayPart) => displayPart.text).join(''); + } + return ''; +} + +//#endregion + +export abstract class Adapter { + constructor( + protected _worker: (...uris: Uri[]) => Promise + ) {} + + // protected _positionToOffset(model: monaco.editor.ITextModel, position: monaco.IPosition): number { + // return model.getOffsetAt(position); + // } + + // protected _offsetToPosition(model: monaco.editor.ITextModel, offset: number): monaco.IPosition { + // return model.getPositionAt(offset); + // } + + protected _textSpanToRange( + model: monaco.editor.ITextModel, + span: ts.TextSpan + ): monaco.IRange { + let p1 = model.getPositionAt(span.start); + let p2 = model.getPositionAt(span.start + span.length); + let { lineNumber: startLineNumber, column: startColumn } = p1; + let { lineNumber: endLineNumber, column: endColumn } = p2; + return { startLineNumber, startColumn, endLineNumber, endColumn }; + } +} + +// --- lib files + +export class LibFiles { + private _libFiles: Record; + private _hasFetchedLibFiles: boolean; + private _fetchLibFilesPromise: Promise | null; + + constructor( + private readonly _worker: (...uris: Uri[]) => Promise + ) { + this._libFiles = {}; + this._hasFetchedLibFiles = false; + this._fetchLibFilesPromise = null; + } + + public isLibFile(uri: Uri | null): boolean { + if (!uri) { + return false; + } + if (uri.path.indexOf('/lib.') === 0) { + return !!libFileSet[uri.path.slice(1)]; + } + return false; + } + + public getOrCreateModel(uri: Uri): monaco.editor.ITextModel | null { + const model = monaco.editor.getModel(uri); + if (model) { + return model; + } + if (this.isLibFile(uri) && this._hasFetchedLibFiles) { + return monaco.editor.createModel( + this._libFiles[uri.path.slice(1)], + 'javascript', + uri + ); + } + return null; + } + + private _containsLibFile(uris: (Uri | null)[]): boolean { + for (let uri of uris) { + if (this.isLibFile(uri)) { + return true; + } + } + return false; + } + + public async fetchLibFilesIfNecessary(uris: (Uri | null)[]): Promise { + if (!this._containsLibFile(uris)) { + // no lib files necessary + return; + } + await this._fetchLibFiles(); + } + + private _fetchLibFiles(): Promise { + if (!this._fetchLibFilesPromise) { + this._fetchLibFilesPromise = this._worker() + .then((w) => w.getLibFiles()) + .then((libFiles) => { + this._hasFetchedLibFiles = true; + this._libFiles = libFiles; + }); + } + return this._fetchLibFilesPromise; + } +} + +// --- diagnostics --- --- + +enum DiagnosticCategory { + Warning = 0, + Error = 1, + Suggestion = 2, + Message = 3 +} + +export class DiagnosticsAdapter extends Adapter { + private _disposables: IDisposable[] = []; + private _listener: { [uri: string]: IDisposable } = Object.create(null); + + constructor( + private readonly _libFiles: LibFiles, + private _defaults: LanguageServiceDefaultsImpl, + private _selector: string, + worker: (...uris: Uri[]) => Promise + ) { + super(worker); + + const onModelAdd = (model: monaco.editor.IModel): void => { + if (model.getModeId() !== _selector) { + return; + } + + let handle: number; + const changeSubscription = model.onDidChangeContent(() => { + clearTimeout(handle); + handle = setTimeout(() => this._doValidate(model), 500); + }); + + this._listener[model.uri.toString()] = { + dispose() { + changeSubscription.dispose(); + clearTimeout(handle); + } + }; + + this._doValidate(model); + }; + + const onModelRemoved = (model: monaco.editor.IModel): void => { + monaco.editor.setModelMarkers(model, this._selector, []); + const key = model.uri.toString(); + if (this._listener[key]) { + this._listener[key].dispose(); + delete this._listener[key]; + } + }; + + this._disposables.push(monaco.editor.onDidCreateModel(onModelAdd)); + this._disposables.push(monaco.editor.onWillDisposeModel(onModelRemoved)); + this._disposables.push( + monaco.editor.onDidChangeModelLanguage((event) => { + onModelRemoved(event.model); + onModelAdd(event.model); + }) + ); + + this._disposables.push({ + dispose() { + for (const model of monaco.editor.getModels()) { + onModelRemoved(model); + } + } + }); + + const recomputeDiagostics = () => { + // redo diagnostics when options change + for (const model of monaco.editor.getModels()) { + onModelRemoved(model); + onModelAdd(model); + } + }; + this._disposables.push(this._defaults.onDidChange(recomputeDiagostics)); + this._disposables.push( + this._defaults.onDidExtraLibsChange(recomputeDiagostics) + ); + + monaco.editor.getModels().forEach(onModelAdd); + } + + public dispose(): void { + this._disposables.forEach((d) => d && d.dispose()); + this._disposables = []; + } + + private async _doValidate(model: monaco.editor.ITextModel): Promise { + const worker = await this._worker(model.uri); + + if (model.isDisposed()) { + // model was disposed in the meantime + return; + } + + const promises: Promise[] = []; + const { + noSyntaxValidation, + noSemanticValidation, + noSuggestionDiagnostics + } = this._defaults.getDiagnosticsOptions(); + if (!noSyntaxValidation) { + promises.push(worker.getSyntacticDiagnostics(model.uri.toString())); + } + if (!noSemanticValidation) { + promises.push(worker.getSemanticDiagnostics(model.uri.toString())); + } + if (!noSuggestionDiagnostics) { + promises.push(worker.getSuggestionDiagnostics(model.uri.toString())); + } + + const allDiagnostics = await Promise.all(promises); + + if (!allDiagnostics || model.isDisposed()) { + // model was disposed in the meantime + return; + } + + const diagnostics = allDiagnostics + .reduce((p, c) => c.concat(p), []) + .filter( + (d) => + ( + this._defaults.getDiagnosticsOptions().diagnosticCodesToIgnore || [] + ).indexOf(d.code) === -1 + ); + + // Fetch lib files if necessary + const relatedUris = diagnostics + .map((d) => d.relatedInformation || []) + .reduce((p, c) => c.concat(p), []) + .map((relatedInformation) => + relatedInformation.file + ? monaco.Uri.parse(relatedInformation.file.fileName) + : null + ); + + await this._libFiles.fetchLibFilesIfNecessary(relatedUris); + + if (model.isDisposed()) { + // model was disposed in the meantime + return; + } + + monaco.editor.setModelMarkers( + model, + this._selector, + diagnostics.map((d) => this._convertDiagnostics(model, d)) + ); + } + + private _convertDiagnostics( + model: monaco.editor.ITextModel, + diag: ts.Diagnostic + ): monaco.editor.IMarkerData { + const diagStart = diag.start || 0; + const diagLength = diag.length || 1; + const { + lineNumber: startLineNumber, + column: startColumn + } = model.getPositionAt(diagStart); + const { + lineNumber: endLineNumber, + column: endColumn + } = model.getPositionAt(diagStart + diagLength); + + return { + severity: this._tsDiagnosticCategoryToMarkerSeverity(diag.category), + startLineNumber, + startColumn, + endLineNumber, + endColumn, + message: flattenDiagnosticMessageText(diag.messageText, '\n'), + code: diag.code.toString(), + tags: diag.reportsUnnecessary ? [monaco.MarkerTag.Unnecessary] : [], + relatedInformation: this._convertRelatedInformation( + model, + diag.relatedInformation + ) + }; + } + + private _convertRelatedInformation( + model: monaco.editor.ITextModel, + relatedInformation?: ts.DiagnosticRelatedInformation[] + ): monaco.editor.IRelatedInformation[] | undefined { + if (!relatedInformation) { + return; + } + + const result: monaco.editor.IRelatedInformation[] = []; + relatedInformation.forEach((info) => { + let relatedResource: monaco.editor.ITextModel | null = model; + if (info.file) { + const relatedResourceUri = monaco.Uri.parse(info.file.fileName); + relatedResource = this._libFiles.getOrCreateModel(relatedResourceUri); + } + + if (!relatedResource) { + return; + } + const infoStart = info.start || 0; + const infoLength = info.length || 1; + const { + lineNumber: startLineNumber, + column: startColumn + } = relatedResource.getPositionAt(infoStart); + const { + lineNumber: endLineNumber, + column: endColumn + } = relatedResource.getPositionAt(infoStart + infoLength); + + result.push({ + resource: relatedResource.uri, + startLineNumber, + startColumn, + endLineNumber, + endColumn, + message: flattenDiagnosticMessageText(info.messageText, '\n') + }); + }); + return result; + } + + private _tsDiagnosticCategoryToMarkerSeverity( + category: ts.DiagnosticCategory + ): monaco.MarkerSeverity { + switch (category) { + case DiagnosticCategory.Error: + return monaco.MarkerSeverity.Error; + case DiagnosticCategory.Message: + return monaco.MarkerSeverity.Info; + case DiagnosticCategory.Warning: + return monaco.MarkerSeverity.Warning; + case DiagnosticCategory.Suggestion: + return monaco.MarkerSeverity.Hint; + } + return monaco.MarkerSeverity.Info; + } +} + +// --- suggest ------ + +interface MyCompletionItem extends monaco.languages.CompletionItem { + label: string; + uri: Uri; + position: Position; +} + +export class SuggestAdapter + extends Adapter + implements monaco.languages.CompletionItemProvider { + public get triggerCharacters(): string[] { + return ['.']; + } + + public async provideCompletionItems( + model: monaco.editor.ITextModel, + position: Position, + _context: monaco.languages.CompletionContext, + token: CancellationToken + ): Promise { + const wordInfo = model.getWordUntilPosition(position); + const wordRange = new Range( + position.lineNumber, + wordInfo.startColumn, + position.lineNumber, + wordInfo.endColumn + ); + const resource = model.uri; + const offset = model.getOffsetAt(position); + + const worker = await this._worker(resource); + const info = await worker.getCompletionsAtPosition( + resource.toString(), + offset + ); + + if (!info || model.isDisposed()) { + return; + } + + const suggestions: MyCompletionItem[] = info.entries.map((entry) => { + let range = wordRange; + if (entry.replacementSpan) { + const p1 = model.getPositionAt(entry.replacementSpan.start); + const p2 = model.getPositionAt( + entry.replacementSpan.start + entry.replacementSpan.length + ); + range = new Range(p1.lineNumber, p1.column, p2.lineNumber, p2.column); + } + + return { + uri: resource, + position: position, + range: range, + label: entry.name, + insertText: entry.name, + sortText: entry.sortText, + kind: SuggestAdapter.convertKind(entry.kind) + }; + }); + + return { + suggestions + }; + } + + public async resolveCompletionItem( + model: monaco.editor.ITextModel, + _position: Position, + item: monaco.languages.CompletionItem, + token: CancellationToken + ): Promise { + const myItem = item; + const resource = myItem.uri; + const position = myItem.position; + const offset = model.getOffsetAt(position); + + const worker = await this._worker(resource); + const details = await worker.getCompletionEntryDetails( + resource.toString(), + offset, + myItem.label + ); + if (!details || model.isDisposed()) { + return myItem; + } + return { + uri: resource, + position: position, + label: details.name, + kind: SuggestAdapter.convertKind(details.kind), + detail: displayPartsToString(details.displayParts), + documentation: { + value: displayPartsToString(details.documentation) + } + }; + } + + private static convertKind( + kind: string + ): monaco.languages.CompletionItemKind { + switch (kind) { + case Kind.primitiveType: + case Kind.keyword: + return monaco.languages.CompletionItemKind.Keyword; + case Kind.variable: + case Kind.localVariable: + return monaco.languages.CompletionItemKind.Variable; + case Kind.memberVariable: + case Kind.memberGetAccessor: + case Kind.memberSetAccessor: + return monaco.languages.CompletionItemKind.Field; + case Kind.function: + case Kind.memberFunction: + case Kind.constructSignature: + case Kind.callSignature: + case Kind.indexSignature: + return monaco.languages.CompletionItemKind.Function; + case Kind.enum: + return monaco.languages.CompletionItemKind.Enum; + case Kind.module: + return monaco.languages.CompletionItemKind.Module; + case Kind.class: + return monaco.languages.CompletionItemKind.Class; + case Kind.interface: + return monaco.languages.CompletionItemKind.Interface; + case Kind.warning: + return monaco.languages.CompletionItemKind.File; + } + + return monaco.languages.CompletionItemKind.Property; + } +} + +export class SignatureHelpAdapter + extends Adapter + implements monaco.languages.SignatureHelpProvider { + public signatureHelpTriggerCharacters = ['(', ',']; + + public async provideSignatureHelp( + model: monaco.editor.ITextModel, + position: Position, + token: CancellationToken + ): Promise { + const resource = model.uri; + const offset = model.getOffsetAt(position); + const worker = await this._worker(resource); + const info = await worker.getSignatureHelpItems( + resource.toString(), + offset + ); + + if (!info || model.isDisposed()) { + return; + } + + const ret: monaco.languages.SignatureHelp = { + activeSignature: info.selectedItemIndex, + activeParameter: info.argumentIndex, + signatures: [] + }; + + info.items.forEach((item) => { + const signature: monaco.languages.SignatureInformation = { + label: '', + parameters: [] + }; + + signature.documentation = { + value: displayPartsToString(item.documentation) + }; + signature.label += displayPartsToString(item.prefixDisplayParts); + item.parameters.forEach((p, i, a) => { + const label = displayPartsToString(p.displayParts); + const parameter: monaco.languages.ParameterInformation = { + label: label, + documentation: { + value: displayPartsToString(p.documentation) + } + }; + signature.label += label; + signature.parameters.push(parameter); + if (i < a.length - 1) { + signature.label += displayPartsToString(item.separatorDisplayParts); + } + }); + signature.label += displayPartsToString(item.suffixDisplayParts); + ret.signatures.push(signature); + }); + + return { + value: ret, + dispose() {} + }; + } +} + +// --- hover ------ + +export class QuickInfoAdapter + extends Adapter + implements monaco.languages.HoverProvider { + public async provideHover( + model: monaco.editor.ITextModel, + position: Position, + token: CancellationToken + ): Promise { + const resource = model.uri; + const offset = model.getOffsetAt(position); + const worker = await this._worker(resource); + const info = await worker.getQuickInfoAtPosition( + resource.toString(), + offset + ); + + if (!info || model.isDisposed()) { + return; + } + + const documentation = displayPartsToString(info.documentation); + const tags = info.tags + ? info.tags + .map((tag) => { + const label = `*@${tag.name}*`; + if (!tag.text) { + return label; + } + return ( + label + + (tag.text.match(/\r\n|\n/g) ? ' \n' + tag.text : ` - ${tag.text}`) + ); + }) + .join(' \n\n') + : ''; + const contents = displayPartsToString(info.displayParts); + return { + range: this._textSpanToRange(model, info.textSpan), + contents: [ + { + value: '```js\n' + contents + '\n```\n' + }, + { + value: documentation + (tags ? '\n\n' + tags : '') + } + ] + }; + } +} + +// --- occurrences ------ + +export class OccurrencesAdapter + extends Adapter + implements monaco.languages.DocumentHighlightProvider { + public async provideDocumentHighlights( + model: monaco.editor.ITextModel, + position: Position, + token: CancellationToken + ): Promise { + const resource = model.uri; + const offset = model.getOffsetAt(position); + const worker = await this._worker(resource); + const entries = await worker.getOccurrencesAtPosition( + resource.toString(), + offset + ); + + if (!entries || model.isDisposed()) { + return; + } + + return entries.map((entry) => { + return { + range: this._textSpanToRange(model, entry.textSpan), + kind: entry.isWriteAccess + ? monaco.languages.DocumentHighlightKind.Write + : monaco.languages.DocumentHighlightKind.Text + }; + }); + } +} + +// --- definition ------ + +export class DefinitionAdapter extends Adapter { + constructor( + private readonly _libFiles: LibFiles, + worker: (...uris: Uri[]) => Promise + ) { + super(worker); + } + + public async provideDefinition( + model: monaco.editor.ITextModel, + position: Position, + token: CancellationToken + ): Promise { + const resource = model.uri; + const offset = model.getOffsetAt(position); + const worker = await this._worker(resource); + const entries = await worker.getDefinitionAtPosition( + resource.toString(), + offset + ); + + if (!entries || model.isDisposed()) { + return; + } + + // Fetch lib files if necessary + await this._libFiles.fetchLibFilesIfNecessary( + entries.map((entry) => Uri.parse(entry.fileName)) + ); + + if (model.isDisposed()) { + return; + } + + const result: monaco.languages.Location[] = []; + for (let entry of entries) { + const uri = Uri.parse(entry.fileName); + const refModel = this._libFiles.getOrCreateModel(uri); + if (refModel) { + result.push({ + uri: uri, + range: this._textSpanToRange(refModel, entry.textSpan) + }); + } + } + return result; + } +} + +// --- references ------ + +export class ReferenceAdapter + extends Adapter + implements monaco.languages.ReferenceProvider { + constructor( + private readonly _libFiles: LibFiles, + worker: (...uris: Uri[]) => Promise + ) { + super(worker); + } + + public async provideReferences( + model: monaco.editor.ITextModel, + position: Position, + context: monaco.languages.ReferenceContext, + token: CancellationToken + ): Promise { + const resource = model.uri; + const offset = model.getOffsetAt(position); + const worker = await this._worker(resource); + const entries = await worker.getReferencesAtPosition( + resource.toString(), + offset + ); + + if (!entries || model.isDisposed()) { + return; + } + + // Fetch lib files if necessary + await this._libFiles.fetchLibFilesIfNecessary( + entries.map((entry) => Uri.parse(entry.fileName)) + ); + + if (model.isDisposed()) { + return; + } + + const result: monaco.languages.Location[] = []; + for (let entry of entries) { + const uri = Uri.parse(entry.fileName); + const refModel = this._libFiles.getOrCreateModel(uri); + if (refModel) { + result.push({ + uri: uri, + range: this._textSpanToRange(refModel, entry.textSpan) + }); + } + } + return result; + } +} + +// --- outline ------ + +export class OutlineAdapter + extends Adapter + implements monaco.languages.DocumentSymbolProvider { + public async provideDocumentSymbols( + model: monaco.editor.ITextModel, + token: CancellationToken + ): Promise { + const resource = model.uri; + const worker = await this._worker(resource); + const items = await worker.getNavigationBarItems(resource.toString()); + + if (!items || model.isDisposed()) { + return; + } + + const convert = ( + bucket: monaco.languages.DocumentSymbol[], + item: ts.NavigationBarItem, + containerLabel?: string + ): void => { + let result: monaco.languages.DocumentSymbol = { + name: item.text, + detail: '', + kind: ( + (outlineTypeTable[item.kind] || monaco.languages.SymbolKind.Variable) + ), + range: this._textSpanToRange(model, item.spans[0]), + selectionRange: this._textSpanToRange(model, item.spans[0]), + tags: [], + containerName: containerLabel + }; + + if (item.childItems && item.childItems.length > 0) { + for (let child of item.childItems) { + convert(bucket, child, result.name); + } + } + + bucket.push(result); + }; + + let result: monaco.languages.DocumentSymbol[] = []; + items.forEach((item) => convert(result, item)); + return result; + } +} + +export class Kind { + public static unknown: string = ''; + public static keyword: string = 'keyword'; + public static script: string = 'script'; + public static module: string = 'module'; + public static class: string = 'class'; + public static interface: string = 'interface'; + public static type: string = 'type'; + public static enum: string = 'enum'; + public static variable: string = 'var'; + public static localVariable: string = 'local var'; + public static function: string = 'function'; + public static localFunction: string = 'local function'; + public static memberFunction: string = 'method'; + public static memberGetAccessor: string = 'getter'; + public static memberSetAccessor: string = 'setter'; + public static memberVariable: string = 'property'; + public static constructorImplementation: string = 'constructor'; + public static callSignature: string = 'call'; + public static indexSignature: string = 'index'; + public static constructSignature: string = 'construct'; + public static parameter: string = 'parameter'; + public static typeParameter: string = 'type parameter'; + public static primitiveType: string = 'primitive type'; + public static label: string = 'label'; + public static alias: string = 'alias'; + public static const: string = 'const'; + public static let: string = 'let'; + public static warning: string = 'warning'; +} + +let outlineTypeTable: { + [kind: string]: monaco.languages.SymbolKind; +} = Object.create(null); +outlineTypeTable[Kind.module] = monaco.languages.SymbolKind.Module; +outlineTypeTable[Kind.class] = monaco.languages.SymbolKind.Class; +outlineTypeTable[Kind.enum] = monaco.languages.SymbolKind.Enum; +outlineTypeTable[Kind.interface] = monaco.languages.SymbolKind.Interface; +outlineTypeTable[Kind.memberFunction] = monaco.languages.SymbolKind.Method; +outlineTypeTable[Kind.memberVariable] = monaco.languages.SymbolKind.Property; +outlineTypeTable[Kind.memberGetAccessor] = monaco.languages.SymbolKind.Property; +outlineTypeTable[Kind.memberSetAccessor] = monaco.languages.SymbolKind.Property; +outlineTypeTable[Kind.variable] = monaco.languages.SymbolKind.Variable; +outlineTypeTable[Kind.const] = monaco.languages.SymbolKind.Variable; +outlineTypeTable[Kind.localVariable] = monaco.languages.SymbolKind.Variable; +outlineTypeTable[Kind.variable] = monaco.languages.SymbolKind.Variable; +outlineTypeTable[Kind.function] = monaco.languages.SymbolKind.Function; +outlineTypeTable[Kind.localFunction] = monaco.languages.SymbolKind.Function; + +// --- formatting ---- + +export abstract class FormatHelper extends Adapter { + protected static _convertOptions( + options: monaco.languages.FormattingOptions + ): ts.FormatCodeOptions { + return { + ConvertTabsToSpaces: options.insertSpaces, + TabSize: options.tabSize, + IndentSize: options.tabSize, + IndentStyle: IndentStyle.Smart, + NewLineCharacter: '\n', + InsertSpaceAfterCommaDelimiter: true, + InsertSpaceAfterSemicolonInForStatements: true, + InsertSpaceBeforeAndAfterBinaryOperators: true, + InsertSpaceAfterKeywordsInControlFlowStatements: true, + InsertSpaceAfterFunctionKeywordForAnonymousFunctions: true, + InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, + InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, + InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, + PlaceOpenBraceOnNewLineForControlBlocks: false, + PlaceOpenBraceOnNewLineForFunctions: false + }; + } + + protected _convertTextChanges( + model: monaco.editor.ITextModel, + change: ts.TextChange + ): monaco.languages.TextEdit { + return { + text: change.newText, + range: this._textSpanToRange(model, change.span) + }; + } +} + +export class FormatAdapter + extends FormatHelper + implements monaco.languages.DocumentRangeFormattingEditProvider { + public async provideDocumentRangeFormattingEdits( + model: monaco.editor.ITextModel, + range: Range, + options: monaco.languages.FormattingOptions, + token: CancellationToken + ): Promise { + const resource = model.uri; + const startOffset = model.getOffsetAt({ + lineNumber: range.startLineNumber, + column: range.startColumn + }); + const endOffset = model.getOffsetAt({ + lineNumber: range.endLineNumber, + column: range.endColumn + }); + const worker = await this._worker(resource); + const edits = await worker.getFormattingEditsForRange( + resource.toString(), + startOffset, + endOffset, + FormatHelper._convertOptions(options) + ); + + if (!edits || model.isDisposed()) { + return; + } + + return edits.map((edit) => this._convertTextChanges(model, edit)); + } +} + +export class FormatOnTypeAdapter + extends FormatHelper + implements monaco.languages.OnTypeFormattingEditProvider { + get autoFormatTriggerCharacters() { + return [';', '}', '\n']; + } + + public async provideOnTypeFormattingEdits( + model: monaco.editor.ITextModel, + position: Position, + ch: string, + options: monaco.languages.FormattingOptions, + token: CancellationToken + ): Promise { + const resource = model.uri; + const offset = model.getOffsetAt(position); + const worker = await this._worker(resource); + const edits = await worker.getFormattingEditsAfterKeystroke( + resource.toString(), + offset, + ch, + FormatHelper._convertOptions(options) + ); + + if (!edits || model.isDisposed()) { + return; + } + + return edits.map((edit) => this._convertTextChanges(model, edit)); + } +} + +// --- code actions ------ + +export class CodeActionAdaptor + extends FormatHelper + implements monaco.languages.CodeActionProvider { + public async provideCodeActions( + model: monaco.editor.ITextModel, + range: Range, + context: monaco.languages.CodeActionContext, + token: CancellationToken + ): Promise { + const resource = model.uri; + const start = model.getOffsetAt({ + lineNumber: range.startLineNumber, + column: range.startColumn + }); + const end = model.getOffsetAt({ + lineNumber: range.endLineNumber, + column: range.endColumn + }); + const formatOptions = FormatHelper._convertOptions(model.getOptions()); + const errorCodes = context.markers + .filter((m) => m.code) + .map((m) => m.code) + .map(Number); + const worker = await this._worker(resource); + const codeFixes = await worker.getCodeFixesAtPosition( + resource.toString(), + start, + end, + errorCodes, + formatOptions + ); + + if (!codeFixes || model.isDisposed()) { + return { actions: [], dispose: () => {} }; + } + + const actions = codeFixes + .filter((fix) => { + // Removes any 'make a new file'-type code fix + return fix.changes.filter((change) => change.isNewFile).length === 0; + }) + .map((fix) => { + return this._tsCodeFixActionToMonacoCodeAction(model, context, fix); + }); + + return { + actions: actions, + dispose: () => {} + }; + } + + private _tsCodeFixActionToMonacoCodeAction( + model: monaco.editor.ITextModel, + context: monaco.languages.CodeActionContext, + codeFix: ts.CodeFixAction + ): monaco.languages.CodeAction { + const edits: monaco.languages.WorkspaceTextEdit[] = []; + for (const change of codeFix.changes) { + for (const textChange of change.textChanges) { + edits.push({ + resource: model.uri, + edit: { + range: this._textSpanToRange(model, textChange.span), + text: textChange.newText + } + }); + } + } + + const action: monaco.languages.CodeAction = { + title: codeFix.description, + edit: { edits: edits }, + diagnostics: context.markers, + kind: 'quickfix' + }; + + return action; + } +} +// --- rename ---- + +export class RenameAdapter + extends Adapter + implements monaco.languages.RenameProvider { + public async provideRenameEdits( + model: monaco.editor.ITextModel, + position: Position, + newName: string, + token: CancellationToken + ): Promise< + (monaco.languages.WorkspaceEdit & monaco.languages.Rejection) | undefined + > { + const resource = model.uri; + const fileName = resource.toString(); + const offset = model.getOffsetAt(position); + const worker = await this._worker(resource); + + const renameInfo = await worker.getRenameInfo(fileName, offset, { + allowRenameOfImportPath: false + }); + if (renameInfo.canRename === false) { + // use explicit comparison so that the discriminated union gets resolved properly + return { + edits: [], + rejectReason: renameInfo.localizedErrorMessage + }; + } + if (renameInfo.fileToRename !== undefined) { + throw new Error('Renaming files is not supported.'); + } + + const renameLocations = await worker.findRenameLocations( + fileName, + offset, + /*strings*/ false, + /*comments*/ false, + /*prefixAndSuffix*/ false + ); + + if (!renameLocations || model.isDisposed()) { + return; + } + + const edits: monaco.languages.WorkspaceTextEdit[] = []; + for (const renameLocation of renameLocations) { + edits.push({ + resource: monaco.Uri.parse(renameLocation.fileName), + edit: { + range: this._textSpanToRange(model, renameLocation.textSpan), + text: newName + } + }); + } + + return { edits }; + } +} diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index a26b7e2..c1a7240 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -1,260 +1,282 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as mode from './tsMode'; -import { typescriptVersion } from './lib/typescriptServicesMetadata'; // do not import the whole typescriptServices here - -import Emitter = monaco.Emitter; -import IEvent = monaco.IEvent; -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 _extraLibs: IExtraLibs; - private _eagerModelSync: boolean; - private _compilerOptions!: monaco.languages.typescript.CompilerOptions; - private _diagnosticsOptions!: monaco.languages.typescript.DiagnosticsOptions; - private _workerOptions!: monaco.languages.typescript.WorkerOptions; - private _onDidExtraLibsChangeTimeout: number; - - constructor(compilerOptions: monaco.languages.typescript.CompilerOptions, diagnosticsOptions: monaco.languages.typescript.DiagnosticsOptions, workerOptions: monaco.languages.typescript.WorkerOptions) { - this._extraLibs = Object.create(null); - this._eagerModelSync = false; - this.setCompilerOptions(compilerOptions); - this.setDiagnosticsOptions(diagnosticsOptions); - this.setWorkerOptions(workerOptions) - this._onDidExtraLibsChangeTimeout = -1; - } - - get onDidChange(): IEvent { - return this._onDidChange.event; - } - - get onDidExtraLibsChange(): IEvent { - return this._onDidExtraLibsChange.event; - } - - get workerOptions(): monaco.languages.typescript.WorkerOptions { - return this._workerOptions - } - - getExtraLibs(): IExtraLibs { - return this._extraLibs; - } - - addExtraLib(content: string, _filePath?: string): IDisposable { - let filePath: string; - if (typeof _filePath === 'undefined') { - filePath = `ts:extralib-${Math.random().toString(36).substring(2, 15)}`; - } else { - filePath = _filePath; - } - - 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]) { - myVersion = this._extraLibs[filePath].version + 1; - } - - this._extraLibs[filePath] = { - content: content, - version: myVersion, - }; - this._fireOnDidExtraLibsChangeSoon(); - - return { - dispose: () => { - let extraLib = this._extraLibs[filePath]; - if (!extraLib) { - return; - } - if (extraLib.version !== myVersion) { - return; - } - - delete this._extraLibs[filePath]; - this._fireOnDidExtraLibsChangeSoon(); - } - }; - } - - setExtraLibs(libs: { content: string; filePath?: string }[]): void { - // clear out everything - this._extraLibs = Object.create(null); - - if (libs && libs.length > 0) { - for (const lib of libs) { - const filePath = lib.filePath || `ts:extralib-${Math.random().toString(36).substring(2, 15)}`; - const content = lib.content; - this._extraLibs[filePath] = { - content: content, - version: 1 - }; - } - } - - 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 { - return this._compilerOptions; - } - - setCompilerOptions(options: monaco.languages.typescript.CompilerOptions): void { - this._compilerOptions = options || Object.create(null); - this._onDidChange.fire(undefined); - } - - getDiagnosticsOptions(): monaco.languages.typescript.DiagnosticsOptions { - return this._diagnosticsOptions; - } - - setDiagnosticsOptions(options: monaco.languages.typescript.DiagnosticsOptions): void { - this._diagnosticsOptions = options || Object.create(null); - this._onDidChange.fire(undefined); - } - - setWorkerOptions(options: monaco.languages.typescript.WorkerOptions): void { - this._workerOptions = options || Object.create(null); - this._onDidChange.fire(undefined); - } - - setMaximumWorkerIdleTime(value: number): void { - } - - setEagerModelSync(value: boolean) { - // doesn't fire an event since no - // worker restart is required here - this._eagerModelSync = value; - } - - getEagerModelSync() { - return this._eagerModelSync; - } -} - -//#region enums copied from typescript to prevent loading the entire typescriptServices --- - -enum ModuleKind { - None = 0, - CommonJS = 1, - AMD = 2, - UMD = 3, - System = 4, - ES2015 = 5, - ESNext = 99 -} - -enum JsxEmit { - None = 0, - Preserve = 1, - React = 2, - ReactNative = 3 -} - -enum NewLineKind { - CarriageReturnLineFeed = 0, - LineFeed = 1 -} - -enum ScriptTarget { - ES3 = 0, - ES5 = 1, - ES2015 = 2, - ES2016 = 3, - ES2017 = 4, - ES2018 = 5, - ES2019 = 6, - ES2020 = 7, - ESNext = 99, - JSON = 100, - Latest = ESNext, -} - -enum ModuleResolutionKind { - Classic = 1, - NodeJs = 2 -} -//#endregion - -const typescriptDefaults = new LanguageServiceDefaultsImpl( - { allowNonTsExtensions: true, target: ScriptTarget.Latest }, - { noSemanticValidation: false, noSyntaxValidation: false }, - {}); - -const javascriptDefaults = new LanguageServiceDefaultsImpl( - { allowNonTsExtensions: true, allowJs: true, target: ScriptTarget.Latest }, - { noSemanticValidation: true, noSyntaxValidation: false }, - {}); - -function getTypeScriptWorker(): Promise<(...uris: monaco.Uri[]) => Promise> { - return getMode().then(mode => mode.getTypeScriptWorker()); -} - -function getJavaScriptWorker(): Promise<(...uris: monaco.Uri[]) => Promise> { - return getMode().then(mode => mode.getJavaScriptWorker()); -} - -// Export API -function createAPI(): typeof monaco.languages.typescript { - return { - ModuleKind: ModuleKind, - JsxEmit: JsxEmit, - NewLineKind: NewLineKind, - ScriptTarget: ScriptTarget, - ModuleResolutionKind: ModuleResolutionKind, - typescriptVersion, - typescriptDefaults: typescriptDefaults, - javascriptDefaults: javascriptDefaults, - getTypeScriptWorker: getTypeScriptWorker, - getJavaScriptWorker: getJavaScriptWorker - } -} -monaco.languages.typescript = createAPI(); - -// --- Registration to monaco editor --- - -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)); -}); +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as mode from './tsMode'; +import { typescriptVersion } from './lib/typescriptServicesMetadata'; // do not import the whole typescriptServices here + +import Emitter = monaco.Emitter; +import IEvent = monaco.IEvent; +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 _extraLibs: IExtraLibs; + private _eagerModelSync: boolean; + private _compilerOptions!: monaco.languages.typescript.CompilerOptions; + private _diagnosticsOptions!: monaco.languages.typescript.DiagnosticsOptions; + private _workerOptions!: monaco.languages.typescript.WorkerOptions; + private _onDidExtraLibsChangeTimeout: number; + + constructor( + compilerOptions: monaco.languages.typescript.CompilerOptions, + diagnosticsOptions: monaco.languages.typescript.DiagnosticsOptions, + workerOptions: monaco.languages.typescript.WorkerOptions + ) { + this._extraLibs = Object.create(null); + this._eagerModelSync = false; + this.setCompilerOptions(compilerOptions); + this.setDiagnosticsOptions(diagnosticsOptions); + this.setWorkerOptions(workerOptions); + this._onDidExtraLibsChangeTimeout = -1; + } + + get onDidChange(): IEvent { + return this._onDidChange.event; + } + + get onDidExtraLibsChange(): IEvent { + return this._onDidExtraLibsChange.event; + } + + get workerOptions(): monaco.languages.typescript.WorkerOptions { + return this._workerOptions; + } + + getExtraLibs(): IExtraLibs { + return this._extraLibs; + } + + addExtraLib(content: string, _filePath?: string): IDisposable { + let filePath: string; + if (typeof _filePath === 'undefined') { + filePath = `ts:extralib-${Math.random().toString(36).substring(2, 15)}`; + } else { + filePath = _filePath; + } + + 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]) { + myVersion = this._extraLibs[filePath].version + 1; + } + + this._extraLibs[filePath] = { + content: content, + version: myVersion + }; + this._fireOnDidExtraLibsChangeSoon(); + + return { + dispose: () => { + let extraLib = this._extraLibs[filePath]; + if (!extraLib) { + return; + } + if (extraLib.version !== myVersion) { + return; + } + + delete this._extraLibs[filePath]; + this._fireOnDidExtraLibsChangeSoon(); + } + }; + } + + setExtraLibs(libs: { content: string; filePath?: string }[]): void { + // clear out everything + this._extraLibs = Object.create(null); + + if (libs && libs.length > 0) { + for (const lib of libs) { + const filePath = + lib.filePath || + `ts:extralib-${Math.random().toString(36).substring(2, 15)}`; + const content = lib.content; + this._extraLibs[filePath] = { + content: content, + version: 1 + }; + } + } + + 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 { + return this._compilerOptions; + } + + setCompilerOptions( + options: monaco.languages.typescript.CompilerOptions + ): void { + this._compilerOptions = options || Object.create(null); + this._onDidChange.fire(undefined); + } + + getDiagnosticsOptions(): monaco.languages.typescript.DiagnosticsOptions { + return this._diagnosticsOptions; + } + + setDiagnosticsOptions( + options: monaco.languages.typescript.DiagnosticsOptions + ): void { + this._diagnosticsOptions = options || Object.create(null); + this._onDidChange.fire(undefined); + } + + setWorkerOptions(options: monaco.languages.typescript.WorkerOptions): void { + this._workerOptions = options || Object.create(null); + this._onDidChange.fire(undefined); + } + + setMaximumWorkerIdleTime(value: number): void {} + + setEagerModelSync(value: boolean) { + // doesn't fire an event since no + // worker restart is required here + this._eagerModelSync = value; + } + + getEagerModelSync() { + return this._eagerModelSync; + } +} + +//#region enums copied from typescript to prevent loading the entire typescriptServices --- + +enum ModuleKind { + None = 0, + CommonJS = 1, + AMD = 2, + UMD = 3, + System = 4, + ES2015 = 5, + ESNext = 99 +} + +enum JsxEmit { + None = 0, + Preserve = 1, + React = 2, + ReactNative = 3 +} + +enum NewLineKind { + CarriageReturnLineFeed = 0, + LineFeed = 1 +} + +enum ScriptTarget { + ES3 = 0, + ES5 = 1, + ES2015 = 2, + ES2016 = 3, + ES2017 = 4, + ES2018 = 5, + ES2019 = 6, + ES2020 = 7, + ESNext = 99, + JSON = 100, + Latest = ESNext +} + +enum ModuleResolutionKind { + Classic = 1, + NodeJs = 2 +} +//#endregion + +const typescriptDefaults = new LanguageServiceDefaultsImpl( + { allowNonTsExtensions: true, target: ScriptTarget.Latest }, + { noSemanticValidation: false, noSyntaxValidation: false }, + {} +); + +const javascriptDefaults = new LanguageServiceDefaultsImpl( + { allowNonTsExtensions: true, allowJs: true, target: ScriptTarget.Latest }, + { noSemanticValidation: true, noSyntaxValidation: false }, + {} +); + +function getTypeScriptWorker(): Promise< + ( + ...uris: monaco.Uri[] + ) => Promise +> { + return getMode().then((mode) => mode.getTypeScriptWorker()); +} + +function getJavaScriptWorker(): Promise< + ( + ...uris: monaco.Uri[] + ) => Promise +> { + return getMode().then((mode) => mode.getJavaScriptWorker()); +} + +// Export API +function createAPI(): typeof monaco.languages.typescript { + return { + ModuleKind: ModuleKind, + JsxEmit: JsxEmit, + NewLineKind: NewLineKind, + ScriptTarget: ScriptTarget, + ModuleResolutionKind: ModuleResolutionKind, + typescriptVersion, + typescriptDefaults: typescriptDefaults, + javascriptDefaults: javascriptDefaults, + getTypeScriptWorker: getTypeScriptWorker, + getJavaScriptWorker: getJavaScriptWorker + }; +} +monaco.languages.typescript = createAPI(); + +// --- Registration to monaco editor --- + +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 d63c60d..2be40dc 100644 --- a/src/monaco.d.ts +++ b/src/monaco.d.ts @@ -1,397 +1,458 @@ - -declare module monaco.languages.typescript { - - enum ModuleKind { - None = 0, - CommonJS = 1, - AMD = 2, - UMD = 3, - System = 4, - ES2015 = 5, - ESNext = 99 - } - - enum JsxEmit { - None = 0, - Preserve = 1, - React = 2, - ReactNative = 3 - } - enum NewLineKind { - CarriageReturnLineFeed = 0, - LineFeed = 1 - } - - enum ScriptTarget { - ES3 = 0, - ES5 = 1, - ES2015 = 2, - ES2016 = 3, - ES2017 = 4, - ES2018 = 5, - ES2019 = 6, - ES2020 = 7, - ESNext = 99, - JSON = 100, - Latest = ESNext, - } - - export enum ModuleResolutionKind { - Classic = 1, - NodeJs = 2 - } - - interface MapLike { - [index: string]: T; - } - - type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | null | undefined; - interface CompilerOptions { - allowJs?: boolean; - allowSyntheticDefaultImports?: boolean; - allowUmdGlobalAccess?: boolean; - allowUnreachableCode?: boolean; - allowUnusedLabels?: boolean; - alwaysStrict?: boolean; - baseUrl?: string; - charset?: string; - checkJs?: boolean; - declaration?: boolean; - declarationMap?: boolean; - emitDeclarationOnly?: boolean; - declarationDir?: string; - disableSizeLimit?: boolean; - disableSourceOfProjectReferenceRedirect?: boolean; - downlevelIteration?: boolean; - emitBOM?: boolean; - emitDecoratorMetadata?: boolean; - experimentalDecorators?: boolean; - forceConsistentCasingInFileNames?: boolean; - importHelpers?: boolean; - inlineSourceMap?: boolean; - inlineSources?: boolean; - isolatedModules?: boolean; - jsx?: JsxEmit; - keyofStringsOnly?: boolean; - lib?: string[]; - locale?: string; - mapRoot?: string; - maxNodeModuleJsDepth?: number; - module?: ModuleKind; - moduleResolution?: ModuleResolutionKind; - newLine?: NewLineKind; - noEmit?: boolean; - noEmitHelpers?: boolean; - noEmitOnError?: boolean; - noErrorTruncation?: boolean; - noFallthroughCasesInSwitch?: boolean; - noImplicitAny?: boolean; - noImplicitReturns?: boolean; - noImplicitThis?: boolean; - noStrictGenericChecks?: boolean; - noUnusedLocals?: boolean; - noUnusedParameters?: boolean; - noImplicitUseStrict?: boolean; - noLib?: boolean; - noResolve?: boolean; - out?: string; - outDir?: string; - outFile?: string; - paths?: MapLike; - preserveConstEnums?: boolean; - preserveSymlinks?: boolean; - project?: string; - reactNamespace?: string; - jsxFactory?: string; - composite?: boolean; - removeComments?: boolean; - rootDir?: string; - rootDirs?: string[]; - skipLibCheck?: boolean; - skipDefaultLibCheck?: boolean; - sourceMap?: boolean; - sourceRoot?: string; - strict?: boolean; - strictFunctionTypes?: boolean; - strictBindCallApply?: boolean; - strictNullChecks?: boolean; - strictPropertyInitialization?: boolean; - stripInternal?: boolean; - suppressExcessPropertyErrors?: boolean; - suppressImplicitAnyIndexErrors?: boolean; - target?: ScriptTarget; - traceResolution?: boolean; - resolveJsonModule?: boolean; - types?: string[]; - /** Paths used to compute primary types search locations */ - typeRoots?: string[]; - esModuleInterop?: boolean; - useDefineForClassFields?: boolean; - [option: string]: CompilerOptionsValue | undefined; - } - - export interface DiagnosticsOptions { - noSemanticValidation?: boolean; - noSyntaxValidation?: boolean; - noSuggestionDiagnostics?: boolean; - diagnosticCodesToIgnore?: number[]; - } - - export interface WorkerOptions { - /** A full HTTP path to a JavaScript file which adds a function `customTSWorkerFactory` to the self inside a web-worker */ - customWorkerPath?: string; - } - - interface IExtraLib { - content: string; - version: number; - } - - interface IExtraLibs { - [path: string]: IExtraLib; - } - - /** - * A linked list of formatted diagnostic messages to be used as part of a multiline message. - * It is built from the bottom up, leaving the head to be the "main" diagnostic. - */ - interface DiagnosticMessageChain { - messageText: string; - /** Diagnostic category: warning = 0, error = 1, suggestion = 2, message = 3 */ - category: 0 | 1 | 2 | 3; - code: number; - next?: DiagnosticMessageChain[]; - } - interface Diagnostic extends DiagnosticRelatedInformation { - /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ - reportsUnnecessary?: {}; - source?: string; - relatedInformation?: DiagnosticRelatedInformation[]; - } - interface DiagnosticRelatedInformation { - /** Diagnostic category: warning = 0, error = 1, suggestion = 2, message = 3 */ - category: 0 | 1 | 2 | 3; - code: number; - /** TypeScriptWorker removes this to avoid serializing circular JSON structures. */ - file: undefined; - start: number | undefined; - length: number | undefined; - messageText: string | DiagnosticMessageChain; - } - - interface EmitOutput { - outputFiles: OutputFile[]; - emitSkipped: boolean; - } - interface OutputFile { - name: string; - writeByteOrderMark: boolean; - text: string; - } - - export interface LanguageServiceDefaults { - /** - * Event fired when compiler options or diagnostics options are changed. - */ - readonly onDidChange: IEvent; - - /** - * Event fired when extra libraries registered with the language service change. - */ - readonly onDidExtraLibsChange: IEvent; - - /** - * Get the current extra libs registered with the language service. - */ - getExtraLibs(): IExtraLibs; - - /** - * Add an additional source file to the language service. Use this - * for typescript (definition) files that won't be loaded as editor - * documents, like `jquery.d.ts`. - * - * @param content The file content - * @param filePath An optional file path - * @returns A disposable which will remove the file from the - * language service upon disposal. - */ - addExtraLib(content: string, filePath?: string): IDisposable; - - /** - * Remove all existing extra libs and set the additional source - * files to the language service. Use this for typescript definition - * files that won't be loaded as editor documents, like `jquery.d.ts`. - * @param libs An array of entries to register. - */ - setExtraLibs(libs: { content: string; filePath?: string }[]): void; - - /** - * Get current TypeScript compiler options for the language service. - */ - getCompilerOptions(): CompilerOptions; - - /** - * Set TypeScript compiler options. - */ - setCompilerOptions(options: CompilerOptions): void; - - /** - * Get the current diagnostics options for the language service. - */ - getDiagnosticsOptions(): DiagnosticsOptions; - - /** - * Configure whether syntactic and/or semantic validation should - * be performed - */ - setDiagnosticsOptions(options: DiagnosticsOptions): void; - - /** - * No-op. - */ - setMaximumWorkerIdleTime(value: number): void; - - /** - * Configure if all existing models should be eagerly sync'd - * to the worker on start or restart. - */ - setEagerModelSync(value: boolean): void; - - /** - * Get the current setting for whether all existing models should be eagerly sync'd - * to the worker on start or restart. - */ - getEagerModelSync(): boolean; - } - - export interface TypeScriptWorker { - /** - * Get diagnostic messages for any syntax issues in the given file. - */ - getSyntacticDiagnostics(fileName: string): Promise; - - /** - * Get diagnostic messages for any semantic issues in the given file. - */ - getSemanticDiagnostics(fileName: string): Promise; - - /** - * Get diagnostic messages for any suggestions related to the given file. - */ - getSuggestionDiagnostics(fileName: string): Promise; - - /** - * Get the content of a given file. - */ - getScriptText(fileName: string): Promise; - - /** - * Get diagnostic messages related to the current compiler options. - * @param fileName Not used - */ - getCompilerOptionsDiagnostics(fileName: string): Promise; - - /** - * Get code completions for the given file and position. - * @returns `Promise` - */ - getCompletionsAtPosition(fileName: string, position: number): Promise; - - /** - * Get code completion details for the given file, position, and entry. - * @returns `Promise` - */ - getCompletionEntryDetails(fileName: string, position: number, entry: string): Promise; - - /** - * Get signature help items for the item at the given file and position. - * @returns `Promise` - */ - getSignatureHelpItems(fileName: string, position: number): Promise; - - /** - * Get quick info for the item at the given position in the file. - * @returns `Promise` - */ - getQuickInfoAtPosition(fileName: string, position: number): Promise; - - /** - * Get other ranges which are related to the item at the given position in the file (often used for highlighting). - * @returns `Promise | undefined>` - */ - getOccurrencesAtPosition(fileName: string, position: number): Promise | undefined>; - - /** - * Get the definition of the item at the given position in the file. - * @returns `Promise | undefined>` - */ - getDefinitionAtPosition(fileName: string, position: number): Promise | undefined>; - - /** - * Get references to the item at the given position in the file. - * @returns `Promise` - */ - getReferencesAtPosition(fileName: string, position: number): Promise; - - /** - * Get outline entries for the item at the given position in the file. - * @returns `Promise` - */ - getNavigationBarItems(fileName: string): Promise; - - /** - * Get changes which should be applied to format the given file. - * @param options `typescript.FormatCodeOptions` - * @returns `Promise` - */ - getFormattingEditsForDocument(fileName: string, options: any): Promise; - - /** - * Get changes which should be applied to format the given range in the file. - * @param options `typescript.FormatCodeOptions` - * @returns `Promise` - */ - getFormattingEditsForRange(fileName: string, start: number, end: number, options: any): Promise; - - /** - * Get formatting changes which should be applied after the given keystroke. - * @param options `typescript.FormatCodeOptions` - * @returns `Promise` - */ - getFormattingEditsAfterKeystroke(fileName: string, postion: number, ch: string, options: any): Promise; - - /** - * Get other occurrences which should be updated when renaming the item at the given file and position. - * @returns `Promise` - */ - findRenameLocations(fileName: string, positon: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename: boolean): Promise; - - /** - * Get edits which should be applied to rename the item at the given file and position (or a failure reason). - * @param options `typescript.RenameInfoOptions` - * @returns `Promise` - */ - getRenameInfo(fileName: string, positon: number, options: any): Promise; - - /** - * Get transpiled output for the given file. - * @returns `typescript.EmitOutput` - */ - getEmitOutput(fileName: string): Promise; - - /** - * Get possible code fixes at the given position in the file. - * @param formatOptions `typescript.FormatCodeOptions` - * @returns `Promise>` - */ - getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: any): Promise>; - } - - export const typescriptVersion: string; - - export const typescriptDefaults: LanguageServiceDefaults; - export const javascriptDefaults: LanguageServiceDefaults; - - export const getTypeScriptWorker: () => Promise<(...uris: Uri[]) => Promise>; - export const getJavaScriptWorker: () => Promise<(...uris: Uri[]) => Promise>; -} +declare module monaco.languages.typescript { + enum ModuleKind { + None = 0, + CommonJS = 1, + AMD = 2, + UMD = 3, + System = 4, + ES2015 = 5, + ESNext = 99 + } + + enum JsxEmit { + None = 0, + Preserve = 1, + React = 2, + ReactNative = 3 + } + enum NewLineKind { + CarriageReturnLineFeed = 0, + LineFeed = 1 + } + + enum ScriptTarget { + ES3 = 0, + ES5 = 1, + ES2015 = 2, + ES2016 = 3, + ES2017 = 4, + ES2018 = 5, + ES2019 = 6, + ES2020 = 7, + ESNext = 99, + JSON = 100, + Latest = ESNext + } + + export enum ModuleResolutionKind { + Classic = 1, + NodeJs = 2 + } + + interface MapLike { + [index: string]: T; + } + + type CompilerOptionsValue = + | string + | number + | boolean + | (string | number)[] + | string[] + | MapLike + | null + | undefined; + interface CompilerOptions { + allowJs?: boolean; + allowSyntheticDefaultImports?: boolean; + allowUmdGlobalAccess?: boolean; + allowUnreachableCode?: boolean; + allowUnusedLabels?: boolean; + alwaysStrict?: boolean; + baseUrl?: string; + charset?: string; + checkJs?: boolean; + declaration?: boolean; + declarationMap?: boolean; + emitDeclarationOnly?: boolean; + declarationDir?: string; + disableSizeLimit?: boolean; + disableSourceOfProjectReferenceRedirect?: boolean; + downlevelIteration?: boolean; + emitBOM?: boolean; + emitDecoratorMetadata?: boolean; + experimentalDecorators?: boolean; + forceConsistentCasingInFileNames?: boolean; + importHelpers?: boolean; + inlineSourceMap?: boolean; + inlineSources?: boolean; + isolatedModules?: boolean; + jsx?: JsxEmit; + keyofStringsOnly?: boolean; + lib?: string[]; + locale?: string; + mapRoot?: string; + maxNodeModuleJsDepth?: number; + module?: ModuleKind; + moduleResolution?: ModuleResolutionKind; + newLine?: NewLineKind; + noEmit?: boolean; + noEmitHelpers?: boolean; + noEmitOnError?: boolean; + noErrorTruncation?: boolean; + noFallthroughCasesInSwitch?: boolean; + noImplicitAny?: boolean; + noImplicitReturns?: boolean; + noImplicitThis?: boolean; + noStrictGenericChecks?: boolean; + noUnusedLocals?: boolean; + noUnusedParameters?: boolean; + noImplicitUseStrict?: boolean; + noLib?: boolean; + noResolve?: boolean; + out?: string; + outDir?: string; + outFile?: string; + paths?: MapLike; + preserveConstEnums?: boolean; + preserveSymlinks?: boolean; + project?: string; + reactNamespace?: string; + jsxFactory?: string; + composite?: boolean; + removeComments?: boolean; + rootDir?: string; + rootDirs?: string[]; + skipLibCheck?: boolean; + skipDefaultLibCheck?: boolean; + sourceMap?: boolean; + sourceRoot?: string; + strict?: boolean; + strictFunctionTypes?: boolean; + strictBindCallApply?: boolean; + strictNullChecks?: boolean; + strictPropertyInitialization?: boolean; + stripInternal?: boolean; + suppressExcessPropertyErrors?: boolean; + suppressImplicitAnyIndexErrors?: boolean; + target?: ScriptTarget; + traceResolution?: boolean; + resolveJsonModule?: boolean; + types?: string[]; + /** Paths used to compute primary types search locations */ + typeRoots?: string[]; + esModuleInterop?: boolean; + useDefineForClassFields?: boolean; + [option: string]: CompilerOptionsValue | undefined; + } + + export interface DiagnosticsOptions { + noSemanticValidation?: boolean; + noSyntaxValidation?: boolean; + noSuggestionDiagnostics?: boolean; + diagnosticCodesToIgnore?: number[]; + } + + export interface WorkerOptions { + /** A full HTTP path to a JavaScript file which adds a function `customTSWorkerFactory` to the self inside a web-worker */ + customWorkerPath?: string; + } + + interface IExtraLib { + content: string; + version: number; + } + + interface IExtraLibs { + [path: string]: IExtraLib; + } + + /** + * A linked list of formatted diagnostic messages to be used as part of a multiline message. + * It is built from the bottom up, leaving the head to be the "main" diagnostic. + */ + interface DiagnosticMessageChain { + messageText: string; + /** Diagnostic category: warning = 0, error = 1, suggestion = 2, message = 3 */ + category: 0 | 1 | 2 | 3; + code: number; + next?: DiagnosticMessageChain[]; + } + interface Diagnostic extends DiagnosticRelatedInformation { + /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ + reportsUnnecessary?: {}; + source?: string; + relatedInformation?: DiagnosticRelatedInformation[]; + } + interface DiagnosticRelatedInformation { + /** Diagnostic category: warning = 0, error = 1, suggestion = 2, message = 3 */ + category: 0 | 1 | 2 | 3; + code: number; + /** TypeScriptWorker removes this to avoid serializing circular JSON structures. */ + file: undefined; + start: number | undefined; + length: number | undefined; + messageText: string | DiagnosticMessageChain; + } + + interface EmitOutput { + outputFiles: OutputFile[]; + emitSkipped: boolean; + } + interface OutputFile { + name: string; + writeByteOrderMark: boolean; + text: string; + } + + export interface LanguageServiceDefaults { + /** + * Event fired when compiler options or diagnostics options are changed. + */ + readonly onDidChange: IEvent; + + /** + * Event fired when extra libraries registered with the language service change. + */ + readonly onDidExtraLibsChange: IEvent; + + /** + * Get the current extra libs registered with the language service. + */ + getExtraLibs(): IExtraLibs; + + /** + * Add an additional source file to the language service. Use this + * for typescript (definition) files that won't be loaded as editor + * documents, like `jquery.d.ts`. + * + * @param content The file content + * @param filePath An optional file path + * @returns A disposable which will remove the file from the + * language service upon disposal. + */ + addExtraLib(content: string, filePath?: string): IDisposable; + + /** + * Remove all existing extra libs and set the additional source + * files to the language service. Use this for typescript definition + * files that won't be loaded as editor documents, like `jquery.d.ts`. + * @param libs An array of entries to register. + */ + setExtraLibs(libs: { content: string; filePath?: string }[]): void; + + /** + * Get current TypeScript compiler options for the language service. + */ + getCompilerOptions(): CompilerOptions; + + /** + * Set TypeScript compiler options. + */ + setCompilerOptions(options: CompilerOptions): void; + + /** + * Get the current diagnostics options for the language service. + */ + getDiagnosticsOptions(): DiagnosticsOptions; + + /** + * Configure whether syntactic and/or semantic validation should + * be performed + */ + setDiagnosticsOptions(options: DiagnosticsOptions): void; + + /** + * No-op. + */ + setMaximumWorkerIdleTime(value: number): void; + + /** + * Configure if all existing models should be eagerly sync'd + * to the worker on start or restart. + */ + setEagerModelSync(value: boolean): void; + + /** + * Get the current setting for whether all existing models should be eagerly sync'd + * to the worker on start or restart. + */ + getEagerModelSync(): boolean; + } + + export interface TypeScriptWorker { + /** + * Get diagnostic messages for any syntax issues in the given file. + */ + getSyntacticDiagnostics(fileName: string): Promise; + + /** + * Get diagnostic messages for any semantic issues in the given file. + */ + getSemanticDiagnostics(fileName: string): Promise; + + /** + * Get diagnostic messages for any suggestions related to the given file. + */ + getSuggestionDiagnostics(fileName: string): Promise; + + /** + * Get the content of a given file. + */ + getScriptText(fileName: string): Promise; + + /** + * Get diagnostic messages related to the current compiler options. + * @param fileName Not used + */ + getCompilerOptionsDiagnostics(fileName: string): Promise; + + /** + * Get code completions for the given file and position. + * @returns `Promise` + */ + getCompletionsAtPosition( + fileName: string, + position: number + ): Promise; + + /** + * Get code completion details for the given file, position, and entry. + * @returns `Promise` + */ + getCompletionEntryDetails( + fileName: string, + position: number, + entry: string + ): Promise; + + /** + * Get signature help items for the item at the given file and position. + * @returns `Promise` + */ + getSignatureHelpItems( + fileName: string, + position: number + ): Promise; + + /** + * Get quick info for the item at the given position in the file. + * @returns `Promise` + */ + getQuickInfoAtPosition( + fileName: string, + position: number + ): Promise; + + /** + * Get other ranges which are related to the item at the given position in the file (often used for highlighting). + * @returns `Promise | undefined>` + */ + getOccurrencesAtPosition( + fileName: string, + position: number + ): Promise | undefined>; + + /** + * Get the definition of the item at the given position in the file. + * @returns `Promise | undefined>` + */ + getDefinitionAtPosition( + fileName: string, + position: number + ): Promise | undefined>; + + /** + * Get references to the item at the given position in the file. + * @returns `Promise` + */ + getReferencesAtPosition( + fileName: string, + position: number + ): Promise; + + /** + * Get outline entries for the item at the given position in the file. + * @returns `Promise` + */ + getNavigationBarItems(fileName: string): Promise; + + /** + * Get changes which should be applied to format the given file. + * @param options `typescript.FormatCodeOptions` + * @returns `Promise` + */ + getFormattingEditsForDocument( + fileName: string, + options: any + ): Promise; + + /** + * Get changes which should be applied to format the given range in the file. + * @param options `typescript.FormatCodeOptions` + * @returns `Promise` + */ + getFormattingEditsForRange( + fileName: string, + start: number, + end: number, + options: any + ): Promise; + + /** + * Get formatting changes which should be applied after the given keystroke. + * @param options `typescript.FormatCodeOptions` + * @returns `Promise` + */ + getFormattingEditsAfterKeystroke( + fileName: string, + postion: number, + ch: string, + options: any + ): Promise; + + /** + * Get other occurrences which should be updated when renaming the item at the given file and position. + * @returns `Promise` + */ + findRenameLocations( + fileName: string, + positon: number, + findInStrings: boolean, + findInComments: boolean, + providePrefixAndSuffixTextForRename: boolean + ): Promise; + + /** + * Get edits which should be applied to rename the item at the given file and position (or a failure reason). + * @param options `typescript.RenameInfoOptions` + * @returns `Promise` + */ + getRenameInfo( + fileName: string, + positon: number, + options: any + ): Promise; + + /** + * Get transpiled output for the given file. + * @returns `typescript.EmitOutput` + */ + getEmitOutput(fileName: string): Promise; + + /** + * Get possible code fixes at the given position in the file. + * @param formatOptions `typescript.FormatCodeOptions` + * @returns `Promise>` + */ + getCodeFixesAtPosition( + fileName: string, + start: number, + end: number, + errorCodes: number[], + formatOptions: any + ): Promise>; + } + + export const typescriptVersion: string; + + export const typescriptDefaults: LanguageServiceDefaults; + export const javascriptDefaults: LanguageServiceDefaults; + + export const getTypeScriptWorker: () => Promise< + (...uris: Uri[]) => Promise + >; + export const getJavaScriptWorker: () => Promise< + (...uris: Uri[]) => Promise + >; +} diff --git a/src/ts.worker.ts b/src/ts.worker.ts index 880b083..ed202ec 100644 --- a/src/ts.worker.ts +++ b/src/ts.worker.ts @@ -9,7 +9,9 @@ import { TypeScriptWorker, ICreateData } from './tsWorker'; self.onmessage = () => { // ignore the first message - worker.initialize((ctx: monaco.worker.IWorkerContext, createData: ICreateData) => { - return new TypeScriptWorker(ctx, createData) - }); + worker.initialize( + (ctx: monaco.worker.IWorkerContext, createData: ICreateData) => { + return new TypeScriptWorker(ctx, createData); + } + ); }; diff --git a/src/tsMode.ts b/src/tsMode.ts index aef2a6e..6ea613a 100644 --- a/src/tsMode.ts +++ b/src/tsMode.ts @@ -1,74 +1,107 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import { WorkerManager } from './workerManager'; -import { TypeScriptWorker } from './tsWorker'; -import { LanguageServiceDefaultsImpl } from './monaco.contribution'; -import * as languageFeatures from './languageFeatures'; - -import Uri = monaco.Uri; - -let javaScriptWorker: (...uris: Uri[]) => Promise; -let typeScriptWorker: (...uris: Uri[]) => Promise; - -export function setupTypeScript(defaults: LanguageServiceDefaultsImpl): void { - typeScriptWorker = setupMode( - defaults, - 'typescript' - ); -} - -export function setupJavaScript(defaults: LanguageServiceDefaultsImpl): void { - javaScriptWorker = setupMode( - defaults, - 'javascript' - ); -} - -export function getJavaScriptWorker(): Promise<(...uris: Uri[]) => Promise> { - return new Promise((resolve, reject) => { - if (!javaScriptWorker) { - return reject("JavaScript not registered!"); - } - - resolve(javaScriptWorker); - }); -} - -export function getTypeScriptWorker(): Promise<(...uris: Uri[]) => Promise> { - return new Promise((resolve, reject) => { - if (!typeScriptWorker) { - return reject("TypeScript not registered!"); - } - - resolve(typeScriptWorker); - }); -} - -function setupMode(defaults: LanguageServiceDefaultsImpl, modeId: string): (...uris: Uri[]) => Promise { - - const client = new WorkerManager(modeId, defaults); - const worker = (...uris: Uri[]): Promise => { - return client.getLanguageServiceWorker(...uris); - }; - - const libFiles = new languageFeatures.LibFiles(worker); - - monaco.languages.registerCompletionItemProvider(modeId, new languageFeatures.SuggestAdapter(worker)); - monaco.languages.registerSignatureHelpProvider(modeId, new languageFeatures.SignatureHelpAdapter(worker)); - monaco.languages.registerHoverProvider(modeId, new languageFeatures.QuickInfoAdapter(worker)); - monaco.languages.registerDocumentHighlightProvider(modeId, new languageFeatures.OccurrencesAdapter(worker)); - monaco.languages.registerDefinitionProvider(modeId, new languageFeatures.DefinitionAdapter(libFiles, worker)); - monaco.languages.registerReferenceProvider(modeId, new languageFeatures.ReferenceAdapter(libFiles, worker)); - monaco.languages.registerDocumentSymbolProvider(modeId, new languageFeatures.OutlineAdapter(worker)); - monaco.languages.registerDocumentRangeFormattingEditProvider(modeId, new languageFeatures.FormatAdapter(worker)); - monaco.languages.registerOnTypeFormattingEditProvider(modeId, new languageFeatures.FormatOnTypeAdapter(worker)); - monaco.languages.registerCodeActionProvider(modeId, new languageFeatures.CodeActionAdaptor(worker)); - monaco.languages.registerRenameProvider(modeId, new languageFeatures.RenameAdapter(worker)); - new languageFeatures.DiagnosticsAdapter(libFiles, defaults, modeId, worker); - - return worker; -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { WorkerManager } from './workerManager'; +import { TypeScriptWorker } from './tsWorker'; +import { LanguageServiceDefaultsImpl } from './monaco.contribution'; +import * as languageFeatures from './languageFeatures'; + +import Uri = monaco.Uri; + +let javaScriptWorker: (...uris: Uri[]) => Promise; +let typeScriptWorker: (...uris: Uri[]) => Promise; + +export function setupTypeScript(defaults: LanguageServiceDefaultsImpl): void { + typeScriptWorker = setupMode(defaults, 'typescript'); +} + +export function setupJavaScript(defaults: LanguageServiceDefaultsImpl): void { + javaScriptWorker = setupMode(defaults, 'javascript'); +} + +export function getJavaScriptWorker(): Promise< + (...uris: Uri[]) => Promise +> { + return new Promise((resolve, reject) => { + if (!javaScriptWorker) { + return reject('JavaScript not registered!'); + } + + resolve(javaScriptWorker); + }); +} + +export function getTypeScriptWorker(): Promise< + (...uris: Uri[]) => Promise +> { + return new Promise((resolve, reject) => { + if (!typeScriptWorker) { + return reject('TypeScript not registered!'); + } + + resolve(typeScriptWorker); + }); +} + +function setupMode( + defaults: LanguageServiceDefaultsImpl, + modeId: string +): (...uris: Uri[]) => Promise { + const client = new WorkerManager(modeId, defaults); + const worker = (...uris: Uri[]): Promise => { + return client.getLanguageServiceWorker(...uris); + }; + + const libFiles = new languageFeatures.LibFiles(worker); + + monaco.languages.registerCompletionItemProvider( + modeId, + new languageFeatures.SuggestAdapter(worker) + ); + monaco.languages.registerSignatureHelpProvider( + modeId, + new languageFeatures.SignatureHelpAdapter(worker) + ); + monaco.languages.registerHoverProvider( + modeId, + new languageFeatures.QuickInfoAdapter(worker) + ); + monaco.languages.registerDocumentHighlightProvider( + modeId, + new languageFeatures.OccurrencesAdapter(worker) + ); + monaco.languages.registerDefinitionProvider( + modeId, + new languageFeatures.DefinitionAdapter(libFiles, worker) + ); + monaco.languages.registerReferenceProvider( + modeId, + new languageFeatures.ReferenceAdapter(libFiles, worker) + ); + monaco.languages.registerDocumentSymbolProvider( + modeId, + new languageFeatures.OutlineAdapter(worker) + ); + monaco.languages.registerDocumentRangeFormattingEditProvider( + modeId, + new languageFeatures.FormatAdapter(worker) + ); + monaco.languages.registerOnTypeFormattingEditProvider( + modeId, + new languageFeatures.FormatOnTypeAdapter(worker) + ); + monaco.languages.registerCodeActionProvider( + modeId, + new languageFeatures.CodeActionAdaptor(worker) + ); + monaco.languages.registerRenameProvider( + modeId, + new languageFeatures.RenameAdapter(worker) + ); + new languageFeatures.DiagnosticsAdapter(libFiles, defaults, modeId, worker); + + return worker; +} diff --git a/src/tsWorker.ts b/src/tsWorker.ts index 360633b..f4aacbe 100644 --- a/src/tsWorker.ts +++ b/src/tsWorker.ts @@ -1,308 +1,432 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as ts from './lib/typescriptServices'; -import { libFileMap } from './lib/lib'; -import { IExtraLibs } from './monaco.contribution'; - -import IWorkerContext = monaco.worker.IWorkerContext; - - -export class TypeScriptWorker implements ts.LanguageServiceHost, monaco.languages.typescript.TypeScriptWorker { - - // --- model sync ----------------------- - - private _ctx: IWorkerContext; - private _extraLibs: IExtraLibs = Object.create(null); - private _languageService = ts.createLanguageService(this); - private _compilerOptions: ts.CompilerOptions; - - constructor(ctx: IWorkerContext, createData: ICreateData) { - this._ctx = ctx; - this._compilerOptions = createData.compilerOptions; - this._extraLibs = createData.extraLibs; - } - - // --- language service host --------------- - - getCompilationSettings(): ts.CompilerOptions { - return this._compilerOptions; - } - - getScriptFileNames(): string[] { - let models = this._ctx.getMirrorModels().map(model => model.uri.toString()); - return models.concat(Object.keys(this._extraLibs)); - } - - private _getModel(fileName: string): monaco.worker.IMirrorModel | null { - let models = this._ctx.getMirrorModels(); - for (let i = 0; i < models.length; i++) { - if (models[i].uri.toString() === fileName) { - return models[i]; - } - } - return null; - } - - getScriptVersion(fileName: string): string { - let model = this._getModel(fileName); - if (model) { - return model.version.toString(); - } else if (this.isDefaultLibFileName(fileName)) { - // default lib is static - return '1'; - } else if (fileName in this._extraLibs) { - return String(this._extraLibs[fileName].version); - } - return ''; - } - - getScriptText(fileName: string): Promise { - return Promise.resolve(this._getScriptText(fileName)); - } - - _getScriptText(fileName: string): string | undefined { - let text: string; - let model = this._getModel(fileName); - if (model) { - // a true editor model - text = model.getValue(); - } else if (fileName in libFileMap) { - text = libFileMap[fileName]; - - } else if (fileName in this._extraLibs) { - // extra lib - text = this._extraLibs[fileName].content; - } else { - return; - } - - return text; - } - - getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined { - const text = this._getScriptText(fileName); - if (text === undefined) { - return; - } - - return { - getText: (start, end) => text.substring(start, end), - getLength: () => text.length, - getChangeRange: () => undefined - }; - } - - getScriptKind?(fileName: string): ts.ScriptKind { - const suffix = fileName.substr(fileName.lastIndexOf('.') + 1); - switch (suffix) { - case 'ts': return ts.ScriptKind.TS; - case 'tsx': return ts.ScriptKind.TSX; - case 'js': return ts.ScriptKind.JS; - case 'jsx': return ts.ScriptKind.JSX; - default: return this.getCompilationSettings().allowJs - ? ts.ScriptKind.JS - : ts.ScriptKind.TS; - } - } - - getCurrentDirectory(): string { - return ''; - } - - getDefaultLibFileName(options: ts.CompilerOptions): string { - switch (options.target) { - case 99 /* ESNext */: - const esnext = "lib.esnext.full.d.ts"; - if (esnext in libFileMap || esnext in this._extraLibs) return esnext - case 7 /* ES2020 */: - case 6 /* ES2019 */: - case 5 /* ES2018 */: - case 4 /* ES2017 */: - case 3 /* ES2016 */: - case 2 /* ES2015 */: - default: - // Support a dynamic lookup for the ES20XX version based on the target - // which is safe unless TC39 changes their numbering system - const eslib = `lib.es${2013 + (options.target || 99)}.full.d.ts`; - // Note: This also looks in _extraLibs, If you want - // to add support for additional target options, you will need to - // add the extra dts files to _extraLibs via the API. - if (eslib in libFileMap || eslib in this._extraLibs) { - return eslib; - } - - return "lib.es6.d.ts"; // We don't use lib.es2015.full.d.ts due to breaking change. - case 1: - case 0: - return "lib.d.ts"; - } - } - - isDefaultLibFileName(fileName: string): boolean { - return fileName === this.getDefaultLibFileName(this._compilerOptions); - } - - getLibFiles(): Promise> { - return Promise.resolve(libFileMap); - } - - // --- language features - - private static clearFiles(diagnostics: ts.Diagnostic[]): monaco.languages.typescript.Diagnostic[] { - // Clear the `file` field, which cannot be JSON'yfied because it - // contains cyclic data structures. - diagnostics.forEach(diag => { - diag.file = undefined; - const related = diag.relatedInformation; - if (related) { - related.forEach(diag2 => diag2.file = undefined); - } - }); - return diagnostics; - } - - getTopLevelExpressions(fileName: string, text: string): Promise { - const ranges: monaco.IRange[] = []; - - const sourceFile = this._languageService.getProgram()?.getSourceFile(fileName); - if (sourceFile) { - sourceFile.forEachChild((node) => { - if (node.kind === ts.SyntaxKind.ExpressionStatement && (node as any).expression?.expression?.getText() === text) { - const start = sourceFile.getLineAndCharacterOfPosition(node.getStart()); - const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd()); - ranges.push({ - startLineNumber: start.line + 1, - startColumn: start.character + 1, - endLineNumber: end.line + 1, - endColumn: end.character + 1 - }); - } - }) - } - - return Promise.resolve(ranges); - } - - getSyntacticDiagnostics(fileName: string): Promise { - const diagnostics = this._languageService.getSyntacticDiagnostics(fileName); - return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics)); - } - - getSemanticDiagnostics(fileName: string): Promise { - const diagnostics = this._languageService.getSemanticDiagnostics(fileName); - return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics)); - } - - getSuggestionDiagnostics(fileName: string): Promise { - const diagnostics = this._languageService.getSuggestionDiagnostics(fileName); - return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics)); - } - - getCompilerOptionsDiagnostics(fileName: string): Promise { - const diagnostics = this._languageService.getCompilerOptionsDiagnostics(); - return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics)); - } - - getCompletionsAtPosition(fileName: string, position: number): Promise { - return Promise.resolve(this._languageService.getCompletionsAtPosition(fileName, position, undefined)); - } - - getCompletionEntryDetails(fileName: string, position: number, entry: string): Promise { - return Promise.resolve(this._languageService.getCompletionEntryDetails(fileName, position, entry, undefined, undefined, undefined)); - } - - getSignatureHelpItems(fileName: string, position: number): Promise { - return Promise.resolve(this._languageService.getSignatureHelpItems(fileName, position, undefined)); - } - - getQuickInfoAtPosition(fileName: string, position: number): Promise { - return Promise.resolve(this._languageService.getQuickInfoAtPosition(fileName, position)); - } - - getOccurrencesAtPosition(fileName: string, position: number): Promise | undefined> { - return Promise.resolve(this._languageService.getOccurrencesAtPosition(fileName, position)); - } - - getDefinitionAtPosition(fileName: string, position: number): Promise | undefined> { - return Promise.resolve(this._languageService.getDefinitionAtPosition(fileName, position)); - } - - getReferencesAtPosition(fileName: string, position: number): Promise { - return Promise.resolve(this._languageService.getReferencesAtPosition(fileName, position)); - } - - getNavigationBarItems(fileName: string): Promise { - return Promise.resolve(this._languageService.getNavigationBarItems(fileName)); - } - - getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): Promise { - return Promise.resolve(this._languageService.getFormattingEditsForDocument(fileName, options)); - } - - getFormattingEditsForRange(fileName: string, start: number, end: number, options: ts.FormatCodeOptions): Promise { - return Promise.resolve(this._languageService.getFormattingEditsForRange(fileName, start, end, options)); - } - - getFormattingEditsAfterKeystroke(fileName: string, postion: number, ch: string, options: ts.FormatCodeOptions): Promise { - return Promise.resolve(this._languageService.getFormattingEditsAfterKeystroke(fileName, postion, ch, options)); - } - - findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename: boolean): Promise { - return Promise.resolve(this._languageService.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename)); - } - - getRenameInfo(fileName: string, position: number, options: ts.RenameInfoOptions): Promise { - return Promise.resolve(this._languageService.getRenameInfo(fileName, position, options)); - } - - getEmitOutput(fileName: string): Promise { - return Promise.resolve(this._languageService.getEmitOutput(fileName)); - } - - getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: ts.FormatCodeOptions): Promise> { - const preferences = {} - return Promise.resolve(this._languageService.getCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences)); - } - - updateExtraLibs(extraLibs: IExtraLibs) { - this._extraLibs = extraLibs; - } -} - -export interface ICreateData { - compilerOptions: ts.CompilerOptions; - extraLibs: IExtraLibs; - customWorkerPath?: string -} - -/** The shape of the factory */ -export interface CustomTSWebWorkerFactory { - (TSWorkerClass: typeof TypeScriptWorker, tsc: typeof ts, libs: Record): typeof TypeScriptWorker -} - -export function create(ctx: IWorkerContext, createData: ICreateData): TypeScriptWorker { - let TSWorkerClass = TypeScriptWorker - if (createData.customWorkerPath) { - // @ts-ignore - This is available in a webworker - if (typeof importScripts === "undefined") { - console.warn("Monaco is not using webworkers for background tasks, and that is needed to support the customWorkerPath flag") - } else { - // @ts-ignore - This is available in a webworker - importScripts(createData.customWorkerPath) - - // @ts-ignore - This should come from the above eval - const workerFactoryFunc: CustomTSWebWorkerFactory | undefined = self.customTSWorkerFactory - if (!workerFactoryFunc) { - throw new Error(`The script at ${createData.customWorkerPath} does not add customTSWorkerFactory to self`) - } - - TSWorkerClass = workerFactoryFunc(TypeScriptWorker, ts, libFileMap) - } - } - - return new TSWorkerClass(ctx, createData); -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as ts from './lib/typescriptServices'; +import { libFileMap } from './lib/lib'; +import { IExtraLibs } from './monaco.contribution'; + +import IWorkerContext = monaco.worker.IWorkerContext; + +export class TypeScriptWorker + implements + ts.LanguageServiceHost, + monaco.languages.typescript.TypeScriptWorker { + // --- model sync ----------------------- + + private _ctx: IWorkerContext; + private _extraLibs: IExtraLibs = Object.create(null); + private _languageService = ts.createLanguageService(this); + private _compilerOptions: ts.CompilerOptions; + + constructor(ctx: IWorkerContext, createData: ICreateData) { + this._ctx = ctx; + this._compilerOptions = createData.compilerOptions; + this._extraLibs = createData.extraLibs; + } + + // --- language service host --------------- + + getCompilationSettings(): ts.CompilerOptions { + return this._compilerOptions; + } + + getScriptFileNames(): string[] { + let models = this._ctx + .getMirrorModels() + .map((model) => model.uri.toString()); + return models.concat(Object.keys(this._extraLibs)); + } + + private _getModel(fileName: string): monaco.worker.IMirrorModel | null { + let models = this._ctx.getMirrorModels(); + for (let i = 0; i < models.length; i++) { + if (models[i].uri.toString() === fileName) { + return models[i]; + } + } + return null; + } + + getScriptVersion(fileName: string): string { + let model = this._getModel(fileName); + if (model) { + return model.version.toString(); + } else if (this.isDefaultLibFileName(fileName)) { + // default lib is static + return '1'; + } else if (fileName in this._extraLibs) { + return String(this._extraLibs[fileName].version); + } + return ''; + } + + getScriptText(fileName: string): Promise { + return Promise.resolve(this._getScriptText(fileName)); + } + + _getScriptText(fileName: string): string | undefined { + let text: string; + let model = this._getModel(fileName); + if (model) { + // a true editor model + text = model.getValue(); + } else if (fileName in libFileMap) { + text = libFileMap[fileName]; + } else if (fileName in this._extraLibs) { + // extra lib + text = this._extraLibs[fileName].content; + } else { + return; + } + + return text; + } + + getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined { + const text = this._getScriptText(fileName); + if (text === undefined) { + return; + } + + return { + getText: (start, end) => text.substring(start, end), + getLength: () => text.length, + getChangeRange: () => undefined + }; + } + + getScriptKind?(fileName: string): ts.ScriptKind { + const suffix = fileName.substr(fileName.lastIndexOf('.') + 1); + switch (suffix) { + case 'ts': + return ts.ScriptKind.TS; + case 'tsx': + return ts.ScriptKind.TSX; + case 'js': + return ts.ScriptKind.JS; + case 'jsx': + return ts.ScriptKind.JSX; + default: + return this.getCompilationSettings().allowJs + ? ts.ScriptKind.JS + : ts.ScriptKind.TS; + } + } + + getCurrentDirectory(): string { + return ''; + } + + getDefaultLibFileName(options: ts.CompilerOptions): string { + switch (options.target) { + case 99 /* ESNext */: + const esnext = 'lib.esnext.full.d.ts'; + if (esnext in libFileMap || esnext in this._extraLibs) return esnext; + case 7 /* ES2020 */: + case 6 /* ES2019 */: + case 5 /* ES2018 */: + case 4 /* ES2017 */: + case 3 /* ES2016 */: + case 2 /* ES2015 */: + default: + // Support a dynamic lookup for the ES20XX version based on the target + // which is safe unless TC39 changes their numbering system + const eslib = `lib.es${2013 + (options.target || 99)}.full.d.ts`; + // Note: This also looks in _extraLibs, If you want + // to add support for additional target options, you will need to + // add the extra dts files to _extraLibs via the API. + if (eslib in libFileMap || eslib in this._extraLibs) { + return eslib; + } + + return 'lib.es6.d.ts'; // We don't use lib.es2015.full.d.ts due to breaking change. + case 1: + case 0: + return 'lib.d.ts'; + } + } + + isDefaultLibFileName(fileName: string): boolean { + return fileName === this.getDefaultLibFileName(this._compilerOptions); + } + + getLibFiles(): Promise> { + return Promise.resolve(libFileMap); + } + + // --- language features + + private static clearFiles( + diagnostics: ts.Diagnostic[] + ): monaco.languages.typescript.Diagnostic[] { + // Clear the `file` field, which cannot be JSON'yfied because it + // contains cyclic data structures. + diagnostics.forEach((diag) => { + diag.file = undefined; + const related = diag.relatedInformation; + if (related) { + related.forEach((diag2) => (diag2.file = undefined)); + } + }); + return diagnostics; + } + + getSyntacticDiagnostics( + fileName: string + ): Promise { + const diagnostics = this._languageService.getSyntacticDiagnostics(fileName); + return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics)); + } + + getSemanticDiagnostics( + fileName: string + ): Promise { + const diagnostics = this._languageService.getSemanticDiagnostics(fileName); + return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics)); + } + + getSuggestionDiagnostics( + fileName: string + ): Promise { + const diagnostics = this._languageService.getSuggestionDiagnostics( + fileName + ); + return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics)); + } + + getCompilerOptionsDiagnostics( + fileName: string + ): Promise { + const diagnostics = this._languageService.getCompilerOptionsDiagnostics(); + return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics)); + } + + getCompletionsAtPosition( + fileName: string, + position: number + ): Promise { + return Promise.resolve( + this._languageService.getCompletionsAtPosition( + fileName, + position, + undefined + ) + ); + } + + getCompletionEntryDetails( + fileName: string, + position: number, + entry: string + ): Promise { + return Promise.resolve( + this._languageService.getCompletionEntryDetails( + fileName, + position, + entry, + undefined, + undefined, + undefined + ) + ); + } + + getSignatureHelpItems( + fileName: string, + position: number + ): Promise { + return Promise.resolve( + this._languageService.getSignatureHelpItems(fileName, position, undefined) + ); + } + + getQuickInfoAtPosition( + fileName: string, + position: number + ): Promise { + return Promise.resolve( + this._languageService.getQuickInfoAtPosition(fileName, position) + ); + } + + getOccurrencesAtPosition( + fileName: string, + position: number + ): Promise | undefined> { + return Promise.resolve( + this._languageService.getOccurrencesAtPosition(fileName, position) + ); + } + + getDefinitionAtPosition( + fileName: string, + position: number + ): Promise | undefined> { + return Promise.resolve( + this._languageService.getDefinitionAtPosition(fileName, position) + ); + } + + getReferencesAtPosition( + fileName: string, + position: number + ): Promise { + return Promise.resolve( + this._languageService.getReferencesAtPosition(fileName, position) + ); + } + + getNavigationBarItems(fileName: string): Promise { + return Promise.resolve( + this._languageService.getNavigationBarItems(fileName) + ); + } + + getFormattingEditsForDocument( + fileName: string, + options: ts.FormatCodeOptions + ): Promise { + return Promise.resolve( + this._languageService.getFormattingEditsForDocument(fileName, options) + ); + } + + getFormattingEditsForRange( + fileName: string, + start: number, + end: number, + options: ts.FormatCodeOptions + ): Promise { + return Promise.resolve( + this._languageService.getFormattingEditsForRange( + fileName, + start, + end, + options + ) + ); + } + + getFormattingEditsAfterKeystroke( + fileName: string, + postion: number, + ch: string, + options: ts.FormatCodeOptions + ): Promise { + return Promise.resolve( + this._languageService.getFormattingEditsAfterKeystroke( + fileName, + postion, + ch, + options + ) + ); + } + + findRenameLocations( + fileName: string, + position: number, + findInStrings: boolean, + findInComments: boolean, + providePrefixAndSuffixTextForRename: boolean + ): Promise { + return Promise.resolve( + this._languageService.findRenameLocations( + fileName, + position, + findInStrings, + findInComments, + providePrefixAndSuffixTextForRename + ) + ); + } + + getRenameInfo( + fileName: string, + position: number, + options: ts.RenameInfoOptions + ): Promise { + return Promise.resolve( + this._languageService.getRenameInfo(fileName, position, options) + ); + } + + getEmitOutput(fileName: string): Promise { + return Promise.resolve(this._languageService.getEmitOutput(fileName)); + } + + getCodeFixesAtPosition( + fileName: string, + start: number, + end: number, + errorCodes: number[], + formatOptions: ts.FormatCodeOptions + ): Promise> { + const preferences = {}; + return Promise.resolve( + this._languageService.getCodeFixesAtPosition( + fileName, + start, + end, + errorCodes, + formatOptions, + preferences + ) + ); + } + + updateExtraLibs(extraLibs: IExtraLibs) { + this._extraLibs = extraLibs; + } +} + +export interface ICreateData { + compilerOptions: ts.CompilerOptions; + extraLibs: IExtraLibs; + customWorkerPath?: string; +} + +/** The shape of the factory */ +export interface CustomTSWebWorkerFactory { + ( + TSWorkerClass: typeof TypeScriptWorker, + tsc: typeof ts, + libs: Record + ): typeof TypeScriptWorker; +} + +declare global { + var importScripts: (path: string) => void | undefined; + var customTSWorkerFactory: CustomTSWebWorkerFactory | undefined; +} + +export function create( + ctx: IWorkerContext, + createData: ICreateData +): TypeScriptWorker { + let TSWorkerClass = TypeScriptWorker; + if (createData.customWorkerPath) { + if (typeof importScripts === 'undefined') { + console.warn( + 'Monaco is not using webworkers for background tasks, and that is needed to support the customWorkerPath flag' + ); + } else { + importScripts(createData.customWorkerPath); + + const workerFactoryFunc: CustomTSWebWorkerFactory | undefined = + self.customTSWorkerFactory; + if (!workerFactoryFunc) { + throw new Error( + `The script at ${createData.customWorkerPath} does not add customTSWorkerFactory to self` + ); + } + + TSWorkerClass = workerFactoryFunc(TypeScriptWorker, ts, libFileMap); + } + } + + return new TSWorkerClass(ctx, createData); +} diff --git a/src/tsconfig.esm.json b/src/tsconfig.esm.json index 0378303..aa682be 100644 --- a/src/tsconfig.esm.json +++ b/src/tsconfig.esm.json @@ -1,22 +1,18 @@ { - "compilerOptions": { - "module": "esnext", - "moduleResolution": "node", - "outDir": "../release/esm", - "target": "es5", - "lib": [ - "dom", - "es5", - "es2015.collection", - "es2015.iterable", - "es2015.promise" - ], - "strict": true - }, - "include": [ - "**/*.ts" - ], - "files": [ - "../node_modules/monaco-editor-core/monaco.d.ts" - ] + "compilerOptions": { + "module": "esnext", + "moduleResolution": "node", + "outDir": "../release/esm", + "target": "es5", + "lib": [ + "dom", + "es5", + "es2015.collection", + "es2015.iterable", + "es2015.promise" + ], + "strict": true + }, + "include": ["**/*.ts"], + "files": ["../node_modules/monaco-editor-core/monaco.d.ts"] } diff --git a/src/tsconfig.json b/src/tsconfig.json index b2a6ca6..8160c13 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,22 +1,18 @@ { - "compilerOptions": { - "module": "amd", - "moduleResolution": "node", - "outDir": "../release/dev", - "target": "es5", - "lib": [ - "dom", - "es5", - "es2015.collection", - "es2015.iterable", - "es2015.promise" - ], - "strict": true - }, - "include": [ - "**/*.ts" - ], - "files": [ - "../node_modules/monaco-editor-core/monaco.d.ts" - ] + "compilerOptions": { + "module": "amd", + "moduleResolution": "node", + "outDir": "../release/dev", + "target": "es5", + "lib": [ + "dom", + "es5", + "es2015.collection", + "es2015.iterable", + "es2015.promise" + ], + "strict": true + }, + "include": ["**/*.ts"], + "files": ["../node_modules/monaco-editor-core/monaco.d.ts"] } diff --git a/src/workerManager.ts b/src/workerManager.ts index fe6e245..48f6bf8 100644 --- a/src/workerManager.ts +++ b/src/workerManager.ts @@ -1,110 +1,117 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import { LanguageServiceDefaultsImpl } from './monaco.contribution'; -import { TypeScriptWorker } from './tsWorker'; - -import IDisposable = monaco.IDisposable; -import Uri = monaco.Uri; - -export class WorkerManager { - - private _modeId: string; - private _defaults: LanguageServiceDefaultsImpl; - private _configChangeListener: IDisposable; - private _updateExtraLibsToken: number; - private _extraLibsChangeListener: IDisposable; - - private _worker: monaco.editor.MonacoWebWorker | null; - private _client: Promise | null; - - constructor(modeId: string, defaults: LanguageServiceDefaultsImpl) { - this._modeId = modeId; - this._defaults = defaults; - this._worker = null; - this._client = null; - this._configChangeListener = this._defaults.onDidChange(() => this._stopWorker()); - this._updateExtraLibsToken = 0; - this._extraLibsChangeListener = this._defaults.onDidExtraLibsChange(() => this._updateExtraLibs()); - } - - private _stopWorker(): void { - if (this._worker) { - this._worker.dispose(); - this._worker = null; - } - this._client = null; - } - - dispose(): void { - 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 _getClient(): Promise { - if (!this._client) { - this._worker = monaco.editor.createWebWorker({ - - // module that exports the create() method and returns a `TypeScriptWorker` instance - moduleId: 'vs/language/typescript/tsWorker', - - label: this._modeId, - - keepIdleModels: true, - - // passed in to the create() method - createData: { - compilerOptions: this._defaults.getCompilerOptions(), - extraLibs: this._defaults.getExtraLibs(), - customWorkerPath: this._defaults.workerOptions.customWorkerPath - } - }); - - let p = >this._worker.getProxy(); - - if (this._defaults.getEagerModelSync()) { - p = p.then(worker => { - if (this._worker) { - return this._worker.withSyncedResources(monaco.editor.getModels() - .filter(model => model.getModeId() === this._modeId) - .map(model => model.uri) - ); - } - return worker; - }); - } - - this._client = p; - } - - return this._client; - } - - getLanguageServiceWorker(...resources: Uri[]): Promise { - let _client: TypeScriptWorker; - return this._getClient().then((client) => { - _client = client - }).then(_ => { - if (this._worker) { - return this._worker.withSyncedResources(resources) - } - }).then(_ => _client); - } -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { LanguageServiceDefaultsImpl } from './monaco.contribution'; +import { TypeScriptWorker } from './tsWorker'; + +import IDisposable = monaco.IDisposable; +import Uri = monaco.Uri; + +export class WorkerManager { + private _modeId: string; + private _defaults: LanguageServiceDefaultsImpl; + private _configChangeListener: IDisposable; + private _updateExtraLibsToken: number; + private _extraLibsChangeListener: IDisposable; + + private _worker: monaco.editor.MonacoWebWorker | null; + private _client: Promise | null; + + constructor(modeId: string, defaults: LanguageServiceDefaultsImpl) { + this._modeId = modeId; + this._defaults = defaults; + this._worker = null; + this._client = null; + this._configChangeListener = this._defaults.onDidChange(() => + this._stopWorker() + ); + this._updateExtraLibsToken = 0; + this._extraLibsChangeListener = this._defaults.onDidExtraLibsChange(() => + this._updateExtraLibs() + ); + } + + private _stopWorker(): void { + if (this._worker) { + this._worker.dispose(); + this._worker = null; + } + this._client = null; + } + + dispose(): void { + 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 _getClient(): Promise { + if (!this._client) { + this._worker = monaco.editor.createWebWorker({ + // module that exports the create() method and returns a `TypeScriptWorker` instance + moduleId: 'vs/language/typescript/tsWorker', + + label: this._modeId, + + keepIdleModels: true, + + // passed in to the create() method + createData: { + compilerOptions: this._defaults.getCompilerOptions(), + extraLibs: this._defaults.getExtraLibs(), + customWorkerPath: this._defaults.workerOptions.customWorkerPath + } + }); + + let p = >this._worker.getProxy(); + + if (this._defaults.getEagerModelSync()) { + p = p.then((worker) => { + if (this._worker) { + return this._worker.withSyncedResources( + monaco.editor + .getModels() + .filter((model) => model.getModeId() === this._modeId) + .map((model) => model.uri) + ); + } + return worker; + }); + } + + this._client = p; + } + + return this._client; + } + + getLanguageServiceWorker(...resources: Uri[]): Promise { + let _client: TypeScriptWorker; + return this._getClient() + .then((client) => { + _client = client; + }) + .then((_) => { + if (this._worker) { + return this._worker.withSyncedResources(resources); + } + }) + .then((_) => _client); + } +} diff --git a/test/custom-worker.html b/test/custom-worker.html index 74bfdbb..e21e291 100644 --- a/test/custom-worker.html +++ b/test/custom-worker.html @@ -8,215 +8,230 @@ - - - - - - + + + + + + +

Monaco Editor TypeScript test page

+ +
+

Custom webworker

+ + -

Monaco Editor TypeScript test page

- -
-

Custom webworker

- - + + + + - - - - + - + document.getElementById('getAST').onclick = async () => { + const model = editor.getModel(); + const worker = await monaco.languages.typescript.getTypeScriptWorker(); + const thisWorker = await worker(model.uri); + const ast = await thisWorker.printAST(model.uri.toString()); + console.log(ast); + }; + }); + + diff --git a/test/custom-worker.js b/test/custom-worker.js index 3eeacd1..3652f3c 100644 --- a/test/custom-worker.js +++ b/test/custom-worker.js @@ -2,59 +2,58 @@ // which can do work on a bg thread. // This version of the vfs edits the global scope (in the case of a webworker, this is 'self') -importScripts("https://unpkg.com/@typescript/vfs@1.3.0/dist/vfs.globals.js") +importScripts('https://unpkg.com/@typescript/vfs@1.3.0/dist/vfs.globals.js'); /** @type { import("@typescript/vfs") } */ -const tsvfs = globalThis.tsvfs +const tsvfs = globalThis.tsvfs; /** @type {import("../src/tsWorker").CustomTSWebWorkerFactory }*/ const worker = (TypeScriptWorker, ts, libFileMap) => { - return class MonacoTSWorker extends TypeScriptWorker { - - // Adds a custom function to the webworker - async getDTSEmitForFile(fileName) { - const result = await this.getEmitOutput(fileName) - const firstDTS = result.outputFiles.find(o => o.name.endsWith(".d.ts")) - return (firstDTS && firstDTS.text) || "" - } - - async printAST(fileName) { - console.log("Creating virtual TS project") - const compilerOptions = this.getCompilationSettings() - const fsMap = new Map() - for (const key of Object.keys(libFileMap)) { - fsMap.set(key, "/" + libFileMap[key]) - } - - const thisCode = await this.getScriptText(fileName) - fsMap.set("index.ts", thisCode) - - console.log("Starting up TS program") - const system = tsvfs.createSystem(fsMap) - const host = tsvfs.createVirtualCompilerHost(system, compilerOptions, ts) - - const program = ts.createProgram({ - rootNames: [...fsMap.keys()], - options: compilerOptions, - host: host.compilerHost, - }) - - // Now I can look at the AST for the .ts file too - const mainSrcFile = program.getSourceFile("index.ts") - let miniAST = "SourceFile" - - const recurse = (parent, depth) => { - if (depth > 5) return - ts.forEachChild(parent, node => { - const spaces = " ".repeat(depth + 1) - miniAST += `\n${spaces}${ts.SyntaxKind[node.kind]}` - recurse(node, depth + 1) - }) - } - recurse(mainSrcFile, 0) - return miniAST - } - } -} - -self.customTSWorkerFactory = worker + return class MonacoTSWorker extends TypeScriptWorker { + // Adds a custom function to the webworker + async getDTSEmitForFile(fileName) { + const result = await this.getEmitOutput(fileName); + const firstDTS = result.outputFiles.find((o) => o.name.endsWith('.d.ts')); + return (firstDTS && firstDTS.text) || ''; + } + + async printAST(fileName) { + console.log('Creating virtual TS project'); + const compilerOptions = this.getCompilationSettings(); + const fsMap = new Map(); + for (const key of Object.keys(libFileMap)) { + fsMap.set(key, '/' + libFileMap[key]); + } + + const thisCode = await this.getScriptText(fileName); + fsMap.set('index.ts', thisCode); + + console.log('Starting up TS program'); + const system = tsvfs.createSystem(fsMap); + const host = tsvfs.createVirtualCompilerHost(system, compilerOptions, ts); + + const program = ts.createProgram({ + rootNames: [...fsMap.keys()], + options: compilerOptions, + host: host.compilerHost + }); + + // Now I can look at the AST for the .ts file too + const mainSrcFile = program.getSourceFile('index.ts'); + let miniAST = 'SourceFile'; + + const recurse = (parent, depth) => { + if (depth > 5) return; + ts.forEachChild(parent, (node) => { + const spaces = ' '.repeat(depth + 1); + miniAST += `\n${spaces}${ts.SyntaxKind[node.kind]}`; + recurse(node, depth + 1); + }); + }; + recurse(mainSrcFile, 0); + return miniAST; + } + }; +}; + +self.customTSWorkerFactory = worker; diff --git a/test/index.html b/test/index.html index 1f3f578..7d9a838 100644 --- a/test/index.html +++ b/test/index.html @@ -1,215 +1,224 @@ - - - - - - - - - -

Monaco Editor TypeScript test page

- - -
-

Compiler settings

-
- - - - - - - - - - + + + + + + + + +

Monaco Editor TypeScript test page

+ +
+

Compiler settings

+
+ + + + + + + + + + From b01b579af752fa84ed08098238aefd2a6bf4ac18 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 7 Sep 2020 21:52:41 +0200 Subject: [PATCH 12/37] Use the global `monaco` only in the AMD case (see microsoft/monaco-editor#1974) --- .gitignore | 1 + .npmignore | 2 + .prettierignore | 1 + .vscode/settings.json | 1 + src/monaco.d.ts => monaco.d.ts | 81 ++-- package.json | 10 +- scripts/bundle.js | 36 +- scripts/dts.js | 44 ++ scripts/release.js | 17 + src/fillers/monaco-editor-core-amd.ts | 12 + src/fillers/monaco-editor-core.ts | 6 + src/languageFeatures.ts | 278 ++++++------ src/lib/editor.worker.d.ts | 16 +- src/monaco.contribution.ts | 587 +++++++++++++++++++++----- src/ts.worker.ts | 11 +- src/tsMode.ts | 35 +- src/tsWorker.ts | 43 +- src/tsconfig.esm.json | 7 +- src/tsconfig.json | 7 +- src/workerManager.ts | 18 +- test/custom-worker.html | 16 +- test/index.html | 6 +- 22 files changed, 843 insertions(+), 392 deletions(-) rename src/monaco.d.ts => monaco.d.ts (92%) create mode 100644 scripts/dts.js create mode 100644 scripts/release.js create mode 100644 src/fillers/monaco-editor-core-amd.ts create mode 100644 src/fillers/monaco-editor-core.ts diff --git a/.gitignore b/.gitignore index 79b9535..d9ef910 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules/ +/out/ /release/ diff --git a/.npmignore b/.npmignore index 46ba196..90de981 100644 --- a/.npmignore +++ b/.npmignore @@ -1,3 +1,4 @@ +/.github/ /.vscode/ /scripts/ /src/ @@ -5,4 +6,5 @@ /gulpfile.js /tsconfig.json /.npmignore +/out/ /release/**/test/ diff --git a/.prettierignore b/.prettierignore index 45537e5..5811aad 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,3 @@ +/out/ /release/ /src/lib/ diff --git a/.vscode/settings.json b/.vscode/settings.json index a5826cb..787e816 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,7 @@ "files.trimTrailingWhitespace": true, "search.exclude": { "**/node_modules": true, + "**/out": true, "**/release": true } } diff --git a/src/monaco.d.ts b/monaco.d.ts similarity index 92% rename from src/monaco.d.ts rename to monaco.d.ts index 2be40dc..57e5774 100644 --- a/src/monaco.d.ts +++ b/monaco.d.ts @@ -1,5 +1,12 @@ -declare module monaco.languages.typescript { - enum ModuleKind { +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// + +declare namespace monaco.languages.typescript { + export enum ModuleKind { None = 0, CommonJS = 1, AMD = 2, @@ -8,19 +15,17 @@ declare module monaco.languages.typescript { ES2015 = 5, ESNext = 99 } - - enum JsxEmit { + export enum JsxEmit { None = 0, Preserve = 1, React = 2, ReactNative = 3 } - enum NewLineKind { + export enum NewLineKind { CarriageReturnLineFeed = 0, LineFeed = 1 } - - enum ScriptTarget { + export enum ScriptTarget { ES3 = 0, ES5 = 1, ES2015 = 2, @@ -31,19 +36,16 @@ declare module monaco.languages.typescript { ES2020 = 7, ESNext = 99, JSON = 100, - Latest = ESNext + Latest = 99 } - export enum ModuleResolutionKind { Classic = 1, NodeJs = 2 } - interface MapLike { [index: string]: T; } - - type CompilerOptionsValue = + declare type CompilerOptionsValue = | string | number | boolean @@ -135,28 +137,23 @@ declare module monaco.languages.typescript { useDefineForClassFields?: boolean; [option: string]: CompilerOptionsValue | undefined; } - export interface DiagnosticsOptions { noSemanticValidation?: boolean; noSyntaxValidation?: boolean; noSuggestionDiagnostics?: boolean; diagnosticCodesToIgnore?: number[]; } - export interface WorkerOptions { /** A full HTTP path to a JavaScript file which adds a function `customTSWorkerFactory` to the self inside a web-worker */ customWorkerPath?: string; } - interface IExtraLib { content: string; version: number; } - - interface IExtraLibs { + export interface IExtraLibs { [path: string]: IExtraLib; } - /** * A linked list of formatted diagnostic messages to be used as part of a multiline message. * It is built from the bottom up, leaving the head to be the "main" diagnostic. @@ -168,7 +165,7 @@ declare module monaco.languages.typescript { code: number; next?: DiagnosticMessageChain[]; } - interface Diagnostic extends DiagnosticRelatedInformation { + export interface Diagnostic extends DiagnosticRelatedInformation { /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ reportsUnnecessary?: {}; source?: string; @@ -184,7 +181,6 @@ declare module monaco.languages.typescript { length: number | undefined; messageText: string | DiagnosticMessageChain; } - interface EmitOutput { outputFiles: OutputFile[]; emitSkipped: boolean; @@ -194,23 +190,20 @@ declare module monaco.languages.typescript { writeByteOrderMark: boolean; text: string; } - export interface LanguageServiceDefaults { /** * Event fired when compiler options or diagnostics options are changed. */ readonly onDidChange: IEvent; - /** * Event fired when extra libraries registered with the language service change. */ readonly onDidExtraLibsChange: IEvent; - + readonly workerOptions: WorkerOptions; /** * Get the current extra libs registered with the language service. */ getExtraLibs(): IExtraLibs; - /** * Add an additional source file to the language service. Use this * for typescript (definition) files that won't be loaded as editor @@ -222,81 +215,72 @@ declare module monaco.languages.typescript { * language service upon disposal. */ addExtraLib(content: string, filePath?: string): IDisposable; - /** * Remove all existing extra libs and set the additional source * files to the language service. Use this for typescript definition * files that won't be loaded as editor documents, like `jquery.d.ts`. * @param libs An array of entries to register. */ - setExtraLibs(libs: { content: string; filePath?: string }[]): void; - + setExtraLibs( + libs: { + content: string; + filePath?: string; + }[] + ): void; /** * Get current TypeScript compiler options for the language service. */ getCompilerOptions(): CompilerOptions; - /** * Set TypeScript compiler options. */ setCompilerOptions(options: CompilerOptions): void; - /** * Get the current diagnostics options for the language service. */ getDiagnosticsOptions(): DiagnosticsOptions; - /** * Configure whether syntactic and/or semantic validation should * be performed */ setDiagnosticsOptions(options: DiagnosticsOptions): void; - /** * No-op. */ setMaximumWorkerIdleTime(value: number): void; - /** * Configure if all existing models should be eagerly sync'd * to the worker on start or restart. */ setEagerModelSync(value: boolean): void; - /** * Get the current setting for whether all existing models should be eagerly sync'd * to the worker on start or restart. */ getEagerModelSync(): boolean; } - export interface TypeScriptWorker { /** * Get diagnostic messages for any syntax issues in the given file. */ getSyntacticDiagnostics(fileName: string): Promise; - /** * Get diagnostic messages for any semantic issues in the given file. */ getSemanticDiagnostics(fileName: string): Promise; - /** * Get diagnostic messages for any suggestions related to the given file. */ getSuggestionDiagnostics(fileName: string): Promise; - /** * Get the content of a given file. */ getScriptText(fileName: string): Promise; - /** * Get diagnostic messages related to the current compiler options. * @param fileName Not used */ getCompilerOptionsDiagnostics(fileName: string): Promise; - /** * Get code completions for the given file and position. * @returns `Promise` @@ -305,7 +289,6 @@ declare module monaco.languages.typescript { fileName: string, position: number ): Promise; - /** * Get code completion details for the given file, position, and entry. * @returns `Promise` @@ -315,7 +298,6 @@ declare module monaco.languages.typescript { position: number, entry: string ): Promise; - /** * Get signature help items for the item at the given file and position. * @returns `Promise` @@ -324,7 +306,6 @@ declare module monaco.languages.typescript { fileName: string, position: number ): Promise; - /** * Get quick info for the item at the given position in the file. * @returns `Promise` @@ -333,7 +314,6 @@ declare module monaco.languages.typescript { fileName: string, position: number ): Promise; - /** * Get other ranges which are related to the item at the given position in the file (often used for highlighting). * @returns `Promise | undefined>` @@ -342,7 +322,6 @@ declare module monaco.languages.typescript { fileName: string, position: number ): Promise | undefined>; - /** * Get the definition of the item at the given position in the file. * @returns `Promise | undefined>` @@ -351,7 +330,6 @@ declare module monaco.languages.typescript { fileName: string, position: number ): Promise | undefined>; - /** * Get references to the item at the given position in the file. * @returns `Promise` @@ -360,13 +338,11 @@ declare module monaco.languages.typescript { fileName: string, position: number ): Promise; - /** * Get outline entries for the item at the given position in the file. * @returns `Promise` */ getNavigationBarItems(fileName: string): Promise; - /** * Get changes which should be applied to format the given file. * @param options `typescript.FormatCodeOptions` @@ -376,7 +352,6 @@ declare module monaco.languages.typescript { fileName: string, options: any ): Promise; - /** * Get changes which should be applied to format the given range in the file. * @param options `typescript.FormatCodeOptions` @@ -388,7 +363,6 @@ declare module monaco.languages.typescript { end: number, options: any ): Promise; - /** * Get formatting changes which should be applied after the given keystroke. * @param options `typescript.FormatCodeOptions` @@ -400,7 +374,6 @@ declare module monaco.languages.typescript { ch: string, options: any ): Promise; - /** * Get other occurrences which should be updated when renaming the item at the given file and position. * @returns `Promise` @@ -412,7 +385,6 @@ declare module monaco.languages.typescript { findInComments: boolean, providePrefixAndSuffixTextForRename: boolean ): Promise; - /** * Get edits which should be applied to rename the item at the given file and position (or a failure reason). * @param options `typescript.RenameInfoOptions` @@ -423,13 +395,11 @@ declare module monaco.languages.typescript { positon: number, options: any ): Promise; - /** * Get transpiled output for the given file. * @returns `typescript.EmitOutput` */ - getEmitOutput(fileName: string): Promise; - + getEmitOutput(fileName: string): Promise; /** * Get possible code fixes at the given position in the file. * @param formatOptions `typescript.FormatCodeOptions` @@ -443,12 +413,9 @@ declare module monaco.languages.typescript { formatOptions: any ): Promise>; } - export const typescriptVersion: string; - export const typescriptDefaults: LanguageServiceDefaults; export const javascriptDefaults: LanguageServiceDefaults; - export const getTypeScriptWorker: () => Promise< (...uris: Uri[]) => Promise >; diff --git a/package.json b/package.json index 70dcfd7..a6fb68e 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,11 @@ "version": "3.7.0", "description": "TypeScript and JavaScript language support for Monaco Editor", "scripts": { - "compile-amd": "mcopy ./src/lib/typescriptServices-amd.js ./release/dev/lib/typescriptServices.js && tsc -p ./src/tsconfig.json", - "compile-esm": "mcopy ./src/lib/typescriptServices.js ./release/esm/lib/typescriptServices.js && tsc -p ./src/tsconfig.esm.json", - "compile": "mrmdir ./release && npm run compile-amd && npm run compile-esm", + "compile-amd": "mcopy ./src/lib/typescriptServices-amd.js ./out/amd/lib/typescriptServices.js && tsc -p ./src/tsconfig.json", + "compile-esm": "mcopy ./src/lib/typescriptServices.js ./out/esm/lib/typescriptServices.js && tsc -p ./src/tsconfig.esm.json", + "compile": "mrmdir ./out && npm run compile-amd && npm run compile-esm && node ./scripts/dts && prettier --write ./monaco.d.ts", "watch": "tsc -p ./src --watch", - "prepublishOnly": "npm run compile && node ./scripts/bundle && mcopy ./src/monaco.d.ts ./release/monaco.d.ts", + "prepublishOnly": "mrmdir ./release && npm run compile && node ./scripts/release.js && node ./scripts/bundle && mcopy ./monaco.d.ts ./release/monaco.d.ts && mcopy ./out/esm/monaco.contribution.d.ts ./release/esm/monaco.contribution.d.ts && mcopy ./out/esm/fillers/monaco-editor-core.d.ts ./release/esm/fillers/monaco-editor-core.d.ts", "import-typescript": "node ./scripts/importTypescript", "prettier": "prettier --write ." }, @@ -20,6 +20,8 @@ "bugs": { "url": "https://github.com/Microsoft/monaco-typescript/issues" }, + "module": "./release/esm/monaco.contribution.js", + "typings": "./release/esm/monaco.contribution.d.ts", "devDependencies": { "@typescript/vfs": "^1.3.0", "husky": "^4.3.0", diff --git a/scripts/bundle.js b/scripts/bundle.js index b15988c..f618521 100644 --- a/scripts/bundle.js +++ b/scripts/bundle.js @@ -26,29 +26,45 @@ const BUNDLED_FILE_HEADER = [ ].join('\n'); bundleOne('monaco.contribution'); -bundleOne('tsMode'); +bundleOne('tsMode', ['vs/language/typescript/monaco.contribution']); bundleOne('tsWorker'); function bundleOne(moduleId, exclude) { requirejs.optimize( { - baseUrl: 'release/dev/', + baseUrl: 'out/amd/', name: 'vs/language/typescript/' + moduleId, - out: 'release/min/' + moduleId + '.js', + out: 'release/dev/' + moduleId + '.js', exclude: exclude, paths: { - 'vs/language/typescript': REPO_ROOT + '/release/dev' + 'vs/language/typescript': REPO_ROOT + '/out/amd', + 'vs/language/typescript/fillers/monaco-editor-core': + REPO_ROOT + '/out/amd/fillers/monaco-editor-core-amd' }, optimize: 'none' }, async function (buildResponse) { - const filePath = path.join(REPO_ROOT, 'release/min/' + moduleId + '.js'); - const fileContents = fs.readFileSync(filePath).toString(); + const devFilePath = path.join( + REPO_ROOT, + 'release/dev/' + moduleId + '.js' + ); + const minFilePath = path.join( + REPO_ROOT, + 'release/min/' + moduleId + '.js' + ); + const fileContents = fs.readFileSync(devFilePath).toString(); console.log(); - console.log(`Minifying ${filePath}...`); - const result = await terser.minify(fileContents); - console.log(`Done minifying ${filePath}.`); - fs.writeFileSync(filePath, BUNDLED_FILE_HEADER + result.code); + console.log(`Minifying ${devFilePath}...`); + const result = await terser.minify(fileContents, { + output: { + comments: 'some' + } + }); + console.log(`Done minifying ${devFilePath}.`); + try { + fs.mkdirSync(path.join(REPO_ROOT, 'release/min')); + } catch (err) {} + fs.writeFileSync(minFilePath, BUNDLED_FILE_HEADER + result.code); } ); } diff --git a/scripts/dts.js b/scripts/dts.js new file mode 100644 index 0000000..a89a9db --- /dev/null +++ b/scripts/dts.js @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const path = require('path'); +const fs = require('fs'); + +const REPO_ROOT = path.join(__dirname, '../'); +const SRC_PATH = path.join(REPO_ROOT, 'out/amd/monaco.contribution.d.ts'); +const DST_PATH = path.join(REPO_ROOT, 'monaco.d.ts'); + +const lines = fs + .readFileSync(SRC_PATH) + .toString() + .split(/\r\n|\r|\n/); +let result = [ + `/*---------------------------------------------------------------------------------------------`, + ` * Copyright (c) Microsoft Corporation. All rights reserved.`, + ` * Licensed under the MIT License. See License.txt in the project root for license information.`, + ` *--------------------------------------------------------------------------------------------*/`, + ``, + `/// `, + ``, + `declare namespace monaco.languages.typescript {` +]; +for (let line of lines) { + if (/^import/.test(line)) { + continue; + } + if (line === 'export {};') { + continue; + } + line = line.replace(/ /g, '\t'); + line = line.replace(/export declare/g, 'export'); + if (line.length > 0) { + line = `\t${line}`; + result.push(line); + } +} +result.push(`}`); +result.push(``); + +fs.writeFileSync(DST_PATH, result.join('\n')); diff --git a/scripts/release.js b/scripts/release.js new file mode 100644 index 0000000..03dd36e --- /dev/null +++ b/scripts/release.js @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const path = require('path'); +const helpers = require('monaco-plugin-helpers'); + +const REPO_ROOT = path.join(__dirname, '../'); + +helpers.packageESM({ + repoRoot: REPO_ROOT, + esmSource: 'out/esm', + esmDestination: 'release/esm', + entryPoints: ['monaco.contribution.js', 'tsMode.js', 'ts.worker.js'], + resolveSkip: ['monaco-editor-core'] +}); diff --git a/src/fillers/monaco-editor-core-amd.ts b/src/fillers/monaco-editor-core-amd.ts new file mode 100644 index 0000000..0e8a7e3 --- /dev/null +++ b/src/fillers/monaco-editor-core-amd.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// Resolves with the global monaco API + +declare var define: any; + +define([], function () { + return (self).monaco; +}); diff --git a/src/fillers/monaco-editor-core.ts b/src/fillers/monaco-editor-core.ts new file mode 100644 index 0000000..cd996aa --- /dev/null +++ b/src/fillers/monaco-editor-core.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export * from 'monaco-editor-core'; diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index 909525f..8eb39ec 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -4,16 +4,22 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { LanguageServiceDefaultsImpl } from './monaco.contribution'; -import * as ts from './lib/typescriptServices'; -import { TypeScriptWorker } from './tsWorker'; +import { LanguageServiceDefaults } from './monaco.contribution'; +import type * as ts from './lib/typescriptServices'; +import type { TypeScriptWorker } from './tsWorker'; import { libFileSet } from './lib/lib.index'; - -import Uri = monaco.Uri; -import Position = monaco.Position; -import Range = monaco.Range; -import CancellationToken = monaco.CancellationToken; -import IDisposable = monaco.IDisposable; +import { + editor, + languages, + Uri, + Position, + Range, + CancellationToken, + IDisposable, + IRange, + MarkerTag, + MarkerSeverity +} from './fillers/monaco-editor-core'; //#region utils copied from typescript to prevent loading the entire typescriptServices --- @@ -67,18 +73,18 @@ export abstract class Adapter { protected _worker: (...uris: Uri[]) => Promise ) {} - // protected _positionToOffset(model: monaco.editor.ITextModel, position: monaco.IPosition): number { + // protected _positionToOffset(model: editor.ITextModel, position: monaco.IPosition): number { // return model.getOffsetAt(position); // } - // protected _offsetToPosition(model: monaco.editor.ITextModel, offset: number): monaco.IPosition { + // protected _offsetToPosition(model: editor.ITextModel, offset: number): monaco.IPosition { // return model.getPositionAt(offset); // } protected _textSpanToRange( - model: monaco.editor.ITextModel, + model: editor.ITextModel, span: ts.TextSpan - ): monaco.IRange { + ): IRange { let p1 = model.getPositionAt(span.start); let p2 = model.getPositionAt(span.start + span.length); let { lineNumber: startLineNumber, column: startColumn } = p1; @@ -112,13 +118,13 @@ export class LibFiles { return false; } - public getOrCreateModel(uri: Uri): monaco.editor.ITextModel | null { - const model = monaco.editor.getModel(uri); + public getOrCreateModel(uri: Uri): editor.ITextModel | null { + const model = editor.getModel(uri); if (model) { return model; } if (this.isLibFile(uri) && this._hasFetchedLibFiles) { - return monaco.editor.createModel( + return editor.createModel( this._libFiles[uri.path.slice(1)], 'javascript', uri @@ -172,13 +178,13 @@ export class DiagnosticsAdapter extends Adapter { constructor( private readonly _libFiles: LibFiles, - private _defaults: LanguageServiceDefaultsImpl, + private _defaults: LanguageServiceDefaults, private _selector: string, worker: (...uris: Uri[]) => Promise ) { super(worker); - const onModelAdd = (model: monaco.editor.IModel): void => { + const onModelAdd = (model: editor.IModel): void => { if (model.getModeId() !== _selector) { return; } @@ -199,8 +205,8 @@ export class DiagnosticsAdapter extends Adapter { this._doValidate(model); }; - const onModelRemoved = (model: monaco.editor.IModel): void => { - monaco.editor.setModelMarkers(model, this._selector, []); + const onModelRemoved = (model: editor.IModel): void => { + editor.setModelMarkers(model, this._selector, []); const key = model.uri.toString(); if (this._listener[key]) { this._listener[key].dispose(); @@ -208,10 +214,10 @@ export class DiagnosticsAdapter extends Adapter { } }; - this._disposables.push(monaco.editor.onDidCreateModel(onModelAdd)); - this._disposables.push(monaco.editor.onWillDisposeModel(onModelRemoved)); + this._disposables.push(editor.onDidCreateModel(onModelAdd)); + this._disposables.push(editor.onWillDisposeModel(onModelRemoved)); this._disposables.push( - monaco.editor.onDidChangeModelLanguage((event) => { + editor.onDidChangeModelLanguage((event) => { onModelRemoved(event.model); onModelAdd(event.model); }) @@ -219,7 +225,7 @@ export class DiagnosticsAdapter extends Adapter { this._disposables.push({ dispose() { - for (const model of monaco.editor.getModels()) { + for (const model of editor.getModels()) { onModelRemoved(model); } } @@ -227,7 +233,7 @@ export class DiagnosticsAdapter extends Adapter { const recomputeDiagostics = () => { // redo diagnostics when options change - for (const model of monaco.editor.getModels()) { + for (const model of editor.getModels()) { onModelRemoved(model); onModelAdd(model); } @@ -237,7 +243,7 @@ export class DiagnosticsAdapter extends Adapter { this._defaults.onDidExtraLibsChange(recomputeDiagostics) ); - monaco.editor.getModels().forEach(onModelAdd); + editor.getModels().forEach(onModelAdd); } public dispose(): void { @@ -245,7 +251,7 @@ export class DiagnosticsAdapter extends Adapter { this._disposables = []; } - private async _doValidate(model: monaco.editor.ITextModel): Promise { + private async _doValidate(model: editor.ITextModel): Promise { const worker = await this._worker(model.uri); if (model.isDisposed()) { @@ -291,7 +297,7 @@ export class DiagnosticsAdapter extends Adapter { .reduce((p, c) => c.concat(p), []) .map((relatedInformation) => relatedInformation.file - ? monaco.Uri.parse(relatedInformation.file.fileName) + ? Uri.parse(relatedInformation.file.fileName) : null ); @@ -302,7 +308,7 @@ export class DiagnosticsAdapter extends Adapter { return; } - monaco.editor.setModelMarkers( + editor.setModelMarkers( model, this._selector, diagnostics.map((d) => this._convertDiagnostics(model, d)) @@ -310,9 +316,9 @@ export class DiagnosticsAdapter extends Adapter { } private _convertDiagnostics( - model: monaco.editor.ITextModel, + model: editor.ITextModel, diag: ts.Diagnostic - ): monaco.editor.IMarkerData { + ): editor.IMarkerData { const diagStart = diag.start || 0; const diagLength = diag.length || 1; const { @@ -332,7 +338,7 @@ export class DiagnosticsAdapter extends Adapter { endColumn, message: flattenDiagnosticMessageText(diag.messageText, '\n'), code: diag.code.toString(), - tags: diag.reportsUnnecessary ? [monaco.MarkerTag.Unnecessary] : [], + tags: diag.reportsUnnecessary ? [MarkerTag.Unnecessary] : [], relatedInformation: this._convertRelatedInformation( model, diag.relatedInformation @@ -341,18 +347,18 @@ export class DiagnosticsAdapter extends Adapter { } private _convertRelatedInformation( - model: monaco.editor.ITextModel, + model: editor.ITextModel, relatedInformation?: ts.DiagnosticRelatedInformation[] - ): monaco.editor.IRelatedInformation[] | undefined { + ): editor.IRelatedInformation[] | undefined { if (!relatedInformation) { return; } - const result: monaco.editor.IRelatedInformation[] = []; + const result: editor.IRelatedInformation[] = []; relatedInformation.forEach((info) => { - let relatedResource: monaco.editor.ITextModel | null = model; + let relatedResource: editor.ITextModel | null = model; if (info.file) { - const relatedResourceUri = monaco.Uri.parse(info.file.fileName); + const relatedResourceUri = Uri.parse(info.file.fileName); relatedResource = this._libFiles.getOrCreateModel(relatedResourceUri); } @@ -384,24 +390,24 @@ export class DiagnosticsAdapter extends Adapter { private _tsDiagnosticCategoryToMarkerSeverity( category: ts.DiagnosticCategory - ): monaco.MarkerSeverity { + ): MarkerSeverity { switch (category) { case DiagnosticCategory.Error: - return monaco.MarkerSeverity.Error; + return MarkerSeverity.Error; case DiagnosticCategory.Message: - return monaco.MarkerSeverity.Info; + return MarkerSeverity.Info; case DiagnosticCategory.Warning: - return monaco.MarkerSeverity.Warning; + return MarkerSeverity.Warning; case DiagnosticCategory.Suggestion: - return monaco.MarkerSeverity.Hint; + return MarkerSeverity.Hint; } - return monaco.MarkerSeverity.Info; + return MarkerSeverity.Info; } } // --- suggest ------ -interface MyCompletionItem extends monaco.languages.CompletionItem { +interface MyCompletionItem extends languages.CompletionItem { label: string; uri: Uri; position: Position; @@ -409,17 +415,17 @@ interface MyCompletionItem extends monaco.languages.CompletionItem { export class SuggestAdapter extends Adapter - implements monaco.languages.CompletionItemProvider { + implements languages.CompletionItemProvider { public get triggerCharacters(): string[] { return ['.']; } public async provideCompletionItems( - model: monaco.editor.ITextModel, + model: editor.ITextModel, position: Position, - _context: monaco.languages.CompletionContext, + _context: languages.CompletionContext, token: CancellationToken - ): Promise { + ): Promise { const wordInfo = model.getWordUntilPosition(position); const wordRange = new Range( position.lineNumber, @@ -467,11 +473,11 @@ export class SuggestAdapter } public async resolveCompletionItem( - model: monaco.editor.ITextModel, + model: editor.ITextModel, _position: Position, - item: monaco.languages.CompletionItem, + item: languages.CompletionItem, token: CancellationToken - ): Promise { + ): Promise { const myItem = item; const resource = myItem.uri; const position = myItem.position; @@ -498,52 +504,50 @@ export class SuggestAdapter }; } - private static convertKind( - kind: string - ): monaco.languages.CompletionItemKind { + private static convertKind(kind: string): languages.CompletionItemKind { switch (kind) { case Kind.primitiveType: case Kind.keyword: - return monaco.languages.CompletionItemKind.Keyword; + return languages.CompletionItemKind.Keyword; case Kind.variable: case Kind.localVariable: - return monaco.languages.CompletionItemKind.Variable; + return languages.CompletionItemKind.Variable; case Kind.memberVariable: case Kind.memberGetAccessor: case Kind.memberSetAccessor: - return monaco.languages.CompletionItemKind.Field; + return languages.CompletionItemKind.Field; case Kind.function: case Kind.memberFunction: case Kind.constructSignature: case Kind.callSignature: case Kind.indexSignature: - return monaco.languages.CompletionItemKind.Function; + return languages.CompletionItemKind.Function; case Kind.enum: - return monaco.languages.CompletionItemKind.Enum; + return languages.CompletionItemKind.Enum; case Kind.module: - return monaco.languages.CompletionItemKind.Module; + return languages.CompletionItemKind.Module; case Kind.class: - return monaco.languages.CompletionItemKind.Class; + return languages.CompletionItemKind.Class; case Kind.interface: - return monaco.languages.CompletionItemKind.Interface; + return languages.CompletionItemKind.Interface; case Kind.warning: - return monaco.languages.CompletionItemKind.File; + return languages.CompletionItemKind.File; } - return monaco.languages.CompletionItemKind.Property; + return languages.CompletionItemKind.Property; } } export class SignatureHelpAdapter extends Adapter - implements monaco.languages.SignatureHelpProvider { + implements languages.SignatureHelpProvider { public signatureHelpTriggerCharacters = ['(', ',']; public async provideSignatureHelp( - model: monaco.editor.ITextModel, + model: editor.ITextModel, position: Position, token: CancellationToken - ): Promise { + ): Promise { const resource = model.uri; const offset = model.getOffsetAt(position); const worker = await this._worker(resource); @@ -556,14 +560,14 @@ export class SignatureHelpAdapter return; } - const ret: monaco.languages.SignatureHelp = { + const ret: languages.SignatureHelp = { activeSignature: info.selectedItemIndex, activeParameter: info.argumentIndex, signatures: [] }; info.items.forEach((item) => { - const signature: monaco.languages.SignatureInformation = { + const signature: languages.SignatureInformation = { label: '', parameters: [] }; @@ -574,7 +578,7 @@ export class SignatureHelpAdapter signature.label += displayPartsToString(item.prefixDisplayParts); item.parameters.forEach((p, i, a) => { const label = displayPartsToString(p.displayParts); - const parameter: monaco.languages.ParameterInformation = { + const parameter: languages.ParameterInformation = { label: label, documentation: { value: displayPartsToString(p.documentation) @@ -601,12 +605,12 @@ export class SignatureHelpAdapter export class QuickInfoAdapter extends Adapter - implements monaco.languages.HoverProvider { + implements languages.HoverProvider { public async provideHover( - model: monaco.editor.ITextModel, + model: editor.ITextModel, position: Position, token: CancellationToken - ): Promise { + ): Promise { const resource = model.uri; const offset = model.getOffsetAt(position); const worker = await this._worker(resource); @@ -653,12 +657,12 @@ export class QuickInfoAdapter export class OccurrencesAdapter extends Adapter - implements monaco.languages.DocumentHighlightProvider { + implements languages.DocumentHighlightProvider { public async provideDocumentHighlights( - model: monaco.editor.ITextModel, + model: editor.ITextModel, position: Position, token: CancellationToken - ): Promise { + ): Promise { const resource = model.uri; const offset = model.getOffsetAt(position); const worker = await this._worker(resource); @@ -672,11 +676,11 @@ export class OccurrencesAdapter } return entries.map((entry) => { - return { + return { range: this._textSpanToRange(model, entry.textSpan), kind: entry.isWriteAccess - ? monaco.languages.DocumentHighlightKind.Write - : monaco.languages.DocumentHighlightKind.Text + ? languages.DocumentHighlightKind.Write + : languages.DocumentHighlightKind.Text }; }); } @@ -693,10 +697,10 @@ export class DefinitionAdapter extends Adapter { } public async provideDefinition( - model: monaco.editor.ITextModel, + model: editor.ITextModel, position: Position, token: CancellationToken - ): Promise { + ): Promise { const resource = model.uri; const offset = model.getOffsetAt(position); const worker = await this._worker(resource); @@ -718,7 +722,7 @@ export class DefinitionAdapter extends Adapter { return; } - const result: monaco.languages.Location[] = []; + const result: languages.Location[] = []; for (let entry of entries) { const uri = Uri.parse(entry.fileName); const refModel = this._libFiles.getOrCreateModel(uri); @@ -737,7 +741,7 @@ export class DefinitionAdapter extends Adapter { export class ReferenceAdapter extends Adapter - implements monaco.languages.ReferenceProvider { + implements languages.ReferenceProvider { constructor( private readonly _libFiles: LibFiles, worker: (...uris: Uri[]) => Promise @@ -746,11 +750,11 @@ export class ReferenceAdapter } public async provideReferences( - model: monaco.editor.ITextModel, + model: editor.ITextModel, position: Position, - context: monaco.languages.ReferenceContext, + context: languages.ReferenceContext, token: CancellationToken - ): Promise { + ): Promise { const resource = model.uri; const offset = model.getOffsetAt(position); const worker = await this._worker(resource); @@ -772,7 +776,7 @@ export class ReferenceAdapter return; } - const result: monaco.languages.Location[] = []; + const result: languages.Location[] = []; for (let entry of entries) { const uri = Uri.parse(entry.fileName); const refModel = this._libFiles.getOrCreateModel(uri); @@ -791,11 +795,11 @@ export class ReferenceAdapter export class OutlineAdapter extends Adapter - implements monaco.languages.DocumentSymbolProvider { + implements languages.DocumentSymbolProvider { public async provideDocumentSymbols( - model: monaco.editor.ITextModel, + model: editor.ITextModel, token: CancellationToken - ): Promise { + ): Promise { const resource = model.uri; const worker = await this._worker(resource); const items = await worker.getNavigationBarItems(resource.toString()); @@ -805,15 +809,15 @@ export class OutlineAdapter } const convert = ( - bucket: monaco.languages.DocumentSymbol[], + bucket: languages.DocumentSymbol[], item: ts.NavigationBarItem, containerLabel?: string ): void => { - let result: monaco.languages.DocumentSymbol = { + let result: languages.DocumentSymbol = { name: item.text, detail: '', - kind: ( - (outlineTypeTable[item.kind] || monaco.languages.SymbolKind.Variable) + kind: ( + (outlineTypeTable[item.kind] || languages.SymbolKind.Variable) ), range: this._textSpanToRange(model, item.spans[0]), selectionRange: this._textSpanToRange(model, item.spans[0]), @@ -830,7 +834,7 @@ export class OutlineAdapter bucket.push(result); }; - let result: monaco.languages.DocumentSymbol[] = []; + let result: languages.DocumentSymbol[] = []; items.forEach((item) => convert(result, item)); return result; } @@ -868,28 +872,28 @@ export class Kind { } let outlineTypeTable: { - [kind: string]: monaco.languages.SymbolKind; + [kind: string]: languages.SymbolKind; } = Object.create(null); -outlineTypeTable[Kind.module] = monaco.languages.SymbolKind.Module; -outlineTypeTable[Kind.class] = monaco.languages.SymbolKind.Class; -outlineTypeTable[Kind.enum] = monaco.languages.SymbolKind.Enum; -outlineTypeTable[Kind.interface] = monaco.languages.SymbolKind.Interface; -outlineTypeTable[Kind.memberFunction] = monaco.languages.SymbolKind.Method; -outlineTypeTable[Kind.memberVariable] = monaco.languages.SymbolKind.Property; -outlineTypeTable[Kind.memberGetAccessor] = monaco.languages.SymbolKind.Property; -outlineTypeTable[Kind.memberSetAccessor] = monaco.languages.SymbolKind.Property; -outlineTypeTable[Kind.variable] = monaco.languages.SymbolKind.Variable; -outlineTypeTable[Kind.const] = monaco.languages.SymbolKind.Variable; -outlineTypeTable[Kind.localVariable] = monaco.languages.SymbolKind.Variable; -outlineTypeTable[Kind.variable] = monaco.languages.SymbolKind.Variable; -outlineTypeTable[Kind.function] = monaco.languages.SymbolKind.Function; -outlineTypeTable[Kind.localFunction] = monaco.languages.SymbolKind.Function; +outlineTypeTable[Kind.module] = languages.SymbolKind.Module; +outlineTypeTable[Kind.class] = languages.SymbolKind.Class; +outlineTypeTable[Kind.enum] = languages.SymbolKind.Enum; +outlineTypeTable[Kind.interface] = languages.SymbolKind.Interface; +outlineTypeTable[Kind.memberFunction] = languages.SymbolKind.Method; +outlineTypeTable[Kind.memberVariable] = languages.SymbolKind.Property; +outlineTypeTable[Kind.memberGetAccessor] = languages.SymbolKind.Property; +outlineTypeTable[Kind.memberSetAccessor] = languages.SymbolKind.Property; +outlineTypeTable[Kind.variable] = languages.SymbolKind.Variable; +outlineTypeTable[Kind.const] = languages.SymbolKind.Variable; +outlineTypeTable[Kind.localVariable] = languages.SymbolKind.Variable; +outlineTypeTable[Kind.variable] = languages.SymbolKind.Variable; +outlineTypeTable[Kind.function] = languages.SymbolKind.Function; +outlineTypeTable[Kind.localFunction] = languages.SymbolKind.Function; // --- formatting ---- export abstract class FormatHelper extends Adapter { protected static _convertOptions( - options: monaco.languages.FormattingOptions + options: languages.FormattingOptions ): ts.FormatCodeOptions { return { ConvertTabsToSpaces: options.insertSpaces, @@ -911,9 +915,9 @@ export abstract class FormatHelper extends Adapter { } protected _convertTextChanges( - model: monaco.editor.ITextModel, + model: editor.ITextModel, change: ts.TextChange - ): monaco.languages.TextEdit { + ): languages.TextEdit { return { text: change.newText, range: this._textSpanToRange(model, change.span) @@ -923,13 +927,13 @@ export abstract class FormatHelper extends Adapter { export class FormatAdapter extends FormatHelper - implements monaco.languages.DocumentRangeFormattingEditProvider { + implements languages.DocumentRangeFormattingEditProvider { public async provideDocumentRangeFormattingEdits( - model: monaco.editor.ITextModel, + model: editor.ITextModel, range: Range, - options: monaco.languages.FormattingOptions, + options: languages.FormattingOptions, token: CancellationToken - ): Promise { + ): Promise { const resource = model.uri; const startOffset = model.getOffsetAt({ lineNumber: range.startLineNumber, @@ -957,18 +961,18 @@ export class FormatAdapter export class FormatOnTypeAdapter extends FormatHelper - implements monaco.languages.OnTypeFormattingEditProvider { + implements languages.OnTypeFormattingEditProvider { get autoFormatTriggerCharacters() { return [';', '}', '\n']; } public async provideOnTypeFormattingEdits( - model: monaco.editor.ITextModel, + model: editor.ITextModel, position: Position, ch: string, - options: monaco.languages.FormattingOptions, + options: languages.FormattingOptions, token: CancellationToken - ): Promise { + ): Promise { const resource = model.uri; const offset = model.getOffsetAt(position); const worker = await this._worker(resource); @@ -991,13 +995,13 @@ export class FormatOnTypeAdapter export class CodeActionAdaptor extends FormatHelper - implements monaco.languages.CodeActionProvider { + implements languages.CodeActionProvider { public async provideCodeActions( - model: monaco.editor.ITextModel, + model: editor.ITextModel, range: Range, - context: monaco.languages.CodeActionContext, + context: languages.CodeActionContext, token: CancellationToken - ): Promise { + ): Promise { const resource = model.uri; const start = model.getOffsetAt({ lineNumber: range.startLineNumber, @@ -1041,11 +1045,11 @@ export class CodeActionAdaptor } private _tsCodeFixActionToMonacoCodeAction( - model: monaco.editor.ITextModel, - context: monaco.languages.CodeActionContext, + model: editor.ITextModel, + context: languages.CodeActionContext, codeFix: ts.CodeFixAction - ): monaco.languages.CodeAction { - const edits: monaco.languages.WorkspaceTextEdit[] = []; + ): languages.CodeAction { + const edits: languages.WorkspaceTextEdit[] = []; for (const change of codeFix.changes) { for (const textChange of change.textChanges) { edits.push({ @@ -1058,7 +1062,7 @@ export class CodeActionAdaptor } } - const action: monaco.languages.CodeAction = { + const action: languages.CodeAction = { title: codeFix.description, edit: { edits: edits }, diagnostics: context.markers, @@ -1070,17 +1074,13 @@ export class CodeActionAdaptor } // --- rename ---- -export class RenameAdapter - extends Adapter - implements monaco.languages.RenameProvider { +export class RenameAdapter extends Adapter implements languages.RenameProvider { public async provideRenameEdits( - model: monaco.editor.ITextModel, + model: editor.ITextModel, position: Position, newName: string, token: CancellationToken - ): Promise< - (monaco.languages.WorkspaceEdit & monaco.languages.Rejection) | undefined - > { + ): Promise<(languages.WorkspaceEdit & languages.Rejection) | undefined> { const resource = model.uri; const fileName = resource.toString(); const offset = model.getOffsetAt(position); @@ -1112,10 +1112,10 @@ export class RenameAdapter return; } - const edits: monaco.languages.WorkspaceTextEdit[] = []; + const edits: languages.WorkspaceTextEdit[] = []; for (const renameLocation of renameLocations) { edits.push({ - resource: monaco.Uri.parse(renameLocation.fileName), + resource: Uri.parse(renameLocation.fileName), edit: { range: this._textSpanToRange(model, renameLocation.textSpan), text: newName diff --git a/src/lib/editor.worker.d.ts b/src/lib/editor.worker.d.ts index 9481c11..1be75b8 100644 --- a/src/lib/editor.worker.d.ts +++ b/src/lib/editor.worker.d.ts @@ -1,8 +1,8 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'monaco-editor-core/esm/vs/editor/editor.worker' { - export function initialize(callback: (ctx: monaco.worker.IWorkerContext, createData: any) => any): void; -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'monaco-editor-core/esm/vs/editor/editor.worker' { + export function initialize(callback: (ctx: any, createData: any) => any): void; +} diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index c1a7240..5c18e1b 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -4,16 +4,171 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import * as mode from './tsMode'; -import { typescriptVersion } from './lib/typescriptServicesMetadata'; // do not import the whole typescriptServices here +import type * as mode from './tsMode'; +import { typescriptVersion as tsversion } from './lib/typescriptServicesMetadata'; // do not import the whole typescriptServices here +import { + languages, + Emitter, + IEvent, + IDisposable, + Uri +} from './fillers/monaco-editor-core'; -import Emitter = monaco.Emitter; -import IEvent = monaco.IEvent; -import IDisposable = monaco.IDisposable; +//#region enums copied from typescript to prevent loading the entire typescriptServices --- -// --- TypeScript configuration and defaults --------- +export enum ModuleKind { + None = 0, + CommonJS = 1, + AMD = 2, + UMD = 3, + System = 4, + ES2015 = 5, + ESNext = 99 +} + +export enum JsxEmit { + None = 0, + Preserve = 1, + React = 2, + ReactNative = 3 +} + +export enum NewLineKind { + CarriageReturnLineFeed = 0, + LineFeed = 1 +} -export interface IExtraLib { +export enum ScriptTarget { + ES3 = 0, + ES5 = 1, + ES2015 = 2, + ES2016 = 3, + ES2017 = 4, + ES2018 = 5, + ES2019 = 6, + ES2020 = 7, + ESNext = 99, + JSON = 100, + Latest = ESNext +} + +export enum ModuleResolutionKind { + Classic = 1, + NodeJs = 2 +} +//#endregion + +interface MapLike { + [index: string]: T; +} + +type CompilerOptionsValue = + | string + | number + | boolean + | (string | number)[] + | string[] + | MapLike + | null + | undefined; + +interface CompilerOptions { + allowJs?: boolean; + allowSyntheticDefaultImports?: boolean; + allowUmdGlobalAccess?: boolean; + allowUnreachableCode?: boolean; + allowUnusedLabels?: boolean; + alwaysStrict?: boolean; + baseUrl?: string; + charset?: string; + checkJs?: boolean; + declaration?: boolean; + declarationMap?: boolean; + emitDeclarationOnly?: boolean; + declarationDir?: string; + disableSizeLimit?: boolean; + disableSourceOfProjectReferenceRedirect?: boolean; + downlevelIteration?: boolean; + emitBOM?: boolean; + emitDecoratorMetadata?: boolean; + experimentalDecorators?: boolean; + forceConsistentCasingInFileNames?: boolean; + importHelpers?: boolean; + inlineSourceMap?: boolean; + inlineSources?: boolean; + isolatedModules?: boolean; + jsx?: JsxEmit; + keyofStringsOnly?: boolean; + lib?: string[]; + locale?: string; + mapRoot?: string; + maxNodeModuleJsDepth?: number; + module?: ModuleKind; + moduleResolution?: ModuleResolutionKind; + newLine?: NewLineKind; + noEmit?: boolean; + noEmitHelpers?: boolean; + noEmitOnError?: boolean; + noErrorTruncation?: boolean; + noFallthroughCasesInSwitch?: boolean; + noImplicitAny?: boolean; + noImplicitReturns?: boolean; + noImplicitThis?: boolean; + noStrictGenericChecks?: boolean; + noUnusedLocals?: boolean; + noUnusedParameters?: boolean; + noImplicitUseStrict?: boolean; + noLib?: boolean; + noResolve?: boolean; + out?: string; + outDir?: string; + outFile?: string; + paths?: MapLike; + preserveConstEnums?: boolean; + preserveSymlinks?: boolean; + project?: string; + reactNamespace?: string; + jsxFactory?: string; + composite?: boolean; + removeComments?: boolean; + rootDir?: string; + rootDirs?: string[]; + skipLibCheck?: boolean; + skipDefaultLibCheck?: boolean; + sourceMap?: boolean; + sourceRoot?: string; + strict?: boolean; + strictFunctionTypes?: boolean; + strictBindCallApply?: boolean; + strictNullChecks?: boolean; + strictPropertyInitialization?: boolean; + stripInternal?: boolean; + suppressExcessPropertyErrors?: boolean; + suppressImplicitAnyIndexErrors?: boolean; + target?: ScriptTarget; + traceResolution?: boolean; + resolveJsonModule?: boolean; + types?: string[]; + /** Paths used to compute primary types search locations */ + typeRoots?: string[]; + esModuleInterop?: boolean; + useDefineForClassFields?: boolean; + [option: string]: CompilerOptionsValue | undefined; +} + +export interface DiagnosticsOptions { + noSemanticValidation?: boolean; + noSyntaxValidation?: boolean; + noSuggestionDiagnostics?: boolean; + diagnosticCodesToIgnore?: number[]; +} + +export interface WorkerOptions { + /** A full HTTP path to a JavaScript file which adds a function `customTSWorkerFactory` to the self inside a web-worker */ + customWorkerPath?: string; +} + +interface IExtraLib { content: string; version: number; } @@ -22,22 +177,305 @@ export interface IExtraLibs { [path: string]: IExtraLib; } -export class LanguageServiceDefaultsImpl - implements monaco.languages.typescript.LanguageServiceDefaults { +/** + * A linked list of formatted diagnostic messages to be used as part of a multiline message. + * It is built from the bottom up, leaving the head to be the "main" diagnostic. + */ +interface DiagnosticMessageChain { + messageText: string; + /** Diagnostic category: warning = 0, error = 1, suggestion = 2, message = 3 */ + category: 0 | 1 | 2 | 3; + code: number; + next?: DiagnosticMessageChain[]; +} +export interface Diagnostic extends DiagnosticRelatedInformation { + /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ + reportsUnnecessary?: {}; + source?: string; + relatedInformation?: DiagnosticRelatedInformation[]; +} +interface DiagnosticRelatedInformation { + /** Diagnostic category: warning = 0, error = 1, suggestion = 2, message = 3 */ + category: 0 | 1 | 2 | 3; + code: number; + /** TypeScriptWorker removes this to avoid serializing circular JSON structures. */ + file: undefined; + start: number | undefined; + length: number | undefined; + messageText: string | DiagnosticMessageChain; +} + +interface EmitOutput { + outputFiles: OutputFile[]; + emitSkipped: boolean; +} +interface OutputFile { + name: string; + writeByteOrderMark: boolean; + text: string; +} + +export interface LanguageServiceDefaults { + /** + * Event fired when compiler options or diagnostics options are changed. + */ + readonly onDidChange: IEvent; + + /** + * Event fired when extra libraries registered with the language service change. + */ + readonly onDidExtraLibsChange: IEvent; + + readonly workerOptions: WorkerOptions; + + /** + * Get the current extra libs registered with the language service. + */ + getExtraLibs(): IExtraLibs; + + /** + * Add an additional source file to the language service. Use this + * for typescript (definition) files that won't be loaded as editor + * documents, like `jquery.d.ts`. + * + * @param content The file content + * @param filePath An optional file path + * @returns A disposable which will remove the file from the + * language service upon disposal. + */ + addExtraLib(content: string, filePath?: string): IDisposable; + + /** + * Remove all existing extra libs and set the additional source + * files to the language service. Use this for typescript definition + * files that won't be loaded as editor documents, like `jquery.d.ts`. + * @param libs An array of entries to register. + */ + setExtraLibs(libs: { content: string; filePath?: string }[]): void; + + /** + * Get current TypeScript compiler options for the language service. + */ + getCompilerOptions(): CompilerOptions; + + /** + * Set TypeScript compiler options. + */ + setCompilerOptions(options: CompilerOptions): void; + + /** + * Get the current diagnostics options for the language service. + */ + getDiagnosticsOptions(): DiagnosticsOptions; + + /** + * Configure whether syntactic and/or semantic validation should + * be performed + */ + setDiagnosticsOptions(options: DiagnosticsOptions): void; + + /** + * No-op. + */ + setMaximumWorkerIdleTime(value: number): void; + + /** + * Configure if all existing models should be eagerly sync'd + * to the worker on start or restart. + */ + setEagerModelSync(value: boolean): void; + + /** + * Get the current setting for whether all existing models should be eagerly sync'd + * to the worker on start or restart. + */ + getEagerModelSync(): boolean; +} + +export interface TypeScriptWorker { + /** + * Get diagnostic messages for any syntax issues in the given file. + */ + getSyntacticDiagnostics(fileName: string): Promise; + + /** + * Get diagnostic messages for any semantic issues in the given file. + */ + getSemanticDiagnostics(fileName: string): Promise; + + /** + * Get diagnostic messages for any suggestions related to the given file. + */ + getSuggestionDiagnostics(fileName: string): Promise; + + /** + * Get the content of a given file. + */ + getScriptText(fileName: string): Promise; + + /** + * Get diagnostic messages related to the current compiler options. + * @param fileName Not used + */ + getCompilerOptionsDiagnostics(fileName: string): Promise; + + /** + * Get code completions for the given file and position. + * @returns `Promise` + */ + getCompletionsAtPosition( + fileName: string, + position: number + ): Promise; + + /** + * Get code completion details for the given file, position, and entry. + * @returns `Promise` + */ + getCompletionEntryDetails( + fileName: string, + position: number, + entry: string + ): Promise; + + /** + * Get signature help items for the item at the given file and position. + * @returns `Promise` + */ + getSignatureHelpItems( + fileName: string, + position: number + ): Promise; + + /** + * Get quick info for the item at the given position in the file. + * @returns `Promise` + */ + getQuickInfoAtPosition( + fileName: string, + position: number + ): Promise; + + /** + * Get other ranges which are related to the item at the given position in the file (often used for highlighting). + * @returns `Promise | undefined>` + */ + getOccurrencesAtPosition( + fileName: string, + position: number + ): Promise | undefined>; + + /** + * Get the definition of the item at the given position in the file. + * @returns `Promise | undefined>` + */ + getDefinitionAtPosition( + fileName: string, + position: number + ): Promise | undefined>; + + /** + * Get references to the item at the given position in the file. + * @returns `Promise` + */ + getReferencesAtPosition( + fileName: string, + position: number + ): Promise; + + /** + * Get outline entries for the item at the given position in the file. + * @returns `Promise` + */ + getNavigationBarItems(fileName: string): Promise; + + /** + * Get changes which should be applied to format the given file. + * @param options `typescript.FormatCodeOptions` + * @returns `Promise` + */ + getFormattingEditsForDocument(fileName: string, options: any): Promise; + + /** + * Get changes which should be applied to format the given range in the file. + * @param options `typescript.FormatCodeOptions` + * @returns `Promise` + */ + getFormattingEditsForRange( + fileName: string, + start: number, + end: number, + options: any + ): Promise; + + /** + * Get formatting changes which should be applied after the given keystroke. + * @param options `typescript.FormatCodeOptions` + * @returns `Promise` + */ + getFormattingEditsAfterKeystroke( + fileName: string, + postion: number, + ch: string, + options: any + ): Promise; + + /** + * Get other occurrences which should be updated when renaming the item at the given file and position. + * @returns `Promise` + */ + findRenameLocations( + fileName: string, + positon: number, + findInStrings: boolean, + findInComments: boolean, + providePrefixAndSuffixTextForRename: boolean + ): Promise; + + /** + * Get edits which should be applied to rename the item at the given file and position (or a failure reason). + * @param options `typescript.RenameInfoOptions` + * @returns `Promise` + */ + getRenameInfo(fileName: string, positon: number, options: any): Promise; + + /** + * Get transpiled output for the given file. + * @returns `typescript.EmitOutput` + */ + getEmitOutput(fileName: string): Promise; + + /** + * Get possible code fixes at the given position in the file. + * @param formatOptions `typescript.FormatCodeOptions` + * @returns `Promise>` + */ + getCodeFixesAtPosition( + fileName: string, + start: number, + end: number, + errorCodes: number[], + formatOptions: any + ): Promise>; +} + +// --- TypeScript configuration and defaults --------- + +class LanguageServiceDefaultsImpl implements LanguageServiceDefaults { private _onDidChange = new Emitter(); private _onDidExtraLibsChange = new Emitter(); private _extraLibs: IExtraLibs; private _eagerModelSync: boolean; - private _compilerOptions!: monaco.languages.typescript.CompilerOptions; - private _diagnosticsOptions!: monaco.languages.typescript.DiagnosticsOptions; - private _workerOptions!: monaco.languages.typescript.WorkerOptions; + private _compilerOptions!: CompilerOptions; + private _diagnosticsOptions!: DiagnosticsOptions; + private _workerOptions!: WorkerOptions; private _onDidExtraLibsChangeTimeout: number; constructor( - compilerOptions: monaco.languages.typescript.CompilerOptions, - diagnosticsOptions: monaco.languages.typescript.DiagnosticsOptions, - workerOptions: monaco.languages.typescript.WorkerOptions + compilerOptions: CompilerOptions, + diagnosticsOptions: DiagnosticsOptions, + workerOptions: WorkerOptions ) { this._extraLibs = Object.create(null); this._eagerModelSync = false; @@ -55,7 +493,7 @@ export class LanguageServiceDefaultsImpl return this._onDidExtraLibsChange.event; } - get workerOptions(): monaco.languages.typescript.WorkerOptions { + get workerOptions(): WorkerOptions { return this._workerOptions; } @@ -139,29 +577,25 @@ export class LanguageServiceDefaultsImpl }, 0); } - getCompilerOptions(): monaco.languages.typescript.CompilerOptions { + getCompilerOptions(): CompilerOptions { return this._compilerOptions; } - setCompilerOptions( - options: monaco.languages.typescript.CompilerOptions - ): void { + setCompilerOptions(options: CompilerOptions): void { this._compilerOptions = options || Object.create(null); this._onDidChange.fire(undefined); } - getDiagnosticsOptions(): monaco.languages.typescript.DiagnosticsOptions { + getDiagnosticsOptions(): DiagnosticsOptions { return this._diagnosticsOptions; } - setDiagnosticsOptions( - options: monaco.languages.typescript.DiagnosticsOptions - ): void { + setDiagnosticsOptions(options: DiagnosticsOptions): void { this._diagnosticsOptions = options || Object.create(null); this._onDidChange.fire(undefined); } - setWorkerOptions(options: monaco.languages.typescript.WorkerOptions): void { + setWorkerOptions(options: WorkerOptions): void { this._workerOptions = options || Object.create(null); this._onDidChange.fire(undefined); } @@ -179,94 +613,45 @@ export class LanguageServiceDefaultsImpl } } -//#region enums copied from typescript to prevent loading the entire typescriptServices --- - -enum ModuleKind { - None = 0, - CommonJS = 1, - AMD = 2, - UMD = 3, - System = 4, - ES2015 = 5, - ESNext = 99 -} - -enum JsxEmit { - None = 0, - Preserve = 1, - React = 2, - ReactNative = 3 -} - -enum NewLineKind { - CarriageReturnLineFeed = 0, - LineFeed = 1 -} +export const typescriptVersion: string = tsversion; -enum ScriptTarget { - ES3 = 0, - ES5 = 1, - ES2015 = 2, - ES2016 = 3, - ES2017 = 4, - ES2018 = 5, - ES2019 = 6, - ES2020 = 7, - ESNext = 99, - JSON = 100, - Latest = ESNext -} - -enum ModuleResolutionKind { - Classic = 1, - NodeJs = 2 -} -//#endregion - -const typescriptDefaults = new LanguageServiceDefaultsImpl( +export const typescriptDefaults: LanguageServiceDefaults = new LanguageServiceDefaultsImpl( { allowNonTsExtensions: true, target: ScriptTarget.Latest }, { noSemanticValidation: false, noSyntaxValidation: false }, {} ); -const javascriptDefaults = new LanguageServiceDefaultsImpl( +export const javascriptDefaults: LanguageServiceDefaults = new LanguageServiceDefaultsImpl( { allowNonTsExtensions: true, allowJs: true, target: ScriptTarget.Latest }, { noSemanticValidation: true, noSyntaxValidation: false }, {} ); -function getTypeScriptWorker(): Promise< - ( - ...uris: monaco.Uri[] - ) => Promise -> { +export const getTypeScriptWorker = (): Promise< + (...uris: Uri[]) => Promise +> => { return getMode().then((mode) => mode.getTypeScriptWorker()); -} +}; -function getJavaScriptWorker(): Promise< - ( - ...uris: monaco.Uri[] - ) => Promise -> { +export const getJavaScriptWorker = (): Promise< + (...uris: Uri[]) => Promise +> => { return getMode().then((mode) => mode.getJavaScriptWorker()); -} - -// Export API -function createAPI(): typeof monaco.languages.typescript { - return { - ModuleKind: ModuleKind, - JsxEmit: JsxEmit, - NewLineKind: NewLineKind, - ScriptTarget: ScriptTarget, - ModuleResolutionKind: ModuleResolutionKind, - typescriptVersion, - typescriptDefaults: typescriptDefaults, - javascriptDefaults: javascriptDefaults, - getTypeScriptWorker: getTypeScriptWorker, - getJavaScriptWorker: getJavaScriptWorker - }; -} -monaco.languages.typescript = createAPI(); +}; + +// export to the global based API +(languages).typescript = { + ModuleKind, + JsxEmit, + NewLineKind, + ScriptTarget, + ModuleResolutionKind, + typescriptVersion, + typescriptDefaults, + javascriptDefaults, + getTypeScriptWorker, + getJavaScriptWorker +}; // --- Registration to monaco editor --- @@ -274,9 +659,9 @@ function getMode(): Promise { return import('./tsMode'); } -monaco.languages.onLanguage('typescript', () => { +languages.onLanguage('typescript', () => { return getMode().then((mode) => mode.setupTypeScript(typescriptDefaults)); }); -monaco.languages.onLanguage('javascript', () => { +languages.onLanguage('javascript', () => { return getMode().then((mode) => mode.setupJavaScript(javascriptDefaults)); }); diff --git a/src/ts.worker.ts b/src/ts.worker.ts index ed202ec..3da5d69 100644 --- a/src/ts.worker.ts +++ b/src/ts.worker.ts @@ -4,14 +4,13 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import * as worker from 'monaco-editor-core/esm/vs/editor/editor.worker'; +import * as edworker from 'monaco-editor-core/esm/vs/editor/editor.worker'; import { TypeScriptWorker, ICreateData } from './tsWorker'; +import { worker } from './fillers/monaco-editor-core'; self.onmessage = () => { // ignore the first message - worker.initialize( - (ctx: monaco.worker.IWorkerContext, createData: ICreateData) => { - return new TypeScriptWorker(ctx, createData); - } - ); + edworker.initialize((ctx: worker.IWorkerContext, createData: ICreateData) => { + return new TypeScriptWorker(ctx, createData); + }); }; diff --git a/src/tsMode.ts b/src/tsMode.ts index 6ea613a..5fa7c6d 100644 --- a/src/tsMode.ts +++ b/src/tsMode.ts @@ -5,20 +5,19 @@ 'use strict'; import { WorkerManager } from './workerManager'; -import { TypeScriptWorker } from './tsWorker'; -import { LanguageServiceDefaultsImpl } from './monaco.contribution'; +import type { TypeScriptWorker } from './tsWorker'; +import { LanguageServiceDefaults } from './monaco.contribution'; import * as languageFeatures from './languageFeatures'; - -import Uri = monaco.Uri; +import { languages, Uri } from './fillers/monaco-editor-core'; let javaScriptWorker: (...uris: Uri[]) => Promise; let typeScriptWorker: (...uris: Uri[]) => Promise; -export function setupTypeScript(defaults: LanguageServiceDefaultsImpl): void { +export function setupTypeScript(defaults: LanguageServiceDefaults): void { typeScriptWorker = setupMode(defaults, 'typescript'); } -export function setupJavaScript(defaults: LanguageServiceDefaultsImpl): void { +export function setupJavaScript(defaults: LanguageServiceDefaults): void { javaScriptWorker = setupMode(defaults, 'javascript'); } @@ -47,7 +46,7 @@ export function getTypeScriptWorker(): Promise< } function setupMode( - defaults: LanguageServiceDefaultsImpl, + defaults: LanguageServiceDefaults, modeId: string ): (...uris: Uri[]) => Promise { const client = new WorkerManager(modeId, defaults); @@ -57,47 +56,47 @@ function setupMode( const libFiles = new languageFeatures.LibFiles(worker); - monaco.languages.registerCompletionItemProvider( + languages.registerCompletionItemProvider( modeId, new languageFeatures.SuggestAdapter(worker) ); - monaco.languages.registerSignatureHelpProvider( + languages.registerSignatureHelpProvider( modeId, new languageFeatures.SignatureHelpAdapter(worker) ); - monaco.languages.registerHoverProvider( + languages.registerHoverProvider( modeId, new languageFeatures.QuickInfoAdapter(worker) ); - monaco.languages.registerDocumentHighlightProvider( + languages.registerDocumentHighlightProvider( modeId, new languageFeatures.OccurrencesAdapter(worker) ); - monaco.languages.registerDefinitionProvider( + languages.registerDefinitionProvider( modeId, new languageFeatures.DefinitionAdapter(libFiles, worker) ); - monaco.languages.registerReferenceProvider( + languages.registerReferenceProvider( modeId, new languageFeatures.ReferenceAdapter(libFiles, worker) ); - monaco.languages.registerDocumentSymbolProvider( + languages.registerDocumentSymbolProvider( modeId, new languageFeatures.OutlineAdapter(worker) ); - monaco.languages.registerDocumentRangeFormattingEditProvider( + languages.registerDocumentRangeFormattingEditProvider( modeId, new languageFeatures.FormatAdapter(worker) ); - monaco.languages.registerOnTypeFormattingEditProvider( + languages.registerOnTypeFormattingEditProvider( modeId, new languageFeatures.FormatOnTypeAdapter(worker) ); - monaco.languages.registerCodeActionProvider( + languages.registerCodeActionProvider( modeId, new languageFeatures.CodeActionAdaptor(worker) ); - monaco.languages.registerRenameProvider( + languages.registerRenameProvider( modeId, new languageFeatures.RenameAdapter(worker) ); diff --git a/src/tsWorker.ts b/src/tsWorker.ts index f4aacbe..4f68e1b 100644 --- a/src/tsWorker.ts +++ b/src/tsWorker.ts @@ -6,22 +6,23 @@ import * as ts from './lib/typescriptServices'; import { libFileMap } from './lib/lib'; -import { IExtraLibs } from './monaco.contribution'; - -import IWorkerContext = monaco.worker.IWorkerContext; +import { + Diagnostic, + IExtraLibs, + TypeScriptWorker as ITypeScriptWorker +} from './monaco.contribution'; +import { worker } from './fillers/monaco-editor-core'; export class TypeScriptWorker - implements - ts.LanguageServiceHost, - monaco.languages.typescript.TypeScriptWorker { + implements ts.LanguageServiceHost, ITypeScriptWorker { // --- model sync ----------------------- - private _ctx: IWorkerContext; + private _ctx: worker.IWorkerContext; private _extraLibs: IExtraLibs = Object.create(null); private _languageService = ts.createLanguageService(this); private _compilerOptions: ts.CompilerOptions; - constructor(ctx: IWorkerContext, createData: ICreateData) { + constructor(ctx: worker.IWorkerContext, createData: ICreateData) { this._ctx = ctx; this._compilerOptions = createData.compilerOptions; this._extraLibs = createData.extraLibs; @@ -40,7 +41,7 @@ export class TypeScriptWorker return models.concat(Object.keys(this._extraLibs)); } - private _getModel(fileName: string): monaco.worker.IMirrorModel | null { + private _getModel(fileName: string): worker.IMirrorModel | null { let models = this._ctx.getMirrorModels(); for (let i = 0; i < models.length; i++) { if (models[i].uri.toString() === fileName) { @@ -159,9 +160,7 @@ export class TypeScriptWorker // --- language features - private static clearFiles( - diagnostics: ts.Diagnostic[] - ): monaco.languages.typescript.Diagnostic[] { + private static clearFiles(diagnostics: ts.Diagnostic[]): Diagnostic[] { // Clear the `file` field, which cannot be JSON'yfied because it // contains cyclic data structures. diagnostics.forEach((diag) => { @@ -171,35 +170,27 @@ export class TypeScriptWorker related.forEach((diag2) => (diag2.file = undefined)); } }); - return diagnostics; + return diagnostics; } - getSyntacticDiagnostics( - fileName: string - ): Promise { + getSyntacticDiagnostics(fileName: string): Promise { const diagnostics = this._languageService.getSyntacticDiagnostics(fileName); return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics)); } - getSemanticDiagnostics( - fileName: string - ): Promise { + getSemanticDiagnostics(fileName: string): Promise { const diagnostics = this._languageService.getSemanticDiagnostics(fileName); return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics)); } - getSuggestionDiagnostics( - fileName: string - ): Promise { + getSuggestionDiagnostics(fileName: string): Promise { const diagnostics = this._languageService.getSuggestionDiagnostics( fileName ); return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics)); } - getCompilerOptionsDiagnostics( - fileName: string - ): Promise { + getCompilerOptionsDiagnostics(fileName: string): Promise { const diagnostics = this._languageService.getCompilerOptionsDiagnostics(); return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics)); } @@ -404,7 +395,7 @@ declare global { } export function create( - ctx: IWorkerContext, + ctx: worker.IWorkerContext, createData: ICreateData ): TypeScriptWorker { let TSWorkerClass = TypeScriptWorker; diff --git a/src/tsconfig.esm.json b/src/tsconfig.esm.json index aa682be..07df409 100644 --- a/src/tsconfig.esm.json +++ b/src/tsconfig.esm.json @@ -2,7 +2,8 @@ "compilerOptions": { "module": "esnext", "moduleResolution": "node", - "outDir": "../release/esm", + "outDir": "../out/esm", + "declaration": true, "target": "es5", "lib": [ "dom", @@ -12,7 +13,5 @@ "es2015.promise" ], "strict": true - }, - "include": ["**/*.ts"], - "files": ["../node_modules/monaco-editor-core/monaco.d.ts"] + } } diff --git a/src/tsconfig.json b/src/tsconfig.json index 8160c13..ec3ce1e 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -2,7 +2,8 @@ "compilerOptions": { "module": "amd", "moduleResolution": "node", - "outDir": "../release/dev", + "outDir": "../out/amd", + "declaration": true, "target": "es5", "lib": [ "dom", @@ -12,7 +13,5 @@ "es2015.promise" ], "strict": true - }, - "include": ["**/*.ts"], - "files": ["../node_modules/monaco-editor-core/monaco.d.ts"] + } } diff --git a/src/workerManager.ts b/src/workerManager.ts index 48f6bf8..a1ffccb 100644 --- a/src/workerManager.ts +++ b/src/workerManager.ts @@ -4,23 +4,21 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { LanguageServiceDefaultsImpl } from './monaco.contribution'; -import { TypeScriptWorker } from './tsWorker'; - -import IDisposable = monaco.IDisposable; -import Uri = monaco.Uri; +import { LanguageServiceDefaults } from './monaco.contribution'; +import type { TypeScriptWorker } from './tsWorker'; +import { editor, Uri, IDisposable } from './fillers/monaco-editor-core'; export class WorkerManager { private _modeId: string; - private _defaults: LanguageServiceDefaultsImpl; + private _defaults: LanguageServiceDefaults; private _configChangeListener: IDisposable; private _updateExtraLibsToken: number; private _extraLibsChangeListener: IDisposable; - private _worker: monaco.editor.MonacoWebWorker | null; + private _worker: editor.MonacoWebWorker | null; private _client: Promise | null; - constructor(modeId: string, defaults: LanguageServiceDefaultsImpl) { + constructor(modeId: string, defaults: LanguageServiceDefaults) { this._modeId = modeId; this._defaults = defaults; this._worker = null; @@ -63,7 +61,7 @@ export class WorkerManager { private _getClient(): Promise { if (!this._client) { - this._worker = monaco.editor.createWebWorker({ + this._worker = editor.createWebWorker({ // module that exports the create() method and returns a `TypeScriptWorker` instance moduleId: 'vs/language/typescript/tsWorker', @@ -85,7 +83,7 @@ export class WorkerManager { p = p.then((worker) => { if (this._worker) { return this._worker.withSyncedResources( - monaco.editor + editor .getModels() .filter((model) => model.getModeId() === this._modeId) .map((model) => model.uri) diff --git a/test/custom-worker.html b/test/custom-worker.html index e21e291..35ecb23 100644 --- a/test/custom-worker.html +++ b/test/custom-worker.html @@ -31,7 +31,9 @@

Custom webworker

diff --git a/test/index.html b/test/index.html index 4cb3d09..e28d745 100644 --- a/test/index.html +++ b/test/index.html @@ -18,7 +18,7 @@

Compiler settings

@@ -178,7 +178,7 @@

Compiler settings

'vs/basic-languages/monaco.contribution', 'vs/language/typescript/monaco.contribution' ], () => { - var editor = monaco.editor.create(document.getElementById('container'), { + const editor = monaco.editor.create(document.getElementById('container'), { value: localStorage.getItem('code') || getDefaultCode(), language: 'typescript', lightbulb: { enabled: true } From a90fae67a58e1c2ac8516f99693b7c1ace712af5 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 18 Sep 2020 20:19:38 +0200 Subject: [PATCH 30/37] Fixes microsoft/monaco-editor#1937: Remove debugger statement --- scripts/importTypescript.js | 5 ++++- src/lib/typescriptServices-amd.js | 4 +++- src/lib/typescriptServices.js | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/importTypescript.js b/scripts/importTypescript.js index 6a551da..c5466e9 100644 --- a/scripts/importTypescript.js +++ b/scripts/importTypescript.js @@ -54,11 +54,14 @@ export const typescriptVersion = "${typeScriptDependencyVersion}";\n` /^( +)var result = ts\.sys\.require\(.*$/m, '$1// MONACOCHANGE\n$1var result = undefined;\n$1// END MONACOCHANGE' ); - tsServices = tsServices.replace( /^( +)fs = require\("fs"\);$/m, '$1// MONACOCHANGE\n$1fs = undefined;\n$1// END MONACOCHANGE' ); + tsServices = tsServices.replace( + /^( +)debugger;$/m, + '$1// MONACOCHANGE\n$1// debugger;\n$1// END MONACOCHANGE' + ); // Flag any new require calls (outside comments) so they can be corrected preemptively. // To avoid missing cases (or using an even more complex regex), temporarily remove comments diff --git a/src/lib/typescriptServices-amd.js b/src/lib/typescriptServices-amd.js index f02906c..88eaf23 100644 --- a/src/lib/typescriptServices-amd.js +++ b/src/lib/typescriptServices-amd.js @@ -2467,7 +2467,9 @@ var ts; return true; } function fail(message, stackCrawlMark) { - debugger; + // MONACOCHANGE + // debugger; + // END MONACOCHANGE var e = new Error(message ? "Debug Failure. " + message : "Debug Failure."); if (Error.captureStackTrace) { Error.captureStackTrace(e, stackCrawlMark || fail); diff --git a/src/lib/typescriptServices.js b/src/lib/typescriptServices.js index 04d0d82..b9e8fad 100644 --- a/src/lib/typescriptServices.js +++ b/src/lib/typescriptServices.js @@ -2467,7 +2467,9 @@ var ts; return true; } function fail(message, stackCrawlMark) { - debugger; + // MONACOCHANGE + // debugger; + // END MONACOCHANGE var e = new Error(message ? "Debug Failure. " + message : "Debug Failure."); if (Error.captureStackTrace) { Error.captureStackTrace(e, stackCrawlMark || fail); From 0f5ed5bee3793ceb9499b41650bb33781464b832 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 18 Sep 2020 20:22:52 +0200 Subject: [PATCH 31/37] Fixes microsoft/monaco-editor#1638: Protect against failing `getCodeFixesAtPosition` calls --- src/tsWorker.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/tsWorker.ts b/src/tsWorker.ts index f2a29fc..049234a 100644 --- a/src/tsWorker.ts +++ b/src/tsWorker.ts @@ -313,14 +313,18 @@ export class TypeScriptWorker implements ts.LanguageServiceHost, ITypeScriptWork formatOptions: ts.FormatCodeOptions ): Promise> { const preferences = {}; - return this._languageService.getCodeFixesAtPosition( - fileName, - start, - end, - errorCodes, - formatOptions, - preferences - ); + try { + return this._languageService.getCodeFixesAtPosition( + fileName, + start, + end, + errorCodes, + formatOptions, + preferences + ); + } catch { + return []; + } } async updateExtraLibs(extraLibs: IExtraLibs): Promise { From 6957b587fbf97aa9433649293a3892d84ca58ffc Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 18 Sep 2020 20:57:40 +0200 Subject: [PATCH 32/37] Fixes microsoft/monaco-editor#1998: make sure to always increase the version number --- src/monaco.contribution.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index 26eedbf..dff671f 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -448,6 +448,7 @@ class LanguageServiceDefaultsImpl implements LanguageServiceDefaults { private _onDidExtraLibsChange = new Emitter(); private _extraLibs: IExtraLibs; + private _removedExtraLibs: { [path: string]: number }; private _eagerModelSync: boolean; private _compilerOptions!: CompilerOptions; private _diagnosticsOptions!: DiagnosticsOptions; @@ -460,6 +461,7 @@ class LanguageServiceDefaultsImpl implements LanguageServiceDefaults { workerOptions: WorkerOptions ) { this._extraLibs = Object.create(null); + this._removedExtraLibs = Object.create(null); this._eagerModelSync = false; this.setCompilerOptions(compilerOptions); this.setDiagnosticsOptions(diagnosticsOptions); @@ -499,6 +501,9 @@ class LanguageServiceDefaultsImpl implements LanguageServiceDefaults { } let myVersion = 1; + if (this._removedExtraLibs[filePath]) { + myVersion = this._removedExtraLibs[filePath] + 1; + } if (this._extraLibs[filePath]) { myVersion = this._extraLibs[filePath].version + 1; } @@ -520,12 +525,16 @@ class LanguageServiceDefaultsImpl implements LanguageServiceDefaults { } delete this._extraLibs[filePath]; + this._removedExtraLibs[filePath] = myVersion; this._fireOnDidExtraLibsChangeSoon(); } }; } setExtraLibs(libs: { content: string; filePath?: string }[]): void { + for (const filePath in this._extraLibs) { + this._removedExtraLibs[filePath] = this._extraLibs[filePath].version; + } // clear out everything this._extraLibs = Object.create(null); @@ -534,9 +543,13 @@ class LanguageServiceDefaultsImpl implements LanguageServiceDefaults { const filePath = lib.filePath || `ts:extralib-${Math.random().toString(36).substring(2, 15)}`; const content = lib.content; + let myVersion = 1; + if (this._removedExtraLibs[filePath]) { + myVersion = this._removedExtraLibs[filePath] + 1; + } this._extraLibs[filePath] = { content: content, - version: 1 + version: myVersion }; } } From a2f4b783e7478cf270cd6ff1bb2f23b2cd49fd88 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 19 Sep 2020 01:36:03 +0200 Subject: [PATCH 33/37] Adopt latest `monaco-editor-core`, update to TS 4.0.3 --- package-lock.json | 24 ++++++++++++------------ package.json | 12 ++++++------ src/languageFeatures.ts | 8 ++++---- src/lib/typescriptServices-amd.js | 21 +++++++++++---------- src/lib/typescriptServices.js | 21 +++++++++++---------- src/lib/typescriptServicesMetadata.ts | 2 +- 6 files changed, 45 insertions(+), 43 deletions(-) diff --git a/package-lock.json b/package-lock.json index b531636..5678b42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -429,15 +429,15 @@ } }, "monaco-editor-core": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/monaco-editor-core/-/monaco-editor-core-0.20.0.tgz", - "integrity": "sha512-4mdmfEejTvRZzrEIn70jqqNl3g15vnkRdTkJ8uMK4jiljntlwhiSc5vknZOLt1QM8za16C3tDrSl2mTL9ma2Sg==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/monaco-editor-core/-/monaco-editor-core-0.21.0.tgz", + "integrity": "sha512-buBxOxwexlb7pdQrIHjj7OGmtVmEe735NN7M8GbhN03eYAKQmaeIAs1AyneOOHvbvh1Ntm7PoHH8f196gd2p6w==", "dev": true }, "monaco-languages": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/monaco-languages/-/monaco-languages-1.10.0.tgz", - "integrity": "sha512-ARAws17Xh0K4WsZYkJY6CqHn9EYdYN8CjzK6w/jgXIwU0owzCdUWxzu+FNJ/LeDLcKxL/YK3phcwGFj9IqX2yw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/monaco-languages/-/monaco-languages-2.1.0.tgz", + "integrity": "sha512-c2afUp3lbUCrvEdVrH8EThQYbj5tb2FpvXFnFN3gOJScDlFknWb5nbaxiaVqY9CSIqzJVZpNc5HXpP5FWOuS3g==", "dev": true }, "monaco-plugin-helpers": { @@ -721,9 +721,9 @@ } }, "terser": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.1.tgz", - "integrity": "sha512-yD80f4hdwCWTH5mojzxe1q8bN1oJbsK/vfJGLcPZM/fl+/jItIVNKhFIHqqR71OipFWMLgj3Kc+GIp6CeIqfnA==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.2.tgz", + "integrity": "sha512-H67sydwBz5jCUA32ZRL319ULu+Su1cAoZnnc+lXnenGRYWyLE3Scgkt8mNoAsMx0h5kdo758zdoS0LG9rYZXDQ==", "dev": true, "requires": { "commander": "^2.20.0", @@ -732,9 +732,9 @@ } }, "typescript": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", - "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz", + "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==", "dev": true }, "which": { diff --git a/package.json b/package.json index 7c90f73..c81bac1 100644 --- a/package.json +++ b/package.json @@ -25,14 +25,14 @@ "devDependencies": { "@typescript/vfs": "^1.3.0", "husky": "^4.3.0", - "monaco-editor-core": "^0.20.0", - "monaco-languages": "^1.10.0", + "monaco-editor-core": "^0.21.0", + "monaco-languages": "^2.1.0", "monaco-plugin-helpers": "^1.0.3", - "prettier": "^2.1.1", - "pretty-quick": "^3.0.0", + "prettier": "^2.1.2", + "pretty-quick": "^3.0.2", "requirejs": "^2.3.6", - "terser": "^5.3.0", - "typescript": "^4.0.2" + "terser": "^5.3.2", + "typescript": "^4.0.3" }, "husky": { "hooks": { diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index cb58f1f..4ac245a 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -387,6 +387,7 @@ interface MyCompletionItem extends languages.CompletionItem { label: string; uri: Uri; position: Position; + offset: number; } export class SuggestAdapter extends Adapter implements languages.CompletionItemProvider { @@ -433,6 +434,7 @@ export class SuggestAdapter extends Adapter implements languages.CompletionItemP return { uri: resource, position: position, + offset: offset, range: range, label: entry.name, insertText: entry.name, @@ -448,15 +450,13 @@ export class SuggestAdapter extends Adapter implements languages.CompletionItemP } public async resolveCompletionItem( - model: editor.ITextModel, - _position: Position, item: languages.CompletionItem, token: CancellationToken ): Promise { const myItem = item; const resource = myItem.uri; const position = myItem.position; - const offset = model.getOffsetAt(position); + const offset = myItem.offset; const worker = await this._worker(resource); const details = await worker.getCompletionEntryDetails( @@ -464,7 +464,7 @@ export class SuggestAdapter extends Adapter implements languages.CompletionItemP offset, myItem.label ); - if (!details || model.isDisposed()) { + if (!details) { return myItem; } return { diff --git a/src/lib/typescriptServices-amd.js b/src/lib/typescriptServices-amd.js index 88eaf23..bde8769 100644 --- a/src/lib/typescriptServices-amd.js +++ b/src/lib/typescriptServices-amd.js @@ -291,7 +291,7 @@ var ts; // If changing the text in this section, be sure to test `configurePrerelease` too. ts.versionMajorMinor = "4.0"; /** The version of the TypeScript compiler release */ - ts.version = "4.0.2"; + ts.version = "4.0.3"; /* @internal */ var Comparison; (function (Comparison) { @@ -70195,13 +70195,10 @@ var ts; function checkTupleType(node) { var elementTypes = node.elements; var seenOptionalElement = false; - var seenNamedElement = false; + var hasNamedElement = ts.some(elementTypes, ts.isNamedTupleMember); for (var i = 0; i < elementTypes.length; i++) { var e = elementTypes[i]; - if (e.kind === 191 /* NamedTupleMember */) { - seenNamedElement = true; - } - else if (seenNamedElement) { + if (e.kind !== 191 /* NamedTupleMember */ && hasNamedElement) { grammarErrorOnNode(e, ts.Diagnostics.Tuple_members_must_all_have_names_or_all_not_have_names); break; } @@ -112424,7 +112421,10 @@ var ts; var newImport = sortedNewImports_1[_i]; var insertionIndex = ts.OrganizeImports.getImportDeclarationInsertionIndex(existingImportStatements, newImport); if (insertionIndex === 0) { - changes.insertNodeBefore(sourceFile, existingImportStatements[0], newImport, /*blankLineBetween*/ false); + // If the first import is top-of-file, insert after the leading comment which is likely the header. + var options = existingImportStatements[0] === sourceFile.statements[0] ? + { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude } : {}; + changes.insertNodeBefore(sourceFile, existingImportStatements[0], newImport, /*blankLineBetween*/ false, options); } else { var prevImport = existingImportStatements[insertionIndex - 1]; @@ -129552,9 +129552,10 @@ var ts; this.insertNodeAt(sourceFile, parameters.pos, newParam); } }; - ChangeTracker.prototype.insertNodeBefore = function (sourceFile, before, newNode, blankLineBetween) { + ChangeTracker.prototype.insertNodeBefore = function (sourceFile, before, newNode, blankLineBetween, options) { if (blankLineBetween === void 0) { blankLineBetween = false; } - this.insertNodeAt(sourceFile, getAdjustedStartPosition(sourceFile, before, {}), newNode, this.getOptionsForInsertNodeBefore(before, newNode, blankLineBetween)); + if (options === void 0) { options = {}; } + this.insertNodeAt(sourceFile, getAdjustedStartPosition(sourceFile, before, options), newNode, this.getOptionsForInsertNodeBefore(before, newNode, blankLineBetween)); }; ChangeTracker.prototype.insertModifierBefore = function (sourceFile, modifier, before) { var pos = before.getStart(sourceFile); @@ -133422,7 +133423,7 @@ var ts; else if (existingSpecifiers === null || existingSpecifiers === void 0 ? void 0 : existingSpecifiers.length) { for (var _b = 0, newSpecifiers_2 = newSpecifiers; _b < newSpecifiers_2.length; _b++) { var spec = newSpecifiers_2[_b]; - changes.insertNodeAtEndOfList(sourceFile, existingSpecifiers, spec); + changes.insertNodeInListAfter(sourceFile, ts.last(existingSpecifiers), spec, existingSpecifiers); } } else { diff --git a/src/lib/typescriptServices.js b/src/lib/typescriptServices.js index b9e8fad..6f82120 100644 --- a/src/lib/typescriptServices.js +++ b/src/lib/typescriptServices.js @@ -291,7 +291,7 @@ var ts; // If changing the text in this section, be sure to test `configurePrerelease` too. ts.versionMajorMinor = "4.0"; /** The version of the TypeScript compiler release */ - ts.version = "4.0.2"; + ts.version = "4.0.3"; /* @internal */ var Comparison; (function (Comparison) { @@ -70195,13 +70195,10 @@ var ts; function checkTupleType(node) { var elementTypes = node.elements; var seenOptionalElement = false; - var seenNamedElement = false; + var hasNamedElement = ts.some(elementTypes, ts.isNamedTupleMember); for (var i = 0; i < elementTypes.length; i++) { var e = elementTypes[i]; - if (e.kind === 191 /* NamedTupleMember */) { - seenNamedElement = true; - } - else if (seenNamedElement) { + if (e.kind !== 191 /* NamedTupleMember */ && hasNamedElement) { grammarErrorOnNode(e, ts.Diagnostics.Tuple_members_must_all_have_names_or_all_not_have_names); break; } @@ -112424,7 +112421,10 @@ var ts; var newImport = sortedNewImports_1[_i]; var insertionIndex = ts.OrganizeImports.getImportDeclarationInsertionIndex(existingImportStatements, newImport); if (insertionIndex === 0) { - changes.insertNodeBefore(sourceFile, existingImportStatements[0], newImport, /*blankLineBetween*/ false); + // If the first import is top-of-file, insert after the leading comment which is likely the header. + var options = existingImportStatements[0] === sourceFile.statements[0] ? + { leadingTriviaOption: ts.textChanges.LeadingTriviaOption.Exclude } : {}; + changes.insertNodeBefore(sourceFile, existingImportStatements[0], newImport, /*blankLineBetween*/ false, options); } else { var prevImport = existingImportStatements[insertionIndex - 1]; @@ -129552,9 +129552,10 @@ var ts; this.insertNodeAt(sourceFile, parameters.pos, newParam); } }; - ChangeTracker.prototype.insertNodeBefore = function (sourceFile, before, newNode, blankLineBetween) { + ChangeTracker.prototype.insertNodeBefore = function (sourceFile, before, newNode, blankLineBetween, options) { if (blankLineBetween === void 0) { blankLineBetween = false; } - this.insertNodeAt(sourceFile, getAdjustedStartPosition(sourceFile, before, {}), newNode, this.getOptionsForInsertNodeBefore(before, newNode, blankLineBetween)); + if (options === void 0) { options = {}; } + this.insertNodeAt(sourceFile, getAdjustedStartPosition(sourceFile, before, options), newNode, this.getOptionsForInsertNodeBefore(before, newNode, blankLineBetween)); }; ChangeTracker.prototype.insertModifierBefore = function (sourceFile, modifier, before) { var pos = before.getStart(sourceFile); @@ -133422,7 +133423,7 @@ var ts; else if (existingSpecifiers === null || existingSpecifiers === void 0 ? void 0 : existingSpecifiers.length) { for (var _b = 0, newSpecifiers_2 = newSpecifiers; _b < newSpecifiers_2.length; _b++) { var spec = newSpecifiers_2[_b]; - changes.insertNodeAtEndOfList(sourceFile, existingSpecifiers, spec); + changes.insertNodeInListAfter(sourceFile, ts.last(existingSpecifiers), spec, existingSpecifiers); } } else { diff --git a/src/lib/typescriptServicesMetadata.ts b/src/lib/typescriptServicesMetadata.ts index 6bc0a4d..2d0fe9b 100644 --- a/src/lib/typescriptServicesMetadata.ts +++ b/src/lib/typescriptServicesMetadata.ts @@ -2,4 +2,4 @@ // **NOTE**: Do not edit directly! This file is generated using `npm run import-typescript` // -export const typescriptVersion = "4.0.2"; +export const typescriptVersion = "4.0.3"; From 694e39b5bfb4323fbb2ac7a66cabd51cc0cc247d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Sat, 19 Sep 2020 01:36:17 +0200 Subject: [PATCH 34/37] 4.1.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5678b42..42d5571 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "monaco-typescript", - "version": "4.0.2", + "version": "4.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c81bac1..0a11dda 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "monaco-typescript", - "version": "4.0.2", + "version": "4.1.0", "description": "TypeScript and JavaScript language support for Monaco Editor", "scripts": { "compile-amd": "mcopy ./src/lib/typescriptServices-amd.js ./out/amd/lib/typescriptServices.js && tsc -p ./src/tsconfig.json", From d57c6c64ea09caf66639e66d444235c0d56266d9 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 21 Sep 2020 09:54:16 -0400 Subject: [PATCH 35/37] Add missing setWorkerOptions --- src/monaco.contribution.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index dff671f..62db52a 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -268,6 +268,11 @@ export interface LanguageServiceDefaults { */ setDiagnosticsOptions(options: DiagnosticsOptions): void; + /** + * Configure webworker options + */ + setWorkerOptions(options: WorkerOptions): void; + /** * No-op. */ From 0f5f2da4dd43346e6e109f697746c3461cec20d4 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 21 Sep 2020 15:57:58 +0200 Subject: [PATCH 36/37] commit generated file --- monaco.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/monaco.d.ts b/monaco.d.ts index 3a048fe..88cdad2 100644 --- a/monaco.d.ts +++ b/monaco.d.ts @@ -244,6 +244,10 @@ declare namespace monaco.languages.typescript { * be performed */ setDiagnosticsOptions(options: DiagnosticsOptions): void; + /** + * Configure webworker options + */ + setWorkerOptions(options: WorkerOptions): void; /** * No-op. */ From a85c9036bdb5088db2397e41e9f833d07c5d8fb5 Mon Sep 17 00:00:00 2001 From: Pierre Guillaume Herveou Date: Mon, 21 Sep 2020 10:51:28 -0400 Subject: [PATCH 37/37] update upstream and enable regex --- .github/workflows/publish.yml | 2 +- package-lock.json | 2 +- package.json | 2 +- src/tsWorker.ts | 40 ++++++++++++++++++++++++++++++++++- test/index.html | 12 +++++++++++ 5 files changed, 54 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ce018a0..f1d5549 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -17,4 +17,4 @@ jobs: - run: npm ci - run: npm publish env: - NODE_AUTH_TOKEN: ${{secrets.npm_token}} \ No newline at end of file + NODE_AUTH_TOKEN: ${{secrets.npm_token}} diff --git a/package-lock.json b/package-lock.json index 42d5571..a401696 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "monaco-typescript", + "name": "@pgherveou/monaco-typescript", "version": "4.1.0", "lockfileVersion": 1, "requires": true, diff --git a/package.json b/package.json index 0a11dda..76d6e3b 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "monaco-typescript", + "name": "@pgherveou/monaco-typescript", "version": "4.1.0", "description": "TypeScript and JavaScript language support for Monaco Editor", "scripts": { diff --git a/src/tsWorker.ts b/src/tsWorker.ts index 049234a..bb27790 100644 --- a/src/tsWorker.ts +++ b/src/tsWorker.ts @@ -11,7 +11,7 @@ import { IExtraLibs, TypeScriptWorker as ITypeScriptWorker } from './monaco.contribution'; -import { worker } from './fillers/monaco-editor-core'; +import { IRange, worker } from './fillers/monaco-editor-core'; export class TypeScriptWorker implements ts.LanguageServiceHost, ITypeScriptWorker { // --- model sync ----------------------- @@ -171,6 +171,44 @@ export class TypeScriptWorker implements ts.LanguageServiceHost, ITypeScriptWork return diagnostics; } + getTopLevelExpressions(fileName: string, search: string | RegExp): Promise { + const ranges: IRange[] = []; + + const matcher: (node: ts.Node) => boolean = (() => { + const textMatch: (text: string) => boolean = + typeof search === 'string' ? (text) => text === search : (text) => search.test(text); + + return (node: ts.Node) => { + if (node.kind !== ts.SyntaxKind.ExpressionStatement) { + return false; + } + + const text = ((node as ts.ExpressionStatement).expression as + | ts.CallExpression + | undefined)?.expression?.getText(); + return text ? textMatch(text) : false; + }; + })(); + + const sourceFile = this._languageService.getProgram()?.getSourceFile(fileName); + if (sourceFile) { + sourceFile.forEachChild((node) => { + if (matcher(node)) { + const start = sourceFile.getLineAndCharacterOfPosition(node.getStart()); + const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd()); + ranges.push({ + startLineNumber: start.line + 1, + startColumn: start.character + 1, + endLineNumber: end.line + 1, + endColumn: end.character + 1 + }); + } + }); + } + + return Promise.resolve(ranges); + } + async getSyntacticDiagnostics(fileName: string): Promise { const diagnostics = this._languageService.getSyntacticDiagnostics(fileName); return TypeScriptWorker.clearFiles(diagnostics); diff --git a/test/index.html b/test/index.html index e28d745..a3b89fd 100644 --- a/test/index.html +++ b/test/index.html @@ -12,6 +12,7 @@

Monaco Editor TypeScript test page

+

Compiler settings


@@ -193,6 +194,17 @@

Compiler settings

editor.setValue(getDefaultCode()); }; + document.getElementById('testBtn').onclick = async () => { + const uri = editor.getModel().uri; + const worker = await monaco.languages.typescript.getTypeScriptWorker(); + const client = await worker(uri); + + const expressions = await client + .getTopLevelExpressions(uri.toString(), /test|bar/) + .catch(() => ''); + console.log(expressions); + }; + const optsString = localStorage.getItem('compiler-opts') || JSON.stringify(getDefaultComplierOpts(), null, 4);