@shapeshift-labs/frontier-lang-compiler 0.2.102 → 0.2.104

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 (134) hide show
  1. package/README.md +13 -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 +58 -0
  5. package/dist/declarations/js-ts-safe-merge.d.ts +120 -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/native-project-admission-semantic-evidence.d.ts +34 -0
  11. package/dist/declarations/native-project-admission.d.ts +6 -10
  12. package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +12 -0
  13. package/dist/declarations/semantic-edit-script.d.ts +10 -4
  14. package/dist/declarations/semantic-patch-bundle-index.d.ts +45 -0
  15. package/dist/declarations/semantic-patch-bundle-overlaps.d.ts +1 -0
  16. package/dist/declarations/semantic-patch-bundle.d.ts +6 -4
  17. package/dist/declarations/semantic-sidecar-example.d.ts +18 -0
  18. package/dist/declarations/semantic-transform-identity.d.ts +3 -0
  19. package/dist/declarations/source-preservation.d.ts +72 -0
  20. package/dist/declarations/universal-capability.d.ts +4 -0
  21. package/dist/declarations/universal-conversion-artifacts.d.ts +61 -1
  22. package/dist/declarations/universal-conversion-compact-counts.d.ts +51 -0
  23. package/dist/declarations/universal-conversion-plan.d.ts +6 -1
  24. package/dist/declarations/universal-representation-coverage.d.ts +90 -0
  25. package/dist/index.d.ts +4 -0
  26. package/dist/index.js +3 -0
  27. package/dist/internal/index-impl/bidirectionalExactSourceBackprojection.js +199 -0
  28. package/dist/internal/index-impl/bidirectionalSameLanguageSourceProjection.js +112 -0
  29. package/dist/internal/index-impl/bidirectionalSourceEditProjection.js +319 -0
  30. package/dist/internal/index-impl/bidirectionalSourceEditProjectionArtifacts.js +67 -0
  31. package/dist/internal/index-impl/bidirectionalTargetChangeRecordInternals.js +17 -5
  32. package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +58 -20
  33. package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +60 -7
  34. package/dist/internal/index-impl/createLightweightNativeImport.js +1 -0
  35. package/dist/internal/index-impl/createNativeSourcePreservation.js +28 -2
  36. package/dist/internal/index-impl/createProjectImportAdmissionRecord.js +14 -2
  37. package/dist/internal/index-impl/diffNativeSymbols.js +82 -1
  38. package/dist/internal/index-impl/nativeChangeProjectionSourceMapLinks.js +2 -0
  39. package/dist/internal/index-impl/projectImportAdmissionImportEvidence.js +1 -1
  40. package/dist/internal/index-impl/projectImportAdmissionSemanticWarnings.js +178 -0
  41. package/dist/internal/index-impl/projectImportAdmissionSummaries.js +22 -3
  42. package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +54 -69
  43. package/dist/internal/index-impl/replaySemanticEditLineEndings.js +34 -0
  44. package/dist/internal/index-impl/replaySemanticEditProjection.js +78 -78
  45. package/dist/internal/index-impl/semanticEditBundleAdmission.js +7 -3
  46. package/dist/internal/index-impl/semanticEditBundleIndex.js +47 -1
  47. package/dist/internal/index-impl/semanticEditExplicitSourceReplacement.js +40 -0
  48. package/dist/internal/index-impl/semanticEditImportProjection.js +53 -0
  49. package/dist/internal/index-impl/semanticEditOperationCoverage.js +33 -3
  50. package/dist/internal/index-impl/semanticEditProjectionRecord.js +108 -0
  51. package/dist/internal/index-impl/semanticEditReplayAnchors.js +63 -0
  52. package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +39 -0
  53. package/dist/internal/index-impl/semanticEditReplaySourceReplacement.js +85 -0
  54. package/dist/internal/index-impl/semanticEditScripts.js +4 -0
  55. package/dist/internal/index-impl/semanticEditSourceRanges.js +32 -0
  56. package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +1 -0
  57. package/dist/internal/index-impl/semanticPatchBundleAdmission.js +92 -9
  58. package/dist/internal/index-impl/semanticPatchBundleOverlaps.js +33 -16
  59. package/dist/internal/index-impl/semanticPatchBundleRecords.js +16 -0
  60. package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +2 -0
  61. package/dist/internal/index-impl/semanticSidecarQuality.js +111 -0
  62. package/dist/internal/index-impl/semanticSourceEditDedupe.js +69 -9
  63. package/dist/internal/index-impl/semanticTransformIdentityRecords.js +85 -9
  64. package/dist/js-ts-safe-member-merge-result.js +158 -0
  65. package/dist/js-ts-safe-member-merge.js +202 -0
  66. package/dist/js-ts-safe-merge-analyze.js +279 -0
  67. package/dist/js-ts-safe-merge-constants.js +50 -0
  68. package/dist/js-ts-safe-merge-context.js +118 -0
  69. package/dist/js-ts-safe-merge-ledger-validation.js +92 -0
  70. package/dist/js-ts-safe-merge-ledger.js +85 -0
  71. package/dist/js-ts-safe-merge-parse-declarations.js +210 -0
  72. package/dist/js-ts-safe-merge-parse-statements.js +155 -0
  73. package/dist/js-ts-safe-merge-plan.js +190 -0
  74. package/dist/js-ts-safe-merge.js +175 -0
  75. package/dist/js-ts-semantic-conflict-sidecar-constants.js +77 -0
  76. package/dist/js-ts-semantic-conflict-sidecar-detectors.js +195 -0
  77. package/dist/js-ts-semantic-conflict-sidecar-normalize.js +203 -0
  78. package/dist/js-ts-semantic-conflict-sidecar-utils.js +190 -0
  79. package/dist/js-ts-semantic-conflict-sidecars.js +81 -0
  80. package/dist/js-ts-semantic-merge-contract-helpers.js +128 -0
  81. package/dist/js-ts-semantic-merge-contracts.js +217 -0
  82. package/dist/js-ts-semantic-merge-member-containers.js +100 -0
  83. package/dist/js-ts-semantic-merge-member-keys.js +142 -0
  84. package/dist/js-ts-semantic-merge-member-segments.js +185 -0
  85. package/dist/js-ts-semantic-merge-member-source.js +64 -0
  86. package/dist/js-ts-semantic-merge-member-utils.js +18 -0
  87. package/dist/js-ts-semantic-merge-parse.js +15 -0
  88. package/dist/js-ts-semantic-merge.js +21 -0
  89. package/dist/lightweight-dependency-effects.js +51 -0
  90. package/dist/lightweight-dependency-language.js +12 -1
  91. package/dist/lightweight-dependency-relations.js +14 -27
  92. package/dist/native-region-scanner-core.js +33 -1
  93. package/dist/native-region-scanner-csharp.js +151 -0
  94. package/dist/native-region-scanner-dart.js +91 -0
  95. package/dist/native-region-scanner-dynamic.js +21 -151
  96. package/dist/native-region-scanner-functional.js +40 -13
  97. package/dist/native-region-scanner-java.js +97 -0
  98. package/dist/native-region-scanner-js-class.js +100 -0
  99. package/dist/native-region-scanner-js-helpers.js +28 -86
  100. package/dist/native-region-scanner-js-imports.js +121 -1
  101. package/dist/native-region-scanner-js-nested.js +96 -8
  102. package/dist/native-region-scanner-js-structure.js +27 -0
  103. package/dist/native-region-scanner-js-types.js +99 -0
  104. package/dist/native-region-scanner-js.js +70 -118
  105. package/dist/native-region-scanner-kotlin.js +94 -0
  106. package/dist/native-region-scanner-main.js +15 -181
  107. package/dist/native-region-scanner-php.js +80 -0
  108. package/dist/native-region-scanner-python.js +62 -0
  109. package/dist/native-region-scanner-ruby.js +72 -0
  110. package/dist/native-region-scanner-scala.js +91 -0
  111. package/dist/native-region-scanner-spans.js +74 -0
  112. package/dist/native-region-scanner-swift.js +155 -0
  113. package/dist/native-region-scanner.js +14 -10
  114. package/dist/native-source-ledger-helpers.js +195 -0
  115. package/dist/native-source-ledger.js +306 -0
  116. package/dist/native-source-preservation-scanner.js +4 -0
  117. package/dist/semantic-import-callsite-regions.js +136 -0
  118. package/dist/semantic-import-effect-regions.js +283 -0
  119. package/dist/semantic-import-regions.js +11 -2
  120. package/dist/semantic-import-sidecar-entry.js +16 -2
  121. package/dist/semantic-import-sidecar-types.d.ts +2 -0
  122. package/dist/semantic-sidecar-example.js +68 -0
  123. package/dist/universal-capability-matrix.js +23 -0
  124. package/dist/universal-conversion-artifact-query.js +79 -2
  125. package/dist/universal-conversion-artifact-semantic-edit.js +103 -0
  126. package/dist/universal-conversion-artifact-summary.js +33 -1
  127. package/dist/universal-conversion-artifacts.js +13 -48
  128. package/dist/universal-conversion-plan-scoring.js +21 -1
  129. package/dist/universal-conversion-plan-summary.js +30 -0
  130. package/dist/universal-conversion-plan.js +25 -9
  131. package/dist/universal-conversion-route-metadata.js +96 -0
  132. package/dist/universal-conversion-route-operations.js +7 -0
  133. package/dist/universal-representation-coverage.js +193 -0
  134. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { idFragment, uniqueStrings, upperFirst } from './native-import-utils.js';
