@macroforge/typescript-plugin 0.1.0
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/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1224 -0
- package/dist/source-map.d.ts +16 -0
- package/dist/source-map.d.ts.map +1 -0
- package/dist/source-map.js +2 -0
- package/package.json +45 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,gCAAgC,CAAC;AAqDrD,iBAAS,IAAI,CAAC,OAAO,EAAE;IAAE,UAAU,EAAE,OAAO,EAAE,CAAA;CAAE;mBACxB,EAAE,CAAC,MAAM,CAAC,gBAAgB;EAsnDjD;AAED,SAAS,IAAI,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,1224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
const macroforge_1 = require("macroforge");
|
|
6
|
+
const path_1 = __importDefault(require("path"));
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const FILE_EXTENSIONS = [".ts", ".tsx", ".svelte"];
|
|
9
|
+
function shouldProcess(fileName) {
|
|
10
|
+
const lower = fileName.toLowerCase();
|
|
11
|
+
if (lower.includes("node_modules"))
|
|
12
|
+
return false;
|
|
13
|
+
if (fileName.includes(`${path_1.default.sep}.macroforge${path_1.default.sep}`))
|
|
14
|
+
return false;
|
|
15
|
+
// Skip generated .d.ts files
|
|
16
|
+
if (fileName.endsWith(".macroforge.d.ts"))
|
|
17
|
+
return false;
|
|
18
|
+
return FILE_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
19
|
+
}
|
|
20
|
+
function hasMacroDirectives(text) {
|
|
21
|
+
return (text.includes("@derive") ||
|
|
22
|
+
/\/\*\*\s*@derive\s*\(/i.test(text) ||
|
|
23
|
+
/\/\*\*\s*import\s+macro\b/i.test(text));
|
|
24
|
+
}
|
|
25
|
+
function loadMacroConfig(startDir) {
|
|
26
|
+
let current = startDir;
|
|
27
|
+
const fallback = { keepDecorators: false };
|
|
28
|
+
while (true) {
|
|
29
|
+
const candidate = path_1.default.join(current, "macroforge.json");
|
|
30
|
+
if (fs_1.default.existsSync(candidate)) {
|
|
31
|
+
try {
|
|
32
|
+
const raw = fs_1.default.readFileSync(candidate, "utf8");
|
|
33
|
+
const parsed = JSON.parse(raw);
|
|
34
|
+
return { keepDecorators: Boolean(parsed.keepDecorators) };
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const parent = path_1.default.dirname(current);
|
|
41
|
+
if (parent === current)
|
|
42
|
+
break;
|
|
43
|
+
current = parent;
|
|
44
|
+
}
|
|
45
|
+
return fallback;
|
|
46
|
+
}
|
|
47
|
+
function init(modules) {
|
|
48
|
+
function create(info) {
|
|
49
|
+
const tsModule = modules.typescript;
|
|
50
|
+
// Map to store generated virtual .d.ts files
|
|
51
|
+
const virtualDtsFiles = new Map();
|
|
52
|
+
// Cache snapshots to ensure identity stability for TypeScript's incremental compiler
|
|
53
|
+
const snapshotCache = new Map();
|
|
54
|
+
// Guard against reentrancy
|
|
55
|
+
const processingFiles = new Set();
|
|
56
|
+
// Instantiate native plugin (handles caching and logging in Rust)
|
|
57
|
+
const nativePlugin = new macroforge_1.NativePlugin();
|
|
58
|
+
const getCurrentDirectory = () => info.project.getCurrentDirectory?.() ??
|
|
59
|
+
info.languageServiceHost.getCurrentDirectory?.() ??
|
|
60
|
+
process.cwd();
|
|
61
|
+
const macroConfig = loadMacroConfig(getCurrentDirectory());
|
|
62
|
+
const keepDecorators = macroConfig.keepDecorators;
|
|
63
|
+
// Log helper - delegates to Rust
|
|
64
|
+
const log = (msg) => {
|
|
65
|
+
const line = `[${new Date().toISOString()}] ${msg}`;
|
|
66
|
+
nativePlugin.log(line);
|
|
67
|
+
try {
|
|
68
|
+
info.project.projectService.logger.info(`[macroforge] ${msg}`);
|
|
69
|
+
}
|
|
70
|
+
catch { }
|
|
71
|
+
try {
|
|
72
|
+
console.error(`[macroforge] ${msg}`);
|
|
73
|
+
}
|
|
74
|
+
catch { }
|
|
75
|
+
};
|
|
76
|
+
const ensureVirtualDtsRegistered = (fileName) => {
|
|
77
|
+
const projectService = info.project.projectService;
|
|
78
|
+
const register = projectService?.getOrCreateScriptInfoNotOpenedByClient;
|
|
79
|
+
if (!register)
|
|
80
|
+
return;
|
|
81
|
+
try {
|
|
82
|
+
const scriptInfo = register(fileName, getCurrentDirectory(), info.languageServiceHost,
|
|
83
|
+
/*deferredDeleteOk*/ false);
|
|
84
|
+
if (scriptInfo?.attachToProject) {
|
|
85
|
+
scriptInfo.attachToProject(info.project);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
log(`Failed to register virtual .d.ts ${fileName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const cleanupVirtualDts = (fileName) => {
|
|
93
|
+
const projectService = info.project.projectService;
|
|
94
|
+
const getScriptInfo = projectService?.getScriptInfo;
|
|
95
|
+
if (!getScriptInfo)
|
|
96
|
+
return;
|
|
97
|
+
try {
|
|
98
|
+
const scriptInfo = getScriptInfo.call(projectService, fileName);
|
|
99
|
+
if (!scriptInfo)
|
|
100
|
+
return;
|
|
101
|
+
scriptInfo.detachFromProject?.(info.project);
|
|
102
|
+
if (!scriptInfo.isScriptOpen?.() &&
|
|
103
|
+
scriptInfo.containingProjects?.length === 0) {
|
|
104
|
+
projectService.deleteScriptInfo?.(scriptInfo);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
log(`Failed to clean up virtual .d.ts ${fileName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const projectService = info.project.projectService;
|
|
112
|
+
if (projectService?.setDocument) {
|
|
113
|
+
projectService.setDocument = (key, filePath, sourceFile) => {
|
|
114
|
+
try {
|
|
115
|
+
const scriptInfo = projectService.getScriptInfoForPath?.(filePath) ??
|
|
116
|
+
projectService.getOrCreateScriptInfoNotOpenedByClient?.(filePath, getCurrentDirectory(), info.languageServiceHost,
|
|
117
|
+
/*deferredDeleteOk*/ false);
|
|
118
|
+
if (!scriptInfo) {
|
|
119
|
+
log(`Skipping cache write for missing ScriptInfo at ${filePath}`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
scriptInfo.attachToProject?.(info.project);
|
|
123
|
+
// Mirror the behavior of the original setDocument but avoid throwing when ScriptInfo is absent.
|
|
124
|
+
scriptInfo.cacheSourceFile = { key, sourceFile };
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
log(`Error in guarded setDocument for ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
// Log plugin initialization
|
|
132
|
+
log("Plugin initialized");
|
|
133
|
+
// Process file through macro expansion (caching handled in Rust)
|
|
134
|
+
function processFile(fileName, content, version) {
|
|
135
|
+
// Fast Exit: Empty Content
|
|
136
|
+
if (!content || content.trim().length === 0) {
|
|
137
|
+
return {
|
|
138
|
+
result: {
|
|
139
|
+
code: content,
|
|
140
|
+
types: undefined,
|
|
141
|
+
metadata: undefined,
|
|
142
|
+
diagnostics: [],
|
|
143
|
+
sourceMapping: undefined,
|
|
144
|
+
},
|
|
145
|
+
code: content,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
log(`Processing ${fileName}`);
|
|
150
|
+
const result = nativePlugin.processFile(fileName, content, {
|
|
151
|
+
keepDecorators,
|
|
152
|
+
version,
|
|
153
|
+
});
|
|
154
|
+
// Update virtual .d.ts files
|
|
155
|
+
const virtualDtsFileName = fileName + ".macroforge.d.ts";
|
|
156
|
+
if (result.types) {
|
|
157
|
+
virtualDtsFiles.set(virtualDtsFileName, tsModule.ScriptSnapshot.fromString(result.types));
|
|
158
|
+
ensureVirtualDtsRegistered(virtualDtsFileName);
|
|
159
|
+
log(`Generated virtual .d.ts for ${fileName}`);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
virtualDtsFiles.delete(virtualDtsFileName);
|
|
163
|
+
cleanupVirtualDts(virtualDtsFileName);
|
|
164
|
+
}
|
|
165
|
+
return { result, code: result.code };
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
const errorMessage = e instanceof Error ? e.stack || e.message : String(e);
|
|
169
|
+
log(`Plugin expansion failed for ${fileName}: ${errorMessage}`);
|
|
170
|
+
virtualDtsFiles.delete(fileName + ".macroforge.d.ts");
|
|
171
|
+
cleanupVirtualDts(fileName + ".macroforge.d.ts");
|
|
172
|
+
return {
|
|
173
|
+
result: {
|
|
174
|
+
code: content,
|
|
175
|
+
types: undefined,
|
|
176
|
+
metadata: undefined,
|
|
177
|
+
diagnostics: [],
|
|
178
|
+
sourceMapping: undefined,
|
|
179
|
+
},
|
|
180
|
+
code: content,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Hook getScriptVersion to provide versions for virtual .d.ts files
|
|
185
|
+
const originalGetScriptVersion = info.languageServiceHost.getScriptVersion.bind(info.languageServiceHost);
|
|
186
|
+
info.languageServiceHost.getScriptVersion = (fileName) => {
|
|
187
|
+
try {
|
|
188
|
+
if (virtualDtsFiles.has(fileName)) {
|
|
189
|
+
const sourceFileName = fileName.replace(".macroforge.d.ts", "");
|
|
190
|
+
return originalGetScriptVersion(sourceFileName);
|
|
191
|
+
}
|
|
192
|
+
return originalGetScriptVersion(fileName);
|
|
193
|
+
}
|
|
194
|
+
catch (e) {
|
|
195
|
+
log(`Error in getScriptVersion: ${e instanceof Error ? e.message : String(e)}`);
|
|
196
|
+
return originalGetScriptVersion(fileName);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
// Hook getScriptFileNames to include our virtual .d.ts files
|
|
200
|
+
// This allows TS to "see" these new files as part of the project
|
|
201
|
+
const originalGetScriptFileNames = info.languageServiceHost
|
|
202
|
+
.getScriptFileNames
|
|
203
|
+
? info.languageServiceHost.getScriptFileNames.bind(info.languageServiceHost)
|
|
204
|
+
: () => [];
|
|
205
|
+
info.languageServiceHost.getScriptFileNames = () => {
|
|
206
|
+
try {
|
|
207
|
+
const originalFiles = originalGetScriptFileNames();
|
|
208
|
+
return [...originalFiles, ...Array.from(virtualDtsFiles.keys())];
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
log(`Error in getScriptFileNames: ${e instanceof Error ? e.message : String(e)}`);
|
|
212
|
+
return originalGetScriptFileNames();
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
// Hook fileExists to resolve our virtual .d.ts files
|
|
216
|
+
const originalFileExists = info.languageServiceHost.fileExists
|
|
217
|
+
? info.languageServiceHost.fileExists.bind(info.languageServiceHost)
|
|
218
|
+
: tsModule.sys.fileExists;
|
|
219
|
+
info.languageServiceHost.fileExists = (fileName) => {
|
|
220
|
+
try {
|
|
221
|
+
if (virtualDtsFiles.has(fileName)) {
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
return originalFileExists(fileName);
|
|
225
|
+
}
|
|
226
|
+
catch (e) {
|
|
227
|
+
log(`Error in fileExists: ${e instanceof Error ? e.message : String(e)}`);
|
|
228
|
+
return originalFileExists(fileName);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
// Hook getScriptSnapshot to provide the "expanded" type definition view
|
|
232
|
+
const originalGetScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
|
|
233
|
+
info.languageServiceHost.getScriptSnapshot = (fileName) => {
|
|
234
|
+
try {
|
|
235
|
+
log(`getScriptSnapshot: ${fileName}`);
|
|
236
|
+
// If it's one of our virtual .d.ts files, return its snapshot
|
|
237
|
+
if (virtualDtsFiles.has(fileName)) {
|
|
238
|
+
log(` -> virtual .d.ts cache hit`);
|
|
239
|
+
return virtualDtsFiles.get(fileName);
|
|
240
|
+
}
|
|
241
|
+
// Guard against reentrancy - if we're already processing this file, return original
|
|
242
|
+
if (processingFiles.has(fileName)) {
|
|
243
|
+
log(` -> REENTRANCY DETECTED, returning original`);
|
|
244
|
+
return originalGetScriptSnapshot(fileName);
|
|
245
|
+
}
|
|
246
|
+
// Don't process non-TypeScript files or .expanded.ts files
|
|
247
|
+
if (!shouldProcess(fileName)) {
|
|
248
|
+
log(` -> not processable (excluded file), returning original`);
|
|
249
|
+
return originalGetScriptSnapshot(fileName);
|
|
250
|
+
}
|
|
251
|
+
const snapshot = originalGetScriptSnapshot(fileName);
|
|
252
|
+
if (!snapshot) {
|
|
253
|
+
// Avoid tsserver crashes when a file was reported but no snapshot exists
|
|
254
|
+
log(` -> no snapshot available for ${fileName}, returning empty snapshot`);
|
|
255
|
+
return tsModule.ScriptSnapshot.fromString("");
|
|
256
|
+
}
|
|
257
|
+
const text = snapshot.getText(0, snapshot.getLength());
|
|
258
|
+
// Only process files with macro directives
|
|
259
|
+
if (!hasMacroDirectives(text)) {
|
|
260
|
+
log(` -> no macro directives, returning original`);
|
|
261
|
+
return snapshot;
|
|
262
|
+
}
|
|
263
|
+
log(` -> has @derive, expanding...`);
|
|
264
|
+
// Mark as processing to prevent reentrancy
|
|
265
|
+
processingFiles.add(fileName);
|
|
266
|
+
try {
|
|
267
|
+
const version = info.languageServiceHost.getScriptVersion(fileName);
|
|
268
|
+
log(` -> version: ${version}`);
|
|
269
|
+
// Check if we have a cached snapshot for this version
|
|
270
|
+
const cached = snapshotCache.get(fileName);
|
|
271
|
+
if (cached && cached.version === version) {
|
|
272
|
+
log(` -> snapshot cache hit`);
|
|
273
|
+
return cached.snapshot;
|
|
274
|
+
}
|
|
275
|
+
const { code } = processFile(fileName, text, version);
|
|
276
|
+
log(` -> processFile returned`);
|
|
277
|
+
if (code && code !== text) {
|
|
278
|
+
log(` -> creating expanded snapshot (${code.length} chars)`);
|
|
279
|
+
const expandedSnapshot = tsModule.ScriptSnapshot.fromString(code);
|
|
280
|
+
// Cache the snapshot for stable identity
|
|
281
|
+
snapshotCache.set(fileName, {
|
|
282
|
+
version,
|
|
283
|
+
snapshot: expandedSnapshot,
|
|
284
|
+
});
|
|
285
|
+
log(` -> returning expanded snapshot`);
|
|
286
|
+
return expandedSnapshot;
|
|
287
|
+
}
|
|
288
|
+
// Cache the original snapshot
|
|
289
|
+
snapshotCache.set(fileName, { version, snapshot });
|
|
290
|
+
return snapshot;
|
|
291
|
+
}
|
|
292
|
+
finally {
|
|
293
|
+
processingFiles.delete(fileName);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
catch (e) {
|
|
297
|
+
log(`ERROR in getScriptSnapshot for ${fileName}: ${e instanceof Error ? e.stack || e.message : String(e)}`);
|
|
298
|
+
// Make sure we clean up on error
|
|
299
|
+
processingFiles.delete(fileName);
|
|
300
|
+
return originalGetScriptSnapshot(fileName);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
function toPlainDiagnostic(diag) {
|
|
304
|
+
const message = typeof diag.messageText === "string"
|
|
305
|
+
? diag.messageText
|
|
306
|
+
: diag.messageText.messageText;
|
|
307
|
+
const category = diag.category === tsModule.DiagnosticCategory.Error
|
|
308
|
+
? "error"
|
|
309
|
+
: diag.category === tsModule.DiagnosticCategory.Warning
|
|
310
|
+
? "warning"
|
|
311
|
+
: "message";
|
|
312
|
+
return {
|
|
313
|
+
start: diag.start,
|
|
314
|
+
length: diag.length,
|
|
315
|
+
message,
|
|
316
|
+
code: diag.code,
|
|
317
|
+
category,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function applyMappedDiagnostics(original, mapped) {
|
|
321
|
+
return original.map((diag, idx) => {
|
|
322
|
+
const mappedDiag = mapped[idx];
|
|
323
|
+
if (!mappedDiag ||
|
|
324
|
+
mappedDiag.start === undefined ||
|
|
325
|
+
mappedDiag.length === undefined) {
|
|
326
|
+
return diag;
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
...diag,
|
|
330
|
+
start: mappedDiag.start,
|
|
331
|
+
length: mappedDiag.length,
|
|
332
|
+
};
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
// Hook getSemanticDiagnostics to provide macro errors and map positions
|
|
336
|
+
const originalGetSemanticDiagnostics = info.languageService.getSemanticDiagnostics.bind(info.languageService);
|
|
337
|
+
info.languageService.getSemanticDiagnostics = (fileName) => {
|
|
338
|
+
try {
|
|
339
|
+
log(`getSemanticDiagnostics: ${fileName}`);
|
|
340
|
+
// If it's one of our virtual .d.ts files, don't get diagnostics for it
|
|
341
|
+
if (virtualDtsFiles.has(fileName)) {
|
|
342
|
+
log(` -> skipping virtual .d.ts`);
|
|
343
|
+
return [];
|
|
344
|
+
}
|
|
345
|
+
if (!shouldProcess(fileName)) {
|
|
346
|
+
log(` -> not processable, using original`);
|
|
347
|
+
return originalGetSemanticDiagnostics(fileName);
|
|
348
|
+
}
|
|
349
|
+
log(` -> getting original diagnostics...`);
|
|
350
|
+
const expandedDiagnostics = originalGetSemanticDiagnostics(fileName);
|
|
351
|
+
log(` -> got ${expandedDiagnostics.length} diagnostics`);
|
|
352
|
+
// Map diagnostics using mapper
|
|
353
|
+
const effectiveMapper = nativePlugin.getMapper(fileName);
|
|
354
|
+
let mappedDiagnostics;
|
|
355
|
+
// Collect diagnostics in generated code to report them at decorator positions
|
|
356
|
+
const generatedCodeDiagnostics = [];
|
|
357
|
+
if (effectiveMapper && !effectiveMapper.isEmpty()) {
|
|
358
|
+
log(` -> mapping diagnostics with mapper`);
|
|
359
|
+
mappedDiagnostics = expandedDiagnostics
|
|
360
|
+
.map((diag) => {
|
|
361
|
+
if (diag.start === undefined || diag.length === undefined) {
|
|
362
|
+
return diag;
|
|
363
|
+
}
|
|
364
|
+
const mapped = effectiveMapper.mapSpanToOriginal(diag.start, diag.length);
|
|
365
|
+
if (!mapped) {
|
|
366
|
+
// Diagnostic is in generated code - check if we should convert it
|
|
367
|
+
if (effectiveMapper.isInGenerated(diag.start)) {
|
|
368
|
+
// This is an error in macro-generated code
|
|
369
|
+
// Collect it to report at decorator position
|
|
370
|
+
const macroName = effectiveMapper.generatedBy(diag.start);
|
|
371
|
+
log(` -> collecting diagnostic in generated code (macro: ${macroName}): "${diag.messageText}"`);
|
|
372
|
+
generatedCodeDiagnostics.push(diag);
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
return diag;
|
|
376
|
+
}
|
|
377
|
+
return {
|
|
378
|
+
...diag,
|
|
379
|
+
start: mapped.start,
|
|
380
|
+
length: mapped.length,
|
|
381
|
+
};
|
|
382
|
+
})
|
|
383
|
+
.filter((diag) => diag !== null);
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
// Native plugin is guaranteed to exist after early return check
|
|
387
|
+
log(` -> mapping diagnostics in native plugin`);
|
|
388
|
+
mappedDiagnostics = applyMappedDiagnostics(expandedDiagnostics, nativePlugin.mapDiagnostics(fileName, expandedDiagnostics.map(toPlainDiagnostic)));
|
|
389
|
+
}
|
|
390
|
+
// Get macro diagnostics from Rust (hits cache if already expanded)
|
|
391
|
+
const snapshot = originalGetScriptSnapshot(fileName);
|
|
392
|
+
if (!snapshot) {
|
|
393
|
+
return mappedDiagnostics;
|
|
394
|
+
}
|
|
395
|
+
const text = snapshot.getText(0, snapshot.getLength());
|
|
396
|
+
const version = info.languageServiceHost.getScriptVersion(fileName);
|
|
397
|
+
const { result } = processFile(fileName, text, version);
|
|
398
|
+
// Convert diagnostics from generated code to macro diagnostics
|
|
399
|
+
// pointing to the specific macro name within the decorator
|
|
400
|
+
const generatedDiagsAsMacro = [];
|
|
401
|
+
if (generatedCodeDiagnostics.length > 0 && result.sourceMapping) {
|
|
402
|
+
// Find all @derive decorators with their macro arguments
|
|
403
|
+
const deriveRegex = /@derive\s*\(([^)]*)\)/g;
|
|
404
|
+
const deriveDecorators = [];
|
|
405
|
+
let match;
|
|
406
|
+
while ((match = deriveRegex.exec(text)) !== null) {
|
|
407
|
+
const fullStart = match.index;
|
|
408
|
+
const fullLength = match[0].length;
|
|
409
|
+
const argsStart = match.index + match[0].indexOf("(") + 1;
|
|
410
|
+
const argsText = match[1];
|
|
411
|
+
// Parse individual macro names from the arguments
|
|
412
|
+
const macros = [];
|
|
413
|
+
const macroNameRegex = /([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
414
|
+
let macroMatch;
|
|
415
|
+
while ((macroMatch = macroNameRegex.exec(argsText)) !== null) {
|
|
416
|
+
macros.push({
|
|
417
|
+
name: macroMatch[1],
|
|
418
|
+
start: argsStart + macroMatch.index,
|
|
419
|
+
length: macroMatch[1].length,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
deriveDecorators.push({ fullStart, fullLength, macros });
|
|
423
|
+
}
|
|
424
|
+
// Helper to find the specific macro position for a given expanded position
|
|
425
|
+
const findMacroPosition = (expandedPos, macroName) => {
|
|
426
|
+
// Find the generated region containing this position
|
|
427
|
+
const region = result.sourceMapping.generatedRegions.find((r) => expandedPos >= r.start && expandedPos < r.end);
|
|
428
|
+
if (!region) {
|
|
429
|
+
// Fallback to first decorator
|
|
430
|
+
const firstDec = deriveDecorators[0];
|
|
431
|
+
if (firstDec) {
|
|
432
|
+
const macro = firstDec.macros.find((m) => m.name === macroName);
|
|
433
|
+
if (macro)
|
|
434
|
+
return { start: macro.start, length: macro.length };
|
|
435
|
+
return {
|
|
436
|
+
start: firstDec.fullStart,
|
|
437
|
+
length: firstDec.fullLength,
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
return { start: 0, length: 7 };
|
|
441
|
+
}
|
|
442
|
+
// Find the segment that ends right before this generated region
|
|
443
|
+
const segments = result.sourceMapping.segments;
|
|
444
|
+
let insertionPointInOriginal = 0;
|
|
445
|
+
for (const seg of segments) {
|
|
446
|
+
if (seg.expandedEnd <= region.start) {
|
|
447
|
+
insertionPointInOriginal = seg.originalEnd;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// Find the nearest @derive decorator before this insertion point
|
|
451
|
+
let nearestDecorator = deriveDecorators[0];
|
|
452
|
+
for (const dec of deriveDecorators) {
|
|
453
|
+
if (dec.fullStart < insertionPointInOriginal) {
|
|
454
|
+
nearestDecorator = dec;
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (nearestDecorator) {
|
|
461
|
+
// Find the specific macro within this decorator
|
|
462
|
+
const macro = nearestDecorator.macros.find((m) => m.name === macroName);
|
|
463
|
+
if (macro) {
|
|
464
|
+
return { start: macro.start, length: macro.length };
|
|
465
|
+
}
|
|
466
|
+
// If macro name is "macro" (generic fallback) and there's exactly one macro,
|
|
467
|
+
// use that macro's position
|
|
468
|
+
if ((macroName === "macro" || macroName === "") &&
|
|
469
|
+
nearestDecorator.macros.length === 1) {
|
|
470
|
+
const onlyMacro = nearestDecorator.macros[0];
|
|
471
|
+
return { start: onlyMacro.start, length: onlyMacro.length };
|
|
472
|
+
}
|
|
473
|
+
// Fallback to full decorator if macro not found
|
|
474
|
+
return {
|
|
475
|
+
start: nearestDecorator.fullStart,
|
|
476
|
+
length: nearestDecorator.fullLength,
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
return { start: 0, length: 7 };
|
|
480
|
+
};
|
|
481
|
+
for (const diag of generatedCodeDiagnostics) {
|
|
482
|
+
const diagStart = diag.start ?? 0;
|
|
483
|
+
// Try to get the macro name from the mapper or from the generated region
|
|
484
|
+
let macroName = effectiveMapper?.generatedBy(diagStart) ?? null;
|
|
485
|
+
// If mapper didn't return a name, try to get it from the generated region
|
|
486
|
+
if (!macroName) {
|
|
487
|
+
const region = result.sourceMapping.generatedRegions.find((r) => diagStart >= r.start && diagStart < r.end);
|
|
488
|
+
macroName = region?.sourceMacro ?? "macro";
|
|
489
|
+
}
|
|
490
|
+
// Extract just the macro name if it contains a path (e.g., "derive::Debug" -> "Debug")
|
|
491
|
+
const simpleMacroName = macroName.includes("::")
|
|
492
|
+
? (macroName.split("::").pop() ?? macroName)
|
|
493
|
+
: macroName;
|
|
494
|
+
log(` -> diagnostic at ${diagStart}, macroName="${macroName}", simpleMacroName="${simpleMacroName}"`);
|
|
495
|
+
log(` -> generatedRegions: ${JSON.stringify(result.sourceMapping.generatedRegions)}`);
|
|
496
|
+
log(` -> deriveDecorators: ${JSON.stringify(deriveDecorators.map((d) => ({ fullStart: d.fullStart, macros: d.macros })))}`);
|
|
497
|
+
const position = findMacroPosition(diagStart, simpleMacroName);
|
|
498
|
+
log(` -> resolved position: ${JSON.stringify(position)}`);
|
|
499
|
+
generatedDiagsAsMacro.push({
|
|
500
|
+
file: info.languageService.getProgram()?.getSourceFile(fileName),
|
|
501
|
+
start: position.start,
|
|
502
|
+
length: position.length,
|
|
503
|
+
messageText: `[${simpleMacroName}] ${typeof diag.messageText === "string" ? diag.messageText : diag.messageText.messageText}`,
|
|
504
|
+
category: diag.category,
|
|
505
|
+
code: 9998, // Different code for generated code errors
|
|
506
|
+
source: "macroforge-generated",
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
log(` -> converted ${generatedDiagsAsMacro.length} generated code diagnostics`);
|
|
510
|
+
}
|
|
511
|
+
else if (generatedCodeDiagnostics.length > 0) {
|
|
512
|
+
// Fallback when no source mapping available
|
|
513
|
+
const deriveMatch = text.match(/@derive\s*\(/);
|
|
514
|
+
const decoratorStart = deriveMatch?.index ?? 0;
|
|
515
|
+
const decoratorLength = deriveMatch?.[0].length ?? 7;
|
|
516
|
+
for (const diag of generatedCodeDiagnostics) {
|
|
517
|
+
const macroName = effectiveMapper?.generatedBy(diag.start ?? 0) ?? "macro";
|
|
518
|
+
generatedDiagsAsMacro.push({
|
|
519
|
+
file: info.languageService.getProgram()?.getSourceFile(fileName),
|
|
520
|
+
start: decoratorStart,
|
|
521
|
+
length: decoratorLength,
|
|
522
|
+
messageText: `[${macroName}] ${typeof diag.messageText === "string" ? diag.messageText : diag.messageText.messageText}`,
|
|
523
|
+
category: diag.category,
|
|
524
|
+
code: 9998, // Different code for generated code errors
|
|
525
|
+
source: "macroforge-generated",
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
log(` -> converted ${generatedDiagsAsMacro.length} generated code diagnostics (fallback)`);
|
|
529
|
+
}
|
|
530
|
+
if (!result.diagnostics || result.diagnostics.length === 0) {
|
|
531
|
+
return [...mappedDiagnostics, ...generatedDiagsAsMacro];
|
|
532
|
+
}
|
|
533
|
+
const macroDiagnostics = result.diagnostics.map((d) => {
|
|
534
|
+
const category = d.level === "error"
|
|
535
|
+
? tsModule.DiagnosticCategory.Error
|
|
536
|
+
: d.level === "warning"
|
|
537
|
+
? tsModule.DiagnosticCategory.Warning
|
|
538
|
+
: tsModule.DiagnosticCategory.Message;
|
|
539
|
+
return {
|
|
540
|
+
file: info.languageService.getProgram()?.getSourceFile(fileName),
|
|
541
|
+
start: d.start || 0,
|
|
542
|
+
length: (d.end || 0) - (d.start || 0),
|
|
543
|
+
messageText: d.message,
|
|
544
|
+
category,
|
|
545
|
+
code: 9999, // Custom error code
|
|
546
|
+
source: "macroforge",
|
|
547
|
+
};
|
|
548
|
+
});
|
|
549
|
+
return [
|
|
550
|
+
...mappedDiagnostics,
|
|
551
|
+
...macroDiagnostics,
|
|
552
|
+
...generatedDiagsAsMacro,
|
|
553
|
+
];
|
|
554
|
+
}
|
|
555
|
+
catch (e) {
|
|
556
|
+
log(`Error in getSemanticDiagnostics for ${fileName}: ${e instanceof Error ? e.stack || e.message : String(e)}`);
|
|
557
|
+
return originalGetSemanticDiagnostics(fileName);
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
// Hook getSyntacticDiagnostics to map positions
|
|
561
|
+
const originalGetSyntacticDiagnostics = info.languageService.getSyntacticDiagnostics.bind(info.languageService);
|
|
562
|
+
info.languageService.getSyntacticDiagnostics = (fileName) => {
|
|
563
|
+
try {
|
|
564
|
+
log(`getSyntacticDiagnostics: ${fileName}`);
|
|
565
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
566
|
+
log(` -> using original`);
|
|
567
|
+
return originalGetSyntacticDiagnostics(fileName);
|
|
568
|
+
}
|
|
569
|
+
// Ensure mapper ready
|
|
570
|
+
nativePlugin.getMapper(fileName);
|
|
571
|
+
const expandedDiagnostics = originalGetSyntacticDiagnostics(fileName);
|
|
572
|
+
log(` -> got ${expandedDiagnostics.length} diagnostics, mapping...`);
|
|
573
|
+
// Native plugin is guaranteed to exist after early return check
|
|
574
|
+
const result = applyMappedDiagnostics(expandedDiagnostics, nativePlugin.mapDiagnostics(fileName, expandedDiagnostics.map(toPlainDiagnostic)));
|
|
575
|
+
log(` -> returning ${result.length} mapped diagnostics`);
|
|
576
|
+
return result;
|
|
577
|
+
}
|
|
578
|
+
catch (e) {
|
|
579
|
+
log(`ERROR in getSyntacticDiagnostics: ${e instanceof Error ? e.stack || e.message : String(e)}`);
|
|
580
|
+
return originalGetSyntacticDiagnostics(fileName);
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
// Hook getQuickInfoAtPosition to map input position and output spans
|
|
584
|
+
const originalGetQuickInfoAtPosition = info.languageService.getQuickInfoAtPosition.bind(info.languageService);
|
|
585
|
+
info.languageService.getQuickInfoAtPosition = (fileName, position) => {
|
|
586
|
+
try {
|
|
587
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
588
|
+
return originalGetQuickInfoAtPosition(fileName, position);
|
|
589
|
+
}
|
|
590
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
591
|
+
if (!mapper) {
|
|
592
|
+
return originalGetQuickInfoAtPosition(fileName, position);
|
|
593
|
+
}
|
|
594
|
+
// Map original position to expanded
|
|
595
|
+
const expandedPos = mapper.originalToExpanded(position);
|
|
596
|
+
const result = originalGetQuickInfoAtPosition(fileName, expandedPos);
|
|
597
|
+
if (!result)
|
|
598
|
+
return result;
|
|
599
|
+
// Map result spans back to original
|
|
600
|
+
const mappedTextSpan = mapper.mapSpanToOriginal(result.textSpan.start, result.textSpan.length);
|
|
601
|
+
if (!mappedTextSpan)
|
|
602
|
+
return undefined; // In generated code - hide hover
|
|
603
|
+
return {
|
|
604
|
+
...result,
|
|
605
|
+
textSpan: {
|
|
606
|
+
start: mappedTextSpan.start,
|
|
607
|
+
length: mappedTextSpan.length,
|
|
608
|
+
},
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
catch (e) {
|
|
612
|
+
log(`Error in getQuickInfoAtPosition: ${e instanceof Error ? e.message : String(e)}`);
|
|
613
|
+
return originalGetQuickInfoAtPosition(fileName, position);
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
// Hook getCompletionsAtPosition to map input position
|
|
617
|
+
const originalGetCompletionsAtPosition = info.languageService.getCompletionsAtPosition.bind(info.languageService);
|
|
618
|
+
info.languageService.getCompletionsAtPosition = (fileName, position, options, formattingSettings) => {
|
|
619
|
+
try {
|
|
620
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
621
|
+
return originalGetCompletionsAtPosition(fileName, position, options, formattingSettings);
|
|
622
|
+
}
|
|
623
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
624
|
+
if (!mapper) {
|
|
625
|
+
return originalGetCompletionsAtPosition(fileName, position, options, formattingSettings);
|
|
626
|
+
}
|
|
627
|
+
const expandedPos = mapper.originalToExpanded(position);
|
|
628
|
+
const result = originalGetCompletionsAtPosition(fileName, expandedPos, options, formattingSettings);
|
|
629
|
+
if (!result)
|
|
630
|
+
return result;
|
|
631
|
+
// Map optionalReplacementSpan if present
|
|
632
|
+
let mappedOptionalSpan = undefined;
|
|
633
|
+
if (result.optionalReplacementSpan) {
|
|
634
|
+
const mapped = mapper.mapSpanToOriginal(result.optionalReplacementSpan.start, result.optionalReplacementSpan.length);
|
|
635
|
+
if (mapped) {
|
|
636
|
+
mappedOptionalSpan = { start: mapped.start, length: mapped.length };
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
// Map entries replacementSpan
|
|
640
|
+
const mappedEntries = result.entries.map((entry) => {
|
|
641
|
+
if (!entry.replacementSpan)
|
|
642
|
+
return entry;
|
|
643
|
+
const mapped = mapper.mapSpanToOriginal(entry.replacementSpan.start, entry.replacementSpan.length);
|
|
644
|
+
if (!mapped)
|
|
645
|
+
return { ...entry, replacementSpan: undefined }; // Remove invalid span
|
|
646
|
+
return {
|
|
647
|
+
...entry,
|
|
648
|
+
replacementSpan: { start: mapped.start, length: mapped.length },
|
|
649
|
+
};
|
|
650
|
+
});
|
|
651
|
+
return {
|
|
652
|
+
...result,
|
|
653
|
+
optionalReplacementSpan: mappedOptionalSpan,
|
|
654
|
+
entries: mappedEntries,
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
catch (e) {
|
|
658
|
+
log(`Error in getCompletionsAtPosition: ${e instanceof Error ? e.message : String(e)}`);
|
|
659
|
+
return originalGetCompletionsAtPosition(fileName, position, options, formattingSettings);
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
// Hook getDefinitionAtPosition to map input and output positions
|
|
663
|
+
const originalGetDefinitionAtPosition = info.languageService.getDefinitionAtPosition.bind(info.languageService);
|
|
664
|
+
info.languageService.getDefinitionAtPosition = (fileName, position) => {
|
|
665
|
+
try {
|
|
666
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
667
|
+
return originalGetDefinitionAtPosition(fileName, position);
|
|
668
|
+
}
|
|
669
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
670
|
+
if (!mapper) {
|
|
671
|
+
return originalGetDefinitionAtPosition(fileName, position);
|
|
672
|
+
}
|
|
673
|
+
const expandedPos = mapper.originalToExpanded(position);
|
|
674
|
+
const definitions = originalGetDefinitionAtPosition(fileName, expandedPos);
|
|
675
|
+
if (!definitions)
|
|
676
|
+
return definitions;
|
|
677
|
+
// Map each definition's span back to original (only for same file)
|
|
678
|
+
return definitions.reduce((acc, def) => {
|
|
679
|
+
if (def.fileName !== fileName) {
|
|
680
|
+
acc.push(def);
|
|
681
|
+
return acc;
|
|
682
|
+
}
|
|
683
|
+
const defMapper = nativePlugin.getMapper(def.fileName);
|
|
684
|
+
if (!defMapper) {
|
|
685
|
+
acc.push(def);
|
|
686
|
+
return acc;
|
|
687
|
+
}
|
|
688
|
+
const mapped = defMapper.mapSpanToOriginal(def.textSpan.start, def.textSpan.length);
|
|
689
|
+
if (mapped) {
|
|
690
|
+
acc.push({
|
|
691
|
+
...def,
|
|
692
|
+
textSpan: { start: mapped.start, length: mapped.length },
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
return acc;
|
|
696
|
+
}, []);
|
|
697
|
+
}
|
|
698
|
+
catch (e) {
|
|
699
|
+
log(`Error in getDefinitionAtPosition: ${e instanceof Error ? e.message : String(e)}`);
|
|
700
|
+
return originalGetDefinitionAtPosition(fileName, position);
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
// Hook getDefinitionAndBoundSpan for more complete definition handling
|
|
704
|
+
const originalGetDefinitionAndBoundSpan = info.languageService.getDefinitionAndBoundSpan.bind(info.languageService);
|
|
705
|
+
info.languageService.getDefinitionAndBoundSpan = (fileName, position) => {
|
|
706
|
+
try {
|
|
707
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
708
|
+
return originalGetDefinitionAndBoundSpan(fileName, position);
|
|
709
|
+
}
|
|
710
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
711
|
+
if (!mapper) {
|
|
712
|
+
return originalGetDefinitionAndBoundSpan(fileName, position);
|
|
713
|
+
}
|
|
714
|
+
const expandedPos = mapper.originalToExpanded(position);
|
|
715
|
+
const result = originalGetDefinitionAndBoundSpan(fileName, expandedPos);
|
|
716
|
+
if (!result)
|
|
717
|
+
return result;
|
|
718
|
+
// Map textSpan back to original
|
|
719
|
+
const mappedTextSpan = mapper.mapSpanToOriginal(result.textSpan.start, result.textSpan.length);
|
|
720
|
+
if (!mappedTextSpan)
|
|
721
|
+
return undefined; // In generated code
|
|
722
|
+
// Map each definition's span
|
|
723
|
+
const mappedDefinitions = result.definitions?.reduce((acc, def) => {
|
|
724
|
+
if (def.fileName !== fileName) {
|
|
725
|
+
acc.push(def);
|
|
726
|
+
return acc;
|
|
727
|
+
}
|
|
728
|
+
const defMapper = nativePlugin.getMapper(def.fileName);
|
|
729
|
+
if (!defMapper) {
|
|
730
|
+
acc.push(def);
|
|
731
|
+
return acc;
|
|
732
|
+
}
|
|
733
|
+
const mapped = defMapper.mapSpanToOriginal(def.textSpan.start, def.textSpan.length);
|
|
734
|
+
if (mapped) {
|
|
735
|
+
acc.push({
|
|
736
|
+
...def,
|
|
737
|
+
textSpan: { start: mapped.start, length: mapped.length },
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
return acc;
|
|
741
|
+
}, []);
|
|
742
|
+
return {
|
|
743
|
+
textSpan: {
|
|
744
|
+
start: mappedTextSpan.start,
|
|
745
|
+
length: mappedTextSpan.length,
|
|
746
|
+
},
|
|
747
|
+
definitions: mappedDefinitions,
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
catch (e) {
|
|
751
|
+
log(`Error in getDefinitionAndBoundSpan: ${e instanceof Error ? e.message : String(e)}`);
|
|
752
|
+
return originalGetDefinitionAndBoundSpan(fileName, position);
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
// Hook getTypeDefinitionAtPosition
|
|
756
|
+
const originalGetTypeDefinitionAtPosition = info.languageService.getTypeDefinitionAtPosition.bind(info.languageService);
|
|
757
|
+
info.languageService.getTypeDefinitionAtPosition = (fileName, position) => {
|
|
758
|
+
try {
|
|
759
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
760
|
+
return originalGetTypeDefinitionAtPosition(fileName, position);
|
|
761
|
+
}
|
|
762
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
763
|
+
if (!mapper) {
|
|
764
|
+
return originalGetTypeDefinitionAtPosition(fileName, position);
|
|
765
|
+
}
|
|
766
|
+
const expandedPos = mapper.originalToExpanded(position);
|
|
767
|
+
const definitions = originalGetTypeDefinitionAtPosition(fileName, expandedPos);
|
|
768
|
+
if (!definitions)
|
|
769
|
+
return definitions;
|
|
770
|
+
return definitions.reduce((acc, def) => {
|
|
771
|
+
if (def.fileName !== fileName) {
|
|
772
|
+
acc.push(def);
|
|
773
|
+
return acc;
|
|
774
|
+
}
|
|
775
|
+
const defMapper = nativePlugin.getMapper(def.fileName);
|
|
776
|
+
if (!defMapper) {
|
|
777
|
+
acc.push(def);
|
|
778
|
+
return acc;
|
|
779
|
+
}
|
|
780
|
+
const mapped = defMapper.mapSpanToOriginal(def.textSpan.start, def.textSpan.length);
|
|
781
|
+
if (mapped) {
|
|
782
|
+
acc.push({
|
|
783
|
+
...def,
|
|
784
|
+
textSpan: { start: mapped.start, length: mapped.length },
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
return acc;
|
|
788
|
+
}, []);
|
|
789
|
+
}
|
|
790
|
+
catch (e) {
|
|
791
|
+
log(`Error in getTypeDefinitionAtPosition: ${e instanceof Error ? e.message : String(e)}`);
|
|
792
|
+
return originalGetTypeDefinitionAtPosition(fileName, position);
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
// Hook getReferencesAtPosition
|
|
796
|
+
const originalGetReferencesAtPosition = info.languageService.getReferencesAtPosition.bind(info.languageService);
|
|
797
|
+
info.languageService.getReferencesAtPosition = (fileName, position) => {
|
|
798
|
+
try {
|
|
799
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
800
|
+
return originalGetReferencesAtPosition(fileName, position);
|
|
801
|
+
}
|
|
802
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
803
|
+
if (!mapper) {
|
|
804
|
+
return originalGetReferencesAtPosition(fileName, position);
|
|
805
|
+
}
|
|
806
|
+
const expandedPos = mapper.originalToExpanded(position);
|
|
807
|
+
const refs = originalGetReferencesAtPosition(fileName, expandedPos);
|
|
808
|
+
if (!refs)
|
|
809
|
+
return refs;
|
|
810
|
+
return refs.reduce((acc, ref) => {
|
|
811
|
+
if (!shouldProcess(ref.fileName)) {
|
|
812
|
+
acc.push(ref);
|
|
813
|
+
return acc;
|
|
814
|
+
}
|
|
815
|
+
const refMapper = nativePlugin.getMapper(ref.fileName);
|
|
816
|
+
if (!refMapper) {
|
|
817
|
+
acc.push(ref);
|
|
818
|
+
return acc;
|
|
819
|
+
}
|
|
820
|
+
const mapped = refMapper.mapSpanToOriginal(ref.textSpan.start, ref.textSpan.length);
|
|
821
|
+
if (mapped) {
|
|
822
|
+
acc.push({
|
|
823
|
+
...ref,
|
|
824
|
+
textSpan: { start: mapped.start, length: mapped.length },
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
return acc;
|
|
828
|
+
}, []);
|
|
829
|
+
}
|
|
830
|
+
catch (e) {
|
|
831
|
+
log(`Error in getReferencesAtPosition: ${e instanceof Error ? e.message : String(e)}`);
|
|
832
|
+
return originalGetReferencesAtPosition(fileName, position);
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
// Hook findReferences
|
|
836
|
+
const originalFindReferences = info.languageService.findReferences.bind(info.languageService);
|
|
837
|
+
info.languageService.findReferences = (fileName, position) => {
|
|
838
|
+
try {
|
|
839
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
840
|
+
return originalFindReferences(fileName, position);
|
|
841
|
+
}
|
|
842
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
843
|
+
if (!mapper) {
|
|
844
|
+
return originalFindReferences(fileName, position);
|
|
845
|
+
}
|
|
846
|
+
const expandedPos = mapper.originalToExpanded(position);
|
|
847
|
+
const refSymbols = originalFindReferences(fileName, expandedPos);
|
|
848
|
+
if (!refSymbols)
|
|
849
|
+
return refSymbols;
|
|
850
|
+
return refSymbols
|
|
851
|
+
.map((refSymbol) => ({
|
|
852
|
+
...refSymbol,
|
|
853
|
+
references: refSymbol.references.reduce((acc, ref) => {
|
|
854
|
+
if (!shouldProcess(ref.fileName)) {
|
|
855
|
+
acc.push(ref);
|
|
856
|
+
return acc;
|
|
857
|
+
}
|
|
858
|
+
const refMapper = nativePlugin.getMapper(ref.fileName);
|
|
859
|
+
if (!refMapper) {
|
|
860
|
+
acc.push(ref);
|
|
861
|
+
return acc;
|
|
862
|
+
}
|
|
863
|
+
const mapped = refMapper.mapSpanToOriginal(ref.textSpan.start, ref.textSpan.length);
|
|
864
|
+
if (mapped) {
|
|
865
|
+
acc.push({
|
|
866
|
+
...ref,
|
|
867
|
+
textSpan: { start: mapped.start, length: mapped.length },
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
return acc;
|
|
871
|
+
}, []),
|
|
872
|
+
}))
|
|
873
|
+
.filter((s) => s.references.length > 0);
|
|
874
|
+
}
|
|
875
|
+
catch (e) {
|
|
876
|
+
log(`Error in findReferences: ${e instanceof Error ? e.message : String(e)}`);
|
|
877
|
+
return originalFindReferences(fileName, position);
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
// Hook getSignatureHelpItems
|
|
881
|
+
const originalGetSignatureHelpItems = info.languageService.getSignatureHelpItems.bind(info.languageService);
|
|
882
|
+
info.languageService.getSignatureHelpItems = (fileName, position, options) => {
|
|
883
|
+
try {
|
|
884
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
885
|
+
return originalGetSignatureHelpItems(fileName, position, options);
|
|
886
|
+
}
|
|
887
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
888
|
+
if (!mapper) {
|
|
889
|
+
return originalGetSignatureHelpItems(fileName, position, options);
|
|
890
|
+
}
|
|
891
|
+
const expandedPos = mapper.originalToExpanded(position);
|
|
892
|
+
const result = originalGetSignatureHelpItems(fileName, expandedPos, options);
|
|
893
|
+
if (!result)
|
|
894
|
+
return result;
|
|
895
|
+
// Map applicableSpan back to original
|
|
896
|
+
const mappedSpan = mapper.mapSpanToOriginal(result.applicableSpan.start, result.applicableSpan.length);
|
|
897
|
+
if (!mappedSpan)
|
|
898
|
+
return undefined;
|
|
899
|
+
return {
|
|
900
|
+
...result,
|
|
901
|
+
applicableSpan: {
|
|
902
|
+
start: mappedSpan.start,
|
|
903
|
+
length: mappedSpan.length,
|
|
904
|
+
},
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
catch (e) {
|
|
908
|
+
log(`Error in getSignatureHelpItems: ${e instanceof Error ? e.message : String(e)}`);
|
|
909
|
+
return originalGetSignatureHelpItems(fileName, position, options);
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
// Hook getRenameInfo
|
|
913
|
+
const originalGetRenameInfo = info.languageService.getRenameInfo.bind(info.languageService);
|
|
914
|
+
const callGetRenameInfo = (fileName, position, options) => {
|
|
915
|
+
// Prefer object overload if available; otherwise fall back to legacy args
|
|
916
|
+
if (originalGetRenameInfo.length <= 2) {
|
|
917
|
+
return originalGetRenameInfo(fileName, position, options);
|
|
918
|
+
}
|
|
919
|
+
return originalGetRenameInfo(fileName, position, options?.allowRenameOfImportPath);
|
|
920
|
+
};
|
|
921
|
+
info.languageService.getRenameInfo = (fileName, position, options) => {
|
|
922
|
+
try {
|
|
923
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
924
|
+
return callGetRenameInfo(fileName, position, options);
|
|
925
|
+
}
|
|
926
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
927
|
+
if (!mapper) {
|
|
928
|
+
return callGetRenameInfo(fileName, position, options);
|
|
929
|
+
}
|
|
930
|
+
const expandedPos = mapper.originalToExpanded(position);
|
|
931
|
+
const result = callGetRenameInfo(fileName, expandedPos, options);
|
|
932
|
+
if (!result.canRename || !result.triggerSpan)
|
|
933
|
+
return result;
|
|
934
|
+
const mappedSpan = mapper.mapSpanToOriginal(result.triggerSpan.start, result.triggerSpan.length);
|
|
935
|
+
if (!mappedSpan) {
|
|
936
|
+
return {
|
|
937
|
+
canRename: false,
|
|
938
|
+
localizedErrorMessage: "Cannot rename in generated code",
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
return {
|
|
942
|
+
...result,
|
|
943
|
+
triggerSpan: { start: mappedSpan.start, length: mappedSpan.length },
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
catch (e) {
|
|
947
|
+
log(`Error in getRenameInfo: ${e instanceof Error ? e.message : String(e)}`);
|
|
948
|
+
return originalGetRenameInfo(fileName, position, options);
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
// Hook findRenameLocations (newer overload prefers options object)
|
|
952
|
+
const originalFindRenameLocations = info.languageService.findRenameLocations.bind(info.languageService);
|
|
953
|
+
const callFindRenameLocations = (fileName, position, opts) => {
|
|
954
|
+
// Prefer object overload if available; otherwise fall back to legacy args
|
|
955
|
+
if (originalFindRenameLocations.length <= 3) {
|
|
956
|
+
return originalFindRenameLocations(fileName, position, opts);
|
|
957
|
+
}
|
|
958
|
+
return originalFindRenameLocations(fileName, position, !!opts?.findInStrings, !!opts?.findInComments, !!opts?.providePrefixAndSuffixTextForRename);
|
|
959
|
+
};
|
|
960
|
+
info.languageService.findRenameLocations = (fileName, position, options) => {
|
|
961
|
+
try {
|
|
962
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
963
|
+
return callFindRenameLocations(fileName, position, options);
|
|
964
|
+
}
|
|
965
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
966
|
+
if (!mapper) {
|
|
967
|
+
return callFindRenameLocations(fileName, position, options);
|
|
968
|
+
}
|
|
969
|
+
const expandedPos = mapper.originalToExpanded(position);
|
|
970
|
+
const locations = callFindRenameLocations(fileName, expandedPos, options);
|
|
971
|
+
if (!locations)
|
|
972
|
+
return locations;
|
|
973
|
+
return locations.reduce((acc, loc) => {
|
|
974
|
+
if (!shouldProcess(loc.fileName)) {
|
|
975
|
+
acc.push(loc);
|
|
976
|
+
return acc;
|
|
977
|
+
}
|
|
978
|
+
const locMapper = nativePlugin.getMapper(loc.fileName);
|
|
979
|
+
if (!locMapper) {
|
|
980
|
+
acc.push(loc);
|
|
981
|
+
return acc;
|
|
982
|
+
}
|
|
983
|
+
const mapped = locMapper.mapSpanToOriginal(loc.textSpan.start, loc.textSpan.length);
|
|
984
|
+
if (mapped) {
|
|
985
|
+
acc.push({
|
|
986
|
+
...loc,
|
|
987
|
+
textSpan: { start: mapped.start, length: mapped.length },
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
return acc;
|
|
991
|
+
}, []);
|
|
992
|
+
}
|
|
993
|
+
catch (e) {
|
|
994
|
+
log(`Error in findRenameLocations: ${e instanceof Error ? e.message : String(e)}`);
|
|
995
|
+
return callFindRenameLocations(fileName, position, options);
|
|
996
|
+
}
|
|
997
|
+
};
|
|
998
|
+
// Hook getDocumentHighlights
|
|
999
|
+
const originalGetDocumentHighlights = info.languageService.getDocumentHighlights.bind(info.languageService);
|
|
1000
|
+
info.languageService.getDocumentHighlights = (fileName, position, filesToSearch) => {
|
|
1001
|
+
try {
|
|
1002
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
1003
|
+
return originalGetDocumentHighlights(fileName, position, filesToSearch);
|
|
1004
|
+
}
|
|
1005
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
1006
|
+
if (!mapper) {
|
|
1007
|
+
return originalGetDocumentHighlights(fileName, position, filesToSearch);
|
|
1008
|
+
}
|
|
1009
|
+
const expandedPos = mapper.originalToExpanded(position);
|
|
1010
|
+
const highlights = originalGetDocumentHighlights(fileName, expandedPos, filesToSearch);
|
|
1011
|
+
if (!highlights)
|
|
1012
|
+
return highlights;
|
|
1013
|
+
return highlights
|
|
1014
|
+
.map((docHighlight) => ({
|
|
1015
|
+
...docHighlight,
|
|
1016
|
+
highlightSpans: docHighlight.highlightSpans.reduce((acc, span) => {
|
|
1017
|
+
if (!shouldProcess(docHighlight.fileName)) {
|
|
1018
|
+
acc.push(span);
|
|
1019
|
+
return acc;
|
|
1020
|
+
}
|
|
1021
|
+
const spanMapper = nativePlugin.getMapper(docHighlight.fileName);
|
|
1022
|
+
if (!spanMapper) {
|
|
1023
|
+
acc.push(span);
|
|
1024
|
+
return acc;
|
|
1025
|
+
}
|
|
1026
|
+
const mapped = spanMapper.mapSpanToOriginal(span.textSpan.start, span.textSpan.length);
|
|
1027
|
+
if (mapped) {
|
|
1028
|
+
acc.push({
|
|
1029
|
+
...span,
|
|
1030
|
+
textSpan: { start: mapped.start, length: mapped.length },
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
return acc;
|
|
1034
|
+
}, []),
|
|
1035
|
+
}))
|
|
1036
|
+
.filter((h) => h.highlightSpans.length > 0);
|
|
1037
|
+
}
|
|
1038
|
+
catch (e) {
|
|
1039
|
+
log(`Error in getDocumentHighlights: ${e instanceof Error ? e.message : String(e)}`);
|
|
1040
|
+
return originalGetDocumentHighlights(fileName, position, filesToSearch);
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
// Hook getImplementationAtPosition
|
|
1044
|
+
const originalGetImplementationAtPosition = info.languageService.getImplementationAtPosition.bind(info.languageService);
|
|
1045
|
+
info.languageService.getImplementationAtPosition = (fileName, position) => {
|
|
1046
|
+
try {
|
|
1047
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
1048
|
+
return originalGetImplementationAtPosition(fileName, position);
|
|
1049
|
+
}
|
|
1050
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
1051
|
+
if (!mapper) {
|
|
1052
|
+
return originalGetImplementationAtPosition(fileName, position);
|
|
1053
|
+
}
|
|
1054
|
+
const expandedPos = mapper.originalToExpanded(position);
|
|
1055
|
+
const implementations = originalGetImplementationAtPosition(fileName, expandedPos);
|
|
1056
|
+
if (!implementations)
|
|
1057
|
+
return implementations;
|
|
1058
|
+
return implementations.reduce((acc, impl) => {
|
|
1059
|
+
if (!shouldProcess(impl.fileName)) {
|
|
1060
|
+
acc.push(impl);
|
|
1061
|
+
return acc;
|
|
1062
|
+
}
|
|
1063
|
+
const implMapper = nativePlugin.getMapper(impl.fileName);
|
|
1064
|
+
if (!implMapper) {
|
|
1065
|
+
acc.push(impl);
|
|
1066
|
+
return acc;
|
|
1067
|
+
}
|
|
1068
|
+
const mapped = implMapper.mapSpanToOriginal(impl.textSpan.start, impl.textSpan.length);
|
|
1069
|
+
if (mapped) {
|
|
1070
|
+
acc.push({
|
|
1071
|
+
...impl,
|
|
1072
|
+
textSpan: { start: mapped.start, length: mapped.length },
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
return acc;
|
|
1076
|
+
}, []);
|
|
1077
|
+
}
|
|
1078
|
+
catch (e) {
|
|
1079
|
+
log(`Error in getImplementationAtPosition: ${e instanceof Error ? e.message : String(e)}`);
|
|
1080
|
+
return originalGetImplementationAtPosition(fileName, position);
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
1083
|
+
// Hook getCodeFixesAtPosition
|
|
1084
|
+
const originalGetCodeFixesAtPosition = info.languageService.getCodeFixesAtPosition.bind(info.languageService);
|
|
1085
|
+
info.languageService.getCodeFixesAtPosition = (fileName, start, end, errorCodes, formatOptions, preferences) => {
|
|
1086
|
+
try {
|
|
1087
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
1088
|
+
return originalGetCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences);
|
|
1089
|
+
}
|
|
1090
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
1091
|
+
if (!mapper) {
|
|
1092
|
+
return originalGetCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences);
|
|
1093
|
+
}
|
|
1094
|
+
const expandedStart = mapper.originalToExpanded(start);
|
|
1095
|
+
const expandedEnd = mapper.originalToExpanded(end);
|
|
1096
|
+
return originalGetCodeFixesAtPosition(fileName, expandedStart, expandedEnd, errorCodes, formatOptions, preferences);
|
|
1097
|
+
}
|
|
1098
|
+
catch (e) {
|
|
1099
|
+
log(`Error in getCodeFixesAtPosition: ${e instanceof Error ? e.message : String(e)}`);
|
|
1100
|
+
return originalGetCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences);
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
1103
|
+
// Hook getNavigationTree
|
|
1104
|
+
const originalGetNavigationTree = info.languageService.getNavigationTree.bind(info.languageService);
|
|
1105
|
+
info.languageService.getNavigationTree = (fileName) => {
|
|
1106
|
+
try {
|
|
1107
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
1108
|
+
return originalGetNavigationTree(fileName);
|
|
1109
|
+
}
|
|
1110
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
1111
|
+
if (!mapper) {
|
|
1112
|
+
return originalGetNavigationTree(fileName);
|
|
1113
|
+
}
|
|
1114
|
+
const navMapper = mapper;
|
|
1115
|
+
const tree = originalGetNavigationTree(fileName);
|
|
1116
|
+
// Recursively map spans in navigation tree
|
|
1117
|
+
function mapNavigationItem(item) {
|
|
1118
|
+
const mappedSpans = item.spans.map((span) => {
|
|
1119
|
+
const mapped = navMapper.mapSpanToOriginal(span.start, span.length);
|
|
1120
|
+
return mapped
|
|
1121
|
+
? { start: mapped.start, length: mapped.length }
|
|
1122
|
+
: span;
|
|
1123
|
+
});
|
|
1124
|
+
const mappedNameSpan = item.nameSpan
|
|
1125
|
+
? (navMapper.mapSpanToOriginal(item.nameSpan.start, item.nameSpan.length) ?? item.nameSpan)
|
|
1126
|
+
: undefined;
|
|
1127
|
+
return {
|
|
1128
|
+
...item,
|
|
1129
|
+
spans: mappedSpans,
|
|
1130
|
+
nameSpan: mappedNameSpan
|
|
1131
|
+
? { start: mappedNameSpan.start, length: mappedNameSpan.length }
|
|
1132
|
+
: undefined,
|
|
1133
|
+
childItems: item.childItems?.map(mapNavigationItem),
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
return mapNavigationItem(tree);
|
|
1137
|
+
}
|
|
1138
|
+
catch (e) {
|
|
1139
|
+
log(`Error in getNavigationTree: ${e instanceof Error ? e.message : String(e)}`);
|
|
1140
|
+
return originalGetNavigationTree(fileName);
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
// Hook getOutliningSpans
|
|
1144
|
+
const originalGetOutliningSpans = info.languageService.getOutliningSpans.bind(info.languageService);
|
|
1145
|
+
info.languageService.getOutliningSpans = (fileName) => {
|
|
1146
|
+
try {
|
|
1147
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
1148
|
+
return originalGetOutliningSpans(fileName);
|
|
1149
|
+
}
|
|
1150
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
1151
|
+
if (!mapper) {
|
|
1152
|
+
return originalGetOutliningSpans(fileName);
|
|
1153
|
+
}
|
|
1154
|
+
const spans = originalGetOutliningSpans(fileName);
|
|
1155
|
+
return spans.map((span) => {
|
|
1156
|
+
const mappedTextSpan = mapper.mapSpanToOriginal(span.textSpan.start, span.textSpan.length);
|
|
1157
|
+
const mappedHintSpan = mapper.mapSpanToOriginal(span.hintSpan.start, span.hintSpan.length);
|
|
1158
|
+
if (!mappedTextSpan || !mappedHintSpan)
|
|
1159
|
+
return span;
|
|
1160
|
+
return {
|
|
1161
|
+
...span,
|
|
1162
|
+
textSpan: {
|
|
1163
|
+
start: mappedTextSpan.start,
|
|
1164
|
+
length: mappedTextSpan.length,
|
|
1165
|
+
},
|
|
1166
|
+
hintSpan: {
|
|
1167
|
+
start: mappedHintSpan.start,
|
|
1168
|
+
length: mappedHintSpan.length,
|
|
1169
|
+
},
|
|
1170
|
+
};
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
catch (e) {
|
|
1174
|
+
log(`Error in getOutliningSpans: ${e instanceof Error ? e.message : String(e)}`);
|
|
1175
|
+
return originalGetOutliningSpans(fileName);
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
// Hook provideInlayHints to map positions
|
|
1179
|
+
const originalProvideInlayHints = info.languageService.provideInlayHints?.bind(info.languageService);
|
|
1180
|
+
if (originalProvideInlayHints) {
|
|
1181
|
+
info.languageService.provideInlayHints = (fileName, span, preferences) => {
|
|
1182
|
+
try {
|
|
1183
|
+
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
1184
|
+
return originalProvideInlayHints(fileName, span, preferences);
|
|
1185
|
+
}
|
|
1186
|
+
const mapper = nativePlugin.getMapper(fileName);
|
|
1187
|
+
if (!mapper) {
|
|
1188
|
+
return originalProvideInlayHints(fileName, span, preferences);
|
|
1189
|
+
}
|
|
1190
|
+
// If no mapping info, avoid remapping to reduce risk
|
|
1191
|
+
if (mapper.isEmpty()) {
|
|
1192
|
+
return originalProvideInlayHints(fileName, span, preferences);
|
|
1193
|
+
}
|
|
1194
|
+
// Map the input span to expanded coordinates
|
|
1195
|
+
const expandedSpan = mapper.mapSpanToExpanded(span.start, span.length);
|
|
1196
|
+
const result = originalProvideInlayHints(fileName, expandedSpan, preferences);
|
|
1197
|
+
if (!result)
|
|
1198
|
+
return result;
|
|
1199
|
+
// Map each hint's position back to original coordinates
|
|
1200
|
+
return result.flatMap((hint) => {
|
|
1201
|
+
const originalPos = mapper.expandedToOriginal(hint.position);
|
|
1202
|
+
if (originalPos === null) {
|
|
1203
|
+
// Hint is in generated code, skip it
|
|
1204
|
+
return [];
|
|
1205
|
+
}
|
|
1206
|
+
return [
|
|
1207
|
+
{
|
|
1208
|
+
...hint,
|
|
1209
|
+
position: originalPos,
|
|
1210
|
+
},
|
|
1211
|
+
];
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
catch (e) {
|
|
1215
|
+
log(`Error in provideInlayHints: ${e instanceof Error ? e.message : String(e)}`);
|
|
1216
|
+
return originalProvideInlayHints(fileName, span, preferences);
|
|
1217
|
+
}
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
return info.languageService;
|
|
1221
|
+
}
|
|
1222
|
+
return { create };
|
|
1223
|
+
}
|
|
1224
|
+
module.exports = init;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SourceMappingResult } from "macroforge";
|
|
2
|
+
export type SourceMapping = SourceMappingResult;
|
|
3
|
+
export interface PositionMapper {
|
|
4
|
+
originalToExpanded(pos: number): number;
|
|
5
|
+
expandedToOriginal(pos: number): number | null;
|
|
6
|
+
mapSpanToOriginal(start: number, length: number): {
|
|
7
|
+
start: number;
|
|
8
|
+
length: number;
|
|
9
|
+
} | null;
|
|
10
|
+
mapSpanToExpanded(start: number, length: number): {
|
|
11
|
+
start: number;
|
|
12
|
+
length: number;
|
|
13
|
+
};
|
|
14
|
+
isEmpty(): boolean;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=source-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-map.d.ts","sourceRoot":"","sources":["../src/source-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC7B,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACxC,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC/C,iBAAiB,CACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5C,iBAAiB,CACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,OAAO,IAAI,OAAO,CAAC;CACpB"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@macroforge/typescript-plugin",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript language service plugin that augments classes decorated with @derive to include macro-generated methods.",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/rymskip/macroforge.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"typescript",
|
|
14
|
+
"tsserver",
|
|
15
|
+
"plugin",
|
|
16
|
+
"macros",
|
|
17
|
+
"derive",
|
|
18
|
+
"language-service"
|
|
19
|
+
],
|
|
20
|
+
"author": "macroforge contributors",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/rymskip/macroforge/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/rymskip/macroforge#readme",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc -p tsconfig.json",
|
|
31
|
+
"clean": "rm -rf dist",
|
|
32
|
+
"test": "bun run build && node --test tests/**/*.test.js",
|
|
33
|
+
"prepublishOnly": "bun run clean && bun run build"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"macroforge": "^0.1.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"typescript": ">=5.0.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^18.19.130",
|
|
43
|
+
"typescript": "^5.9.3"
|
|
44
|
+
}
|
|
45
|
+
}
|