@optave/codegraph 3.8.0 → 3.8.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 (296) hide show
  1. package/README.md +9 -8
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +95 -87
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/metrics.d.ts +0 -3
  6. package/dist/ast-analysis/metrics.d.ts.map +1 -1
  7. package/dist/ast-analysis/metrics.js +30 -13
  8. package/dist/ast-analysis/metrics.js.map +1 -1
  9. package/dist/ast-analysis/shared.d.ts.map +1 -1
  10. package/dist/ast-analysis/shared.js +24 -19
  11. package/dist/ast-analysis/shared.js.map +1 -1
  12. package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
  13. package/dist/ast-analysis/visitor-utils.js +55 -39
  14. package/dist/ast-analysis/visitor-utils.js.map +1 -1
  15. package/dist/ast-analysis/visitor.d.ts.map +1 -1
  16. package/dist/ast-analysis/visitor.js +91 -70
  17. package/dist/ast-analysis/visitor.js.map +1 -1
  18. package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
  19. package/dist/ast-analysis/visitors/ast-store-visitor.js +54 -58
  20. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  21. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  22. package/dist/ast-analysis/visitors/complexity-visitor.js +32 -39
  23. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  24. package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
  25. package/dist/ast-analysis/visitors/dataflow-visitor.js +57 -38
  26. package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
  27. package/dist/cli/commands/watch.d.ts.map +1 -1
  28. package/dist/cli/commands/watch.js +16 -2
  29. package/dist/cli/commands/watch.js.map +1 -1
  30. package/dist/db/connection.d.ts.map +1 -1
  31. package/dist/db/connection.js +29 -26
  32. package/dist/db/connection.js.map +1 -1
  33. package/dist/db/query-builder.d.ts.map +1 -1
  34. package/dist/db/query-builder.js +16 -5
  35. package/dist/db/query-builder.js.map +1 -1
  36. package/dist/db/repository/base.d.ts +10 -0
  37. package/dist/db/repository/base.d.ts.map +1 -1
  38. package/dist/db/repository/base.js +17 -0
  39. package/dist/db/repository/base.js.map +1 -1
  40. package/dist/db/repository/native-repository.d.ts +6 -1
  41. package/dist/db/repository/native-repository.d.ts.map +1 -1
  42. package/dist/db/repository/native-repository.js +77 -1
  43. package/dist/db/repository/native-repository.js.map +1 -1
  44. package/dist/db/repository/nodes.d.ts.map +1 -1
  45. package/dist/db/repository/nodes.js +8 -4
  46. package/dist/db/repository/nodes.js.map +1 -1
  47. package/dist/db/repository/sqlite-repository.d.ts +3 -0
  48. package/dist/db/repository/sqlite-repository.d.ts.map +1 -1
  49. package/dist/db/repository/sqlite-repository.js +26 -0
  50. package/dist/db/repository/sqlite-repository.js.map +1 -1
  51. package/dist/domain/analysis/brief.d.ts.map +1 -1
  52. package/dist/domain/analysis/brief.js +13 -17
  53. package/dist/domain/analysis/brief.js.map +1 -1
  54. package/dist/domain/analysis/context.d.ts.map +1 -1
  55. package/dist/domain/analysis/context.js +14 -11
  56. package/dist/domain/analysis/context.js.map +1 -1
  57. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  58. package/dist/domain/analysis/dependencies.js +53 -52
  59. package/dist/domain/analysis/dependencies.js.map +1 -1
  60. package/dist/domain/analysis/fn-impact.d.ts +2 -7
  61. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  62. package/dist/domain/analysis/fn-impact.js +33 -31
  63. package/dist/domain/analysis/fn-impact.js.map +1 -1
  64. package/dist/domain/analysis/implementations.d.ts.map +1 -1
  65. package/dist/domain/analysis/implementations.js +11 -19
  66. package/dist/domain/analysis/implementations.js.map +1 -1
  67. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  68. package/dist/domain/analysis/module-map.js +55 -76
  69. package/dist/domain/analysis/module-map.js.map +1 -1
  70. package/dist/domain/analysis/query-helpers.d.ts +7 -0
  71. package/dist/domain/analysis/query-helpers.d.ts.map +1 -1
  72. package/dist/domain/analysis/query-helpers.js +15 -1
  73. package/dist/domain/analysis/query-helpers.js.map +1 -1
  74. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  75. package/dist/domain/graph/builder/pipeline.js +255 -105
  76. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  77. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  78. package/dist/domain/graph/builder/stages/build-edges.js +22 -17
  79. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  80. package/dist/domain/graph/builder/stages/detect-changes.js +2 -2
  81. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  82. package/dist/domain/graph/builder/stages/finalize.js +2 -2
  83. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  84. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  85. package/dist/domain/graph/builder/stages/insert-nodes.js +32 -21
  86. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  87. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  88. package/dist/domain/graph/builder/stages/resolve-imports.js +95 -84
  89. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  90. package/dist/domain/graph/cycles.d.ts +6 -0
  91. package/dist/domain/graph/cycles.d.ts.map +1 -1
  92. package/dist/domain/graph/cycles.js +114 -22
  93. package/dist/domain/graph/cycles.js.map +1 -1
  94. package/dist/domain/graph/resolve.js +1 -1
  95. package/dist/domain/graph/resolve.js.map +1 -1
  96. package/dist/domain/graph/watcher.d.ts +2 -0
  97. package/dist/domain/graph/watcher.d.ts.map +1 -1
  98. package/dist/domain/graph/watcher.js +170 -75
  99. package/dist/domain/graph/watcher.js.map +1 -1
  100. package/dist/domain/parser.d.ts +0 -5
  101. package/dist/domain/parser.d.ts.map +1 -1
  102. package/dist/domain/parser.js +13 -28
  103. package/dist/domain/parser.js.map +1 -1
  104. package/dist/domain/search/generator.js +1 -1
  105. package/dist/domain/search/generator.js.map +1 -1
  106. package/dist/domain/search/models.d.ts +4 -3
  107. package/dist/domain/search/models.d.ts.map +1 -1
  108. package/dist/domain/search/models.js +18 -5
  109. package/dist/domain/search/models.js.map +1 -1
  110. package/dist/domain/search/search/hybrid.d.ts.map +1 -1
  111. package/dist/domain/search/search/hybrid.js +29 -18
  112. package/dist/domain/search/search/hybrid.js.map +1 -1
  113. package/dist/extractors/go.js +36 -33
  114. package/dist/extractors/go.js.map +1 -1
  115. package/dist/extractors/helpers.d.ts.map +1 -1
  116. package/dist/extractors/helpers.js +40 -29
  117. package/dist/extractors/helpers.js.map +1 -1
  118. package/dist/extractors/java.js +58 -46
  119. package/dist/extractors/java.js.map +1 -1
  120. package/dist/extractors/javascript.js +46 -45
  121. package/dist/extractors/javascript.js.map +1 -1
  122. package/dist/extractors/kotlin.js +84 -78
  123. package/dist/extractors/kotlin.js.map +1 -1
  124. package/dist/extractors/python.js +29 -24
  125. package/dist/extractors/python.js.map +1 -1
  126. package/dist/extractors/rust.js +41 -32
  127. package/dist/extractors/rust.js.map +1 -1
  128. package/dist/extractors/solidity.js +58 -67
  129. package/dist/extractors/solidity.js.map +1 -1
  130. package/dist/extractors/swift.js +83 -81
  131. package/dist/extractors/swift.js.map +1 -1
  132. package/dist/extractors/zig.js +58 -60
  133. package/dist/extractors/zig.js.map +1 -1
  134. package/dist/features/ast.d.ts +16 -14
  135. package/dist/features/ast.d.ts.map +1 -1
  136. package/dist/features/ast.js +83 -81
  137. package/dist/features/ast.js.map +1 -1
  138. package/dist/features/audit.d.ts.map +1 -1
  139. package/dist/features/audit.js +8 -6
  140. package/dist/features/audit.js.map +1 -1
  141. package/dist/features/branch-compare.d.ts.map +1 -1
  142. package/dist/features/branch-compare.js +69 -72
  143. package/dist/features/branch-compare.js.map +1 -1
  144. package/dist/features/communities.d.ts.map +1 -1
  145. package/dist/features/communities.js +19 -7
  146. package/dist/features/communities.js.map +1 -1
  147. package/dist/features/complexity.d.ts.map +1 -1
  148. package/dist/features/complexity.js +120 -125
  149. package/dist/features/complexity.js.map +1 -1
  150. package/dist/features/dataflow.d.ts.map +1 -1
  151. package/dist/features/dataflow.js +136 -137
  152. package/dist/features/dataflow.js.map +1 -1
  153. package/dist/features/flow.d.ts.map +1 -1
  154. package/dist/features/flow.js +84 -79
  155. package/dist/features/flow.js.map +1 -1
  156. package/dist/features/structure-query.d.ts.map +1 -1
  157. package/dist/features/structure-query.js +69 -65
  158. package/dist/features/structure-query.js.map +1 -1
  159. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  160. package/dist/graph/algorithms/leiden/optimiser.js +70 -55
  161. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  162. package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
  163. package/dist/graph/algorithms/leiden/partition.js +288 -266
  164. package/dist/graph/algorithms/leiden/partition.js.map +1 -1
  165. package/dist/graph/model.d.ts.map +1 -1
  166. package/dist/graph/model.js +5 -1
  167. package/dist/graph/model.js.map +1 -1
  168. package/dist/infrastructure/config.d.ts.map +1 -1
  169. package/dist/infrastructure/config.js +6 -4
  170. package/dist/infrastructure/config.js.map +1 -1
  171. package/dist/infrastructure/suppress.d.ts +25 -0
  172. package/dist/infrastructure/suppress.d.ts.map +1 -0
  173. package/dist/infrastructure/suppress.js +43 -0
  174. package/dist/infrastructure/suppress.js.map +1 -0
  175. package/dist/mcp/server.d.ts.map +1 -1
  176. package/dist/mcp/server.js +29 -24
  177. package/dist/mcp/server.js.map +1 -1
  178. package/dist/presentation/dataflow.d.ts.map +1 -1
  179. package/dist/presentation/dataflow.js +47 -38
  180. package/dist/presentation/dataflow.js.map +1 -1
  181. package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -1
  182. package/dist/presentation/diff-impact-mermaid.js +60 -51
  183. package/dist/presentation/diff-impact-mermaid.js.map +1 -1
  184. package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
  185. package/dist/presentation/queries-cli/exports.js +20 -14
  186. package/dist/presentation/queries-cli/exports.js.map +1 -1
  187. package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
  188. package/dist/presentation/queries-cli/impact.js +15 -13
  189. package/dist/presentation/queries-cli/impact.js.map +1 -1
  190. package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
  191. package/dist/presentation/queries-cli/inspect.js +101 -79
  192. package/dist/presentation/queries-cli/inspect.js.map +1 -1
  193. package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
  194. package/dist/presentation/queries-cli/overview.js +25 -16
  195. package/dist/presentation/queries-cli/overview.js.map +1 -1
  196. package/dist/presentation/queries-cli/path.js +26 -20
  197. package/dist/presentation/queries-cli/path.js.map +1 -1
  198. package/dist/presentation/result-formatter.d.ts +10 -0
  199. package/dist/presentation/result-formatter.d.ts.map +1 -1
  200. package/dist/presentation/result-formatter.js +16 -1
  201. package/dist/presentation/result-formatter.js.map +1 -1
  202. package/dist/presentation/viewer.d.ts.map +1 -1
  203. package/dist/presentation/viewer.js +18 -12
  204. package/dist/presentation/viewer.js.map +1 -1
  205. package/dist/shared/errors.d.ts +5 -0
  206. package/dist/shared/errors.d.ts.map +1 -1
  207. package/dist/shared/errors.js +5 -0
  208. package/dist/shared/errors.js.map +1 -1
  209. package/dist/shared/hierarchy.d.ts +8 -2
  210. package/dist/shared/hierarchy.d.ts.map +1 -1
  211. package/dist/shared/hierarchy.js +42 -1
  212. package/dist/shared/hierarchy.js.map +1 -1
  213. package/dist/shared/normalize.d.ts +6 -1
  214. package/dist/shared/normalize.d.ts.map +1 -1
  215. package/dist/shared/normalize.js +20 -12
  216. package/dist/shared/normalize.js.map +1 -1
  217. package/dist/shared/paginate.d.ts +0 -9
  218. package/dist/shared/paginate.d.ts.map +1 -1
  219. package/dist/shared/paginate.js +0 -15
  220. package/dist/shared/paginate.js.map +1 -1
  221. package/dist/types.d.ts +10 -4
  222. package/dist/types.d.ts.map +1 -1
  223. package/package.json +7 -7
  224. package/src/ast-analysis/engine.ts +126 -105
  225. package/src/ast-analysis/metrics.ts +33 -11
  226. package/src/ast-analysis/shared.ts +33 -24
  227. package/src/ast-analysis/visitor-utils.ts +52 -32
  228. package/src/ast-analysis/visitor.ts +132 -71
  229. package/src/ast-analysis/visitors/ast-store-visitor.ts +53 -50
  230. package/src/ast-analysis/visitors/complexity-visitor.ts +35 -40
  231. package/src/ast-analysis/visitors/dataflow-visitor.ts +87 -43
  232. package/src/cli/commands/watch.ts +16 -2
  233. package/src/db/connection.ts +29 -28
  234. package/src/db/query-builder.ts +15 -3
  235. package/src/db/repository/base.ts +20 -0
  236. package/src/db/repository/native-repository.ts +79 -1
  237. package/src/db/repository/nodes.ts +13 -8
  238. package/src/db/repository/sqlite-repository.ts +29 -0
  239. package/src/domain/analysis/brief.ts +15 -25
  240. package/src/domain/analysis/context.ts +17 -10
  241. package/src/domain/analysis/dependencies.ts +67 -76
  242. package/src/domain/analysis/fn-impact.ts +36 -43
  243. package/src/domain/analysis/implementations.ts +11 -17
  244. package/src/domain/analysis/module-map.ts +58 -92
  245. package/src/domain/analysis/query-helpers.ts +18 -1
  246. package/src/domain/graph/builder/pipeline.ts +286 -97
  247. package/src/domain/graph/builder/stages/build-edges.ts +22 -18
  248. package/src/domain/graph/builder/stages/detect-changes.ts +2 -2
  249. package/src/domain/graph/builder/stages/finalize.ts +2 -2
  250. package/src/domain/graph/builder/stages/insert-nodes.ts +59 -34
  251. package/src/domain/graph/builder/stages/resolve-imports.ts +122 -100
  252. package/src/domain/graph/cycles.ts +110 -23
  253. package/src/domain/graph/resolve.ts +1 -1
  254. package/src/domain/graph/watcher.ts +202 -96
  255. package/src/domain/parser.ts +14 -26
  256. package/src/domain/search/generator.ts +1 -1
  257. package/src/domain/search/models.ts +17 -4
  258. package/src/domain/search/search/hybrid.ts +69 -51
  259. package/src/extractors/go.ts +43 -33
  260. package/src/extractors/helpers.ts +37 -23
  261. package/src/extractors/java.ts +66 -47
  262. package/src/extractors/javascript.ts +45 -46
  263. package/src/extractors/kotlin.ts +84 -77
  264. package/src/extractors/python.ts +31 -25
  265. package/src/extractors/rust.ts +37 -29
  266. package/src/extractors/solidity.ts +57 -61
  267. package/src/extractors/swift.ts +81 -80
  268. package/src/extractors/zig.ts +58 -61
  269. package/src/features/ast.ts +130 -110
  270. package/src/features/audit.ts +8 -6
  271. package/src/features/branch-compare.ts +105 -79
  272. package/src/features/communities.ts +25 -10
  273. package/src/features/complexity.ts +171 -134
  274. package/src/features/dataflow.ts +165 -175
  275. package/src/features/flow.ts +129 -92
  276. package/src/features/structure-query.ts +79 -64
  277. package/src/graph/algorithms/leiden/optimiser.ts +99 -55
  278. package/src/graph/algorithms/leiden/partition.ts +359 -294
  279. package/src/graph/model.ts +6 -1
  280. package/src/infrastructure/config.ts +6 -4
  281. package/src/infrastructure/suppress.ts +47 -0
  282. package/src/mcp/server.ts +53 -37
  283. package/src/presentation/dataflow.ts +50 -44
  284. package/src/presentation/diff-impact-mermaid.ts +104 -62
  285. package/src/presentation/queries-cli/exports.ts +21 -13
  286. package/src/presentation/queries-cli/impact.ts +15 -13
  287. package/src/presentation/queries-cli/inspect.ts +100 -81
  288. package/src/presentation/queries-cli/overview.ts +26 -16
  289. package/src/presentation/queries-cli/path.ts +33 -25
  290. package/src/presentation/result-formatter.ts +19 -1
  291. package/src/presentation/viewer.ts +42 -14
  292. package/src/shared/errors.ts +6 -0
  293. package/src/shared/hierarchy.ts +50 -2
  294. package/src/shared/normalize.ts +31 -12
  295. package/src/shared/paginate.ts +0 -17
  296. package/src/types.ts +24 -4
