@optave/codegraph 3.4.1 → 3.6.0

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 (349) hide show
  1. package/README.md +50 -28
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +119 -127
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
  6. package/dist/ast-analysis/rules/javascript.js +1 -0
  7. package/dist/ast-analysis/rules/javascript.js.map +1 -1
  8. package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
  9. package/dist/ast-analysis/visitors/ast-store-visitor.js +116 -35
  10. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  11. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  12. package/dist/ast-analysis/visitors/complexity-visitor.js +11 -13
  13. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  14. package/dist/db/better-sqlite3.d.ts +3 -0
  15. package/dist/db/better-sqlite3.d.ts.map +1 -0
  16. package/dist/db/better-sqlite3.js +19 -0
  17. package/dist/db/better-sqlite3.js.map +1 -0
  18. package/dist/db/connection.d.ts +25 -4
  19. package/dist/db/connection.d.ts.map +1 -1
  20. package/dist/db/connection.js +125 -23
  21. package/dist/db/connection.js.map +1 -1
  22. package/dist/db/index.d.ts +2 -2
  23. package/dist/db/index.d.ts.map +1 -1
  24. package/dist/db/index.js +1 -1
  25. package/dist/db/index.js.map +1 -1
  26. package/dist/db/migrations.d.ts.map +1 -1
  27. package/dist/db/migrations.js +40 -32
  28. package/dist/db/migrations.js.map +1 -1
  29. package/dist/db/query-builder.d.ts +5 -5
  30. package/dist/db/query-builder.d.ts.map +1 -1
  31. package/dist/db/query-builder.js +20 -4
  32. package/dist/db/query-builder.js.map +1 -1
  33. package/dist/db/repository/index.d.ts +1 -0
  34. package/dist/db/repository/index.d.ts.map +1 -1
  35. package/dist/db/repository/index.js +1 -0
  36. package/dist/db/repository/index.js.map +1 -1
  37. package/dist/db/repository/native-repository.d.ts +58 -0
  38. package/dist/db/repository/native-repository.d.ts.map +1 -0
  39. package/dist/db/repository/native-repository.js +261 -0
  40. package/dist/db/repository/native-repository.js.map +1 -0
  41. package/dist/db/repository/nodes.d.ts +4 -4
  42. package/dist/db/repository/nodes.d.ts.map +1 -1
  43. package/dist/db/repository/nodes.js +6 -6
  44. package/dist/db/repository/nodes.js.map +1 -1
  45. package/dist/domain/analysis/context.d.ts.map +1 -1
  46. package/dist/domain/analysis/context.js +51 -66
  47. package/dist/domain/analysis/context.js.map +1 -1
  48. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  49. package/dist/domain/analysis/dependencies.js +62 -70
  50. package/dist/domain/analysis/dependencies.js.map +1 -1
  51. package/dist/domain/analysis/diff-impact.d.ts +9 -7
  52. package/dist/domain/analysis/diff-impact.d.ts.map +1 -1
  53. package/dist/domain/analysis/exports.d.ts.map +1 -1
  54. package/dist/domain/analysis/exports.js +29 -33
  55. package/dist/domain/analysis/exports.js.map +1 -1
  56. package/dist/domain/analysis/fn-impact.d.ts +15 -17
  57. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  58. package/dist/domain/analysis/fn-impact.js +35 -65
  59. package/dist/domain/analysis/fn-impact.js.map +1 -1
  60. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  61. package/dist/domain/analysis/module-map.js +91 -6
  62. package/dist/domain/analysis/module-map.js.map +1 -1
  63. package/dist/domain/analysis/query-helpers.d.ts +20 -0
  64. package/dist/domain/analysis/query-helpers.d.ts.map +1 -0
  65. package/dist/domain/analysis/query-helpers.js +27 -0
  66. package/dist/domain/analysis/query-helpers.js.map +1 -0
  67. package/dist/domain/graph/builder/context.d.ts +2 -1
  68. package/dist/domain/graph/builder/context.d.ts.map +1 -1
  69. package/dist/domain/graph/builder/context.js +1 -0
  70. package/dist/domain/graph/builder/context.js.map +1 -1
  71. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  72. package/dist/domain/graph/builder/helpers.js +15 -9
  73. package/dist/domain/graph/builder/helpers.js.map +1 -1
  74. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  75. package/dist/domain/graph/builder/incremental.js +3 -2
  76. package/dist/domain/graph/builder/incremental.js.map +1 -1
  77. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  78. package/dist/domain/graph/builder/pipeline.js +95 -7
  79. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  80. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  81. package/dist/domain/graph/builder/stages/build-edges.js +101 -57
  82. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  83. package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
  84. package/dist/domain/graph/builder/stages/build-structure.js +33 -3
  85. package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
  86. package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
  87. package/dist/domain/graph/builder/stages/collect-files.js +70 -6
  88. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  89. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  90. package/dist/domain/graph/builder/stages/detect-changes.js +36 -14
  91. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  92. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  93. package/dist/domain/graph/builder/stages/finalize.js +130 -88
  94. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  95. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  96. package/dist/domain/graph/builder/stages/insert-nodes.js +124 -16
  97. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  98. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  99. package/dist/domain/graph/builder/stages/resolve-imports.js +3 -2
  100. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  101. package/dist/domain/graph/resolve.d.ts +0 -4
  102. package/dist/domain/graph/resolve.d.ts.map +1 -1
  103. package/dist/domain/graph/resolve.js +32 -48
  104. package/dist/domain/graph/resolve.js.map +1 -1
  105. package/dist/domain/graph/watcher.d.ts.map +1 -1
  106. package/dist/domain/graph/watcher.js +12 -12
  107. package/dist/domain/graph/watcher.js.map +1 -1
  108. package/dist/domain/parser.d.ts +1 -1
  109. package/dist/domain/parser.d.ts.map +1 -1
  110. package/dist/domain/parser.js +165 -101
  111. package/dist/domain/parser.js.map +1 -1
  112. package/dist/domain/search/search/cli-formatter.d.ts.map +1 -1
  113. package/dist/domain/search/search/cli-formatter.js +88 -83
  114. package/dist/domain/search/search/cli-formatter.js.map +1 -1
  115. package/dist/extractors/bash.d.ts +6 -0
  116. package/dist/extractors/bash.d.ts.map +1 -0
  117. package/dist/extractors/bash.js +91 -0
  118. package/dist/extractors/bash.js.map +1 -0
  119. package/dist/extractors/c.d.ts +6 -0
  120. package/dist/extractors/c.d.ts.map +1 -0
  121. package/dist/extractors/c.js +204 -0
  122. package/dist/extractors/c.js.map +1 -0
  123. package/dist/extractors/cpp.d.ts +6 -0
  124. package/dist/extractors/cpp.d.ts.map +1 -0
  125. package/dist/extractors/cpp.js +283 -0
  126. package/dist/extractors/cpp.js.map +1 -0
  127. package/dist/extractors/csharp.d.ts.map +1 -1
  128. package/dist/extractors/csharp.js +42 -54
  129. package/dist/extractors/csharp.js.map +1 -1
  130. package/dist/extractors/go.d.ts.map +1 -1
  131. package/dist/extractors/go.js +126 -130
  132. package/dist/extractors/go.js.map +1 -1
  133. package/dist/extractors/hcl.js +6 -6
  134. package/dist/extractors/hcl.js.map +1 -1
  135. package/dist/extractors/helpers.d.ts +32 -1
  136. package/dist/extractors/helpers.d.ts.map +1 -1
  137. package/dist/extractors/helpers.js +74 -0
  138. package/dist/extractors/helpers.js.map +1 -1
  139. package/dist/extractors/index.d.ts +6 -0
  140. package/dist/extractors/index.d.ts.map +1 -1
  141. package/dist/extractors/index.js +6 -0
  142. package/dist/extractors/index.js.map +1 -1
  143. package/dist/extractors/java.d.ts.map +1 -1
  144. package/dist/extractors/java.js +32 -47
  145. package/dist/extractors/java.js.map +1 -1
  146. package/dist/extractors/javascript.d.ts.map +1 -1
  147. package/dist/extractors/javascript.js +359 -330
  148. package/dist/extractors/javascript.js.map +1 -1
  149. package/dist/extractors/kotlin.d.ts +6 -0
  150. package/dist/extractors/kotlin.d.ts.map +1 -0
  151. package/dist/extractors/kotlin.js +275 -0
  152. package/dist/extractors/kotlin.js.map +1 -0
  153. package/dist/extractors/php.d.ts.map +1 -1
  154. package/dist/extractors/php.js +39 -44
  155. package/dist/extractors/php.js.map +1 -1
  156. package/dist/extractors/python.d.ts.map +1 -1
  157. package/dist/extractors/python.js +75 -93
  158. package/dist/extractors/python.js.map +1 -1
  159. package/dist/extractors/ruby.js +6 -13
  160. package/dist/extractors/ruby.js.map +1 -1
  161. package/dist/extractors/rust.d.ts.map +1 -1
  162. package/dist/extractors/rust.js +58 -82
  163. package/dist/extractors/rust.js.map +1 -1
  164. package/dist/extractors/scala.d.ts +6 -0
  165. package/dist/extractors/scala.d.ts.map +1 -0
  166. package/dist/extractors/scala.js +269 -0
  167. package/dist/extractors/scala.js.map +1 -0
  168. package/dist/extractors/swift.d.ts +6 -0
  169. package/dist/extractors/swift.d.ts.map +1 -0
  170. package/dist/extractors/swift.js +275 -0
  171. package/dist/extractors/swift.js.map +1 -0
  172. package/dist/features/ast.d.ts +16 -1
  173. package/dist/features/ast.d.ts.map +1 -1
  174. package/dist/features/ast.js +45 -23
  175. package/dist/features/ast.js.map +1 -1
  176. package/dist/features/audit.d.ts.map +1 -1
  177. package/dist/features/audit.js +17 -21
  178. package/dist/features/audit.js.map +1 -1
  179. package/dist/features/branch-compare.d.ts.map +1 -1
  180. package/dist/features/branch-compare.js +50 -4
  181. package/dist/features/branch-compare.js.map +1 -1
  182. package/dist/features/cfg.d.ts +7 -1
  183. package/dist/features/cfg.d.ts.map +1 -1
  184. package/dist/features/cfg.js +118 -62
  185. package/dist/features/cfg.js.map +1 -1
  186. package/dist/features/check.d.ts.map +1 -1
  187. package/dist/features/check.js +79 -62
  188. package/dist/features/check.js.map +1 -1
  189. package/dist/features/complexity-query.d.ts.map +1 -1
  190. package/dist/features/complexity-query.js +142 -137
  191. package/dist/features/complexity-query.js.map +1 -1
  192. package/dist/features/complexity.d.ts +7 -1
  193. package/dist/features/complexity.d.ts.map +1 -1
  194. package/dist/features/complexity.js +62 -1
  195. package/dist/features/complexity.js.map +1 -1
  196. package/dist/features/dataflow.d.ts +7 -1
  197. package/dist/features/dataflow.d.ts.map +1 -1
  198. package/dist/features/dataflow.js +356 -188
  199. package/dist/features/dataflow.js.map +1 -1
  200. package/dist/features/graph-enrichment.d.ts.map +1 -1
  201. package/dist/features/graph-enrichment.js +117 -104
  202. package/dist/features/graph-enrichment.js.map +1 -1
  203. package/dist/features/sequence.d.ts.map +1 -1
  204. package/dist/features/sequence.js +25 -4
  205. package/dist/features/sequence.js.map +1 -1
  206. package/dist/features/snapshot.d.ts.map +1 -1
  207. package/dist/features/snapshot.js +2 -1
  208. package/dist/features/snapshot.js.map +1 -1
  209. package/dist/features/structure-query.d.ts.map +1 -1
  210. package/dist/features/structure-query.js +29 -4
  211. package/dist/features/structure-query.js.map +1 -1
  212. package/dist/features/structure.d.ts.map +1 -1
  213. package/dist/features/structure.js +35 -15
  214. package/dist/features/structure.js.map +1 -1
  215. package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
  216. package/dist/graph/algorithms/leiden/adapter.js +88 -73
  217. package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
  218. package/dist/graph/algorithms/leiden/index.js +43 -28
  219. package/dist/graph/algorithms/leiden/index.js.map +1 -1
  220. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  221. package/dist/graph/algorithms/leiden/optimiser.js +90 -104
  222. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  223. package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
  224. package/dist/graph/algorithms/leiden/partition.js +89 -106
  225. package/dist/graph/algorithms/leiden/partition.js.map +1 -1
  226. package/dist/graph/model.d.ts +2 -0
  227. package/dist/graph/model.d.ts.map +1 -1
  228. package/dist/graph/model.js +20 -8
  229. package/dist/graph/model.js.map +1 -1
  230. package/dist/infrastructure/config.d.ts +0 -8
  231. package/dist/infrastructure/config.d.ts.map +1 -1
  232. package/dist/infrastructure/config.js +73 -62
  233. package/dist/infrastructure/config.js.map +1 -1
  234. package/dist/infrastructure/registry.d.ts +0 -8
  235. package/dist/infrastructure/registry.d.ts.map +1 -1
  236. package/dist/infrastructure/registry.js +12 -14
  237. package/dist/infrastructure/registry.js.map +1 -1
  238. package/dist/mcp/server.d.ts.map +1 -1
  239. package/dist/mcp/server.js +47 -45
  240. package/dist/mcp/server.js.map +1 -1
  241. package/dist/presentation/audit.d.ts.map +1 -1
  242. package/dist/presentation/audit.js +61 -57
  243. package/dist/presentation/audit.js.map +1 -1
  244. package/dist/presentation/branch-compare.d.ts.map +1 -1
  245. package/dist/presentation/branch-compare.js +56 -38
  246. package/dist/presentation/branch-compare.js.map +1 -1
  247. package/dist/presentation/check.d.ts.map +1 -1
  248. package/dist/presentation/check.js +30 -32
  249. package/dist/presentation/check.js.map +1 -1
  250. package/dist/presentation/colors.d.ts.map +1 -1
  251. package/dist/presentation/colors.js +2 -0
  252. package/dist/presentation/colors.js.map +1 -1
  253. package/dist/presentation/complexity.d.ts.map +1 -1
  254. package/dist/presentation/complexity.js +25 -19
  255. package/dist/presentation/complexity.js.map +1 -1
  256. package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
  257. package/dist/presentation/queries-cli/exports.js +15 -15
  258. package/dist/presentation/queries-cli/exports.js.map +1 -1
  259. package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
  260. package/dist/presentation/queries-cli/impact.js +29 -19
  261. package/dist/presentation/queries-cli/impact.js.map +1 -1
  262. package/dist/types.d.ts +406 -3
  263. package/dist/types.d.ts.map +1 -1
  264. package/grammars/tree-sitter-bash.wasm +0 -0
  265. package/grammars/tree-sitter-c.wasm +0 -0
  266. package/grammars/tree-sitter-cpp.wasm +0 -0
  267. package/grammars/tree-sitter-kotlin.wasm +0 -0
  268. package/grammars/tree-sitter-scala.wasm +0 -0
  269. package/grammars/tree-sitter-swift.wasm +0 -0
  270. package/package.json +67 -11
  271. package/src/ast-analysis/engine.ts +147 -138
  272. package/src/ast-analysis/rules/javascript.ts +1 -0
  273. package/src/ast-analysis/visitors/ast-store-visitor.ts +116 -34
  274. package/src/ast-analysis/visitors/complexity-visitor.ts +11 -11
  275. package/src/db/better-sqlite3.ts +20 -0
  276. package/src/db/connection.ts +148 -26
  277. package/src/db/index.ts +4 -1
  278. package/src/db/migrations.ts +38 -32
  279. package/src/db/query-builder.ts +30 -5
  280. package/src/db/repository/index.ts +1 -0
  281. package/src/db/repository/native-repository.ts +361 -0
  282. package/src/db/repository/nodes.ts +7 -3
  283. package/src/domain/analysis/context.ts +73 -75
  284. package/src/domain/analysis/dependencies.ts +78 -68
  285. package/src/domain/analysis/exports.ts +45 -34
  286. package/src/domain/analysis/fn-impact.ts +67 -64
  287. package/src/domain/analysis/module-map.ts +103 -8
  288. package/src/domain/analysis/query-helpers.ts +35 -0
  289. package/src/domain/graph/builder/context.ts +2 -0
  290. package/src/domain/graph/builder/helpers.ts +12 -6
  291. package/src/domain/graph/builder/incremental.ts +3 -2
  292. package/src/domain/graph/builder/pipeline.ts +98 -6
  293. package/src/domain/graph/builder/stages/build-edges.ts +116 -83
  294. package/src/domain/graph/builder/stages/build-structure.ts +46 -8
  295. package/src/domain/graph/builder/stages/collect-files.ts +83 -6
  296. package/src/domain/graph/builder/stages/detect-changes.ts +44 -21
  297. package/src/domain/graph/builder/stages/finalize.ts +172 -109
  298. package/src/domain/graph/builder/stages/insert-nodes.ts +147 -17
  299. package/src/domain/graph/builder/stages/resolve-imports.ts +3 -2
  300. package/src/domain/graph/resolve.ts +34 -46
  301. package/src/domain/graph/watcher.ts +12 -14
  302. package/src/domain/parser.ts +169 -97
  303. package/src/domain/search/search/cli-formatter.ts +121 -94
  304. package/src/extractors/bash.ts +97 -0
  305. package/src/extractors/c.ts +212 -0
  306. package/src/extractors/cpp.ts +298 -0
  307. package/src/extractors/csharp.ts +53 -56
  308. package/src/extractors/go.ts +152 -134
  309. package/src/extractors/hcl.ts +6 -6
  310. package/src/extractors/helpers.ts +93 -1
  311. package/src/extractors/index.ts +6 -0
  312. package/src/extractors/java.ts +43 -48
  313. package/src/extractors/javascript.ts +382 -317
  314. package/src/extractors/kotlin.ts +293 -0
  315. package/src/extractors/php.ts +46 -40
  316. package/src/extractors/python.ts +81 -104
  317. package/src/extractors/ruby.ts +6 -13
  318. package/src/extractors/rust.ts +65 -84
  319. package/src/extractors/scala.ts +285 -0
  320. package/src/extractors/swift.ts +293 -0
  321. package/src/features/ast.ts +74 -24
  322. package/src/features/audit.ts +24 -20
  323. package/src/features/branch-compare.ts +54 -5
  324. package/src/features/cfg.ts +158 -65
  325. package/src/features/check.ts +90 -74
  326. package/src/features/complexity-query.ts +181 -163
  327. package/src/features/complexity.ts +64 -1
  328. package/src/features/dataflow.ts +462 -217
  329. package/src/features/graph-enrichment.ts +161 -117
  330. package/src/features/sequence.ts +27 -4
  331. package/src/features/snapshot.ts +2 -1
  332. package/src/features/structure-query.ts +43 -4
  333. package/src/features/structure.ts +50 -22
  334. package/src/graph/algorithms/leiden/adapter.ts +126 -71
  335. package/src/graph/algorithms/leiden/index.ts +67 -28
  336. package/src/graph/algorithms/leiden/optimiser.ts +114 -105
  337. package/src/graph/algorithms/leiden/partition.ts +131 -98
  338. package/src/graph/model.ts +19 -7
  339. package/src/infrastructure/config.ts +60 -58
  340. package/src/infrastructure/registry.ts +17 -14
  341. package/src/mcp/server.ts +48 -47
  342. package/src/presentation/audit.ts +72 -67
  343. package/src/presentation/branch-compare.ts +54 -50
  344. package/src/presentation/check.ts +34 -34
  345. package/src/presentation/colors.ts +2 -0
  346. package/src/presentation/complexity.ts +39 -33
  347. package/src/presentation/queries-cli/exports.ts +17 -17
  348. package/src/presentation/queries-cli/impact.ts +30 -22
  349. package/src/types.ts +458 -3
