@optave/codegraph 3.7.0 → 3.8.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 (148) hide show
  1. package/README.md +25 -14
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +158 -1
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
  6. package/dist/ast-analysis/rules/javascript.js +0 -1
  7. package/dist/ast-analysis/rules/javascript.js.map +1 -1
  8. package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
  9. package/dist/ast-analysis/visitors/ast-store-visitor.js +2 -75
  10. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  11. package/dist/cli/commands/ast.js +2 -2
  12. package/dist/cli/commands/ast.js.map +1 -1
  13. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  14. package/dist/domain/graph/builder/pipeline.js +128 -6
  15. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  16. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  17. package/dist/domain/graph/builder/stages/build-edges.js +101 -1
  18. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  19. package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
  20. package/dist/domain/graph/builder/stages/collect-files.js +17 -5
  21. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  22. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  23. package/dist/domain/graph/builder/stages/detect-changes.js +98 -50
  24. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  25. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  26. package/dist/domain/graph/builder/stages/finalize.js +32 -5
  27. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  28. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  29. package/dist/domain/graph/builder/stages/insert-nodes.js +20 -7
  30. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  31. package/dist/domain/parser.d.ts +1 -1
  32. package/dist/domain/parser.d.ts.map +1 -1
  33. package/dist/domain/parser.js +88 -4
  34. package/dist/domain/parser.js.map +1 -1
  35. package/dist/extractors/clojure.d.ts +12 -0
  36. package/dist/extractors/clojure.d.ts.map +1 -0
  37. package/dist/extractors/clojure.js +245 -0
  38. package/dist/extractors/clojure.js.map +1 -0
  39. package/dist/extractors/cuda.d.ts +11 -0
  40. package/dist/extractors/cuda.d.ts.map +1 -0
  41. package/dist/extractors/cuda.js +302 -0
  42. package/dist/extractors/cuda.js.map +1 -0
  43. package/dist/extractors/erlang.d.ts +14 -0
  44. package/dist/extractors/erlang.d.ts.map +1 -0
  45. package/dist/extractors/erlang.js +239 -0
  46. package/dist/extractors/erlang.js.map +1 -0
  47. package/dist/extractors/fsharp.d.ts +13 -0
  48. package/dist/extractors/fsharp.d.ts.map +1 -0
  49. package/dist/extractors/fsharp.js +218 -0
  50. package/dist/extractors/fsharp.js.map +1 -0
  51. package/dist/extractors/gleam.d.ts +14 -0
  52. package/dist/extractors/gleam.d.ts.map +1 -0
  53. package/dist/extractors/gleam.js +229 -0
  54. package/dist/extractors/gleam.js.map +1 -0
  55. package/dist/extractors/groovy.d.ts +10 -0
  56. package/dist/extractors/groovy.d.ts.map +1 -0
  57. package/dist/extractors/groovy.js +304 -0
  58. package/dist/extractors/groovy.js.map +1 -0
  59. package/dist/extractors/index.d.ts +11 -0
  60. package/dist/extractors/index.d.ts.map +1 -1
  61. package/dist/extractors/index.js +11 -0
  62. package/dist/extractors/index.js.map +1 -1
  63. package/dist/extractors/julia.d.ts +16 -0
  64. package/dist/extractors/julia.d.ts.map +1 -0
  65. package/dist/extractors/julia.js +287 -0
  66. package/dist/extractors/julia.js.map +1 -0
  67. package/dist/extractors/objc.d.ts +9 -0
  68. package/dist/extractors/objc.d.ts.map +1 -0
  69. package/dist/extractors/objc.js +406 -0
  70. package/dist/extractors/objc.js.map +1 -0
  71. package/dist/extractors/ocaml.js +74 -0
  72. package/dist/extractors/ocaml.js.map +1 -1
  73. package/dist/extractors/r.d.ts +13 -0
  74. package/dist/extractors/r.d.ts.map +1 -0
  75. package/dist/extractors/r.js +251 -0
  76. package/dist/extractors/r.js.map +1 -0
  77. package/dist/extractors/solidity.d.ts +9 -0
  78. package/dist/extractors/solidity.d.ts.map +1 -0
  79. package/dist/extractors/solidity.js +374 -0
  80. package/dist/extractors/solidity.js.map +1 -0
  81. package/dist/extractors/verilog.d.ts +9 -0
  82. package/dist/extractors/verilog.d.ts.map +1 -0
  83. package/dist/extractors/verilog.js +286 -0
  84. package/dist/extractors/verilog.js.map +1 -0
  85. package/dist/features/ast.d.ts.map +1 -1
  86. package/dist/features/ast.js +1 -2
  87. package/dist/features/ast.js.map +1 -1
  88. package/dist/graph/algorithms/bfs.d.ts +2 -0
  89. package/dist/graph/algorithms/bfs.d.ts.map +1 -1
  90. package/dist/graph/algorithms/bfs.js +27 -0
  91. package/dist/graph/algorithms/bfs.js.map +1 -1
  92. package/dist/graph/algorithms/centrality.d.ts +2 -0
  93. package/dist/graph/algorithms/centrality.d.ts.map +1 -1
  94. package/dist/graph/algorithms/centrality.js +28 -0
  95. package/dist/graph/algorithms/centrality.js.map +1 -1
  96. package/dist/graph/algorithms/louvain.d.ts +3 -4
  97. package/dist/graph/algorithms/louvain.d.ts.map +1 -1
  98. package/dist/graph/algorithms/louvain.js +29 -0
  99. package/dist/graph/algorithms/louvain.js.map +1 -1
  100. package/dist/graph/algorithms/shortest-path.d.ts +2 -0
  101. package/dist/graph/algorithms/shortest-path.d.ts.map +1 -1
  102. package/dist/graph/algorithms/shortest-path.js +18 -1
  103. package/dist/graph/algorithms/shortest-path.js.map +1 -1
  104. package/dist/types.d.ts +122 -2
  105. package/dist/types.d.ts.map +1 -1
  106. package/grammars/tree-sitter-clojure.wasm +0 -0
  107. package/grammars/tree-sitter-cuda.wasm +0 -0
  108. package/grammars/tree-sitter-erlang.wasm +0 -0
  109. package/grammars/tree-sitter-fsharp.wasm +0 -0
  110. package/grammars/tree-sitter-gleam.wasm +0 -0
  111. package/grammars/tree-sitter-groovy.wasm +0 -0
  112. package/grammars/tree-sitter-julia.wasm +0 -0
  113. package/grammars/tree-sitter-objc.wasm +0 -0
  114. package/grammars/tree-sitter-ocaml_interface.wasm +0 -0
  115. package/grammars/tree-sitter-r.wasm +0 -0
  116. package/grammars/tree-sitter-solidity.wasm +0 -0
  117. package/grammars/tree-sitter-verilog.wasm +0 -0
  118. package/package.json +18 -7
  119. package/src/ast-analysis/engine.ts +183 -1
  120. package/src/ast-analysis/rules/javascript.ts +0 -1
  121. package/src/ast-analysis/visitors/ast-store-visitor.ts +2 -75
  122. package/src/cli/commands/ast.ts +2 -2
  123. package/src/domain/graph/builder/pipeline.ts +142 -6
  124. package/src/domain/graph/builder/stages/build-edges.ts +158 -1
  125. package/src/domain/graph/builder/stages/collect-files.ts +18 -7
  126. package/src/domain/graph/builder/stages/detect-changes.ts +109 -55
  127. package/src/domain/graph/builder/stages/finalize.ts +39 -9
  128. package/src/domain/graph/builder/stages/insert-nodes.ts +18 -7
  129. package/src/domain/parser.ts +108 -2
  130. package/src/extractors/clojure.ts +273 -0
  131. package/src/extractors/cuda.ts +316 -0
  132. package/src/extractors/erlang.ts +252 -0
  133. package/src/extractors/fsharp.ts +253 -0
  134. package/src/extractors/gleam.ts +246 -0
  135. package/src/extractors/groovy.ts +332 -0
  136. package/src/extractors/index.ts +11 -0
  137. package/src/extractors/julia.ts +318 -0
  138. package/src/extractors/objc.ts +431 -0
  139. package/src/extractors/ocaml.ts +78 -0
  140. package/src/extractors/r.ts +253 -0
  141. package/src/extractors/solidity.ts +398 -0
  142. package/src/extractors/verilog.ts +315 -0
  143. package/src/features/ast.ts +1 -2
  144. package/src/graph/algorithms/bfs.ts +34 -0
  145. package/src/graph/algorithms/centrality.ts +30 -0
  146. package/src/graph/algorithms/louvain.ts +31 -4
  147. package/src/graph/algorithms/shortest-path.ts +20 -1
  148. package/src/types.ts +117 -2
