@optave/codegraph 3.10.0 → 3.11.1

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 (312) hide show
  1. package/README.md +40 -33
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +91 -60
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/rules/index.d.ts.map +1 -1
  6. package/dist/ast-analysis/rules/index.js +77 -0
  7. package/dist/ast-analysis/rules/index.js.map +1 -1
  8. package/dist/ast-analysis/visitor-utils.d.ts +3 -0
  9. package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
  10. package/dist/ast-analysis/visitor-utils.js +83 -49
  11. package/dist/ast-analysis/visitor-utils.js.map +1 -1
  12. package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
  13. package/dist/ast-analysis/visitors/ast-store-visitor.js +78 -62
  14. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  15. package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
  16. package/dist/ast-analysis/visitors/dataflow-visitor.js +61 -42
  17. package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
  18. package/dist/cli/commands/audit.js +1 -1
  19. package/dist/cli/commands/audit.js.map +1 -1
  20. package/dist/cli/commands/build.d.ts.map +1 -1
  21. package/dist/cli/commands/build.js +2 -0
  22. package/dist/cli/commands/build.js.map +1 -1
  23. package/dist/cli/commands/check.js +1 -1
  24. package/dist/cli/commands/check.js.map +1 -1
  25. package/dist/cli/commands/children.js +1 -1
  26. package/dist/cli/commands/children.js.map +1 -1
  27. package/dist/cli/commands/diff-impact.js +1 -1
  28. package/dist/cli/commands/diff-impact.js.map +1 -1
  29. package/dist/cli/commands/embed.d.ts.map +1 -1
  30. package/dist/cli/commands/embed.js +49 -4
  31. package/dist/cli/commands/embed.js.map +1 -1
  32. package/dist/cli/commands/roles.js +1 -1
  33. package/dist/cli/commands/roles.js.map +1 -1
  34. package/dist/cli/commands/structure.js +1 -1
  35. package/dist/cli/commands/structure.js.map +1 -1
  36. package/dist/cli/shared/options.js +1 -1
  37. package/dist/cli/shared/options.js.map +1 -1
  38. package/dist/db/connection.d.ts.map +1 -1
  39. package/dist/db/connection.js +8 -0
  40. package/dist/db/connection.js.map +1 -1
  41. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  42. package/dist/domain/analysis/dependencies.js +106 -80
  43. package/dist/domain/analysis/dependencies.js.map +1 -1
  44. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  45. package/dist/domain/analysis/fn-impact.js +77 -52
  46. package/dist/domain/analysis/fn-impact.js.map +1 -1
  47. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  48. package/dist/domain/analysis/module-map.js +132 -121
  49. package/dist/domain/analysis/module-map.js.map +1 -1
  50. package/dist/domain/graph/builder/helpers.d.ts +4 -4
  51. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  52. package/dist/domain/graph/builder/helpers.js +47 -33
  53. package/dist/domain/graph/builder/helpers.js.map +1 -1
  54. package/dist/domain/graph/builder/incremental.d.ts +6 -6
  55. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  56. package/dist/domain/graph/builder/incremental.js +148 -99
  57. package/dist/domain/graph/builder/incremental.js.map +1 -1
  58. package/dist/domain/graph/builder/pipeline.d.ts +1 -0
  59. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  60. package/dist/domain/graph/builder/pipeline.js +23 -637
  61. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  62. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  63. package/dist/domain/graph/builder/stages/build-edges.js +141 -98
  64. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  65. package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
  66. package/dist/domain/graph/builder/stages/build-structure.js +82 -65
  67. package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
  68. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  69. package/dist/domain/graph/builder/stages/detect-changes.js +84 -56
  70. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  71. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  72. package/dist/domain/graph/builder/stages/finalize.js +60 -51
  73. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  74. package/dist/domain/graph/builder/stages/insert-nodes.d.ts +8 -6
  75. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  76. package/dist/domain/graph/builder/stages/insert-nodes.js +107 -122
  77. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  78. package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts +14 -0
  79. package/dist/domain/graph/builder/stages/native-db-lifecycle.d.ts.map +1 -0
  80. package/dist/domain/graph/builder/stages/native-db-lifecycle.js +77 -0
  81. package/dist/domain/graph/builder/stages/native-db-lifecycle.js.map +1 -0
  82. package/dist/domain/graph/builder/stages/native-orchestrator.d.ts +62 -0
  83. package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -0
  84. package/dist/domain/graph/builder/stages/native-orchestrator.js +747 -0
  85. package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -0
  86. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  87. package/dist/domain/graph/builder/stages/resolve-imports.js +73 -22
  88. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  89. package/dist/domain/graph/cycles.d.ts +6 -4
  90. package/dist/domain/graph/cycles.d.ts.map +1 -1
  91. package/dist/domain/graph/cycles.js +50 -55
  92. package/dist/domain/graph/cycles.js.map +1 -1
  93. package/dist/domain/graph/journal.d.ts.map +1 -1
  94. package/dist/domain/graph/journal.js +89 -70
  95. package/dist/domain/graph/journal.js.map +1 -1
  96. package/dist/domain/graph/watcher.d.ts.map +1 -1
  97. package/dist/domain/graph/watcher.js +28 -20
  98. package/dist/domain/graph/watcher.js.map +1 -1
  99. package/dist/domain/parser.d.ts +12 -23
  100. package/dist/domain/parser.d.ts.map +1 -1
  101. package/dist/domain/parser.js +153 -80
  102. package/dist/domain/parser.js.map +1 -1
  103. package/dist/domain/search/generator.d.ts +3 -1
  104. package/dist/domain/search/generator.d.ts.map +1 -1
  105. package/dist/domain/search/generator.js +68 -45
  106. package/dist/domain/search/generator.js.map +1 -1
  107. package/dist/domain/search/models.d.ts +18 -0
  108. package/dist/domain/search/models.d.ts.map +1 -1
  109. package/dist/domain/search/models.js +72 -4
  110. package/dist/domain/search/models.js.map +1 -1
  111. package/dist/domain/search/search/hybrid.d.ts.map +1 -1
  112. package/dist/domain/search/search/hybrid.js +49 -40
  113. package/dist/domain/search/search/hybrid.js.map +1 -1
  114. package/dist/domain/search/search/semantic.d.ts.map +1 -1
  115. package/dist/domain/search/search/semantic.js +69 -49
  116. package/dist/domain/search/search/semantic.js.map +1 -1
  117. package/dist/domain/wasm-worker-entry.js +209 -137
  118. package/dist/domain/wasm-worker-entry.js.map +1 -1
  119. package/dist/extractors/c.js +25 -6
  120. package/dist/extractors/c.js.map +1 -1
  121. package/dist/extractors/cpp.js +47 -6
  122. package/dist/extractors/cpp.js.map +1 -1
  123. package/dist/extractors/cuda.js +90 -14
  124. package/dist/extractors/cuda.js.map +1 -1
  125. package/dist/extractors/elixir.js +108 -4
  126. package/dist/extractors/elixir.js.map +1 -1
  127. package/dist/extractors/erlang.js +56 -20
  128. package/dist/extractors/erlang.js.map +1 -1
  129. package/dist/extractors/fsharp.d.ts +7 -0
  130. package/dist/extractors/fsharp.d.ts.map +1 -1
  131. package/dist/extractors/fsharp.js +94 -0
  132. package/dist/extractors/fsharp.js.map +1 -1
  133. package/dist/extractors/gleam.d.ts.map +1 -1
  134. package/dist/extractors/gleam.js +29 -33
  135. package/dist/extractors/gleam.js.map +1 -1
  136. package/dist/extractors/groovy.js +41 -1
  137. package/dist/extractors/groovy.js.map +1 -1
  138. package/dist/extractors/haskell.js +48 -4
  139. package/dist/extractors/haskell.js.map +1 -1
  140. package/dist/extractors/helpers.d.ts +79 -1
  141. package/dist/extractors/helpers.d.ts.map +1 -1
  142. package/dist/extractors/helpers.js +137 -0
  143. package/dist/extractors/helpers.js.map +1 -1
  144. package/dist/extractors/java.d.ts.map +1 -1
  145. package/dist/extractors/java.js +37 -49
  146. package/dist/extractors/java.js.map +1 -1
  147. package/dist/extractors/javascript.d.ts.map +1 -1
  148. package/dist/extractors/javascript.js +44 -44
  149. package/dist/extractors/javascript.js.map +1 -1
  150. package/dist/extractors/julia.js +198 -74
  151. package/dist/extractors/julia.js.map +1 -1
  152. package/dist/extractors/kotlin.js +4 -0
  153. package/dist/extractors/kotlin.js.map +1 -1
  154. package/dist/extractors/objc.js +184 -47
  155. package/dist/extractors/objc.js.map +1 -1
  156. package/dist/extractors/python.js +7 -4
  157. package/dist/extractors/python.js.map +1 -1
  158. package/dist/extractors/r.d.ts.map +1 -1
  159. package/dist/extractors/r.js +103 -87
  160. package/dist/extractors/r.js.map +1 -1
  161. package/dist/extractors/scala.d.ts.map +1 -1
  162. package/dist/extractors/scala.js +18 -32
  163. package/dist/extractors/scala.js.map +1 -1
  164. package/dist/extractors/solidity.d.ts.map +1 -1
  165. package/dist/extractors/solidity.js +55 -69
  166. package/dist/extractors/solidity.js.map +1 -1
  167. package/dist/extractors/verilog.js +80 -15
  168. package/dist/extractors/verilog.js.map +1 -1
  169. package/dist/features/boundaries.d.ts.map +1 -1
  170. package/dist/features/boundaries.js +49 -39
  171. package/dist/features/boundaries.js.map +1 -1
  172. package/dist/features/cfg.d.ts.map +1 -1
  173. package/dist/features/cfg.js +90 -63
  174. package/dist/features/cfg.js.map +1 -1
  175. package/dist/features/check.d.ts.map +1 -1
  176. package/dist/features/check.js +43 -34
  177. package/dist/features/check.js.map +1 -1
  178. package/dist/features/cochange.d.ts.map +1 -1
  179. package/dist/features/cochange.js +68 -56
  180. package/dist/features/cochange.js.map +1 -1
  181. package/dist/features/complexity.d.ts.map +1 -1
  182. package/dist/features/complexity.js +105 -75
  183. package/dist/features/complexity.js.map +1 -1
  184. package/dist/features/dataflow.d.ts.map +1 -1
  185. package/dist/features/dataflow.js +37 -29
  186. package/dist/features/dataflow.js.map +1 -1
  187. package/dist/features/flow.d.ts.map +1 -1
  188. package/dist/features/flow.js +31 -22
  189. package/dist/features/flow.js.map +1 -1
  190. package/dist/features/graph-enrichment.d.ts.map +1 -1
  191. package/dist/features/graph-enrichment.js +77 -70
  192. package/dist/features/graph-enrichment.js.map +1 -1
  193. package/dist/features/owners.d.ts +17 -26
  194. package/dist/features/owners.d.ts.map +1 -1
  195. package/dist/features/owners.js +120 -109
  196. package/dist/features/owners.js.map +1 -1
  197. package/dist/features/sequence.d.ts.map +1 -1
  198. package/dist/features/sequence.js +59 -54
  199. package/dist/features/sequence.js.map +1 -1
  200. package/dist/features/structure-query.d.ts.map +1 -1
  201. package/dist/features/structure-query.js +60 -60
  202. package/dist/features/structure-query.js.map +1 -1
  203. package/dist/features/structure.js +28 -36
  204. package/dist/features/structure.js.map +1 -1
  205. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  206. package/dist/graph/algorithms/leiden/optimiser.js +100 -69
  207. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  208. package/dist/graph/classifiers/roles.d.ts.map +1 -1
  209. package/dist/graph/classifiers/roles.js +63 -59
  210. package/dist/graph/classifiers/roles.js.map +1 -1
  211. package/dist/infrastructure/config.d.ts +1 -1
  212. package/dist/infrastructure/config.d.ts.map +1 -1
  213. package/dist/infrastructure/config.js +1 -1
  214. package/dist/infrastructure/config.js.map +1 -1
  215. package/dist/mcp/tool-registry.d.ts.map +1 -1
  216. package/dist/mcp/tool-registry.js +4 -0
  217. package/dist/mcp/tool-registry.js.map +1 -1
  218. package/dist/mcp/tools/semantic-search.d.ts +1 -0
  219. package/dist/mcp/tools/semantic-search.d.ts.map +1 -1
  220. package/dist/mcp/tools/semantic-search.js +1 -0
  221. package/dist/mcp/tools/semantic-search.js.map +1 -1
  222. package/dist/presentation/cfg.d.ts.map +1 -1
  223. package/dist/presentation/cfg.js +44 -29
  224. package/dist/presentation/cfg.js.map +1 -1
  225. package/dist/presentation/flow.d.ts.map +1 -1
  226. package/dist/presentation/flow.js +58 -38
  227. package/dist/presentation/flow.js.map +1 -1
  228. package/dist/types.d.ts +16 -2
  229. package/dist/types.d.ts.map +1 -1
  230. package/grammars/tree-sitter-erlang.wasm +0 -0
  231. package/grammars/tree-sitter-fsharp.wasm +0 -0
  232. package/grammars/tree-sitter-fsharp_signature.wasm +0 -0
  233. package/grammars/tree-sitter-gleam.wasm +0 -0
  234. package/package.json +10 -10
  235. package/src/ast-analysis/engine.ts +145 -61
  236. package/src/ast-analysis/rules/index.ts +87 -0
  237. package/src/ast-analysis/visitor-utils.ts +86 -46
  238. package/src/ast-analysis/visitors/ast-store-visitor.ts +104 -69
  239. package/src/ast-analysis/visitors/dataflow-visitor.ts +86 -47
  240. package/src/cli/commands/audit.ts +1 -1
  241. package/src/cli/commands/build.ts +2 -0
  242. package/src/cli/commands/check.ts +1 -1
  243. package/src/cli/commands/children.ts +1 -1
  244. package/src/cli/commands/diff-impact.ts +1 -1
  245. package/src/cli/commands/embed.ts +54 -4
  246. package/src/cli/commands/roles.ts +1 -1
  247. package/src/cli/commands/structure.ts +1 -1
  248. package/src/cli/shared/options.ts +1 -1
  249. package/src/db/connection.ts +8 -0
  250. package/src/domain/analysis/dependencies.ts +166 -85
  251. package/src/domain/analysis/fn-impact.ts +120 -50
  252. package/src/domain/analysis/module-map.ts +175 -140
  253. package/src/domain/graph/builder/helpers.ts +85 -76
  254. package/src/domain/graph/builder/incremental.ts +223 -131
  255. package/src/domain/graph/builder/pipeline.ts +32 -785
  256. package/src/domain/graph/builder/stages/build-edges.ts +207 -142
  257. package/src/domain/graph/builder/stages/build-structure.ts +115 -82
  258. package/src/domain/graph/builder/stages/detect-changes.ts +107 -64
  259. package/src/domain/graph/builder/stages/finalize.ts +72 -70
  260. package/src/domain/graph/builder/stages/insert-nodes.ts +154 -120
  261. package/src/domain/graph/builder/stages/native-db-lifecycle.ts +74 -0
  262. package/src/domain/graph/builder/stages/native-orchestrator.ts +942 -0
  263. package/src/domain/graph/builder/stages/resolve-imports.ts +79 -25
  264. package/src/domain/graph/cycles.ts +51 -49
  265. package/src/domain/graph/journal.ts +84 -69
  266. package/src/domain/graph/watcher.ts +29 -25
  267. package/src/domain/parser.ts +170 -67
  268. package/src/domain/search/generator.ts +132 -74
  269. package/src/domain/search/models.ts +75 -4
  270. package/src/domain/search/search/hybrid.ts +53 -42
  271. package/src/domain/search/search/semantic.ts +105 -65
  272. package/src/domain/wasm-worker-entry.ts +243 -153
  273. package/src/extractors/c.ts +27 -8
  274. package/src/extractors/cpp.ts +50 -8
  275. package/src/extractors/cuda.ts +90 -16
  276. package/src/extractors/elixir.ts +103 -4
  277. package/src/extractors/erlang.ts +63 -20
  278. package/src/extractors/fsharp.ts +104 -0
  279. package/src/extractors/gleam.ts +40 -39
  280. package/src/extractors/groovy.ts +45 -1
  281. package/src/extractors/haskell.ts +45 -4
  282. package/src/extractors/helpers.ts +205 -1
  283. package/src/extractors/java.ts +42 -45
  284. package/src/extractors/javascript.ts +44 -43
  285. package/src/extractors/julia.ts +191 -77
  286. package/src/extractors/kotlin.ts +4 -0
  287. package/src/extractors/objc.ts +171 -47
  288. package/src/extractors/python.ts +5 -3
  289. package/src/extractors/r.ts +104 -82
  290. package/src/extractors/scala.ts +24 -36
  291. package/src/extractors/solidity.ts +59 -78
  292. package/src/extractors/verilog.ts +83 -15
  293. package/src/features/boundaries.ts +64 -46
  294. package/src/features/cfg.ts +145 -74
  295. package/src/features/check.ts +60 -43
  296. package/src/features/cochange.ts +95 -72
  297. package/src/features/complexity.ts +134 -79
  298. package/src/features/dataflow.ts +57 -34
  299. package/src/features/flow.ts +48 -24
  300. package/src/features/graph-enrichment.ts +105 -70
  301. package/src/features/owners.ts +186 -146
  302. package/src/features/sequence.ts +99 -69
  303. package/src/features/structure-query.ts +94 -79
  304. package/src/features/structure.ts +56 -56
  305. package/src/graph/algorithms/leiden/optimiser.ts +142 -87
  306. package/src/graph/classifiers/roles.ts +64 -54
  307. package/src/infrastructure/config.ts +1 -1
  308. package/src/mcp/tool-registry.ts +5 -0
  309. package/src/mcp/tools/semantic-search.ts +2 -0
  310. package/src/presentation/cfg.ts +48 -32
  311. package/src/presentation/flow.ts +100 -52
  312. package/src/types.ts +16 -1
