gitnexus 1.6.3-rc.8 → 1.6.3

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 (285) hide show
  1. package/README.md +21 -5
  2. package/dist/_shared/graph/types.d.ts +16 -0
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/index.d.ts +20 -2
  5. package/dist/_shared/index.d.ts.map +1 -1
  6. package/dist/_shared/index.js +11 -0
  7. package/dist/_shared/index.js.map +1 -1
  8. package/dist/_shared/scope-resolution/def-index.js +2 -2
  9. package/dist/_shared/scope-resolution/def-index.js.map +1 -1
  10. package/dist/_shared/scope-resolution/method-dispatch-index.d.ts +8 -0
  11. package/dist/_shared/scope-resolution/method-dispatch-index.d.ts.map +1 -1
  12. package/dist/_shared/scope-resolution/method-dispatch-index.js +2 -2
  13. package/dist/_shared/scope-resolution/method-dispatch-index.js.map +1 -1
  14. package/dist/_shared/scope-resolution/module-scope-index.d.ts +8 -0
  15. package/dist/_shared/scope-resolution/module-scope-index.d.ts.map +1 -1
  16. package/dist/_shared/scope-resolution/module-scope-index.js +10 -2
  17. package/dist/_shared/scope-resolution/module-scope-index.js.map +1 -1
  18. package/dist/_shared/scope-resolution/parsed-file.d.ts +76 -0
  19. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -0
  20. package/dist/_shared/scope-resolution/parsed-file.js +54 -0
  21. package/dist/_shared/scope-resolution/parsed-file.js.map +1 -0
  22. package/dist/_shared/scope-resolution/position-index.d.ts +12 -0
  23. package/dist/_shared/scope-resolution/position-index.d.ts.map +1 -1
  24. package/dist/_shared/scope-resolution/position-index.js +2 -2
  25. package/dist/_shared/scope-resolution/position-index.js.map +1 -1
  26. package/dist/_shared/scope-resolution/qualified-name-index.js +2 -2
  27. package/dist/_shared/scope-resolution/qualified-name-index.js.map +1 -1
  28. package/dist/_shared/scope-resolution/reference-site.d.ts +75 -0
  29. package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -0
  30. package/dist/_shared/scope-resolution/reference-site.js +24 -0
  31. package/dist/_shared/scope-resolution/reference-site.js.map +1 -0
  32. package/dist/_shared/scope-resolution/registries/class-registry.d.ts +27 -0
  33. package/dist/_shared/scope-resolution/registries/class-registry.d.ts.map +1 -0
  34. package/dist/_shared/scope-resolution/registries/class-registry.js +30 -0
  35. package/dist/_shared/scope-resolution/registries/class-registry.js.map +1 -0
  36. package/dist/_shared/scope-resolution/registries/context.d.ts +69 -0
  37. package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -0
  38. package/dist/_shared/scope-resolution/registries/context.js +44 -0
  39. package/dist/_shared/scope-resolution/registries/context.js.map +1 -0
  40. package/dist/_shared/scope-resolution/registries/evidence.d.ts +56 -0
  41. package/dist/_shared/scope-resolution/registries/evidence.d.ts.map +1 -0
  42. package/dist/_shared/scope-resolution/registries/evidence.js +150 -0
  43. package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -0
  44. package/dist/_shared/scope-resolution/registries/field-registry.d.ts +26 -0
  45. package/dist/_shared/scope-resolution/registries/field-registry.d.ts.map +1 -0
  46. package/dist/_shared/scope-resolution/registries/field-registry.js +31 -0
  47. package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -0
  48. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts +81 -0
  49. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts.map +1 -0
  50. package/dist/_shared/scope-resolution/registries/lookup-core.js +332 -0
  51. package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -0
  52. package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts +33 -0
  53. package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts.map +1 -0
  54. package/dist/_shared/scope-resolution/registries/lookup-qualified.js +56 -0
  55. package/dist/_shared/scope-resolution/registries/lookup-qualified.js.map +1 -0
  56. package/dist/_shared/scope-resolution/registries/method-registry.d.ts +36 -0
  57. package/dist/_shared/scope-resolution/registries/method-registry.d.ts.map +1 -0
  58. package/dist/_shared/scope-resolution/registries/method-registry.js +32 -0
  59. package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -0
  60. package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts +43 -0
  61. package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts.map +1 -0
  62. package/dist/_shared/scope-resolution/registries/tie-breaks.js +60 -0
  63. package/dist/_shared/scope-resolution/registries/tie-breaks.js.map +1 -0
  64. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts +1 -10
  65. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -1
  66. package/dist/_shared/scope-resolution/resolve-type-ref.js +6 -0
  67. package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -1
  68. package/dist/_shared/scope-resolution/scope-tree.d.ts +4 -4
  69. package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
  70. package/dist/_shared/scope-resolution/scope-tree.js +3 -2
  71. package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
  72. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +6 -2
  73. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +1 -1
  74. package/dist/_shared/scope-resolution/shadow/aggregate.js +5 -0
  75. package/dist/_shared/scope-resolution/shadow/aggregate.js.map +1 -1
  76. package/dist/_shared/scope-resolution/types.d.ts +11 -0
  77. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  78. package/dist/cli/ai-context.js +35 -4
  79. package/dist/cli/analyze.d.ts +27 -0
  80. package/dist/cli/analyze.js +31 -1
  81. package/dist/cli/clean.js +19 -1
  82. package/dist/cli/group.js +73 -0
  83. package/dist/cli/index-repo.js +8 -1
  84. package/dist/cli/index.js +26 -1
  85. package/dist/cli/list.js +11 -1
  86. package/dist/cli/remove.d.ts +30 -0
  87. package/dist/cli/remove.js +99 -0
  88. package/dist/cli/setup.js +185 -57
  89. package/dist/cli/tool.d.ts +5 -0
  90. package/dist/cli/tool.js +42 -0
  91. package/dist/config/ignore-service.d.ts +9 -0
  92. package/dist/config/ignore-service.js +80 -13
  93. package/dist/core/embedding-mode.d.ts +30 -0
  94. package/dist/core/embedding-mode.js +30 -0
  95. package/dist/core/embeddings/ast-utils.js +22 -22
  96. package/dist/core/embeddings/chunker.js +30 -25
  97. package/dist/core/embeddings/embedding-pipeline.d.ts +6 -0
  98. package/dist/core/embeddings/embedding-pipeline.js +15 -6
  99. package/dist/core/embeddings/text-generator.d.ts +1 -1
  100. package/dist/core/embeddings/text-generator.js +33 -24
  101. package/dist/core/embeddings/types.d.ts +43 -1
  102. package/dist/core/embeddings/types.js +101 -29
  103. package/dist/core/git-staleness.d.ts +18 -0
  104. package/dist/core/git-staleness.js +108 -0
  105. package/dist/core/graph/graph.js +115 -20
  106. package/dist/core/graph/types.d.ts +12 -1
  107. package/dist/core/group/config-parser.d.ts +4 -0
  108. package/dist/core/group/config-parser.js +18 -1
  109. package/dist/core/group/cross-impact.d.ts +41 -0
  110. package/dist/core/group/cross-impact.js +441 -0
  111. package/dist/core/group/extractors/http-patterns/php.js +126 -18
  112. package/dist/core/group/group-path-utils.d.ts +17 -0
  113. package/dist/core/group/group-path-utils.js +40 -0
  114. package/dist/core/group/resolve-at-member.d.ts +10 -0
  115. package/dist/core/group/resolve-at-member.js +31 -0
  116. package/dist/core/group/service.d.ts +9 -0
  117. package/dist/core/group/service.js +259 -25
  118. package/dist/core/group/types.d.ts +30 -0
  119. package/dist/core/ingestion/ast-cache.d.ts +16 -1
  120. package/dist/core/ingestion/ast-cache.js +14 -2
  121. package/dist/core/ingestion/call-processor.js +9 -0
  122. package/dist/core/ingestion/emit-references.d.ts +88 -0
  123. package/dist/core/ingestion/emit-references.js +229 -0
  124. package/dist/core/ingestion/filesystem-walker.js +6 -4
  125. package/dist/core/ingestion/finalize-orchestrator.d.ts +63 -0
  126. package/dist/core/ingestion/finalize-orchestrator.js +139 -0
  127. package/dist/core/ingestion/framework-detection.js +6 -2
  128. package/dist/core/ingestion/import-processor.js +4 -0
  129. package/dist/core/ingestion/import-resolvers/python.js +9 -6
  130. package/dist/core/ingestion/import-target-adapter.d.ts +73 -0
  131. package/dist/core/ingestion/import-target-adapter.js +95 -0
  132. package/dist/core/ingestion/language-provider.d.ts +36 -33
  133. package/dist/core/ingestion/languages/csharp/accessor-unwrap.d.ts +21 -0
  134. package/dist/core/ingestion/languages/csharp/accessor-unwrap.js +56 -0
  135. package/dist/core/ingestion/languages/csharp/arity-metadata.d.ts +26 -0
  136. package/dist/core/ingestion/languages/csharp/arity-metadata.js +46 -0
  137. package/dist/core/ingestion/languages/csharp/arity.d.ts +23 -0
  138. package/dist/core/ingestion/languages/csharp/arity.js +37 -0
  139. package/dist/core/ingestion/languages/csharp/cache-stats.d.ts +15 -0
  140. package/dist/core/ingestion/languages/csharp/cache-stats.js +26 -0
  141. package/dist/core/ingestion/languages/csharp/captures.d.ts +19 -0
  142. package/dist/core/ingestion/languages/csharp/captures.js +249 -0
  143. package/dist/core/ingestion/languages/csharp/import-decomposer.d.ts +19 -0
  144. package/dist/core/ingestion/languages/csharp/import-decomposer.js +93 -0
  145. package/dist/core/ingestion/languages/csharp/import-target.d.ts +25 -0
  146. package/dist/core/ingestion/languages/csharp/import-target.js +123 -0
  147. package/dist/core/ingestion/languages/csharp/index.d.ts +82 -0
  148. package/dist/core/ingestion/languages/csharp/index.js +82 -0
  149. package/dist/core/ingestion/languages/csharp/interpret.d.ts +15 -0
  150. package/dist/core/ingestion/languages/csharp/interpret.js +132 -0
  151. package/dist/core/ingestion/languages/csharp/merge-bindings.d.ts +27 -0
  152. package/dist/core/ingestion/languages/csharp/merge-bindings.js +55 -0
  153. package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +50 -0
  154. package/dist/core/ingestion/languages/csharp/namespace-siblings.js +374 -0
  155. package/dist/core/ingestion/languages/csharp/query.d.ts +35 -0
  156. package/dist/core/ingestion/languages/csharp/query.js +515 -0
  157. package/dist/core/ingestion/languages/csharp/receiver-binding.d.ts +31 -0
  158. package/dist/core/ingestion/languages/csharp/receiver-binding.js +135 -0
  159. package/dist/core/ingestion/languages/csharp/scope-resolver.d.ts +10 -0
  160. package/dist/core/ingestion/languages/csharp/scope-resolver.js +63 -0
  161. package/dist/core/ingestion/languages/csharp/simple-hooks.d.ts +53 -0
  162. package/dist/core/ingestion/languages/csharp/simple-hooks.js +76 -0
  163. package/dist/core/ingestion/languages/csharp.js +14 -0
  164. package/dist/core/ingestion/languages/python/arity-metadata.d.ts +24 -0
  165. package/dist/core/ingestion/languages/python/arity-metadata.js +45 -0
  166. package/dist/core/ingestion/languages/python/arity.d.ts +22 -0
  167. package/dist/core/ingestion/languages/python/arity.js +38 -0
  168. package/dist/core/ingestion/languages/python/cache-stats.d.ts +17 -0
  169. package/dist/core/ingestion/languages/python/cache-stats.js +28 -0
  170. package/dist/core/ingestion/languages/python/captures.d.ts +19 -0
  171. package/dist/core/ingestion/languages/python/captures.js +106 -0
  172. package/dist/core/ingestion/languages/python/import-decomposer.d.ts +15 -0
  173. package/dist/core/ingestion/languages/python/import-decomposer.js +112 -0
  174. package/dist/core/ingestion/languages/python/import-target.d.ts +21 -0
  175. package/dist/core/ingestion/languages/python/import-target.js +99 -0
  176. package/dist/core/ingestion/languages/python/index.d.ts +80 -0
  177. package/dist/core/ingestion/languages/python/index.js +80 -0
  178. package/dist/core/ingestion/languages/python/interpret.d.ts +15 -0
  179. package/dist/core/ingestion/languages/python/interpret.js +191 -0
  180. package/dist/core/ingestion/languages/python/merge-bindings.d.ts +16 -0
  181. package/dist/core/ingestion/languages/python/merge-bindings.js +44 -0
  182. package/dist/core/ingestion/languages/python/query.d.ts +9 -0
  183. package/dist/core/ingestion/languages/python/query.js +267 -0
  184. package/dist/core/ingestion/languages/python/receiver-binding.d.ts +21 -0
  185. package/dist/core/ingestion/languages/python/receiver-binding.js +116 -0
  186. package/dist/core/ingestion/languages/python/scope-resolver.d.ts +16 -0
  187. package/dist/core/ingestion/languages/python/scope-resolver.js +53 -0
  188. package/dist/core/ingestion/languages/python/simple-hooks.d.ts +23 -0
  189. package/dist/core/ingestion/languages/python/simple-hooks.js +35 -0
  190. package/dist/core/ingestion/languages/python.js +14 -0
  191. package/dist/core/ingestion/model/method-registry.d.ts +9 -0
  192. package/dist/core/ingestion/model/method-registry.js +4 -0
  193. package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +59 -0
  194. package/dist/core/ingestion/model/scope-resolution-indexes.js +42 -0
  195. package/dist/core/ingestion/model/semantic-model.d.ts +64 -0
  196. package/dist/core/ingestion/model/semantic-model.js +55 -0
  197. package/dist/core/ingestion/mro-processor.js +38 -22
  198. package/dist/core/ingestion/parsing-processor.d.ts +18 -1
  199. package/dist/core/ingestion/parsing-processor.js +45 -11
  200. package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
  201. package/dist/core/ingestion/pipeline-phases/index.js +1 -0
  202. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +10 -0
  203. package/dist/core/ingestion/pipeline-phases/parse-impl.js +17 -2
  204. package/dist/core/ingestion/pipeline-phases/parse.d.ts +18 -0
  205. package/dist/core/ingestion/pipeline.js +2 -1
  206. package/dist/core/ingestion/registry-primary-flag.d.ts +86 -0
  207. package/dist/core/ingestion/registry-primary-flag.js +111 -0
  208. package/dist/core/ingestion/resolve-references.d.ts +63 -0
  209. package/dist/core/ingestion/resolve-references.js +175 -0
  210. package/dist/core/ingestion/scope-extractor-bridge.d.ts +32 -0
  211. package/dist/core/ingestion/scope-extractor-bridge.js +44 -0
  212. package/dist/core/ingestion/scope-extractor.d.ts +86 -0
  213. package/dist/core/ingestion/scope-extractor.js +758 -0
  214. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +372 -0
  215. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +212 -0
  216. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +43 -0
  217. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +79 -0
  218. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +57 -0
  219. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +112 -0
  220. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.d.ts +17 -0
  221. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +46 -0
  222. package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.d.ts +19 -0
  223. package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +30 -0
  224. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.d.ts +37 -0
  225. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +113 -0
  226. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +38 -0
  227. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +73 -0
  228. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +42 -0
  229. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +198 -0
  230. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +27 -0
  231. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +131 -0
  232. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +48 -0
  233. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +130 -0
  234. package/dist/core/ingestion/scope-resolution/passes/mro.d.ts +42 -0
  235. package/dist/core/ingestion/scope-resolution/passes/mro.js +99 -0
  236. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +26 -0
  237. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +61 -0
  238. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +46 -0
  239. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +327 -0
  240. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +47 -0
  241. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +130 -0
  242. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.d.ts +68 -0
  243. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +125 -0
  244. package/dist/core/ingestion/scope-resolution/pipeline/registry.d.ts +17 -0
  245. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +21 -0
  246. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +66 -0
  247. package/dist/core/ingestion/scope-resolution/pipeline/run.js +157 -0
  248. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +36 -0
  249. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +52 -0
  250. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +127 -0
  251. package/dist/core/ingestion/scope-resolution/scope/walkers.js +349 -0
  252. package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +52 -0
  253. package/dist/core/ingestion/scope-resolution/workspace-index.js +61 -0
  254. package/dist/core/ingestion/shadow-harness.d.ts +113 -0
  255. package/dist/core/ingestion/shadow-harness.js +148 -0
  256. package/dist/core/ingestion/utils/ast-helpers.d.ts +19 -1
  257. package/dist/core/ingestion/utils/ast-helpers.js +70 -0
  258. package/dist/core/ingestion/utils/max-file-size.d.ts +20 -0
  259. package/dist/core/ingestion/utils/max-file-size.js +52 -0
  260. package/dist/core/ingestion/workers/parse-worker.d.ts +9 -0
  261. package/dist/core/ingestion/workers/parse-worker.js +57 -21
  262. package/dist/core/lbug/lbug-adapter.d.ts +22 -2
  263. package/dist/core/lbug/lbug-adapter.js +58 -14
  264. package/dist/core/lbug/pool-adapter.d.ts +17 -0
  265. package/dist/core/lbug/pool-adapter.js +24 -14
  266. package/dist/core/run-analyze.d.ts +32 -0
  267. package/dist/core/run-analyze.js +74 -19
  268. package/dist/core/search/bm25-index.d.ts +18 -0
  269. package/dist/core/search/bm25-index.js +125 -12
  270. package/dist/core/tree-sitter/parser-loader.js +6 -1
  271. package/dist/mcp/local/local-backend.d.ts +67 -3
  272. package/dist/mcp/local/local-backend.js +296 -34
  273. package/dist/mcp/resources.d.ts +31 -0
  274. package/dist/mcp/resources.js +100 -17
  275. package/dist/mcp/tools.d.ts +4 -1
  276. package/dist/mcp/tools.js +75 -54
  277. package/dist/server/api.js +6 -2
  278. package/dist/storage/git.d.ts +49 -0
  279. package/dist/storage/git.js +111 -0
  280. package/dist/storage/repo-manager.d.ts +246 -1
  281. package/dist/storage/repo-manager.js +391 -9
  282. package/package.json +7 -6
  283. package/scripts/bench-scope-resolution.ts +134 -0
  284. package/scripts/ci-list-migrated-languages.ts +24 -0
  285. package/skills/gitnexus-cli.md +1 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * C# `ScopeResolver` registered in `SCOPE_RESOLVERS` and consumed by