@@ -245,35 +245,41 @@ function extractPythonParameters(fnNode: TreeSitterNode): SubDeclaration[] {
245
245
  for (let i = 0; i < paramsNode.childCount; i++) {
246
246
  const child = paramsNode.child(i);
247
247
  if (!child) continue;
248
- const t = child.type;
249
- if (t === 'identifier') {
250
- params.push({ name: child.text, kind: 'parameter', line: child.startPosition.row + 1 });
251
- } else if (
252
- t === 'typed_parameter' ||
253
- t === 'default_parameter' ||
254
- t === 'typed_default_parameter'
255
- ) {
256
- const nameNode = child.childForFieldName('name') || child.child(0);
257
- if (nameNode && nameNode.type === 'identifier') {
258
- params.push({
259
- name: nameNode.text,
260
- kind: 'parameter',
261
- line: child.startPosition.row + 1,
262
- });
263
- }
264
- } else if (t === 'list_splat_pattern' || t === 'dictionary_splat_pattern') {
265
- for (let j = 0; j < child.childCount; j++) {
266
- const inner = child.child(j);
267
- if (inner && inner.type === 'identifier') {
268
- params.push({ name: inner.text, kind: 'parameter', line: child.startPosition.row + 1 });
269
- break;
270
- }
271
- }
272
- }
248
+ const param = extractSinglePyParam(child);
249
+ if (param) params.push(param);
273
250
  }
274
251
  return params;
275
252
  }