@@ -1,5 +1,5 @@
1
1
  import type { ExtractorOutput, SubDeclaration, TreeSitterNode, TreeSitterTree } from '../types.js';
2
- import { findChild, nodeEndLine } from './helpers.js';
2
+ import { findChild, nodeEndLine, nodeStartLine, pushCall, pushImport } from './helpers.js';
3
3
 
4
4
  /**
5
5
  * Extract symbols from Julia files.
@@ -76,29 +76,61 @@ function handleModuleDef(node: TreeSitterNode, ctx: ExtractorOutput): string | n
76
76
  ctx.definitions.push({
77
77
  name: nameNode.text,
78
78
  kind: 'module',
79
- line: node.startPosition.row + 1,
79
+ line: nodeStartLine(node),
80
80
  endLine: nodeEndLine(node),
81
81
  });
82
82
 
83
83
  return nameNode.text;
84
84
  }
85
85
 
86
+ function qualifyName(base: string, currentModule: string | null): string {
87
+ // For qualified names (`function Base.show ... end` inside `module Foo`,
88
+ // or short-form `Foo.bar(x, y) = x + y` inside `module Outer`), the LHS
89
+ // is a `scoped_identifier` already containing the qualifier — skip the
90
+ // module prefix to avoid producing `Foo.Base.show` / `Outer.Foo.bar`.
91
+ if (currentModule && !base.includes('.')) return `${currentModule}.${base}`;
92
+ return base;
93
+ }
94
+
95
+ /**
96
+ * Extract the call_expression from a function/macro definition's signature.
97
+ *
98
+ * tree-sitter-julia wraps the signature in a `signature` node whose direct
99
+ * children include the `call_expression` for the function name and parameters.
100
+ * `findChild` only inspects direct children, so we unwrap one level explicitly.
101
+ * Without this step, `findChild(node, 'call_expression')` on a
102
+ * `function_definition` would match the *body's* first call_expression
103
+ * (e.g. `println(...)` inside the body) instead of the signature.
104
+ *
105
+ * Grammar assumption: every `function_definition` / `macro_definition` emits a
106
+ * `signature` child in the current tree-sitter-julia grammar. The fallback to
107
+ * `findChild(node, 'call_expression')` exists only as a defensive measure for
108
+ * grammar drift — if it ever fires on a real definition, that fallback would
109
+ * silently match the first body call_expression and mis-record the function
110
+ * name. Callers must therefore treat a missing `signature` as a parser/grammar
111
+ * mismatch worth investigating, not as a routine code path.
112
+ */
113
+ function signatureCall(node: TreeSitterNode): TreeSitterNode | null {
114
+ const sig = findChild(node, 'signature');
115
+ if (sig) return findChild(sig, 'call_expression');
116
+ return findChild(node, 'call_expression');
117
+ }
118
+
86
119
  function handleFunctionDef(
87
120
  node: TreeSitterNode,
88
121
  ctx: ExtractorOutput,
89
122
  currentModule: string | null,
90
123
  ): void {
91
- // function_definition may have a call_expression child as the signature
92
- const callSig = findChild(node, 'call_expression');
124
+ const callSig = signatureCall(node);
93
125
  if (callSig) {
94
126
  const funcNameNode = callSig.child(0);
95
127
  if (funcNameNode) {
96
- const name = currentModule ? `${currentModule}.${funcNameNode.text}` : funcNameNode.text;
128
+ const name = qualifyName(funcNameNode.text, currentModule);
97
129
  const params = extractJuliaParams(callSig);
98
130
  ctx.definitions.push({
99
131
  name,
100
132
  kind: 'function',
101
- line: node.startPosition.row + 1,
133
+ line: nodeStartLine(node),
102
134
  endLine: nodeEndLine(node),
103
135
  children: params.length > 0 ? params : undefined,
104
136
  });
@@ -110,11 +142,10 @@ function handleFunctionDef(
110
142
  const nameNode = node.childForFieldName('name') || findChild(node, 'identifier');
111
143
  if (!nameNode) return;
112
144
 
113
- const name = currentModule ? `${currentModule}.${nameNode.text}` : nameNode.text;
114
145
  ctx.definitions.push({
115
- name,
146
+ name: qualifyName(nameNode.text, currentModule),
116
147
  kind: 'function',
117
- line: node.startPosition.row + 1,
148
+ line: nodeStartLine(node),
118
149
  endLine: nodeEndLine(node),
119
150
  });
120
151
  }
@@ -133,29 +164,86 @@ function handleAssignment(
133
164
  const funcNameNode = lhs.child(0);
134
165
  if (!funcNameNode) return;
135
166
 
136
- const name = currentModule ? `${currentModule}.${funcNameNode.text}` : funcNameNode.text;
137
167
  const params = extractJuliaParams(lhs);
138
168
 
139
169
  ctx.definitions.push({
140
- name,
170
+ name: qualifyName(funcNameNode.text, currentModule),
141
171
  kind: 'function',
142
- line: node.startPosition.row + 1,
172
+ line: nodeStartLine(node),
143
173
  endLine: nodeEndLine(node),
144
174
  children: params.length > 0 ? params : undefined,
145
175
  });
146
176
  }
147
177
  }
148
178
 
179
+ /**
180
+ * Locate the base-name identifier within a `type_head` node.
181
+ *
182
+ * Handles plain identifiers, `Name <: Super` binary expressions, and
183
+ * parameterized forms like `Name{T}` / `Name{T} <: Super{T,1}` by recursing
184
+ * into wrapper kinds the Julia grammar actually emits for type heads
185
+ * (binary expressions, parametrized type expressions, parameterized
186
+ * identifiers). Returns `null` when no identifier can be located — callers
187
+ * should skip emitting a definition in that case.
188
+ *
189
+ * Note: `type_parameter_list` / `type_argument_list` are intentionally
190
+ * excluded — Julia's grammar uses `curly_expression` for `{T}` constructs,
191
+ * not those node kinds. Including them would risk recursing into a
192
+ * type-parameter list and returning a type variable (e.g. `T`) instead of
193
+ * the struct name if `findBaseName` were ever called on a node lacking a
194
+ * direct `identifier` child.
195
+ */
196
+ const TYPE_HEAD_WRAPPERS: ReadonlySet<string> = new Set([
197
+ 'binary_expression',
198
+ 'parametrized_type_expression',
199
+ 'parameterized_identifier',
200
+ ]);
201
+
202
+ function findBaseName(node: TreeSitterNode): TreeSitterNode | null {
203
+ if (node.type === 'identifier') return node;
204
+ const direct = findChild(node, 'identifier');
205
+ if (direct) return direct;
206
+ for (let i = 0; i < node.childCount; i++) {
207
+ const child = node.child(i);
208
+ if (!child) continue;
209
+ if (TYPE_HEAD_WRAPPERS.has(child.type)) {
210
+ const found = findBaseName(child);
211
+ if (found) return found;
212
+ }
213
+ }
214
+ return null;
215
+ }
216
+
149
217
  function handleStructDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
150
218
  // struct_definition: struct type_head fields... end
219
+ // type_head wraps the name and optional supertype. The name may be a
220
+ // bare `identifier`, a parameterized form (e.g. `Vec{T}`), or either
221
+ // of those nested inside a `binary_expression` (`Name <: Super`).
151
222
  const typeHead = findChild(node, 'type_head');
152
- const nameNode = typeHead
153
- ? (findChild(typeHead, 'identifier') ?? typeHead)
154
- : findChild(node, 'identifier');
223
+ if (!typeHead) return;
224
+
225
+ let nameNode: TreeSitterNode | null;
226
+ let supertypeNode: TreeSitterNode | null = null;
227
+
228
+ const binary = findChild(typeHead, 'binary_expression');
229
+ if (binary) {
230
+ // Walk into each side of the binary expression to find the base-name
231
+ // identifier — handles parameterized forms like `Vec{T} <: AbstractArray{T,1}`.
232
+ const sides: TreeSitterNode[] = [];
233
+ for (let i = 0; i < binary.childCount; i++) {
234
+ const c = binary.child(i);
235
+ if (c && c.type !== 'operator') sides.push(c);
236
+ }
237
+ nameNode = sides[0] ? findBaseName(sides[0]) : null;
238
+ supertypeNode = sides[1] ? findBaseName(sides[1]) : null;
239
+ } else {
240
+ nameNode = findBaseName(typeHead);
241
+ }
242
+
155
243
  if (!nameNode) return;
244
+ const structName = nameNode.text;
156
245
 
157
246
  const children: SubDeclaration[] = [];
158
- // Fields are typed_expression children of struct_definition
159
247
  for (let i = 0; i < node.childCount; i++) {
160
248
  const child = node.child(i);
161
249
  if (!child) continue;
@@ -165,51 +253,49 @@ function handleStructDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
165
253
  children.push({
166
254
  name: fieldName.text,
167
255
  kind: 'property',
168
- line: child.startPosition.row + 1,
256
+ line: nodeStartLine(child),
169
257
  });
170
258
  }
171
- }
172
- // Plain identifier fields (no type annotation)
173
- if (child.type === 'identifier' && child !== nameNode && typeHead && child !== typeHead) {
174
- children.push({ name: child.text, kind: 'property', line: child.startPosition.row + 1 });
259
+ } else if (child.type === 'identifier') {
260
+ // Plain identifier fields (no type annotation) appear as direct
261
+ // identifier children of struct_definition. The type_head is a
262
+ // separate node so there is nothing to filter out here.
263
+ children.push({ name: child.text, kind: 'property', line: nodeStartLine(child) });
175
264
  }
176
265
  }
