@shapeshift-labs/frontier-lang-compiler 0.2.103 → 0.2.105

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.
Files changed (125) hide show
  1. package/README.md +14 -0
  2. package/dist/declarations/bidirectional-target-change-source-edit.d.ts +30 -0
  3. package/dist/declarations/bidirectional-target-change.d.ts +10 -0
  4. package/dist/declarations/js-ts-safe-member-merge.d.ts +86 -0
  5. package/dist/declarations/js-ts-safe-merge.d.ts +131 -0
  6. package/dist/declarations/js-ts-semantic-conflict-sidecars.d.ts +235 -0
  7. package/dist/declarations/js-ts-semantic-merge-contracts.d.ts +287 -0
  8. package/dist/declarations/js-ts-semantic-merge.d.ts +4 -0
  9. package/dist/declarations/native-import-losses.d.ts +3 -0
  10. package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +12 -0
  11. package/dist/declarations/semantic-edit-script.d.ts +7 -4
  12. package/dist/declarations/semantic-patch-bundle-index.d.ts +45 -0
  13. package/dist/declarations/semantic-patch-bundle.d.ts +6 -4
  14. package/dist/declarations/semantic-sidecar-example.d.ts +18 -0
  15. package/dist/declarations/semantic-transform-identity.d.ts +3 -0
  16. package/dist/declarations/source-preservation.d.ts +72 -0
  17. package/dist/declarations/universal-capability.d.ts +4 -0
  18. package/dist/declarations/universal-conversion-artifacts.d.ts +61 -1
  19. package/dist/declarations/universal-conversion-compact-counts.d.ts +51 -0
  20. package/dist/declarations/universal-conversion-plan.d.ts +6 -1
  21. package/dist/declarations/universal-representation-coverage.d.ts +90 -0
  22. package/dist/index.d.ts +4 -0
  23. package/dist/index.js +3 -0
  24. package/dist/internal/index-impl/bidirectionalExactSourceBackprojection.js +199 -0
  25. package/dist/internal/index-impl/bidirectionalSameLanguageSourceProjection.js +112 -0
  26. package/dist/internal/index-impl/bidirectionalSourceEditProjection.js +319 -0
  27. package/dist/internal/index-impl/bidirectionalSourceEditProjectionArtifacts.js +67 -0
  28. package/dist/internal/index-impl/bidirectionalTargetChangeRecordInternals.js +17 -5
  29. package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +58 -20
  30. package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +60 -7
  31. package/dist/internal/index-impl/createLightweightNativeImport.js +1 -0
  32. package/dist/internal/index-impl/createNativeSourcePreservation.js +28 -2
  33. package/dist/internal/index-impl/diffNativeSymbols.js +3 -3
  34. package/dist/internal/index-impl/nativeChangeProjectionSourceMapLinks.js +2 -0
  35. package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +43 -8
  36. package/dist/internal/index-impl/replaySemanticEditLineEndings.js +34 -0
  37. package/dist/internal/index-impl/replaySemanticEditProjection.js +39 -19
  38. package/dist/internal/index-impl/semanticEditBundleAdmission.js +7 -3
  39. package/dist/internal/index-impl/semanticEditBundleIndex.js +47 -1
  40. package/dist/internal/index-impl/semanticEditExplicitSourceReplacement.js +40 -0
  41. package/dist/internal/index-impl/semanticEditOperationCoverage.js +33 -3
  42. package/dist/internal/index-impl/semanticEditProjectionRecord.js +29 -0
  43. package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +39 -0
  44. package/dist/internal/index-impl/semanticEditReplaySourceReplacement.js +85 -0
  45. package/dist/internal/index-impl/semanticEditScripts.js +4 -0
  46. package/dist/internal/index-impl/semanticEditSourceRanges.js +27 -0
  47. package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +1 -0
  48. package/dist/internal/index-impl/semanticPatchBundleAdmission.js +41 -7
  49. package/dist/internal/index-impl/semanticPatchBundleRecords.js +16 -0
  50. package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +2 -0
  51. package/dist/internal/index-impl/semanticSidecarQuality.js +111 -0
  52. package/dist/internal/index-impl/semanticSourceEditDedupe.js +69 -9
  53. package/dist/internal/index-impl/semanticTransformIdentityRecords.js +85 -9
  54. package/dist/js-ts-safe-member-merge-result.js +158 -0
  55. package/dist/js-ts-safe-member-merge.js +265 -0
  56. package/dist/js-ts-safe-merge-analyze.js +279 -0
  57. package/dist/js-ts-safe-merge-composed.js +170 -0
  58. package/dist/js-ts-safe-merge-constants.js +50 -0
  59. package/dist/js-ts-safe-merge-context.js +118 -0
  60. package/dist/js-ts-safe-merge-ledger-validation.js +92 -0
  61. package/dist/js-ts-safe-merge-ledger.js +85 -0
  62. package/dist/js-ts-safe-merge-parse-declarations.js +210 -0
  63. package/dist/js-ts-safe-merge-parse-statements.js +155 -0
  64. package/dist/js-ts-safe-merge-plan.js +190 -0
  65. package/dist/js-ts-safe-merge.js +175 -0
  66. package/dist/js-ts-semantic-conflict-sidecar-constants.js +77 -0
  67. package/dist/js-ts-semantic-conflict-sidecar-detectors.js +195 -0
  68. package/dist/js-ts-semantic-conflict-sidecar-normalize.js +203 -0
  69. package/dist/js-ts-semantic-conflict-sidecar-utils.js +190 -0
  70. package/dist/js-ts-semantic-conflict-sidecars.js +81 -0
  71. package/dist/js-ts-semantic-merge-contract-helpers.js +128 -0
  72. package/dist/js-ts-semantic-merge-contracts.js +217 -0
  73. package/dist/js-ts-semantic-merge-member-containers.js +100 -0
  74. package/dist/js-ts-semantic-merge-member-keys.js +142 -0
  75. package/dist/js-ts-semantic-merge-member-segments.js +185 -0
  76. package/dist/js-ts-semantic-merge-member-source.js +82 -0
  77. package/dist/js-ts-semantic-merge-member-utils.js +18 -0
  78. package/dist/js-ts-semantic-merge-parse.js +16 -0
  79. package/dist/js-ts-semantic-merge.js +24 -0
  80. package/dist/lightweight-dependency-effects.js +51 -0
  81. package/dist/lightweight-dependency-language.js +12 -1
  82. package/dist/lightweight-dependency-relations.js +14 -27
  83. package/dist/native-region-scanner-core.js +33 -1
  84. package/dist/native-region-scanner-csharp.js +151 -0
  85. package/dist/native-region-scanner-dart.js +91 -0
  86. package/dist/native-region-scanner-dynamic.js +21 -151
  87. package/dist/native-region-scanner-functional.js +40 -13
  88. package/dist/native-region-scanner-java.js +97 -0
  89. package/dist/native-region-scanner-js-class.js +100 -0
  90. package/dist/native-region-scanner-js-helpers.js +28 -86
  91. package/dist/native-region-scanner-js-imports.js +121 -1
  92. package/dist/native-region-scanner-js-nested.js +96 -8
  93. package/dist/native-region-scanner-js-structure.js +27 -0
  94. package/dist/native-region-scanner-js-types.js +99 -0
  95. package/dist/native-region-scanner-js.js +70 -118
  96. package/dist/native-region-scanner-kotlin.js +94 -0
  97. package/dist/native-region-scanner-main.js +15 -181
  98. package/dist/native-region-scanner-php.js +80 -0
  99. package/dist/native-region-scanner-python.js +62 -0
  100. package/dist/native-region-scanner-ruby.js +72 -0
  101. package/dist/native-region-scanner-scala.js +91 -0
  102. package/dist/native-region-scanner-spans.js +74 -0
  103. package/dist/native-region-scanner-swift.js +155 -0
  104. package/dist/native-region-scanner.js +14 -10
  105. package/dist/native-source-ledger-helpers.js +195 -0
  106. package/dist/native-source-ledger.js +306 -0
  107. package/dist/native-source-preservation-scanner.js +4 -0
  108. package/dist/semantic-import-callsite-regions.js +136 -0
  109. package/dist/semantic-import-effect-regions.js +283 -0
  110. package/dist/semantic-import-regions.js +11 -2
  111. package/dist/semantic-import-sidecar-entry.js +16 -2
  112. package/dist/semantic-import-sidecar-types.d.ts +2 -0
  113. package/dist/semantic-sidecar-example.js +68 -0
  114. package/dist/universal-capability-matrix.js +23 -0
  115. package/dist/universal-conversion-artifact-query.js +79 -2
  116. package/dist/universal-conversion-artifact-semantic-edit.js +103 -0
  117. package/dist/universal-conversion-artifact-summary.js +33 -1
  118. package/dist/universal-conversion-artifacts.js +13 -48
  119. package/dist/universal-conversion-plan-scoring.js +21 -1
  120. package/dist/universal-conversion-plan-summary.js +30 -0
  121. package/dist/universal-conversion-plan.js +25 -9
  122. package/dist/universal-conversion-route-metadata.js +96 -0
  123. package/dist/universal-conversion-route-operations.js +7 -0
  124. package/dist/universal-representation-coverage.js +193 -0
  125. package/package.json +1 -1
