@optave/codegraph 3.5.0 → 3.7.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 (346) hide show
  1. package/README.md +47 -21
  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 +206 -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/dart.d.ts +6 -0
  103. package/dist/extractors/dart.d.ts.map +1 -0
  104. package/dist/extractors/dart.js +277 -0
  105. package/dist/extractors/dart.js.map +1 -0
  106. package/dist/extractors/elixir.d.ts +9 -0
  107. package/dist/extractors/elixir.d.ts.map +1 -0
  108. package/dist/extractors/elixir.js +223 -0
  109. package/dist/extractors/elixir.js.map +1 -0
  110. package/dist/extractors/go.d.ts.map +1 -1
  111. package/dist/extractors/go.js +126 -130
  112. package/dist/extractors/go.js.map +1 -1
  113. package/dist/extractors/haskell.d.ts +8 -0
  114. package/dist/extractors/haskell.d.ts.map +1 -0
  115. package/dist/extractors/haskell.js +217 -0
  116. package/dist/extractors/haskell.js.map +1 -0
  117. package/dist/extractors/hcl.js +6 -6
  118. package/dist/extractors/hcl.js.map +1 -1
  119. package/dist/extractors/helpers.d.ts +32 -1
  120. package/dist/extractors/helpers.d.ts.map +1 -1
  121. package/dist/extractors/helpers.js +74 -0
  122. package/dist/extractors/helpers.js.map +1 -1
  123. package/dist/extractors/index.d.ts +12 -0
  124. package/dist/extractors/index.d.ts.map +1 -1
  125. package/dist/extractors/index.js +12 -0
  126. package/dist/extractors/index.js.map +1 -1
  127. package/dist/extractors/java.d.ts.map +1 -1
  128. package/dist/extractors/java.js +32 -47
  129. package/dist/extractors/java.js.map +1 -1
  130. package/dist/extractors/javascript.d.ts.map +1 -1
  131. package/dist/extractors/javascript.js +306 -292
  132. package/dist/extractors/javascript.js.map +1 -1
  133. package/dist/extractors/kotlin.d.ts +6 -0
  134. package/dist/extractors/kotlin.d.ts.map +1 -0
  135. package/dist/extractors/kotlin.js +275 -0
  136. package/dist/extractors/kotlin.js.map +1 -0
  137. package/dist/extractors/lua.d.ts +6 -0
  138. package/dist/extractors/lua.d.ts.map +1 -0
  139. package/dist/extractors/lua.js +162 -0
  140. package/dist/extractors/lua.js.map +1 -0
  141. package/dist/extractors/ocaml.d.ts +6 -0
  142. package/dist/extractors/ocaml.d.ts.map +1 -0
  143. package/dist/extractors/ocaml.js +236 -0
  144. package/dist/extractors/ocaml.js.map +1 -0
  145. package/dist/extractors/php.d.ts.map +1 -1
  146. package/dist/extractors/php.js +39 -44
  147. package/dist/extractors/php.js.map +1 -1
  148. package/dist/extractors/python.d.ts.map +1 -1
  149. package/dist/extractors/python.js +75 -93
  150. package/dist/extractors/python.js.map +1 -1
  151. package/dist/extractors/ruby.js +6 -13
  152. package/dist/extractors/ruby.js.map +1 -1
  153. package/dist/extractors/rust.d.ts.map +1 -1
  154. package/dist/extractors/rust.js +58 -83
  155. package/dist/extractors/rust.js.map +1 -1
  156. package/dist/extractors/scala.d.ts +6 -0
  157. package/dist/extractors/scala.d.ts.map +1 -0
  158. package/dist/extractors/scala.js +269 -0
  159. package/dist/extractors/scala.js.map +1 -0
  160. package/dist/extractors/swift.d.ts +6 -0
  161. package/dist/extractors/swift.d.ts.map +1 -0
  162. package/dist/extractors/swift.js +275 -0
  163. package/dist/extractors/swift.js.map +1 -0
  164. package/dist/extractors/zig.d.ts +9 -0
  165. package/dist/extractors/zig.d.ts.map +1 -0
  166. package/dist/extractors/zig.js +276 -0
  167. package/dist/extractors/zig.js.map +1 -0
  168. package/dist/features/ast.d.ts +2 -0
  169. package/dist/features/ast.d.ts.map +1 -1
  170. package/dist/features/ast.js +9 -24
  171. package/dist/features/ast.js.map +1 -1
  172. package/dist/features/audit.d.ts.map +1 -1
  173. package/dist/features/audit.js +17 -21
  174. package/dist/features/audit.js.map +1 -1
  175. package/dist/features/branch-compare.d.ts.map +1 -1
  176. package/dist/features/branch-compare.js +47 -3
  177. package/dist/features/branch-compare.js.map +1 -1
  178. package/dist/features/cfg.d.ts +7 -1
  179. package/dist/features/cfg.d.ts.map +1 -1
  180. package/dist/features/cfg.js +72 -61
  181. package/dist/features/cfg.js.map +1 -1
  182. package/dist/features/check.d.ts.map +1 -1
  183. package/dist/features/check.js +79 -62
  184. package/dist/features/check.js.map +1 -1
  185. package/dist/features/complexity-query.d.ts.map +1 -1
  186. package/dist/features/complexity-query.js +142 -137
  187. package/dist/features/complexity-query.js.map +1 -1
  188. package/dist/features/complexity.d.ts +7 -1
  189. package/dist/features/complexity.d.ts.map +1 -1
  190. package/dist/features/complexity.js +62 -1
  191. package/dist/features/complexity.js.map +1 -1
  192. package/dist/features/dataflow.d.ts +7 -1
  193. package/dist/features/dataflow.d.ts.map +1 -1
  194. package/dist/features/dataflow.js +356 -188
  195. package/dist/features/dataflow.js.map +1 -1
  196. package/dist/features/graph-enrichment.d.ts.map +1 -1
  197. package/dist/features/graph-enrichment.js +117 -104
  198. package/dist/features/graph-enrichment.js.map +1 -1
  199. package/dist/features/sequence.d.ts.map +1 -1
  200. package/dist/features/sequence.js +25 -4
  201. package/dist/features/sequence.js.map +1 -1
  202. package/dist/features/structure-query.d.ts.map +1 -1
  203. package/dist/features/structure-query.js +29 -4
  204. package/dist/features/structure-query.js.map +1 -1
  205. package/dist/features/structure.d.ts.map +1 -1
  206. package/dist/features/structure.js +35 -15
  207. package/dist/features/structure.js.map +1 -1
  208. package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
  209. package/dist/graph/algorithms/leiden/adapter.js +88 -73
  210. package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
  211. package/dist/graph/algorithms/leiden/index.js +43 -28
  212. package/dist/graph/algorithms/leiden/index.js.map +1 -1
  213. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  214. package/dist/graph/algorithms/leiden/optimiser.js +90 -104
  215. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  216. package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
  217. package/dist/graph/algorithms/leiden/partition.js +89 -106
  218. package/dist/graph/algorithms/leiden/partition.js.map +1 -1
  219. package/dist/graph/model.d.ts +2 -0
  220. package/dist/graph/model.d.ts.map +1 -1
  221. package/dist/graph/model.js +20 -8
  222. package/dist/graph/model.js.map +1 -1
  223. package/dist/infrastructure/config.d.ts +0 -8
  224. package/dist/infrastructure/config.d.ts.map +1 -1
  225. package/dist/infrastructure/config.js +73 -62
  226. package/dist/infrastructure/config.js.map +1 -1
  227. package/dist/infrastructure/registry.d.ts +0 -8
  228. package/dist/infrastructure/registry.d.ts.map +1 -1
  229. package/dist/infrastructure/registry.js +12 -14
  230. package/dist/infrastructure/registry.js.map +1 -1
  231. package/dist/mcp/server.d.ts.map +1 -1
  232. package/dist/mcp/server.js +45 -36
  233. package/dist/mcp/server.js.map +1 -1
  234. package/dist/presentation/audit.d.ts.map +1 -1
  235. package/dist/presentation/audit.js +61 -57
  236. package/dist/presentation/audit.js.map +1 -1
  237. package/dist/presentation/branch-compare.d.ts.map +1 -1
  238. package/dist/presentation/branch-compare.js +56 -38
  239. package/dist/presentation/branch-compare.js.map +1 -1
  240. package/dist/presentation/check.d.ts.map +1 -1
  241. package/dist/presentation/check.js +30 -32
  242. package/dist/presentation/check.js.map +1 -1
  243. package/dist/presentation/colors.d.ts.map +1 -1
  244. package/dist/presentation/colors.js +2 -0
  245. package/dist/presentation/colors.js.map +1 -1
  246. package/dist/presentation/complexity.d.ts.map +1 -1
  247. package/dist/presentation/complexity.js +25 -19
  248. package/dist/presentation/complexity.js.map +1 -1
  249. package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
  250. package/dist/presentation/queries-cli/exports.js +15 -15
  251. package/dist/presentation/queries-cli/exports.js.map +1 -1
  252. package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
  253. package/dist/presentation/queries-cli/impact.js +29 -19
  254. package/dist/presentation/queries-cli/impact.js.map +1 -1
  255. package/dist/types.d.ts +182 -7
  256. package/dist/types.d.ts.map +1 -1
  257. package/grammars/tree-sitter-bash.wasm +0 -0
  258. package/grammars/tree-sitter-c.wasm +0 -0
  259. package/grammars/tree-sitter-cpp.wasm +0 -0
  260. package/grammars/tree-sitter-dart.wasm +0 -0
  261. package/grammars/tree-sitter-elixir.wasm +0 -0
  262. package/grammars/tree-sitter-haskell.wasm +0 -0
  263. package/grammars/tree-sitter-kotlin.wasm +0 -0
  264. package/grammars/tree-sitter-lua.wasm +0 -0
  265. package/grammars/tree-sitter-ocaml.wasm +0 -0
  266. package/grammars/tree-sitter-scala.wasm +0 -0
  267. package/grammars/tree-sitter-swift.wasm +0 -0
  268. package/grammars/tree-sitter-zig.wasm +0 -0
  269. package/package.json +19 -7
  270. package/src/ast-analysis/engine.ts +147 -138
  271. package/src/ast-analysis/visitors/ast-store-visitor.ts +15 -2
  272. package/src/ast-analysis/visitors/complexity-visitor.ts +11 -11
  273. package/src/db/connection.ts +90 -59
  274. package/src/db/index.ts +1 -0
  275. package/src/db/migrations.ts +36 -32
  276. package/src/domain/analysis/context.ts +73 -75
  277. package/src/domain/analysis/dependencies.ts +78 -68
  278. package/src/domain/analysis/exports.ts +45 -34
  279. package/src/domain/analysis/fn-impact.ts +67 -64
  280. package/src/domain/analysis/module-map.ts +103 -8
  281. package/src/domain/analysis/query-helpers.ts +35 -0
  282. package/src/domain/graph/builder/helpers.ts +12 -6
  283. package/src/domain/graph/builder/incremental.ts +3 -2
  284. package/src/domain/graph/builder/pipeline.ts +71 -3
  285. package/src/domain/graph/builder/stages/build-edges.ts +10 -75
  286. package/src/domain/graph/builder/stages/build-structure.ts +9 -7
  287. package/src/domain/graph/builder/stages/collect-files.ts +2 -2
  288. package/src/domain/graph/builder/stages/detect-changes.ts +7 -2
  289. package/src/domain/graph/builder/stages/finalize.ts +159 -125
  290. package/src/domain/graph/builder/stages/insert-nodes.ts +32 -21
  291. package/src/domain/graph/builder/stages/resolve-imports.ts +3 -2
  292. package/src/domain/graph/resolve.ts +34 -46
  293. package/src/domain/graph/watcher.ts +12 -14
  294. package/src/domain/parser.ts +222 -97
  295. package/src/domain/search/search/cli-formatter.ts +121 -94
  296. package/src/extractors/bash.ts +97 -0
  297. package/src/extractors/c.ts +212 -0
  298. package/src/extractors/cpp.ts +298 -0
  299. package/src/extractors/csharp.ts +53 -56
  300. package/src/extractors/dart.ts +304 -0
  301. package/src/extractors/elixir.ts +251 -0
  302. package/src/extractors/go.ts +152 -134
  303. package/src/extractors/haskell.ts +235 -0
  304. package/src/extractors/hcl.ts +6 -6
  305. package/src/extractors/helpers.ts +93 -1
  306. package/src/extractors/index.ts +12 -0
  307. package/src/extractors/java.ts +43 -48
  308. package/src/extractors/javascript.ts +328 -281
  309. package/src/extractors/kotlin.ts +293 -0
  310. package/src/extractors/lua.ts +169 -0
  311. package/src/extractors/ocaml.ts +259 -0
  312. package/src/extractors/php.ts +46 -40
  313. package/src/extractors/python.ts +81 -104
  314. package/src/extractors/ruby.ts +6 -13
  315. package/src/extractors/rust.ts +65 -85
  316. package/src/extractors/scala.ts +285 -0
  317. package/src/extractors/swift.ts +293 -0
  318. package/src/extractors/zig.ts +294 -0
  319. package/src/features/ast.ts +10 -25
  320. package/src/features/audit.ts +24 -20
  321. package/src/features/branch-compare.ts +51 -4
  322. package/src/features/cfg.ts +113 -65
  323. package/src/features/check.ts +90 -74
  324. package/src/features/complexity-query.ts +181 -163
  325. package/src/features/complexity.ts +64 -1
  326. package/src/features/dataflow.ts +462 -217
  327. package/src/features/graph-enrichment.ts +161 -117
  328. package/src/features/sequence.ts +27 -4
  329. package/src/features/structure-query.ts +43 -4
  330. package/src/features/structure.ts +50 -22
  331. package/src/graph/algorithms/leiden/adapter.ts +126 -71
  332. package/src/graph/algorithms/leiden/index.ts +67 -28
  333. package/src/graph/algorithms/leiden/optimiser.ts +114 -105
  334. package/src/graph/algorithms/leiden/partition.ts +131 -98
  335. package/src/graph/model.ts +19 -7
  336. package/src/infrastructure/config.ts +60 -58
  337. package/src/infrastructure/registry.ts +17 -14
  338. package/src/mcp/server.ts +46 -37
  339. package/src/presentation/audit.ts +72 -67
  340. package/src/presentation/branch-compare.ts +54 -50
  341. package/src/presentation/check.ts +34 -34
  342. package/src/presentation/colors.ts +2 -0
  343. package/src/presentation/complexity.ts +39 -33
  344. package/src/presentation/queries-cli/exports.ts +17 -17
  345. package/src/presentation/queries-cli/impact.ts +30 -22
  346. package/src/types.ts +195 -7