177
266
 
178
- // Check for supertype in type_head (Point <: AbstractPoint)
179
- if (typeHead) {
180
- const subtypeExpr = findChild(typeHead, 'subtype_expression');
181
- if (subtypeExpr) {
182
- // Find the supertype identifier
183
- for (let i = 0; i < subtypeExpr.childCount; i++) {
184
- const child = subtypeExpr.child(i);
185
- if (child?.type === 'identifier' && i > 0) {
186
- ctx.classes.push({
187
- name: nameNode.text,
188
- extends: child.text,
189
- line: node.startPosition.row + 1,
190
- });
191
- }
192
- }
193
- }
267
+ if (supertypeNode) {
268
+ ctx.classes.push({
269
+ name: structName,
270
+ extends: supertypeNode.text,
271
+ line: nodeStartLine(node),
272
+ });
194
273
  }
195
274
 
196
275
  ctx.definitions.push({
197
- name: nameNode.text,
276
+ name: structName,
198
277
  kind: 'struct',
199
- line: node.startPosition.row + 1,
278
+ line: nodeStartLine(node),
200
279
  endLine: nodeEndLine(node),
201
280
  children: children.length > 0 ? children : undefined,
202
281
  });
203
282
  }
204
283
 
205
284
  function handleAbstractDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
