@macroforge/typescript-plugin 0.1.37 → 0.1.38
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 +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +343 -15
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -59,17 +59,17 @@ npm install @macroforge/typescript-plugin
|
|
|
59
59
|
|
|
60
60
|
### Functions
|
|
61
61
|
|
|
62
|
+
- **`parseMacroImportComments`** - Parses macro import comments to extract macro name to module path mappings.
|
|
63
|
+
- **`getExternalManifest`** - Attempts to load the manifest from an external macro package.
|
|
64
|
+
- **`getExternalMacroInfo`** - Looks up macro info from an external package manifest.
|
|
65
|
+
- **`getExternalDecoratorInfo`** - Looks up decorator info from an external package manifest.
|
|
62
66
|
- **`findDeriveAtPosition`** - Finds a macro name within `@derive(...)` decorators at a given cursor position.
|
|
67
|
+
- **`findDeriveKeywordAtPosition`** - Finds the `@derive` keyword at a given cursor position.
|
|
63
68
|
- **`findDecoratorAtPosition`** - Finds a field decorator (like `@serde` or `@debug`) at a given cursor position.
|
|
64
|
-
- **`
|
|
69
|
+
- **`findEnclosingDeriveContext`** - const lastCommentEnd = beforeMatch.lastIndexOf("*/
|
|
70
|
+
- **`getMacroHoverInfo`** - Generates hover information (QuickInfo) for macros and decorators at a cursor position.
|
|
65
71
|
- **`shouldProcess`** - Determines whether a file should be processed for macro expansion.
|
|
66
|
-
-
|
|
67
|
-
- **`loadMacroConfig`** - Whether to preserve decorator syntax in the expanded output.
|
|
68
|
-
- **`init`** - Main plugin factory function conforming to the TypeScript Language Service Plugin API.
|
|
69
|
-
- **`create`** - Creates the plugin instance for a TypeScript project.
|
|
70
|
-
- **`processFile`** - Processes a file through macro expansion via the native Rust plugin.
|
|
71
|
-
- **`toPlainDiagnostic`** - Converts a TypeScript diagnostic to a plain object for the native plugin.
|
|
72
|
-
- ... and 1 more
|
|
72
|
+
- ... and 7 more
|
|
73
73
|
|
|
74
74
|
### Types
|
|
75
75
|
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,KAAK,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,KAAK,EAAE,MAAM,gCAAgC,CAAC;AAw1BrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,iBAAS,IAAI,CAAC,OAAO,EAAE;IAAE,UAAU,EAAE,OAAO,EAAE,CAAA;CAAE;mBAcxB,EAAE,CAAC,MAAM,CAAC,gBAAgB;EAg+DjD;AAED,SAAS,IAAI,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -102,6 +102,99 @@ function getMacroManifest() {
|
|
|
102
102
|
return null;
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Parses macro import comments to extract macro name to module path mappings.
|
|
107
|
+
*
|
|
108
|
+
* Macroforge supports importing external macros using a special JSDoc comment syntax:
|
|
109
|
+
* `/** import macro {MacroName, Another} from "@scope/package"; */`
|
|
110
|
+
*
|
|
111
|
+
* @param text - The source text to search for import comments
|
|
112
|
+
* @returns A Map of macro name to module path
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* const text = `/** import macro {Gigaform, CustomMacro} from "@playground/macro"; */`;
|
|
117
|
+
* parseMacroImportComments(text);
|
|
118
|
+
* // => Map { "Gigaform" => "@playground/macro", "CustomMacro" => "@playground/macro" }
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
function parseMacroImportComments(text) {
|
|
122
|
+
const imports = new Map();
|
|
123
|
+
const pattern = /\/\*\*\s*import\s+macro\s*\{([^}]+)\}\s*from\s*["']([^"']+)["']/gi;
|
|
124
|
+
let match;
|
|
125
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
126
|
+
const names = match[1]
|
|
127
|
+
.split(",")
|
|
128
|
+
.map((n) => n.trim())
|
|
129
|
+
.filter(Boolean);
|
|
130
|
+
const modulePath = match[2];
|
|
131
|
+
for (const name of names) {
|
|
132
|
+
imports.set(name, modulePath);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return imports;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Cache for external macro package manifests.
|
|
139
|
+
* Maps package path to its manifest (or null if failed to load).
|
|
140
|
+
*/
|
|
141
|
+
const externalManifestCache = new Map();
|
|
142
|
+
/**
|
|
143
|
+
* Attempts to load the manifest from an external macro package.
|
|
144
|
+
*
|
|
145
|
+
* External macro packages (like `@playground/macro`) export their own
|
|
146
|
+
* `__macroforgeGetManifest()` function that provides macro metadata
|
|
147
|
+
* including descriptions.
|
|
148
|
+
*
|
|
149
|
+
* @param modulePath - The package path (e.g., "@playground/macro")
|
|
150
|
+
* @returns The macro manifest, or null if loading failed
|
|
151
|
+
*/
|
|
152
|
+
function getExternalManifest(modulePath) {
|
|
153
|
+
if (externalManifestCache.has(modulePath)) {
|
|
154
|
+
return externalManifestCache.get(modulePath) ?? null;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
// Try to require the external package
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
159
|
+
const pkg = require(modulePath);
|
|
160
|
+
if (typeof pkg.__macroforgeGetManifest === "function") {
|
|
161
|
+
const manifest = pkg.__macroforgeGetManifest();
|
|
162
|
+
externalManifestCache.set(modulePath, manifest);
|
|
163
|
+
return manifest;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// Package not found or doesn't export manifest
|
|
168
|
+
}
|
|
169
|
+
externalManifestCache.set(modulePath, null);
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Looks up macro info from an external package manifest.
|
|
174
|
+
*
|
|
175
|
+
* @param macroName - The macro name to look up
|
|
176
|
+
* @param modulePath - The package path
|
|
177
|
+
* @returns The macro manifest entry, or null if not found
|
|
178
|
+
*/
|
|
179
|
+
function getExternalMacroInfo(macroName, modulePath) {
|
|
180
|
+
const manifest = getExternalManifest(modulePath);
|
|
181
|
+
if (!manifest)
|
|
182
|
+
return null;
|
|
183
|
+
return (manifest.macros.find((m) => m.name.toLowerCase() === macroName.toLowerCase()) ?? null);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Looks up decorator info from an external package manifest.
|
|
187
|
+
*
|
|
188
|
+
* @param decoratorName - The decorator name to look up
|
|
189
|
+
* @param modulePath - The package path
|
|
190
|
+
* @returns The decorator manifest entry, or null if not found
|
|
191
|
+
*/
|
|
192
|
+
function getExternalDecoratorInfo(decoratorName, modulePath) {
|
|
193
|
+
const manifest = getExternalManifest(modulePath);
|
|
194
|
+
if (!manifest)
|
|
195
|
+
return null;
|
|
196
|
+
return (manifest.decorators.find((d) => d.export.toLowerCase() === decoratorName.toLowerCase()) ?? null);
|
|
197
|
+
}
|
|
105
198
|
/**
|
|
106
199
|
* Finds a macro name within `@derive(...)` decorators at a given cursor position.
|
|
107
200
|
*
|
|
@@ -164,6 +257,41 @@ function findDeriveAtPosition(text, position) {
|
|
|
164
257
|
}
|
|
165
258
|
return null;
|
|
166
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Finds the `@derive` keyword at a given cursor position.
|
|
262
|
+
* This matches the literal "@derive" text before the opening parenthesis,
|
|
263
|
+
* allowing hover documentation on the directive keyword itself.
|
|
264
|
+
*
|
|
265
|
+
* @param text - The source text to search
|
|
266
|
+
* @param position - The cursor position as a 0-indexed character offset
|
|
267
|
+
* @returns An object with start/end positions, or `null` if not on @derive keyword
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```typescript
|
|
271
|
+
* // Given text: "/** @derive(Debug) */"
|
|
272
|
+
* findDeriveKeywordAtPosition(text, 5);
|
|
273
|
+
* // => { start: 4, end: 11 } // covers "@derive"
|
|
274
|
+
*
|
|
275
|
+
* // Position on "Debug" (inside parens) returns null
|
|
276
|
+
* findDeriveKeywordAtPosition(text, 12);
|
|
277
|
+
* // => null
|
|
278
|
+
* ```
|
|
279
|
+
*
|
|
280
|
+
* @see {@link findDeriveAtPosition} - For macro names inside @derive()
|
|
281
|
+
*/
|
|
282
|
+
function findDeriveKeywordAtPosition(text, position) {
|
|
283
|
+
// Match @derive only when followed by ( to distinguish from other uses
|
|
284
|
+
const deriveKeywordPattern = /@derive(?=\s*\()/gi;
|
|
285
|
+
let match;
|
|
286
|
+
while ((match = deriveKeywordPattern.exec(text)) !== null) {
|
|
287
|
+
const start = match.index; // Position of @
|
|
288
|
+
const end = start + "@derive".length;
|
|
289
|
+
if (position >= start && position < end) {
|
|
290
|
+
return { start, end };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
167
295
|
/**
|
|
168
296
|
* Finds a field decorator (like `@serde` or `@debug`) at a given cursor position.
|
|
169
297
|
*
|
|
@@ -221,12 +349,54 @@ function findDecoratorAtPosition(text, position) {
|
|
|
221
349
|
}
|
|
222
350
|
return null;
|
|
223
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Finds what `@derive` macros apply to code at a given position.
|
|
354
|
+
*
|
|
355
|
+
* This function uses a heuristic: it finds the nearest `@derive(...)` decorator
|
|
356
|
+
* that appears before the given position. This is useful for determining which
|
|
357
|
+
* macros might be responsible for a particular field decorator.
|
|
358
|
+
*
|
|
359
|
+
* @param text - The source text to search
|
|
360
|
+
* @param position - The cursor position as a 0-indexed character offset
|
|
361
|
+
* @returns An array of macro names from the enclosing @derive, or `null` if not found
|
|
362
|
+
*
|
|
363
|
+
* @example
|
|
364
|
+
* ```typescript
|
|
365
|
+
* const text = `/** @derive(Debug, Serialize) */
|
|
366
|
+
* class User {
|
|
367
|
+
* @serde({ skip: true })
|
|
368
|
+
* password: string;
|
|
369
|
+
* }`;
|
|
370
|
+
*
|
|
371
|
+
* // Position on @serde
|
|
372
|
+
* findEnclosingDeriveContext(text, text.indexOf("@serde"));
|
|
373
|
+
* // => ["Debug", "Serialize"]
|
|
374
|
+
* ```
|
|
375
|
+
*/
|
|
376
|
+
function findEnclosingDeriveContext(text, position) {
|
|
377
|
+
const beforePosition = text.substring(0, position);
|
|
378
|
+
const derivePattern = /@derive\s*\(\s*([^)]+)\s*\)/gi;
|
|
379
|
+
let lastMatch = null;
|
|
380
|
+
let match;
|
|
381
|
+
while ((match = derivePattern.exec(beforePosition)) !== null) {
|
|
382
|
+
lastMatch = match;
|
|
383
|
+
}
|
|
384
|
+
if (lastMatch) {
|
|
385
|
+
const macros = lastMatch[1]
|
|
386
|
+
.split(",")
|
|
387
|
+
.map((m) => m.trim())
|
|
388
|
+
.filter(Boolean);
|
|
389
|
+
return macros;
|
|
390
|
+
}
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
224
393
|
/**
|
|
225
394
|
* Generates hover information (QuickInfo) for macros and decorators at a cursor position.
|
|
226
395
|
*
|
|
227
396
|
* This function provides IDE hover tooltips for Macroforge-specific syntax:
|
|
228
|
-
* -
|
|
229
|
-
* -
|
|
397
|
+
* - The `@derive` keyword itself
|
|
398
|
+
* - Macro names within `@derive(...)` JSDoc decorators (both built-in and external)
|
|
399
|
+
* - Field decorators like `@serde`, `@debug`, and custom decorators from external macros
|
|
230
400
|
*
|
|
231
401
|
* @param text - The source text to analyze
|
|
232
402
|
* @param position - The cursor position as a 0-indexed character offset
|
|
@@ -236,42 +406,93 @@ function findDecoratorAtPosition(text, position) {
|
|
|
236
406
|
*
|
|
237
407
|
* @remarks
|
|
238
408
|
* The function checks positions in the following order:
|
|
239
|
-
* 1.
|
|
240
|
-
* 2.
|
|
409
|
+
* 1. Check if cursor is on the `@derive` keyword via {@link findDeriveKeywordAtPosition}
|
|
410
|
+
* 2. Check if cursor is on a macro name within `@derive(...)` via {@link findDeriveAtPosition}
|
|
411
|
+
* - First checks built-in manifest via {@link getMacroManifest}
|
|
412
|
+
* - Then checks external macro imports via {@link parseMacroImportComments}
|
|
413
|
+
* - Falls back to generic hover for unknown macros
|
|
414
|
+
* 3. Check if cursor is on a field decorator via {@link findDecoratorAtPosition}
|
|
415
|
+
* - First checks built-in manifest (macros and decorators)
|
|
416
|
+
* - Then checks external package manifests via {@link getExternalDecoratorInfo}
|
|
417
|
+
* - Falls back to generic hover showing enclosing derive context
|
|
418
|
+
*
|
|
419
|
+
* For external macros (imported via `/** import macro {Name} from "package"; * /`),
|
|
420
|
+
* the function attempts to load the external package's manifest to retrieve
|
|
421
|
+
* descriptions and documentation. See {@link getExternalMacroInfo}.
|
|
241
422
|
*
|
|
242
423
|
* The returned QuickInfo includes:
|
|
243
|
-
* - `kind`:
|
|
424
|
+
* - `kind`: `keyword` for @derive, `functionElement` for macros/decorators
|
|
244
425
|
* - `textSpan`: The highlighted range in the editor
|
|
245
426
|
* - `displayParts`: The formatted display text (e.g., "@derive(Debug)")
|
|
246
427
|
* - `documentation`: The macro/decorator description from the manifest
|
|
247
428
|
*
|
|
248
429
|
* @example
|
|
249
430
|
* ```typescript
|
|
431
|
+
* // Hovering over "@derive" keyword
|
|
432
|
+
* const info = getMacroHoverInfo(text, 4, ts);
|
|
433
|
+
* // Returns QuickInfo with documentation about the derive directive
|
|
434
|
+
*
|
|
250
435
|
* // Hovering over "Debug" in "@derive(Debug, Clone)"
|
|
251
436
|
* const info = getMacroHoverInfo(text, 14, ts);
|
|
252
437
|
* // Returns QuickInfo with:
|
|
253
438
|
* // - displayParts: "@derive(Debug)"
|
|
254
439
|
* // - documentation: "Generates a fmt_debug() method for debugging output"
|
|
255
440
|
*
|
|
441
|
+
* // Hovering over external macro "Gigaform" in "@derive(Gigaform)"
|
|
442
|
+
* const info = getMacroHoverInfo(text, 14, ts);
|
|
443
|
+
* // Returns QuickInfo with description loaded from @playground/macro package
|
|
444
|
+
*
|
|
256
445
|
* // Hovering over "@serde" field decorator
|
|
257
446
|
* const info = getMacroHoverInfo(text, 5, ts);
|
|
258
447
|
* // Returns QuickInfo with:
|
|
259
448
|
* // - displayParts: "@serde"
|
|
260
449
|
* // - documentation: "Serialization/deserialization field options"
|
|
450
|
+
*
|
|
451
|
+
* // Hovering over "@hiddenController" from external Gigaform macro
|
|
452
|
+
* const info = getMacroHoverInfo(text, 5, ts);
|
|
453
|
+
* // Returns QuickInfo with docs loaded from external package manifest
|
|
261
454
|
* ```
|
|
262
455
|
*
|
|
456
|
+
* @see {@link findDeriveKeywordAtPosition} - Locates the @derive keyword
|
|
263
457
|
* @see {@link findDeriveAtPosition} - Locates macro names in @derive decorators
|
|
264
458
|
* @see {@link findDecoratorAtPosition} - Locates field decorators
|
|
265
|
-
* @see {@link
|
|
459
|
+
* @see {@link findEnclosingDeriveContext} - Finds macros that apply to a position
|
|
460
|
+
* @see {@link getMacroManifest} - Provides built-in macro/decorator metadata
|
|
461
|
+
* @see {@link getExternalMacroInfo} - Provides external macro metadata
|
|
462
|
+
* @see {@link getExternalDecoratorInfo} - Provides external decorator metadata
|
|
266
463
|
*/
|
|
267
464
|
function getMacroHoverInfo(text, position, tsModule) {
|
|
268
465
|
const manifest = getMacroManifest();
|
|
269
|
-
if
|
|
270
|
-
|
|
271
|
-
|
|
466
|
+
// 1. Check if hovering on @derive keyword itself
|
|
467
|
+
const deriveKeyword = findDeriveKeywordAtPosition(text, position);
|
|
468
|
+
if (deriveKeyword) {
|
|
469
|
+
return {
|
|
470
|
+
kind: tsModule.ScriptElementKind.keyword,
|
|
471
|
+
kindModifiers: "",
|
|
472
|
+
textSpan: {
|
|
473
|
+
start: deriveKeyword.start,
|
|
474
|
+
length: deriveKeyword.end - deriveKeyword.start,
|
|
475
|
+
},
|
|
476
|
+
displayParts: [{ text: "@derive", kind: "keyword" }],
|
|
477
|
+
documentation: [
|
|
478
|
+
{
|
|
479
|
+
text: "Derive directive - applies compile-time macros to generate methods and implementations.\n\n" +
|
|
480
|
+
"**Usage:** `/** @derive(MacroName, AnotherMacro) */`\n\n" +
|
|
481
|
+
"**Built-in macros:** Debug, Clone, Default, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize\n\n" +
|
|
482
|
+
"External macros can be imported using:\n" +
|
|
483
|
+
'`/** import macro {Name} from "package"; */`',
|
|
484
|
+
kind: "text",
|
|
485
|
+
},
|
|
486
|
+
],
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
// Parse external macro imports for later use
|
|
490
|
+
const externalMacros = parseMacroImportComments(text);
|
|
491
|
+
// 2. Check for @derive(MacroName) in JSDoc comments
|
|
272
492
|
const deriveMatch = findDeriveAtPosition(text, position);
|
|
273
493
|
if (deriveMatch) {
|
|
274
|
-
|
|
494
|
+
// 2a. Check built-in manifest
|
|
495
|
+
const macroInfo = manifest?.macros.get(deriveMatch.macroName.toLowerCase());
|
|
275
496
|
if (macroInfo) {
|
|
276
497
|
return {
|
|
277
498
|
kind: tsModule.ScriptElementKind.functionElement,
|
|
@@ -290,12 +511,63 @@ function getMacroHoverInfo(text, position, tsModule) {
|
|
|
290
511
|
: [],
|
|
291
512
|
};
|
|
292
513
|
}
|
|
514
|
+
// 2b. Check external macro imports
|
|
515
|
+
const modulePath = externalMacros.get(deriveMatch.macroName);
|
|
516
|
+
if (modulePath) {
|
|
517
|
+
// Try to get detailed info from the external package manifest
|
|
518
|
+
const externalMacroInfo = getExternalMacroInfo(deriveMatch.macroName, modulePath);
|
|
519
|
+
const description = externalMacroInfo?.description
|
|
520
|
+
? externalMacroInfo.description
|
|
521
|
+
: "This macro is loaded from an external package at compile time.";
|
|
522
|
+
return {
|
|
523
|
+
kind: tsModule.ScriptElementKind.functionElement,
|
|
524
|
+
kindModifiers: "external",
|
|
525
|
+
textSpan: {
|
|
526
|
+
start: deriveMatch.start,
|
|
527
|
+
length: deriveMatch.end - deriveMatch.start,
|
|
528
|
+
},
|
|
529
|
+
displayParts: [
|
|
530
|
+
{ text: "@derive(", kind: "punctuation" },
|
|
531
|
+
{ text: externalMacroInfo?.name ?? deriveMatch.macroName, kind: "functionName" },
|
|
532
|
+
{ text: ")", kind: "punctuation" },
|
|
533
|
+
],
|
|
534
|
+
documentation: [
|
|
535
|
+
{
|
|
536
|
+
text: `**External macro** from \`${modulePath}\`\n\n${description}`,
|
|
537
|
+
kind: "text",
|
|
538
|
+
},
|
|
539
|
+
],
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
// 2c. Fallback for unknown/unrecognized macros
|
|
543
|
+
return {
|
|
544
|
+
kind: tsModule.ScriptElementKind.functionElement,
|
|
545
|
+
kindModifiers: "",
|
|
546
|
+
textSpan: {
|
|
547
|
+
start: deriveMatch.start,
|
|
548
|
+
length: deriveMatch.end - deriveMatch.start,
|
|
549
|
+
},
|
|
550
|
+
displayParts: [
|
|
551
|
+
{ text: "@derive(", kind: "punctuation" },
|
|
552
|
+
{ text: deriveMatch.macroName, kind: "functionName" },
|
|
553
|
+
{ text: ")", kind: "punctuation" },
|
|
554
|
+
],
|
|
555
|
+
documentation: [
|
|
556
|
+
{
|
|
557
|
+
text: `**Macro:** ${deriveMatch.macroName}\n\n` +
|
|
558
|
+
"This macro is not in the built-in manifest. If it's a custom macro, " +
|
|
559
|
+
"ensure it's imported using:\n\n" +
|
|
560
|
+
`\`/** import macro {${deriveMatch.macroName}} from "your-package"; */\``,
|
|
561
|
+
kind: "text",
|
|
562
|
+
},
|
|
563
|
+
],
|
|
564
|
+
};
|
|
293
565
|
}
|
|
294
|
-
// Check for @decorator patterns
|
|
566
|
+
// 3. Check for @decorator patterns
|
|
295
567
|
const decoratorMatch = findDecoratorAtPosition(text, position);
|
|
296
568
|
if (decoratorMatch) {
|
|
297
|
-
// Check if it's a macro name
|
|
298
|
-
const macroInfo = manifest
|
|
569
|
+
// 3a. Check if it's a built-in macro name
|
|
570
|
+
const macroInfo = manifest?.macros.get(decoratorMatch.name.toLowerCase());
|
|
299
571
|
if (macroInfo) {
|
|
300
572
|
return {
|
|
301
573
|
kind: tsModule.ScriptElementKind.functionElement,
|
|
@@ -313,8 +585,8 @@ function getMacroHoverInfo(text, position, tsModule) {
|
|
|
313
585
|
: [],
|
|
314
586
|
};
|
|
315
587
|
}
|
|
316
|
-
// Check if it's a decorator
|
|
317
|
-
const decoratorInfo = manifest
|
|
588
|
+
// 3b. Check if it's a built-in decorator
|
|
589
|
+
const decoratorInfo = manifest?.decorators.get(decoratorMatch.name.toLowerCase());
|
|
318
590
|
if (decoratorInfo && decoratorInfo.docs) {
|
|
319
591
|
return {
|
|
320
592
|
kind: tsModule.ScriptElementKind.functionElement,
|
|
@@ -330,6 +602,62 @@ function getMacroHoverInfo(text, position, tsModule) {
|
|
|
330
602
|
documentation: [{ text: decoratorInfo.docs, kind: "text" }],
|
|
331
603
|
};
|
|
332
604
|
}
|
|
605
|
+
// 3c. Check if this decorator is in a macro context (for external/custom decorators)
|
|
606
|
+
const enclosingMacros = findEnclosingDeriveContext(text, decoratorMatch.start);
|
|
607
|
+
if (enclosingMacros && enclosingMacros.length > 0) {
|
|
608
|
+
// Find which external macro might define this decorator
|
|
609
|
+
const likelySourceMacro = enclosingMacros.find((m) => externalMacros.has(m));
|
|
610
|
+
if (likelySourceMacro) {
|
|
611
|
+
const modulePath = externalMacros.get(likelySourceMacro);
|
|
612
|
+
// Try to get detailed decorator info from the external package
|
|
613
|
+
const externalDecoratorInfo = modulePath
|
|
614
|
+
? getExternalDecoratorInfo(decoratorMatch.name, modulePath)
|
|
615
|
+
: null;
|
|
616
|
+
const description = externalDecoratorInfo?.docs
|
|
617
|
+
? externalDecoratorInfo.docs
|
|
618
|
+
: "This decorator configures field-level behavior for the macro.";
|
|
619
|
+
return {
|
|
620
|
+
kind: tsModule.ScriptElementKind.functionElement,
|
|
621
|
+
kindModifiers: "external",
|
|
622
|
+
textSpan: {
|
|
623
|
+
start: decoratorMatch.start,
|
|
624
|
+
length: decoratorMatch.end - decoratorMatch.start,
|
|
625
|
+
},
|
|
626
|
+
displayParts: [
|
|
627
|
+
{ text: "@", kind: "punctuation" },
|
|
628
|
+
{ text: externalDecoratorInfo?.export ?? decoratorMatch.name, kind: "functionName" },
|
|
629
|
+
],
|
|
630
|
+
documentation: [
|
|
631
|
+
{
|
|
632
|
+
text: `**Field decorator** from \`${likelySourceMacro}\` macro (\`${modulePath}\`)\n\n` +
|
|
633
|
+
description,
|
|
634
|
+
kind: "text",
|
|
635
|
+
},
|
|
636
|
+
],
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
// Fallback: Generic decorator in macro context
|
|
640
|
+
return {
|
|
641
|
+
kind: tsModule.ScriptElementKind.functionElement,
|
|
642
|
+
kindModifiers: "",
|
|
643
|
+
textSpan: {
|
|
644
|
+
start: decoratorMatch.start,
|
|
645
|
+
length: decoratorMatch.end - decoratorMatch.start,
|
|
646
|
+
},
|
|
647
|
+
displayParts: [
|
|
648
|
+
{ text: "@", kind: "punctuation" },
|
|
649
|
+
{ text: decoratorMatch.name, kind: "functionName" },
|
|
650
|
+
],
|
|
651
|
+
documentation: [
|
|
652
|
+
{
|
|
653
|
+
text: `**Field decorator:** ${decoratorMatch.name}\n\n` +
|
|
654
|
+
`Used with @derive(${enclosingMacros.join(", ")}).\n` +
|
|
655
|
+
"This decorator configures field-level behavior for the applied macros.",
|
|
656
|
+
kind: "text",
|
|
657
|
+
},
|
|
658
|
+
],
|
|
659
|
+
};
|
|
660
|
+
}
|
|
333
661
|
}
|
|
334
662
|
return null;
|
|
335
663
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@macroforge/typescript-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"description": "TypeScript language service plugin that augments classes decorated with @derive to include macro-generated methods.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"test": "bun run build && node --test tests/**/*.test.js"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"macroforge": "^0.1.
|
|
36
|
+
"macroforge": "^0.1.38"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"typescript": ">=5.0.0"
|