From ee95401a407e42e1044813087ba6dc621ecc25e9 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 7 Sep 2020 21:52:41 +0200 Subject: [PATCH] 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