@optave/codegraph 3.5.0 → 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 (310) hide show
  1. package/README.md +35 -14
  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/visitors/ast-store-visitor.d.ts.map +1 -1
  6. package/dist/ast-analysis/visitors/ast-store-visitor.js +14 -1
  7. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  8. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  9. package/dist/ast-analysis/visitors/complexity-visitor.js +11 -13
  10. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  11. package/dist/db/connection.d.ts +12 -2
  12. package/dist/db/connection.d.ts.map +1 -1
  13. package/dist/db/connection.js +81 -53
  14. package/dist/db/connection.js.map +1 -1
  15. package/dist/db/index.d.ts +1 -1
  16. package/dist/db/index.d.ts.map +1 -1
  17. package/dist/db/index.js +1 -1
  18. package/dist/db/index.js.map +1 -1
  19. package/dist/db/migrations.d.ts.map +1 -1
  20. package/dist/db/migrations.js +38 -32
  21. package/dist/db/migrations.js.map +1 -1
  22. package/dist/domain/analysis/context.d.ts.map +1 -1
  23. package/dist/domain/analysis/context.js +51 -66
  24. package/dist/domain/analysis/context.js.map +1 -1
  25. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  26. package/dist/domain/analysis/dependencies.js +62 -70
  27. package/dist/domain/analysis/dependencies.js.map +1 -1
  28. package/dist/domain/analysis/diff-impact.d.ts +9 -7
  29. package/dist/domain/analysis/diff-impact.d.ts.map +1 -1
  30. package/dist/domain/analysis/exports.d.ts.map +1 -1
  31. package/dist/domain/analysis/exports.js +29 -33
  32. package/dist/domain/analysis/exports.js.map +1 -1
  33. package/dist/domain/analysis/fn-impact.d.ts +15 -17
  34. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  35. package/dist/domain/analysis/fn-impact.js +35 -65
  36. package/dist/domain/analysis/fn-impact.js.map +1 -1
  37. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  38. package/dist/domain/analysis/module-map.js +91 -6
  39. package/dist/domain/analysis/module-map.js.map +1 -1
  40. package/dist/domain/analysis/query-helpers.d.ts +20 -0
  41. package/dist/domain/analysis/query-helpers.d.ts.map +1 -0
  42. package/dist/domain/analysis/query-helpers.js +27 -0
  43. package/dist/domain/analysis/query-helpers.js.map +1 -0
  44. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  45. package/dist/domain/graph/builder/helpers.js +15 -9
  46. package/dist/domain/graph/builder/helpers.js.map +1 -1
  47. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  48. package/dist/domain/graph/builder/incremental.js +3 -2
  49. package/dist/domain/graph/builder/incremental.js.map +1 -1
  50. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  51. package/dist/domain/graph/builder/pipeline.js +69 -3
  52. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  53. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  54. package/dist/domain/graph/builder/stages/build-edges.js +7 -51
  55. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  56. package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
  57. package/dist/domain/graph/builder/stages/build-structure.js +7 -5
  58. package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
  59. package/dist/domain/graph/builder/stages/collect-files.js +2 -2
  60. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  61. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  62. package/dist/domain/graph/builder/stages/detect-changes.js +2 -2
  63. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  64. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  65. package/dist/domain/graph/builder/stages/finalize.js +124 -105
  66. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  67. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  68. package/dist/domain/graph/builder/stages/insert-nodes.js +28 -15
  69. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  70. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  71. package/dist/domain/graph/builder/stages/resolve-imports.js +3 -2
  72. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  73. package/dist/domain/graph/resolve.d.ts +0 -4
  74. package/dist/domain/graph/resolve.d.ts.map +1 -1
  75. package/dist/domain/graph/resolve.js +32 -48
  76. package/dist/domain/graph/resolve.js.map +1 -1
  77. package/dist/domain/graph/watcher.d.ts.map +1 -1
  78. package/dist/domain/graph/watcher.js +12 -12
  79. package/dist/domain/graph/watcher.js.map +1 -1
  80. package/dist/domain/parser.d.ts +1 -1
  81. package/dist/domain/parser.d.ts.map +1 -1
  82. package/dist/domain/parser.js +164 -101
  83. package/dist/domain/parser.js.map +1 -1
  84. package/dist/domain/search/search/cli-formatter.d.ts.map +1 -1
  85. package/dist/domain/search/search/cli-formatter.js +88 -83
  86. package/dist/domain/search/search/cli-formatter.js.map +1 -1
  87. package/dist/extractors/bash.d.ts +6 -0
  88. package/dist/extractors/bash.d.ts.map +1 -0
  89. package/dist/extractors/bash.js +91 -0
  90. package/dist/extractors/bash.js.map +1 -0
  91. package/dist/extractors/c.d.ts +6 -0
  92. package/dist/extractors/c.d.ts.map +1 -0
  93. package/dist/extractors/c.js +204 -0
  94. package/dist/extractors/c.js.map +1 -0
  95. package/dist/extractors/cpp.d.ts +6 -0
  96. package/dist/extractors/cpp.d.ts.map +1 -0
  97. package/dist/extractors/cpp.js +283 -0
  98. package/dist/extractors/cpp.js.map +1 -0
  99. package/dist/extractors/csharp.d.ts.map +1 -1
  100. package/dist/extractors/csharp.js +42 -54
  101. package/dist/extractors/csharp.js.map +1 -1
  102. package/dist/extractors/go.d.ts.map +1 -1
  103. package/dist/extractors/go.js +126 -130
  104. package/dist/extractors/go.js.map +1 -1
  105. package/dist/extractors/hcl.js +6 -6
  106. package/dist/extractors/hcl.js.map +1 -1
  107. package/dist/extractors/helpers.d.ts +32 -1
  108. package/dist/extractors/helpers.d.ts.map +1 -1
  109. package/dist/extractors/helpers.js +74 -0
  110. package/dist/extractors/helpers.js.map +1 -1
  111. package/dist/extractors/index.d.ts +6 -0
  112. package/dist/extractors/index.d.ts.map +1 -1
  113. package/dist/extractors/index.js +6 -0
  114. package/dist/extractors/index.js.map +1 -1
  115. package/dist/extractors/java.d.ts.map +1 -1
  116. package/dist/extractors/java.js +32 -47
  117. package/dist/extractors/java.js.map +1 -1
  118. package/dist/extractors/javascript.d.ts.map +1 -1
  119. package/dist/extractors/javascript.js +306 -292
  120. package/dist/extractors/javascript.js.map +1 -1
  121. package/dist/extractors/kotlin.d.ts +6 -0
  122. package/dist/extractors/kotlin.d.ts.map +1 -0
  123. package/dist/extractors/kotlin.js +275 -0
  124. package/dist/extractors/kotlin.js.map +1 -0
  125. package/dist/extractors/php.d.ts.map +1 -1
  126. package/dist/extractors/php.js +39 -44
  127. package/dist/extractors/php.js.map +1 -1
  128. package/dist/extractors/python.d.ts.map +1 -1
  129. package/dist/extractors/python.js +75 -93
  130. package/dist/extractors/python.js.map +1 -1
  131. package/dist/extractors/ruby.js +6 -13
  132. package/dist/extractors/ruby.js.map +1 -1
  133. package/dist/extractors/rust.d.ts.map +1 -1
  134. package/dist/extractors/rust.js +58 -83
  135. package/dist/extractors/rust.js.map +1 -1
  136. package/dist/extractors/scala.d.ts +6 -0
  137. package/dist/extractors/scala.d.ts.map +1 -0
  138. package/dist/extractors/scala.js +269 -0
  139. package/dist/extractors/scala.js.map +1 -0
  140. package/dist/extractors/swift.d.ts +6 -0
  141. package/dist/extractors/swift.d.ts.map +1 -0
  142. package/dist/extractors/swift.js +275 -0
  143. package/dist/extractors/swift.js.map +1 -0
  144. package/dist/features/ast.d.ts +2 -0
  145. package/dist/features/ast.d.ts.map +1 -1
  146. package/dist/features/ast.js +9 -24
  147. package/dist/features/ast.js.map +1 -1
  148. package/dist/features/audit.d.ts.map +1 -1
  149. package/dist/features/audit.js +17 -21
  150. package/dist/features/audit.js.map +1 -1
  151. package/dist/features/branch-compare.d.ts.map +1 -1
  152. package/dist/features/branch-compare.js +47 -3
  153. package/dist/features/branch-compare.js.map +1 -1
  154. package/dist/features/cfg.d.ts +7 -1
  155. package/dist/features/cfg.d.ts.map +1 -1
  156. package/dist/features/cfg.js +118 -62
  157. package/dist/features/cfg.js.map +1 -1
  158. package/dist/features/check.d.ts.map +1 -1
  159. package/dist/features/check.js +79 -62
  160. package/dist/features/check.js.map +1 -1
  161. package/dist/features/complexity-query.d.ts.map +1 -1
  162. package/dist/features/complexity-query.js +142 -137
  163. package/dist/features/complexity-query.js.map +1 -1
  164. package/dist/features/complexity.d.ts +7 -1
  165. package/dist/features/complexity.d.ts.map +1 -1
  166. package/dist/features/complexity.js +62 -1
  167. package/dist/features/complexity.js.map +1 -1
  168. package/dist/features/dataflow.d.ts +7 -1
  169. package/dist/features/dataflow.d.ts.map +1 -1
  170. package/dist/features/dataflow.js +356 -188
  171. package/dist/features/dataflow.js.map +1 -1
  172. package/dist/features/graph-enrichment.d.ts.map +1 -1
  173. package/dist/features/graph-enrichment.js +117 -104
  174. package/dist/features/graph-enrichment.js.map +1 -1
  175. package/dist/features/sequence.d.ts.map +1 -1
  176. package/dist/features/sequence.js +25 -4
  177. package/dist/features/sequence.js.map +1 -1
  178. package/dist/features/structure-query.d.ts.map +1 -1
  179. package/dist/features/structure-query.js +29 -4
  180. package/dist/features/structure-query.js.map +1 -1
  181. package/dist/features/structure.d.ts.map +1 -1
  182. package/dist/features/structure.js +35 -15
  183. package/dist/features/structure.js.map +1 -1
  184. package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
  185. package/dist/graph/algorithms/leiden/adapter.js +88 -73
  186. package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
  187. package/dist/graph/algorithms/leiden/index.js +43 -28
  188. package/dist/graph/algorithms/leiden/index.js.map +1 -1
  189. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  190. package/dist/graph/algorithms/leiden/optimiser.js +90 -104
  191. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  192. package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
  193. package/dist/graph/algorithms/leiden/partition.js +89 -106
  194. package/dist/graph/algorithms/leiden/partition.js.map +1 -1
  195. package/dist/graph/model.d.ts +2 -0
  196. package/dist/graph/model.d.ts.map +1 -1
  197. package/dist/graph/model.js +20 -8
  198. package/dist/graph/model.js.map +1 -1
  199. package/dist/infrastructure/config.d.ts +0 -8
  200. package/dist/infrastructure/config.d.ts.map +1 -1
  201. package/dist/infrastructure/config.js +73 -62
  202. package/dist/infrastructure/config.js.map +1 -1
  203. package/dist/infrastructure/registry.d.ts +0 -8
  204. package/dist/infrastructure/registry.d.ts.map +1 -1
  205. package/dist/infrastructure/registry.js +12 -14
  206. package/dist/infrastructure/registry.js.map +1 -1
  207. package/dist/mcp/server.d.ts.map +1 -1
  208. package/dist/mcp/server.js +45 -36
  209. package/dist/mcp/server.js.map +1 -1
  210. package/dist/presentation/audit.d.ts.map +1 -1
  211. package/dist/presentation/audit.js +61 -57
  212. package/dist/presentation/audit.js.map +1 -1
  213. package/dist/presentation/branch-compare.d.ts.map +1 -1
  214. package/dist/presentation/branch-compare.js +56 -38
  215. package/dist/presentation/branch-compare.js.map +1 -1
  216. package/dist/presentation/check.d.ts.map +1 -1
  217. package/dist/presentation/check.js +30 -32
  218. package/dist/presentation/check.js.map +1 -1
  219. package/dist/presentation/colors.d.ts.map +1 -1
  220. package/dist/presentation/colors.js +2 -0
  221. package/dist/presentation/colors.js.map +1 -1
  222. package/dist/presentation/complexity.d.ts.map +1 -1
  223. package/dist/presentation/complexity.js +25 -19
  224. package/dist/presentation/complexity.js.map +1 -1
  225. package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
  226. package/dist/presentation/queries-cli/exports.js +15 -15
  227. package/dist/presentation/queries-cli/exports.js.map +1 -1
  228. package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
  229. package/dist/presentation/queries-cli/impact.js +29 -19
  230. package/dist/presentation/queries-cli/impact.js.map +1 -1
  231. package/dist/types.d.ts +182 -7
  232. package/dist/types.d.ts.map +1 -1
  233. package/grammars/tree-sitter-bash.wasm +0 -0
  234. package/grammars/tree-sitter-c.wasm +0 -0
  235. package/grammars/tree-sitter-cpp.wasm +0 -0
  236. package/grammars/tree-sitter-kotlin.wasm +0 -0
  237. package/grammars/tree-sitter-scala.wasm +0 -0
  238. package/grammars/tree-sitter-swift.wasm +0 -0
  239. package/package.json +13 -7
  240. package/src/ast-analysis/engine.ts +147 -138
  241. package/src/ast-analysis/visitors/ast-store-visitor.ts +15 -2
  242. package/src/ast-analysis/visitors/complexity-visitor.ts +11 -11
  243. package/src/db/connection.ts +90 -59
  244. package/src/db/index.ts +1 -0
  245. package/src/db/migrations.ts +36 -32
  246. package/src/domain/analysis/context.ts +73 -75
  247. package/src/domain/analysis/dependencies.ts +78 -68
  248. package/src/domain/analysis/exports.ts +45 -34
  249. package/src/domain/analysis/fn-impact.ts +67 -64
  250. package/src/domain/analysis/module-map.ts +103 -8
  251. package/src/domain/analysis/query-helpers.ts +35 -0
  252. package/src/domain/graph/builder/helpers.ts +12 -6
  253. package/src/domain/graph/builder/incremental.ts +3 -2
  254. package/src/domain/graph/builder/pipeline.ts +71 -3
  255. package/src/domain/graph/builder/stages/build-edges.ts +10 -75
  256. package/src/domain/graph/builder/stages/build-structure.ts +9 -7
  257. package/src/domain/graph/builder/stages/collect-files.ts +2 -2
  258. package/src/domain/graph/builder/stages/detect-changes.ts +7 -2
  259. package/src/domain/graph/builder/stages/finalize.ts +159 -125
  260. package/src/domain/graph/builder/stages/insert-nodes.ts +32 -21
  261. package/src/domain/graph/builder/stages/resolve-imports.ts +3 -2
  262. package/src/domain/graph/resolve.ts +34 -46
  263. package/src/domain/graph/watcher.ts +12 -14
  264. package/src/domain/parser.ts +168 -97
  265. package/src/domain/search/search/cli-formatter.ts +121 -94
  266. package/src/extractors/bash.ts +97 -0
  267. package/src/extractors/c.ts +212 -0
  268. package/src/extractors/cpp.ts +298 -0
  269. package/src/extractors/csharp.ts +53 -56
  270. package/src/extractors/go.ts +152 -134
  271. package/src/extractors/hcl.ts +6 -6
  272. package/src/extractors/helpers.ts +93 -1
  273. package/src/extractors/index.ts +6 -0
  274. package/src/extractors/java.ts +43 -48
  275. package/src/extractors/javascript.ts +328 -281
  276. package/src/extractors/kotlin.ts +293 -0
  277. package/src/extractors/php.ts +46 -40
  278. package/src/extractors/python.ts +81 -104
  279. package/src/extractors/ruby.ts +6 -13
  280. package/src/extractors/rust.ts +65 -85
  281. package/src/extractors/scala.ts +285 -0
  282. package/src/extractors/swift.ts +293 -0
  283. package/src/features/ast.ts +10 -25
  284. package/src/features/audit.ts +24 -20
  285. package/src/features/branch-compare.ts +51 -4
  286. package/src/features/cfg.ts +158 -65
  287. package/src/features/check.ts +90 -74
  288. package/src/features/complexity-query.ts +181 -163
  289. package/src/features/complexity.ts +64 -1
  290. package/src/features/dataflow.ts +462 -217
  291. package/src/features/graph-enrichment.ts +161 -117
  292. package/src/features/sequence.ts +27 -4
  293. package/src/features/structure-query.ts +43 -4
  294. package/src/features/structure.ts +50 -22
  295. package/src/graph/algorithms/leiden/adapter.ts +126 -71
  296. package/src/graph/algorithms/leiden/index.ts +67 -28
  297. package/src/graph/algorithms/leiden/optimiser.ts +114 -105
  298. package/src/graph/algorithms/leiden/partition.ts +131 -98
  299. package/src/graph/model.ts +19 -7
  300. package/src/infrastructure/config.ts +60 -58
  301. package/src/infrastructure/registry.ts +17 -14
  302. package/src/mcp/server.ts +46 -37
  303. package/src/presentation/audit.ts +72 -67
  304. package/src/presentation/branch-compare.ts +54 -50
  305. package/src/presentation/check.ts +34 -34
  306. package/src/presentation/colors.ts +2 -0
  307. package/src/presentation/complexity.ts +39 -33
  308. package/src/presentation/queries-cli/exports.ts +17 -17
  309. package/src/presentation/queries-cli/impact.ts +30 -22
  310. package/src/types.ts +189 -7