@@ -17,49 +17,71 @@ import type {
17
17
  // Re-export all extractors for backward compatibility
18
18
  export {
19
19
  extractBashSymbols,
20
+ extractClojureSymbols,
20
21
  extractCppSymbols,
21
22
  extractCSharpSymbols,
22
23
  extractCSymbols,
24
+ extractCudaSymbols,
23
25
  extractDartSymbols,
24
26
  extractElixirSymbols,
27
+ extractErlangSymbols,
28
+ extractFSharpSymbols,
29
+ extractGleamSymbols,
25
30
  extractGoSymbols,
31
+ extractGroovySymbols,
26
32
  extractHaskellSymbols,
27
33
  extractHCLSymbols,
28
34
  extractJavaSymbols,
35
+ extractJuliaSymbols,
29
36
  extractKotlinSymbols,
30
37
  extractLuaSymbols,
38
+ extractObjCSymbols,
31
39
  extractOCamlSymbols,
32
40
  extractPHPSymbols,
33
41
  extractPythonSymbols,
42
+ extractRSymbols,
34
43
  extractRubySymbols,
35
44
  extractRustSymbols,
36
45
  extractScalaSymbols,
46
+ extractSoliditySymbols,
37
47
  extractSwiftSymbols,
38
48
  extractSymbols,
49
+ extractVerilogSymbols,
39
50
  extractZigSymbols,
40
51
  } from '../extractors/index.js';
41
52
 
42
53
  import {
43
54
  extractBashSymbols,
55
+ extractClojureSymbols,
44
56
  extractCppSymbols,
45
57
  extractCSharpSymbols,
46
58
  extractCSymbols,
59
+ extractCudaSymbols,
47
60
  extractDartSymbols,
48
61
  extractElixirSymbols,
62
+ extractErlangSymbols,
63
+ extractFSharpSymbols,
64
+ extractGleamSymbols,
49
65
  extractGoSymbols,
66
+ extractGroovySymbols,
50
67
  extractHaskellSymbols,
51
68
  extractHCLSymbols,
52
69
  extractJavaSymbols,
70
+ extractJuliaSymbols,
53
71
  extractKotlinSymbols,
54
72
  extractLuaSymbols,
73
+ extractObjCSymbols,
55
74
  extractOCamlSymbols,
56
75
  extractPHPSymbols,
57
76
  extractPythonSymbols,
77
+ extractRSymbols,
58
78
  extractRubySymbols,
59
79
  extractRustSymbols,
60
80
  extractScalaSymbols,
81
+ extractSoliditySymbols,
61
82
  extractSwiftSymbols,
62
83
  extractSymbols,
84
+ extractVerilogSymbols,
63
85
  extractZigSymbols,
64
86
  } from '../extractors/index.js';
65
87
 
@@ -529,11 +551,95 @@ export const LANGUAGE_REGISTRY: LanguageRegistryEntry[] = [
529
551
  },
530
552
  {
531
553
  id: 'ocaml',
532
- extensions: ['.ml', '.mli'],
554
+ extensions: ['.ml'],
533
555
  grammarFile: 'tree-sitter-ocaml.wasm',
534
556
  extractor: extractOCamlSymbols,
535
557
  required: false,
536
558
  },
559
+ {
560
+ id: 'ocaml-interface',
561
+ extensions: ['.mli'],
562
+ grammarFile: 'tree-sitter-ocaml_interface.wasm',
563
+ extractor: extractOCamlSymbols,
564
+ required: false,
565
+ },
566
+ {
567
+ id: 'fsharp',
568
+ extensions: ['.fs', '.fsx', '.fsi'],
569
+ grammarFile: 'tree-sitter-fsharp.wasm',
570
+ extractor: extractFSharpSymbols,
571
+ required: false,
572
+ },
573
+ {
574
+ id: 'gleam',
575
+ extensions: ['.gleam'],
576
+ grammarFile: 'tree-sitter-gleam.wasm',
577
+ extractor: extractGleamSymbols,
578
+ required: false,
579
+ },
580
+ {
581
+ id: 'clojure',
582
+ extensions: ['.clj', '.cljs', '.cljc'],
583
+ grammarFile: 'tree-sitter-clojure.wasm',
584
+ extractor: extractClojureSymbols,
585
+ required: false,
586
+ },
587
+ {
588
+ id: 'julia',
589
+ extensions: ['.jl'],
590
+ grammarFile: 'tree-sitter-julia.wasm',
591
+ extractor: extractJuliaSymbols,
592
+ required: false,
593
+ },
594
+ {
595
+ id: 'r',
596
+ extensions: ['.r', '.R'],
597
+ grammarFile: 'tree-sitter-r.wasm',
598
+ extractor: extractRSymbols,
599
+ required: false,
600
+ },
601
+ {
602
+ id: 'erlang',
603
+ extensions: ['.erl', '.hrl'],
604
+ grammarFile: 'tree-sitter-erlang.wasm',
605
+ extractor: extractErlangSymbols,
606
+ required: false,
607
+ },
608
+ {
609
+ id: 'solidity',
610
+ extensions: ['.sol'],
611
+ grammarFile: 'tree-sitter-solidity.wasm',
612
+ extractor: extractSoliditySymbols,
613
+ required: false,
614
+ },
615
+ {
616
+ id: 'objc',
617
+ extensions: ['.m'],
618
+ grammarFile: 'tree-sitter-objc.wasm',
619
+ extractor: extractObjCSymbols,
620
+ required: false,
621
+ },
622
+ {
623
+ id: 'cuda',
624
+ extensions: ['.cu', '.cuh'],
625
+ grammarFile: 'tree-sitter-cuda.wasm',
626
+ extractor: extractCudaSymbols,
627
+ required: false,
628
+ },
629
+ {
630
+ id: 'groovy',
631
+ extensions: ['.groovy', '.gvy'],
632
+ grammarFile: 'tree-sitter-groovy.wasm',
633
+ extractor: extractGroovySymbols,
634
+ required: false,
635
+ },
636
+ {
637
+ id: 'verilog',
638
+ extensions: ['.v', '.sv'],
639
+ grammarFile: 'tree-sitter-verilog.wasm',
640
+ extractor: extractVerilogSymbols,
641
+ required: false,
642
+ },
537
643
  ];
