@volar/typescript 2.4.11 → 2.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -170,8 +170,6 @@ function proxyCreateProgram(ts, original, create) {
170
170
  }
171
171
  const program = Reflect.apply(target, thisArg, args);
172
172
  (0, decorateProgram_1.decorateProgram)(language, program);
173
- // TODO: #128
174
- program.__volar__ = { language };
175
173
  return program;
176
174
  },
177
175
  });
@@ -1,5 +1,17 @@
1
1
  import { Language } from '@volar/language-core';
2
2
  import type * as ts from 'typescript';
3
+ /**
4
+ * Creates and returns a Proxy around the base TypeScript LanguageService.
5
+ *
6
+ * This is used by the Volar TypeScript Plugin (which can be created by `createLanguageServicePlugin`
7
+ * and `createAsyncLanguageServicePlugin`) as an adapter layer between the TypeScript Language Service
8
+ * plugin API (see https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin)
9
+ * and a Volar `Language`.
10
+ *
11
+ * Once the `initialize` method is called, the proxy will begin intercepting requests and
12
+ * enhancing the default behavior of the LanguageService with enhancements based on
13
+ * the Volar `Language` that has been passed to `initialize`.
14
+ */
3
15
  export declare function createProxyLanguageService(languageService: ts.LanguageService): {
4
16
  initialize(language: Language<string>): void;
5
17
  proxy: ts.LanguageService;
@@ -6,6 +6,18 @@ const dedupe_1 = require("./dedupe");
6
6
  const transform_1 = require("./transform");
7
7
  const utils_1 = require("./utils");
8
8
  const windowsPathReg = /\\/g;
9
+ /**
10
+ * Creates and returns a Proxy around the base TypeScript LanguageService.
11
+ *
12
+ * This is used by the Volar TypeScript Plugin (which can be created by `createLanguageServicePlugin`
13
+ * and `createAsyncLanguageServicePlugin`) as an adapter layer between the TypeScript Language Service
14
+ * plugin API (see https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin)
15
+ * and a Volar `Language`.
16
+ *
17
+ * Once the `initialize` method is called, the proxy will begin intercepting requests and
18
+ * enhancing the default behavior of the LanguageService with enhancements based on
19
+ * the Volar `Language` that has been passed to `initialize`.
20
+ */
9
21
  function createProxyLanguageService(languageService) {
10
22
  const proxyCache = new Map();
11
23
  let getProxyMethod;
@@ -128,10 +140,9 @@ function getFormattingEditsForRange(language, getFormattingEditsForRange) {
128
140
  return [];
129
141
  }
130
142
  if (serviceScript) {
131
- const generateStart = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, start, language_core_1.isFormattingEnabled);
132
- const generateEnd = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, end, language_core_1.isFormattingEnabled);
133
- if (generateStart !== undefined && generateEnd !== undefined) {
134
- const edits = getFormattingEditsForRange(targetScript.id, generateStart, generateEnd, options);
143
+ const generatedRange = (0, transform_1.toGeneratedRange)(language, serviceScript, sourceScript, start, end, language_core_1.isFormattingEnabled);
144
+ if (generatedRange !== undefined) {
145
+ const edits = getFormattingEditsForRange(targetScript.id, generatedRange[0], generatedRange[1], options);
135
146
  return edits
136
147
  .map(edit => (0, transform_1.transformTextChange)(sourceScript, language, serviceScript, edit, false, language_core_1.isFormattingEnabled)?.[1])
137
148
  .filter(edit => !!edit);
@@ -523,10 +534,9 @@ function getCodeFixesAtPosition(language, getCodeFixesAtPosition) {
523
534
  return [];
524
535
  }
525
536
  if (serviceScript) {
526
- const generateStart = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, start, language_core_1.isCodeActionsEnabled);
527
- const generateEnd = (0, transform_1.toGeneratedOffset)(language, serviceScript, sourceScript, end, language_core_1.isCodeActionsEnabled);
528
- if (generateStart !== undefined && generateEnd !== undefined) {
529
- fixes = getCodeFixesAtPosition(targetScript.id, generateStart, generateEnd, errorCodes, formatOptions, preferences);
537
+ const generateRange = (0, transform_1.toGeneratedRange)(language, serviceScript, sourceScript, start, end, language_core_1.isCodeActionsEnabled);
538
+ if (generateRange !== undefined) {
539
+ fixes = getCodeFixesAtPosition(targetScript.id, generateRange[0], generateRange[1], errorCodes, formatOptions, preferences);
530
540
  }
531
541
  }
532
542
  else {
@@ -550,23 +560,17 @@ function getEncodedSemanticClassifications(language, getEncodedSemanticClassific
550
560
  };
551
561
  }
552
562
  if (serviceScript) {
553
- let start;
554
- let end;
555
563
  const map = language.maps.get(serviceScript.code, targetScript);
556
- for (const mapping of map.mappings) {
557
- // TODO reuse the logic from language service
558
- if ((0, language_core_1.isSemanticTokensEnabled)(mapping.data) && mapping.sourceOffsets[0] >= span.start && mapping.sourceOffsets[0] <= span.start + span.length) {
559
- start ??= mapping.generatedOffsets[0];
560
- end ??= mapping.generatedOffsets[mapping.generatedOffsets.length - 1] + (mapping.generatedLengths ?? mapping.lengths)[mapping.lengths.length - 1];
561
- start = Math.min(start, mapping.generatedOffsets[0]);
562
- end = Math.max(end, mapping.generatedOffsets[mapping.generatedOffsets.length - 1] + (mapping.generatedLengths ?? mapping.lengths)[mapping.lengths.length - 1]);
563
- }
564
+ const mapped = (0, language_core_1.findOverlapCodeRange)(span.start, span.start + span.length, map, language_core_1.isSemanticTokensEnabled);
565
+ if (!mapped) {
566
+ return {
567
+ spans: [],
568
+ endOfLineState: 0
569
+ };
564
570
  }
565
- start ??= 0;
566
- end ??= targetScript.snapshot.getLength();
567
571
  const mappingOffset = (0, transform_1.getMappingOffset)(language, serviceScript);
568
- start += mappingOffset;
569
- end += mappingOffset;
572
+ const start = mapped.start + mappingOffset;
573
+ const end = mapped.end + mappingOffset;
570
574
  const result = getEncodedSemanticClassifications(targetScript.id, { start, length: end - start }, format);
571
575
  const spans = [];
572
576
  for (let i = 0; i < result.spans.length; i += 3) {
@@ -2,6 +2,12 @@ import type { CodeInformation, SourceScript } from '@volar/language-core';
2
2
  import { Language } from '@volar/language-core';
3
3
  import type * as ts from 'typescript';
4
4
  import type { TypeScriptServiceScript } from '../..';
5
+ /**
6
+ * This file contains a number of facilities for transforming `ts.Diagnostic`s returned
7
+ * from the base TypeScript LanguageService, which reference locations in generated
8
+ * TS code (e.g. the TypeScript codegen'd from the script portion of a .vue file) into locations
9
+ * in the script portion of the .vue file.
10
+ */
5
11
  export declare function transformCallHierarchyItem(language: Language<string>, item: ts.CallHierarchyItem, fallbackToAnyMatch: boolean, filter: (data: CodeInformation) => boolean): ts.CallHierarchyItem;
6
12
  export declare function transformDiagnostic<T extends ts.Diagnostic>(language: Language<string>, diagnostic: T, program: ts.Program | undefined, isTsc: boolean): T | undefined;
7
13
  export declare function fillSourceFileText(language: Language<string>, sourceFile: ts.SourceFile): void;
@@ -16,6 +22,7 @@ export declare function transformTextSpan(sourceScript: SourceScript<string> | u
16
22
  export declare function toSourceOffset(sourceScript: SourceScript<string> | undefined, language: Language<string>, serviceScript: TypeScriptServiceScript, position: number, filter: (data: CodeInformation) => boolean): [fileName: string, offset: number] | undefined;
17
23
  export declare function toSourceRanges(sourceScript: SourceScript<string> | undefined, language: Language<string>, serviceScript: TypeScriptServiceScript, start: number, end: number, fallbackToAnyMatch: boolean, filter: (data: CodeInformation) => boolean): Generator<[fileName: string, start: number, end: number]>;
18
24
  export declare function toSourceOffsets(sourceScript: SourceScript<string> | undefined, language: Language<string>, serviceScript: TypeScriptServiceScript, position: number, filter: (data: CodeInformation) => boolean): Generator<[fileName: string, offset: number]>;
25
+ export declare function toGeneratedRange(language: Language, serviceScript: TypeScriptServiceScript, sourceScript: SourceScript<string>, start: number, end: number, filter: (data: CodeInformation) => boolean): readonly [number, number] | undefined;
19
26
  export declare function toGeneratedRanges(language: Language, serviceScript: TypeScriptServiceScript, sourceScript: SourceScript<string>, start: number, end: number, filter: (data: CodeInformation) => boolean): Generator<readonly [number, number], void, unknown>;
20
27
  export declare function toGeneratedOffset(language: Language, serviceScript: TypeScriptServiceScript, sourceScript: SourceScript<string>, position: number, filter: (data: CodeInformation) => boolean): number | undefined;
21
28
  export declare function toGeneratedOffsets(language: Language, serviceScript: TypeScriptServiceScript, sourceScript: SourceScript<string>, position: number, filter: (data: CodeInformation) => boolean): Generator<readonly [number, import("@volar/language-core").Mapping<CodeInformation>], void, unknown>;
@@ -11,6 +11,7 @@ exports.transformTextSpan = transformTextSpan;
11
11
  exports.toSourceOffset = toSourceOffset;
12
12
  exports.toSourceRanges = toSourceRanges;
13
13
  exports.toSourceOffsets = toSourceOffsets;
14
+ exports.toGeneratedRange = toGeneratedRange;
14
15
  exports.toGeneratedRanges = toGeneratedRanges;
15
16
  exports.toGeneratedOffset = toGeneratedOffset;
16
17
  exports.toGeneratedOffsets = toGeneratedOffsets;
@@ -19,6 +20,12 @@ const language_core_1 = require("@volar/language-core");
19
20
  const utils_1 = require("./utils");
20
21
  const transformedDiagnostics = new WeakMap();
21
22
  const transformedSourceFile = new WeakSet();
23
+ /**
24
+ * This file contains a number of facilities for transforming `ts.Diagnostic`s returned
25
+ * from the base TypeScript LanguageService, which reference locations in generated
26
+ * TS code (e.g. the TypeScript codegen'd from the script portion of a .vue file) into locations
27
+ * in the script portion of the .vue file.
28
+ */
22
29
  function transformCallHierarchyItem(language, item, fallbackToAnyMatch, filter) {
23
30
  const span = transformSpan(language, item.file, item.span, fallbackToAnyMatch, filter);
24
31
  const selectionSpan = transformSpan(language, item.file, item.selectionSpan, fallbackToAnyMatch, filter);
@@ -224,6 +231,11 @@ function* toSourceOffsets(sourceScript, language, serviceScript, position, filte
224
231
  }
225
232
  }
226
233
  }
234
+ function toGeneratedRange(language, serviceScript, sourceScript, start, end, filter) {
235
+ for (const result of toGeneratedRanges(language, serviceScript, sourceScript, start, end, filter)) {
236
+ return result;
237
+ }
238
+ }
227
239
  function* toGeneratedRanges(language, serviceScript, sourceScript, start, end, filter) {
228
240
  const map = language.maps.get(serviceScript.code, sourceScript);
229
241
  for (const [generateStart, generateEnd] of map.toGeneratedRange(start, end, true, filter)) {
@@ -1,6 +1,22 @@
1
- import { Language, LanguagePlugin } from '@volar/language-core';
2
1
  import type * as ts from 'typescript';
3
- export declare function createAsyncLanguageServicePlugin(extensions: string[], getScriptKindForExtraExtensions: ts.ScriptKind | ((fileName: string) => ts.ScriptKind), create: (ts: typeof import('typescript'), info: ts.server.PluginCreateInfo) => Promise<{
4
- languagePlugins: LanguagePlugin<string>[];
5
- setup?: (language: Language<string>) => void;
6
- }>): ts.server.PluginModuleFactory;
2
+ import type { createPluginCallbackAsync } from './languageServicePluginCommon';
3
+ /**
4
+ * Creates and returns a TS Service Plugin that supports async initialization.
5
+ * Essentially, this functions the same as `createLanguageServicePlugin`, but supports
6
+ * use cases in which the plugin callback must be async. For example in mdx-analyzer
7
+ * and Glint, this async variant is required because Glint + mdx-analyzer are written
8
+ * in ESM and get transpiled to CJS, which requires usage of `await import()` to load
9
+ * the necessary dependencies and fully initialize the plugin.
10
+ *
11
+ * To handle the period of time in which the plugin is initializing, this async
12
+ * variant stubs a number of methods on the LanguageServiceHost to handle the uninitialized state.
13
+ *
14
+ * Additionally, this async variant requires a few extra args pertaining to
15
+ * file extensions intended to be handled by the TS Plugin. In the synchronous variant,
16
+ * these can be synchronously inferred from elsewhere but for the async variant, they
17
+ * need to be passed in.
18
+ *
19
+ * See https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin for
20
+ * more information.
21
+ */
22
+ export declare function createAsyncLanguageServicePlugin(extensions: string[], getScriptKindForExtraExtensions: ts.ScriptKind | ((fileName: string) => ts.ScriptKind), createPluginCallbackAsync: createPluginCallbackAsync): ts.server.PluginModuleFactory;
@@ -1,130 +1,107 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createAsyncLanguageServicePlugin = createAsyncLanguageServicePlugin;
4
- const language_core_1 = require("@volar/language-core");
5
- const common_1 = require("../common");
6
4
  const proxyLanguageService_1 = require("../node/proxyLanguageService");
7
- const decorateLanguageServiceHost_1 = require("../node/decorateLanguageServiceHost");
8
- const createLanguageServicePlugin_1 = require("./createLanguageServicePlugin");
9
- function createAsyncLanguageServicePlugin(extensions, getScriptKindForExtraExtensions, create) {
5
+ const languageServicePluginCommon_1 = require("./languageServicePluginCommon");
6
+ /**
7
+ * Creates and returns a TS Service Plugin that supports async initialization.
8
+ * Essentially, this functions the same as `createLanguageServicePlugin`, but supports
9
+ * use cases in which the plugin callback must be async. For example in mdx-analyzer
10
+ * and Glint, this async variant is required because Glint + mdx-analyzer are written
11
+ * in ESM and get transpiled to CJS, which requires usage of `await import()` to load
12
+ * the necessary dependencies and fully initialize the plugin.
13
+ *
14
+ * To handle the period of time in which the plugin is initializing, this async
15
+ * variant stubs a number of methods on the LanguageServiceHost to handle the uninitialized state.
16
+ *
17
+ * Additionally, this async variant requires a few extra args pertaining to
18
+ * file extensions intended to be handled by the TS Plugin. In the synchronous variant,
19
+ * these can be synchronously inferred from elsewhere but for the async variant, they
20
+ * need to be passed in.
21
+ *
22
+ * See https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin for
23
+ * more information.
24
+ */
25
+ function createAsyncLanguageServicePlugin(extensions, getScriptKindForExtraExtensions, createPluginCallbackAsync) {
10
26
  return modules => {
11
27
  const { typescript: ts } = modules;
12
28
  const pluginModule = {
13
29
  create(info) {
14
- if (!createLanguageServicePlugin_1.decoratedLanguageServices.has(info.languageService)
15
- && !createLanguageServicePlugin_1.decoratedLanguageServiceHosts.has(info.languageServiceHost)) {
16
- createLanguageServicePlugin_1.decoratedLanguageServices.add(info.languageService);
17
- createLanguageServicePlugin_1.decoratedLanguageServiceHosts.add(info.languageServiceHost);
18
- const emptySnapshot = ts.ScriptSnapshot.fromString('');
19
- const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
20
- const getScriptVersion = info.languageServiceHost.getScriptVersion.bind(info.languageServiceHost);
21
- const getScriptKind = info.languageServiceHost.getScriptKind?.bind(info.languageServiceHost);
22
- const getProjectVersion = info.languageServiceHost.getProjectVersion?.bind(info.languageServiceHost);
23
- let initialized = false;
24
- info.languageServiceHost.getScriptSnapshot = fileName => {
25
- if (!initialized) {
26
- if (extensions.some(ext => fileName.endsWith(ext))) {
27
- return emptySnapshot;
28
- }
29
- if (getScriptInfo(fileName)?.isScriptOpen()) {
30
- return emptySnapshot;
31
- }
32
- }
33
- return getScriptSnapshot(fileName);
34
- };
35
- info.languageServiceHost.getScriptVersion = fileName => {
36
- if (!initialized) {
37
- if (extensions.some(ext => fileName.endsWith(ext))) {
38
- return 'initializing...';
39
- }
40
- if (getScriptInfo(fileName)?.isScriptOpen()) {
41
- return getScriptVersion(fileName) + ',initializing...';
42
- }
43
- }
44
- return getScriptVersion(fileName);
45
- };
46
- if (getScriptKind) {
47
- info.languageServiceHost.getScriptKind = fileName => {
48
- if (!initialized && extensions.some(ext => fileName.endsWith(ext))) {
49
- // bypass upstream bug https://github.com/microsoft/TypeScript/issues/57631
50
- // TODO: check if the bug is fixed in 5.5
51
- if (typeof getScriptKindForExtraExtensions === 'function') {
52
- return getScriptKindForExtraExtensions(fileName);
53
- }
54
- else {
55
- return getScriptKindForExtraExtensions;
56
- }
57
- }
58
- return getScriptKind(fileName);
59
- };
60
- }
61
- if (getProjectVersion) {
62
- info.languageServiceHost.getProjectVersion = () => {
63
- if (!initialized) {
64
- return getProjectVersion() + ',initializing...';
65
- }
66
- return getProjectVersion();
67
- };
68
- }
30
+ if (!(0, languageServicePluginCommon_1.isHasAlreadyDecoratedLanguageService)(info)) {
31
+ const state = decorateWithAsyncInitializationHandling(ts, info, extensions, getScriptKindForExtraExtensions);
69
32
  const { proxy, initialize } = (0, proxyLanguageService_1.createProxyLanguageService)(info.languageService);
70
33
  info.languageService = proxy;
71
- create(ts, info).then(({ languagePlugins, setup }) => {
72
- const language = (0, language_core_1.createLanguage)([
73
- ...languagePlugins,
74
- { getLanguageId: common_1.resolveFileLanguageId },
75
- ], new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames), (fileName, _, shouldRegister) => {
76
- let snapshot;
77
- if (shouldRegister) {
78
- // We need to trigger registration of the script file with the project, see #250
79
- snapshot = getScriptSnapshot(fileName);
80
- }
81
- else {
82
- snapshot = getScriptInfo(fileName)?.getSnapshot();
83
- if (!snapshot) {
84
- // trigger projectService.getOrCreateScriptInfoNotOpenedByClient
85
- info.project.getScriptVersion(fileName);
86
- snapshot = getScriptInfo(fileName)?.getSnapshot();
87
- }
88
- }
89
- if (snapshot) {
90
- language.scripts.set(fileName, snapshot);
91
- }
92
- else {
93
- language.scripts.delete(fileName);
94
- }
95
- });
96
- initialize(language);
97
- (0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(ts, language, info.languageServiceHost);
98
- setup?.(language);
99
- initialized = true;
34
+ createPluginCallbackAsync(ts, info).then(createPluginResult => {
35
+ (0, languageServicePluginCommon_1.createLanguageCommon)(createPluginResult, ts, info, initialize);
36
+ state.initialized = true;
100
37
  if ('markAsDirty' in info.project && typeof info.project.markAsDirty === 'function') {
38
+ // This is an attempt to mark the project as dirty so that in case the IDE/tsserver
39
+ // already finished a first pass of generating diagnostics (or other things), another
40
+ // pass will be triggered which should hopefully make use of this now-initialized plugin.
101
41
  info.project.markAsDirty();
102
42
  }
103
43
  });
104
44
  }
105
45
  return info.languageService;
106
- function getScriptInfo(fileName) {
107
- // getSnapshot could be crashed if the file is too large
108
- try {
109
- return info.project.getScriptInfo(fileName);
110
- }
111
- catch { }
112
- }
113
- },
114
- getExternalFiles(project, updateLevel = 0) {
115
- if (updateLevel >= 1
116
- || !createLanguageServicePlugin_1.externalFiles.has(project)) {
117
- const oldFiles = createLanguageServicePlugin_1.externalFiles.get(project);
118
- const newFiles = extensions.length ? (0, decorateLanguageServiceHost_1.searchExternalFiles)(ts, project, extensions) : [];
119
- createLanguageServicePlugin_1.externalFiles.set(project, newFiles);
120
- if (oldFiles && !(0, createLanguageServicePlugin_1.arrayItemsEqual)(oldFiles, newFiles)) {
121
- project.refreshDiagnostics();
122
- }
123
- }
124
- return createLanguageServicePlugin_1.externalFiles.get(project);
125
46
  },
47
+ getExternalFiles: (0, languageServicePluginCommon_1.makeGetExternalFiles)(ts),
126
48
  };
127
49
  return pluginModule;
128
50
  };
129
51
  }
52
+ function decorateWithAsyncInitializationHandling(ts, info, extensions, getScriptKindForExtraExtensions) {
53
+ const emptySnapshot = ts.ScriptSnapshot.fromString('');
54
+ const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
55
+ const getScriptVersion = info.languageServiceHost.getScriptVersion.bind(info.languageServiceHost);
56
+ const getScriptKind = info.languageServiceHost.getScriptKind?.bind(info.languageServiceHost);
57
+ const getProjectVersion = info.languageServiceHost.getProjectVersion?.bind(info.languageServiceHost);
58
+ const getScriptInfo = (0, languageServicePluginCommon_1.makeGetScriptInfoWithLargeFileFailsafe)(info);
59
+ const state = { initialized: false };
60
+ info.languageServiceHost.getScriptSnapshot = fileName => {
61
+ if (!state.initialized) {
62
+ if (extensions.some(ext => fileName.endsWith(ext))) {
63
+ return emptySnapshot;
64
+ }
65
+ if (getScriptInfo(fileName)?.isScriptOpen()) {
66
+ return emptySnapshot;
67
+ }
68
+ }
69
+ return getScriptSnapshot(fileName);
70
+ };
71
+ info.languageServiceHost.getScriptVersion = fileName => {
72
+ if (!state.initialized) {
73
+ if (extensions.some(ext => fileName.endsWith(ext))) {
74
+ return 'initializing...';
75
+ }
76
+ if (getScriptInfo(fileName)?.isScriptOpen()) {
77
+ return getScriptVersion(fileName) + ',initializing...';
78
+ }
79
+ }
80
+ return getScriptVersion(fileName);
81
+ };
82
+ if (getScriptKind) {
83
+ info.languageServiceHost.getScriptKind = fileName => {
84
+ if (!state.initialized && extensions.some(ext => fileName.endsWith(ext))) {
85
+ // bypass upstream bug https://github.com/microsoft/TypeScript/issues/57631
86
+ // TODO: check if the bug is fixed in 5.5
87
+ if (typeof getScriptKindForExtraExtensions === 'function') {
88
+ return getScriptKindForExtraExtensions(fileName);
89
+ }
90
+ else {
91
+ return getScriptKindForExtraExtensions;
92
+ }
93
+ }
94
+ return getScriptKind(fileName);
95
+ };
96
+ }
97
+ if (getProjectVersion) {
98
+ info.languageServiceHost.getProjectVersion = () => {
99
+ if (!state.initialized) {
100
+ return getProjectVersion() + ',initializing...';
101
+ }
102
+ return getProjectVersion();
103
+ };
104
+ }
105
+ return state;
106
+ }
130
107
  //# sourceMappingURL=createAsyncLanguageServicePlugin.js.map
@@ -1,11 +1,9 @@
1
- import { Language, LanguagePlugin } from '@volar/language-core';
2
1
  import type * as ts from 'typescript';
3
- export declare const externalFiles: WeakMap<ts.server.Project, string[]>;
4
- export declare const projectExternalFileExtensions: WeakMap<ts.server.Project, string[]>;
5
- export declare const decoratedLanguageServices: WeakSet<ts.LanguageService>;
6
- export declare const decoratedLanguageServiceHosts: WeakSet<ts.LanguageServiceHost>;
7
- export declare function createLanguageServicePlugin(create: (ts: typeof import('typescript'), info: ts.server.PluginCreateInfo) => {
8
- languagePlugins: LanguagePlugin<string>[];
9
- setup?: (language: Language<string>) => void;
10
- }): ts.server.PluginModuleFactory;
11
- export declare function arrayItemsEqual(a: string[], b: string[]): boolean;
2
+ import type { createPluginCallbackSync } from './languageServicePluginCommon';
3
+ /**
4
+ * Creates and returns a TS Service Plugin using Volar primitives.
5
+ *
6
+ * See https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin for
7
+ * more information.
8
+ */
9
+ export declare function createLanguageServicePlugin(createPluginCallback: createPluginCallbackSync): ts.server.PluginModuleFactory;
@@ -1,97 +1,36 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.decoratedLanguageServiceHosts = exports.decoratedLanguageServices = exports.projectExternalFileExtensions = exports.externalFiles = void 0;
4
3
  exports.createLanguageServicePlugin = createLanguageServicePlugin;
5
- exports.arrayItemsEqual = arrayItemsEqual;
6
- const language_core_1 = require("@volar/language-core");
7
- const common_1 = require("../common");
8
4
  const proxyLanguageService_1 = require("../node/proxyLanguageService");
9
- const decorateLanguageServiceHost_1 = require("../node/decorateLanguageServiceHost");
10
- exports.externalFiles = new WeakMap();
11
- exports.projectExternalFileExtensions = new WeakMap();
12
- exports.decoratedLanguageServices = new WeakSet();
13
- exports.decoratedLanguageServiceHosts = new WeakSet();
14
- function createLanguageServicePlugin(create) {
5
+ const languageServicePluginCommon_1 = require("./languageServicePluginCommon");
6
+ /**
7
+ * Creates and returns a TS Service Plugin using Volar primitives.
8
+ *
9
+ * See https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin for
10
+ * more information.
11
+ */
12
+ function createLanguageServicePlugin(createPluginCallback) {
15
13
  return modules => {
16
14
  const { typescript: ts } = modules;
17
15
  const pluginModule = {
18
16
  create(info) {
19
- if (!exports.decoratedLanguageServices.has(info.languageService)
20
- && !exports.decoratedLanguageServiceHosts.has(info.languageServiceHost)) {
21
- exports.decoratedLanguageServices.add(info.languageService);
22
- exports.decoratedLanguageServiceHosts.add(info.languageServiceHost);
23
- const { languagePlugins, setup } = create(ts, info);
24
- const extensions = languagePlugins
17
+ if (!(0, languageServicePluginCommon_1.isHasAlreadyDecoratedLanguageService)(info)) {
18
+ const createPluginResult = createPluginCallback(ts, info);
19
+ const extensions = createPluginResult.languagePlugins
25
20
  .map(plugin => plugin.typescript?.extraFileExtensions.map(ext => '.' + ext.extension) ?? [])
26
21
  .flat();
27
- exports.projectExternalFileExtensions.set(info.project, extensions);
28
- const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
29
- const language = (0, language_core_1.createLanguage)([
30
- ...languagePlugins,
31
- { getLanguageId: common_1.resolveFileLanguageId },
32
- ], new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames), (fileName, _, shouldRegister) => {
33
- let snapshot;
34
- if (shouldRegister) {
35
- // We need to trigger registration of the script file with the project, see #250
36
- snapshot = getScriptSnapshot(fileName);
37
- }
38
- else {
39
- snapshot = getScriptInfo(fileName)?.getSnapshot();
40
- if (!snapshot) {
41
- // trigger projectService.getOrCreateScriptInfoNotOpenedByClient
42
- info.project.getScriptVersion(fileName);
43
- snapshot = getScriptInfo(fileName)?.getSnapshot();
44
- }
45
- }
46
- if (snapshot) {
47
- language.scripts.set(fileName, snapshot);
48
- }
49
- else {
50
- language.scripts.delete(fileName);
51
- }
52
- });
22
+ // TODO: this logic does not seem to appear in the async variant
23
+ // (createAsyncLanguageServicePlugin)... bug?
24
+ languageServicePluginCommon_1.projectExternalFileExtensions.set(info.project, extensions);
53
25
  const { proxy, initialize } = (0, proxyLanguageService_1.createProxyLanguageService)(info.languageService);
54
26
  info.languageService = proxy;
55
- initialize(language);
56
- (0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(ts, language, info.languageServiceHost);
57
- setup?.(language);
27
+ (0, languageServicePluginCommon_1.createLanguageCommon)(createPluginResult, ts, info, initialize);
58
28
  }
59
29
  return info.languageService;
60
- function getScriptInfo(fileName) {
61
- // getSnapshot could be crashed if the file is too large
62
- try {
63
- return info.project.getScriptInfo(fileName);
64
- }
65
- catch { }
66
- }
67
- },
68
- getExternalFiles(project, updateLevel = 0) {
69
- if (updateLevel >= 1
70
- || !exports.externalFiles.has(project)) {
71
- const oldFiles = exports.externalFiles.get(project);
72
- const extensions = exports.projectExternalFileExtensions.get(project);
73
- const newFiles = extensions?.length ? (0, decorateLanguageServiceHost_1.searchExternalFiles)(ts, project, extensions) : [];
74
- exports.externalFiles.set(project, newFiles);
75
- if (oldFiles && !arrayItemsEqual(oldFiles, newFiles)) {
76
- project.refreshDiagnostics();
77
- }
78
- }
79
- return exports.externalFiles.get(project);
80
30
  },
31
+ getExternalFiles: (0, languageServicePluginCommon_1.makeGetExternalFiles)(ts),
81
32
  };
82
33
  return pluginModule;
83
34
  };
84
35
  }
85
- function arrayItemsEqual(a, b) {
86
- if (a.length !== b.length) {
87
- return false;
88
- }
89
- const set = new Set(a);
90
- for (const file of b) {
91
- if (!set.has(file)) {
92
- return false;
93
- }
94
- }
95
- return true;
96
- }
97
36
  //# sourceMappingURL=createLanguageServicePlugin.js.map
@@ -0,0 +1,24 @@
1
+ import { Language, LanguagePlugin } from '@volar/language-core/lib/types';
2
+ import type * as ts from 'typescript';
3
+ export declare const externalFiles: WeakMap<ts.server.Project, string[]>;
4
+ export declare const projectExternalFileExtensions: WeakMap<ts.server.Project, string[]>;
5
+ export declare const decoratedLanguageServices: WeakSet<ts.LanguageService>;
6
+ export declare const decoratedLanguageServiceHosts: WeakSet<ts.LanguageServiceHost>;
7
+ /**
8
+ * Wrap `getScriptInfo` to handle large files that may crash the language service.
9
+ *
10
+ * Introduced to fix issues with converting `relatedInformation` (in Diagnostics)
11
+ * when working with large files.
12
+ *
13
+ * https://github.com/volarjs/volar.js/commit/e242709a91e9d2919dc4fa59278dd266fd11e7a3
14
+ */
15
+ export declare function makeGetScriptInfoWithLargeFileFailsafe(info: ts.server.PluginCreateInfo): (fileName: string) => ts.server.ScriptInfo | undefined;
16
+ export declare function createLanguageCommon(createPluginResult: createPluginCallbackReturnValue, ts: typeof import('typescript'), info: ts.server.PluginCreateInfo, initializeProxiedLanguageService: (language: Language<string>) => void): void;
17
+ export declare const makeGetExternalFiles: (ts: typeof import("typescript")) => (project: ts.server.Project, updateLevel?: number) => string[];
18
+ export type createPluginCallbackReturnValue = {
19
+ languagePlugins: LanguagePlugin<string>[];
20
+ setup?: (language: Language<string>) => void;
21
+ };
22
+ export type createPluginCallbackSync = (ts: typeof import('typescript'), info: ts.server.PluginCreateInfo) => createPluginCallbackReturnValue;
23
+ export type createPluginCallbackAsync = (ts: typeof import('typescript'), info: ts.server.PluginCreateInfo) => Promise<createPluginCallbackReturnValue>;
24
+ export declare function isHasAlreadyDecoratedLanguageService(info: ts.server.PluginCreateInfo): boolean;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeGetExternalFiles = exports.decoratedLanguageServiceHosts = exports.decoratedLanguageServices = exports.projectExternalFileExtensions = exports.externalFiles = void 0;
4
+ exports.makeGetScriptInfoWithLargeFileFailsafe = makeGetScriptInfoWithLargeFileFailsafe;
5
+ exports.createLanguageCommon = createLanguageCommon;
6
+ exports.isHasAlreadyDecoratedLanguageService = isHasAlreadyDecoratedLanguageService;
7
+ const language_core_1 = require("@volar/language-core");
8
+ const common_1 = require("../common");
9
+ const decorateLanguageServiceHost_1 = require("../node/decorateLanguageServiceHost");
10
+ exports.externalFiles = new WeakMap();
11
+ exports.projectExternalFileExtensions = new WeakMap();
12
+ exports.decoratedLanguageServices = new WeakSet();
13
+ exports.decoratedLanguageServiceHosts = new WeakSet();
14
+ /**
15
+ * Wrap `getScriptInfo` to handle large files that may crash the language service.
16
+ *
17
+ * Introduced to fix issues with converting `relatedInformation` (in Diagnostics)
18
+ * when working with large files.
19
+ *
20
+ * https://github.com/volarjs/volar.js/commit/e242709a91e9d2919dc4fa59278dd266fd11e7a3
21
+ */
22
+ function makeGetScriptInfoWithLargeFileFailsafe(info) {
23
+ return (fileName) => {
24
+ // getSnapshot could be crashed if the file is too large
25
+ try {
26
+ return info.project.getScriptInfo(fileName);
27
+ }
28
+ catch { }
29
+ };
30
+ }
31
+ function createLanguageCommon(createPluginResult, ts, info, initializeProxiedLanguageService) {
32
+ const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
33
+ const getScriptInfo = makeGetScriptInfoWithLargeFileFailsafe(info);
34
+ const language = (0, language_core_1.createLanguage)([
35
+ ...createPluginResult.languagePlugins,
36
+ { getLanguageId: common_1.resolveFileLanguageId },
37
+ ], new language_core_1.FileMap(ts.sys.useCaseSensitiveFileNames), (fileName, _, shouldRegister) => {
38
+ let snapshot;
39
+ if (shouldRegister) {
40
+ // We need to trigger registration of the script file with the project, see #250
41
+ snapshot = getScriptSnapshot(fileName);
42
+ }
43
+ else {
44
+ snapshot = getScriptInfo(fileName)?.getSnapshot();
45
+ if (!snapshot) {
46
+ // trigger projectService.getOrCreateScriptInfoNotOpenedByClient
47
+ info.project.getScriptVersion(fileName);
48
+ snapshot = getScriptInfo(fileName)?.getSnapshot();
49
+ }
50
+ }
51
+ if (snapshot) {
52
+ language.scripts.set(fileName, snapshot);
53
+ }
54
+ else {
55
+ language.scripts.delete(fileName);
56
+ }
57
+ }, targetFileName => {
58
+ // https://github.com/JetBrains/intellij-plugins/blob/6435723ad88fa296b41144162ebe3b8513f4949b/Angular/src-js/angular-service/src/ngCommands.ts#L88
59
+ info.session.change({
60
+ file: targetFileName,
61
+ line: 1,
62
+ offset: 1,
63
+ endLine: 1,
64
+ endOffset: 1,
65
+ insertString: '',
66
+ });
67
+ });
68
+ initializeProxiedLanguageService(language);
69
+ (0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(ts, language, info.languageServiceHost);
70
+ createPluginResult.setup?.(language);
71
+ }
72
+ const makeGetExternalFiles = (ts) => (project, updateLevel = 0) => {
73
+ if (updateLevel >= 1
74
+ || !exports.externalFiles.has(project)) {
75
+ const oldFiles = exports.externalFiles.get(project);
76
+ const extensions = exports.projectExternalFileExtensions.get(project);
77
+ const newFiles = extensions?.length ? (0, decorateLanguageServiceHost_1.searchExternalFiles)(ts, project, extensions) : [];
78
+ exports.externalFiles.set(project, newFiles);
79
+ if (oldFiles && !arrayItemsEqual(oldFiles, newFiles)) {
80
+ project.refreshDiagnostics();
81
+ }
82
+ }
83
+ return exports.externalFiles.get(project);
84
+ };
85
+ exports.makeGetExternalFiles = makeGetExternalFiles;
86
+ function arrayItemsEqual(a, b) {
87
+ if (a.length !== b.length) {
88
+ return false;
89
+ }
90
+ const set = new Set(a);
91
+ for (const file of b) {
92
+ if (!set.has(file)) {
93
+ return false;
94
+ }
95
+ }
96
+ return true;
97
+ }
98
+ function isHasAlreadyDecoratedLanguageService(info) {
99
+ if (exports.decoratedLanguageServices.has(info.languageService)
100
+ || exports.decoratedLanguageServiceHosts.has(info.languageServiceHost)) {
101
+ return true;
102
+ }
103
+ else {
104
+ exports.decoratedLanguageServices.add(info.languageService);
105
+ exports.decoratedLanguageServiceHosts.add(info.languageServiceHost);
106
+ return false;
107
+ }
108
+ }
109
+ //# sourceMappingURL=languageServicePluginCommon.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volar/typescript",
3
- "version": "2.4.11",
3
+ "version": "2.4.13",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "**/*.js",
@@ -12,14 +12,14 @@
12
12
  "directory": "packages/typescript"
13
13
  },
14
14
  "dependencies": {
15
- "@volar/language-core": "2.4.11",
15
+ "@volar/language-core": "2.4.13",
16
16
  "path-browserify": "^1.0.1",
17
17
  "vscode-uri": "^3.0.8"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@types/node": "latest",
21
21
  "@types/path-browserify": "latest",
22
- "@volar/language-service": "2.4.11"
22
+ "@volar/language-service": "2.4.13"
23
23
  },
24
- "gitHead": "42ccae005cc8516e07ad38f4d7730cab9b723340"
24
+ "gitHead": "c5d727b9e73d0c6a45c64b38d74ca5c52363818f"
25
25
  }