276
253
 
254
+ /** Extract a single parameter declaration from a parameter node. */
255
+ function extractSinglePyParam(child: TreeSitterNode): SubDeclaration | null {
256
+ const t = child.type;
257
+ if (t === 'identifier') {
258
+ return { name: child.text, kind: 'parameter', line: child.startPosition.row + 1 };
259
+ }
260
+ if (t === 'typed_parameter' || t === 'default_parameter' || t === 'typed_default_parameter') {
261
+ const nameNode = child.childForFieldName('name') || child.child(0);
262
+ if (nameNode && nameNode.type === 'identifier') {
263
+ return { name: nameNode.text, kind: 'parameter', line: child.startPosition.row + 1 };
264
+ }
265
+ }
266
+ if (t === 'list_splat_pattern' || t === 'dictionary_splat_pattern') {
267
+ return extractSplatParam(child);
268
+ }
269
+ return null;
270
+ }
271
+
272
+ /** Extract the identifier name from a *args or **kwargs splat pattern. */
273
+ function extractSplatParam(node: TreeSitterNode): SubDeclaration | null {
274
+ for (let j = 0; j < node.childCount; j++) {
275
+ const inner = node.child(j);
276
+ if (inner && inner.type === 'identifier') {
277
+ return { name: inner.text, kind: 'parameter', line: node.startPosition.row + 1 };
278
+ }
279
+ }
280
+ return null;
281
+ }
282
+
277
283
  /** Extract class-level assignment properties from expression statements. */