@@ -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,8 +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
- // Skip self parameters — matches native engine behaviour
231
- continue;
230
+ // Skip self — matches native engine behaviour
232
231
  } else if (param.type === 'parameter') {
233
232
  const pattern = param.childForFieldName('pattern');
234
233
  if (pattern) {
@@ -240,34 +239,16 @@ function extractRustParameters(paramListNode: TreeSitterNode | null): SubDeclara
240
239
  }
241
240
 
242
241
  function extractStructFields(structNode: TreeSitterNode): SubDeclaration[] {
243
- const fields: SubDeclaration[] = [];
244
- const fieldList =
245
- structNode.childForFieldName('body') || findChild(structNode, 'field_declaration_list');
246
- if (!fieldList) return fields;
247
- for (let i = 0; i < fieldList.childCount; i++) {
248
- const field = fieldList.child(i);
249
- if (!field || field.type !== 'field_declaration') continue;
250
- const nameNode = field.childForFieldName('name');
251
- if (nameNode) {
252
- fields.push({ name: nameNode.text, kind: 'property', line: field.startPosition.row + 1 });
253
- }
254
- }
255
- return fields;
242
+ return extractBodyMembers(
243
+ structNode,
244
+ ['body', 'field_declaration_list'],
245
+ 'field_declaration',
246
+ 'property',
247
+ );
256
248
  }
257
249
 
258
250
  function extractEnumVariants(enumNode: TreeSitterNode): SubDeclaration[] {
259
- const variants: SubDeclaration[] = [];
260
- const body = enumNode.childForFieldName('body') || findChild(enumNode, 'enum_variant_list');
261
- if (!body) return variants;
262
- for (let i = 0; i < body.childCount; i++) {
263
- const variant = body.child(i);
264
- if (!variant || variant.type !== 'enum_variant') continue;
265
- const nameNode = variant.childForFieldName('name');
266
- if (nameNode) {
267
- variants.push({ name: nameNode.text, kind: 'constant', line: variant.startPosition.row + 1 });
268
- }
269
- }
270
- return variants;
251
+ return extractBodyMembers(enumNode, ['body', 'enum_variant_list'], 'enum_variant', 'constant');
271
252
  }
272
253
 
273
254
  function extractRustTypeMap(node: TreeSitterNode, ctx: ExtractorOutput): void {
@@ -283,7 +264,7 @@ function extractRustTypeMapDepth(node: TreeSitterNode, ctx: ExtractorOutput, dep
283
264
  const typeNode = node.childForFieldName('type');
284
265
  if (pattern && pattern.type === 'identifier' && typeNode) {
285
266
  const typeName = extractRustTypeName(typeNode);
286
- 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);
287
268
  }
288
269
  }
289
270
 
@@ -295,7 +276,7 @@ function extractRustTypeMapDepth(node: TreeSitterNode, ctx: ExtractorOutput, dep
295
276
  const name = pattern.type === 'identifier' ? pattern.text : null;
296
277
  if (name && name !== 'self' && name !== '&self' && name !== '&mut self') {
297
278
  const typeName = extractRustTypeName(typeNode);
298
- if (typeName) ctx.typeMap?.set(name, { type: typeName, confidence: 0.9 });
279
+ if (typeName && ctx.typeMap) setTypeMapEntry(ctx.typeMap, name, typeName, 0.9);
299
280
  }
300
281
  }
301
282
  }