538
644
 
539
645
  const _extToLang: Map<string, LanguageRegistryEntry> = new Map();
@@ -766,7 +872,7 @@ export function getActiveEngine(opts: ParseEngineOpts = {}): {
766
872
  */
767
873
  export function createParseTreeCache(): any {
768
874
  const native = loadNative();
769
- if (!native || !native.ParseTreeCache) return null;
875
+ if (!native?.ParseTreeCache) return null;
770
876
  return new native.ParseTreeCache();
771
877
  }
772
878
 
@@ -0,0 +1,273 @@
1
+ import type { ExtractorOutput, SubDeclaration, TreeSitterNode, TreeSitterTree } from '../types.js';
2
+ import { nodeEndLine } from './helpers.js';
3
+
4
+ /**
5
+ * Extract symbols from Clojure files.
6
+ *
7
+ * Clojure tree-sitter grammar (sogaiu/tree-sitter-clojure) notes:
8
+ * - The grammar is minimal: everything is a list/vector/map/symbol
9
+ * - We detect definitions by the first symbol in a list: defn, def, defprotocol, etc.
10
+ * - Namespace: (ns name ...)
11
+ * - Imports: (:require ...) inside ns, or (require ...)
12
+ */
13
+ export function extractClojureSymbols(tree: TreeSitterTree, _filePath: string): ExtractorOutput {
14
+ const ctx: ExtractorOutput = {
15
+ definitions: [],
16
+ calls: [],
17
+ imports: [],
18
+ classes: [],
19
+ exports: [],
20
+ typeMap: new Map(),
21
+ };
22
+
23
+ walkClojureNode(tree.rootNode, ctx, null);
24
+ return ctx;
25
+ }
26
+
27
+ function walkClojureNode(
28
+ node: TreeSitterNode,
29
+ ctx: ExtractorOutput,
30
+ currentNs: string | null,
31
+ ): void {
32
+ let nextNs = currentNs;
33
+
34
+ if (node.type === 'list_lit') {
35
+ nextNs = handleListForm(node, ctx, currentNs) ?? currentNs;
36
+ }
37
+
38
+ for (let i = 0; i < node.childCount; i++) {
39
+ const child = node.child(i);
40
+ if (child) walkClojureNode(child, ctx, nextNs);
41
+ }
42
+ }
43
+
44
+ /** Returns new namespace name if this is an `ns` form, otherwise null. */
45
+ function handleListForm(
46
+ node: TreeSitterNode,
47
+ ctx: ExtractorOutput,
48
+ currentNs: string | null,
49
+ ): string | null {
50
+ const firstSym = findFirstSymbol(node);
51
+ if (!firstSym) return null;
52
+
53
+ const name = firstSym.text;
54
+
55
+ switch (name) {
56
+ case 'ns':
57
+ return handleNsForm(node, ctx);
58
+ case 'def':
59
+ case 'defonce':
60
+ handleDefForm(node, ctx, currentNs, 'variable');
61
+ return null;
62
+ case 'defn':
63
+ case 'defn-':
64
+ handleDefnForm(node, ctx, currentNs, name === 'defn-' ? 'private' : 'public');
65
+ return null;
66
+ case 'defmacro':
67
+ handleDefnForm(node, ctx, currentNs, 'public');
68
+ return null;
69
+ case 'defprotocol':
70
+ handleDefprotocol(node, ctx);
71
+ return null;
72
+ case 'defrecord':
73
+ case 'deftype':
74
+ handleDefrecord(node, ctx, name);
75
+ return null;
76
+ case 'defmulti':
77
+ handleDefForm(node, ctx, currentNs, 'function');
78
+ return null;
79
+ case 'defmethod':
80
+ handleDefnForm(node, ctx, currentNs, 'public');
81
+ return null;
82
+ case 'require':
83
+ case 'use':
84
+ case 'import':
85
+ handleImportForm(node, ctx, name);
86
+ return null;
87
+ default: {
88
+ // Regular function call
89
+ if (!name.startsWith(':') && !name.startsWith('(')) {
90
+ ctx.calls.push({ name, line: node.startPosition.row + 1 });
91
+ }
92
+ return null;
93
+ }
94
+ }
95
+ }
96
+
97
+ function findFirstSymbol(listNode: TreeSitterNode): TreeSitterNode | null {
98
+ for (let i = 0; i < listNode.childCount; i++) {
99
+ const child = listNode.child(i);
100
+ if (!child) continue;
101
+ // Skip delimiters and metadata
102
+ if ('()[]{}#'.includes(child.type) || child.type === 'meta_lit') continue;
103
+ if (child.type === 'sym_lit' || child.type === 'kwd_lit') return child;
104
+ break;
105
+ }
106
+ return null;
107
+ }
108
+
109
+ function findSecondSymbol(listNode: TreeSitterNode): TreeSitterNode | null {
110
+ let count = 0;
111
+ for (let i = 0; i < listNode.childCount; i++) {
112
+ const child = listNode.child(i);
113
+ if (!child) continue;
114
+ if ('()[]{}#'.includes(child.type) || child.type === 'meta_lit') continue;
115
+ if (child.type === 'sym_lit' || child.type === 'kwd_lit') {
116
+ count++;
117
+ if (count === 2) return child;
118
+ }
119
+ }
120
+ return null;
121
+ }
122
+
123
+ function handleNsForm(node: TreeSitterNode, ctx: ExtractorOutput): string | null {
124
+ const nameNode = findSecondSymbol(node);
125
+ if (!nameNode) return null;
126
+
127
+ const nsName = nameNode.text;
128
+ ctx.definitions.push({
129
+ name: nsName,
130
+ kind: 'module',
131
+ line: node.startPosition.row + 1,
132
+ endLine: nodeEndLine(node),
133
+ });
134
+
135
+ // Extract requires from ns form
136
+ for (let i = 0; i < node.childCount; i++) {
137
+ const child = node.child(i);
138
+ if (!child) continue;
139
+ if (child.type === 'list_lit') {
140
+ const kw = findFirstSymbol(child);
141
+ if (kw && (kw.text === ':require' || kw.text === ':import' || kw.text === ':use')) {
142
+ extractNsRequires(child, ctx);
143
+ }
144
+ }
145
+ }
146
+
147
+ return nsName;
148
+ }
149
+
150
+ function extractNsRequires(requireForm: TreeSitterNode, ctx: ExtractorOutput): void {
151
+ for (let i = 0; i < requireForm.childCount; i++) {
152
+ const child = requireForm.child(i);
153
+ if (!child) continue;
154
+ // Vector form: [some.ns :as alias]
155
+ if (child.type === 'vec_lit') {
156
+ const sym = findFirstSymbol(child);
157
+ if (sym) {
158
+ ctx.imports.push({
159
+ source: sym.text,
160
+ names: [sym.text.split('.').pop() || sym.text],
161
+ line: child.startPosition.row + 1,
162
+ });
163
+ }
164
+ }
165
+ // Symbol form: some.ns
166
+ if (child.type === 'sym_lit' && i > 0) {
167
+ const text = child.text;
168
+ if (!text.startsWith(':')) {
169
+ ctx.imports.push({
170
+ source: text,
171
+ names: [text.split('.').pop() || text],
172
+ line: child.startPosition.row + 1,
173
+ });
174
+ }
175
+ }
176
+ }
177
+ }
178
+
179
+ function handleDefForm(
180
+ node: TreeSitterNode,
181
+ ctx: ExtractorOutput,
182
+ currentNs: string | null,
183
+ kindOrFallback: 'variable' | 'function',
184
+ ): void {
185
+ const nameNode = findSecondSymbol(node);
186
+ if (!nameNode) return;
187
+
188
+ const rawName = nameNode.text;
189
+ const fullName = currentNs ? `${currentNs}/${rawName}` : rawName;
190
+
191
+ ctx.definitions.push({
192
+ name: fullName,
193
+ kind: kindOrFallback,
194
+ line: node.startPosition.row + 1,
195
+ endLine: nodeEndLine(node),
196
+ });
197
+ }
198
+
199
+ function handleDefnForm(
200
+ node: TreeSitterNode,
201
+ ctx: ExtractorOutput,
202
+ currentNs: string | null,
203
+ visibility: 'public' | 'private',
204
+ ): void {
205
+ const nameNode = findSecondSymbol(node);
206
+ if (!nameNode) return;
207
+
208
+ const rawName = nameNode.text;
209
+ const fullName = currentNs ? `${currentNs}/${rawName}` : rawName;
210
+ const params = extractClojureParams(node);
211
+
212
+ ctx.definitions.push({
213
+ name: fullName,
214
+ kind: 'function',
215
+ line: node.startPosition.row + 1,
216
+ endLine: nodeEndLine(node),
217
+ visibility,
218
+ children: params.length > 0 ? params : undefined,
219
+ });
220
+ }
221
+
222
+ function extractClojureParams(defnNode: TreeSitterNode): SubDeclaration[] {
223
+ const params: SubDeclaration[] = [];
224
+ // Find the parameter vector [x y z]
225
+ for (let i = 0; i < defnNode.childCount; i++) {
226
+ const child = defnNode.child(i);
227
+ if (!child || child.type !== 'vec_lit') continue;
228
+ for (let j = 0; j < child.childCount; j++) {
229
+ const param = child.child(j);
230
+ if (!param) continue;
231
+ if (param.type === 'sym_lit') {
232
+ params.push({ name: param.text, kind: 'parameter', line: param.startPosition.row + 1 });
233
+ }
234
+ }
235
+ break; // Only first vector is params
236
+ }
237
+ return params;
238
+ }
239
+
240
+ function handleDefprotocol(node: TreeSitterNode, ctx: ExtractorOutput): void {
241
+ const nameNode = findSecondSymbol(node);
242
+ if (!nameNode) return;
243
+
244
+ ctx.definitions.push({
245
+ name: nameNode.text,
246
+ kind: 'interface',
247
+ line: node.startPosition.row + 1,
248
+ endLine: nodeEndLine(node),
249
+ });
250
+ }
251
+
252
+ function handleDefrecord(node: TreeSitterNode, ctx: ExtractorOutput, keyword: string): void {
253
+ const nameNode = findSecondSymbol(node);
254
+ if (!nameNode) return;
255
+
256
+ ctx.definitions.push({
257
+ name: nameNode.text,
258
+ kind: keyword === 'defrecord' ? 'record' : 'type',
259
+ line: node.startPosition.row + 1,
260
+ endLine: nodeEndLine(node),
261
+ });
262
+ }
263
+
264
+ function handleImportForm(node: TreeSitterNode, ctx: ExtractorOutput, keyword: string): void {
265
+ const nameNode = findSecondSymbol(node);
266
+ if (!nameNode) return;
267
+
268
+ ctx.imports.push({
269
+ source: nameNode.text,
270
+ names: [keyword],
271
+ line: node.startPosition.row + 1,
272
+ });
273
+ }