278
284
  function extractClassAssignment(
279
285
  child: TreeSitterNode,
@@ -138,19 +138,22 @@ function handleRustTraitItem(node: TreeSitterNode, ctx: ExtractorOutput): void {
138
138
  endLine: nodeEndLine(node),
139
139
  });
140
140
  const body = node.childForFieldName('body');
141
- if (body) {
142
- for (let i = 0; i < body.childCount; i++) {
143
- const child = body.child(i);
144
- if (child && (child.type === 'function_signature_item' || child.type === 'function_item')) {
145
- const methName = child.childForFieldName('name');
146
- if (methName) {
147
- ctx.definitions.push({
148
- name: `${nameNode.text}.${methName.text}`,
149
- kind: 'method',
150
- line: child.startPosition.row + 1,
151
- endLine: child.endPosition.row + 1,
152
- });
153
- }
141
+ if (body) extractTraitMethods(body, nameNode.text, ctx);
142
+ }
143
+
144
+ /** Extract method signatures/definitions from a trait body. */
145
+ function extractTraitMethods(body: TreeSitterNode, traitName: string, ctx: ExtractorOutput): void {
146
+ for (let i = 0; i < body.childCount; i++) {
147
+ const child = body.child(i);
148
+ if (child && (child.type === 'function_signature_item' || child.type === 'function_item')) {
149
+ const methName = child.childForFieldName('name');
150
+ if (methName) {
151
+ ctx.definitions.push({
152
+ name: `${traitName}.${methName.text}`,
153
+ kind: 'method',
154
+ line: child.startPosition.row + 1,
155
+ endLine: child.endPosition.row + 1,
156
+ });
154
157
  }
155
158
  }
156
159
  }
@@ -185,25 +188,30 @@ function handleRustUseDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
185
188
  function handleRustCallExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
186
189
  const fn = node.childForFieldName('function');
187
190
  if (!fn) return;
188
- if (fn.type === 'identifier') {
189
- ctx.calls.push({ name: fn.text, line: node.startPosition.row + 1 });
190
- } else if (fn.type === 'field_expression') {
191
+ const call = extractRustCallInfo(fn, node.startPosition.row + 1);
192
+ if (call) ctx.calls.push(call);
193
+ }
194
+
195
+ /** Extract call info from a Rust call function node. */
196
+ function extractRustCallInfo(fn: TreeSitterNode, line: number): Call | null {
197
+ if (fn.type === 'identifier') return { name: fn.text, line };
198
+ if (fn.type === 'field_expression') {
191
199
  const field = fn.childForFieldName('field');
192
- if (field) {
193
- const value = fn.childForFieldName('value');
194
- const call: Call = { name: field.text, line: node.startPosition.row + 1 };
195
- if (value) call.receiver = value.text;
196
- ctx.calls.push(call);
197
- }
198
- } else if (fn.type === 'scoped_identifier') {
200
+ if (!field) return null;
201
+ const value = fn.childForFieldName('value');
202
+ const call: Call = { name: field.text, line };
203
+ if (value) call.receiver = value.text;
204
+ return call;
205
+ }
206
+ if (fn.type === 'scoped_identifier') {
199
207
  const name = fn.childForFieldName('name');
200
- if (name) {
201
- const path = fn.childForFieldName('path');
202
- const call: Call = { name: name.text, line: node.startPosition.row + 1 };
203
- if (path) call.receiver = path.text;
204
- ctx.calls.push(call);
205
- }
208
+ if (!name) return null;
209
+ const path = fn.childForFieldName('path');
210
+ const call: Call = { name: name.text, line };
211
+ if (path) call.receiver = path.text;
212
+ return call;
206
213
  }
214
+ return null;
207
215
  }
208
216
 
209
217
  function handleRustMacroInvocation(node: TreeSitterNode, ctx: ExtractorOutput): void {
@@ -97,60 +97,8 @@ function handleContractDecl(
97
97
  if (!nameNode) return;
98
98
  const name = nameNode.text;
99
99
 
100
- const members: SubDeclaration[] = [];
101
100
  const body = node.childForFieldName('body') || findChild(node, 'contract_body');
102
- if (body) {
103
- for (let i = 0; i < body.childCount; i++) {
104
- const child = body.child(i);
105
- if (!child) continue;
106
- if (child.type === 'function_definition') {
107
- const fnName = child.childForFieldName('name');
108
- if (fnName) {
109
- members.push({ name: fnName.text, kind: 'method', line: child.startPosition.row + 1 });
110
- }
111
- } else if (child.type === 'state_variable_declaration') {
112
- const varName = child.childForFieldName('name');
113
- if (varName) {
114
- members.push({
115
- name: varName.text,
116
- kind: 'property',
117
- line: child.startPosition.row + 1,
118
- visibility: extractSolVisibility(child),
119
- });
120
- }
121
- } else if (child.type === 'event_definition') {
122
- const evName = child.childForFieldName('name');
123
- if (evName) {
124
- members.push({
125
- name: evName.text,
126
- kind: 'property',
127
- decorators: ['event'],
128
- line: child.startPosition.row + 1,
129
- });
130
- }
131
- } else if (child.type === 'error_declaration') {
132
- const errName = child.childForFieldName('name');
133
- if (errName) {
134
- members.push({
135
- name: errName.text,
136
- kind: 'property',
137
- decorators: ['error'],
138
- line: child.startPosition.row + 1,
139
- });
140
- }
141
- } else if (child.type === 'modifier_definition') {
142
- const modName = child.childForFieldName('name');
143
- if (modName) {
144
- members.push({
145
- name: modName.text,
146
- kind: 'method',
147
- decorators: ['modifier'],
148
- line: child.startPosition.row + 1,
149
- });
150
- }
151
- }
152
- }
153
- }
101
+ const members = body ? extractContractMembers(body) : [];
154
102
 
155
103
  ctx.definitions.push({
156
104
  name,
@@ -160,15 +108,63 @@ function handleContractDecl(
160
108
  children: members.length > 0 ? members : undefined,
161
109
  });
162
110
 
163
- // Inheritance
111
+ extractInheritance(node, name, ctx);
112
+ }
113
+
114
+ /** Extract member declarations from a contract body node. */
115
+ function extractContractMembers(body: TreeSitterNode): SubDeclaration[] {
116
+ const members: SubDeclaration[] = [];
117
+ for (let i = 0; i < body.childCount; i++) {
118
+ const child = body.child(i);
119
+ if (!child) continue;
120
+ const member = extractContractMember(child);
121
+ if (member) members.push(member);
122
+ }
123
+ return members;
124
+ }
125
+
126
+ /** Map a single contract body child to a SubDeclaration, or null if not a recognized member. */
127
+ function extractContractMember(child: TreeSitterNode): SubDeclaration | null {
128
+ const line = child.startPosition.row + 1;
129
+ switch (child.type) {
130
+ case 'function_definition': {
131
+ const fnName = child.childForFieldName('name');
132
+ return fnName ? { name: fnName.text, kind: 'method', line } : null;
133
+ }
134
+ case 'state_variable_declaration': {
135
+ const varName = child.childForFieldName('name');
136
+ return varName
137
+ ? { name: varName.text, kind: 'property', line, visibility: extractSolVisibility(child) }
138
+ : null;
139
+ }
140
+ case 'event_definition': {
141
+ const evName = child.childForFieldName('name');
142
+ return evName ? { name: evName.text, kind: 'property', decorators: ['event'], line } : null;
143
+ }
144
+ case 'error_declaration': {
145
+ const errName = child.childForFieldName('name');
146
+ return errName ? { name: errName.text, kind: 'property', decorators: ['error'], line } : null;
147
+ }
148
+ case 'modifier_definition': {
149
+ const modName = child.childForFieldName('name');
150
+ return modName
151
+ ? { name: modName.text, kind: 'method', decorators: ['modifier'], line }
152
+ : null;
153
+ }
154
+ default:
155
+ return null;
156
+ }
157
+ }
158
+
159
+ /** Extract inheritance (extends) relationships from a contract node. */
160
+ function extractInheritance(node: TreeSitterNode, name: string, ctx: ExtractorOutput): void {
164
161
  const inheritance = findChild(node, 'inheritance_specifier');
165
- if (inheritance) {
166
- for (let i = 0; i < inheritance.childCount; i++) {
167
- const child = inheritance.child(i);
168
- if (!child) continue;
169
- if (child.type === 'user_defined_type' || child.type === 'identifier') {
170
- ctx.classes.push({ name, extends: child.text, line: node.startPosition.row + 1 });
171
- }
162
+ if (!inheritance) return;
163
+ for (let i = 0; i < inheritance.childCount; i++) {
164
+ const child = inheritance.child(i);
165
+ if (!child) continue;
166
+ if (child.type === 'user_defined_type' || child.type === 'identifier') {
167
+ ctx.classes.push({ name, extends: child.text, line: node.startPosition.row + 1 });
172
168
  }
173
169
  }
174
170
  }
@@ -73,49 +73,7 @@ function handleSwiftClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void
73
73
 
74
74
  const kind = isEnum ? 'enum' : isStruct ? 'struct' : 'class';
75
75
 
76
- const children: SubDeclaration[] = [];
77
-
78
- if (isEnum) {
79
- // Enum cases: enum_entry > simple_identifier, inside enum_class_body
80
- const body = findChild(node, 'enum_class_body');
81
- if (body) {
82
- for (let i = 0; i < body.childCount; i++) {
83
- const child = body.child(i);
84
- if (child && child.type === 'enum_entry') {
85
- const entryName = findChild(child, 'simple_identifier');
86
- if (entryName) {
87
- children.push({
88
- name: entryName.text,
89
- kind: 'constant',
90
- line: child.startPosition.row + 1,
91
- });
92
- }
93
- }
94
- }
95
- }
96
- } else {
97
- // Extract properties from class_body
98
- const body = findChild(node, 'class_body');
99
- if (body) {
100
- for (let i = 0; i < body.childCount; i++) {
101
- const child = body.child(i);
102
- if (child && child.type === 'property_declaration') {
103
- const pattern = findChild(child, 'pattern');
104
- if (pattern) {
105
- const propName = findChild(pattern, 'simple_identifier');
106
- if (propName) {
107
- children.push({
108
- name: propName.text,
109
- kind: 'property',
110
- line: child.startPosition.row + 1,
111
- visibility: extractModifierVisibility(child),
112
- });
113
- }
114
- }
115
- }
116
- }
117
- }
118
- }
76
+ const children = isEnum ? collectSwiftEnumEntries(node) : collectSwiftProperties(node);
119
77
 
120
78
  ctx.definitions.push({
121
79
  name,
@@ -125,52 +83,95 @@ function handleSwiftClassDecl(node: TreeSitterNode, ctx: ExtractorOutput): void
125
83
  children: children.length > 0 ? children : undefined,
126
84
  });
127
85
 
128
- // Methods inside class_body or enum_class_body
86
+ collectSwiftMethods(node, name, ctx);
87
+ collectSwiftInheritance(node, name, ctx);
88
+ }
89
+
90
+ /** Collect enum constant entries from an enum_class_body. */
91
+ function collectSwiftEnumEntries(node: TreeSitterNode): SubDeclaration[] {
92
+ const entries: SubDeclaration[] = [];
93
+ const body = findChild(node, 'enum_class_body');
94
+ if (!body) return entries;
95
+ for (let i = 0; i < body.childCount; i++) {
96
+ const child = body.child(i);
97
+ if (!child || child.type !== 'enum_entry') continue;
98
+ const entryName = findChild(child, 'simple_identifier');
99
+ if (entryName) {
100
+ entries.push({
101
+ name: entryName.text,
102
+ kind: 'constant',
103
+ line: child.startPosition.row + 1,
104
+ });
105
+ }
106
+ }
107
+ return entries;
108
+ }
109
+
110
+ /** Collect property declarations from a class_body. */
111
+ function collectSwiftProperties(node: TreeSitterNode): SubDeclaration[] {
112
+ const props: SubDeclaration[] = [];
113
+ const body = findChild(node, 'class_body');
114
+ if (!body) return props;
115
+ for (let i = 0; i < body.childCount; i++) {
116
+ const child = body.child(i);
117
+ if (!child || child.type !== 'property_declaration') continue;
118
+ const pattern = findChild(child, 'pattern');
119
+ if (!pattern) continue;
120
+ const propName = findChild(pattern, 'simple_identifier');
121
+ if (propName) {
122
+ props.push({
123
+ name: propName.text,
124
+ kind: 'property',
125
+ line: child.startPosition.row + 1,
126
+ visibility: extractModifierVisibility(child),
127
+ });
128
+ }
129
+ }
130
+ return props;
131
+ }
132
+
133
+ /** Collect method declarations from class_body or enum_class_body. */
134
+ function collectSwiftMethods(node: TreeSitterNode, className: string, ctx: ExtractorOutput): void {
129
135
  const body = findChild(node, 'class_body') || findChild(node, 'enum_class_body');
130
- if (body) {
131
- for (let i = 0; i < body.childCount; i++) {
132
- const child = body.child(i);
133
- if (child && child.type === 'function_declaration') {
134
- const methName = findChild(child, 'simple_identifier');
135
- if (methName) {
136
- ctx.definitions.push({
137
- name: `${name}.${methName.text}`,
138
- kind: 'method',
139
- line: child.startPosition.row + 1,
140
- endLine: child.endPosition.row + 1,
141
- visibility: extractModifierVisibility(child),
142
- });
143
- }
144
- }
136
+ if (!body) return;
137
+ for (let i = 0; i < body.childCount; i++) {
138
+ const child = body.child(i);
139
+ if (!child || child.type !== 'function_declaration') continue;
140
+ const methName = findChild(child, 'simple_identifier');
141
+ if (methName) {
142
+ ctx.definitions.push({
143
+ name: `${className}.${methName.text}`,
144
+ kind: 'method',
145
+ line: child.startPosition.row + 1,
146
+ endLine: child.endPosition.row + 1,
147
+ visibility: extractModifierVisibility(child),
148
+ });
145
149
  }
146
150
  }
151
+ }
147
152
 
148
- // Inheritance: inheritance_specifier nodes are DIRECT children of class_declaration
149
- // First specifier is the superclass (extends), rest are protocol conformances (implements)
153
+ /** Collect inheritance from inheritance_specifier children. First = extends, rest = implements. */
154
+ function collectSwiftInheritance(
155
+ node: TreeSitterNode,
156
+ className: string,
157
+ ctx: ExtractorOutput,
158
+ ): void {
150
159
  let first = true;
151
160
  for (let i = 0; i < node.childCount; i++) {
152
161
  const child = node.child(i);
153
162
  if (!child || child.type !== 'inheritance_specifier') continue;
154
- // inheritance_specifier > user_type > type_identifier
155
163
  const userType = findChild(child, 'user_type');
156
- if (userType) {
157
- const typeId = findChild(userType, 'type_identifier');
158
- if (typeId) {
159
- if (first) {
160
- ctx.classes.push({
161
- name,
162
- extends: typeId.text,
163
- line: node.startPosition.row + 1,
164
- });
165
- first = false;
166
- } else {
167
- ctx.classes.push({
168
- name,
169
- implements: typeId.text,
170
- line: node.startPosition.row + 1,
171
- });
172
- }
173
- }
164
+ const typeId = userType ? findChild(userType, 'type_identifier') : null;
165
+ if (!typeId) continue;
166
+ if (first) {
167
+ ctx.classes.push({ name: className, extends: typeId.text, line: node.startPosition.row + 1 });
168
+ first = false;
169
+ } else {
170
+ ctx.classes.push({
171
+ name: className,
172
+ implements: typeId.text,
173
+ line: node.startPosition.row + 1,
174
+ });
174
175
  }
175
176
  }
176
177
  }
@@ -100,78 +100,75 @@ function handleZigVariable(node: TreeSitterNode, ctx: ExtractorOutput): void {
100
100
  if (!nameNode) return;
101
101
  const name = nameNode.text;
102
102
 
103
- // Check if this is a struct/enum/union definition
103
+ if (tryHandleZigContainerDecl(node, name, ctx)) return;
104
+ if (tryHandleZigImportDecl(node, name, ctx)) return;
105
+
106
+ // Regular constant/variable
107
+ const isConst = hasChildText(node, 'const');
108
+ ctx.definitions.push({
109
+ name,
110
+ kind: isConst ? 'constant' : 'variable',
111
+ line: node.startPosition.row + 1,
112
+ endLine: nodeEndLine(node),
113
+ });
114
+ }
115
+
116
+ /** Try to handle a variable_declaration as a struct/enum/union. Returns true if handled. */
117
+ function tryHandleZigContainerDecl(
118
+ node: TreeSitterNode,
119
+ name: string,
120
+ ctx: ExtractorOutput,
121
+ ): boolean {
104
122
  for (let i = 0; i < node.childCount; i++) {
105
123
  const child = node.child(i);
106
124
  if (!child) continue;
125
+ const containerKind = zigContainerKind(child.type);
126
+ if (!containerKind) continue;
107
127
 
108
- if (child.type === 'struct_declaration') {
109
- const members = extractZigContainerFields(child);
110
- ctx.definitions.push({
111
- name,
112
- kind: 'struct',
113
- line: node.startPosition.row + 1,
114
- endLine: nodeEndLine(node),
115
- children: members.length > 0 ? members : undefined,
116
- visibility: isZigPub(node) ? 'public' : undefined,
117
- });
118
- extractZigContainerMethods(child, name, ctx);
119
- return;
120
- }
121
- if (child.type === 'enum_declaration') {
122
- ctx.definitions.push({
123
- name,
124
- kind: 'enum',
125
- line: node.startPosition.row + 1,
126
- endLine: nodeEndLine(node),
127
- visibility: isZigPub(node) ? 'public' : undefined,
128
- });
129
- return;
130
- }
131
- if (child.type === 'union_declaration') {
132
- ctx.definitions.push({
133
- name,
134
- kind: 'struct',
135
- line: node.startPosition.row + 1,
136
- endLine: nodeEndLine(node),
137
- visibility: isZigPub(node) ? 'public' : undefined,
138
- });
139
- return;
140
- }
128
+ const members = child.type === 'struct_declaration' ? extractZigContainerFields(child) : [];
129
+ ctx.definitions.push({
130
+ name,
131
+ kind: containerKind,
132
+ line: node.startPosition.row + 1,
133
+ endLine: nodeEndLine(node),
134
+ children: members.length > 0 ? members : undefined,
135
+ visibility: isZigPub(node) ? 'public' : undefined,
136
+ });
137
+ if (child.type === 'struct_declaration') extractZigContainerMethods(child, name, ctx);
138
+ return true;
141
139
  }
140
+ return false;
141
+ }
142
142
 
143
- // Check for @import
143
+ /** Map a Zig container node type to a definition kind, or undefined if not a container. */
144
+ function zigContainerKind(nodeType: string): 'struct' | 'enum' | undefined {
145
+ if (nodeType === 'struct_declaration' || nodeType === 'union_declaration') return 'struct';
146
+ if (nodeType === 'enum_declaration') return 'enum';
147
+ return undefined;
148
+ }
149
+
150
+ /** Try to handle a variable_declaration as an @import. Returns true if handled. */
151
+ function tryHandleZigImportDecl(node: TreeSitterNode, name: string, ctx: ExtractorOutput): boolean {
144
152
  for (let i = 0; i < node.childCount; i++) {
145
153
  const child = node.child(i);
146
- if (!child) continue;
147
- if (child.type === 'builtin_function') {
148
- const builtinId = findChild(child, 'builtin_identifier');
149
- if (builtinId?.text === '@import') {
150
- const args = findChild(child, 'arguments');
151
- if (args) {
152
- const strArg = findChild(args, 'string_literal') || findChild(args, 'string');
153
- if (strArg) {
154
- const source = strArg.text.replace(/^"|"$/g, '');
155
- ctx.imports.push({
156
- source,
157
- names: [name],
158
- line: node.startPosition.row + 1,
159
- });
160
- return;
161
- }
162
- }
163
- }
154
+ if (!child || child.type !== 'builtin_function') continue;
155
+ const source = extractZigImportSource(child);
156
+ if (source) {
157
+ ctx.imports.push({ source, names: [name], line: node.startPosition.row + 1 });
158
+ return true;
164
159
  }
165
160
  }
161
+ return false;
162
+ }
166
163
 
167
- // Regular constant/variable
168
- const isConst = hasChildText(node, 'const');
169
- ctx.definitions.push({
170
- name,
171
- kind: isConst ? 'constant' : 'variable',
172
- line: node.startPosition.row + 1,
173
- endLine: nodeEndLine(node),
174
- });
164
+ /** Extract the import source path from a builtin_function node if it is @import. */
165
+ function extractZigImportSource(builtinNode: TreeSitterNode): string | null {
166
+ const builtinId = findChild(builtinNode, 'builtin_identifier');
167
+ if (builtinId?.text !== '@import') return null;
168
+ const args = findChild(builtinNode, 'arguments');
169
+ if (!args) return null;
170
+ const strArg = findChild(args, 'string_literal') || findChild(args, 'string');
171
+ return strArg ? strArg.text.replace(/^"|"$/g, '') : null;
175
172
  }
176
173
 
177
174
  function extractZigContainerFields(container: TreeSitterNode): SubDeclaration[] {