206
- const nameNode = node.childForFieldName('name') || findChild(node, 'identifier');
285
+ // abstract_definition: `abstract type` type_head `end`
286
+ // The identifier is nested inside `type_head` — possibly wrapped in a
287
+ // `Name <: Super` binary_expression or a `Name{T,...}` parameterized form.
288
+ // Mirror handleStructDef and skip rather than emit a garbled name when no
289
+ // base identifier can be located.
290
+ const typeHead = findChild(node, 'type_head');
291
+ if (!typeHead) return;
292
+ const nameNode = findBaseName(typeHead);
207
293
  if (!nameNode) return;
208
294
 
209
295
  ctx.definitions.push({
210
296
  name: nameNode.text,
211
297
  kind: 'type',
212
- line: node.startPosition.row + 1,
298
+ line: nodeStartLine(node),
213
299
  endLine: nodeEndLine(node),
214
300
  });
215
301
  }
@@ -219,69 +305,108 @@ function handleMacroDef(
219
305
  ctx: ExtractorOutput,
220
306
  currentModule: string | null,
221
307
  ): void {
222
- const nameNode = node.childForFieldName('name') || findChild(node, 'identifier');
308
+ // macro_definition: `macro` signature/call_expression body `end`.
309
+ // The name lives in the same shape as a function signature — unwrap via
310
+ // signatureCall so we don't pick up an identifier from the body (e.g.
311
+ // `macro mymac(x) x end` would otherwise resolve to `@x`).
312
+ const callSig = signatureCall(node);
313
+ const nameNode =
314
+ callSig?.child(0) ?? node.childForFieldName('name') ?? findChild(node, 'identifier');
223
315
  if (!nameNode) return;
224
316
 
225
- const name = currentModule ? `${currentModule}.@${nameNode.text}` : `@${nameNode.text}`;
317
+ const base = nameNode.text;
318
+ const name = currentModule ? `${currentModule}.@${base}` : `@${base}`;
226
319
  ctx.definitions.push({
227
320
  name,
228
321
  kind: 'function',
229
- line: node.startPosition.row + 1,
322
+ line: nodeStartLine(node),
230
323
  endLine: nodeEndLine(node),
231
324
  });
232
325
  }