3
+ * the generic `runScopeResolution` orchestrator (RFC #909 Ring 3).
4
+ *
5
+ * Second migration after Python — see `pythonScopeResolver` for the
6
+ * canonical shape.
7
+ */
8
+ import { SupportedLanguages } from '../../../../_shared/index.js';
9
+ import { buildMro, defaultLinearize } from '../../scope-resolution/passes/mro.js';
10
+ import { populateClassOwnedMembers } from '../../scope-resolution/scope/walkers.js';
11
+ import { csharpProvider } from '../csharp.js';
12
+ import { csharpArityCompatibility, csharpMergeBindings, resolveCsharpImportTarget, } from './index.js';
13
+ import { populateCsharpNamespaceSiblings } from './namespace-siblings.js';
14
+ import { unwrapCsharpCollectionAccessor } from './accessor-unwrap.js';
15
+ const csharpScopeResolver = {
16
+ language: SupportedLanguages.CSharp,
17
+ languageProvider: csharpProvider,
18
+ importEdgeReason: 'csharp-scope: using',
19
+ resolveImportTarget: (targetRaw, fromFile, allFilePaths) => {
20
+ const ws = { fromFile, allFilePaths };
21
+ // `WorkspaceIndex` is an opaque `unknown` placeholder in the
22
+ // shared contract, so `ws` passes structurally without a cast.
23
+ return resolveCsharpImportTarget({ kind: 'namespace', localName: '_', importedName: '_', targetRaw }, ws);
24
+ },
25
+ // C# shadowing: local > using > using static. The per-scope id is
26
+ // unused by the C# implementation (shadowing is computed purely
27
+ // from the binding tier), so we don't need to synthesize a Scope.
28
+ mergeBindings: (existing, incoming) => [...csharpMergeBindings([...existing, ...incoming])],
29
+ // Adapter: csharpArityCompatibility uses (def, callsite); the
30
+ // contract is (callsite, def).
31
+ arityCompatibility: (callsite, def) => csharpArityCompatibility(def, callsite),
32
+ buildMro: (graph, parsedFiles, nodeLookup) => buildMro(graph, parsedFiles, nodeLookup, defaultLinearize),
33
+ populateOwners: (parsed) => populateClassOwnedMembers(parsed),
34
+ // C# uses `base` for super-class dispatch, not `super`. Match as a
35
+ // plain identifier (no `()` call like Python's `super(...)`) — `base`
36
+ // is a keyword-like receiver, not a callable.
37
+ isSuperReceiver: (text) => text.trim() === 'base',
38
+ // Same-namespace cross-file visibility — C# makes every type
39
+ // declared in `namespace X` visible to other files declaring the
40
+ // same namespace, without any `using` directive. See
41
+ // `namespace-siblings.ts` for the implementation.
42
+ populateNamespaceSiblings: populateCsharpNamespaceSiblings,
43
+ // C# is statically typed — type information is reliable. Field-
44
+ // fallback heuristic stays off (the type-binding layer already
45
+ // produces precise owner types); return-type propagation on is fine
46
+ // since signatures are authoritative.
47
+ fieldFallbackOnMethodLookup: false,
48
+ propagatesReturnTypesAcrossImports: true,
49
+ // `data.Values` / `data.Keys` on Dictionary-like receivers unwrap
50
+ // to the value / key element type. Other languages use method-call
51
+ // syntax for the same access and leave this hook undefined.
52
+ unwrapCollectionAccessor: unwrapCsharpCollectionAccessor,
53
+ // C# matches legacy DAG by collapsing member-call CALLS edges to
54
+ // `(caller, target)` — multiple `g.Greet(...)` sites from Main
55
+ // yield ONE edge, not one per site.
56
+ collapseMemberCallsByCallerTarget: true,
57
+ // C# hoists method return-type bindings to the enclosing Module
58
+ // scope so `propagateImportedReturnTypes` can mirror them across
59
+ // files. The compound-receiver walker needs to walk up from the
60
+ // class scope to find them; see the contract field for rationale.
61
+ hoistTypeBindingsToModule: true,
62
+ };
63
+ export { csharpScopeResolver };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Trivial / no-op-ish hooks for the C# provider. Kept together because
3
+ * each is a few lines and they share a common theme: they make the
4
+ * provider's choice explicit rather than relying on "absence == default"
5
+ * so reviewers don't have to re-derive the analysis.
6
+ */
7
+ import type { CaptureMatch, ParsedImport, Scope, ScopeId, ScopeTree, TypeRef } from '../../../../_shared/index.js';
8
+ /** C# has block scope, but the central extractor's "innermost enclosing
9
+ * scope" default already handles it correctly: class-body declarations
10
+ * attach to the innermost Class scope, method-body declarations attach
11
+ * to the innermost Function scope, and namespace-body declarations
12
+ * attach to the innermost Namespace scope (which the scope query emits
13
+ * for both `namespace X { }` and `namespace X;` forms).
14
+ *
15
+ * Exception: **method return-type bindings** (`@type-binding.return`)
16
+ * must hoist all the way to the Module scope. The default auto-hoist
17
+ * in the central extractor only promotes one level (Function → its
18
+ * parent). For C# methods the parent is always a Class, so without
19
+ * this override the return binding gets stuck at the Class scope,
20
+ * where it's invisible to:
21
+ * - chain-follow's parent-chain walk in `followChainPostFinalize`
22
+ * (tests: `var u = GetUser(); u.Save()` single-file);
23
+ * - cross-file `propagateImportedReturnTypes`, which reads only
24
+ * `sourceModule.typeBindings`.
25
+ * Walking to Module restores both paths. */
26
+ export declare function csharpBindingScopeFor(decl: CaptureMatch, innermost: Scope, tree: ScopeTree): ScopeId | null;
27
+ /** `using` inside `namespace X { }` binds to that namespace's scope (its
28
+ * types are visible only within that namespace's members). File-level
29
+ * `using` delegates to the module default. Class-body `using` is not
30
+ * legal C# — defensively handle it by attaching to the class if it
31
+ * ever slips through.
32
+ *
33
+ * `global using X;` (C# 10+) at the compilation_unit level is treated
34
+ * as a file-scoped using for Unit 2's purposes; cross-file propagation
35
+ * will be addressed if Unit 7's parity gate flags it. */
36
+ export declare function csharpImportOwningScope(_imp: ParsedImport, innermost: Scope, _tree: ScopeTree): ScopeId | null;
37
+ /** Look up `this` or `base` in the function scope's type bindings.
38
+ *
39
+ * `this` and `base` are synthesized as type bindings on instance
40
+ * methods during capture emission (`receiver-binding.ts`) — `this`
41
+ * for every method inside a class/struct/record/interface body, and
42
+ * `base` additionally for methods of a class-like type with an
43
+ * explicit `base_list`. This hook therefore returns a non-null
44
+ * `TypeRef` for instance-method bodies.
45
+ *
46
+ * Returns `null` for:
47
+ * - static methods (no `this` synthesized)
48
+ * - free functions / module-level code (no enclosing class)
49
+ * - non-Function scopes
50
+ *
51
+ * Matches `pythonReceiverBinding`'s shape so the two provider
52
+ * wirings stay symmetric. */
53
+ export declare function csharpReceiverBinding(functionScope: Scope): TypeRef | null;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Trivial / no-op-ish hooks for the C# provider. Kept together because
3
+ * each is a few lines and they share a common theme: they make the
4
+ * provider's choice explicit rather than relying on "absence == default"
5
+ * so reviewers don't have to re-derive the analysis.
6
+ */
7
+ // ─── bindingScopeFor ──────────────────────────────────────────────────────
8
+ /** C# has block scope, but the central extractor's "innermost enclosing
9
+ * scope" default already handles it correctly: class-body declarations
10
+ * attach to the innermost Class scope, method-body declarations attach
11
+ * to the innermost Function scope, and namespace-body declarations
12
+ * attach to the innermost Namespace scope (which the scope query emits
13
+ * for both `namespace X { }` and `namespace X;` forms).
14
+ *
15
+ * Exception: **method return-type bindings** (`@type-binding.return`)
16
+ * must hoist all the way to the Module scope. The default auto-hoist
17
+ * in the central extractor only promotes one level (Function → its
18
+ * parent). For C# methods the parent is always a Class, so without
19
+ * this override the return binding gets stuck at the Class scope,
20
+ * where it's invisible to:
21
+ * - chain-follow's parent-chain walk in `followChainPostFinalize`
22
+ * (tests: `var u = GetUser(); u.Save()` single-file);
23
+ * - cross-file `propagateImportedReturnTypes`, which reads only
24
+ * `sourceModule.typeBindings`.
25
+ * Walking to Module restores both paths. */
26
+ export function csharpBindingScopeFor(decl, innermost, tree) {
27
+ if (decl['@type-binding.return'] !== undefined) {
28
+ let cur = innermost;
29
+ while (cur !== undefined && cur.kind !== 'Module') {
30
+ const parentId = cur.parent ?? null;
31
+ if (parentId === null)
32
+ break;
33
+ cur = tree.getScope(parentId);
34
+ }
35
+ if (cur !== undefined && cur.kind === 'Module')
36
+ return cur.id;
37
+ }
38
+ return null;
39
+ }
40
+ // ─── importOwningScope ────────────────────────────────────────────────────
41
+ /** `using` inside `namespace X { }` binds to that namespace's scope (its
42
+ * types are visible only within that namespace's members). File-level
43
+ * `using` delegates to the module default. Class-body `using` is not
44
+ * legal C# — defensively handle it by attaching to the class if it
45
+ * ever slips through.
46
+ *
47
+ * `global using X;` (C# 10+) at the compilation_unit level is treated
48
+ * as a file-scoped using for Unit 2's purposes; cross-file propagation
49
+ * will be addressed if Unit 7's parity gate flags it. */
50
+ export function csharpImportOwningScope(_imp, innermost, _tree) {
51
+ if (innermost.kind === 'Namespace' || innermost.kind === 'Class' || innermost.kind === 'Function')
52
+ return innermost.id;
53
+ return null;
54
+ }
55
+ // ─── receiverBinding ──────────────────────────────────────────────────────
56
+ /** Look up `this` or `base` in the function scope's type bindings.
57
+ *
58
+ * `this` and `base` are synthesized as type bindings on instance
59
+ * methods during capture emission (`receiver-binding.ts`) — `this`
60
+ * for every method inside a class/struct/record/interface body, and
61
+ * `base` additionally for methods of a class-like type with an
62
+ * explicit `base_list`. This hook therefore returns a non-null
63
+ * `TypeRef` for instance-method bodies.
64
+ *
65
+ * Returns `null` for:
66
+ * - static methods (no `this` synthesized)
67
+ * - free functions / module-level code (no enclosing class)
68
+ * - non-Function scopes
69
+ *
70
+ * Matches `pythonReceiverBinding`'s shape so the two provider
71
+ * wirings stay symmetric. */
72
+ export function csharpReceiverBinding(functionScope) {
73
+ if (functionScope.kind !== 'Function')
74
+ return null;
75
+ return functionScope.typeBindings.get('this') ?? functionScope.typeBindings.get('base') ?? null;
76
+ }
@@ -24,6 +24,7 @@ import { csharpMethodConfig } from '../method-extractors/configs/csharp.js';
24
24
  import { createVariableExtractor } from '../variable-extractors/generic.js';
25
25
  import { csharpVariableConfig } from '../variable-extractors/configs/csharp.js';
26
26
  import { createHeritageExtractor } from '../heritage-extractors/generic.js';
27
+ import { emitCsharpScopeCaptures, interpretCsharpImport, interpretCsharpTypeBinding, csharpBindingScopeFor, csharpImportOwningScope, csharpMergeBindings, csharpReceiverBinding, csharpArityCompatibility, resolveCsharpImportTarget, } from './csharp/index.js';
27
28
  const BUILT_INS = new Set([
28
29
  'Console',
29
30
  'WriteLine',
@@ -135,4 +136,17 @@ export const csharpProvider = defineLanguage({
135
136
  classExtractor: createClassExtractor(csharpClassConfig),
136
137
  heritageExtractor: createHeritageExtractor(SupportedLanguages.CSharp),
137
138
  builtInNames: BUILT_INS,
139
+ // ── RFC #909 Ring 3: scope-based resolution hooks (RFC §5) ──────────
140
+ // C# is the second migration after Python. See ./csharp/index.ts for
141
+ // the full per-hook rationale and the canonical capture vocabulary
142
+ // in ./csharp/query.ts (CSHARP_SCOPE_QUERY constant).
143
+ emitScopeCaptures: emitCsharpScopeCaptures,
144
+ interpretImport: interpretCsharpImport,
145
+ interpretTypeBinding: interpretCsharpTypeBinding,
146
+ bindingScopeFor: csharpBindingScopeFor,
147
+ importOwningScope: csharpImportOwningScope,
148
+ mergeBindings: (_scope, bindings) => csharpMergeBindings(bindings),
149
+ receiverBinding: csharpReceiverBinding,
150
+ arityCompatibility: csharpArityCompatibility,
151
+ resolveImportTarget: resolveCsharpImportTarget,
138
152
  });
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Extract Python arity metadata from a `function_definition` tree-sitter
3
+ * node — parameter count, required count, and (where present) a type
4
+ * list that the existing `pythonArityCompatibility` hook reads.
5
+ *
6
+ * Mirrors the legacy `buildMethodProps` conversion so scope-extracted
7
+ * defs carry the same arity semantics as the parse-worker path:
8
+ * - `self` / `cls` are stripped (consumed by `extractPythonParameters`).
9
+ * - Defaulted params contribute to `optionalCount`, flipping
10
+ * `requiredParameterCount = total − optionalCount`.
11
+ * - Variadic (`*args` / `**kwargs`) collapses `parameterCount` to
12
+ * `undefined`, which `pythonArityCompatibility` then treats as
13
+ * `'unknown'` — keeping the candidate in the registry's lookup set.
14
+ * - `parameterTypes` is populated only with real type text, matching
15
+ * legacy behavior.
16
+ */
17
+ import type { SyntaxNode } from '../../utils/ast-helpers.js';
18
+ interface PythonArityMetadata {
19
+ readonly parameterCount: number | undefined;
20
+ readonly requiredParameterCount: number | undefined;
21
+ readonly parameterTypes: readonly string[] | undefined;
22
+ }
23
+ export declare function computePythonArityMetadata(fnNode: SyntaxNode): PythonArityMetadata;
24
+ export {};
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Extract Python arity metadata from a `function_definition` tree-sitter
3
+ * node — parameter count, required count, and (where present) a type
4
+ * list that the existing `pythonArityCompatibility` hook reads.
5
+ *
6
+ * Mirrors the legacy `buildMethodProps` conversion so scope-extracted
7
+ * defs carry the same arity semantics as the parse-worker path:
8
+ * - `self` / `cls` are stripped (consumed by `extractPythonParameters`).
9
+ * - Defaulted params contribute to `optionalCount`, flipping
10
+ * `requiredParameterCount = total − optionalCount`.
11
+ * - Variadic (`*args` / `**kwargs`) collapses `parameterCount` to
12
+ * `undefined`, which `pythonArityCompatibility` then treats as
13
+ * `'unknown'` — keeping the candidate in the registry's lookup set.
14
+ * - `parameterTypes` is populated only with real type text, matching
15
+ * legacy behavior.
16
+ */
17
+ import { pythonMethodConfig } from '../../method-extractors/configs/python.js';
18
+ export function computePythonArityMetadata(fnNode) {
19
+ const params = pythonMethodConfig.extractParameters?.(fnNode) ?? [];
20
+ let hasVariadic = false;
21
+ let optionalCount = 0;
22
+ const types = [];
23
+ for (const p of params) {
24
+ if (p.isVariadic)
25
+ hasVariadic = true;
26
+ else if (p.isOptional)
27
+ optionalCount++;
28
+ if (p.type !== null)
29
+ types.push(p.type);
30
+ }
31
+ const total = params.length;
32
+ const parameterCount = hasVariadic ? undefined : total;
33
+ // Unlike legacy `buildMethodProps`, we populate `requiredParameterCount`
34
+ // whenever the function isn't variadic — even when it equals
35
+ // `parameterCount`. The scope-resolution registry needs a concrete min
36
+ // to rule out under-application (e.g. picking `write_audit(x, y)` for
37
+ // a 1-arg call). Legacy could get away with leaving it undefined
38
+ // because its call-graph builder had a separate arity pre-filter.
39
+ const requiredParameterCount = hasVariadic ? undefined : total - optionalCount;
40
+ return {
41
+ parameterCount,
42
+ requiredParameterCount,
43
+ parameterTypes: types.length > 0 ? types : undefined,
44
+ };
45
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Python arity check, accommodating `*args`, `**kwargs`, and defaults.
3
+ *
4
+ * The `def` metadata we care about (set by the existing Python method/
5
+ * function extractor):
6
+ * - `parameterCount` — total positional + keyword params
7
+ * - `requiredParameterCount` — min required (excludes defaults / `*args` / `**kwargs`)
8
+ * - `parameterTypes` — present when types are known; we also use it
9
+ * as a "we have varargs" hint (`'*args'`,
10
+ * `'**kwargs'` literals appear in the array).
11
+ *
12
+ * Verdicts:
13
+ * - `'compatible'` — `requiredParameterCount <= argCount <= parameterCount`,
14
+ * OR the def takes `*args` (then any `argCount >= required` ok).
15
+ * - `'incompatible'` — argCount is below required, OR above max with no `*args`.
16
+ * - `'unknown'` — def metadata is absent / incomplete.
17
+ *
18
+ * `'incompatible'` is a soft signal in `Registry.lookup` (penalized but
19
+ * still considered when no compatible candidate exists), per RFC §4.
20
+ */
21
+ import type { Callsite, SymbolDefinition } from '../../../../_shared/index.js';
22
+ export declare function pythonArityCompatibility(def: SymbolDefinition, callsite: Callsite): 'compatible' | 'unknown' | 'incompatible';
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Python arity check, accommodating `*args`, `**kwargs`, and defaults.
3
+ *
4
+ * The `def` metadata we care about (set by the existing Python method/
5
+ * function extractor):
6
+ * - `parameterCount` — total positional + keyword params
7
+ * - `requiredParameterCount` — min required (excludes defaults / `*args` / `**kwargs`)
8
+ * - `parameterTypes` — present when types are known; we also use it
9
+ * as a "we have varargs" hint (`'*args'`,
10
+ * `'**kwargs'` literals appear in the array).
11
+ *
12
+ * Verdicts:
13
+ * - `'compatible'` — `requiredParameterCount <= argCount <= parameterCount`,
14
+ * OR the def takes `*args` (then any `argCount >= required` ok).
15
+ * - `'incompatible'` — argCount is below required, OR above max with no `*args`.
16
+ * - `'unknown'` — def metadata is absent / incomplete.
17
+ *
18
+ * `'incompatible'` is a soft signal in `Registry.lookup` (penalized but
19
+ * still considered when no compatible candidate exists), per RFC §4.
20
+ */
21
+ export function pythonArityCompatibility(def, callsite) {
22
+ const max = def.parameterCount;
23
+ const min = def.requiredParameterCount;
24
+ if (max === undefined && min === undefined)
25
+ return 'unknown';
26
+ const argCount = callsite.arity;
27
+ if (!Number.isFinite(argCount) || argCount < 0)
28
+ return 'unknown';
29
+ // Detect varargs/kwargs from parameterTypes if present (the Python
30
+ // method extractor stores `'*args'`/`'**kwargs'` in this list).
31
+ const hasVarArgs = def.parameterTypes !== undefined &&
32
+ def.parameterTypes.some((t) => t === '*args' || t === '**kwargs' || t.startsWith('*'));
33
+ if (min !== undefined && argCount < min)
34
+ return 'incompatible';
35
+ if (max !== undefined && argCount > max && !hasVarArgs)
36
+ return 'incompatible';
37
+ return 'compatible';
38
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Dev-mode counters for the cross-phase scope-captures parse cache.
3
+ *
4
+ * Gated by `PROF_SCOPE_RESOLUTION=1`. In production the module-level
5
+ * `PROF` constant is `false` and V8 folds every increment site into
6
+ * dead code, so the hot path in `captures.ts` stays branch-free.
7
+ *
8
+ * Extracted from `captures.ts` so the production hot-path module
9
+ * doesn't carry a module-global counter and its reset/export surface.
10
+ */
11
+ export declare function recordCacheHit(): void;
12
+ export declare function recordCacheMiss(): void;
13
+ export declare function getPythonCaptureCacheStats(): {
14
+ hits: number;
15
+ misses: number;
16
+ };
17
+ export declare function resetPythonCaptureCacheStats(): void;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Dev-mode counters for the cross-phase scope-captures parse cache.
3
+ *
4
+ * Gated by `PROF_SCOPE_RESOLUTION=1`. In production the module-level
5
+ * `PROF` constant is `false` and V8 folds every increment site into
6
+ * dead code, so the hot path in `captures.ts` stays branch-free.
7
+ *
8
+ * Extracted from `captures.ts` so the production hot-path module
9
+ * doesn't carry a module-global counter and its reset/export surface.
10
+ */
11
+ const PROF = process.env.PROF_SCOPE_RESOLUTION === '1';
12
+ let CACHE_HITS = 0;
13
+ let CACHE_MISSES = 0;
14
+ export function recordCacheHit() {
15
+ if (PROF)
16
+ CACHE_HITS++;
17
+ }
18
+ export function recordCacheMiss() {
19
+ if (PROF)
20
+ CACHE_MISSES++;
21
+ }
22
+ export function getPythonCaptureCacheStats() {
23
+ return { hits: CACHE_HITS, misses: CACHE_MISSES };
24
+ }
25
+ export function resetPythonCaptureCacheStats() {
26
+ CACHE_HITS = 0;
27
+ CACHE_MISSES = 0;
28
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `emitScopeCaptures` for Python.
3
+ *
4
+ * Drives the scope query against `tree-sitter-python` and groups raw
5
+ * matches into `CaptureMatch[]` for the central extractor, then layers
6
+ * two synthesized streams on top:
7
+ *
8
+ * 1. **Per-name import statements** — `import a, b` and
9
+ * `from m import x, y` decompose to one match per imported name
10
+ * (see `import-decomposer.ts`).
11
+ * 2. **Receiver type bindings** — each `function_definition` inside a
12
+ * class body emits a `@type-binding.self` (or `@type-binding.cls`
13
+ * for `@classmethod`) capture so Pass-4 attaches the implicit
14
+ * receiver (see `receiver-binding.ts`).
15
+ *
16
+ * Pure given the input source text. No I/O, no globals consulted.
17
+ */
18
+ import type { CaptureMatch } from '../../../../_shared/index.js';
19
+ export declare function emitPythonScopeCaptures(sourceText: string, _filePath: string, cachedTree?: unknown): readonly CaptureMatch[];
@@ -0,0 +1,106 @@
1
+ /**
2
+ * `emitScopeCaptures` for Python.
3
+ *
4
+ * Drives the scope query against `tree-sitter-python` and groups raw
5
+ * matches into `CaptureMatch[]` for the central extractor, then layers
6
+ * two synthesized streams on top:
7
+ *
8
+ * 1. **Per-name import statements** — `import a, b` and
9
+ * `from m import x, y` decompose to one match per imported name
10
+ * (see `import-decomposer.ts`).
11
+ * 2. **Receiver type bindings** — each `function_definition` inside a
12
+ * class body emits a `@type-binding.self` (or `@type-binding.cls`
13
+ * for `@classmethod`) capture so Pass-4 attaches the implicit
14
+ * receiver (see `receiver-binding.ts`).
15
+ *
16
+ * Pure given the input source text. No I/O, no globals consulted.
17
+ */
18
+ import { findNodeAtRange, nodeToCapture, syntheticCapture } from '../../utils/ast-helpers.js';
19
+ import { splitImportStatement } from './import-decomposer.js';
20
+ import { getPythonParser, getPythonScopeQuery } from './query.js';
21
+ import { synthesizeReceiverTypeBinding } from './receiver-binding.js';
22
+ import { computePythonArityMetadata } from './arity-metadata.js';
23
+ import { recordCacheHit, recordCacheMiss } from './cache-stats.js';
24
+ export function emitPythonScopeCaptures(sourceText, _filePath, cachedTree) {
25
+ // Skip the parse when the caller (parse phase's ASTCache) already
26
+ // produced a Tree for this source. Cache miss = re-parse, same as
27
+ // before. The cachedTree parameter is typed as `unknown` at the
28
+ // contract layer (see `LanguageProvider.emitScopeCaptures`); cast
29
+ // here at the use site.
30
+ let tree = cachedTree;
31
+ if (tree === undefined) {
32
+ tree = getPythonParser().parse(sourceText);
33
+ recordCacheMiss();
34
+ }
35
+ else {
36
+ recordCacheHit();
37
+ }
38
+ const rawMatches = getPythonScopeQuery().matches(tree.rootNode);
39
+ const out = [];
40
+ for (const m of rawMatches) {
41
+ // Group captures by their tag name. Tree-sitter strips the leading
42
+ // `@`; we put it back so the central extractor's prefix lookups
43
+ // (`@scope.`, `@declaration.`, …) work.
44
+ const grouped = {};
45
+ for (const c of m.captures) {
46
+ const tag = '@' + c.name;
47
+ grouped[tag] = nodeToCapture(tag, c.node);
48
+ }
49
+ if (Object.keys(grouped).length === 0)
50
+ continue;
51
+ if (grouped['@import.statement'] !== undefined) {
52
+ // Decompose multi-name imports. Both `import_statement` and
53
+ // `import_from_statement` share the matched range, so we try the
54
+ // `from` form first and fall back to plain.
55
+ const stmtCapture = grouped['@import.statement'];
56
+ const stmtNode = findNodeAtRange(tree.rootNode, stmtCapture.range, 'import_from_statement') ??
57
+ findNodeAtRange(tree.rootNode, stmtCapture.range, 'import_statement');
58
+ if (stmtNode !== null) {
59
+ for (const piece of splitImportStatement(stmtNode))
60
+ out.push(piece);
61
+ }
62
+ else {
63
+ // Defensive fallback: emit the raw match.
64
+ out.push(grouped);
65
+ }
66
+ continue;
67
+ }
68
+ if (grouped['@scope.function'] !== undefined) {
69
+ out.push(grouped);
70
+ const fnNode = findNodeAtRange(tree.rootNode, grouped['@scope.function'].range, 'function_definition');
71
+ if (fnNode !== null) {
72
+ const synth = synthesizeReceiverTypeBinding(fnNode);
73
+ if (synth !== null)
74
+ out.push(synth);
75
+ }
76
+ continue;
77
+ }
78
+ if (grouped['@declaration.function'] !== undefined) {
79
+ // Synthesize arity captures on the declaration match so the
80
+ // central scope-extractor picks them up alongside @declaration.name.
81
+ // The anchor range is the function_definition itself — we resolve
82
+ // the node and pipe it through the arity helper.
83
+ const anchorCap = grouped['@declaration.function'];
84
+ const fnNode = findNodeAtRange(tree.rootNode, anchorCap.range, 'function_definition');
85
+ if (fnNode !== null) {
86
+ const arity = computePythonArityMetadata(fnNode);
87
+ if (arity.parameterCount !== undefined) {
88
+ grouped['@declaration.parameter-count'] = syntheticCapture('@declaration.parameter-count', fnNode, String(arity.parameterCount));
89
+ }
90
+ if (arity.requiredParameterCount !== undefined) {
91
+ grouped['@declaration.required-parameter-count'] = syntheticCapture('@declaration.required-parameter-count', fnNode, String(arity.requiredParameterCount));
92
+ }
93
+ if (arity.parameterTypes !== undefined) {
94
+ // Serialize as JSON so the consumer can round-trip without
95
+ // inventing a quoting convention for type names that may
96
+ // contain commas (`Dict[str, int]`).
97
+ grouped['@declaration.parameter-types'] = syntheticCapture('@declaration.parameter-types', fnNode, JSON.stringify(arity.parameterTypes));
98
+ }
99
+ }
100
+ out.push(grouped);
101
+ continue;
102
+ }
103
+ out.push(grouped);
104
+ }
105
+ return out;
106
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Decompose a Python `import_statement` / `import_from_statement` into
3
+ * one `CaptureMatch` per imported name.
4
+ *
5
+ * Why split here? The `LanguageProvider.interpretImport` contract is
6
+ * one `ParsedImport` per call. Tree-sitter delivers `import a, b as c`
7
+ * and `from m import x, y, z` as a single match each, so without
8
+ * decomposition we'd lose names. The synthesized markers
9
+ * (`@import.kind` / `@import.name` / `@import.alias` / `@import.source`)
10
+ * carry everything `interpretPythonImport` needs to recover the original
11
+ * `ParsedImport` shape — see `interpret.ts`.
12
+ */
13
+ import type { CaptureMatch } from '../../../../_shared/index.js';
14
+ import { type SyntaxNode } from '../../utils/ast-helpers.js';
15
+ export declare function splitImportStatement(stmtNode: SyntaxNode): CaptureMatch[];
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Decompose a Python `import_statement` / `import_from_statement` into
3
+ * one `CaptureMatch` per imported name.
4
+ *
5
+ * Why split here? The `LanguageProvider.interpretImport` contract is
6
+ * one `ParsedImport` per call. Tree-sitter delivers `import a, b as c`
7
+ * and `from m import x, y, z` as a single match each, so without
8
+ * decomposition we'd lose names. The synthesized markers
9
+ * (`@import.kind` / `@import.name` / `@import.alias` / `@import.source`)
10
+ * carry everything `interpretPythonImport` needs to recover the original
11
+ * `ParsedImport` shape — see `interpret.ts`.
12
+ */
13
+ import { findChild, nodeToCapture, syntheticCapture, } from '../../utils/ast-helpers.js';
14
+ export function splitImportStatement(stmtNode) {
15
+ if (stmtNode.type === 'import_statement')
16
+ return splitImportStmt(stmtNode);
17
+ if (stmtNode.type === 'import_from_statement')
18
+ return splitImportFromStmt(stmtNode);
19
+ return [];
20
+ }
21
+ function splitImportStmt(stmtNode) {
22
+ // `import a, b as c, d.e`
23
+ const out = [];
24
+ for (let i = 0; i < stmtNode.namedChildCount; i++) {
25
+ const child = stmtNode.namedChild(i);
26
+ if (child === null)
27
+ continue;
28
+ if (child.type === 'dotted_name') {
29
+ out.push(buildImportMatch(stmtNode, {
30
+ kind: 'plain',
31
+ source: child.text,
32
+ name: child.text.split('.')[0],
33
+ atNode: child,
34
+ }));
35
+ }
36
+ else if (child.type === 'aliased_import') {
37
+ const dotted = findChild(child, 'dotted_name');
38
+ const alias = findChild(child, 'identifier');
39
+ if (dotted !== null && alias !== null) {
40
+ out.push(buildImportMatch(stmtNode, {
41
+ kind: 'aliased',
42
+ source: dotted.text,
43
+ name: dotted.text,
44
+ alias: alias.text,
45
+ atNode: child,
46
+ }));
47
+ }
48
+ }
49
+ }
50
+ return out;
51
+ }
52
+ function splitImportFromStmt(stmtNode) {
53
+ // `from m import a, b as c` / `from m import *` / `from . import x`
54
+ const out = [];
55
+ const moduleField = stmtNode.childForFieldName('module_name');
56
+ const moduleText = moduleField?.text ?? '';
57
+ // Wildcard? tree-sitter-python represents `*` as a `wildcard_import`
58
+ // child and emits no name children.
59
+ const wildcardChild = findChild(stmtNode, 'wildcard_import');
60
+ if (wildcardChild !== null) {
61
+ out.push(buildImportMatch(stmtNode, {
62
+ kind: 'wildcard',
63
+ source: moduleText,
64
+ name: '*',
65
+ atNode: wildcardChild,
66
+ }));
67
+ return out;
68
+ }
69
+ // Names = every dotted_name / aliased_import that isn't the module.
70
+ for (let i = 0; i < stmtNode.namedChildCount; i++) {
71
+ const child = stmtNode.namedChild(i);
72
+ if (child === null)
73
+ continue;
74
+ if (moduleField !== null && child.startIndex === moduleField.startIndex)
75
+ continue;
76
+ if (child.type === 'dotted_name') {
77
+ out.push(buildImportMatch(stmtNode, {
78
+ kind: 'from',
79
+ source: moduleText,
80
+ name: child.text,
81
+ atNode: child,
82
+ }));
83
+ }
84
+ else if (child.type === 'aliased_import') {
85
+ const dotted = findChild(child, 'dotted_name');
86
+ const alias = findChild(child, 'identifier');
87
+ if (dotted !== null && alias !== null) {
88
+ out.push(buildImportMatch(stmtNode, {
89
+ kind: 'from-alias',
90
+ source: moduleText,
91
+ name: dotted.text,
92
+ alias: alias.text,
93
+ atNode: child,
94
+ }));
95
+ }
96
+ }
97
+ }
98
+ return out;
99
+ }
100
+ function buildImportMatch(stmtNode, spec) {
101
+ const stmtCap = nodeToCapture('@import.statement', stmtNode);
102
+ const m = {
103
+ '@import.statement': stmtCap,
104
+ '@import.kind': syntheticCapture('@import.kind', spec.atNode, spec.kind),
105
+ '@import.source': syntheticCapture('@import.source', spec.atNode, spec.source),
106
+ '@import.name': syntheticCapture('@import.name', spec.atNode, spec.name),
107
+ };
108
+ if (spec.alias !== undefined) {
109
+ m['@import.alias'] = syntheticCapture('@import.alias', spec.atNode, spec.alias);
110
+ }
111
+ return m;
112
+ }