@@ -328,56 +309,55 @@ function extractRustTypeName(typeNode: TreeSitterNode): string | null {
328
309
  return null;
329
310
  }
330
311
 
331
- function extractRustUsePath(node: TreeSitterNode | null): { source: string; names: string[] }[] {
332
- if (!node) return [];
333
-
334
- if (node.type === 'use_list') {
335
- const results: { source: string; names: string[] }[] = [];
336
- for (let i = 0; i < node.childCount; i++) {
337
- 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);
338
323
  }
339
- return results;
340
324
  }
325
+ return names;
326
+ }
341
327
 
342
- if (node.type === 'scoped_use_list') {
343
- const pathNode = node.childForFieldName('path');
344
- const listNode = node.childForFieldName('list');
345
- const prefix = pathNode ? pathNode.text : '';
346
- if (listNode) {
347
- const names: string[] = [];
348
- for (let i = 0; i < listNode.childCount; i++) {
349
- const child = listNode.child(i);
350
- if (
351
- child &&
352
- (child.type === 'identifier' || child.type === 'use_as_clause' || child.type === 'self')
353
- ) {
354
- const name =
355
- child.type === 'use_as_clause'
356
- ? (child.childForFieldName('alias') || child.childForFieldName('name'))?.text
357
- : child.text;
358
- if (name) names.push(name);
359
- }
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)));
360
336
  }
361
- return [{ source: prefix, names }];
337
+ return results;
362
338
  }
363
- return [{ source: prefix, names: [] }];
364
- }
365
-
366
- if (node.type === 'use_as_clause') {
367
- const name = node.childForFieldName('alias') || node.childForFieldName('name');
368
- return [{ source: node.text, names: name ? [name.text] : [] }];
369
- }
370
-
371
- if (node.type === 'use_wildcard') {
372
- const pathNode = node.childForFieldName('path');
373
- return [{ source: pathNode ? pathNode.text : '*', names: ['*'] }];
374
- }
375
-
376
- if (node.type === 'scoped_identifier' || node.type === 'identifier') {
377
- const text = node.text;
378
- const lastName = text.split('::').pop() ?? text;
379
- 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 [];
380
362
  }
381
-
382
- return [];
383
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
+ }