@macroforge/typescript-plugin 0.1.64 → 0.1.67
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/README.md +24 -17
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +204 -170
- package/dist/index.js.map +1 -1
- package/dist/source-map.d.ts +1 -1
- package/dist/source-map.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* this plugin intercepts the call and returns the macro-expanded version instead.
|
|
14
14
|
*
|
|
15
15
|
* 2. **Position Mapping**: Since expanded code has different positions than the original,
|
|
16
|
-
* the plugin maintains a
|
|
16
|
+
* the plugin maintains a PositionMapper for each file to translate positions
|
|
17
17
|
* between original and expanded coordinates.
|
|
18
18
|
*
|
|
19
19
|
* 3. **Virtual .d.ts Files**: For each macro-containing file, the plugin generates a
|
|
@@ -44,13 +44,13 @@
|
|
|
44
44
|
* ```
|
|
45
45
|
*
|
|
46
46
|
* @see {@link init} - The main plugin factory function
|
|
47
|
-
* @see
|
|
47
|
+
* @see PositionMapper from macroforge - Position mapping between original and expanded code
|
|
48
48
|
* @module @macroforge/typescript-plugin
|
|
49
49
|
*/
|
|
50
|
-
import {
|
|
51
|
-
import { createRequire } from
|
|
52
|
-
import path from
|
|
53
|
-
import {
|
|
50
|
+
import { __macroforgeGetManifest, loadConfig as nativeLoadConfig, NativePlugin } from 'macroforge';
|
|
51
|
+
import { createRequire } from 'node:module';
|
|
52
|
+
import path from 'node:path';
|
|
53
|
+
import { getExternalDecoratorInfo, getExternalMacroInfo, loadMacroConfig, parseMacroImportComments } from '@macroforge/shared';
|
|
54
54
|
// Create require for external package loading
|
|
55
55
|
const moduleRequire = createRequire(import.meta.url);
|
|
56
56
|
/**
|
|
@@ -92,8 +92,8 @@ function getMacroManifest() {
|
|
|
92
92
|
try {
|
|
93
93
|
const manifest = __macroforgeGetManifest();
|
|
94
94
|
macroManifestCache = {
|
|
95
|
-
macros: new Map(manifest.macros.map(m => [m.name.toLowerCase(), m])),
|
|
96
|
-
decorators: new Map(manifest.decorators.map(d => [d.export.toLowerCase(), d]))
|
|
95
|
+
macros: new Map(manifest.macros.map((m) => [m.name.toLowerCase(), m])),
|
|
96
|
+
decorators: new Map(manifest.decorators.map((d) => [d.export.toLowerCase(), d]))
|
|
97
97
|
};
|
|
98
98
|
return macroManifestCache;
|
|
99
99
|
}
|
|
@@ -144,11 +144,11 @@ function findDeriveAtPosition(text, position) {
|
|
|
144
144
|
const deriveStart = match.index;
|
|
145
145
|
const deriveEnd = deriveStart + match[0].length;
|
|
146
146
|
if (position >= deriveStart && position <= deriveEnd) {
|
|
147
|
-
const argsStart = text.indexOf(
|
|
148
|
-
const argsEnd = text.indexOf(
|
|
147
|
+
const argsStart = text.indexOf('(', deriveStart) + 1;
|
|
148
|
+
const argsEnd = text.indexOf(')', argsStart);
|
|
149
149
|
const argsContent = text.substring(argsStart, argsEnd);
|
|
150
150
|
let currentPos = argsStart;
|
|
151
|
-
const macroNames = argsContent.split(
|
|
151
|
+
const macroNames = argsContent.split(',');
|
|
152
152
|
for (const rawName of macroNames) {
|
|
153
153
|
const trimmedName = rawName.trim();
|
|
154
154
|
const nameStartInArgs = rawName.indexOf(trimmedName);
|
|
@@ -191,7 +191,7 @@ function findDeriveKeywordAtPosition(text, position) {
|
|
|
191
191
|
let match;
|
|
192
192
|
while ((match = deriveKeywordPattern.exec(text)) !== null) {
|
|
193
193
|
const start = match.index; // Position of @
|
|
194
|
-
const end = start +
|
|
194
|
+
const end = start + '@derive'.length;
|
|
195
195
|
if (position >= start && position < end) {
|
|
196
196
|
return { start, end };
|
|
197
197
|
}
|
|
@@ -242,10 +242,10 @@ function findDecoratorAtPosition(text, position) {
|
|
|
242
242
|
const nameEnd = nameStart + match[1].length;
|
|
243
243
|
if (position >= atSign && position <= nameEnd) {
|
|
244
244
|
// Skip @derive in JSDoc comments - let findDeriveAtPosition handle those
|
|
245
|
-
if (match[1].toLowerCase() ===
|
|
245
|
+
if (match[1].toLowerCase() === 'derive') {
|
|
246
246
|
const beforeMatch = text.substring(0, atSign);
|
|
247
|
-
const lastCommentStart = beforeMatch.lastIndexOf(
|
|
248
|
-
const lastCommentEnd = beforeMatch.lastIndexOf(
|
|
247
|
+
const lastCommentStart = beforeMatch.lastIndexOf('/**');
|
|
248
|
+
const lastCommentEnd = beforeMatch.lastIndexOf('*/');
|
|
249
249
|
if (lastCommentStart > lastCommentEnd) {
|
|
250
250
|
continue;
|
|
251
251
|
}
|
|
@@ -289,7 +289,7 @@ function findEnclosingDeriveContext(text, position) {
|
|
|
289
289
|
}
|
|
290
290
|
if (lastMatch) {
|
|
291
291
|
const macros = lastMatch[1]
|
|
292
|
-
.split(
|
|
292
|
+
.split(',')
|
|
293
293
|
.map((m) => m.trim())
|
|
294
294
|
.filter(Boolean);
|
|
295
295
|
return macros;
|
|
@@ -374,22 +374,22 @@ function getMacroHoverInfo(text, position, tsModule) {
|
|
|
374
374
|
if (deriveKeyword) {
|
|
375
375
|
return {
|
|
376
376
|
kind: tsModule.ScriptElementKind.keyword,
|
|
377
|
-
kindModifiers:
|
|
377
|
+
kindModifiers: '',
|
|
378
378
|
textSpan: {
|
|
379
379
|
start: deriveKeyword.start,
|
|
380
|
-
length: deriveKeyword.end - deriveKeyword.start
|
|
380
|
+
length: deriveKeyword.end - deriveKeyword.start
|
|
381
381
|
},
|
|
382
|
-
displayParts: [{ text:
|
|
382
|
+
displayParts: [{ text: '@derive', kind: 'keyword' }],
|
|
383
383
|
documentation: [
|
|
384
384
|
{
|
|
385
|
-
text:
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
385
|
+
text: 'Derive directive - applies compile-time macros to generate methods and implementations.\n\n' +
|
|
386
|
+
'**Usage:** `/** @derive(MacroName, AnotherMacro) */`\n\n' +
|
|
387
|
+
'**Built-in macros:** Debug, Clone, Default, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize\n\n' +
|
|
388
|
+
'External macros can be imported using:\n' +
|
|
389
389
|
'`/** import macro {Name} from "package"; */`',
|
|
390
|
-
kind:
|
|
391
|
-
}
|
|
392
|
-
]
|
|
390
|
+
kind: 'text'
|
|
391
|
+
}
|
|
392
|
+
]
|
|
393
393
|
};
|
|
394
394
|
}
|
|
395
395
|
// Parse external macro imports for later use
|
|
@@ -402,19 +402,19 @@ function getMacroHoverInfo(text, position, tsModule) {
|
|
|
402
402
|
if (macroInfo) {
|
|
403
403
|
return {
|
|
404
404
|
kind: tsModule.ScriptElementKind.functionElement,
|
|
405
|
-
kindModifiers:
|
|
405
|
+
kindModifiers: '',
|
|
406
406
|
textSpan: {
|
|
407
407
|
start: deriveMatch.start,
|
|
408
|
-
length: deriveMatch.end - deriveMatch.start
|
|
408
|
+
length: deriveMatch.end - deriveMatch.start
|
|
409
409
|
},
|
|
410
410
|
displayParts: [
|
|
411
|
-
{ text:
|
|
412
|
-
{ text: macroInfo.name, kind:
|
|
413
|
-
{ text:
|
|
411
|
+
{ text: '@derive(', kind: 'punctuation' },
|
|
412
|
+
{ text: macroInfo.name, kind: 'functionName' },
|
|
413
|
+
{ text: ')', kind: 'punctuation' }
|
|
414
414
|
],
|
|
415
415
|
documentation: macroInfo.description
|
|
416
|
-
? [{ text: macroInfo.description, kind:
|
|
417
|
-
: []
|
|
416
|
+
? [{ text: macroInfo.description, kind: 'text' }]
|
|
417
|
+
: []
|
|
418
418
|
};
|
|
419
419
|
}
|
|
420
420
|
// 2b. Check external macro imports
|
|
@@ -424,39 +424,42 @@ function getMacroHoverInfo(text, position, tsModule) {
|
|
|
424
424
|
const externalMacroInfo = getExternalMacroInfo(deriveMatch.macroName, modulePath, moduleRequire);
|
|
425
425
|
const description = externalMacroInfo?.description
|
|
426
426
|
? externalMacroInfo.description
|
|
427
|
-
:
|
|
427
|
+
: 'This macro is loaded from an external package at compile time.';
|
|
428
428
|
return {
|
|
429
429
|
kind: tsModule.ScriptElementKind.functionElement,
|
|
430
|
-
kindModifiers:
|
|
430
|
+
kindModifiers: 'external',
|
|
431
431
|
textSpan: {
|
|
432
432
|
start: deriveMatch.start,
|
|
433
|
-
length: deriveMatch.end - deriveMatch.start
|
|
433
|
+
length: deriveMatch.end - deriveMatch.start
|
|
434
434
|
},
|
|
435
435
|
displayParts: [
|
|
436
|
-
{ text:
|
|
437
|
-
{
|
|
438
|
-
|
|
436
|
+
{ text: '@derive(', kind: 'punctuation' },
|
|
437
|
+
{
|
|
438
|
+
text: externalMacroInfo?.name ?? deriveMatch.macroName,
|
|
439
|
+
kind: 'functionName'
|
|
440
|
+
},
|
|
441
|
+
{ text: ')', kind: 'punctuation' }
|
|
439
442
|
],
|
|
440
443
|
documentation: [
|
|
441
444
|
{
|
|
442
445
|
text: `**External macro** from \`${modulePath}\`\n\n${description}`,
|
|
443
|
-
kind:
|
|
444
|
-
}
|
|
445
|
-
]
|
|
446
|
+
kind: 'text'
|
|
447
|
+
}
|
|
448
|
+
]
|
|
446
449
|
};
|
|
447
450
|
}
|
|
448
451
|
// 2c. Fallback for unknown/unrecognized macros
|
|
449
452
|
return {
|
|
450
453
|
kind: tsModule.ScriptElementKind.functionElement,
|
|
451
|
-
kindModifiers:
|
|
454
|
+
kindModifiers: '',
|
|
452
455
|
textSpan: {
|
|
453
456
|
start: deriveMatch.start,
|
|
454
|
-
length: deriveMatch.end - deriveMatch.start
|
|
457
|
+
length: deriveMatch.end - deriveMatch.start
|
|
455
458
|
},
|
|
456
459
|
displayParts: [
|
|
457
|
-
{ text:
|
|
458
|
-
{ text: deriveMatch.macroName, kind:
|
|
459
|
-
{ text:
|
|
460
|
+
{ text: '@derive(', kind: 'punctuation' },
|
|
461
|
+
{ text: deriveMatch.macroName, kind: 'functionName' },
|
|
462
|
+
{ text: ')', kind: 'punctuation' }
|
|
460
463
|
],
|
|
461
464
|
documentation: [
|
|
462
465
|
{
|
|
@@ -464,9 +467,9 @@ function getMacroHoverInfo(text, position, tsModule) {
|
|
|
464
467
|
"This macro is not in the built-in manifest. If it's a custom macro, " +
|
|
465
468
|
"ensure it's imported using:\n\n" +
|
|
466
469
|
`\`/** import macro {${deriveMatch.macroName}} from "your-package"; */\``,
|
|
467
|
-
kind:
|
|
468
|
-
}
|
|
469
|
-
]
|
|
470
|
+
kind: 'text'
|
|
471
|
+
}
|
|
472
|
+
]
|
|
470
473
|
};
|
|
471
474
|
}
|
|
472
475
|
// 3. Check for @decorator patterns
|
|
@@ -477,18 +480,18 @@ function getMacroHoverInfo(text, position, tsModule) {
|
|
|
477
480
|
if (macroInfo) {
|
|
478
481
|
return {
|
|
479
482
|
kind: tsModule.ScriptElementKind.functionElement,
|
|
480
|
-
kindModifiers:
|
|
483
|
+
kindModifiers: '',
|
|
481
484
|
textSpan: {
|
|
482
485
|
start: decoratorMatch.start,
|
|
483
|
-
length: decoratorMatch.end - decoratorMatch.start
|
|
486
|
+
length: decoratorMatch.end - decoratorMatch.start
|
|
484
487
|
},
|
|
485
488
|
displayParts: [
|
|
486
|
-
{ text:
|
|
487
|
-
{ text: macroInfo.name, kind:
|
|
489
|
+
{ text: '@', kind: 'punctuation' },
|
|
490
|
+
{ text: macroInfo.name, kind: 'functionName' }
|
|
488
491
|
],
|
|
489
492
|
documentation: macroInfo.description
|
|
490
|
-
? [{ text: macroInfo.description, kind:
|
|
491
|
-
: []
|
|
493
|
+
? [{ text: macroInfo.description, kind: 'text' }]
|
|
494
|
+
: []
|
|
492
495
|
};
|
|
493
496
|
}
|
|
494
497
|
// 3b. Check if it's a built-in decorator
|
|
@@ -496,16 +499,16 @@ function getMacroHoverInfo(text, position, tsModule) {
|
|
|
496
499
|
if (decoratorInfo && decoratorInfo.docs) {
|
|
497
500
|
return {
|
|
498
501
|
kind: tsModule.ScriptElementKind.functionElement,
|
|
499
|
-
kindModifiers:
|
|
502
|
+
kindModifiers: '',
|
|
500
503
|
textSpan: {
|
|
501
504
|
start: decoratorMatch.start,
|
|
502
|
-
length: decoratorMatch.end - decoratorMatch.start
|
|
505
|
+
length: decoratorMatch.end - decoratorMatch.start
|
|
503
506
|
},
|
|
504
507
|
displayParts: [
|
|
505
|
-
{ text:
|
|
506
|
-
{ text: decoratorInfo.export, kind:
|
|
508
|
+
{ text: '@', kind: 'punctuation' },
|
|
509
|
+
{ text: decoratorInfo.export, kind: 'functionName' }
|
|
507
510
|
],
|
|
508
|
-
documentation: [{ text: decoratorInfo.docs, kind:
|
|
511
|
+
documentation: [{ text: decoratorInfo.docs, kind: 'text' }]
|
|
509
512
|
};
|
|
510
513
|
}
|
|
511
514
|
// 3c. Check if this decorator is in a macro context (for external/custom decorators)
|
|
@@ -521,47 +524,50 @@ function getMacroHoverInfo(text, position, tsModule) {
|
|
|
521
524
|
: null;
|
|
522
525
|
const description = externalDecoratorInfo?.docs
|
|
523
526
|
? externalDecoratorInfo.docs
|
|
524
|
-
:
|
|
527
|
+
: 'This decorator configures field-level behavior for the macro.';
|
|
525
528
|
return {
|
|
526
529
|
kind: tsModule.ScriptElementKind.functionElement,
|
|
527
|
-
kindModifiers:
|
|
530
|
+
kindModifiers: 'external',
|
|
528
531
|
textSpan: {
|
|
529
532
|
start: decoratorMatch.start,
|
|
530
|
-
length: decoratorMatch.end - decoratorMatch.start
|
|
533
|
+
length: decoratorMatch.end - decoratorMatch.start
|
|
531
534
|
},
|
|
532
535
|
displayParts: [
|
|
533
|
-
{ text:
|
|
534
|
-
{
|
|
536
|
+
{ text: '@', kind: 'punctuation' },
|
|
537
|
+
{
|
|
538
|
+
text: externalDecoratorInfo?.export ?? decoratorMatch.name,
|
|
539
|
+
kind: 'functionName'
|
|
540
|
+
}
|
|
535
541
|
],
|
|
536
542
|
documentation: [
|
|
537
543
|
{
|
|
538
544
|
text: `**Field decorator** from \`${likelySourceMacro}\` macro (\`${modulePath}\`)\n\n` +
|
|
539
545
|
description,
|
|
540
|
-
kind:
|
|
541
|
-
}
|
|
542
|
-
]
|
|
546
|
+
kind: 'text'
|
|
547
|
+
}
|
|
548
|
+
]
|
|
543
549
|
};
|
|
544
550
|
}
|
|
545
551
|
// Fallback: Generic decorator in macro context
|
|
546
552
|
return {
|
|
547
553
|
kind: tsModule.ScriptElementKind.functionElement,
|
|
548
|
-
kindModifiers:
|
|
554
|
+
kindModifiers: '',
|
|
549
555
|
textSpan: {
|
|
550
556
|
start: decoratorMatch.start,
|
|
551
|
-
length: decoratorMatch.end - decoratorMatch.start
|
|
557
|
+
length: decoratorMatch.end - decoratorMatch.start
|
|
552
558
|
},
|
|
553
559
|
displayParts: [
|
|
554
|
-
{ text:
|
|
555
|
-
{ text: decoratorMatch.name, kind:
|
|
560
|
+
{ text: '@', kind: 'punctuation' },
|
|
561
|
+
{ text: decoratorMatch.name, kind: 'functionName' }
|
|
556
562
|
],
|
|
557
563
|
documentation: [
|
|
558
564
|
{
|
|
559
565
|
text: `**Field decorator:** ${decoratorMatch.name}\n\n` +
|
|
560
|
-
`Used with @derive(${enclosingMacros.join(
|
|
561
|
-
|
|
562
|
-
kind:
|
|
563
|
-
}
|
|
564
|
-
]
|
|
566
|
+
`Used with @derive(${enclosingMacros.join(', ')}).\n` +
|
|
567
|
+
'This decorator configures field-level behavior for the applied macros.',
|
|
568
|
+
kind: 'text'
|
|
569
|
+
}
|
|
570
|
+
]
|
|
565
571
|
};
|
|
566
572
|
}
|
|
567
573
|
}
|
|
@@ -571,7 +577,7 @@ function getMacroHoverInfo(text, position, tsModule) {
|
|
|
571
577
|
* File extensions that the plugin will process for macro expansion.
|
|
572
578
|
* @internal
|
|
573
579
|
*/
|
|
574
|
-
const FILE_EXTENSIONS = [
|
|
580
|
+
const FILE_EXTENSIONS = ['.ts', '.tsx', '.svelte'];
|
|
575
581
|
/**
|
|
576
582
|
* Determines whether a file should be processed for macro expansion.
|
|
577
583
|
*
|
|
@@ -599,12 +605,12 @@ const FILE_EXTENSIONS = [".ts", ".tsx", ".svelte"];
|
|
|
599
605
|
*/
|
|
600
606
|
function shouldProcess(fileName) {
|
|
601
607
|
const lower = fileName.toLowerCase();
|
|
602
|
-
if (lower.includes(
|
|
608
|
+
if (lower.includes('node_modules'))
|
|
603
609
|
return false;
|
|
604
610
|
if (fileName.includes(`${path.sep}.macroforge${path.sep}`))
|
|
605
611
|
return false;
|
|
606
612
|
// Skip generated .d.ts files
|
|
607
|
-
if (fileName.endsWith(
|
|
613
|
+
if (fileName.endsWith('.macroforge.d.ts'))
|
|
608
614
|
return false;
|
|
609
615
|
return FILE_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
610
616
|
}
|
|
@@ -636,7 +642,7 @@ function shouldProcess(fileName) {
|
|
|
636
642
|
* ```
|
|
637
643
|
*/
|
|
638
644
|
function hasMacroDirectives(text) {
|
|
639
|
-
return (text.includes(
|
|
645
|
+
return (text.includes('@derive') ||
|
|
640
646
|
/\/\*\*\s*@derive\s*\(/i.test(text) ||
|
|
641
647
|
/\/\*\*\s*import\s+macro\b/i.test(text));
|
|
642
648
|
}
|
|
@@ -773,7 +779,7 @@ function init(modules) {
|
|
|
773
779
|
const result = nativeLoadConfig(content, filepath);
|
|
774
780
|
return {
|
|
775
781
|
...result,
|
|
776
|
-
returnTypes:
|
|
782
|
+
returnTypes: 'vanilla' // TODO: Extract from config if available
|
|
777
783
|
};
|
|
778
784
|
}
|
|
779
785
|
catch {
|
|
@@ -782,7 +788,7 @@ function init(modules) {
|
|
|
782
788
|
generateConvenienceConst: false,
|
|
783
789
|
hasForeignTypes: false,
|
|
784
790
|
foreignTypeCount: 0,
|
|
785
|
-
returnTypes:
|
|
791
|
+
returnTypes: 'vanilla'
|
|
786
792
|
};
|
|
787
793
|
}
|
|
788
794
|
};
|
|
@@ -804,11 +810,15 @@ function init(modules) {
|
|
|
804
810
|
try {
|
|
805
811
|
info.project.projectService.logger.info(`[macroforge] ${msg}`);
|
|
806
812
|
}
|
|
807
|
-
catch {
|
|
813
|
+
catch {
|
|
814
|
+
// Ignore logging failures
|
|
815
|
+
}
|
|
808
816
|
try {
|
|
809
817
|
console.error(`[macroforge] ${msg}`);
|
|
810
818
|
}
|
|
811
|
-
catch {
|
|
819
|
+
catch {
|
|
820
|
+
// Ignore logging failures
|
|
821
|
+
}
|
|
812
822
|
};
|
|
813
823
|
/**
|
|
814
824
|
* Registers a virtual `.macroforge.d.ts` file with TypeScript's project service.
|
|
@@ -897,7 +907,7 @@ function init(modules) {
|
|
|
897
907
|
}
|
|
898
908
|
};
|
|
899
909
|
}
|
|
900
|
-
log(
|
|
910
|
+
log('Plugin initialized');
|
|
901
911
|
/**
|
|
902
912
|
* Processes a file through macro expansion via the native Rust plugin.
|
|
903
913
|
*
|
|
@@ -941,9 +951,9 @@ function init(modules) {
|
|
|
941
951
|
types: undefined,
|
|
942
952
|
metadata: undefined,
|
|
943
953
|
diagnostics: [],
|
|
944
|
-
sourceMapping: undefined
|
|
954
|
+
sourceMapping: undefined
|
|
945
955
|
},
|
|
946
|
-
code: content
|
|
956
|
+
code: content
|
|
947
957
|
};
|
|
948
958
|
}
|
|
949
959
|
try {
|
|
@@ -951,10 +961,10 @@ function init(modules) {
|
|
|
951
961
|
const result = nativePlugin.processFile(fileName, content, {
|
|
952
962
|
keepDecorators,
|
|
953
963
|
version,
|
|
954
|
-
configPath: macroConfig.configPath
|
|
964
|
+
configPath: macroConfig.configPath
|
|
955
965
|
});
|
|
956
966
|
// Update virtual .d.ts files
|
|
957
|
-
const virtualDtsFileName = fileName +
|
|
967
|
+
const virtualDtsFileName = fileName + '.macroforge.d.ts';
|
|
958
968
|
if (result.types) {
|
|
959
969
|
virtualDtsFiles.set(virtualDtsFileName, tsModule.ScriptSnapshot.fromString(result.types));
|
|
960
970
|
ensureVirtualDtsRegistered(virtualDtsFileName);
|
|
@@ -969,17 +979,17 @@ function init(modules) {
|
|
|
969
979
|
catch (e) {
|
|
970
980
|
const errorMessage = e instanceof Error ? e.stack || e.message : String(e);
|
|
971
981
|
log(`Plugin expansion failed for ${fileName}: ${errorMessage}`);
|
|
972
|
-
virtualDtsFiles.delete(fileName +
|
|
973
|
-
cleanupVirtualDts(fileName +
|
|
982
|
+
virtualDtsFiles.delete(fileName + '.macroforge.d.ts');
|
|
983
|
+
cleanupVirtualDts(fileName + '.macroforge.d.ts');
|
|
974
984
|
return {
|
|
975
985
|
result: {
|
|
976
986
|
code: content,
|
|
977
987
|
types: undefined,
|
|
978
988
|
metadata: undefined,
|
|
979
989
|
diagnostics: [],
|
|
980
|
-
sourceMapping: undefined
|
|
990
|
+
sourceMapping: undefined
|
|
981
991
|
},
|
|
982
|
-
code: content
|
|
992
|
+
code: content
|
|
983
993
|
};
|
|
984
994
|
}
|
|
985
995
|
}
|
|
@@ -995,12 +1005,13 @@ function init(modules) {
|
|
|
995
1005
|
* them from the source file's version. This ensures TypeScript invalidates
|
|
996
1006
|
* the virtual file when its source changes.
|
|
997
1007
|
*/
|
|
998
|
-
const originalGetScriptVersion = info.languageServiceHost.getScriptVersion
|
|
1008
|
+
const originalGetScriptVersion = info.languageServiceHost.getScriptVersion
|
|
1009
|
+
.bind(info.languageServiceHost);
|
|
999
1010
|
info.languageServiceHost.getScriptVersion = (fileName) => {
|
|
1000
1011
|
try {
|
|
1001
1012
|
if (virtualDtsFiles.has(fileName)) {
|
|
1002
1013
|
// Virtual .d.ts files inherit version from their source file
|
|
1003
|
-
const sourceFileName = fileName.replace(
|
|
1014
|
+
const sourceFileName = fileName.replace('.macroforge.d.ts', '');
|
|
1004
1015
|
return originalGetScriptVersion(sourceFileName);
|
|
1005
1016
|
}
|
|
1006
1017
|
return originalGetScriptVersion(fileName);
|
|
@@ -1073,7 +1084,8 @@ function init(modules) {
|
|
|
1073
1084
|
* - Uses `processingFiles` Set to prevent infinite loops during expansion
|
|
1074
1085
|
* - Version-based cache invalidation ensures fresh expansions on file changes
|
|
1075
1086
|
*/
|
|
1076
|
-
const originalGetScriptSnapshot = info.languageServiceHost.getScriptSnapshot
|
|
1087
|
+
const originalGetScriptSnapshot = info.languageServiceHost.getScriptSnapshot
|
|
1088
|
+
.bind(info.languageServiceHost);
|
|
1077
1089
|
info.languageServiceHost.getScriptSnapshot = (fileName) => {
|
|
1078
1090
|
try {
|
|
1079
1091
|
log(`getScriptSnapshot: ${fileName}`);
|
|
@@ -1096,7 +1108,7 @@ function init(modules) {
|
|
|
1096
1108
|
if (!snapshot) {
|
|
1097
1109
|
// Avoid tsserver crashes when a file was reported but no snapshot exists
|
|
1098
1110
|
log(` -> no snapshot available for ${fileName}, returning empty snapshot`);
|
|
1099
|
-
return tsModule.ScriptSnapshot.fromString(
|
|
1111
|
+
return tsModule.ScriptSnapshot.fromString('');
|
|
1100
1112
|
}
|
|
1101
1113
|
const text = snapshot.getText(0, snapshot.getLength());
|
|
1102
1114
|
// Scenario 4: No macro directives - return original
|
|
@@ -1124,7 +1136,7 @@ function init(modules) {
|
|
|
1124
1136
|
// Cache for stable identity across TS requests
|
|
1125
1137
|
snapshotCache.set(fileName, {
|
|
1126
1138
|
version,
|
|
1127
|
-
snapshot: expandedSnapshot
|
|
1139
|
+
snapshot: expandedSnapshot
|
|
1128
1140
|
});
|
|
1129
1141
|
log(` -> returning expanded snapshot`);
|
|
1130
1142
|
return expandedSnapshot;
|
|
@@ -1159,20 +1171,20 @@ function init(modules) {
|
|
|
1159
1171
|
* @returns A plain object with diagnostic information
|
|
1160
1172
|
*/
|
|
1161
1173
|
function toPlainDiagnostic(diag) {
|
|
1162
|
-
const message = typeof diag.messageText ===
|
|
1174
|
+
const message = typeof diag.messageText === 'string'
|
|
1163
1175
|
? diag.messageText
|
|
1164
1176
|
: diag.messageText.messageText;
|
|
1165
1177
|
const category = diag.category === tsModule.DiagnosticCategory.Error
|
|
1166
|
-
?
|
|
1178
|
+
? 'error'
|
|
1167
1179
|
: diag.category === tsModule.DiagnosticCategory.Warning
|
|
1168
|
-
?
|
|
1169
|
-
:
|
|
1180
|
+
? 'warning'
|
|
1181
|
+
: 'message';
|
|
1170
1182
|
return {
|
|
1171
1183
|
start: diag.start,
|
|
1172
1184
|
length: diag.length,
|
|
1173
1185
|
message,
|
|
1174
1186
|
code: diag.code,
|
|
1175
|
-
category
|
|
1187
|
+
category
|
|
1176
1188
|
};
|
|
1177
1189
|
}
|
|
1178
1190
|
/**
|
|
@@ -1197,7 +1209,7 @@ function init(modules) {
|
|
|
1197
1209
|
return {
|
|
1198
1210
|
...diag,
|
|
1199
1211
|
start: mappedDiag.start,
|
|
1200
|
-
length: mappedDiag.length
|
|
1212
|
+
length: mappedDiag.length
|
|
1201
1213
|
};
|
|
1202
1214
|
});
|
|
1203
1215
|
}
|
|
@@ -1218,7 +1230,8 @@ function init(modules) {
|
|
|
1218
1230
|
* meaningful locations in the user's source code, even when the actual error
|
|
1219
1231
|
* occurred in macro-generated code.
|
|
1220
1232
|
*/
|
|
1221
|
-
const originalGetSemanticDiagnostics = info.languageService
|
|
1233
|
+
const originalGetSemanticDiagnostics = info.languageService
|
|
1234
|
+
.getSemanticDiagnostics.bind(info.languageService);
|
|
1222
1235
|
info.languageService.getSemanticDiagnostics = (fileName) => {
|
|
1223
1236
|
try {
|
|
1224
1237
|
log(`getSemanticDiagnostics: ${fileName}`);
|
|
@@ -1262,7 +1275,7 @@ function init(modules) {
|
|
|
1262
1275
|
return {
|
|
1263
1276
|
...diag,
|
|
1264
1277
|
start: mapped.start,
|
|
1265
|
-
length: mapped.length
|
|
1278
|
+
length: mapped.length
|
|
1266
1279
|
};
|
|
1267
1280
|
})
|
|
1268
1281
|
.filter((diag) => diag !== null);
|
|
@@ -1291,7 +1304,7 @@ function init(modules) {
|
|
|
1291
1304
|
while ((match = deriveRegex.exec(text)) !== null) {
|
|
1292
1305
|
const fullStart = match.index;
|
|
1293
1306
|
const fullLength = match[0].length;
|
|
1294
|
-
const argsStart = match.index + match[0].indexOf(
|
|
1307
|
+
const argsStart = match.index + match[0].indexOf('(') + 1;
|
|
1295
1308
|
const argsText = match[1];
|
|
1296
1309
|
// Parse individual macro names from the arguments
|
|
1297
1310
|
const macros = [];
|
|
@@ -1301,7 +1314,7 @@ function init(modules) {
|
|
|
1301
1314
|
macros.push({
|
|
1302
1315
|
name: macroMatch[1],
|
|
1303
1316
|
start: argsStart + macroMatch.index,
|
|
1304
|
-
length: macroMatch[1].length
|
|
1317
|
+
length: macroMatch[1].length
|
|
1305
1318
|
});
|
|
1306
1319
|
}
|
|
1307
1320
|
deriveDecorators.push({ fullStart, fullLength, macros });
|
|
@@ -1319,7 +1332,7 @@ function init(modules) {
|
|
|
1319
1332
|
return { start: macro.start, length: macro.length };
|
|
1320
1333
|
return {
|
|
1321
1334
|
start: firstDec.fullStart,
|
|
1322
|
-
length: firstDec.fullLength
|
|
1335
|
+
length: firstDec.fullLength
|
|
1323
1336
|
};
|
|
1324
1337
|
}
|
|
1325
1338
|
return { start: 0, length: 7 };
|
|
@@ -1350,7 +1363,7 @@ function init(modules) {
|
|
|
1350
1363
|
}
|
|
1351
1364
|
// If macro name is "macro" (generic fallback) and there's exactly one macro,
|
|
1352
1365
|
// use that macro's position
|
|
1353
|
-
if ((macroName ===
|
|
1366
|
+
if ((macroName === 'macro' || macroName === '') &&
|
|
1354
1367
|
nearestDecorator.macros.length === 1) {
|
|
1355
1368
|
const onlyMacro = nearestDecorator.macros[0];
|
|
1356
1369
|
return { start: onlyMacro.start, length: onlyMacro.length };
|
|
@@ -1358,7 +1371,7 @@ function init(modules) {
|
|
|
1358
1371
|
// Fallback to full decorator if macro not found
|
|
1359
1372
|
return {
|
|
1360
1373
|
start: nearestDecorator.fullStart,
|
|
1361
|
-
length: nearestDecorator.fullLength
|
|
1374
|
+
length: nearestDecorator.fullLength
|
|
1362
1375
|
};
|
|
1363
1376
|
}
|
|
1364
1377
|
return { start: 0, length: 7 };
|
|
@@ -1370,25 +1383,30 @@ function init(modules) {
|
|
|
1370
1383
|
// If mapper didn't return a name, try to get it from the generated region
|
|
1371
1384
|
if (!macroName) {
|
|
1372
1385
|
const region = result.sourceMapping.generatedRegions.find((r) => diagStart >= r.start && diagStart < r.end);
|
|
1373
|
-
macroName = region?.sourceMacro ??
|
|
1386
|
+
macroName = region?.sourceMacro ?? 'macro';
|
|
1374
1387
|
}
|
|
1375
1388
|
// Extract just the macro name if it contains a path (e.g., "derive::Debug" -> "Debug")
|
|
1376
|
-
const simpleMacroName = macroName.includes(
|
|
1377
|
-
? (macroName.split(
|
|
1389
|
+
const simpleMacroName = macroName.includes('::')
|
|
1390
|
+
? (macroName.split('::').pop() ?? macroName)
|
|
1378
1391
|
: macroName;
|
|
1379
1392
|
log(` -> diagnostic at ${diagStart}, macroName="${macroName}", simpleMacroName="${simpleMacroName}"`);
|
|
1380
1393
|
log(` -> generatedRegions: ${JSON.stringify(result.sourceMapping.generatedRegions)}`);
|
|
1381
|
-
log(` -> deriveDecorators: ${JSON.stringify(deriveDecorators.map((d) => ({
|
|
1394
|
+
log(` -> deriveDecorators: ${JSON.stringify(deriveDecorators.map((d) => ({
|
|
1395
|
+
fullStart: d.fullStart,
|
|
1396
|
+
macros: d.macros
|
|
1397
|
+
})))}`);
|
|
1382
1398
|
const position = findMacroPosition(diagStart, simpleMacroName);
|
|
1383
1399
|
log(` -> resolved position: ${JSON.stringify(position)}`);
|
|
1384
1400
|
generatedDiagsAsMacro.push({
|
|
1385
1401
|
file: info.languageService.getProgram()?.getSourceFile(fileName),
|
|
1386
1402
|
start: position.start,
|
|
1387
1403
|
length: position.length,
|
|
1388
|
-
messageText: `[${simpleMacroName}] ${typeof diag.messageText ===
|
|
1404
|
+
messageText: `[${simpleMacroName}] ${typeof diag.messageText === 'string'
|
|
1405
|
+
? diag.messageText
|
|
1406
|
+
: diag.messageText.messageText}`,
|
|
1389
1407
|
category: diag.category,
|
|
1390
1408
|
code: 9998, // Different code for generated code errors
|
|
1391
|
-
source:
|
|
1409
|
+
source: 'macroforge-generated'
|
|
1392
1410
|
});
|
|
1393
1411
|
}
|
|
1394
1412
|
log(` -> converted ${generatedDiagsAsMacro.length} generated code diagnostics`);
|
|
@@ -1399,15 +1417,18 @@ function init(modules) {
|
|
|
1399
1417
|
const decoratorStart = deriveMatch?.index ?? 0;
|
|
1400
1418
|
const decoratorLength = deriveMatch?.[0].length ?? 7;
|
|
1401
1419
|
for (const diag of generatedCodeDiagnostics) {
|
|
1402
|
-
const macroName = effectiveMapper?.generatedBy(diag.start ?? 0) ??
|
|
1420
|
+
const macroName = effectiveMapper?.generatedBy(diag.start ?? 0) ??
|
|
1421
|
+
'macro';
|
|
1403
1422
|
generatedDiagsAsMacro.push({
|
|
1404
1423
|
file: info.languageService.getProgram()?.getSourceFile(fileName),
|
|
1405
1424
|
start: decoratorStart,
|
|
1406
1425
|
length: decoratorLength,
|
|
1407
|
-
messageText: `[${macroName}] ${typeof diag.messageText ===
|
|
1426
|
+
messageText: `[${macroName}] ${typeof diag.messageText === 'string'
|
|
1427
|
+
? diag.messageText
|
|
1428
|
+
: diag.messageText.messageText}`,
|
|
1408
1429
|
category: diag.category,
|
|
1409
1430
|
code: 9998, // Different code for generated code errors
|
|
1410
|
-
source:
|
|
1431
|
+
source: 'macroforge-generated'
|
|
1411
1432
|
});
|
|
1412
1433
|
}
|
|
1413
1434
|
log(` -> converted ${generatedDiagsAsMacro.length} generated code diagnostics (fallback)`);
|
|
@@ -1416,9 +1437,9 @@ function init(modules) {
|
|
|
1416
1437
|
return [...mappedDiagnostics, ...generatedDiagsAsMacro];
|
|
1417
1438
|
}
|
|
1418
1439
|
const macroDiagnostics = result.diagnostics.map((d) => {
|
|
1419
|
-
const category = d.level ===
|
|
1440
|
+
const category = d.level === 'error'
|
|
1420
1441
|
? tsModule.DiagnosticCategory.Error
|
|
1421
|
-
: d.level ===
|
|
1442
|
+
: d.level === 'warning'
|
|
1422
1443
|
? tsModule.DiagnosticCategory.Warning
|
|
1423
1444
|
: tsModule.DiagnosticCategory.Message;
|
|
1424
1445
|
return {
|
|
@@ -1428,13 +1449,13 @@ function init(modules) {
|
|
|
1428
1449
|
messageText: d.message,
|
|
1429
1450
|
category,
|
|
1430
1451
|
code: 9999, // Custom error code
|
|
1431
|
-
source:
|
|
1452
|
+
source: 'macroforge'
|
|
1432
1453
|
};
|
|
1433
1454
|
});
|
|
1434
1455
|
return [
|
|
1435
1456
|
...mappedDiagnostics,
|
|
1436
1457
|
...macroDiagnostics,
|
|
1437
|
-
...generatedDiagsAsMacro
|
|
1458
|
+
...generatedDiagsAsMacro
|
|
1438
1459
|
];
|
|
1439
1460
|
}
|
|
1440
1461
|
catch (e) {
|
|
@@ -1449,7 +1470,8 @@ function init(modules) {
|
|
|
1449
1470
|
* Simpler than semantic diagnostics as it doesn't need to handle generated
|
|
1450
1471
|
* code errors (syntax errors are in user code, not generated code).
|
|
1451
1472
|
*/
|
|
1452
|
-
const originalGetSyntacticDiagnostics = info.languageService
|
|
1473
|
+
const originalGetSyntacticDiagnostics = info.languageService
|
|
1474
|
+
.getSyntacticDiagnostics.bind(info.languageService);
|
|
1453
1475
|
info.languageService.getSyntacticDiagnostics = (fileName) => {
|
|
1454
1476
|
try {
|
|
1455
1477
|
log(`getSyntacticDiagnostics: ${fileName}`);
|
|
@@ -1493,7 +1515,8 @@ function init(modules) {
|
|
|
1493
1515
|
* If the hover would be in generated code, returns undefined to hide it
|
|
1494
1516
|
* (prevents confusing users with hover info for code they can't see).
|
|
1495
1517
|
*/
|
|
1496
|
-
const originalGetQuickInfoAtPosition = info.languageService
|
|
1518
|
+
const originalGetQuickInfoAtPosition = info.languageService
|
|
1519
|
+
.getQuickInfoAtPosition.bind(info.languageService);
|
|
1497
1520
|
info.languageService.getQuickInfoAtPosition = (fileName, position) => {
|
|
1498
1521
|
try {
|
|
1499
1522
|
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
@@ -1528,8 +1551,8 @@ function init(modules) {
|
|
|
1528
1551
|
...result,
|
|
1529
1552
|
textSpan: {
|
|
1530
1553
|
start: mappedTextSpan.start,
|
|
1531
|
-
length: mappedTextSpan.length
|
|
1532
|
-
}
|
|
1554
|
+
length: mappedTextSpan.length
|
|
1555
|
+
}
|
|
1533
1556
|
};
|
|
1534
1557
|
}
|
|
1535
1558
|
catch (e) {
|
|
@@ -1544,7 +1567,8 @@ function init(modules) {
|
|
|
1544
1567
|
* coordinates to get accurate completions that include generated methods,
|
|
1545
1568
|
* then maps any replacement spans back to original coordinates.
|
|
1546
1569
|
*/
|
|
1547
|
-
const originalGetCompletionsAtPosition = info.languageService
|
|
1570
|
+
const originalGetCompletionsAtPosition = info.languageService
|
|
1571
|
+
.getCompletionsAtPosition.bind(info.languageService);
|
|
1548
1572
|
info.languageService.getCompletionsAtPosition = (fileName, position, options, formattingSettings) => {
|
|
1549
1573
|
try {
|
|
1550
1574
|
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
@@ -1575,13 +1599,13 @@ function init(modules) {
|
|
|
1575
1599
|
return { ...entry, replacementSpan: undefined }; // Remove invalid span
|
|
1576
1600
|
return {
|
|
1577
1601
|
...entry,
|
|
1578
|
-
replacementSpan: { start: mapped.start, length: mapped.length }
|
|
1602
|
+
replacementSpan: { start: mapped.start, length: mapped.length }
|
|
1579
1603
|
};
|
|
1580
1604
|
});
|
|
1581
1605
|
return {
|
|
1582
1606
|
...result,
|
|
1583
1607
|
optionalReplacementSpan: mappedOptionalSpan,
|
|
1584
|
-
entries: mappedEntries
|
|
1608
|
+
entries: mappedEntries
|
|
1585
1609
|
};
|
|
1586
1610
|
}
|
|
1587
1611
|
catch (e) {
|
|
@@ -1601,7 +1625,8 @@ function init(modules) {
|
|
|
1601
1625
|
* passed through unchanged. Only same-file definitions need mapping.
|
|
1602
1626
|
* Definitions pointing to generated code are filtered out.
|
|
1603
1627
|
*/
|
|
1604
|
-
const originalGetDefinitionAtPosition = info.languageService
|
|
1628
|
+
const originalGetDefinitionAtPosition = info.languageService
|
|
1629
|
+
.getDefinitionAtPosition.bind(info.languageService);
|
|
1605
1630
|
info.languageService.getDefinitionAtPosition = (fileName, position) => {
|
|
1606
1631
|
try {
|
|
1607
1632
|
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
@@ -1630,7 +1655,7 @@ function init(modules) {
|
|
|
1630
1655
|
if (mapped) {
|
|
1631
1656
|
acc.push({
|
|
1632
1657
|
...def,
|
|
1633
|
-
textSpan: { start: mapped.start, length: mapped.length }
|
|
1658
|
+
textSpan: { start: mapped.start, length: mapped.length }
|
|
1634
1659
|
});
|
|
1635
1660
|
}
|
|
1636
1661
|
return acc;
|
|
@@ -1648,7 +1673,8 @@ function init(modules) {
|
|
|
1648
1673
|
* text span that was used to find the definition (useful for highlighting).
|
|
1649
1674
|
* Maps both the bound span and definition spans.
|
|
1650
1675
|
*/
|
|
1651
|
-
const originalGetDefinitionAndBoundSpan = info.languageService
|
|
1676
|
+
const originalGetDefinitionAndBoundSpan = info.languageService
|
|
1677
|
+
.getDefinitionAndBoundSpan.bind(info.languageService);
|
|
1652
1678
|
info.languageService.getDefinitionAndBoundSpan = (fileName, position) => {
|
|
1653
1679
|
try {
|
|
1654
1680
|
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
@@ -1681,7 +1707,7 @@ function init(modules) {
|
|
|
1681
1707
|
if (mapped) {
|
|
1682
1708
|
acc.push({
|
|
1683
1709
|
...def,
|
|
1684
|
-
textSpan: { start: mapped.start, length: mapped.length }
|
|
1710
|
+
textSpan: { start: mapped.start, length: mapped.length }
|
|
1685
1711
|
});
|
|
1686
1712
|
}
|
|
1687
1713
|
return acc;
|
|
@@ -1689,9 +1715,9 @@ function init(modules) {
|
|
|
1689
1715
|
return {
|
|
1690
1716
|
textSpan: {
|
|
1691
1717
|
start: mappedTextSpan.start,
|
|
1692
|
-
length: mappedTextSpan.length
|
|
1718
|
+
length: mappedTextSpan.length
|
|
1693
1719
|
},
|
|
1694
|
-
definitions: mappedDefinitions
|
|
1720
|
+
definitions: mappedDefinitions
|
|
1695
1721
|
};
|
|
1696
1722
|
}
|
|
1697
1723
|
catch (e) {
|
|
@@ -1706,7 +1732,8 @@ function init(modules) {
|
|
|
1706
1732
|
* getDefinitionAtPosition but navigates to the type's definition
|
|
1707
1733
|
* rather than the symbol's definition.
|
|
1708
1734
|
*/
|
|
1709
|
-
const originalGetTypeDefinitionAtPosition = info.languageService
|
|
1735
|
+
const originalGetTypeDefinitionAtPosition = info.languageService
|
|
1736
|
+
.getTypeDefinitionAtPosition.bind(info.languageService);
|
|
1710
1737
|
info.languageService.getTypeDefinitionAtPosition = (fileName, position) => {
|
|
1711
1738
|
try {
|
|
1712
1739
|
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
@@ -1734,7 +1761,7 @@ function init(modules) {
|
|
|
1734
1761
|
if (mapped) {
|
|
1735
1762
|
acc.push({
|
|
1736
1763
|
...def,
|
|
1737
|
-
textSpan: { start: mapped.start, length: mapped.length }
|
|
1764
|
+
textSpan: { start: mapped.start, length: mapped.length }
|
|
1738
1765
|
});
|
|
1739
1766
|
}
|
|
1740
1767
|
return acc;
|
|
@@ -1752,7 +1779,8 @@ function init(modules) {
|
|
|
1752
1779
|
* finds all references in the expanded code, then maps each reference
|
|
1753
1780
|
* span back to original positions. References in generated code are filtered.
|
|
1754
1781
|
*/
|
|
1755
|
-
const originalGetReferencesAtPosition = info.languageService
|
|
1782
|
+
const originalGetReferencesAtPosition = info.languageService
|
|
1783
|
+
.getReferencesAtPosition.bind(info.languageService);
|
|
1756
1784
|
info.languageService.getReferencesAtPosition = (fileName, position) => {
|
|
1757
1785
|
try {
|
|
1758
1786
|
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
@@ -1780,7 +1808,7 @@ function init(modules) {
|
|
|
1780
1808
|
if (mapped) {
|
|
1781
1809
|
acc.push({
|
|
1782
1810
|
...ref,
|
|
1783
|
-
textSpan: { start: mapped.start, length: mapped.length }
|
|
1811
|
+
textSpan: { start: mapped.start, length: mapped.length }
|
|
1784
1812
|
});
|
|
1785
1813
|
}
|
|
1786
1814
|
return acc;
|
|
@@ -1828,11 +1856,11 @@ function init(modules) {
|
|
|
1828
1856
|
if (mapped) {
|
|
1829
1857
|
acc.push({
|
|
1830
1858
|
...ref,
|
|
1831
|
-
textSpan: { start: mapped.start, length: mapped.length }
|
|
1859
|
+
textSpan: { start: mapped.start, length: mapped.length }
|
|
1832
1860
|
});
|
|
1833
1861
|
}
|
|
1834
1862
|
return acc;
|
|
1835
|
-
}, [])
|
|
1863
|
+
}, [])
|
|
1836
1864
|
}))
|
|
1837
1865
|
.filter((s) => s.references.length > 0);
|
|
1838
1866
|
}
|
|
@@ -1847,7 +1875,8 @@ function init(modules) {
|
|
|
1847
1875
|
* Provides function signature help (parameter hints shown while typing
|
|
1848
1876
|
* function arguments). Maps cursor position and the applicable span.
|
|
1849
1877
|
*/
|
|
1850
|
-
const originalGetSignatureHelpItems = info.languageService
|
|
1878
|
+
const originalGetSignatureHelpItems = info.languageService
|
|
1879
|
+
.getSignatureHelpItems.bind(info.languageService);
|
|
1851
1880
|
info.languageService.getSignatureHelpItems = (fileName, position, options) => {
|
|
1852
1881
|
try {
|
|
1853
1882
|
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
@@ -1869,8 +1898,8 @@ function init(modules) {
|
|
|
1869
1898
|
...result,
|
|
1870
1899
|
applicableSpan: {
|
|
1871
1900
|
start: mappedSpan.start,
|
|
1872
|
-
length: mappedSpan.length
|
|
1873
|
-
}
|
|
1901
|
+
length: mappedSpan.length
|
|
1902
|
+
}
|
|
1874
1903
|
};
|
|
1875
1904
|
}
|
|
1876
1905
|
catch (e) {
|
|
@@ -1918,12 +1947,12 @@ function init(modules) {
|
|
|
1918
1947
|
if (!mappedSpan) {
|
|
1919
1948
|
return {
|
|
1920
1949
|
canRename: false,
|
|
1921
|
-
localizedErrorMessage:
|
|
1950
|
+
localizedErrorMessage: 'Cannot rename in generated code'
|
|
1922
1951
|
};
|
|
1923
1952
|
}
|
|
1924
1953
|
return {
|
|
1925
1954
|
...result,
|
|
1926
|
-
triggerSpan: { start: mappedSpan.start, length: mappedSpan.length }
|
|
1955
|
+
triggerSpan: { start: mappedSpan.start, length: mappedSpan.length }
|
|
1927
1956
|
};
|
|
1928
1957
|
}
|
|
1929
1958
|
catch (e) {
|
|
@@ -1942,7 +1971,8 @@ function init(modules) {
|
|
|
1942
1971
|
* Uses a compatibility wrapper (callFindRenameLocations) to handle
|
|
1943
1972
|
* different TypeScript version signatures.
|
|
1944
1973
|
*/
|
|
1945
|
-
const originalFindRenameLocations = info.languageService.findRenameLocations
|
|
1974
|
+
const originalFindRenameLocations = info.languageService.findRenameLocations
|
|
1975
|
+
.bind(info.languageService);
|
|
1946
1976
|
/**
|
|
1947
1977
|
* Compatibility wrapper for findRenameLocations that handles both old
|
|
1948
1978
|
* and new TypeScript API signatures.
|
|
@@ -1981,7 +2011,7 @@ function init(modules) {
|
|
|
1981
2011
|
if (mapped) {
|
|
1982
2012
|
acc.push({
|
|
1983
2013
|
...loc,
|
|
1984
|
-
textSpan: { start: mapped.start, length: mapped.length }
|
|
2014
|
+
textSpan: { start: mapped.start, length: mapped.length }
|
|
1985
2015
|
});
|
|
1986
2016
|
}
|
|
1987
2017
|
return acc;
|
|
@@ -1999,7 +2029,8 @@ function init(modules) {
|
|
|
1999
2029
|
* click on a variable and see all usages highlighted). Maps highlight
|
|
2000
2030
|
* spans back to original positions.
|
|
2001
2031
|
*/
|
|
2002
|
-
const originalGetDocumentHighlights = info.languageService
|
|
2032
|
+
const originalGetDocumentHighlights = info.languageService
|
|
2033
|
+
.getDocumentHighlights.bind(info.languageService);
|
|
2003
2034
|
info.languageService.getDocumentHighlights = (fileName, position, filesToSearch) => {
|
|
2004
2035
|
try {
|
|
2005
2036
|
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
@@ -2030,11 +2061,11 @@ function init(modules) {
|
|
|
2030
2061
|
if (mapped) {
|
|
2031
2062
|
acc.push({
|
|
2032
2063
|
...span,
|
|
2033
|
-
textSpan: { start: mapped.start, length: mapped.length }
|
|
2064
|
+
textSpan: { start: mapped.start, length: mapped.length }
|
|
2034
2065
|
});
|
|
2035
2066
|
}
|
|
2036
2067
|
return acc;
|
|
2037
|
-
}, [])
|
|
2068
|
+
}, [])
|
|
2038
2069
|
}))
|
|
2039
2070
|
.filter((h) => h.highlightSpans.length > 0);
|
|
2040
2071
|
}
|
|
@@ -2049,7 +2080,8 @@ function init(modules) {
|
|
|
2049
2080
|
* Provides "Go to Implementation" functionality. Similar to definition
|
|
2050
2081
|
* but finds concrete implementations of abstract methods/interfaces.
|
|
2051
2082
|
*/
|
|
2052
|
-
const originalGetImplementationAtPosition = info.languageService
|
|
2083
|
+
const originalGetImplementationAtPosition = info.languageService
|
|
2084
|
+
.getImplementationAtPosition.bind(info.languageService);
|
|
2053
2085
|
info.languageService.getImplementationAtPosition = (fileName, position) => {
|
|
2054
2086
|
try {
|
|
2055
2087
|
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
@@ -2077,7 +2109,7 @@ function init(modules) {
|
|
|
2077
2109
|
if (mapped) {
|
|
2078
2110
|
acc.push({
|
|
2079
2111
|
...impl,
|
|
2080
|
-
textSpan: { start: mapped.start, length: mapped.length }
|
|
2112
|
+
textSpan: { start: mapped.start, length: mapped.length }
|
|
2081
2113
|
});
|
|
2082
2114
|
}
|
|
2083
2115
|
return acc;
|
|
@@ -2100,7 +2132,8 @@ function init(modules) {
|
|
|
2100
2132
|
* could be problematic. Consider filtering or mapping fix edits in
|
|
2101
2133
|
* future versions.
|
|
2102
2134
|
*/
|
|
2103
|
-
const originalGetCodeFixesAtPosition = info.languageService
|
|
2135
|
+
const originalGetCodeFixesAtPosition = info.languageService
|
|
2136
|
+
.getCodeFixesAtPosition.bind(info.languageService);
|
|
2104
2137
|
info.languageService.getCodeFixesAtPosition = (fileName, start, end, errorCodes, formatOptions, preferences) => {
|
|
2105
2138
|
try {
|
|
2106
2139
|
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
@@ -2125,7 +2158,8 @@ function init(modules) {
|
|
|
2125
2158
|
* Provides the document outline/structure tree (shown in the Outline
|
|
2126
2159
|
* panel). Recursively maps all spans in the tree back to original positions.
|
|
2127
2160
|
*/
|
|
2128
|
-
const originalGetNavigationTree = info.languageService.getNavigationTree
|
|
2161
|
+
const originalGetNavigationTree = info.languageService.getNavigationTree
|
|
2162
|
+
.bind(info.languageService);
|
|
2129
2163
|
info.languageService.getNavigationTree = (fileName) => {
|
|
2130
2164
|
try {
|
|
2131
2165
|
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
@@ -2141,9 +2175,7 @@ function init(modules) {
|
|
|
2141
2175
|
function mapNavigationItem(item) {
|
|
2142
2176
|
const mappedSpans = item.spans.map((span) => {
|
|
2143
2177
|
const mapped = navMapper.mapSpanToOriginal(span.start, span.length);
|
|
2144
|
-
return mapped
|
|
2145
|
-
? { start: mapped.start, length: mapped.length }
|
|
2146
|
-
: span;
|
|
2178
|
+
return mapped ? { start: mapped.start, length: mapped.length } : span;
|
|
2147
2179
|
});
|
|
2148
2180
|
const mappedNameSpan = item.nameSpan
|
|
2149
2181
|
? (navMapper.mapSpanToOriginal(item.nameSpan.start, item.nameSpan.length) ?? item.nameSpan)
|
|
@@ -2154,7 +2186,7 @@ function init(modules) {
|
|
|
2154
2186
|
nameSpan: mappedNameSpan
|
|
2155
2187
|
? { start: mappedNameSpan.start, length: mappedNameSpan.length }
|
|
2156
2188
|
: undefined,
|
|
2157
|
-
childItems: item.childItems?.map(mapNavigationItem)
|
|
2189
|
+
childItems: item.childItems?.map(mapNavigationItem)
|
|
2158
2190
|
};
|
|
2159
2191
|
}
|
|
2160
2192
|
return mapNavigationItem(tree);
|
|
@@ -2170,7 +2202,8 @@ function init(modules) {
|
|
|
2170
2202
|
* Provides code folding regions. Maps both the text span (what gets
|
|
2171
2203
|
* folded) and hint span (what's shown when collapsed) back to original.
|
|
2172
2204
|
*/
|
|
2173
|
-
const originalGetOutliningSpans = info.languageService.getOutliningSpans
|
|
2205
|
+
const originalGetOutliningSpans = info.languageService.getOutliningSpans
|
|
2206
|
+
.bind(info.languageService);
|
|
2174
2207
|
info.languageService.getOutliningSpans = (fileName) => {
|
|
2175
2208
|
try {
|
|
2176
2209
|
if (virtualDtsFiles.has(fileName) || !shouldProcess(fileName)) {
|
|
@@ -2190,12 +2223,12 @@ function init(modules) {
|
|
|
2190
2223
|
...span,
|
|
2191
2224
|
textSpan: {
|
|
2192
2225
|
start: mappedTextSpan.start,
|
|
2193
|
-
length: mappedTextSpan.length
|
|
2226
|
+
length: mappedTextSpan.length
|
|
2194
2227
|
},
|
|
2195
2228
|
hintSpan: {
|
|
2196
2229
|
start: mappedHintSpan.start,
|
|
2197
|
-
length: mappedHintSpan.length
|
|
2198
|
-
}
|
|
2230
|
+
length: mappedHintSpan.length
|
|
2231
|
+
}
|
|
2199
2232
|
};
|
|
2200
2233
|
});
|
|
2201
2234
|
}
|
|
@@ -2215,7 +2248,8 @@ function init(modules) {
|
|
|
2215
2248
|
* This hook is conditional - provideInlayHints may not exist in older
|
|
2216
2249
|
* TypeScript versions.
|
|
2217
2250
|
*/
|
|
2218
|
-
const originalProvideInlayHints = info.languageService.provideInlayHints
|
|
2251
|
+
const originalProvideInlayHints = info.languageService.provideInlayHints
|
|
2252
|
+
?.bind(info.languageService);
|
|
2219
2253
|
if (originalProvideInlayHints) {
|
|
2220
2254
|
info.languageService.provideInlayHints = (fileName, span, preferences) => {
|
|
2221
2255
|
try {
|
|
@@ -2245,8 +2279,8 @@ function init(modules) {
|
|
|
2245
2279
|
return [
|
|
2246
2280
|
{
|
|
2247
2281
|
...hint,
|
|
2248
|
-
position: originalPos
|
|
2249
|
-
}
|
|
2282
|
+
position: originalPos
|
|
2283
|
+
}
|
|
2250
2284
|
];
|
|
2251
2285
|
});
|
|
2252
2286
|
}
|