233
326
 
234
327
  function handleImport(node: TreeSitterNode, ctx: ExtractorOutput): void {
328
+ // tree-sitter-julia shapes:
329
+ // `using LinearAlgebra` → using_statement [ using, identifier ]
330
+ // `import Foo.Bar` → import_statement [ import, scoped_identifier ]
331
+ // `import Base: show` → import_statement [ import, selected_import[Base, show] ]
332
+ // `import Foo.Bar: baz` → import_statement [ import, selected_import[scoped_identifier, baz] ]
235
333
  const names: string[] = [];
236
334
  let source = '';
237
335
 
238
336
  for (let i = 0; i < node.childCount; i++) {
239
337
  const child = node.child(i);
240
338
  if (!child) continue;
241
- if (
242
- child.type === 'identifier' ||
243
- child.type === 'scoped_identifier' ||
244
- child.type === 'selected_import'
245
- ) {
246
- if (!source) source = child.text;
247
- names.push(child.text.split('.').pop() || child.text);
339
+ if (child.type === 'identifier' || child.type === 'scoped_identifier') {
340
+ const txt = child.text;
341
+ if (!source) source = txt;
342
+ names.push(txt.split('.').pop() || txt);
343
+ } else if (child.type === 'selected_import') {
344
+ // First identifier-bearing node is the source module; the rest are
345
+ // imported names. The module may itself be a `scoped_identifier`
346
+ // (e.g. `import Foo.Bar: baz`) — handle it alongside bare
347
+ // `identifier` and use the trailing segment as the display name,
348
+ // mirroring the outer loop.
349
+ let first = true;
350
+ for (let j = 0; j < child.childCount; j++) {
351
+ const part = child.child(j);
352
+ if (!part) continue;
353
+ if (part.type !== 'identifier' && part.type !== 'scoped_identifier') continue;
354
+ const txt = part.text;
355
+ if (first) {
356
+ if (!source) source = txt;
357
+ first = false;
358
+ } else {
359
+ names.push(txt.split('.').pop() || txt);
360
+ }
361
+ }
248
362
  }
249
363
  }
250
364
 
251
365
  if (source) {
252
- ctx.imports.push({
253
- source,
254
- names: names.length > 0 ? names : [source],
255
- line: node.startPosition.row + 1,
256
- });
366
+ // pushImport falls back to source basename for empty `names`. Julia module
367
+ // sources have no `/` separator, so the basename equals `source` — matching
368
+ // the previous explicit `[source]` fallback.
369
+ pushImport(ctx, node, source, names);
257
370
  }
258
371
  }
259
372
 
260
373
  function handleCall(node: TreeSitterNode, ctx: ExtractorOutput): void {
261
374
  // Don't record if parent is assignment LHS (that's a function definition)
262
375
  if (node.parent?.type === 'assignment' && node === node.parent.child(0)) return;
263
- // Don't record if parent is function_definition (that's a signature)
264
- if (node.parent?.type === 'function_definition') return;
376
+ // Skip when this call is the signature of a function/macro definition.
377
+ // tree-sitter-julia wraps the signature in a `signature` node whose parent
378
+ // is `function_definition` or `macro_definition`. Body calls (e.g.
379
+ // `println(name)` inside `function greet ... end`) appear as descendants of
380
+ // the body, not as direct children of `signature`, so they are unaffected.
381
+ if (node.parent?.type === 'signature') {
382
+ const grand = node.parent.parent;
383
+ if (grand?.type === 'function_definition' || grand?.type === 'macro_definition') return;
384
+ }
265
385
 
266
386
  const funcNode = node.child(0);
267
387
  if (!funcNode) return;
268
388
 
269
389
  if (funcNode.type === 'identifier') {
270
- ctx.calls.push({ name: funcNode.text, line: node.startPosition.row + 1 });
390
+ pushCall(ctx, node, funcNode.text);
271
391
  } else if (funcNode.type === 'field_expression' || funcNode.type === 'scoped_identifier') {
272
392
  const parts = funcNode.text.split('.');
273
393
  if (parts.length >= 2) {
274
- ctx.calls.push({
275
- name: parts[parts.length - 1]!,
394
+ pushCall(ctx, node, parts[parts.length - 1]!, {
276
395
  receiver: parts.slice(0, -1).join('.'),
277
- line: node.startPosition.row + 1,
278
396
  });
279
397
  } else {
280
- ctx.calls.push({ name: funcNode.text, line: node.startPosition.row + 1 });
398
+ pushCall(ctx, node, funcNode.text);
281
399
  }
282
400
  }
283
401
  }
284
402
 
403
+ const JULIA_PARAM_WRAPPER_TYPES = new Set([
404
+ 'typed_parameter',
405
+ 'typed_expression',
406
+ 'optional_parameter',
407
+ 'default_parameter',
408
+ ]);
409
+
285
410
  function extractJuliaParams(callExpr: TreeSitterNode): SubDeclaration[] {
286
411
  const params: SubDeclaration[] = [];
287
412
  const argList = findChild(callExpr, 'argument_list') || findChild(callExpr, 'tuple_expression');
@@ -291,25 +416,14 @@ function extractJuliaParams(callExpr: TreeSitterNode): SubDeclaration[] {
291
416
  const child = argList.child(i);
292
417
  if (!child) continue;
293
418
  if (child.type === 'identifier') {
294
- params.push({ name: child.text, kind: 'parameter', line: child.startPosition.row + 1 });
295
- }
296
- if (child.type === 'typed_parameter' || child.type === 'typed_expression') {
297
- const nameNode = findChild(child, 'identifier');
298
- if (nameNode) {
299
- params.push({
300
- name: nameNode.text,
301
- kind: 'parameter',
302
- line: child.startPosition.row + 1,
303
- });
304
- }
305
- }
306
- if (child.type === 'optional_parameter' || child.type === 'default_parameter') {
419
+ params.push({ name: child.text, kind: 'parameter', line: nodeStartLine(child) });
420
+ } else if (JULIA_PARAM_WRAPPER_TYPES.has(child.type)) {
307
421
  const nameNode = findChild(child, 'identifier');
308
422
  if (nameNode) {
309
423
  params.push({
310
424
  name: nameNode.text,
311
425
  kind: 'parameter',
312
- line: child.startPosition.row + 1,
426
+ line: nodeStartLine(child),
313
427
  });
314
428
  }
315
429
  }
@@ -147,11 +147,13 @@ function collectKotlinMethods(node: TreeSitterNode, className: string, ctx: Extr
147
147
  if (!child || child.type !== 'function_declaration') continue;
148
148
  const methName = findChild(child, 'simple_identifier');
149
149
  if (methName) {
150
+ const params = extractKotlinParameters(child);
150
151
  ctx.definitions.push({
151
152
  name: `${className}.${methName.text}`,
152
153
  kind: 'method',
153
154
  line: child.startPosition.row + 1,
154
155
  endLine: child.endPosition.row + 1,
156
+ children: params.length > 0 ? params : undefined,
155
157
  visibility: extractModifierVisibility(child),
156
158
  });
157
159
  }
@@ -214,11 +216,13 @@ function handleKotlinObjectDecl(node: TreeSitterNode, ctx: ExtractorOutput): voi
214
216
  if (child && child.type === 'function_declaration') {
215
217
  const methName = findChild(child, 'simple_identifier');
216
218
  if (methName) {
219
+ const params = extractKotlinParameters(child);
217
220
  ctx.definitions.push({
218
221
  name: `${nameNode.text}.${methName.text}`,
219
222
  kind: 'method',
220
223
  line: child.startPosition.row + 1,
221
224
  endLine: child.endPosition.row + 1,
225
+ children: params.length > 0 ? params : undefined,
222
226
  visibility: extractModifierVisibility(child),
223
227
  });
224
228
  }