tsWorker.ts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. 'use strict';
  6. import * as ts from './lib/typescriptServices';
  7. import { libFileMap } from './lib/lib';
  8. import { IExtraLibs } from './monaco.contribution';
  9. import IWorkerContext = monaco.worker.IWorkerContext;
  10. export class TypeScriptWorker implements ts.LanguageServiceHost, monaco.languages.typescript.TypeScriptWorker {
  11. // --- model sync -----------------------
  12. private _ctx: IWorkerContext;
  13. private _extraLibs: IExtraLibs = Object.create(null);
  14. private _languageService = ts.createLanguageService(this);
  15. private _compilerOptions: ts.CompilerOptions;
  16. constructor(ctx: IWorkerContext, createData: ICreateData) {
  17. this._ctx = ctx;
  18. this._compilerOptions = createData.compilerOptions;
  19. this._extraLibs = createData.extraLibs;
  20. }
  21. // --- language service host ---------------
  22. getCompilationSettings(): ts.CompilerOptions {
  23. return this._compilerOptions;
  24. }
  25. getScriptFileNames(): string[] {
  26. let models = this._ctx.getMirrorModels().map(model => model.uri.toString());
  27. return models.concat(Object.keys(this._extraLibs));
  28. }
  29. private _getModel(fileName: string): monaco.worker.IMirrorModel | null {
  30. let models = this._ctx.getMirrorModels();
  31. for (let i = 0; i < models.length; i++) {
  32. if (models[i].uri.toString() === fileName) {
  33. return models[i];
  34. }
  35. }
  36. return null;
  37. }
  38. getScriptVersion(fileName: string): string {
  39. let model = this._getModel(fileName);
  40. if (model) {
  41. return model.version.toString();
  42. } else if (this.isDefaultLibFileName(fileName)) {
  43. // default lib is static
  44. return '1';
  45. } else if (fileName in this._extraLibs) {
  46. return String(this._extraLibs[fileName].version);
  47. }
  48. return '';
  49. }
  50. getScriptText(fileName: string): Promise<string | undefined> {
  51. return Promise.resolve(this._getScriptText(fileName));
  52. }
  53. _getScriptText(fileName: string): string | undefined {
  54. let text: string;
  55. let model = this._getModel(fileName);
  56. if (model) {
  57. // a true editor model
  58. text = model.getValue();
  59. } else if (fileName in libFileMap) {
  60. text = libFileMap[fileName];
  61. } else if (fileName in this._extraLibs) {
  62. // extra lib
  63. text = this._extraLibs[fileName].content;
  64. } else {
  65. return;
  66. }
  67. return text;
  68. }
  69. getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined {
  70. const text = this._getScriptText(fileName);
  71. if (!text) {
  72. return;
  73. }
  74. return <ts.IScriptSnapshot>{
  75. getText: (start, end) => text.substring(start, end),
  76. getLength: () => text.length,
  77. getChangeRange: () => undefined
  78. };
  79. }
  80. getScriptKind?(fileName: string): ts.ScriptKind {
  81. const suffix = fileName.substr(fileName.lastIndexOf('.') + 1);
  82. switch (suffix) {
  83. case 'ts': return ts.ScriptKind.TS;
  84. case 'tsx': return ts.ScriptKind.TSX;
  85. case 'js': return ts.ScriptKind.JS;
  86. case 'jsx': return ts.ScriptKind.JSX;
  87. default: return this.getCompilationSettings().allowJs
  88. ? ts.ScriptKind.JS
  89. : ts.ScriptKind.TS;
  90. }
  91. }
  92. getCurrentDirectory(): string {
  93. return '';
  94. }
  95. getDefaultLibFileName(options: ts.CompilerOptions): string {
  96. switch (options.target) {
  97. case 99 /* ESNext */:
  98. const esnext = "lib.esnext.full.d.ts";
  99. if (esnext in libFileMap || esnext in this._extraLibs) return esnext
  100. case 7 /* ES2020 */:
  101. case 6 /* ES2019 */:
  102. case 5 /* ES2018 */:
  103. case 4 /* ES2017 */:
  104. case 3 /* ES2016 */:
  105. case 2 /* ES2015 */:
  106. default:
  107. // Support a dynamic lookup for the ES20XX version based on the target
  108. // which is safe unless TC39 changes their numbering system
  109. const eslib = `lib.es${2013 + (options.target || 99)}.full.d.ts`;
  110. // Note: This also looks in _extraLibs, If you want
  111. // to add support for additional target options, you will need to
  112. // add the extra dts files to _extraLibs via the API.
  113. if (eslib in libFileMap || eslib in this._extraLibs) {
  114. return eslib;
  115. }
  116. return "lib.es6.d.ts"; // We don't use lib.es2015.full.d.ts due to breaking change.
  117. case 1:
  118. case 0:
  119. return "lib.d.ts";
  120. }
  121. }
  122. isDefaultLibFileName(fileName: string): boolean {
  123. return fileName === this.getDefaultLibFileName(this._compilerOptions);
  124. }
  125. getLibFiles(): Promise<Record<string, string>> {
  126. return Promise.resolve(libFileMap);
  127. }
  128. // --- language features
  129. private static clearFiles(diagnostics: ts.Diagnostic[]): monaco.languages.typescript.Diagnostic[] {
  130. // Clear the `file` field, which cannot be JSON'yfied because it
  131. // contains cyclic data structures.
  132. diagnostics.forEach(diag => {
  133. diag.file = undefined;
  134. const related = <ts.Diagnostic[]>diag.relatedInformation;
  135. if (related) {
  136. related.forEach(diag2 => diag2.file = undefined);
  137. }
  138. });
  139. return <monaco.languages.typescript.Diagnostic[]>diagnostics;
  140. }
  141. getSyntacticDiagnostics(fileName: string): Promise<monaco.languages.typescript.Diagnostic[]> {
  142. const diagnostics = this._languageService.getSyntacticDiagnostics(fileName);
  143. return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics));
  144. }
  145. getSemanticDiagnostics(fileName: string): Promise<monaco.languages.typescript.Diagnostic[]> {
  146. const diagnostics = this._languageService.getSemanticDiagnostics(fileName);
  147. return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics));
  148. }
  149. getSuggestionDiagnostics(fileName: string): Promise<monaco.languages.typescript.Diagnostic[]> {
  150. const diagnostics = this._languageService.getSuggestionDiagnostics(fileName);
  151. return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics));
  152. }
  153. getCompilerOptionsDiagnostics(fileName: string): Promise<monaco.languages.typescript.Diagnostic[]> {
  154. const diagnostics = this._languageService.getCompilerOptionsDiagnostics();
  155. return Promise.resolve(TypeScriptWorker.clearFiles(diagnostics));
  156. }
  157. getCompletionsAtPosition(fileName: string, position: number): Promise<ts.CompletionInfo | undefined> {
  158. return Promise.resolve(this._languageService.getCompletionsAtPosition(fileName, position, undefined));
  159. }
  160. getCompletionEntryDetails(fileName: string, position: number, entry: string): Promise<ts.CompletionEntryDetails | undefined> {
  161. return Promise.resolve(this._languageService.getCompletionEntryDetails(fileName, position, entry, undefined, undefined, undefined));
  162. }
  163. getSignatureHelpItems(fileName: string, position: number): Promise<ts.SignatureHelpItems | undefined> {
  164. return Promise.resolve(this._languageService.getSignatureHelpItems(fileName, position, undefined));
  165. }
  166. getQuickInfoAtPosition(fileName: string, position: number): Promise<ts.QuickInfo | undefined> {
  167. return Promise.resolve(this._languageService.getQuickInfoAtPosition(fileName, position));
  168. }
  169. getOccurrencesAtPosition(fileName: string, position: number): Promise<ReadonlyArray<ts.ReferenceEntry> | undefined> {
  170. return Promise.resolve(this._languageService.getOccurrencesAtPosition(fileName, position));
  171. }
  172. getDefinitionAtPosition(fileName: string, position: number): Promise<ReadonlyArray<ts.DefinitionInfo> | undefined> {
  173. return Promise.resolve(this._languageService.getDefinitionAtPosition(fileName, position));
  174. }
  175. getReferencesAtPosition(fileName: string, position: number): Promise<ts.ReferenceEntry[] | undefined> {
  176. return Promise.resolve(this._languageService.getReferencesAtPosition(fileName, position));
  177. }
  178. getNavigationBarItems(fileName: string): Promise<ts.NavigationBarItem[]> {
  179. return Promise.resolve(this._languageService.getNavigationBarItems(fileName));
  180. }
  181. getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): Promise<ts.TextChange[]> {
  182. return Promise.resolve(this._languageService.getFormattingEditsForDocument(fileName, options));
  183. }
  184. getFormattingEditsForRange(fileName: string, start: number, end: number, options: ts.FormatCodeOptions): Promise<ts.TextChange[]> {
  185. return Promise.resolve(this._languageService.getFormattingEditsForRange(fileName, start, end, options));
  186. }
  187. getFormattingEditsAfterKeystroke(fileName: string, postion: number, ch: string, options: ts.FormatCodeOptions): Promise<ts.TextChange[]> {
  188. return Promise.resolve(this._languageService.getFormattingEditsAfterKeystroke(fileName, postion, ch, options));
  189. }
  190. findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename: boolean): Promise<readonly ts.RenameLocation[] | undefined> {
  191. return Promise.resolve(this._languageService.findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename));
  192. }
  193. getRenameInfo(fileName: string, position: number, options: ts.RenameInfoOptions): Promise<ts.RenameInfo> {
  194. return Promise.resolve(this._languageService.getRenameInfo(fileName, position, options));
  195. }
  196. getEmitOutput(fileName: string): Promise<ts.EmitOutput> {
  197. return Promise.resolve(this._languageService.getEmitOutput(fileName));
  198. }
  199. getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: ts.FormatCodeOptions): Promise<ReadonlyArray<ts.CodeFixAction>> {
  200. const preferences = {}
  201. return Promise.resolve(this._languageService.getCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences));
  202. }
  203. updateExtraLibs(extraLibs: IExtraLibs) {
  204. this._extraLibs = extraLibs;
  205. }
  206. }
  207. export interface ICreateData {
  208. compilerOptions: ts.CompilerOptions;
  209. extraLibs: IExtraLibs;
  210. }
  211. export function create(ctx: IWorkerContext, createData: ICreateData): TypeScriptWorker {
  212. return new TypeScriptWorker(ctx, createData);
  213. }