1
+ import { idFragment } from './native-import-utils.js';
2
2
  import {
3
3
  nativeDeclaration,
4
4
  nativeImportDeclaration,
@@ -7,30 +7,16 @@ import {
7
7
  splitParameters,
8
8
  splitTypeParameters
9
9
  } from './native-region-scanner-core.js';
10
-
11
- function scanPython(input) {
12
- const declarations = [];
13
- for (const { line, number } of sourceLines(input.sourceText)) {
14
- const trimmed = line.trim();
15
- let match;
16
- if ((match = trimmed.match(/^(?:async\s+)?def\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*:/))) {
17
- declarations.push(nativeDeclaration(input, number, 'FunctionDef', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
18
- } else if ((match = trimmed.match(/^class\s+([A-Za-z_]\w*)/))) {
19
- declarations.push(nativeDeclaration(input, number, 'ClassDef', 'class', match[1], {}, true));
20
- } else if ((match = trimmed.match(/^(?:from\s+([A-Za-z_][\w.]*)\s+import\s+.+|import\s+([A-Za-z_][\w.]*))/))) {
21
- declarations.push(nativeImportDeclaration(input, number, match[1] ?? match[2], 'Import', 'module'));
22
- }
23
- }
24
- return declarations;
25
- }
10
+ import { braceBlockSpan } from './native-region-scanner-spans.js';
26
11
 
27
12
  function scanRust(input) {
28
13
  const declarations = [];
29
- for (const { line, number } of sourceLines(input.sourceText)) {
14
+ const lines = sourceLines(input.sourceText);
15
+ for (const [index, { line, number }] of lines.entries()) {
30
16
  const trimmed = line.trim();
31
17
  let match;
32
18
  if ((match = trimmed.match(/^(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?fn\s+([A-Za-z_]\w*)\s*\(([^)]*)\)/))) {
33
- declarations.push(nativeDeclaration(input, number, 'ItemFn', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
19
+ declarations.push(nativeDeclaration(input, number, 'ItemFn', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{'), { span: trimmed.includes('{') ? braceBlockSpan(input, lines, index) : undefined }));
34
20
  } else if ((match = trimmed.match(/^(?:pub(?:\([^)]*\))?\s+)?struct\s+([A-Za-z_]\w*)/))) {
35
21
  declarations.push(nativeDeclaration(input, number, 'ItemStruct', 'type', match[1], {}, trimmed.includes('{')));
36
22
  } else if ((match = trimmed.match(/^(?:pub(?:\([^)]*\))?\s+)?enum\s+([A-Za-z_]\w*)/))) {
@@ -52,7 +38,8 @@ function scanRust(input) {
52
38
 
53
39
  function scanCLike(input) {
54
40
  const declarations = [];
55
- for (const { line, number } of sourceLines(input.sourceText)) {
41
+ const lines = sourceLines(input.sourceText);
42
+ for (const [index, { line, number }] of lines.entries()) {
56
43
  const trimmed = line.trim();
57
44
  let match;
58
45
  if ((match = trimmed.match(/^#\s*include\s+[<"]([^>"]+)[>"]/))) {
@@ -60,30 +47,11 @@ function scanCLike(input) {
60
47
  } else if ((match = trimmed.match(/^#\s*define\s+([A-Za-z_]\w*)/))) {
61
48
  declarations.push(nativeMacroLoss(input, number, trimmed, 'preprocessor', match[1]));
62
49
  } else if ((match = trimmed.match(/^typedef\s+struct(?:\s+([A-Za-z_]\w*))?/))) {
63
- declarations.push(nativeDeclaration(input, number, 'TypedefStructDeclaration', 'type', match[1] ?? `anonymous_struct_${number}`, {}, trimmed.includes('{')));
50
+ declarations.push(nativeDeclaration(input, number, 'TypedefStructDeclaration', 'type', match[1] ?? `anonymous_struct_${number}`, {}, trimmed.includes('{'), { span: trimmed.includes('{') ? braceBlockSpan(input, lines, index) : undefined }));
64
51
  } else if ((match = trimmed.match(/^(?:struct|enum)\s+([A-Za-z_]\w*)/))) {
65
- declarations.push(nativeDeclaration(input, number, 'TagDeclaration', 'type', match[1], {}, trimmed.includes('{')));
52
+ declarations.push(nativeDeclaration(input, number, 'TagDeclaration', 'type', match[1], {}, trimmed.includes('{'), { span: trimmed.includes('{') ? braceBlockSpan(input, lines, index) : undefined }));
66
53
  } else if ((match = trimmed.match(/^(?:[A-Za-z_][\w\s*:&<>]+)\s+([A-Za-z_]\w*)\s*\(([^;{}]*)\)\s*(?:;|\{)?$/))) {
67
- declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.endsWith('{')));
68
- }
69
- }
70
- return declarations;
71
- }
72
-
73
- function scanJava(input) {
74
- const declarations = [];
75
- for (const { line, number } of sourceLines(input.sourceText)) {
76
- const trimmed = line.trim();
77
- let match;
78
- if ((match = trimmed.match(/^package\s+([A-Za-z_][\w.]*);/))) {
79
- declarations.push(nativeDeclaration(input, number, 'PackageDeclaration', 'package', match[1], {}, false));
80
- } else if ((match = trimmed.match(/^import\s+(?:static\s+)?([A-Za-z_][\w.*]*);/))) {
81
- declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDeclaration', 'package'));
82
- } else if ((match = trimmed.match(/^(?:(?:public|protected|private|abstract|final|static|sealed|non-sealed)\s+)*(class|interface|enum|record|@interface)\s+([A-Za-z_$][\w$]*)/))) {
83
- const kind = match[1] === '@interface' ? 'AnnotationDeclaration' : `${upperFirst(match[1])}Declaration`;
84
- declarations.push(nativeDeclaration(input, number, kind, javaSymbolKind(match[1]), match[2], {}, trimmed.includes('{')));
85
- } else if ((match = trimmed.match(/^(?:(?:public|protected|private|abstract|final|static|synchronized|native)\s+)*(?:<[^>]+>\s+)?[A-Za-z_$][\w$<>\[\].?,\s]*\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*(?:throws\s+[^{]+)?(?:\{|;)?$/))) {
86
- declarations.push(nativeDeclaration(input, number, 'MethodDeclaration', 'method', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
54
+ declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.endsWith('{'), { span: trimmed.endsWith('{') ? braceBlockSpan(input, lines, index) : undefined }));
87
55
  }
88
56
  }
89
57
  return declarations;
@@ -92,7 +60,8 @@ function scanJava(input) {
92
60
  function scanGo(input) {
93
61
  const declarations = [];
94
62
  let inImportBlock = false;
95
- for (const { line, number } of sourceLines(input.sourceText)) {
63
+ const lines = sourceLines(input.sourceText);
64
+ for (const [index, { line, number }] of lines.entries()) {
96
65
  const trimmed = line.trim();
97
66
  let match;
98
67
  if (inImportBlock) {
@@ -120,12 +89,12 @@ function scanGo(input) {
120
89
  receiver,
121
90
  typeParameters: splitTypeParameters(match[3]),
122
91
  parameters: splitParameters(match[4])
123
- }, trimmed.includes('{')));
92
+ }, trimmed.includes('{'), { span: trimmed.includes('{') ? braceBlockSpan(input, lines, index) : undefined }));
124
93
  } else if ((match = trimmed.match(/^func\s+([A-Za-z_]\w*)(?:\s*\[([^\]]+)\])?\s*\(([^)]*)\)/))) {
125
94
  declarations.push(nativeDeclaration(input, number, 'FuncDecl', 'function', match[1], {
126
95
  typeParameters: splitTypeParameters(match[2]),
127
96
  parameters: splitParameters(match[3])
128
- }, trimmed.includes('{')));
97
+ }, trimmed.includes('{'), { span: trimmed.includes('{') ? braceBlockSpan(input, lines, index) : undefined }));
129
98
  } else if ((match = trimmed.match(/^var\s+([A-Za-z_]\w*)\b/))) {
130
99
  declarations.push(nativeDeclaration(input, number, 'VarDecl', 'variable', match[1], {}, false));
131
100
  } else if ((match = trimmed.match(/^const\s+([A-Za-z_]\w*)\b/))) {
@@ -135,82 +104,6 @@ function scanGo(input) {
135
104
  return declarations;
136
105
  }
137
106
 
138
- function scanSwift(input) {
139
- const declarations = [];
140
- const protocols = new Set();
141
- for (const { line, number } of sourceLines(input.sourceText)) {
142
- const trimmed = line.trim();
143
- const declarationLine = trimmed.replace(/^(?:@[A-Za-z_][\w.]+(?:\([^)]*\))?\s+)*/, '');
144
- let match;
145
- if ((match = declarationLine.match(/^import\s+(?:(?:struct|class|enum|protocol|func|var)\s+)?([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/))) {
146
- declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDecl', 'module'));
147
- } else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open|final|indirect)\s+)*(struct|class|enum|protocol|actor)\s+([A-Za-z_]\w*)/))) {
148
- if (match[1] === 'protocol') protocols.add(match[2]);
149
- declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Decl`, swiftSymbolKind(match[1]), match[2], {}, declarationLine.includes('{')));
150
- } else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open)\s+)*extension\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)(.*)$/))) {
151
- const extensionFields = parseSwiftExtensionTail(match[2]);
152
- const isProtocolExtension = protocols.has(match[1]) || /Protocol$/.test(match[1]);
153
- declarations.push(nativeDeclaration(input, number, isProtocolExtension ? 'ProtocolExtensionDecl' : 'ExtensionDecl', 'implementation', `${match[1]}.${isProtocolExtension ? 'protocolExtension' : 'extension'}`, {
154
- extendedType: match[1],
155
- ...extensionFields
156
- }, declarationLine.includes('{')));
157
- } else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open|static|class|mutating|nonmutating|override|required|convenience|isolated|nonisolated)\s+)*func\s+([A-Za-z_]\w*|`[^`]+`)(?:\s*<([^>]+)>)?\s*\(([^)]*)\)/))) {
158
- declarations.push(nativeDeclaration(input, number, 'FunctionDecl', 'function', unquoteSwiftIdentifier(match[1]), {
159
- typeParameters: splitTypeParameters(match[2]),
160
- parameters: splitParameters(match[3])
161
- }, declarationLine.includes('{')));
162
- } else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open|static|class|final|lazy|weak|unowned|override|required|nonisolated)\s+)*(let|var)\s+([A-Za-z_]\w*)\b(?::\s*([^={]+))?/))) {
163
- declarations.push(nativeDeclaration(input, number, 'PropertyDecl', 'property', match[2], {
164
- binding: match[1],
165
- valueType: match[3]?.trim()
166
- }, declarationLine.includes('{') || declarationLine.includes('=>')));
167
- } else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open)\s+)*typealias\s+([A-Za-z_]\w*)\b(?:\s*=\s*(.+))?/))) {
168
- declarations.push(nativeDeclaration(input, number, 'TypealiasDecl', 'type', match[1], { target: match[2]?.trim() }, false));
169
- }
170
- }
171
- return declarations;
172
- }
173
-
174
- function scanCSharp(input) {
175
- const declarations = [];
176
- for (const { line, number } of sourceLines(input.sourceText)) {
177
- const trimmed = line.trim();
178
- let match;
179
- if ((match = trimmed.match(/^using\s+([A-Za-z_]\w*)\s*=\s*(.+?)\s*;/))) {
180
- declarations.push(nativeDeclaration(input, number, 'UsingAliasDirective', 'type', match[1], { target: match[2].trim() }, false));
181
- } else if ((match = trimmed.match(/^using\s+(?:static\s+)?([A-Za-z_][\w.]*)\s*;/))) {
182
- declarations.push(nativeImportDeclaration(input, number, match[1], 'UsingDirective', 'namespace'));
183
- } else if ((match = trimmed.match(/^namespace\s+([A-Za-z_][\w.]*)/))) {
184
- declarations.push(nativeDeclaration(input, number, 'NamespaceDeclaration', 'namespace', match[1], {}, trimmed.includes('{')));
185
- } else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|static|unsafe|new)\s+)*delegate\s+(.+?)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*;/))) {
186
- declarations.push(nativeDeclaration(input, number, 'DelegateDeclaration', 'type', match[2], {
187
- returnType: match[1].trim(),
188
- parameters: splitParameters(match[3])
189
- }, false));
190
- } 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*)/))) {
191
- declarations.push(nativeDeclaration(input, number, csharpDeclarationKind(match[1]), csharpSymbolKind(match[1]), match[2], { csharpKind: match[1].replace(/\s+/g, ' ') }, trimmed.includes('{')));
192
- } 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*(?:=>.*|\{|;)?$/))) {
193
- const parameters = splitParameters(match[2]);
194
- const extensionReceiver = csharpExtensionReceiver(parameters);
195
- declarations.push(nativeDeclaration(input, number, extensionReceiver ? 'ExtensionMethodDeclaration' : 'MethodDeclaration', 'method', match[1], {
196
- parameters,
197
- ...(extensionReceiver ? { extensionReceiver } : {})
198
- }, trimmed.includes('{') || trimmed.includes('=>')));
199
- } 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*(?:[;{=]|=>)/))) {
200
- declarations.push(nativeDeclaration(input, number, 'EventDeclaration', 'event', match[2], {
201
- eventType: match[1].trim(),
202
- accessors: csharpAccessors(trimmed)
203
- }, trimmed.includes('{')));
204
- } 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*(?:\{|=>)/))) {
205
- declarations.push(nativeDeclaration(input, number, 'PropertyDeclaration', 'property', match[2], {
206
- propertyType: match[1].trim(),
207
- accessors: csharpAccessors(trimmed)
208
- }, trimmed.includes('{') || trimmed.includes('=>')));
209
- }
210
- }
211
- return declarations;
212
- }
213
-
214
107
  function parseGoReceiver(raw) {
215
108
  const value = String(raw ?? '').trim();
216
109
  const match = value.match(/^(?:(\w+)\s+)?(.+)$/);
@@ -235,67 +128,8 @@ function goReceiverMethodName(receiver, methodName) {
235
128
  return receiver?.type ? `${receiver.type}.${methodName}` : methodName;
236
129
  }
237
130
 
238
- function parseSwiftExtensionTail(rawTail) {
239
- let tail = String(rawTail ?? '').split('{')[0].trim();
240
- const fields = {};
241
- const whereMatch = tail.match(/\bwhere\b(.+)$/);
242
- if (whereMatch) {
243
- fields.constraints = whereMatch[1].trim();
244
- tail = tail.slice(0, whereMatch.index).trim();
245
- }
246
- if (tail.startsWith(':')) {
247
- fields.conformances = tail.slice(1).split(',').map((part) => part.trim()).filter(Boolean);
248
- }
249
- return fields;
250
- }
251
-
252
- function unquoteSwiftIdentifier(identifier) {
253
- return String(identifier).replace(/^`|`$/g, '');
254
- }
255
-
256
- function javaSymbolKind(kind) {
257
- if (kind === 'interface' || kind === '@interface') return 'interface';
258
- if (kind === 'enum' || kind === 'record') return 'type';
259
- return 'class';
260
- }
261
-
262
- function swiftSymbolKind(kind) {
263
- if (kind === 'protocol') return 'protocol';
264
- if (kind === 'extension') return 'implementation';
265
- if (kind === 'struct' || kind === 'enum' || kind === 'actor') return 'type';
266
- return 'class';
267
- }
268
-
269
- function csharpSymbolKind(kind) {
270
- const normalized = String(kind).replace(/\s+/g, ' ');
271
- if (normalized === 'interface') return 'interface';
272
- if (normalized === 'struct' || normalized === 'enum' || normalized.startsWith('record')) return 'type';
273
- return 'class';
274
- }
275
-
276
- function csharpDeclarationKind(kind) {
277
- const normalized = String(kind).replace(/\s+/g, ' ');
278
- if (normalized === 'record struct') return 'RecordStructDeclaration';
279
- if (normalized === 'record class') return 'RecordClassDeclaration';
280
- if (normalized === 'record') return 'RecordDeclaration';
281
- return `${upperFirst(normalized)}Declaration`;
282
- }
283
-
284
- function csharpExtensionReceiver(parameters) {
285
- const match = String(parameters?.[0] ?? '').match(/^this\s+(.+?)\s+([A-Za-z_]\w*)$/);
286
- return match ? { type: match[1].trim(), name: match[2] } : undefined;
287
- }
288
-
289
- function csharpAccessors(source) {
290
- return uniqueStrings([...String(source ?? '').matchAll(/\b(get|set|init|add|remove)\b/g)].map((match) => match[1]));
291
- }
292
-
293
131
  export {
294
132
  scanCLike,
295
- scanCSharp,
296
133
  scanGo,
297
- scanJava,
298
- scanPython,
299
- scanRust,
300
- scanSwift
134
+ scanRust
301
135
  };
@@ -0,0 +1,80 @@
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 scanPhp(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().replace(/^<\?php\s*/, '');
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(/^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
+ const hasBody = trimmed.includes('{');
22
+ declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Declaration`, phpSymbolKind(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(/^((?:(?:public|protected|private|static|final|abstract)\s+)*)function\s+&?\s*([A-Za-z_]\w*)\s*\(([^)]*)\)/))) {
25
+ const owner = nearestContainer(blockStack);
26
+ const target = phpFunctionTarget(owner, match[1], match[2]);
27
+ const hasBody = trimmed.includes('{');
28
+ declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', target.owner ? 'method' : 'function', target.name, {
29
+ parameters: splitParameters(match[3]),
30
+ ...(target.owner ? { owner: target.owner, methodName: match[2], receiverKind: target.receiverKind } : {}),
31
+ ...(match[1].trim() ? { modifiers: splitParameters(match[1].trim().replace(/\s+/g, ',')) } : {})
32
+ }, hasBody, {
33
+ ...spanOptions(input, lines, index, hasBody),
34
+ metadata: {
35
+ ...(target.owner ? { owner: target.owner, methodName: match[2], receiverKind: target.receiverKind } : {}),
36
+ ...(match[1].trim() ? { modifiers: splitParameters(match[1].trim().replace(/\s+/g, ',')) } : {})
37
+ }
38
+ }));
39
+ if (hasBody) blockStack.push({ kind: 'function', name: target.name, bodyDepth: braceDepth + 1 });
40
+ }
41
+ braceDepth = Math.max(0, braceDepth + braceDelta(line));
42
+ }
43
+ return declarations;
44
+ }
45
+
46
+ function spanOptions(input, lines, index, hasBraceBody) {
47
+ return hasBraceBody ? { span: braceBlockSpan(input, lines, index) } : {};
48
+ }
49
+
50
+ function depthAfterLeadingClosers(trimmed, depth) {
51
+ const closers = String(trimmed).match(/^}+/)?.[0].length ?? 0;
52
+ return Math.max(0, depth - closers);
53
+ }
54
+
55
+ function nearestContainer(blockStack) {
56
+ for (let index = blockStack.length - 1; index >= 0; index -= 1) {
57
+ if (blockStack[index].kind === 'function') return undefined;
58
+ if (['class', 'interface', 'trait', 'enum'].includes(blockStack[index].kind)) return blockStack[index];
59
+ }
60
+ return undefined;
61
+ }
62
+
63
+ function phpFunctionTarget(owner, modifiers, methodName) {
64
+ if (!owner) return { name: methodName };
65
+ const receiverKind = /\bstatic\b/.test(modifiers) ? 'static' : 'instance';
66
+ return { name: receiverKind === 'static' ? `${owner.name}.static.${methodName}` : `${owner.name}.${methodName}`, owner: owner.name, receiverKind };
67
+ }
68
+
69
+ function braceDelta(source) {
70
+ return [...String(source ?? '')].reduce((delta, char) => delta + (char === '{' ? 1 : char === '}' ? -1 : 0), 0);
71
+ }
72
+
73
+ function phpSymbolKind(kind) {
74
+ if (kind === 'interface') return 'interface';
75
+ if (kind === 'trait') return 'trait';
76
+ if (kind === 'enum') return 'type';
77
+ return 'class';
78
+ }
79
+
80
+ export { scanPhp };
@@ -0,0 +1,62 @@
1
+ import { nativeDeclaration, nativeImportDeclaration, sourceLines, splitParameters } from './native-region-scanner-core.js';
2
+ import { pythonBlockSpan } from './native-region-scanner-spans.js';
3
+
4
+ function scanPython(input) {
5
+ const declarations = [];
6
+ const lines = sourceLines(input.sourceText);
7
+ const blockStack = [];
8
+ let decorators = [];
9
+ for (const [index, { line, number }] of lines.entries()) {
10
+ const trimmed = line.trim();
11
+ if (!trimmed) {
12
+ decorators = [];
13
+ continue;
14
+ }
15
+ const indent = indentationLength(line);
16
+ while (blockStack.length && indent <= blockStack[blockStack.length - 1].indent) blockStack.pop();
17
+
18
+ let match;
19
+ if ((match = trimmed.match(/^@([A-Za-z_][\w.]*(?:\([^)]*\))?)/))) {
20
+ decorators.push(match[1]);
21
+ continue;
22
+ }
23
+ if ((match = trimmed.match(/^(?:async\s+)?def\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*:/))) {
24
+ const owner = blockStack[blockStack.length - 1]?.kind === 'class' ? blockStack[blockStack.length - 1].name : undefined;
25
+ const name = owner ? `${owner}.${match[1]}` : match[1];
26
+ declarations.push(nativeDeclaration(input, number, 'FunctionDef', owner ? 'method' : 'function', name, {
27
+ parameters: splitParameters(match[2]),
28
+ ...(owner ? { owner, methodName: match[1] } : {}),
29
+ ...(decorators.length ? { decorators } : {})
30
+ }, true, {
31
+ span: pythonBlockSpan(input, lines, index),
32
+ metadata: {
33
+ ...(owner ? { owner, methodName: match[1] } : {}),
34
+ ...(decorators.length ? { decorators } : {})
35
+ }
36
+ }));
37
+ blockStack.push({ kind: 'def', name, indent });
38
+ } else if ((match = trimmed.match(/^class\s+([A-Za-z_]\w*)/))) {
39
+ const owner = blockStack[blockStack.length - 1]?.kind === 'class' ? blockStack[blockStack.length - 1].name : undefined;
40
+ const name = owner ? `${owner}.${match[1]}` : match[1];
41
+ declarations.push(nativeDeclaration(input, number, 'ClassDef', 'class', name, {
42
+ ...(owner ? { owner } : {}),
43
+ ...(decorators.length ? { decorators } : {})
44
+ }, true, {
45
+ span: pythonBlockSpan(input, lines, index),
46
+ metadata: {
47
+ ...(owner ? { owner } : {}),
48
+ ...(decorators.length ? { decorators } : {})
49
+ }
50
+ }));
51
+ blockStack.push({ kind: 'class', name, indent });
52
+ } else if ((match = trimmed.match(/^(?:from\s+([A-Za-z_][\w.]*)\s+import\s+.+|import\s+([A-Za-z_][\w.]*))/))) {
53
+ declarations.push(nativeImportDeclaration(input, number, match[1] ?? match[2], 'Import', 'module'));
54
+ }
55
+ decorators = [];
56
+ }
57
+ return declarations;
58
+ }
59
+
60
+ function indentationLength(line) { return String(line ?? '').match(/^\s*/)?.[0].length ?? 0; }
61
+
62
+ export { scanPython };
@@ -0,0 +1,72 @@
1
+ import { nativeDeclaration, nativeImportDeclaration, sourceLines, splitParameters } from './native-region-scanner-core.js';
2
+ import { endKeywordBlockSpan } from './native-region-scanner-spans.js';
3
+
4
+ function scanRuby(input) {
5
+ const declarations = [];
6
+ const lines = sourceLines(input.sourceText);
7
+ const blockStack = [];
8
+ for (const [index, { line, number }] of lines.entries()) {
9
+ const trimmed = line.trim();
10
+ if (!trimmed) continue;
11
+ if (/^end\b/.test(trimmed)) {
12
+ blockStack.pop();
13
+ continue;
14
+ }
15
+
16
+ let match;
17
+ if ((match = trimmed.match(/^(?:require|load)\s+['"]([^'"]+)['"]/))) {
18
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'Require', 'module'));
19
+ } else if ((match = trimmed.match(/^module\s+([A-Za-z_]\w*(?:::[A-Za-z_]\w*)*)/))) {
20
+ const name = qualifiedContainerName(blockStack, match[1]);
21
+ declarations.push(nativeDeclaration(input, number, 'Module', 'module', name, {}, true, endSpanOptions(input, lines, index)));
22
+ blockStack.push({ kind: 'module', name });
23
+ } else if ((match = trimmed.match(/^class\s+([A-Za-z_]\w*(?:::[A-Za-z_]\w*)*)/))) {
24
+ const name = qualifiedContainerName(blockStack, match[1]);
25
+ declarations.push(nativeDeclaration(input, number, 'Class', 'class', name, {}, true, endSpanOptions(input, lines, index)));
26
+ blockStack.push({ kind: 'class', name });
27
+ } else if ((match = trimmed.match(/^def\s+(?:(self|[A-Za-z_]\w*(?:::[A-Za-z_]\w*)*)\.)?([A-Za-z_]\w*[!?=]?)\s*(?:\(([^)]*)\)|([^#=]*))?/))) {
28
+ const owner = nearestContainer(blockStack);
29
+ const target = rubyMethodTarget(owner, match[1], match[2]);
30
+ declarations.push(nativeDeclaration(input, number, 'Def', target.owner ? 'method' : 'function', target.name, {
31
+ parameters: splitParameters(match[3] ?? match[4]),
32
+ methodName: match[2],
33
+ ...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {})
34
+ }, true, {
35
+ ...endSpanOptions(input, lines, index),
36
+ metadata: {
37
+ methodName: match[2],
38
+ ...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {})
39
+ }
40
+ }));
41
+ blockStack.push({ kind: 'def', name: target.name });
42
+ }
43
+ }
44
+ return declarations;
45
+ }
46
+
47
+ function endSpanOptions(input, lines, index) {
48
+ return { span: endKeywordBlockSpan(input, lines, index) };
49
+ }
50
+
51
+ function qualifiedContainerName(blockStack, name) {
52
+ if (name.includes('::')) return name;
53
+ const owner = nearestContainer(blockStack);
54
+ return owner?.kind === 'module' ? `${owner.name}::${name}` : name;
55
+ }
56
+
57
+ function nearestContainer(blockStack) {
58
+ for (let index = blockStack.length - 1; index >= 0; index -= 1) {
59
+ if (blockStack[index].kind === 'class' || blockStack[index].kind === 'module') return blockStack[index];
60
+ if (blockStack[index].kind === 'def') return undefined;
61
+ }
62
+ return undefined;
63
+ }
64
+
65
+ function rubyMethodTarget(owner, receiver, methodName) {
66
+ if (receiver && receiver !== 'self') return { name: `${receiver}.singleton.${methodName}`, owner: receiver, receiverKind: 'singleton' };
67
+ if (receiver === 'self' && owner) return { name: `${owner.name}.singleton.${methodName}`, owner: owner.name, receiverKind: 'singleton' };
68
+ if (owner) return { name: `${owner.name}.instance.${methodName}`, owner: owner.name, receiverKind: 'instance' };
69
+ return { name: methodName };
70
+ }
71
+
72
+ export { scanRuby };
@@ -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 scanScala(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(/^package\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/))) {
17
+ declarations.push(nativeDeclaration(input, number, 'PackageClause', 'package', match[1], {}, false));
18
+ } else if ((match = trimmed.match(/^import\s+(.+?);?$/))) {
19
+ declarations.push(nativeImportDeclaration(input, number, match[1].trim(), 'Import', 'package'));
20
+ } 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*)/))) {
21
+ const owner = nearestContainer(blockStack);
22
+ const name = scalaContainerName(owner, match[1], match[2]);
23
+ const hasBody = trimmed.includes('{') || trimmed.includes(':');
24
+ declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Def`, scalaSymbolKind(match[1]), name, owner ? { owner: owner.name } : {}, hasBody, spanOptions(input, lines, index, trimmed.includes('{'))));
25
+ if (trimmed.includes('{')) blockStack.push({ kind: match[1], name, bodyDepth: braceDepth + 1 });
26
+ } else if ((match = trimmed.match(/^(?:(?:private|protected|final|implicit|override|inline)\s+)*def\s+([A-Za-z_]\w*)\s*(?:\[[^\]]+\])?\s*\(([^)]*)\)/))) {
27
+ const owner = nearestContainer(blockStack);
28
+ const target = scalaMethodTarget(owner, match[1]);
29
+ const hasBody = trimmed.includes('{') || trimmed.includes('=');
30
+ declarations.push(nativeDeclaration(input, number, 'DefDef', target.owner ? 'method' : 'function', target.name, {
31
+ parameters: splitParameters(match[2]),
32
+ methodName: match[1],
33
+ ...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {})
34
+ }, hasBody, {
35
+ ...spanOptions(input, lines, index, trimmed.includes('{')),
36
+ metadata: {
37
+ methodName: match[1],
38
+ ...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {})
39
+ }
40
+ }));
41
+ if (trimmed.includes('{')) blockStack.push({ kind: 'function', name: target.name, bodyDepth: braceDepth + 1 });
42
+ } else if ((match = trimmed.match(/^(?:(?:private|protected|final|implicit|opaque)\s+)*type\s+([A-Za-z_]\w*)\b/))) {
43
+ declarations.push(nativeDeclaration(input, number, 'TypeDef', 'type', match[1], {}, false));
44
+ } else if ((match = trimmed.match(/^(?:(?:private|protected|final|implicit|lazy|override|inline)\s+)*(?:val|var)\s+([A-Za-z_]\w*)\b/))) {
45
+ declarations.push(nativeDeclaration(input, number, 'ValDef', 'variable', match[1], {}, false));
46
+ }
47
+ braceDepth = Math.max(0, braceDepth + braceDelta(line));
48
+ }
49
+ return declarations;
50
+ }
51
+
52
+ function spanOptions(input, lines, index, hasBraceBody) {
53
+ return hasBraceBody ? { span: braceBlockSpan(input, lines, index) } : {};
54
+ }
55
+
56
+ function depthAfterLeadingClosers(trimmed, depth) {
57
+ const closers = String(trimmed).match(/^}+/)?.[0].length ?? 0;
58
+ return Math.max(0, depth - closers);
59
+ }
60
+
61
+ function nearestContainer(blockStack) {
62
+ for (let index = blockStack.length - 1; index >= 0; index -= 1) {
63
+ if (blockStack[index].kind === 'function') return undefined;
64
+ if (['class', 'trait', 'object', 'enum'].includes(blockStack[index].kind)) return blockStack[index];
65
+ }
66
+ return undefined;
67
+ }
68
+
69
+ function scalaContainerName(owner, kind, name) {
70
+ const base = kind === 'object' ? `${name}.object` : name;
71
+ return owner ? `${owner.name}.${base}` : base;
72
+ }
73
+
74
+ function scalaMethodTarget(owner, methodName) {
75
+ if (!owner) return { name: methodName };
76
+ const receiverKind = owner.kind === 'object' ? 'object' : 'member';
77
+ return { name: `${owner.name}.${methodName}`, owner: owner.name, receiverKind };
78
+ }
79
+
80
+ function braceDelta(source) {
81
+ return [...String(source ?? '')].reduce((delta, char) => delta + (char === '{' ? 1 : char === '}' ? -1 : 0), 0);
82
+ }
83
+
84
+ function scalaSymbolKind(kind) {
85
+ if (kind === 'trait') return 'trait';
86
+ if (kind === 'object') return 'module';
87
+ if (kind === 'enum') return 'type';
88
+ return 'class';
89
+ }
90
+
91
+ export { scanScala };
@@ -0,0 +1,74 @@
1
+ function pythonBlockSpan(input, lines, index) {
2
+ const baseIndent = indentationLength(lines[index]?.line);
3
+ let end = index;
4
+ for (let cursor = index + 1; cursor < lines.length; cursor += 1) {
5
+ const line = lines[cursor].line;
6
+ if (line.trim() && indentationLength(line) <= baseIndent) break;
7
+ end = cursor;
8
+ }
9
+ return lineSpan(input, lines[index], lines[end] ?? lines[index]);
10
+ }
11
+
12
+ function braceBlockSpan(input, lines, index) {
13
+ let depth = 0;
14
+ let seenOpen = false;
15
+ let end = index;
16
+ for (let cursor = index; cursor < lines.length; cursor += 1) {
17
+ for (const char of lines[cursor].line) {
18
+ if (char === '{') {
19
+ seenOpen = true;
20
+ depth += 1;
21
+ } else if (char === '}') depth -= 1;
22
+ }
23
+ end = cursor;
24
+ if (seenOpen && depth <= 0) break;
25
+ }
26
+ return lineSpan(input, lines[index], lines[end] ?? lines[index]);
27
+ }
28
+
29
+ function endKeywordBlockSpan(input, lines, index) {
30
+ let depth = 0;
31
+ let end = index;
32
+ for (let cursor = index; cursor < lines.length; cursor += 1) {
33
+ const trimmed = lines[cursor].line.trim();
34
+ if (endBlockStart(trimmed)) depth += 1;
35
+ if (/^end\b/.test(trimmed)) depth -= 1;
36
+ end = cursor;
37
+ if (depth <= 0 && cursor > index) break;
38
+ }
39
+ return lineSpan(input, lines[index], lines[end] ?? lines[index]);
40
+ }
41
+
42
+ function terminatedBlockSpan(input, lines, index, terminator) {
43
+ let end = index;
44
+ for (let cursor = index; cursor < lines.length; cursor += 1) {
45
+ end = cursor;
46
+ if (terminator.test(lines[cursor].line.trim())) break;
47
+ }
48
+ return lineSpan(input, lines[index], lines[end] ?? lines[index]);
49
+ }
50
+
51
+ function sqlStatementSpan(input, lines, index) {
52
+ let end = index;
53
+ let inDollarQuote = false;
54
+ for (let cursor = index; cursor < lines.length; cursor += 1) {
55
+ const line = lines[cursor].line;
56
+ const markerCount = (line.match(/\$\$/g) ?? []).length;
57
+ if (markerCount % 2 === 1) inDollarQuote = !inDollarQuote;
58
+ end = cursor;
59
+ if (!inDollarQuote && /;\s*$/.test(line.trim())) break;
60
+ }
61
+ return lineSpan(input, lines[index], lines[end] ?? lines[index]);
62
+ }
63
+
64
+ function lineSpan(input, startLine, endLine) {
65
+ return { sourceId: input.sourceHash, path: input.sourcePath, startLine: startLine.number, endLine: endLine.number, startColumn: 1, endColumn: endLine.line.length + 1 };
66
+ }
67
+
68
+ function indentationLength(line) { return String(line ?? '').match(/^\s*/)?.[0].length ?? 0; }
69
+
70
+ function endBlockStart(line) {
71
+ return /^(?:class|module|def|defp?|defmodule|function|if|unless|case|while|for|begin|try|receive)\b/.test(line);
72
+ }
73
+
74
+ export { braceBlockSpan, endKeywordBlockSpan, pythonBlockSpan, sqlStatementSpan, terminatedBlockSpan };