@@ -5,7 +5,14 @@ import type {
5
5
  TreeSitterNode,
6
6
  TreeSitterTree,
7
7
  } from '../types.js';
8
- import { findChild, MAX_WALK_DEPTH, nodeEndLine, rustVisibility } from './helpers.js';
8
+ import {
9
+ extractBodyMembers,
10
+ findParentNode,
11
+ MAX_WALK_DEPTH,
12
+ nodeEndLine,
13
+ rustVisibility,
14
+ setTypeMapEntry,
15
+ } from './helpers.js';
9
16
 
10
17
  /**
11
18
  * Extract symbols from Rust files.
@@ -206,16 +213,9 @@ function handleRustMacroInvocation(node: TreeSitterNode, ctx: ExtractorOutput):
206
213
  }
207
214
  }
208
215
 
216
+ const RUST_IMPL_TYPES = ['impl_item'] as const;
209
217
  function findCurrentImpl(node: TreeSitterNode): string | null {
210
- let current = node.parent;
211
- while (current) {
212
- if (current.type === 'impl_item') {
213
- const typeNode = current.childForFieldName('type');
214
- return typeNode ? typeNode.text : null;
215
- }
216
- current = current.parent;
217
- }
218
- return null;
218
+ return findParentNode(node, RUST_IMPL_TYPES, 'type');
219
219
  }
220
220
 
221
221
  // ── Child extraction helpers ────────────────────────────────────────────────
@@ -227,7 +227,7 @@ function extractRustParameters(paramListNode: TreeSitterNode | null): SubDeclara
227
227
  const param = paramListNode.child(i);
228
228
  if (!param) continue;
229
229
  if (param.type === 'self_parameter') {
230
- params.push({ name: 'self', kind: 'parameter', line: param.startPosition.row + 1 });
230
+ // Skip self matches native engine behaviour
231
231
  } else if (param.type === 'parameter') {
232
232
  const pattern = param.childForFieldName('pattern');
233
233
  if (pattern) {
@@ -239,34 +239,16 @@ function extractRustParameters(paramListNode: TreeSitterNode | null): SubDeclara
239
239
  }
240
240
 
241
241
  function extractStructFields(structNode: TreeSitterNode): SubDeclaration[] {
242
- const fields: SubDeclaration[] = [];
243
- const fieldList =
244
- structNode.childForFieldName('body') || findChild(structNode, 'field_declaration_list');
245
- if (!fieldList) return fields;
246
- for (let i = 0; i < fieldList.childCount; i++) {
247
- const field = fieldList.child(i);
248
- if (!field || field.type !== 'field_declaration') continue;
249
- const nameNode = field.childForFieldName('name');
250
- if (nameNode) {
251
- fields.push({ name: nameNode.text, kind: 'property', line: field.startPosition.row + 1 });
252
- }
253
- }
254
- return fields;
242
+ return extractBodyMembers(
243
+ structNode,
244
+ ['body', 'field_declaration_list'],
245
+ 'field_declaration',
246
+ 'property',
247
+ );
255
248
  }
256
249
 
257
250
  function extractEnumVariants(enumNode: TreeSitterNode): SubDeclaration[] {
258
- const variants: SubDeclaration[] = [];
259
- const body = enumNode.childForFieldName('body') || findChild(enumNode, 'enum_variant_list');
260
- if (!body) return variants;
261
- for (let i = 0; i < body.childCount; i++) {
262
- const variant = body.child(i);
263
- if (!variant || variant.type !== 'enum_variant') continue;
264
- const nameNode = variant.childForFieldName('name');
265
- if (nameNode) {
266
- variants.push({ name: nameNode.text, kind: 'constant', line: variant.startPosition.row + 1 });
267
- }
268
- }
269
- return variants;
251
+ return extractBodyMembers(enumNode, ['body', 'enum_variant_list'], 'enum_variant', 'constant');
270
252
  }
271
253
 
272
254
  function extractRustTypeMap(node: TreeSitterNode, ctx: ExtractorOutput): void {
@@ -282,7 +264,7 @@ function extractRustTypeMapDepth(node: TreeSitterNode, ctx: ExtractorOutput, dep
282
264
  const typeNode = node.childForFieldName('type');
283
265
  if (pattern && pattern.type === 'identifier' && typeNode) {
284
266
  const typeName = extractRustTypeName(typeNode);
285
- if (typeName) ctx.typeMap?.set(pattern.text, { type: typeName, confidence: 0.9 });
267
+ if (typeName && ctx.typeMap) setTypeMapEntry(ctx.typeMap, pattern.text, typeName, 0.9);
286
268
  }
287
269
  }
288
270
 
@@ -294,7 +276,7 @@ function extractRustTypeMapDepth(node: TreeSitterNode, ctx: ExtractorOutput, dep
294
276
  const name = pattern.type === 'identifier' ? pattern.text : null;
295
277
  if (name && name !== 'self' && name !== '&self' && name !== '&mut self') {
296
278
  const typeName = extractRustTypeName(typeNode);
297
- if (typeName) ctx.typeMap?.set(name, { type: typeName, confidence: 0.9 });
279
+ if (typeName && ctx.typeMap) setTypeMapEntry(ctx.typeMap, name, typeName, 0.9);
298
280
  }
299
281
  }
300
282
  }
@@ -327,56 +309,55 @@ function extractRustTypeName(typeNode: TreeSitterNode): string | null {
327
309
  return null;
328
310
  }
329
311
 
330
- function extractRustUsePath(node: TreeSitterNode | null): { source: string; names: string[] }[] {
331
- if (!node) return [];
332
-
333
- if (node.type === 'use_list') {
334
- const results: { source: string; names: string[] }[] = [];
335
- for (let i = 0; i < node.childCount; i++) {
336
- results.push(...extractRustUsePath(node.child(i)));
312
+ /** Collect names from a scoped_use_list's list node. */
313
+ function collectScopedNames(listNode: TreeSitterNode): string[] {
314
+ const names: string[] = [];
315
+ for (let i = 0; i < listNode.childCount; i++) {
316
+ const child = listNode.child(i);
317
+ if (!child) continue;
318
+ if (child.type === 'identifier' || child.type === 'self') {
319
+ names.push(child.text);
320
+ } else if (child.type === 'use_as_clause') {
321
+ const name = (child.childForFieldName('alias') || child.childForFieldName('name'))?.text;
322
+ if (name) names.push(name);
337
323
  }
338
- return results;
339
324
  }
325
+ return names;
326
+ }
340
327
 