@@ -6,7 +6,16 @@ import type {
6
6
  TreeSitterNode,
7
7
  TreeSitterTree,
8
8
  } from '../types.js';
9
- import { extractModifierVisibility, findChild, MAX_WALK_DEPTH, nodeEndLine } from './helpers.js';
9
+ import {
10
+ extractBodyMembers,
11
+ extractModifierVisibility,
12
+ findChild,
13
+ findParentNode,
14
+ lastPathSegment,
15
+ MAX_WALK_DEPTH,
16
+ nodeEndLine,
17
+ setTypeMapEntry,
18
+ } from './helpers.js';
10
19
 
11
20
  /**
12
21
  * Extract symbols from C# files.
@@ -208,7 +217,7 @@ function handleCsUsingDirective(node: TreeSitterNode, ctx: ExtractorOutput): voi
208
217
  findChild(node, 'identifier');
209
218
  if (!nameNode) return;
210
219
  const fullPath = nameNode.text;
211
- const lastName = fullPath.split('.').pop() ?? fullPath;
220
+ const lastName = lastPathSegment(fullPath, '.');
212
221
  ctx.imports.push({
213
222
  source: fullPath,
214
223
  names: [lastName],
@@ -246,22 +255,15 @@ function handleCsObjectCreation(node: TreeSitterNode, ctx: ExtractorOutput): voi
246
255
  if (typeName) ctx.calls.push({ name: typeName, line: node.startPosition.row + 1 });
247
256
  }
248
257
 
258
+ const CS_PARENT_TYPES = [
259
+ 'class_declaration',
260
+ 'struct_declaration',
261
+ 'interface_declaration',
262
+ 'enum_declaration',
263
+ 'record_declaration',
264
+ ] as const;
249
265
  function findCSharpParentType(node: TreeSitterNode): string | null {
250
- let current = node.parent;
251
- while (current) {
252
- if (
253
- current.type === 'class_declaration' ||
254
- current.type === 'struct_declaration' ||
255
- current.type === 'interface_declaration' ||
256
- current.type === 'enum_declaration' ||
257
- current.type === 'record_declaration'
258
- ) {
259
- const nameNode = current.childForFieldName('name');
260
- return nameNode ? nameNode.text : null;
261
- }
262
- current = current.parent;
263
- }
264
- return null;
266
+ return findParentNode(node, CS_PARENT_TYPES);
265
267
  }
266
268
 
267
269
  // ── Child extraction helpers ────────────────────────────────────────────────
@@ -307,19 +309,12 @@ function extractCSharpClassFields(classNode: TreeSitterNode): SubDeclaration[] {
307
309
  }
308
310
 
309
311
  function extractCSharpEnumMembers(enumNode: TreeSitterNode): SubDeclaration[] {
310
- const constants: SubDeclaration[] = [];
311
- const body =
312
- enumNode.childForFieldName('body') || findChild(enumNode, 'enum_member_declaration_list');
313
- if (!body) return constants;
314
- for (let i = 0; i < body.childCount; i++) {
315
- const member = body.child(i);
316
- if (!member || member.type !== 'enum_member_declaration') continue;
317
- const nameNode = member.childForFieldName('name');
318
- if (nameNode) {
319
- constants.push({ name: nameNode.text, kind: 'constant', line: member.startPosition.row + 1 });
320
- }
321
- }
322
- return constants;
312
+ return extractBodyMembers(
313
+ enumNode,
314
+ ['body', 'enum_member_declaration_list'],
315
+ 'enum_member_declaration',
316
+ 'constant',
317
+ );
323
318
  }
324
319
 
325
320
  // ── Type map extraction ──────────────────────────────────────────────────────
@@ -328,6 +323,31 @@ function extractCSharpTypeMap(node: TreeSitterNode, ctx: ExtractorOutput): void
328
323
  extractCSharpTypeMapDepth(node, ctx, 0);
329
324
  }
330
325
 
326
+ /** Extract type info from a variable_declaration node (local vars with explicit types). */
327
+ function handleCSharpVarDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
328
+ const typeNode = node.childForFieldName('type') || node.child(0);
329
+ if (!typeNode || typeNode.type === 'var_keyword') return;
330
+ const typeName = extractCSharpTypeName(typeNode);
331
+ if (!typeName) return;
332
+ for (let i = 0; i < node.childCount; i++) {
333
+ const child = node.child(i);
334
+ if (!child || child.type !== 'variable_declarator') continue;
335
+ const nameNode = child.childForFieldName('name') || child.child(0);
336
+ if (nameNode && nameNode.type === 'identifier' && ctx.typeMap) {
337
+ setTypeMapEntry(ctx.typeMap, nameNode.text, typeName, 0.9);
338
+ }
339
+ }
340
+ }
341
+
342
+ /** Extract type info from a parameter node. */
343
+ function handleCSharpParam(node: TreeSitterNode, ctx: ExtractorOutput): void {
344
+ const typeNode = node.childForFieldName('type');
345
+ const nameNode = node.childForFieldName('name');
346
+ if (!typeNode || !nameNode) return;
347
+ const typeName = extractCSharpTypeName(typeNode);
348
+ if (typeName && ctx.typeMap) setTypeMapEntry(ctx.typeMap, nameNode.text, typeName, 0.9);
349
+ }
350
+
331
351
  function extractCSharpTypeMapDepth(
332
352
  node: TreeSitterNode,
333
353
  ctx: ExtractorOutput,
@@ -335,33 +355,10 @@ function extractCSharpTypeMapDepth(
335
355
  ): void {
336
356
  if (depth >= MAX_WALK_DEPTH) return;
337
357
 
338
- // local_declaration_statement → variable_declaration → type + variable_declarator(s)
339
358
  if (node.type === 'variable_declaration') {
340
- const typeNode = node.childForFieldName('type') || node.child(0);
341
- if (typeNode && typeNode.type !== 'var_keyword') {
342
- const typeName = extractCSharpTypeName(typeNode);
343
- if (typeName) {
344
- for (let i = 0; i < node.childCount; i++) {
345
- const child = node.child(i);
346
- if (child && child.type === 'variable_declarator') {
347
- const nameNode = child.childForFieldName('name') || child.child(0);
348
- if (nameNode && nameNode.type === 'identifier') {
349
- ctx.typeMap?.set(nameNode.text, { type: typeName, confidence: 0.9 });
350
- }
351
- }
352
- }
353
- }
354
- }
355
- }
356
-
357
- // Method/constructor parameter: parameter node has type + name fields
358
- if (node.type === 'parameter') {
359
- const typeNode = node.childForFieldName('type');
360
- const nameNode = node.childForFieldName('name');
361
- if (typeNode && nameNode) {
362
- const typeName = extractCSharpTypeName(typeNode);
363
- if (typeName) ctx.typeMap?.set(nameNode.text, { type: typeName, confidence: 0.9 });
364
- }
359
+ handleCSharpVarDecl(node, ctx);
360
+ } else if (node.type === 'parameter') {
361
+ handleCSharpParam(node, ctx);
365
362
  }
366
363
 
367
364
  for (let i = 0; i < node.childCount; i++) {
@@ -0,0 +1,304 @@
1
+ import type { ExtractorOutput, SubDeclaration, TreeSitterNode, TreeSitterTree } from '../types.js';
2
+ import { findChild, nodeEndLine } from './helpers.js';
3
+
4
+ /**
5
+ * Extract symbols from Dart files.
6
+ */
7
+ export function extractDartSymbols(tree: TreeSitterTree, _filePath: string): ExtractorOutput {
8
+ const ctx: ExtractorOutput = {
9
+ definitions: [],
10
+ calls: [],
11
+ imports: [],
12
+ classes: [],
13
+ exports: [],
14
+ typeMap: new Map(),
15
+ };
16
+
17
+ walkDartNode(tree.rootNode, ctx);
18
+ return ctx;
19
+ }
20
+
21
+ function walkDartNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
22
+ switch (node.type) {
23
+ case 'class_definition':
24
+ handleDartClass(node, ctx);
25
+ break;
26
+ case 'enum_declaration':
27
+ handleDartEnum(node, ctx);
28
+ break;
29
+ case 'mixin_declaration':
30
+ handleDartMixin(node, ctx);
31
+ break;
32
+ case 'extension_declaration':
33
+ handleDartExtension(node, ctx);
34
+ break;
35
+ case 'function_signature':
36
+ handleDartFunction(node, ctx);
37
+ break;
38
+ case 'method_signature':
39
+ handleDartMethodSig(node, ctx);
40
+ break;
41
+ case 'library_import':
42
+ handleDartImport(node, ctx);
43
+ break;
44
+ case 'constructor_invocation':
45
+ case 'new_expression':
46
+ handleDartConstructorCall(node, ctx);
47
+ break;
48
+ case 'type_alias':
49
+ handleDartTypeAlias(node, ctx);
50
+ break;
51
+ case 'selector':
52
+ handleDartSelector(node, ctx);
53
+ break;
54
+ }
55
+
56
+ for (let i = 0; i < node.childCount; i++) {
57
+ const child = node.child(i);
58
+ if (child) walkDartNode(child, ctx);
59
+ }
60
+ }
61
+
62
+ function handleDartClass(node: TreeSitterNode, ctx: ExtractorOutput): void {
63
+ const nameNode = node.childForFieldName('name');
64
+ if (!nameNode) return;
65
+ const name = nameNode.text;
66
+ const children: SubDeclaration[] = [];
67
+
68
+ const body = node.childForFieldName('body') || findChild(node, 'class_body');
69
+ if (body) {
70
+ extractDartClassMembers(body, name, ctx, children);
71
+ }
72
+
73
+ ctx.definitions.push({
74
+ name,
75
+ kind: 'class',
76
+ line: node.startPosition.row + 1,
77
+ endLine: nodeEndLine(node),
78
+ children: children.length > 0 ? children : undefined,
79
+ });
80
+
81
+ extractDartInheritance(node, name, ctx);
82
+ }
83
+
84
+ function extractDartClassMembers(
85
+ body: TreeSitterNode,
86
+ className: string,
87
+ ctx: ExtractorOutput,
88
+ children: SubDeclaration[],
89
+ ): void {
90
+ for (let i = 0; i < body.childCount; i++) {
91
+ const member = body.child(i);
92
+ if (!member) continue;
93
+
94
+ if (member.type === 'method_signature' || member.type === 'function_signature') {
95
+ const fnName = extractDartFunctionName(member);
96
+ if (fnName) {
97
+ ctx.definitions.push({
98
+ name: `${className}.${fnName}`,
99
+ kind: 'method',
100
+ line: member.startPosition.row + 1,
101
+ endLine: nodeEndLine(member),
102
+ });
103
+ }
104
+ } else if (member.type === 'declaration') {
105
+ // Field declarations
106
+ for (let j = 0; j < member.childCount; j++) {
107
+ const decl = member.child(j);
108
+ if (decl?.type === 'identifier') {
109
+ children.push({
110
+ name: decl.text,
111
+ kind: 'property',
112
+ line: member.startPosition.row + 1,
113
+ });
114
+ break;
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ function extractDartFunctionName(node: TreeSitterNode): string | null {
122
+ const nameNode = node.childForFieldName('name');
123
+ if (nameNode) return nameNode.text;
124
+
125
+ // Walk children for function_signature inside method_signature
126
+ for (let i = 0; i < node.childCount; i++) {
127
+ const child = node.child(i);
128
+ if (!child) continue;
129
+ if (
130
+ child.type === 'function_signature' ||
131
+ child.type === 'getter_signature' ||
132
+ child.type === 'setter_signature' ||
133
+ child.type === 'constructor_signature'
134
+ ) {
135
+ const name = child.childForFieldName('name');
136
+ if (name) return name.text;
137
+ }
138
+ }
139
+ return null;
140
+ }
141
+
142
+ function handleDartEnum(node: TreeSitterNode, ctx: ExtractorOutput): void {
143
+ const nameNode = node.childForFieldName('name');
144
+ if (!nameNode) return;
145
+
146
+ ctx.definitions.push({
147
+ name: nameNode.text,
148
+ kind: 'enum',
149
+ line: node.startPosition.row + 1,
150
+ endLine: nodeEndLine(node),
151
+ });
152
+ }
153
+
154
+ function handleDartMixin(node: TreeSitterNode, ctx: ExtractorOutput): void {
155
+ const nameNode = findChild(node, 'identifier');
156
+ if (!nameNode) return;
157
+
158
+ ctx.definitions.push({
159
+ name: nameNode.text,
160
+ kind: 'class',
161
+ line: node.startPosition.row + 1,
162
+ endLine: nodeEndLine(node),
163
+ });
164
+ }
165
+
166
+ function handleDartExtension(node: TreeSitterNode, ctx: ExtractorOutput): void {
167
+ const nameNode = node.childForFieldName('name');
168
+ if (!nameNode) return;
169
+
170
+ ctx.definitions.push({
171
+ name: nameNode.text,
172
+ kind: 'class',
173
+ line: node.startPosition.row + 1,
174
+ endLine: nodeEndLine(node),
175
+ });
176
+ }
177
+
178
+ function handleDartFunction(node: TreeSitterNode, ctx: ExtractorOutput): void {
179
+ // Skip methods already emitted by class handler
180
+ if (isInsideDartClass(node)) return;
181
+
182
+ const nameNode = node.childForFieldName('name');
183
+ if (!nameNode) return;
184
+
185
+ ctx.definitions.push({
186
+ name: nameNode.text,
187
+ kind: 'function',
188
+ line: node.startPosition.row + 1,
189
+ endLine: nodeEndLine(node),
190
+ });
191
+ }
192
+
193
+ function handleDartMethodSig(node: TreeSitterNode, ctx: ExtractorOutput): void {
194
+ if (isInsideDartClass(node)) return;
195
+ const fnName = extractDartFunctionName(node);
196
+ if (!fnName) return;
197
+
198
+ ctx.definitions.push({
199
+ name: fnName,
200
+ kind: 'function',
201
+ line: node.startPosition.row + 1,
202
+ endLine: nodeEndLine(node),
203
+ });
204
+ }
205
+
206
+ function isInsideDartClass(node: TreeSitterNode): boolean {
207
+ let current = node.parent;
208
+ while (current) {
209
+ if (
210
+ current.type === 'class_body' ||
211
+ current.type === 'class_definition' ||
212
+ current.type === 'enum_body' ||
213
+ current.type === 'mixin_declaration'
214
+ ) {
215
+ return true;
216
+ }
217
+ current = current.parent;
218
+ }
219
+ return false;
220
+ }
221
+
222
+ function handleDartImport(node: TreeSitterNode, ctx: ExtractorOutput): void {
223
+ const spec = findChild(node, 'import_specification');
224
+ if (!spec) return;
225
+
226
+ const uri = findChild(spec, 'configurable_uri') || findChild(spec, 'uri');
227
+ if (!uri) return;
228
+
229
+ const source = uri.text.replace(/^['"]|['"]$/g, '');
230
+ const names: string[] = [];
231
+
232
+ // Check for `as` alias
233
+ const alias = findChild(spec, 'identifier');
234
+ if (alias) names.push(alias.text);
235
+
236
+ ctx.imports.push({
237
+ source,
238
+ names: names.length > 0 ? names : [source.split('/').pop() || source],
239
+ line: node.startPosition.row + 1,
240
+ });
241
+ }
242
+
243
+ function handleDartConstructorCall(node: TreeSitterNode, ctx: ExtractorOutput): void {
244
+ const nameNode = findChild(node, 'type_identifier') || findChild(node, 'identifier');
245
+ if (!nameNode) return;
246
+
247
+ ctx.calls.push({
248
+ name: nameNode.text,
249
+ line: node.startPosition.row + 1,
250
+ });
251
+ }
252
+
253
+ function handleDartSelector(node: TreeSitterNode, ctx: ExtractorOutput): void {
254
+ // selector with argument_part represents a function call
255
+ const argPart = findChild(node, 'argument_part');
256
+ if (!argPart) return;
257
+
258
+ // Look for the identifier this selector belongs to
259
+ const unconditional = findChild(node, 'unconditional_assignable_selector');
260
+ if (unconditional) {
261
+ const id = findChild(unconditional, 'identifier');
262
+ if (id) {
263
+ ctx.calls.push({ name: id.text, line: node.startPosition.row + 1 });
264
+ }
265
+ }
266
+ }
267
+
268
+ function handleDartTypeAlias(node: TreeSitterNode, ctx: ExtractorOutput): void {
269
+ const nameNode = findChild(node, 'type_identifier') || findChild(node, 'identifier');
270
+ if (!nameNode) return;
271
+
272
+ ctx.definitions.push({
273
+ name: nameNode.text,
274
+ kind: 'type',
275
+ line: node.startPosition.row + 1,
276
+ endLine: nodeEndLine(node),
277
+ });
278
+ }
279
+
280
+ function extractDartInheritance(node: TreeSitterNode, name: string, ctx: ExtractorOutput): void {
281
+ const superclass = node.childForFieldName('superclass');
282
+ if (superclass) {
283
+ const typeName =
284
+ findChild(superclass, 'type_identifier') || findChild(superclass, 'identifier');
285
+ if (typeName) {
286
+ ctx.classes.push({ name, extends: typeName.text, line: node.startPosition.row + 1 });
287
+ }
288
+ }
289
+
290
+ const interfaces = node.childForFieldName('interfaces');
291
+ if (interfaces) {
292
+ for (let i = 0; i < interfaces.childCount; i++) {
293
+ const iface = interfaces.child(i);
294
+ if (!iface) continue;
295
+ const typeName =
296
+ iface.type === 'type_identifier'
297
+ ? iface
298
+ : findChild(iface, 'type_identifier') || findChild(iface, 'identifier');
299
+ if (typeName) {
300
+ ctx.classes.push({ name, implements: typeName.text, line: node.startPosition.row + 1 });
301
+ }
302
+ }
303
+ }
304
+ }
@@ -0,0 +1,251 @@
1
+ import type {
2
+ Call,
3
+ ExtractorOutput,
4
+ SubDeclaration,
5
+ TreeSitterNode,
6
+ TreeSitterTree,
7
+ } from '../types.js';
8
+ import { findChild, nodeEndLine } from './helpers.js';
9
+
10
+ /**
11
+ * Extract symbols from Elixir files.
12
+ *
13
+ * Elixir's tree-sitter grammar represents most constructs as generic `call` nodes.
14
+ * We distinguish modules, functions, imports etc. by the call target's identifier text.
15
+ */
16
+ export function extractElixirSymbols(tree: TreeSitterTree, _filePath: string): ExtractorOutput {
17
+ const ctx: ExtractorOutput = {
18
+ definitions: [],
19
+ calls: [],
20
+ imports: [],
21
+ classes: [],
22
+ exports: [],
23
+ typeMap: new Map(),
24
+ };
25
+
26
+ walkElixirNode(tree.rootNode, ctx, null);
27
+ return ctx;
28
+ }
29
+
30
+ function walkElixirNode(
31
+ node: TreeSitterNode,
32
+ ctx: ExtractorOutput,
33
+ currentModule: string | null,
34
+ ): void {
35
+ let nextModule = currentModule;
36
+
37
+ if (node.type === 'call') {
38
+ const target = node.childForFieldName('target');
39
+ if (target?.type === 'identifier' && target.text === 'defmodule') {
40
+ const args = findChild(node, 'arguments');
41
+ const aliasNode = args && findChild(args, 'alias');
42
+ if (aliasNode) nextModule = aliasNode.text;
43
+ }
44
+ handleElixirCall(node, ctx, nextModule);
45
+ }
46
+
47
+ for (let i = 0; i < node.childCount; i++) {
48
+ const child = node.child(i);
49
+ if (child) walkElixirNode(child, ctx, nextModule);
50
+ }
51
+ }
52
+
53
+ function handleElixirCall(
54
+ node: TreeSitterNode,
55
+ ctx: ExtractorOutput,
56
+ currentModule: string | null,
57
+ ): void {
58
+ const target = node.childForFieldName('target');
59
+ if (!target) return;
60
+
61
+ if (target.type === 'identifier') {
62
+ const keyword = target.text;
63
+ switch (keyword) {
64
+ case 'defmodule':
65
+ handleDefmodule(node, ctx);
66
+ return;
67
+ case 'def':
68
+ case 'defp':
69
+ handleDefFunction(node, ctx, currentModule, keyword === 'defp' ? 'private' : 'public');
70
+ return;
71
+ case 'defprotocol':
72
+ handleDefprotocol(node, ctx);
73
+ return;
74
+ case 'defimpl':
75
+ handleDefimpl(node, ctx);
76
+ return;
77
+ case 'import':
78
+ case 'use':
79
+ case 'require':
80
+ case 'alias':
81
+ handleElixirImport(node, ctx, keyword);
82
+ return;
83
+ default:
84
+ // Regular function call
85
+ ctx.calls.push({ name: keyword, line: node.startPosition.row + 1 });
86
+ return;
87
+ }
88
+ }
89
+
90
+ if (target.type === 'dot') {
91
+ handleDotCall(node, target, ctx);
92
+ }
93
+ }
94
+
95
+ function handleDefmodule(node: TreeSitterNode, ctx: ExtractorOutput): void {
96
+ const args = findChild(node, 'arguments');
97
+ if (!args) return;
98
+ const aliasNode = findChild(args, 'alias');
99
+ if (!aliasNode) return;
100
+ const name = aliasNode.text;
101
+
102
+ const children: SubDeclaration[] = [];
103
+ const doBlock = findChild(node, 'do_block');
104
+ if (doBlock) {
105
+ collectModuleMembers(doBlock, ctx, name, children);
106
+ }
107
+
108
+ ctx.definitions.push({
109
+ name,
110
+ kind: 'module',
111
+ line: node.startPosition.row + 1,
112
+ endLine: nodeEndLine(node),
113
+ children: children.length > 0 ? children : undefined,
114
+ });
115
+ }
116
+
117
+ function collectModuleMembers(
118
+ doBlock: TreeSitterNode,
119
+ _ctx: ExtractorOutput,
120
+ _moduleName: string,
121
+ children: SubDeclaration[],
122
+ ): void {
123
+ for (let i = 0; i < doBlock.childCount; i++) {
124
+ const child = doBlock.child(i);
125
+ if (!child || child.type !== 'call') continue;
126
+ const target = child.childForFieldName('target');
127
+ if (!target || target.type !== 'identifier') continue;
128
+
129
+ if (target.text === 'def' || target.text === 'defp') {
130
+ const fnName = extractFunctionName(child);
131
+ if (fnName) {
132
+ children.push({
133
+ name: fnName,
134
+ kind: 'property',
135
+ line: child.startPosition.row + 1,
136
+ });
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ function handleDefFunction(
143
+ node: TreeSitterNode,
144
+ ctx: ExtractorOutput,
145
+ currentModule: string | null,
146
+ visibility: 'public' | 'private',
147
+ ): void {
148
+ const fnName = extractFunctionName(node);
149
+ if (!fnName) return;
150
+
151
+ const fullName = currentModule ? `${currentModule}.${fnName}` : fnName;
152
+ const params = extractElixirParams(node);
153
+
154
+ ctx.definitions.push({
155
+ name: fullName,
156
+ kind: 'function',
157
+ line: node.startPosition.row + 1,
158
+ endLine: nodeEndLine(node),
159
+ visibility,
160
+ children: params.length > 0 ? params : undefined,
161
+ });
162
+ }
163
+
164
+ function extractFunctionName(defCallNode: TreeSitterNode): string | null {
165
+ const args = findChild(defCallNode, 'arguments');
166
+ if (!args) return null;
167
+
168
+ for (let i = 0; i < args.childCount; i++) {
169
+ const child = args.child(i);
170
+ if (!child) continue;
171
+ if (child.type === 'call') {
172
+ const target = child.childForFieldName('target');
173
+ if (target?.type === 'identifier') return target.text;
174
+ }
175
+ if (child.type === 'identifier') return child.text;
176
+ }
177
+ return null;
178
+ }
179
+
180
+ function extractElixirParams(defCallNode: TreeSitterNode): SubDeclaration[] {
181
+ const params: SubDeclaration[] = [];
182
+ const args = findChild(defCallNode, 'arguments');
183
+ if (!args) return params;
184
+
185
+ for (let i = 0; i < args.childCount; i++) {
186
+ const child = args.child(i);
187
+ if (!child || child.type !== 'call') continue;
188
+ const innerArgs = findChild(child, 'arguments');
189
+ if (!innerArgs) continue;
190
+ for (let j = 0; j < innerArgs.childCount; j++) {
191
+ const param = innerArgs.child(j);
192
+ if (!param) continue;
193
+ if (param.type === 'identifier') {
194
+ params.push({ name: param.text, kind: 'parameter', line: param.startPosition.row + 1 });
195
+ }
196
+ }
197
+ }
198
+ return params;
199
+ }
200
+
201
+ function handleDefprotocol(node: TreeSitterNode, ctx: ExtractorOutput): void {
202
+ const args = findChild(node, 'arguments');
203
+ if (!args) return;
204
+ const aliasNode = findChild(args, 'alias');
205
+ if (!aliasNode) return;
206
+
207
+ ctx.definitions.push({
208
+ name: aliasNode.text,
209
+ kind: 'interface',
210
+ line: node.startPosition.row + 1,
211
+ endLine: nodeEndLine(node),
212
+ });
213
+ }
214
+
215
+ function handleDefimpl(node: TreeSitterNode, ctx: ExtractorOutput): void {
216
+ const args = findChild(node, 'arguments');
217
+ if (!args) return;
218
+ const aliasNode = findChild(args, 'alias');
219
+ if (!aliasNode) return;
220
+
221
+ ctx.definitions.push({
222
+ name: aliasNode.text,
223
+ kind: 'class',
224
+ line: node.startPosition.row + 1,
225
+ endLine: nodeEndLine(node),
226
+ });
227
+ }
228
+
229
+ function handleElixirImport(node: TreeSitterNode, ctx: ExtractorOutput, keyword: string): void {
230
+ const args = findChild(node, 'arguments');
231
+ if (!args) return;
232
+ const aliasNode = findChild(args, 'alias');
233
+ if (!aliasNode) return;
234
+
235
+ ctx.imports.push({
236
+ source: aliasNode.text,
237
+ names: [keyword],
238
+ line: node.startPosition.row + 1,
239
+ });
240
+ }
241
+
242
+ function handleDotCall(node: TreeSitterNode, dotNode: TreeSitterNode, ctx: ExtractorOutput): void {
243
+ const call: Call = { name: '', line: node.startPosition.row + 1 };
244
+ const right = findChild(dotNode, 'identifier');
245
+ const left = findChild(dotNode, 'alias');
246
+
247
+ if (right) call.name = right.text;
248
+ if (left) call.receiver = left.text;
249
+
250
+ if (call.name) ctx.calls.push(call);
251
+ }