@volar/language-core 2.3.0-alpha.0 → 2.3.0-alpha.10
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.
- package/index.d.ts +1 -4
- package/index.js +106 -70
- package/lib/linkedCodeMap.d.ts +1 -1
- package/lib/types.d.ts +54 -25
- package/package.json +3 -3
package/index.d.ts
CHANGED
|
@@ -3,9 +3,6 @@ export * from './lib/editorFeatures';
|
|
|
3
3
|
export * from './lib/linkedCodeMap';
|
|
4
4
|
export * from './lib/types';
|
|
5
5
|
export * from './lib/utils';
|
|
6
|
-
import {
|
|
7
|
-
import type * as ts from 'typescript';
|
|
8
|
-
import type { CodeInformation, Language, LanguagePlugin, SourceScript, VirtualCode } from './lib/types';
|
|
6
|
+
import type { Language, LanguagePlugin, SourceScript, VirtualCode } from './lib/types';
|
|
9
7
|
export declare function createLanguage<T>(plugins: LanguagePlugin<T>[], scriptRegistry: Map<T, SourceScript<T>>, sync: (id: T) => void): Language<T>;
|
|
10
|
-
export declare function updateVirtualCodeMapOfMap<T>(virtualCode: VirtualCode, mapOfMap: Map<T, [ts.IScriptSnapshot, SourceMap<CodeInformation>]>, getSourceSnapshot: (source: string | undefined) => [T, ts.IScriptSnapshot] | undefined): void;
|
|
11
8
|
export declare function forEachEmbeddedCode(virtualCode: VirtualCode): Generator<VirtualCode>;
|
package/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.forEachEmbeddedCode = exports.
|
|
17
|
+
exports.forEachEmbeddedCode = exports.createLanguage = void 0;
|
|
18
18
|
__exportStar(require("@volar/source-map"), exports);
|
|
19
19
|
__exportStar(require("./lib/editorFeatures"), exports);
|
|
20
20
|
__exportStar(require("./lib/linkedCodeMap"), exports);
|
|
@@ -23,14 +23,22 @@ __exportStar(require("./lib/utils"), exports);
|
|
|
23
23
|
const source_map_1 = require("@volar/source-map");
|
|
24
24
|
const linkedCodeMap_1 = require("./lib/linkedCodeMap");
|
|
25
25
|
function createLanguage(plugins, scriptRegistry, sync) {
|
|
26
|
-
const
|
|
27
|
-
const
|
|
26
|
+
const virtualCodeToSourceScriptMap = new WeakMap();
|
|
27
|
+
const virtualCodeToSourceMap = new WeakMap();
|
|
28
28
|
const virtualCodeToLinkedCodeMap = new WeakMap();
|
|
29
29
|
return {
|
|
30
30
|
plugins,
|
|
31
31
|
scripts: {
|
|
32
|
+
fromVirtualCode(virtualCode) {
|
|
33
|
+
return virtualCodeToSourceScriptMap.get(virtualCode);
|
|
34
|
+
},
|
|
32
35
|
get(id) {
|
|
33
36
|
sync(id);
|
|
37
|
+
const result = scriptRegistry.get(id);
|
|
38
|
+
// The sync function provider may not always call the set function due to caching, so it is necessary to explicitly check isAssociationDirty.
|
|
39
|
+
if (result?.isAssociationDirty) {
|
|
40
|
+
this.set(id, result.snapshot, result.languageId);
|
|
41
|
+
}
|
|
34
42
|
return scriptRegistry.get(id);
|
|
35
43
|
},
|
|
36
44
|
set(id, snapshot, languageId, _plugins = plugins) {
|
|
@@ -46,23 +54,36 @@ function createLanguage(plugins, scriptRegistry, sync) {
|
|
|
46
54
|
console.warn(`languageId not found for ${id}`);
|
|
47
55
|
return;
|
|
48
56
|
}
|
|
57
|
+
let associatedOnly = false;
|
|
58
|
+
for (const plugin of plugins) {
|
|
59
|
+
if (plugin.isAssociatedFileOnly?.(id, languageId)) {
|
|
60
|
+
associatedOnly = true;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
49
64
|
if (scriptRegistry.has(id)) {
|
|
50
65
|
const sourceScript = scriptRegistry.get(id);
|
|
51
|
-
if (sourceScript.languageId !== languageId) {
|
|
52
|
-
// languageId changed
|
|
66
|
+
if (sourceScript.languageId !== languageId || sourceScript.associatedOnly !== associatedOnly) {
|
|
53
67
|
this.delete(id);
|
|
54
68
|
return this.set(id, snapshot, languageId);
|
|
55
69
|
}
|
|
56
|
-
else if (
|
|
70
|
+
else if (associatedOnly) {
|
|
71
|
+
sourceScript.snapshot = snapshot;
|
|
72
|
+
}
|
|
73
|
+
else if (sourceScript.isAssociationDirty || sourceScript.snapshot !== snapshot) {
|
|
57
74
|
// snapshot updated
|
|
58
75
|
sourceScript.snapshot = snapshot;
|
|
76
|
+
const codegenCtx = prepareCreateVirtualCode(sourceScript);
|
|
59
77
|
if (sourceScript.generated) {
|
|
60
|
-
const
|
|
78
|
+
const { updateVirtualCode, createVirtualCode } = sourceScript.generated.languagePlugin;
|
|
79
|
+
const newVirtualCode = updateVirtualCode
|
|
80
|
+
? updateVirtualCode(id, sourceScript.generated.root, snapshot, codegenCtx)
|
|
81
|
+
: createVirtualCode?.(id, languageId, snapshot, codegenCtx);
|
|
61
82
|
if (newVirtualCode) {
|
|
62
83
|
sourceScript.generated.root = newVirtualCode;
|
|
63
84
|
sourceScript.generated.embeddedCodes.clear();
|
|
64
85
|
for (const code of forEachEmbeddedCode(sourceScript.generated.root)) {
|
|
65
|
-
|
|
86
|
+
virtualCodeToSourceScriptMap.set(code, sourceScript);
|
|
66
87
|
sourceScript.generated.embeddedCodes.set(code.id, code);
|
|
67
88
|
}
|
|
68
89
|
return sourceScript;
|
|
@@ -72,6 +93,7 @@ function createLanguage(plugins, scriptRegistry, sync) {
|
|
|
72
93
|
return;
|
|
73
94
|
}
|
|
74
95
|
}
|
|
96
|
+
triggerTargetsDirty(sourceScript);
|
|
75
97
|
}
|
|
76
98
|
else {
|
|
77
99
|
// not changed
|
|
@@ -80,10 +102,20 @@ function createLanguage(plugins, scriptRegistry, sync) {
|
|
|
80
102
|
}
|
|
81
103
|
else {
|
|
82
104
|
// created
|
|
83
|
-
const sourceScript = {
|
|
105
|
+
const sourceScript = {
|
|
106
|
+
id: id,
|
|
107
|
+
languageId,
|
|
108
|
+
snapshot,
|
|
109
|
+
associatedIds: new Set(),
|
|
110
|
+
targetIds: new Set(),
|
|
111
|
+
associatedOnly
|
|
112
|
+
};
|
|
84
113
|
scriptRegistry.set(id, sourceScript);
|
|
114
|
+
if (associatedOnly) {
|
|
115
|
+
return sourceScript;
|
|
116
|
+
}
|
|
85
117
|
for (const languagePlugin of _plugins) {
|
|
86
|
-
const virtualCode = languagePlugin.createVirtualCode?.(id, languageId, snapshot);
|
|
118
|
+
const virtualCode = languagePlugin.createVirtualCode?.(id, languageId, snapshot, prepareCreateVirtualCode(sourceScript));
|
|
87
119
|
if (virtualCode) {
|
|
88
120
|
sourceScript.generated = {
|
|
89
121
|
root: virtualCode,
|
|
@@ -91,7 +123,7 @@ function createLanguage(plugins, scriptRegistry, sync) {
|
|
|
91
123
|
embeddedCodes: new Map(),
|
|
92
124
|
};
|
|
93
125
|
for (const code of forEachEmbeddedCode(virtualCode)) {
|
|
94
|
-
|
|
126
|
+
virtualCodeToSourceScriptMap.set(code, sourceScript);
|
|
95
127
|
sourceScript.generated.embeddedCodes.set(code.id, code);
|
|
96
128
|
}
|
|
97
129
|
break;
|
|
@@ -101,86 +133,90 @@ function createLanguage(plugins, scriptRegistry, sync) {
|
|
|
101
133
|
}
|
|
102
134
|
},
|
|
103
135
|
delete(id) {
|
|
104
|
-
const
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
value.generated.languagePlugin.disposeVirtualCode?.(id, value.generated.root);
|
|
108
|
-
}
|
|
136
|
+
const sourceScript = scriptRegistry.get(id);
|
|
137
|
+
if (sourceScript) {
|
|
138
|
+
sourceScript.generated?.languagePlugin.disposeVirtualCode?.(id, sourceScript.generated.root);
|
|
109
139
|
scriptRegistry.delete(id);
|
|
140
|
+
triggerTargetsDirty(sourceScript);
|
|
110
141
|
}
|
|
111
142
|
},
|
|
112
143
|
},
|
|
113
144
|
maps: {
|
|
114
|
-
get(virtualCode,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
scriptId = sourceScript.id;
|
|
145
|
+
get(virtualCode, sourceScript, mappings) {
|
|
146
|
+
let mapCache = virtualCodeToSourceMap.get(virtualCode.snapshot);
|
|
147
|
+
if (!mapCache) {
|
|
148
|
+
virtualCodeToSourceMap.set(virtualCode.snapshot, mapCache = new WeakMap());
|
|
121
149
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return map;
|
|
125
|
-
}
|
|
150
|
+
if (!mapCache.has(sourceScript.snapshot)) {
|
|
151
|
+
mapCache.set(sourceScript.snapshot, new source_map_1.SourceMap(mappings ?? virtualCode.mappings));
|
|
126
152
|
}
|
|
153
|
+
return mapCache.get(sourceScript.snapshot);
|
|
127
154
|
},
|
|
128
|
-
forEach(virtualCode) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
155
|
+
*forEach(virtualCode) {
|
|
156
|
+
const sourceScript = virtualCodeToSourceScriptMap.get(virtualCode);
|
|
157
|
+
yield [
|
|
158
|
+
sourceScript.id,
|
|
159
|
+
sourceScript.snapshot,
|
|
160
|
+
this.get(virtualCode, sourceScript),
|
|
161
|
+
];
|
|
162
|
+
if (virtualCode.associatedScriptMappings) {
|
|
163
|
+
for (const [relatedScriptId, relatedMappings] of virtualCode.associatedScriptMappings) {
|
|
164
|
+
const relatedSourceScript = scriptRegistry.get(relatedScriptId);
|
|
165
|
+
if (relatedSourceScript) {
|
|
166
|
+
yield [
|
|
167
|
+
relatedSourceScript.id,
|
|
168
|
+
relatedSourceScript.snapshot,
|
|
169
|
+
this.get(virtualCode, relatedSourceScript, relatedMappings),
|
|
170
|
+
];
|
|
171
|
+
}
|
|
143
172
|
}
|
|
144
|
-
}
|
|
145
|
-
return map;
|
|
173
|
+
}
|
|
146
174
|
},
|
|
147
175
|
},
|
|
148
176
|
linkedCodeMaps: {
|
|
149
177
|
get(virtualCode) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
178
|
+
const sourceScript = virtualCodeToSourceScriptMap.get(virtualCode);
|
|
179
|
+
let mapCache = virtualCodeToLinkedCodeMap.get(virtualCode.snapshot);
|
|
180
|
+
if (mapCache?.[0] !== sourceScript.snapshot) {
|
|
181
|
+
virtualCodeToLinkedCodeMap.set(virtualCode.snapshot, mapCache = [
|
|
182
|
+
sourceScript.snapshot,
|
|
183
|
+
virtualCode.linkedCodeMappings
|
|
184
|
+
? new linkedCodeMap_1.LinkedCodeMap(virtualCode.linkedCodeMappings)
|
|
185
|
+
: undefined,
|
|
186
|
+
]);
|
|
154
187
|
}
|
|
155
|
-
return
|
|
188
|
+
return mapCache[1];
|
|
156
189
|
},
|
|
157
190
|
},
|
|
158
191
|
};
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
mapOfMap.set(source[0], [source[1], new source_map_1.SourceMap([])]);
|
|
167
|
-
}
|
|
192
|
+
function triggerTargetsDirty(sourceScript) {
|
|
193
|
+
sourceScript.targetIds.forEach(id => {
|
|
194
|
+
const sourceScript = scriptRegistry.get(id);
|
|
195
|
+
if (sourceScript) {
|
|
196
|
+
sourceScript.isAssociationDirty = true;
|
|
197
|
+
}
|
|
198
|
+
});
|
|
168
199
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
sources.add(mapping.source);
|
|
174
|
-
const source = getSourceSnapshot(mapping.source);
|
|
175
|
-
if (!source) {
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
if (!mapOfMap.has(source[0]) || mapOfMap.get(source[0])[0] !== source[1]) {
|
|
179
|
-
mapOfMap.set(source[0], [source[1], new source_map_1.SourceMap(virtualCode.mappings.filter(mapping2 => mapping2.source === mapping.source))]);
|
|
200
|
+
function prepareCreateVirtualCode(sourceScript) {
|
|
201
|
+
for (const id of sourceScript.associatedIds) {
|
|
202
|
+
scriptRegistry.get(id)?.targetIds.delete(sourceScript.id);
|
|
180
203
|
}
|
|
204
|
+
sourceScript.associatedIds.clear();
|
|
205
|
+
sourceScript.isAssociationDirty = false;
|
|
206
|
+
return {
|
|
207
|
+
getAssociatedScript(id) {
|
|
208
|
+
sync(id);
|
|
209
|
+
const relatedSourceScript = scriptRegistry.get(id);
|
|
210
|
+
if (relatedSourceScript) {
|
|
211
|
+
relatedSourceScript.targetIds.add(sourceScript.id);
|
|
212
|
+
sourceScript.associatedIds.add(relatedSourceScript.id);
|
|
213
|
+
}
|
|
214
|
+
return relatedSourceScript;
|
|
215
|
+
},
|
|
216
|
+
};
|
|
181
217
|
}
|
|
182
218
|
}
|
|
183
|
-
exports.
|
|
219
|
+
exports.createLanguage = createLanguage;
|
|
184
220
|
function* forEachEmbeddedCode(virtualCode) {
|
|
185
221
|
yield virtualCode;
|
|
186
222
|
if (virtualCode.embeddedCodes) {
|
package/lib/linkedCodeMap.d.ts
CHANGED
package/lib/types.d.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type { Mapping, SourceMap } from '@volar/source-map';
|
|
2
2
|
import type * as ts from 'typescript';
|
|
3
3
|
import type { LinkedCodeMap } from './linkedCodeMap';
|
|
4
|
-
export interface Language<T> {
|
|
4
|
+
export interface Language<T = unknown> {
|
|
5
5
|
plugins: LanguagePlugin<T>[];
|
|
6
6
|
scripts: {
|
|
7
7
|
get(id: T): SourceScript<T> | undefined;
|
|
8
8
|
set(id: T, snapshot: ts.IScriptSnapshot, languageId?: string, plugins?: LanguagePlugin<T>[]): SourceScript<T> | undefined;
|
|
9
9
|
delete(id: T): void;
|
|
10
|
+
fromVirtualCode(virtualCode: VirtualCode): SourceScript<T>;
|
|
10
11
|
};
|
|
11
12
|
maps: {
|
|
12
|
-
get(virtualCode: VirtualCode,
|
|
13
|
-
forEach(virtualCode: VirtualCode):
|
|
13
|
+
get(virtualCode: VirtualCode, sourceScript: SourceScript<T>, mappings?: Mapping<CodeInformation>[]): SourceMap<CodeInformation>;
|
|
14
|
+
forEach(virtualCode: VirtualCode): Generator<[id: T, snapshot: ts.IScriptSnapshot, map: SourceMap<CodeInformation>]>;
|
|
14
15
|
};
|
|
15
16
|
linkedCodeMaps: {
|
|
16
17
|
get(virtualCode: VirtualCode): LinkedCodeMap | undefined;
|
|
@@ -27,10 +28,14 @@ export interface Language<T> {
|
|
|
27
28
|
asFileName(scriptId: T): string;
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
|
-
export interface SourceScript<T> {
|
|
31
|
+
export interface SourceScript<T = unknown> {
|
|
31
32
|
id: T;
|
|
32
33
|
languageId: string;
|
|
33
34
|
snapshot: ts.IScriptSnapshot;
|
|
35
|
+
targetIds: Set<T>;
|
|
36
|
+
associatedIds: Set<T>;
|
|
37
|
+
associatedOnly: boolean;
|
|
38
|
+
isAssociationDirty?: boolean;
|
|
34
39
|
generated?: {
|
|
35
40
|
root: VirtualCode;
|
|
36
41
|
languagePlugin: LanguagePlugin<T>;
|
|
@@ -43,6 +48,7 @@ export interface VirtualCode {
|
|
|
43
48
|
languageId: string;
|
|
44
49
|
snapshot: ts.IScriptSnapshot;
|
|
45
50
|
mappings: CodeMapping[];
|
|
51
|
+
associatedScriptMappings?: Map<unknown, CodeMapping[]>;
|
|
46
52
|
embeddedCodes?: VirtualCode[];
|
|
47
53
|
linkedCodeMappings?: Mapping[];
|
|
48
54
|
}
|
|
@@ -75,31 +81,54 @@ export interface TypeScriptServiceScript {
|
|
|
75
81
|
code: VirtualCode;
|
|
76
82
|
extension: '.ts' | '.js' | '.mts' | '.mjs' | '.cjs' | '.cts' | '.d.ts' | string;
|
|
77
83
|
scriptKind: ts.ScriptKind;
|
|
84
|
+
/** See #188 */
|
|
85
|
+
preventLeadingOffset?: boolean;
|
|
78
86
|
}
|
|
79
87
|
export interface TypeScriptExtraServiceScript extends TypeScriptServiceScript {
|
|
80
88
|
fileName: string;
|
|
81
89
|
}
|
|
82
|
-
export interface LanguagePlugin<T, K extends VirtualCode = VirtualCode> {
|
|
90
|
+
export interface LanguagePlugin<T = unknown, K extends VirtualCode = VirtualCode> {
|
|
91
|
+
/**
|
|
92
|
+
* For files that are not opened in the IDE, the language ID will not be synchronized to the language server, so a hook is needed to parse the language ID of files that are known extension but not opened in the IDE.
|
|
93
|
+
*/
|
|
83
94
|
getLanguageId(scriptId: T): string | undefined;
|
|
84
|
-
|
|
85
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Generate a virtual code.
|
|
97
|
+
*/
|
|
98
|
+
createVirtualCode?(scriptId: T, languageId: string, snapshot: ts.IScriptSnapshot, ctx: CodegenContext<T>): K | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Incremental update a virtual code. If not provide, call createVirtualCode again.
|
|
101
|
+
*/
|
|
102
|
+
updateVirtualCode?(scriptId: T, virtualCode: K, newSnapshot: ts.IScriptSnapshot, ctx: CodegenContext<T>): K | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* Cleanup a virtual code.
|
|
105
|
+
*/
|
|
86
106
|
disposeVirtualCode?(scriptId: T, virtualCode: K): void;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Some file types should not be parsed or processed as TypeScript files,
|
|
109
|
+
* as they are used only as sources for generated files.
|
|
110
|
+
*
|
|
111
|
+
* This functionality is required only in TS plugin mode.
|
|
112
|
+
*/
|
|
113
|
+
isAssociatedFileOnly?(scriptId: T, languageId: string): boolean;
|
|
114
|
+
typescript?: TypeScriptGenericOptions<K> & TypeScriptNonTSPluginOptions<K>;
|
|
115
|
+
}
|
|
116
|
+
export interface CodegenContext<T = unknown> {
|
|
117
|
+
getAssociatedScript(scriptId: T): SourceScript<T> | undefined;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The following options available to all situations.
|
|
121
|
+
*/
|
|
122
|
+
interface TypeScriptGenericOptions<K> {
|
|
123
|
+
extraFileExtensions: ts.FileExtensionInfo[];
|
|
124
|
+
resolveHiddenExtensions?: boolean;
|
|
125
|
+
getServiceScript(root: K): TypeScriptServiceScript | undefined;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The following options will not be available in TS plugin.
|
|
129
|
+
*/
|
|
130
|
+
interface TypeScriptNonTSPluginOptions<K> {
|
|
131
|
+
getExtraServiceScripts?(fileName: string, rootVirtualCode: K): TypeScriptExtraServiceScript[];
|
|
132
|
+
resolveLanguageServiceHost?(host: ts.LanguageServiceHost): ts.LanguageServiceHost;
|
|
105
133
|
}
|
|
134
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volar/language-core",
|
|
3
|
-
"version": "2.3.0-alpha.
|
|
3
|
+
"version": "2.3.0-alpha.10",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"files": [
|
|
6
6
|
"**/*.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"directory": "packages/language-core"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@volar/source-map": "2.3.0-alpha.
|
|
15
|
+
"@volar/source-map": "2.3.0-alpha.10"
|
|
16
16
|
},
|
|
17
|
-
"gitHead": "
|
|
17
|
+
"gitHead": "44fffdc323ca3ff55818a8656a2726867df53a76"
|
|
18
18
|
}
|