341
- if (node.type === 'scoped_use_list') {
342
- const pathNode = node.childForFieldName('path');
343
- const listNode = node.childForFieldName('list');
344
- const prefix = pathNode ? pathNode.text : '';
345
- if (listNode) {
346
- const names: string[] = [];
347
- for (let i = 0; i < listNode.childCount; i++) {
348
- const child = listNode.child(i);
349
- if (
350
- child &&
351
- (child.type === 'identifier' || child.type === 'use_as_clause' || child.type === 'self')
352
- ) {
353
- const name =
354
- child.type === 'use_as_clause'
355
- ? (child.childForFieldName('alias') || child.childForFieldName('name'))?.text
356
- : child.text;
357
- if (name) names.push(name);
358
- }
328
+ function extractRustUsePath(node: TreeSitterNode | null): { source: string; names: string[] }[] {
329
+ if (!node) return [];
330
+
331
+ switch (node.type) {
332
+ case 'use_list': {
333
+ const results: { source: string; names: string[] }[] = [];
334
+ for (let i = 0; i < node.childCount; i++) {
335
+ results.push(...extractRustUsePath(node.child(i)));
359
336
  }
360
- return [{ source: prefix, names }];
337
+ return results;
361
338
  }
362
- return [{ source: prefix, names: [] }];
363
- }
364
-
365
- if (node.type === 'use_as_clause') {
366
- const name = node.childForFieldName('alias') || node.childForFieldName('name');
367
- return [{ source: node.text, names: name ? [name.text] : [] }];
368
- }
369
-
370
- if (node.type === 'use_wildcard') {
371
- const pathNode = node.childForFieldName('path');
372
- return [{ source: pathNode ? pathNode.text : '*', names: ['*'] }];
373
- }
374
-
375
- if (node.type === 'scoped_identifier' || node.type === 'identifier') {
376
- const text = node.text;
377
- const lastName = text.split('::').pop() ?? text;
378
- return [{ source: text, names: [lastName] }];
339
+ case 'scoped_use_list': {
340
+ const pathNode = node.childForFieldName('path');
341
+ const listNode = node.childForFieldName('list');
342
+ const prefix = pathNode ? pathNode.text : '';
343
+ if (!listNode) return [{ source: prefix, names: [] }];
344
+ return [{ source: prefix, names: collectScopedNames(listNode) }];
345
+ }
346
+ case 'use_as_clause': {
347
+ const name = node.childForFieldName('alias') || node.childForFieldName('name');
348
+ return [{ source: node.text, names: name ? [name.text] : [] }];
349
+ }
350
+ case 'use_wildcard': {
351
+ const pathNode = node.childForFieldName('path');
352
+ return [{ source: pathNode ? pathNode.text : '*', names: ['*'] }];
353
+ }
354
+ case 'scoped_identifier':
355
+ case 'identifier': {
356
+ const text = node.text;
357
+ const lastName = text.split('::').pop() ?? text;
358
+ return [{ source: text, names: [lastName] }];
359
+ }
360
+ default:
361
+ return [];
379
362
  }
380
-
381
- return [];
382
363
  }
@@ -0,0 +1,285 @@
1
+ import type {
2
+ Call,
3
+ ExtractorOutput,
4
+ SubDeclaration,
5
+ TreeSitterNode,
6
+ TreeSitterTree,
7
+ } from '../types.js';
8
+ import { extractModifierVisibility, findChild, nodeEndLine } from './helpers.js';
9
+
10
+ /**
11
+ * Extract symbols from Scala files.
12
+ */
13
+ export function extractScalaSymbols(tree: TreeSitterTree, _filePath: string): ExtractorOutput {
14
+ const ctx: ExtractorOutput = {
15
+ definitions: [],
16
+ calls: [],
17
+ imports: [],
18
+ classes: [],
19
+ exports: [],
20
+ typeMap: new Map(),
21
+ };
22
+
23
+ walkScalaNode(tree.rootNode, ctx);
24
+ return ctx;
25
+ }
26
+
27
+ function walkScalaNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
28
+ switch (node.type) {
29
+ case 'class_definition':
30
+ handleScalaClassDef(node, ctx);
31
+ break;
32
+ case 'trait_definition':
33
+ handleScalaTraitDef(node, ctx);
34
+ break;
35
+ case 'object_definition':
36
+ handleScalaObjectDef(node, ctx);
37
+ break;
38
+ case 'function_definition':
39
+ handleScalaFunctionDef(node, ctx);
40
+ break;
41
+ case 'import_declaration':
42
+ handleScalaImportDecl(node, ctx);
43
+ break;
44
+ case 'call_expression':
45
+ handleScalaCallExpression(node, ctx);
46
+ break;
47
+ case 'val_definition':
48
+ case 'var_definition':
49
+ handleScalaValVarDef(node, ctx);
50
+ break;
51
+ }
52
+
53
+ for (let i = 0; i < node.childCount; i++) {
54
+ const child = node.child(i);
55
+ if (child) walkScalaNode(child, ctx);
56
+ }
57
+ }
58
+
59
+ // ── Walk-path per-node-type handlers ────────────────────────────────────────
60
+
61
+ function handleScalaClassDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
62
+ const nameNode = node.childForFieldName('name');
63
+ if (!nameNode) return;
64
+ const name = nameNode.text;
65
+ const children = extractScalaBodyMembers(node, name, ctx);
66
+
67
+ ctx.definitions.push({
68
+ name,
69
+ kind: 'class',
70
+ line: node.startPosition.row + 1,
71
+ endLine: nodeEndLine(node),
72
+ children: children.length > 0 ? children : undefined,
73
+ });
74
+
75
+ extractScalaInheritance(node, name, ctx);
76
+ }
77
+
78
+ function handleScalaTraitDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
79
+ const nameNode = node.childForFieldName('name');
80
+ if (!nameNode) return;
81
+ const name = nameNode.text;
82
+ const children = extractScalaBodyMembers(node, name, ctx);
83
+
84
+ ctx.definitions.push({
85
+ name,
86
+ kind: 'interface',
87
+ line: node.startPosition.row + 1,
88
+ endLine: nodeEndLine(node),
89
+ children: children.length > 0 ? children : undefined,
90
+ });
91
+
92
+ extractScalaInheritance(node, name, ctx);
93
+ }
94
+
95
+ function handleScalaObjectDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
96
+ const nameNode = node.childForFieldName('name');
97
+ if (!nameNode) return;
98
+ const name = nameNode.text;
99
+ const children = extractScalaBodyMembers(node, name, ctx);
100
+
101
+ ctx.definitions.push({
102
+ name,
103
+ kind: 'class',
104
+ line: node.startPosition.row + 1,
105
+ endLine: nodeEndLine(node),
106
+ children: children.length > 0 ? children : undefined,
107
+ });
108
+
109
+ extractScalaInheritance(node, name, ctx);
110
+ }
111
+
112
+ function handleScalaFunctionDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
113
+ // Skip methods already emitted by class/trait/object handlers
114
+ if (node.parent?.type === 'template_body') {
115
+ const grandparent = node.parent.parent;
116
+ if (
117
+ grandparent &&
118
+ (grandparent.type === 'class_definition' ||
119
+ grandparent.type === 'trait_definition' ||
120
+ grandparent.type === 'object_definition')
121
+ ) {
122
+ return;
123
+ }
124
+ }
125
+ const nameNode = node.childForFieldName('name');
126
+ if (!nameNode) return;
127
+ const params = extractScalaParameters(node);
128
+ ctx.definitions.push({
129
+ name: nameNode.text,
130
+ kind: 'function',
131
+ line: node.startPosition.row + 1,
132
+ endLine: nodeEndLine(node),
133
+ children: params.length > 0 ? params : undefined,
134
+ visibility: extractModifierVisibility(node),
135
+ });
136
+ }
137
+
138
+ function handleScalaImportDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
139
+ // import_declaration has alternating `identifier` and `.` children directly (NO import_expression wrapper)
140
+ const parts: string[] = [];
141
+ for (let i = 0; i < node.childCount; i++) {
142
+ const child = node.child(i);
143
+ if (!child) continue;
144
+ if (child.type === 'identifier' || child.type === 'type_identifier') {
145
+ parts.push(child.text);
146
+ }
147
+ }
148
+ if (parts.length === 0) return;
149
+ const fullPath = parts.join('.');
150
+ const lastName = parts[parts.length - 1] ?? fullPath;
151
+ ctx.imports.push({
152
+ source: fullPath,
153
+ names: [lastName],
154
+ line: node.startPosition.row + 1,
155
+ scalaImport: true,
156
+ });
157
+ }
158
+
159
+ function handleScalaCallExpression(node: TreeSitterNode, ctx: ExtractorOutput): void {
160
+ const funcNode = node.childForFieldName('function');
161
+ if (!funcNode) return;
162
+ const call: Call = { name: '', line: node.startPosition.row + 1 };
163
+ if (funcNode.type === 'field_expression') {
164
+ const field = funcNode.childForFieldName('field');
165
+ const value = funcNode.childForFieldName('value');
166
+ if (field) call.name = field.text;
167
+ if (value) call.receiver = value.text;
168
+ } else {
169
+ call.name = funcNode.text;
170
+ }
171
+ if (call.name) ctx.calls.push(call);
172
+ }
173
+
174
+ function handleScalaValVarDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
175
+ // Only handle top-level vals/vars — skip class members and function-local bindings
176
+ if (node.parent?.type === 'template_body') return;
177
+ if (node.parent?.type === 'block' || node.parent?.type === 'indented_block') return;
178
+ const pattern = node.childForFieldName('pattern');
179
+ if (!pattern) return;
180
+ const nameNode = pattern.type === 'identifier' ? pattern : findChild(pattern, 'identifier');
181
+ if (!nameNode) return;
182
+ const kind = node.type === 'val_definition' ? 'constant' : 'variable';
183
+ ctx.definitions.push({
184
+ name: nameNode.text,
185
+ kind,
186
+ line: node.startPosition.row + 1,
187
+ endLine: nodeEndLine(node),
188
+ });
189
+ }
190
+
191
+ // ── Inheritance helpers ─────────────────────────────────────────────────────
192
+
193
+ function extractScalaInheritance(node: TreeSitterNode, name: string, ctx: ExtractorOutput): void {
194
+ const extendsClause = findChild(node, 'extends_clause');
195
+ if (!extendsClause) return;
196
+ let foundExtends = false;
197
+ for (let i = 0; i < extendsClause.childCount; i++) {
198
+ const child = extendsClause.child(i);
199
+ if (!child) continue;
200
+ if (
201
+ child.type === 'type_identifier' ||
202
+ child.type === 'generic_type' ||
203
+ child.type === 'identifier'
204
+ ) {
205
+ const typeName = child.type === 'generic_type' ? child.child(0)?.text : child.text;
206
+ if (!typeName) continue;
207
+ if (!foundExtends) {
208
+ ctx.classes.push({
209
+ name,
210
+ extends: typeName,
211
+ line: node.startPosition.row + 1,
212
+ });
213
+ foundExtends = true;
214
+ } else {
215
+ ctx.classes.push({
216
+ name,
217
+ implements: typeName,
218
+ line: node.startPosition.row + 1,
219
+ });
220
+ }
221
+ }
222
+ }
223
+ }
224
+
225
+ // ── Body member extraction ──────────────────────────────────────────────────
226
+
227
+ function extractScalaBodyMembers(
228
+ parentNode: TreeSitterNode,
229
+ parentName: string,
230
+ ctx: ExtractorOutput,
231
+ ): SubDeclaration[] {
232
+ const children: SubDeclaration[] = [];
233
+ const body = findChild(parentNode, 'template_body');
234
+ if (!body) return children;
235
+
236
+ for (let i = 0; i < body.childCount; i++) {
237
+ const member = body.child(i);
238
+ if (!member) continue;
239
+
240
+ if (member.type === 'function_definition') {
241
+ const methName = member.childForFieldName('name');
242
+ if (methName) {
243
+ ctx.definitions.push({
244
+ name: `${parentName}.${methName.text}`,
245
+ kind: 'method',
246
+ line: member.startPosition.row + 1,
247
+ endLine: member.endPosition.row + 1,
248
+ visibility: extractModifierVisibility(member),
249
+ });
250
+ }
251
+ } else if (member.type === 'val_definition' || member.type === 'var_definition') {
252
+ const pattern = member.childForFieldName('pattern');
253
+ if (pattern) {
254
+ const nameNode = pattern.type === 'identifier' ? pattern : findChild(pattern, 'identifier');
255
+ if (nameNode) {
256
+ children.push({
257
+ name: nameNode.text,
258
+ kind: 'property',
259
+ line: member.startPosition.row + 1,
260
+ visibility: extractModifierVisibility(member),
261
+ });
262
+ }
263
+ }
264
+ }
265
+ }
266
+
267
+ return children;
268
+ }
269
+
270
+ // ── Parameter extraction ────────────────────────────────────────────────────
271
+
272
+ function extractScalaParameters(funcNode: TreeSitterNode): SubDeclaration[] {
273
+ const params: SubDeclaration[] = [];
274
+ const paramList = findChild(funcNode, 'parameters');
275
+ if (!paramList) return params;
276
+ for (let i = 0; i < paramList.childCount; i++) {
277
+ const param = paramList.child(i);
278
+ if (!param || param.type !== 'parameter') continue;
279
+ const nameNode = findChild(param, 'identifier');
280
+ if (nameNode) {
281
+ params.push({ name: nameNode.text, kind: 'parameter', line: param.startPosition.row + 1 });
282
+ }
283
+ }
284
+ return params;
285
+ }