@@ -7,6 +7,7 @@ import {
7
7
  isDependencyIdentifier,
8
8
  maskDependencyLine
9
9
  } from './lightweight-dependency-language.js';
10
+ import { lightweightEffectKinds } from './lightweight-dependency-effects.js';
10
11
  import { sourceLines } from './native-region-scanner-core.js';
11
12
 
12
13
  export function lightweightDependencyRelations(input, declarations, documentId) {
@@ -108,6 +109,7 @@ function addLightweightSemanticFacts(input, documentId, declaration, line, lineN
108
109
  function shouldScanRuntimeFacts(input, declaration) {
109
110
  if (!isJavaScriptLike(input)) return true;
110
111
  if (declaration?.fields?.typeKind) return false;
112
+ if (['object', 'array'].includes(declaration?.fields?.initializerKind)) return false;
111
113
  if (/^Type(?:Alias|Method|Property|FunctionProperty)/.test(String(declaration?.kind ?? ''))) return false;
112
114
  return !['interface', 'type'].includes(String(declaration?.symbolKind ?? '').toLowerCase());
113
115
  }
@@ -119,6 +121,10 @@ function isJavaScriptLike(input) {
119
121
  function isIgnoredDependencyOccurrence(input, line, startIndex, name) {
120
122
  if (!isJavaScriptLike(input)) return false;
121
123
  const endIndex = startIndex + String(name).length;
124
+ const beforeText = line.slice(0, startIndex);
125
+ if (/\b(?:async\s+)?function\*?\s*$/.test(beforeText) || /\bclass\s*$/.test(beforeText)) return true;
126
+ const afterText = line.slice(endIndex);
127
+ if (/^\s*\([^)]*\)\s*\{/.test(afterText) && /^\s*(?:async\s+)?(?:get\s+|set\s+)?$/.test(beforeText)) return true;
122
128
  const previous = previousNonSpace(line, startIndex - 1);
123
129
  const next = nextNonSpace(line, endIndex);
124
130
  return previous === '.' || next === ':';
@@ -148,17 +154,6 @@ function lightweightControlFlowKinds(line, state = {}) {
148
154
  return kinds;
149
155
  }
150
156
 
151
- function lightweightEffectKinds(line) {
152
- const kinds = [];
153
- if (/\bawait\b|import\s*\(/.test(line)) kinds.push('async');
154
- if (hasGlobalNetworkCall(line)) kinds.push('network');
155
- if (/\b(localStorage|sessionStorage|indexedDB|caches|cookie)\b/.test(line)) kinds.push('storage');
156
- if (/\b(setTimeout|setInterval|requestAnimationFrame|queueMicrotask)\s*\(/.test(line)) kinds.push('scheduler');
157
- if (/\b(console|process|Deno|Bun)\s*\./.test(line)) kinds.push('host');
158
- if (/\b(document|window|navigator|location|history)\s*\./.test(line)) kinds.push('browser');
159
- return kinds;
160
- }
161
-
162
157
  function lightweightMutationKinds(line) {
163
158
  const kinds = [];
164
159
  if (/\bdelete\s+[A-Za-z_$][\w$.[\]]*/.test(line)) kinds.push('delete');
@@ -199,24 +194,11 @@ function blockBraceDelta(line) {
199
194
  return delta;
200
195
  }
201
196
 
202
- function hasGlobalNetworkCall(line) {
203
- return hasBareCall(line, ['fetch', 'XMLHttpRequest', 'WebSocket', 'EventSource'])
204
- || hasGlobalPropertyCall(line, ['fetch', 'XMLHttpRequest', 'WebSocket', 'EventSource']);
205
- }
206
-
207
- function hasBareCall(line, names) {
208
- return names.some((name) => new RegExp(`(?:^|[^\\w$.])${name}\\s*\\(`).test(line));
209
- }
210
-
211
- function hasGlobalPropertyCall(line, names) {
212
- return names.some((name) => new RegExp(`\\b(?:window|globalThis|self)\\s*\\.\\s*${name}\\s*\\(`).test(line));
213
- }
214
-
215
197
  function hasRuntimeAssignment(line) {
216
198
  const text = String(line ?? '');
217
199
  for (let index = 0; index < text.length; index += 1) {
218
200
  if (text[index] !== '=' || !isPlainAssignmentOperator(text, index)) continue;
219
- if (!isLocalDeclarationInitializer(text, index)) return true;
201
+ if (!isLocalDeclarationInitializer(text, index) && !isJsxAttributeInitializer(text, index)) return true;
220
202
  }
221
203
  return false;
222
204
  }
@@ -237,6 +219,8 @@ function isLocalDeclarationInitializer(text, index) {
237
219
  || /^(?:export\s+)?type\s+[A-Za-z_$][\w$]*(?:\s*<[^>]+>)?\s*$/.test(statement);
238
220
  }
239
221
 
222
+ function isJsxAttributeInitializer(text, index) { const before = text.slice(0, index); const open = before.lastIndexOf('<'); return open >= 0 && before.lastIndexOf('>') < open && /<[A-Za-z][^<>{}]*\s[A-Za-z_$][\w:.-]*\s*$/.test(before.slice(open)); }
223
+
240
224
  function addFactRecord(input, documentId, declaration, predicate, factKind, lineNumber, records) {
241
225
  const key = `${declaration.symbolId}|${predicate}|${factKind}|${lineNumber}`;
242
226
  if (records.seen.has(key)) return;
@@ -280,7 +264,9 @@ function addDependencyRecord(input, documentId, caller, target, occurrence, reco
280
264
  confidence: 'lexical-reference',
281
265
  sourceDocumentId: documentId,
282
266
  sourceName: caller.name,
283
- targetName: target.name
267
+ targetName: target.name,
268
+ occurrenceId,
269
+ sourceSpan: span
284
270
  }
285
271
  });
286
272
  records.occurrences.push({
@@ -306,5 +292,6 @@ function addDependencyRecord(input, documentId, caller, target, occurrence, reco
306
292
  }
307
293
 
308
294
  function isCallReference(line, afterIdentifierIndex) {
309
- return /^\s*\(/.test(String(line ?? '').slice(afterIdentifierIndex));
295
+ const rest = String(line ?? '').slice(afterIdentifierIndex);
296
+ return /^\s*(?:\(|\?\.\s*\(|\?\.\s*(?:[A-Za-z_$][\w$]*|\[[^\]]+\])(?:\s*(?:\.|\?\.)\s*(?:[A-Za-z_$][\w$]*|\[[^\]]+\]))*\s*(?:\?\.)?\s*\()/.test(rest);
310
297
  }
@@ -8,7 +8,7 @@ function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fi
8
8
  languageKind: `${input.language}.${languageKind}`,
9
9
  name,
10
10
  symbolKind,
11
- symbolId: `symbol:${input.language}:${idFragment(name)}`,
11
+ symbolId: options.symbolId ?? `symbol:${input.language}:${idFragment(name)}`,
12
12
  span: options.span ?? spanForLine(input, lineNumber),
13
13
  fields,
14
14
  metadata: { scan: 'lightweight-declaration', hasBody, ...options.metadata },
@@ -18,6 +18,22 @@ function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fi
18
18
  };
19
19
  }
20
20
 
21
+ function nativeSignatureDeclaration(input, lineNumber, languageKind, symbolKind, name, fields = {}, _hasBody = false, options = {}) {
22
+ const signatureParts = [name, fields.signature, fields.returnType, fields.valueType, ...(fields.parameters ?? [])];
23
+ const signatureKey = idFragment(signatureParts.filter(Boolean).join(':') || `${name}:${lineNumber}`);
24
+ const sourcePath = input.sourcePath ?? `${input.language}:memory`;
25
+ const regionKind = options.regionKind ?? 'declaration';
26
+ return nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fields, false, {
27
+ regionKind,
28
+ symbolId: options.symbolId ?? `symbol:${input.language}:signature:${idFragment(name)}:${signatureKey}`,
29
+ metadata: {
30
+ signatureOnly: true,
31
+ ownershipRegionKey: options.ownershipRegionKey ?? `source#${sourcePath}#${regionKind}#${name}#${signatureKey}`,
32
+ ...options.metadata
33
+ }
34
+ });
35
+ }
36
+
21
37
  function nativeImportDeclaration(input, lineNumber, importPath, languageKind, symbolKind, options = {}) {
22
38
  const name = String(options.name ?? importPath);
23
39
  const nodeId = `native_${idFragment(languageKind)}_${lineNumber}_${idFragment(name)}`;
@@ -65,6 +81,20 @@ function nativeImportBindingDeclaration(input, lineNumber, importPath, binding,
65
81
  });
66
82
  }
67
83
 
84
+ function nativeExportDeclaration(input, lineNumber, exportedName, languageKind = 'ExportDeclaration', fields = {}, options = {}) {
85
+ const name = String(options.name ?? exportedName ?? 'default');
86
+ return nativeDeclaration(input, lineNumber, languageKind, options.symbolKind ?? 'export', name, {
87
+ exportedName: String(exportedName ?? name),
88
+ ...fields
89
+ }, false, {
90
+ role: options.role ?? 'export',
91
+ regionKind: options.regionKind ?? 'export',
92
+ span: options.span,
93
+ symbolId: options.symbolId ?? `symbol:${input.language}:export:${idFragment(name)}`,
94
+ metadata: { scan: 'lightweight-export', ...options.metadata }
95
+ });
96
+ }
97
+
68
98
  function nativeMacroLoss(input, lineNumber, source, kind, name = idFragment(source).slice(0, 40)) {
69
99
  const nodeId = `native_${kind}_${lineNumber}_${idFragment(name)}`;
70
100
  return {
@@ -202,9 +232,11 @@ export {
202
232
  jsControlKeyword,
203
233
  lightweightCoverageLosses,
204
234
  nativeDeclaration,
235
+ nativeExportDeclaration,
205
236
  nativeImportDeclaration,
206
237
  nativeImportBindingDeclaration,
207
238
  nativeMacroLoss,
239
+ nativeSignatureDeclaration,
208
240
  sourceLines,
209
241
  splitParameters,
210
242
  splitTypeParameters
@@ -0,0 +1,151 @@
1
+ import { uniqueStrings, upperFirst } from './native-import-utils.js';
2
+ import { nativeDeclaration, nativeImportDeclaration, sourceLines, splitParameters } from './native-region-scanner-core.js';
3
+ import { braceBlockSpan } from './native-region-scanner-spans.js';
4
+
5
+ function scanCSharp(input) {
6
+ const declarations = [];
7
+ const lines = sourceLines(input.sourceText);
8
+ const blockStack = [];
9
+ let braceDepth = 0;
10
+ for (const [index, { line, number }] of lines.entries()) {
11
+ const trimmed = line.trim();
12
+ const lineStartDepth = depthAfterLeadingClosers(trimmed, braceDepth);
13
+ while (blockStack.length && blockStack[blockStack.length - 1].bodyDepth > lineStartDepth) blockStack.pop();
14
+
15
+ let match;
16
+ if ((match = trimmed.match(/^using\s+([A-Za-z_]\w*)\s*=\s*(.+?)\s*;/))) {
17
+ declarations.push(nativeDeclaration(input, number, 'UsingAliasDirective', 'type', match[1], { target: match[2].trim() }, false));
18
+ } else if ((match = trimmed.match(/^using\s+(?:static\s+)?([A-Za-z_][\w.]*)\s*;/))) {
19
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'UsingDirective', 'namespace'));
20
+ } else if ((match = trimmed.match(/^namespace\s+([A-Za-z_][\w.]*)/))) {
21
+ const hasBody = trimmed.includes('{');
22
+ declarations.push(nativeDeclaration(input, number, 'NamespaceDeclaration', 'namespace', match[1], {}, hasBody, spanOptions(input, lines, index, hasBody)));
23
+ if (hasBody) blockStack.push({ kind: 'namespace', name: match[1], bodyDepth: braceDepth + 1 });
24
+ } else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|static|unsafe|new)\s+)*)delegate\s+(.+?)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*;/))) {
25
+ declarations.push(nativeDeclaration(input, number, 'DelegateDeclaration', 'type', match[3], {
26
+ returnType: match[2].trim(),
27
+ parameters: splitParameters(match[4]),
28
+ modifiers: csharpModifiers(match[1])
29
+ }, false));
30
+ } else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|abstract|sealed|static|partial|readonly|ref|unsafe)\s+)*)(class|interface|struct|enum|record(?:\s+(?:class|struct))?)\s+([A-Za-z_]\w*)/))) {
31
+ const owner = nearestType(blockStack);
32
+ const name = owner ? `${owner.name}.${match[3]}` : match[3];
33
+ const hasBody = trimmed.includes('{');
34
+ declarations.push(nativeDeclaration(input, number, csharpDeclarationKind(match[2]), csharpSymbolKind(match[2]), name, {
35
+ csharpKind: match[2].replace(/\s+/g, ' '),
36
+ modifiers: csharpModifiers(match[1]),
37
+ ...(owner ? { owner: owner.name } : {})
38
+ }, hasBody, spanOptions(input, lines, index, hasBody)));
39
+ if (hasBody) blockStack.push({ kind: csharpTypeStackKind(match[2]), name, bodyDepth: braceDepth + 1 });
40
+ } else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|static|virtual|override|async|partial|sealed|abstract|extern|new|unsafe|readonly)\s+)*)(?:[A-Za-z_][\w<>\[\].?,\s]*\??|void)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:=>.*|\{.*|;)?$/))) {
41
+ const parameters = splitParameters(match[3]);
42
+ const owner = nearestType(blockStack);
43
+ const extensionReceiver = csharpExtensionReceiver(parameters);
44
+ const target = csharpMethodTarget(owner, match[1], match[2], extensionReceiver);
45
+ const hasBody = trimmed.includes('{') || trimmed.includes('=>');
46
+ declarations.push(nativeDeclaration(input, number, extensionReceiver ? 'ExtensionMethodDeclaration' : 'MethodDeclaration', target.owner ? 'method' : 'function', target.name, {
47
+ parameters,
48
+ methodName: match[2],
49
+ modifiers: csharpModifiers(match[1]),
50
+ ...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {}),
51
+ ...(extensionReceiver ? { extensionReceiver } : {})
52
+ }, hasBody, {
53
+ ...spanOptions(input, lines, index, trimmed.includes('{')),
54
+ metadata: {
55
+ methodName: match[2],
56
+ modifiers: csharpModifiers(match[1]),
57
+ ...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {}),
58
+ ...(extensionReceiver ? { extensionReceiver } : {})
59
+ }
60
+ }));
61
+ if (trimmed.includes('{')) blockStack.push({ kind: 'method', name: target.name, bodyDepth: braceDepth + 1 });
62
+ } else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|static|virtual|override|abstract|sealed|new|unsafe)\s+)*)event\s+(.+?)\s+([A-Za-z_]\w*)\s*(?:[;{=]|=>)/))) {
63
+ const owner = nearestType(blockStack);
64
+ const name = owner ? `${owner.name}.${match[3]}` : match[3];
65
+ declarations.push(nativeDeclaration(input, number, 'EventDeclaration', 'event', name, {
66
+ eventType: match[2].trim(),
67
+ accessors: csharpAccessors(trimmed),
68
+ modifiers: csharpModifiers(match[1]),
69
+ ...(owner ? { owner: owner.name, eventName: match[3] } : {})
70
+ }, trimmed.includes('{'), spanOptions(input, lines, index, trimmed.includes('{'))));
71
+ } else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|static|virtual|override|abstract|sealed|new|required|readonly|unsafe)\s+)*)([A-Za-z_][\w<>\[\].?,\s]*\??)\s+([A-Za-z_]\w*)\s*(?:\{|=>)/))) {
72
+ const owner = nearestType(blockStack);
73
+ const name = owner ? `${owner.name}.${match[3]}` : match[3];
74
+ declarations.push(nativeDeclaration(input, number, 'PropertyDeclaration', 'property', name, {
75
+ propertyType: match[2].trim(),
76
+ accessors: csharpAccessors(trimmed),
77
+ modifiers: csharpModifiers(match[1]),
78
+ ...(owner ? { owner: owner.name, propertyName: match[3] } : {})
79
+ }, trimmed.includes('{') || trimmed.includes('=>'), spanOptions(input, lines, index, trimmed.includes('{'))));
80
+ }
81
+ braceDepth = Math.max(0, braceDepth + braceDelta(line));
82
+ }
83
+ return declarations;
84
+ }
85
+
86
+ function spanOptions(input, lines, index, hasBraceBody) {
87
+ return hasBraceBody ? { span: braceBlockSpan(input, lines, index) } : {};
88
+ }
89
+
90
+ function depthAfterLeadingClosers(trimmed, depth) {
91
+ const closers = String(trimmed).match(/^}+/)?.[0].length ?? 0;
92
+ return Math.max(0, depth - closers);
93
+ }
94
+
95
+ function nearestType(blockStack) {
96
+ for (let index = blockStack.length - 1; index >= 0; index -= 1) {
97
+ if (blockStack[index].kind === 'method') return undefined;
98
+ if (['class', 'interface', 'struct', 'record'].includes(blockStack[index].kind)) return blockStack[index];
99
+ }
100
+ return undefined;
101
+ }
102
+
103
+ function csharpMethodTarget(owner, modifiers, methodName, extensionReceiver) {
104
+ if (extensionReceiver) return { name: `${extensionReceiver.type}.extension.${methodName}`, owner: extensionReceiver.type, receiverKind: 'extension' };
105
+ if (!owner) return { name: methodName };
106
+ const receiverKind = /\bstatic\b/.test(modifiers) ? 'static' : 'member';
107
+ return { name: receiverKind === 'static' ? `${owner.name}.static.${methodName}` : `${owner.name}.${methodName}`, owner: owner.name, receiverKind };
108
+ }
109
+
110
+ function csharpModifiers(raw) {
111
+ return splitParameters(String(raw ?? '').trim().replace(/\s+/g, ','));
112
+ }
113
+
114
+ function csharpTypeStackKind(kind) {
115
+ const normalized = String(kind).replace(/\s+/g, ' ');
116
+ if (normalized.startsWith('record')) return 'record';
117
+ if (normalized === 'enum') return 'enum';
118
+ if (normalized === 'struct') return 'struct';
119
+ if (normalized === 'interface') return 'interface';
120
+ return 'class';
121
+ }
122
+
123
+ function csharpSymbolKind(kind) {
124
+ const normalized = String(kind).replace(/\s+/g, ' ');
125
+ if (normalized === 'interface') return 'interface';
126
+ if (normalized === 'struct' || normalized === 'enum' || normalized.startsWith('record')) return 'type';
127
+ return 'class';
128
+ }
129
+
130
+ function csharpDeclarationKind(kind) {
131
+ const normalized = String(kind).replace(/\s+/g, ' ');
132
+ if (normalized === 'record struct') return 'RecordStructDeclaration';
133
+ if (normalized === 'record class') return 'RecordClassDeclaration';
134
+ if (normalized === 'record') return 'RecordDeclaration';
135
+ return `${upperFirst(normalized)}Declaration`;
136
+ }
137
+
138
+ function csharpExtensionReceiver(parameters) {
139
+ const match = String(parameters?.[0] ?? '').match(/^this\s+(.+?)\s+([A-Za-z_]\w*)$/);
140
+ return match ? { type: match[1].trim(), name: match[2] } : undefined;
141
+ }
142
+
143
+ function csharpAccessors(source) {
144
+ return uniqueStrings([...String(source ?? '').matchAll(/\b(get|set|init|add|remove)\b/g)].map((match) => match[1]));
145
+ }
146
+
147
+ function braceDelta(source) {
148
+ return [...String(source ?? '')].reduce((delta, char) => delta + (char === '{' ? 1 : char === '}' ? -1 : 0), 0);
149
+ }
150
+
151
+ export { scanCSharp };
@@ -0,0 +1,91 @@
1
+ import { upperFirst } from './native-import-utils.js';
2
+ import { nativeDeclaration, nativeImportDeclaration, sourceLines, splitParameters } from './native-region-scanner-core.js';
3
+ import { braceBlockSpan } from './native-region-scanner-spans.js';
4
+
5
+ function scanDart(input) {
6
+ const declarations = [];
7
+ const lines = sourceLines(input.sourceText);
8
+ const blockStack = [];
9
+ let braceDepth = 0;
10
+ for (const [index, { line, number }] of lines.entries()) {
11
+ const trimmed = line.trim();
12
+ const lineStartDepth = depthAfterLeadingClosers(trimmed, braceDepth);
13
+ while (blockStack.length && blockStack[blockStack.length - 1].bodyDepth > lineStartDepth) blockStack.pop();
14
+
15
+ let match;
16
+ if ((match = trimmed.match(/^(?:import|export)\s+['"]([^'"]+)['"]/))) {
17
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'UriBasedDirective', 'library'));
18
+ } else if ((match = trimmed.match(/^part\s+['"]([^'"]+)['"]/))) {
19
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'PartDirective', 'library'));
20
+ } else if ((match = trimmed.match(/^(?:(?:abstract|base|final|interface|sealed)\s+)*(class|mixin|enum)\s+([A-Za-z_]\w*)/))) {
21
+ const hasBody = trimmed.includes('{');
22
+ declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Declaration`, dartSymbolKind(match[1]), match[2], {}, hasBody, spanOptions(input, lines, index, hasBody)));
23
+ if (hasBody) blockStack.push({ kind: match[1], name: match[2], bodyDepth: braceDepth + 1 });
24
+ } else if ((match = trimmed.match(/^extension(?:\s+([A-Za-z_]\w*))?\s+on\s+([A-Za-z_]\w*(?:<[^>]+>)?)\s*\{/))) {
25
+ const name = match[1] ? `${match[1]}.extension` : `${match[2]}.extension`;
26
+ declarations.push(nativeDeclaration(input, number, 'ExtensionDeclaration', 'implementation', name, {
27
+ receiverType: match[2],
28
+ ...(match[1] ? { extensionName: match[1] } : {})
29
+ }, true, spanOptions(input, lines, index, true)));
30
+ blockStack.push({ kind: 'extension', name, receiverType: match[2], bodyDepth: braceDepth + 1 });
31
+ } else if ((match = trimmed.match(/^typedef\s+([A-Za-z_]\w*)\b/))) {
32
+ declarations.push(nativeDeclaration(input, number, 'TypeAlias', 'type', match[1], {}, false));
33
+ } else if ((match = trimmed.match(/^((?:(?:external|static)\s+)*)(?:[A-Za-z_]\w*(?:<[^>]+>)?\??|void)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:async\s*)?(?:\{|=>|;)/))) {
34
+ const owner = nearestContainer(blockStack);
35
+ const target = dartFunctionTarget(owner, match[1], match[2]);
36
+ const hasBraceBody = trimmed.includes('{');
37
+ declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', target.owner ? 'method' : 'function', target.name, {
38
+ parameters: splitParameters(match[3]),
39
+ ...(target.owner ? { owner: target.owner, methodName: match[2], receiverKind: target.receiverKind } : {}),
40
+ ...(owner?.receiverType ? { receiverType: owner.receiverType } : {})
41
+ }, hasBraceBody || trimmed.includes('=>'), {
42
+ ...spanOptions(input, lines, index, hasBraceBody),
43
+ metadata: {
44
+ ...(target.owner ? { owner: target.owner, methodName: match[2], receiverKind: target.receiverKind } : {}),
45
+ ...(owner?.receiverType ? { receiverType: owner.receiverType } : {})
46
+ }
47
+ }));
48
+ if (hasBraceBody) blockStack.push({ kind: 'function', name: target.name, bodyDepth: braceDepth + 1 });
49
+ } else if ((match = trimmed.match(/^(?:(?:static|external|late)\s+)*(?:const|final|var)\s+(?:[A-Za-z_]\w*(?:<[^>]+>)?\??\s+)?([A-Za-z_]\w*)\b/))) {
50
+ declarations.push(nativeDeclaration(input, number, 'VariableDeclaration', 'variable', match[1], {}, false));
51
+ }
52
+ braceDepth = Math.max(0, braceDepth + braceDelta(line));
53
+ }
54
+ return declarations;
55
+ }
56
+
57
+ function spanOptions(input, lines, index, hasBraceBody) {
58
+ return hasBraceBody ? { span: braceBlockSpan(input, lines, index) } : {};
59
+ }
60
+
61
+ function depthAfterLeadingClosers(trimmed, depth) {
62
+ const closers = String(trimmed).match(/^}+/)?.[0].length ?? 0;
63
+ return Math.max(0, depth - closers);
64
+ }
65
+
66
+ function nearestContainer(blockStack) {
67
+ for (let index = blockStack.length - 1; index >= 0; index -= 1) {
68
+ if (blockStack[index].kind === 'function') return undefined;
69
+ if (['class', 'mixin', 'enum', 'extension'].includes(blockStack[index].kind)) return blockStack[index];
70
+ }
71
+ return undefined;
72
+ }
73
+
74
+ function dartFunctionTarget(owner, modifiers, methodName) {
75
+ if (!owner) return { name: methodName };
76
+ if (owner.kind === 'extension') return { name: `${owner.name}.${methodName}`, owner: owner.name, receiverKind: 'extension' };
77
+ const receiverKind = /\bstatic\b/.test(modifiers) ? 'static' : 'member';
78
+ return { name: receiverKind === 'static' ? `${owner.name}.static.${methodName}` : `${owner.name}.${methodName}`, owner: owner.name, receiverKind };
79
+ }
80
+
81
+ function braceDelta(source) {
82
+ return [...String(source ?? '')].reduce((delta, char) => delta + (char === '{' ? 1 : char === '}' ? -1 : 0), 0);
83
+ }
84
+
85
+ function dartSymbolKind(kind) {
86
+ if (kind === 'mixin') return 'trait';
87
+ if (kind === 'enum') return 'type';
88
+ return 'class';
89
+ }
90
+
91
+ export { scanDart };
@@ -4,125 +4,22 @@ import {
4
4
  nativeImportDeclaration,
5
5
  nativeMacroLoss,
6
6
  sourceLines,
7
- splitParameters,
8
- splitTypeParameters
7
+ splitParameters
9
8
  } from './native-region-scanner-core.js';
10
-
11
- function scanPhp(input) {
12
- const declarations = [];
13
- for (const { line, number } of sourceLines(input.sourceText)) {
14
- const trimmed = line.trim().replace(/^<\?php\s*/, '');
15
- let match;
16
- if ((match = trimmed.match(/^namespace\s+([A-Za-z_][\w\\]*)\s*;/))) {
17
- declarations.push(nativeDeclaration(input, number, 'NamespaceDefinition', 'namespace', match[1], {}, false));
18
- } else if ((match = trimmed.match(/^use\s+([A-Za-z_][\w\\]*)(?:\s+as\s+([A-Za-z_]\w*))?\s*;/))) {
19
- declarations.push(nativeImportDeclaration(input, number, match[1], 'UseDeclaration', 'namespace'));
20
- } else if ((match = trimmed.match(/^(?:(?:abstract|final|readonly)\s+)*(class|interface|trait|enum)\s+([A-Za-z_]\w*)/))) {
21
- declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Declaration`, phpSymbolKind(match[1]), match[2], {}, trimmed.includes('{')));
22
- } else if ((match = trimmed.match(/^(?:(?:public|protected|private|static|final|abstract)\s+)*function\s+&?\s*([A-Za-z_]\w*)\s*\(([^)]*)\)/))) {
23
- declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
24
- }
25
- }
26
- return declarations;
27
- }
28
-
29
- function scanRuby(input) {
30
- const declarations = [];
31
- for (const { line, number } of sourceLines(input.sourceText)) {
32
- const trimmed = line.trim();
33
- let match;
34
- if ((match = trimmed.match(/^(?:require|load)\s+['"]([^'"]+)['"]/))) {
35
- declarations.push(nativeImportDeclaration(input, number, match[1], 'Require', 'module'));
36
- } else if ((match = trimmed.match(/^module\s+([A-Za-z_]\w*(?:::[A-Za-z_]\w*)*)/))) {
37
- declarations.push(nativeDeclaration(input, number, 'Module', 'module', match[1], {}, true));
38
- } else if ((match = trimmed.match(/^class\s+([A-Za-z_]\w*(?:::[A-Za-z_]\w*)*)/))) {
39
- declarations.push(nativeDeclaration(input, number, 'Class', 'class', match[1], {}, true));
40
- } else if ((match = trimmed.match(/^def\s+(?:self\.)?([A-Za-z_]\w*[!?=]?)\s*(?:\(([^)]*)\)|([^#=]*))?/))) {
41
- declarations.push(nativeDeclaration(input, number, 'Def', 'method', match[1], { parameters: splitParameters(match[2] ?? match[3]) }, true));
42
- }
43
- }
44
- return declarations;
45
- }
46
-
47
- function scanKotlin(input) {
48
- const declarations = [];
49
- for (const { line, number } of sourceLines(input.sourceText)) {
50
- const trimmed = line.trim();
51
- let match;
52
- if ((match = trimmed.match(/^package\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/))) {
53
- declarations.push(nativeDeclaration(input, number, 'PackageHeader', 'package', match[1], {}, false));
54
- } else if ((match = trimmed.match(/^import\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*(?:\.\*)?)(?:\s+as\s+[A-Za-z_]\w*)?$/))) {
55
- declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDirective', 'package'));
56
- } else if ((match = trimmed.match(/^(?:(?:public|private|protected|internal|expect|actual|open|final|abstract|sealed|data|value)\s+)*(?:(enum|annotation)\s+)?(class|interface|object)\s+([A-Za-z_]\w*)/))) {
57
- declarations.push(nativeDeclaration(input, number, kotlinDeclarationKind(match[2], match[1]), kotlinSymbolKind(match[2], match[1]), match[3], {}, trimmed.includes('{')));
58
- } else if ((match = trimmed.match(/^(?:(?:public|private|protected|internal|expect|actual|open|final|abstract|inline|tailrec|operator|infix|external|suspend|override)\s+)*fun\s+(?:<[^>]+>\s*)?(?:[A-Za-z_][\w.<>?]*\.)?([A-Za-z_]\w*)\s*\(([^)]*)\)/))) {
59
- declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{') || trimmed.includes('=')));
60
- } else if ((match = trimmed.match(/^(?:(?:public|private|protected|internal|expect|actual)\s+)*typealias\s+([A-Za-z_]\w*)\s*=/))) {
61
- declarations.push(nativeDeclaration(input, number, 'TypeAliasDeclaration', 'type', match[1], {}, false));
62
- } else if ((match = trimmed.match(/^(?:(?:public|private|protected|internal|expect|actual|open|final|abstract|override|const|lateinit)\s+)*(?:val|var)\s+([A-Za-z_]\w*)\b/))) {
63
- declarations.push(nativeDeclaration(input, number, 'PropertyDeclaration', 'variable', match[1], {}, false));
64
- }
65
- }
66
- return declarations;
67
- }
68
-
69
- function scanScala(input) {
70
- const declarations = [];
71
- for (const { line, number } of sourceLines(input.sourceText)) {
72
- const trimmed = line.trim();
73
- let match;
74
- if ((match = trimmed.match(/^package\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/))) {
75
- declarations.push(nativeDeclaration(input, number, 'PackageClause', 'package', match[1], {}, false));
76
- } else if ((match = trimmed.match(/^import\s+(.+?);?$/))) {
77
- declarations.push(nativeImportDeclaration(input, number, match[1].trim(), 'Import', 'package'));
78
- } else if ((match = trimmed.match(/^(?:(?:private|protected|final|sealed|abstract|case|implicit|lazy|override|inline|transparent|open)\s+)*(class|trait|object|enum)\s+([A-Za-z_]\w*)/))) {
79
- declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Def`, scalaSymbolKind(match[1]), match[2], {}, trimmed.includes('{') || trimmed.includes(':')));
80
- } else if ((match = trimmed.match(/^(?:(?:private|protected|final|implicit|override|inline)\s+)*def\s+([A-Za-z_]\w*)\s*(?:\[[^\]]+\])?\s*\(([^)]*)\)/))) {
81
- declarations.push(nativeDeclaration(input, number, 'DefDef', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{') || trimmed.includes('=')));
82
- } else if ((match = trimmed.match(/^(?:(?:private|protected|final|implicit|opaque)\s+)*type\s+([A-Za-z_]\w*)\b/))) {
83
- declarations.push(nativeDeclaration(input, number, 'TypeDef', 'type', match[1], {}, false));
84
- } else if ((match = trimmed.match(/^(?:(?:private|protected|final|implicit|lazy|override|inline)\s+)*(?:val|var)\s+([A-Za-z_]\w*)\b/))) {
85
- declarations.push(nativeDeclaration(input, number, 'ValDef', 'variable', match[1], {}, false));
86
- }
87
- }
88
- return declarations;
89
- }
90
-
91
- function scanDart(input) {
92
- const declarations = [];
93
- for (const { line, number } of sourceLines(input.sourceText)) {
94
- const trimmed = line.trim();
95
- let match;
96
- if ((match = trimmed.match(/^(?:import|export)\s+['"]([^'"]+)['"]/))) {
97
- declarations.push(nativeImportDeclaration(input, number, match[1], 'UriBasedDirective', 'library'));
98
- } else if ((match = trimmed.match(/^part\s+['"]([^'"]+)['"]/))) {
99
- declarations.push(nativeImportDeclaration(input, number, match[1], 'PartDirective', 'library'));
100
- } else if ((match = trimmed.match(/^(?:(?:abstract|base|final|interface|sealed)\s+)*(class|mixin|enum)\s+([A-Za-z_]\w*)/))) {
101
- declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Declaration`, dartSymbolKind(match[1]), match[2], {}, trimmed.includes('{')));
102
- } else if ((match = trimmed.match(/^extension\s+([A-Za-z_]\w*)\s+on\s+.+\{/))) {
103
- declarations.push(nativeDeclaration(input, number, 'ExtensionDeclaration', 'implementation', match[1], {}, true));
104
- } else if ((match = trimmed.match(/^typedef\s+([A-Za-z_]\w*)\b/))) {
105
- declarations.push(nativeDeclaration(input, number, 'TypeAlias', 'type', match[1], {}, false));
106
- } else if ((match = trimmed.match(/^(?:(?:external|static)\s+)*(?:[A-Za-z_]\w*(?:<[^>]+>)?\??|void)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:async\s*)?(?:\{|=>|;)/))) {
107
- declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{') || trimmed.includes('=>')));
108
- } else if ((match = trimmed.match(/^(?:(?:static|external|late)\s+)*(?:const|final|var)\s+(?:[A-Za-z_]\w*(?:<[^>]+>)?\??\s+)?([A-Za-z_]\w*)\b/))) {
109
- declarations.push(nativeDeclaration(input, number, 'VariableDeclaration', 'variable', match[1], {}, false));
110
- }
111
- }
112
- return declarations;
113
- }
9
+ import { braceBlockSpan, endKeywordBlockSpan, sqlStatementSpan } from './native-region-scanner-spans.js';
114
10
 
115
11
  function scanLua(input) {
116
12
  const declarations = [];
117
- for (const { line, number } of sourceLines(input.sourceText)) {
13
+ const lines = sourceLines(input.sourceText);
14
+ for (const [index, { line, number }] of lines.entries()) {
118
15
  const trimmed = line.trim();
119
16
  let match;
120
17
  if ((match = trimmed.match(/^(?:local\s+[A-Za-z_]\w*\s*=\s*)?require\s*\(?\s*['"]([^'"]+)['"]\s*\)?/))) {
121
18
  declarations.push(nativeImportDeclaration(input, number, match[1], 'RequireCall', 'module'));
122
19
  } else if ((match = trimmed.match(/^(?:local\s+)?function\s+([A-Za-z_]\w*(?:[.:][A-Za-z_]\w*)*)\s*\(([^)]*)\)/))) {
123
- declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
20
+ declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, true, endSpanOptions(input, lines, index)));
124
21
  } else if ((match = trimmed.match(/^(?:local\s+)?([A-Za-z_]\w*(?:[.:][A-Za-z_]\w*)*)\s*=\s*function\s*\(([^)]*)\)/))) {
125
- declarations.push(nativeDeclaration(input, number, 'FunctionAssignment', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
22
+ declarations.push(nativeDeclaration(input, number, 'FunctionAssignment', 'function', match[1], { parameters: splitParameters(match[2]) }, true, endSpanOptions(input, lines, index)));
126
23
  } else if ((match = trimmed.match(/^local\s+([A-Za-z_]\w*)\s*=\s*(?:\{|\w+)/))) {
127
24
  declarations.push(nativeDeclaration(input, number, 'LocalDeclaration', 'variable', match[1], {}, false));
128
25
  }
@@ -132,15 +29,16 @@ function scanLua(input) {
132
29
 
133
30
  function scanShell(input) {
134
31
  const declarations = [];
135
- for (const { line, number } of sourceLines(input.sourceText)) {
32
+ const lines = sourceLines(input.sourceText);
33
+ for (const [index, { line, number }] of lines.entries()) {
136
34
  const trimmed = line.trim();
137
35
  let match;
138
36
  if ((match = trimmed.match(/^(?:source|\.)\s+(?:"([^"]+)"|'([^']+)'|([./A-Za-z0-9_-][\w./-]*))(?:\s|$)/))) {
139
37
  declarations.push(nativeImportDeclaration(input, number, match[1] ?? match[2] ?? match[3], 'SourceCommand', 'file'));
140
38
  } else if ((match = trimmed.match(/^function\s+([A-Za-z_][\w-]*)\s*(?:\(\s*\))?\s*(?:\{|$)/))) {
141
- declarations.push(nativeDeclaration(input, number, 'FunctionDefinition', 'function', match[1], {}, true));
39
+ declarations.push(nativeDeclaration(input, number, 'FunctionDefinition', 'function', match[1], {}, true, spanOptions(input, lines, index, trimmed.includes('{'))));
142
40
  } else if ((match = trimmed.match(/^([A-Za-z_][\w-]*)\s*\(\s*\)\s*(?:\{|$)/))) {
143
- declarations.push(nativeDeclaration(input, number, 'FunctionDefinition', 'function', match[1], {}, true));
41
+ declarations.push(nativeDeclaration(input, number, 'FunctionDefinition', 'function', match[1], {}, true, spanOptions(input, lines, index, trimmed.includes('{'))));
144
42
  } else if ((match = trimmed.match(/^(?:export\s+)?(?:readonly\s+)?([A-Za-z_]\w*)=/))) {
145
43
  declarations.push(nativeDeclaration(input, number, 'VariableAssignment', 'variable', match[1], {}, false));
146
44
  } else if ((match = trimmed.match(/^alias\s+([A-Za-z_][\w-]*)=/))) {
@@ -152,14 +50,15 @@ function scanShell(input) {
152
50
 
153
51
  function scanSql(input) {
154
52
  const declarations = [];
155
- for (const { line, number } of sourceLines(input.sourceText)) {
53
+ const lines = sourceLines(input.sourceText);
54
+ for (const [index, { line, number }] of lines.entries()) {
156
55
  const trimmed = line.trim();
157
56
  let match;
158
57
  if ((match = trimmed.match(/^CREATE\s+EXTENSION\s+(?:IF\s+NOT\s+EXISTS\s+)?((?:"[^"]+"|`[^`]+`|\[[^\]]+\]|[A-Za-z_][\w$-]*))/i))) {
159
58
  declarations.push(nativeImportDeclaration(input, number, normalizeSqlIdentifier(match[1]), 'CreateExtensionStatement', 'extension'));
160
59
  } else if ((match = trimmed.match(/^CREATE\s+(?:OR\s+REPLACE\s+)?(?:TEMP(?:ORARY)?\s+)?((?:UNIQUE\s+)?INDEX|MATERIALIZED\s+VIEW|TABLE|VIEW|FUNCTION|PROCEDURE|TRIGGER|SCHEMA|TYPE)\s+(?:IF\s+NOT\s+EXISTS\s+)?((?:"[^"]+"|`[^`]+`|\[[^\]]+\]|[A-Za-z_][\w$]*)(?:\s*\.\s*(?:"[^"]+"|`[^`]+`|\[[^\]]+\]|[A-Za-z_][\w$]*))?)/i))) {
161
60
  const objectKind = match[1].toUpperCase().replace(/\s+/g, ' ');
162
- declarations.push(nativeDeclaration(input, number, sqlLanguageKind(objectKind), sqlSymbolKind(objectKind), normalizeSqlIdentifier(match[2]), { objectKind }, trimmed.includes('(')));
61
+ declarations.push(nativeDeclaration(input, number, sqlLanguageKind(objectKind), sqlSymbolKind(objectKind), normalizeSqlIdentifier(match[2]), { objectKind }, trimmed.includes('('), { span: sqlStatementSpan(input, lines, index) }));
163
62
  }
164
63
  }
165
64
  return declarations;
@@ -167,7 +66,8 @@ function scanSql(input) {
167
66
 
168
67
  function scanZig(input) {
169
68
  const declarations = [];
170
- for (const { line, number } of sourceLines(input.sourceText)) {
69
+ const lines = sourceLines(input.sourceText);
70
+ for (const [index, { line, number }] of lines.entries()) {
171
71
  const trimmed = line.trim();
172
72
  let match;
173
73
  if ((match = trimmed.match(/^(?:(?:pub|export)\s+)?(?:const|var)\s+([A-Za-z_]\w*)\s*=\s*@import\(\s*["']([^"']+)["']\s*\)\s*;?/))) {
@@ -175,9 +75,9 @@ function scanZig(input) {
175
75
  } else if ((match = trimmed.match(/^(?:(?:pub|export)\s+)?usingnamespace\s+@import\(\s*["']([^"']+)["']\s*\)\s*;?/))) {
176
76
  declarations.push(nativeImportDeclaration(input, number, match[1], 'UsingNamespaceImport', 'module'));
177
77
  } else if ((match = trimmed.match(/^(?:(?:pub|export)\s+)?const\s+([A-Za-z_]\w*)\s*=\s*(?:extern\s+)?(struct|enum|union|opaque|error)\b/))) {
178
- declarations.push(nativeDeclaration(input, number, `Const${upperFirst(match[2])}Declaration`, 'type', match[1], { zigKind: match[2] }, trimmed.includes('{')));
78
+ declarations.push(nativeDeclaration(input, number, `Const${upperFirst(match[2])}Declaration`, 'type', match[1], { zigKind: match[2] }, trimmed.includes('{'), spanOptions(input, lines, index, trimmed.includes('{'))));
179
79
  } else if ((match = trimmed.match(/^(?:(?:pub|export|extern|inline)\s+)*(?:fn)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)/))) {
180
- declarations.push(nativeDeclaration(input, number, 'FnDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
80
+ declarations.push(nativeDeclaration(input, number, 'FnDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{'), spanOptions(input, lines, index, trimmed.includes('{'))));
181
81
  } else if ((match = trimmed.match(/^(?:(?:pub|export)\s+)?const\s+([A-Za-z_]\w*)\b/))) {
182
82
  declarations.push(nativeDeclaration(input, number, 'ConstDeclaration', 'constant', match[1], {}, false));
183
83
  } else if ((match = trimmed.match(/^(?:(?:pub|export)\s+)?var\s+([A-Za-z_]\w*)\b/))) {
@@ -190,37 +90,12 @@ function scanZig(input) {
190
90
  return declarations;
191
91
  }
192
92
 
193
- function phpSymbolKind(kind) {
194
- if (kind === 'interface') return 'interface';
195
- if (kind === 'trait') return 'trait';
196
- if (kind === 'enum') return 'type';
197
- return 'class';
198
- }
199
-
200
- function kotlinDeclarationKind(kind, prefix) {
201
- if (prefix === 'enum') return 'EnumClassDeclaration';
202
- if (prefix === 'annotation') return 'AnnotationClassDeclaration';
203
- return `${upperFirst(kind)}Declaration`;
204
- }
205
-
206
- function kotlinSymbolKind(kind, prefix) {
207
- if (kind === 'interface') return 'interface';
208
- if (kind === 'object') return 'module';
209
- if (prefix === 'enum' || prefix === 'annotation') return 'type';
210
- return 'class';
211
- }
212
-
213
- function scalaSymbolKind(kind) {
214
- if (kind === 'trait') return 'trait';
215
- if (kind === 'object') return 'module';
216
- if (kind === 'enum') return 'type';
217
- return 'class';
93
+ function spanOptions(input, lines, index, hasBraceBody) {
94
+ return hasBraceBody ? { span: braceBlockSpan(input, lines, index) } : {};
218
95
  }
219
96
 
220
- function dartSymbolKind(kind) {
221
- if (kind === 'mixin') return 'trait';
222
- if (kind === 'enum') return 'type';
223
- return 'class';
97
+ function endSpanOptions(input, lines, index) {
98
+ return { span: endKeywordBlockSpan(input, lines, index) };
224
99
  }
225
100
 
226
101
  function sqlSymbolKind(kind) {
@@ -247,12 +122,7 @@ function zigMetaName(source) {
247
122
  }
248
123
 
249
124
  export {
250
- scanDart,
251
- scanKotlin,
252
125
  scanLua,
253
- scanPhp,
254
- scanRuby,
255
- scanScala,
256
126
  scanShell,
257
127
  scanSql,
258
128
  scanZig