gitnexus 1.4.7 → 1.4.9

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 (242) hide show
  1. package/README.md +29 -1
  2. package/dist/cli/ai-context.d.ts +1 -1
  3. package/dist/cli/ai-context.js +1 -1
  4. package/dist/cli/analyze.d.ts +2 -0
  5. package/dist/cli/analyze.js +54 -21
  6. package/dist/cli/index-repo.d.ts +15 -0
  7. package/dist/cli/index-repo.js +115 -0
  8. package/dist/cli/index.js +13 -3
  9. package/dist/cli/setup.js +90 -10
  10. package/dist/cli/wiki.d.ts +4 -0
  11. package/dist/cli/wiki.js +174 -53
  12. package/dist/config/supported-languages.d.ts +33 -1
  13. package/dist/config/supported-languages.js +32 -0
  14. package/dist/core/embeddings/embedder.d.ts +6 -1
  15. package/dist/core/embeddings/embedder.js +65 -5
  16. package/dist/core/embeddings/embedding-pipeline.js +11 -9
  17. package/dist/core/embeddings/http-client.d.ts +31 -0
  18. package/dist/core/embeddings/http-client.js +179 -0
  19. package/dist/core/embeddings/index.d.ts +1 -0
  20. package/dist/core/embeddings/index.js +1 -0
  21. package/dist/core/embeddings/types.d.ts +1 -1
  22. package/dist/core/graph/graph.js +9 -1
  23. package/dist/core/graph/types.d.ts +11 -2
  24. package/dist/core/ingestion/call-processor.d.ts +66 -2
  25. package/dist/core/ingestion/call-processor.js +650 -30
  26. package/dist/core/ingestion/call-routing.d.ts +9 -18
  27. package/dist/core/ingestion/call-routing.js +0 -19
  28. package/dist/core/ingestion/cobol/cobol-copy-expander.d.ts +57 -0
  29. package/dist/core/ingestion/cobol/cobol-copy-expander.js +385 -0
  30. package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +210 -0
  31. package/dist/core/ingestion/cobol/cobol-preprocessor.js +1509 -0
  32. package/dist/core/ingestion/cobol/jcl-parser.d.ts +68 -0
  33. package/dist/core/ingestion/cobol/jcl-parser.js +217 -0
  34. package/dist/core/ingestion/cobol/jcl-processor.d.ts +33 -0
  35. package/dist/core/ingestion/cobol/jcl-processor.js +229 -0
  36. package/dist/core/ingestion/cobol-processor.d.ts +54 -0
  37. package/dist/core/ingestion/cobol-processor.js +1186 -0
  38. package/dist/core/ingestion/entry-point-scoring.d.ts +17 -0
  39. package/dist/core/ingestion/entry-point-scoring.js +52 -28
  40. package/dist/core/ingestion/export-detection.d.ts +47 -8
  41. package/dist/core/ingestion/export-detection.js +29 -50
  42. package/dist/core/ingestion/field-extractor.d.ts +29 -0
  43. package/dist/core/ingestion/field-extractor.js +25 -0
  44. package/dist/core/ingestion/field-extractors/configs/c-cpp.d.ts +3 -0
  45. package/dist/core/ingestion/field-extractors/configs/c-cpp.js +108 -0
  46. package/dist/core/ingestion/field-extractors/configs/csharp.d.ts +8 -0
  47. package/dist/core/ingestion/field-extractors/configs/csharp.js +73 -0
  48. package/dist/core/ingestion/field-extractors/configs/dart.d.ts +8 -0
  49. package/dist/core/ingestion/field-extractors/configs/dart.js +76 -0
  50. package/dist/core/ingestion/field-extractors/configs/go.d.ts +11 -0
  51. package/dist/core/ingestion/field-extractors/configs/go.js +64 -0
  52. package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +44 -0
  53. package/dist/core/ingestion/field-extractors/configs/helpers.js +134 -0
  54. package/dist/core/ingestion/field-extractors/configs/jvm.d.ts +3 -0
  55. package/dist/core/ingestion/field-extractors/configs/jvm.js +118 -0
  56. package/dist/core/ingestion/field-extractors/configs/php.d.ts +8 -0
  57. package/dist/core/ingestion/field-extractors/configs/php.js +67 -0
  58. package/dist/core/ingestion/field-extractors/configs/python.d.ts +12 -0
  59. package/dist/core/ingestion/field-extractors/configs/python.js +91 -0
  60. package/dist/core/ingestion/field-extractors/configs/ruby.d.ts +16 -0
  61. package/dist/core/ingestion/field-extractors/configs/ruby.js +75 -0
  62. package/dist/core/ingestion/field-extractors/configs/rust.d.ts +9 -0
  63. package/dist/core/ingestion/field-extractors/configs/rust.js +55 -0
  64. package/dist/core/ingestion/field-extractors/configs/swift.d.ts +8 -0
  65. package/dist/core/ingestion/field-extractors/configs/swift.js +63 -0
  66. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.d.ts +3 -0
  67. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +60 -0
  68. package/dist/core/ingestion/field-extractors/generic.d.ts +46 -0
  69. package/dist/core/ingestion/field-extractors/generic.js +111 -0
  70. package/dist/core/ingestion/field-extractors/typescript.d.ts +77 -0
  71. package/dist/core/ingestion/field-extractors/typescript.js +291 -0
  72. package/dist/core/ingestion/field-types.d.ts +59 -0
  73. package/dist/core/ingestion/field-types.js +2 -0
  74. package/dist/core/ingestion/framework-detection.d.ts +97 -2
  75. package/dist/core/ingestion/framework-detection.js +114 -14
  76. package/dist/core/ingestion/heritage-processor.js +62 -66
  77. package/dist/core/ingestion/import-processor.d.ts +9 -10
  78. package/dist/core/ingestion/import-processor.js +150 -196
  79. package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.d.ts +6 -9
  80. package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.js +20 -2
  81. package/dist/core/ingestion/import-resolvers/dart.d.ts +7 -0
  82. package/dist/core/ingestion/import-resolvers/dart.js +44 -0
  83. package/dist/core/ingestion/{resolvers → import-resolvers}/go.d.ts +4 -5
  84. package/dist/core/ingestion/{resolvers → import-resolvers}/go.js +17 -0
  85. package/dist/core/ingestion/{resolvers → import-resolvers}/jvm.d.ts +10 -1
  86. package/dist/core/ingestion/import-resolvers/jvm.js +159 -0
  87. package/dist/core/ingestion/import-resolvers/php.d.ts +25 -0
  88. package/dist/core/ingestion/import-resolvers/php.js +80 -0
  89. package/dist/core/ingestion/{resolvers → import-resolvers}/python.d.ts +9 -3
  90. package/dist/core/ingestion/{resolvers → import-resolvers}/python.js +35 -3
  91. package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.d.ts +5 -2
  92. package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.js +7 -2
  93. package/dist/core/ingestion/{resolvers → import-resolvers}/rust.d.ts +5 -2
  94. package/dist/core/ingestion/{resolvers → import-resolvers}/rust.js +41 -2
  95. package/dist/core/ingestion/{resolvers → import-resolvers}/standard.d.ts +15 -7
  96. package/dist/core/ingestion/{resolvers → import-resolvers}/standard.js +22 -3
  97. package/dist/core/ingestion/import-resolvers/swift.d.ts +7 -0
  98. package/dist/core/ingestion/import-resolvers/swift.js +23 -0
  99. package/dist/core/ingestion/import-resolvers/types.d.ts +44 -0
  100. package/dist/core/ingestion/import-resolvers/types.js +6 -0
  101. package/dist/core/ingestion/{resolvers → import-resolvers}/utils.d.ts +2 -0
  102. package/dist/core/ingestion/{resolvers → import-resolvers}/utils.js +7 -0
  103. package/dist/core/ingestion/language-config.d.ts +6 -0
  104. package/dist/core/ingestion/language-config.js +13 -0
  105. package/dist/core/ingestion/language-provider.d.ts +121 -0
  106. package/dist/core/ingestion/language-provider.js +24 -0
  107. package/dist/core/ingestion/languages/c-cpp.d.ts +12 -0
  108. package/dist/core/ingestion/languages/c-cpp.js +71 -0
  109. package/dist/core/ingestion/languages/cobol.d.ts +1 -0
  110. package/dist/core/ingestion/languages/cobol.js +26 -0
  111. package/dist/core/ingestion/languages/csharp.d.ts +8 -0
  112. package/dist/core/ingestion/languages/csharp.js +49 -0
  113. package/dist/core/ingestion/languages/dart.d.ts +12 -0
  114. package/dist/core/ingestion/languages/dart.js +58 -0
  115. package/dist/core/ingestion/languages/go.d.ts +11 -0
  116. package/dist/core/ingestion/languages/go.js +28 -0
  117. package/dist/core/ingestion/languages/index.d.ts +38 -0
  118. package/dist/core/ingestion/languages/index.js +63 -0
  119. package/dist/core/ingestion/languages/java.d.ts +9 -0
  120. package/dist/core/ingestion/languages/java.js +29 -0
  121. package/dist/core/ingestion/languages/kotlin.d.ts +9 -0
  122. package/dist/core/ingestion/languages/kotlin.js +53 -0
  123. package/dist/core/ingestion/languages/php.d.ts +8 -0
  124. package/dist/core/ingestion/languages/php.js +145 -0
  125. package/dist/core/ingestion/languages/python.d.ts +12 -0
  126. package/dist/core/ingestion/languages/python.js +39 -0
  127. package/dist/core/ingestion/languages/ruby.d.ts +9 -0
  128. package/dist/core/ingestion/languages/ruby.js +44 -0
  129. package/dist/core/ingestion/languages/rust.d.ts +12 -0
  130. package/dist/core/ingestion/languages/rust.js +44 -0
  131. package/dist/core/ingestion/languages/swift.d.ts +12 -0
  132. package/dist/core/ingestion/languages/swift.js +133 -0
  133. package/dist/core/ingestion/languages/typescript.d.ts +10 -0
  134. package/dist/core/ingestion/languages/typescript.js +60 -0
  135. package/dist/core/ingestion/markdown-processor.d.ts +17 -0
  136. package/dist/core/ingestion/markdown-processor.js +124 -0
  137. package/dist/core/ingestion/mro-processor.js +22 -18
  138. package/dist/core/ingestion/named-binding-processor.d.ts +18 -0
  139. package/dist/core/ingestion/named-binding-processor.js +42 -0
  140. package/dist/core/ingestion/named-bindings/csharp.d.ts +3 -0
  141. package/dist/core/ingestion/named-bindings/csharp.js +37 -0
  142. package/dist/core/ingestion/named-bindings/java.d.ts +3 -0
  143. package/dist/core/ingestion/named-bindings/java.js +29 -0
  144. package/dist/core/ingestion/named-bindings/kotlin.d.ts +3 -0
  145. package/dist/core/ingestion/named-bindings/kotlin.js +36 -0
  146. package/dist/core/ingestion/named-bindings/php.d.ts +3 -0
  147. package/dist/core/ingestion/named-bindings/php.js +61 -0
  148. package/dist/core/ingestion/named-bindings/python.d.ts +3 -0
  149. package/dist/core/ingestion/named-bindings/python.js +49 -0
  150. package/dist/core/ingestion/named-bindings/rust.d.ts +3 -0
  151. package/dist/core/ingestion/named-bindings/rust.js +64 -0
  152. package/dist/core/ingestion/named-bindings/types.d.ts +16 -0
  153. package/dist/core/ingestion/named-bindings/types.js +6 -0
  154. package/dist/core/ingestion/named-bindings/typescript.d.ts +3 -0
  155. package/dist/core/ingestion/named-bindings/typescript.js +58 -0
  156. package/dist/core/ingestion/parsing-processor.d.ts +6 -2
  157. package/dist/core/ingestion/parsing-processor.js +125 -85
  158. package/dist/core/ingestion/pipeline.d.ts +10 -0
  159. package/dist/core/ingestion/pipeline.js +1235 -317
  160. package/dist/core/ingestion/resolution-context.d.ts +5 -0
  161. package/dist/core/ingestion/resolution-context.js +8 -5
  162. package/dist/core/ingestion/route-extractors/expo.d.ts +1 -0
  163. package/dist/core/ingestion/route-extractors/expo.js +36 -0
  164. package/dist/core/ingestion/route-extractors/middleware.d.ts +47 -0
  165. package/dist/core/ingestion/route-extractors/middleware.js +143 -0
  166. package/dist/core/ingestion/route-extractors/nextjs.d.ts +3 -0
  167. package/dist/core/ingestion/route-extractors/nextjs.js +76 -0
  168. package/dist/core/ingestion/route-extractors/php.d.ts +7 -0
  169. package/dist/core/ingestion/route-extractors/php.js +21 -0
  170. package/dist/core/ingestion/route-extractors/response-shapes.d.ts +20 -0
  171. package/dist/core/ingestion/route-extractors/response-shapes.js +290 -0
  172. package/dist/core/ingestion/symbol-table.d.ts +16 -0
  173. package/dist/core/ingestion/symbol-table.js +20 -6
  174. package/dist/core/ingestion/tree-sitter-queries.d.ts +10 -9
  175. package/dist/core/ingestion/tree-sitter-queries.js +274 -11
  176. package/dist/core/ingestion/type-env.d.ts +42 -18
  177. package/dist/core/ingestion/type-env.js +481 -106
  178. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +5 -0
  179. package/dist/core/ingestion/type-extractors/c-cpp.js +119 -0
  180. package/dist/core/ingestion/type-extractors/csharp.js +149 -16
  181. package/dist/core/ingestion/type-extractors/dart.d.ts +15 -0
  182. package/dist/core/ingestion/type-extractors/dart.js +371 -0
  183. package/dist/core/ingestion/type-extractors/jvm.js +169 -66
  184. package/dist/core/ingestion/type-extractors/rust.js +35 -1
  185. package/dist/core/ingestion/type-extractors/shared.d.ts +1 -15
  186. package/dist/core/ingestion/type-extractors/shared.js +14 -112
  187. package/dist/core/ingestion/type-extractors/swift.js +338 -7
  188. package/dist/core/ingestion/type-extractors/types.d.ts +40 -8
  189. package/dist/core/ingestion/type-extractors/typescript.js +141 -9
  190. package/dist/core/ingestion/utils/ast-helpers.d.ts +83 -0
  191. package/dist/core/ingestion/utils/ast-helpers.js +817 -0
  192. package/dist/core/ingestion/utils/call-analysis.d.ts +73 -0
  193. package/dist/core/ingestion/utils/call-analysis.js +527 -0
  194. package/dist/core/ingestion/utils/event-loop.d.ts +5 -0
  195. package/dist/core/ingestion/utils/event-loop.js +5 -0
  196. package/dist/core/ingestion/utils/language-detection.d.ts +9 -0
  197. package/dist/core/ingestion/utils/language-detection.js +70 -0
  198. package/dist/core/ingestion/utils/verbose.d.ts +1 -0
  199. package/dist/core/ingestion/utils/verbose.js +7 -0
  200. package/dist/core/ingestion/workers/parse-worker.d.ts +55 -5
  201. package/dist/core/ingestion/workers/parse-worker.js +415 -225
  202. package/dist/core/lbug/csv-generator.js +51 -1
  203. package/dist/core/lbug/lbug-adapter.d.ts +10 -0
  204. package/dist/core/lbug/lbug-adapter.js +75 -4
  205. package/dist/core/lbug/schema.d.ts +8 -4
  206. package/dist/core/lbug/schema.js +65 -4
  207. package/dist/core/tree-sitter/parser-loader.js +7 -1
  208. package/dist/core/wiki/cursor-client.d.ts +31 -0
  209. package/dist/core/wiki/cursor-client.js +127 -0
  210. package/dist/core/wiki/generator.d.ts +28 -9
  211. package/dist/core/wiki/generator.js +115 -18
  212. package/dist/core/wiki/graph-queries.d.ts +4 -0
  213. package/dist/core/wiki/graph-queries.js +7 -1
  214. package/dist/core/wiki/llm-client.d.ts +2 -0
  215. package/dist/core/wiki/llm-client.js +8 -4
  216. package/dist/core/wiki/prompts.d.ts +3 -3
  217. package/dist/core/wiki/prompts.js +6 -0
  218. package/dist/mcp/core/embedder.js +11 -3
  219. package/dist/mcp/core/lbug-adapter.d.ts +5 -0
  220. package/dist/mcp/core/lbug-adapter.js +23 -2
  221. package/dist/mcp/local/local-backend.d.ts +38 -5
  222. package/dist/mcp/local/local-backend.js +804 -63
  223. package/dist/mcp/resources.js +2 -0
  224. package/dist/mcp/tools.js +73 -4
  225. package/dist/server/api.d.ts +19 -1
  226. package/dist/server/api.js +66 -6
  227. package/dist/storage/git.d.ts +12 -0
  228. package/dist/storage/git.js +21 -0
  229. package/dist/storage/repo-manager.d.ts +3 -0
  230. package/package.json +25 -16
  231. package/dist/core/ingestion/named-binding-extraction.d.ts +0 -61
  232. package/dist/core/ingestion/named-binding-extraction.js +0 -363
  233. package/dist/core/ingestion/resolvers/index.d.ts +0 -18
  234. package/dist/core/ingestion/resolvers/index.js +0 -13
  235. package/dist/core/ingestion/resolvers/jvm.js +0 -87
  236. package/dist/core/ingestion/resolvers/php.d.ts +0 -15
  237. package/dist/core/ingestion/resolvers/php.js +0 -35
  238. package/dist/core/ingestion/type-extractors/index.d.ts +0 -22
  239. package/dist/core/ingestion/type-extractors/index.js +0 -31
  240. package/dist/core/ingestion/utils.d.ts +0 -138
  241. package/dist/core/ingestion/utils.js +0 -1290
  242. package/scripts/patch-tree-sitter-swift.cjs +0 -74
@@ -0,0 +1,817 @@
1
+ import { generateId } from '../../../lib/utils.js';
2
+ import { extractSimpleTypeName } from '../type-extractors/shared.js';
3
+ /**
4
+ * Ordered list of definition capture keys for tree-sitter query matches.
5
+ * Used to extract the definition node from a capture map.
6
+ */
7
+ export const DEFINITION_CAPTURE_KEYS = [
8
+ 'definition.function',
9
+ 'definition.class',
10
+ 'definition.interface',
11
+ 'definition.method',
12
+ 'definition.struct',
13
+ 'definition.enum',
14
+ 'definition.namespace',
15
+ 'definition.module',
16
+ 'definition.trait',
17
+ 'definition.impl',
18
+ 'definition.type',
19
+ 'definition.const',
20
+ 'definition.static',
21
+ 'definition.typedef',
22
+ 'definition.macro',
23
+ 'definition.union',
24
+ 'definition.property',
25
+ 'definition.record',
26
+ 'definition.delegate',
27
+ 'definition.annotation',
28
+ 'definition.constructor',
29
+ 'definition.template',
30
+ ];
31
+ /** Extract the definition node from a tree-sitter query capture map. */
32
+ export const getDefinitionNodeFromCaptures = (captureMap) => {
33
+ for (const key of DEFINITION_CAPTURE_KEYS) {
34
+ if (captureMap[key])
35
+ return captureMap[key];
36
+ }
37
+ return null;
38
+ };
39
+ /**
40
+ * Node types that represent function/method definitions across languages.
41
+ * Used to find the enclosing function for a call site.
42
+ */
43
+ export const FUNCTION_NODE_TYPES = new Set([
44
+ // TypeScript/JavaScript
45
+ 'function_declaration',
46
+ 'arrow_function',
47
+ 'function_expression',
48
+ 'method_definition',
49
+ 'generator_function_declaration',
50
+ // Python
51
+ 'function_definition',
52
+ // Common async variants
53
+ 'async_function_declaration',
54
+ 'async_arrow_function',
55
+ // Java
56
+ 'method_declaration',
57
+ 'constructor_declaration',
58
+ // C/C++
59
+ // 'function_definition' already included above
60
+ // Go
61
+ // 'method_declaration' already included from Java
62
+ // C#
63
+ 'local_function_statement',
64
+ // Rust
65
+ 'function_item',
66
+ 'impl_item', // Methods inside impl blocks
67
+ // PHP
68
+ 'anonymous_function',
69
+ // Kotlin
70
+ 'lambda_literal',
71
+ // Swift
72
+ 'init_declaration',
73
+ 'deinit_declaration',
74
+ // Ruby
75
+ 'method', // def foo
76
+ 'singleton_method', // def self.foo
77
+ // Dart
78
+ 'function_signature',
79
+ 'method_signature',
80
+ ]);
81
+ /**
82
+ * Node types for standard function declarations that need C/C++ declarator handling.
83
+ * Used by extractFunctionName to determine how to extract the function name.
84
+ */
85
+ export const FUNCTION_DECLARATION_TYPES = new Set([
86
+ 'function_declaration',
87
+ 'function_definition',
88
+ 'async_function_declaration',
89
+ 'generator_function_declaration',
90
+ 'function_item',
91
+ ]);
92
+ /** AST node types that represent a class-like container (for HAS_METHOD edge extraction) */
93
+ export const CLASS_CONTAINER_TYPES = new Set([
94
+ 'class_declaration', 'abstract_class_declaration',
95
+ 'interface_declaration', 'struct_declaration', 'record_declaration',
96
+ 'class_specifier', 'struct_specifier',
97
+ 'impl_item', 'trait_item', 'struct_item', 'enum_item',
98
+ 'class_definition',
99
+ 'trait_declaration',
100
+ 'protocol_declaration',
101
+ // Ruby
102
+ 'class',
103
+ 'module',
104
+ // Kotlin
105
+ 'object_declaration',
106
+ 'companion_object',
107
+ ]);
108
+ export const CONTAINER_TYPE_TO_LABEL = {
109
+ class_declaration: 'Class',
110
+ abstract_class_declaration: 'Class',
111
+ interface_declaration: 'Interface',
112
+ struct_declaration: 'Struct',
113
+ struct_specifier: 'Struct',
114
+ class_specifier: 'Class',
115
+ class_definition: 'Class',
116
+ impl_item: 'Impl',
117
+ trait_item: 'Trait',
118
+ struct_item: 'Struct',
119
+ enum_item: 'Enum',
120
+ trait_declaration: 'Trait',
121
+ record_declaration: 'Record',
122
+ protocol_declaration: 'Interface',
123
+ class: 'Class',
124
+ module: 'Module',
125
+ object_declaration: 'Class',
126
+ companion_object: 'Class',
127
+ };
128
+ /** Check if a Kotlin function_declaration capture is inside a class_body (i.e., a method).
129
+ * Kotlin grammar uses function_declaration for both top-level functions and class methods.
130
+ * Returns true when the captured definition node has a class_body ancestor. */
131
+ export function isKotlinClassMethod(captureNode) {
132
+ let ancestor = captureNode?.parent;
133
+ while (ancestor) {
134
+ if (ancestor.type === 'class_body')
135
+ return true;
136
+ ancestor = ancestor.parent;
137
+ }
138
+ return false;
139
+ }
140
+ /**
141
+ * Determine the graph node label from a tree-sitter capture map.
142
+ * Handles language-specific reclassification via the provider's labelOverride hook
143
+ * (e.g. C/C++ duplicate skipping, Kotlin Method promotion).
144
+ * Returns null if the capture should be skipped (import, call, C/C++ duplicate, missing name).
145
+ */
146
+ export function getLabelFromCaptures(captureMap, provider) {
147
+ if (captureMap['import'] || captureMap['call'])
148
+ return null;
149
+ if (!captureMap['name'] && !captureMap['definition.constructor'])
150
+ return null;
151
+ if (captureMap['definition.function']) {
152
+ if (provider.labelOverride) {
153
+ const override = provider.labelOverride(captureMap['definition.function'], 'Function');
154
+ if (override !== 'Function')
155
+ return override;
156
+ }
157
+ return 'Function';
158
+ }
159
+ if (captureMap['definition.class'])
160
+ return 'Class';
161
+ if (captureMap['definition.interface'])
162
+ return 'Interface';
163
+ if (captureMap['definition.method'])
164
+ return 'Method';
165
+ if (captureMap['definition.struct'])
166
+ return 'Struct';
167
+ if (captureMap['definition.enum'])
168
+ return 'Enum';
169
+ if (captureMap['definition.namespace'])
170
+ return 'Namespace';
171
+ if (captureMap['definition.module'])
172
+ return 'Module';
173
+ if (captureMap['definition.trait'])
174
+ return 'Trait';
175
+ if (captureMap['definition.impl'])
176
+ return 'Impl';
177
+ if (captureMap['definition.type'])
178
+ return 'TypeAlias';
179
+ if (captureMap['definition.const'])
180
+ return 'Const';
181
+ if (captureMap['definition.static'])
182
+ return 'Static';
183
+ if (captureMap['definition.typedef'])
184
+ return 'Typedef';
185
+ if (captureMap['definition.macro'])
186
+ return 'Macro';
187
+ if (captureMap['definition.union'])
188
+ return 'Union';
189
+ if (captureMap['definition.property'])
190
+ return 'Property';
191
+ if (captureMap['definition.record'])
192
+ return 'Record';
193
+ if (captureMap['definition.delegate'])
194
+ return 'Delegate';
195
+ if (captureMap['definition.annotation'])
196
+ return 'Annotation';
197
+ if (captureMap['definition.constructor'])
198
+ return 'Constructor';
199
+ if (captureMap['definition.template'])
200
+ return 'Template';
201
+ return 'CodeElement';
202
+ }
203
+ /** Walk up AST to find enclosing class/struct/interface/impl, return its generateId or null.
204
+ * For Go method_declaration nodes, extracts receiver type (e.g. `func (u *User) Save()` → User struct). */
205
+ export const findEnclosingClassId = (node, filePath) => {
206
+ let current = node.parent;
207
+ while (current) {
208
+ // Go: method_declaration has a receiver parameter with the struct type
209
+ if (current.type === 'method_declaration') {
210
+ const receiver = current.childForFieldName?.('receiver');
211
+ if (receiver) {
212
+ // receiver is a parameter_list: (u *User) or (u User)
213
+ const paramDecl = receiver.namedChildren?.find?.((c) => c.type === 'parameter_declaration');
214
+ if (paramDecl) {
215
+ const typeNode = paramDecl.childForFieldName?.('type');
216
+ if (typeNode) {
217
+ // Unwrap pointer_type (*User → User)
218
+ const inner = typeNode.type === 'pointer_type' ? typeNode.firstNamedChild : typeNode;
219
+ if (inner && (inner.type === 'type_identifier' || inner.type === 'identifier')) {
220
+ return generateId('Struct', `${filePath}:${inner.text}`);
221
+ }
222
+ }
223
+ }
224
+ }
225
+ }
226
+ // Go: type_declaration wrapping a struct_type (type User struct { ... })
227
+ // field_declaration → field_declaration_list → struct_type → type_spec → type_declaration
228
+ if (current.type === 'type_declaration') {
229
+ const typeSpec = current.children?.find((c) => c.type === 'type_spec');
230
+ if (typeSpec) {
231
+ const typeBody = typeSpec.childForFieldName?.('type');
232
+ if (typeBody?.type === 'struct_type' || typeBody?.type === 'interface_type') {
233
+ const nameNode = typeSpec.childForFieldName?.('name');
234
+ if (nameNode) {
235
+ const label = typeBody.type === 'struct_type' ? 'Struct' : 'Interface';
236
+ return generateId(label, `${filePath}:${nameNode.text}`);
237
+ }
238
+ }
239
+ }
240
+ }
241
+ if (CLASS_CONTAINER_TYPES.has(current.type)) {
242
+ // Rust impl_item: for `impl Trait for Struct {}`, pick the type after `for`
243
+ if (current.type === 'impl_item') {
244
+ const children = current.children ?? [];
245
+ const forIdx = children.findIndex((c) => c.text === 'for');
246
+ if (forIdx !== -1) {
247
+ const nameNode = children.slice(forIdx + 1).find((c) => c.type === 'type_identifier' || c.type === 'identifier');
248
+ if (nameNode) {
249
+ return generateId('Impl', `${filePath}:${nameNode.text}`);
250
+ }
251
+ }
252
+ // Fall through: plain `impl Struct {}` — use first type_identifier below
253
+ }
254
+ const nameNode = current.childForFieldName?.('name')
255
+ ?? current.children?.find((c) => c.type === 'type_identifier' || c.type === 'identifier' || c.type === 'name' || c.type === 'constant');
256
+ if (nameNode) {
257
+ const label = CONTAINER_TYPE_TO_LABEL[current.type] || 'Class';
258
+ return generateId(label, `${filePath}:${nameNode.text}`);
259
+ }
260
+ }
261
+ current = current.parent;
262
+ }
263
+ return null;
264
+ };
265
+ /**
266
+ * Find a child of `childType` within a sibling node of `siblingType`.
267
+ * Used for Kotlin AST traversal where visibility_modifier lives inside a modifiers sibling.
268
+ */
269
+ export const findSiblingChild = (parent, siblingType, childType) => {
270
+ for (let i = 0; i < parent.childCount; i++) {
271
+ const sibling = parent.child(i);
272
+ if (sibling?.type === siblingType) {
273
+ for (let j = 0; j < sibling.childCount; j++) {
274
+ const child = sibling.child(j);
275
+ if (child?.type === childType)
276
+ return child;
277
+ }
278
+ }
279
+ }
280
+ return null;
281
+ };
282
+ /**
283
+ * Extract function name and label from a function_definition or similar AST node.
284
+ * Handles C/C++ qualified_identifier (ClassName::MethodName) and other language patterns.
285
+ */
286
+ export const extractFunctionName = (node) => {
287
+ let funcName = null;
288
+ let label = 'Function';
289
+ // Swift init/deinit
290
+ if (node.type === 'init_declaration' || node.type === 'deinit_declaration') {
291
+ return {
292
+ funcName: node.type === 'init_declaration' ? 'init' : 'deinit',
293
+ label: 'Constructor',
294
+ };
295
+ }
296
+ if (FUNCTION_DECLARATION_TYPES.has(node.type)) {
297
+ // C/C++: function_definition -> [pointer_declarator ->] function_declarator -> qualified_identifier/identifier
298
+ // Unwrap pointer_declarator / reference_declarator wrappers to reach function_declarator
299
+ let declarator = node.childForFieldName?.('declarator');
300
+ if (!declarator) {
301
+ for (let i = 0; i < node.childCount; i++) {
302
+ const c = node.child(i);
303
+ if (c?.type === 'function_declarator') {
304
+ declarator = c;
305
+ break;
306
+ }
307
+ }
308
+ }
309
+ while (declarator && (declarator.type === 'pointer_declarator' || declarator.type === 'reference_declarator')) {
310
+ let nextDeclarator = declarator.childForFieldName?.('declarator');
311
+ if (!nextDeclarator) {
312
+ for (let i = 0; i < declarator.childCount; i++) {
313
+ const c = declarator.child(i);
314
+ if (c?.type === 'function_declarator' || c?.type === 'pointer_declarator' || c?.type === 'reference_declarator') {
315
+ nextDeclarator = c;
316
+ break;
317
+ }
318
+ }
319
+ }
320
+ declarator = nextDeclarator;
321
+ }
322
+ if (declarator) {
323
+ let innerDeclarator = declarator.childForFieldName?.('declarator');
324
+ if (!innerDeclarator) {
325
+ for (let i = 0; i < declarator.childCount; i++) {
326
+ const c = declarator.child(i);
327
+ if (c?.type === 'qualified_identifier' || c?.type === 'identifier'
328
+ || c?.type === 'field_identifier' || c?.type === 'parenthesized_declarator') {
329
+ innerDeclarator = c;
330
+ break;
331
+ }
332
+ }
333
+ }
334
+ if (innerDeclarator?.type === 'qualified_identifier') {
335
+ let nameNode = innerDeclarator.childForFieldName?.('name');
336
+ if (!nameNode) {
337
+ for (let i = 0; i < innerDeclarator.childCount; i++) {
338
+ const c = innerDeclarator.child(i);
339
+ if (c?.type === 'identifier') {
340
+ nameNode = c;
341
+ break;
342
+ }
343
+ }
344
+ }
345
+ if (nameNode?.text) {
346
+ funcName = nameNode.text;
347
+ label = 'Method';
348
+ }
349
+ }
350
+ else if (innerDeclarator?.type === 'identifier' || innerDeclarator?.type === 'field_identifier') {
351
+ // field_identifier is used for method names inside C++ class bodies
352
+ funcName = innerDeclarator.text;
353
+ if (innerDeclarator.type === 'field_identifier')
354
+ label = 'Method';
355
+ }
356
+ else if (innerDeclarator?.type === 'parenthesized_declarator') {
357
+ let nestedId = null;
358
+ for (let i = 0; i < innerDeclarator.childCount; i++) {
359
+ const c = innerDeclarator.child(i);
360
+ if (c?.type === 'qualified_identifier' || c?.type === 'identifier') {
361
+ nestedId = c;
362
+ break;
363
+ }
364
+ }
365
+ if (nestedId?.type === 'qualified_identifier') {
366
+ let nameNode = nestedId.childForFieldName?.('name');
367
+ if (!nameNode) {
368
+ for (let i = 0; i < nestedId.childCount; i++) {
369
+ const c = nestedId.child(i);
370
+ if (c?.type === 'identifier') {
371
+ nameNode = c;
372
+ break;
373
+ }
374
+ }
375
+ }
376
+ if (nameNode?.text) {
377
+ funcName = nameNode.text;
378
+ label = 'Method';
379
+ }
380
+ }
381
+ else if (nestedId?.type === 'identifier') {
382
+ funcName = nestedId.text;
383
+ }
384
+ }
385
+ }
386
+ // Fallback for other languages (Kotlin uses simple_identifier, Swift uses simple_identifier)
387
+ if (!funcName) {
388
+ let nameNode = node.childForFieldName?.('name');
389
+ if (!nameNode) {
390
+ for (let i = 0; i < node.childCount; i++) {
391
+ const c = node.child(i);
392
+ if (c?.type === 'identifier' || c?.type === 'property_identifier' || c?.type === 'simple_identifier') {
393
+ nameNode = c;
394
+ break;
395
+ }
396
+ }
397
+ }
398
+ funcName = nameNode?.text;
399
+ }
400
+ }
401
+ else if (node.type === 'impl_item') {
402
+ let funcItem = null;
403
+ for (let i = 0; i < node.childCount; i++) {
404
+ const c = node.child(i);
405
+ if (c?.type === 'function_item') {
406
+ funcItem = c;
407
+ break;
408
+ }
409
+ }
410
+ if (funcItem) {
411
+ let nameNode = funcItem.childForFieldName?.('name');
412
+ if (!nameNode) {
413
+ for (let i = 0; i < funcItem.childCount; i++) {
414
+ const c = funcItem.child(i);
415
+ if (c?.type === 'identifier') {
416
+ nameNode = c;
417
+ break;
418
+ }
419
+ }
420
+ }
421
+ funcName = nameNode?.text;
422
+ label = 'Method';
423
+ }
424
+ }
425
+ else if (node.type === 'method_definition') {
426
+ let nameNode = node.childForFieldName?.('name');
427
+ if (!nameNode) {
428
+ for (let i = 0; i < node.childCount; i++) {
429
+ const c = node.child(i);
430
+ if (c?.type === 'property_identifier') {
431
+ nameNode = c;
432
+ break;
433
+ }
434
+ }
435
+ }
436
+ funcName = nameNode?.text;
437
+ label = 'Method';
438
+ }
439
+ else if (node.type === 'method_declaration' || node.type === 'constructor_declaration') {
440
+ let nameNode = node.childForFieldName?.('name');
441
+ if (!nameNode) {
442
+ for (let i = 0; i < node.childCount; i++) {
443
+ const c = node.child(i);
444
+ if (c?.type === 'identifier') {
445
+ nameNode = c;
446
+ break;
447
+ }
448
+ }
449
+ }
450
+ funcName = nameNode?.text;
451
+ label = 'Method';
452
+ }
453
+ else if (node.type === 'arrow_function' || node.type === 'function_expression') {
454
+ const parent = node.parent;
455
+ if (parent?.type === 'variable_declarator') {
456
+ let nameNode = parent.childForFieldName?.('name');
457
+ if (!nameNode) {
458
+ for (let i = 0; i < parent.childCount; i++) {
459
+ const c = parent.child(i);
460
+ if (c?.type === 'identifier') {
461
+ nameNode = c;
462
+ break;
463
+ }
464
+ }
465
+ }
466
+ funcName = nameNode?.text;
467
+ }
468
+ }
469
+ else if (node.type === 'method' || node.type === 'singleton_method') {
470
+ let nameNode = node.childForFieldName?.('name');
471
+ if (!nameNode) {
472
+ for (let i = 0; i < node.childCount; i++) {
473
+ const c = node.child(i);
474
+ if (c?.type === 'identifier') {
475
+ nameNode = c;
476
+ break;
477
+ }
478
+ }
479
+ }
480
+ funcName = nameNode?.text;
481
+ label = 'Method';
482
+ }
483
+ else if (node.type === 'function_signature') {
484
+ // Dart: top-level function signatures
485
+ let nameNode = node.childForFieldName?.('name');
486
+ if (!nameNode) {
487
+ for (let i = 0; i < node.childCount; i++) {
488
+ const c = node.child(i);
489
+ if (c?.type === 'identifier') {
490
+ nameNode = c;
491
+ break;
492
+ }
493
+ }
494
+ }
495
+ funcName = nameNode?.text ?? null;
496
+ }
497
+ else if (node.type === 'method_signature') {
498
+ // Dart: method_signature wraps function_signature
499
+ let funcSig = null;
500
+ for (let i = 0; i < node.childCount; i++) {
501
+ const c = node.child(i);
502
+ if (c?.type === 'function_signature') {
503
+ funcSig = c;
504
+ break;
505
+ }
506
+ }
507
+ if (funcSig) {
508
+ let nameNode = funcSig.childForFieldName?.('name');
509
+ if (!nameNode) {
510
+ for (let i = 0; i < funcSig.childCount; i++) {
511
+ const c = funcSig.child(i);
512
+ if (c?.type === 'identifier') {
513
+ nameNode = c;
514
+ break;
515
+ }
516
+ }
517
+ }
518
+ funcName = nameNode?.text ?? null;
519
+ }
520
+ label = 'Method';
521
+ }
522
+ return { funcName, label };
523
+ };
524
+ /** Argument list node types shared between extractMethodSignature and countCallArguments. */
525
+ export const CALL_ARGUMENT_LIST_TYPES = new Set([
526
+ 'arguments',
527
+ 'argument_list',
528
+ 'value_arguments',
529
+ ]);
530
+ /**
531
+ * Extract parameter count and return type text from an AST method/function node.
532
+ * Works across languages by looking for common AST patterns.
533
+ */
534
+ export const extractMethodSignature = (node) => {
535
+ let parameterCount = 0;
536
+ let requiredCount = 0;
537
+ let returnType;
538
+ let isVariadic = false;
539
+ const paramTypes = [];
540
+ if (!node)
541
+ return { parameterCount, requiredParameterCount: undefined, parameterTypes: undefined, returnType };
542
+ const paramListTypes = new Set([
543
+ 'formal_parameters', 'parameters', 'parameter_list',
544
+ 'function_parameters', 'method_parameters', 'function_value_parameters',
545
+ 'formal_parameter_list', // Dart
546
+ ]);
547
+ // Node types that indicate variadic/rest parameters
548
+ const VARIADIC_PARAM_TYPES = new Set([
549
+ 'variadic_parameter_declaration', // Go: ...string
550
+ 'variadic_parameter', // Rust: extern "C" fn(...)
551
+ 'spread_parameter', // Java: Object... args
552
+ 'list_splat_pattern', // Python: *args
553
+ 'dictionary_splat_pattern', // Python: **kwargs
554
+ ]);
555
+ /** AST node types that represent parameters with default values. */
556
+ const OPTIONAL_PARAM_TYPES = new Set([
557
+ 'optional_parameter', // TypeScript, Ruby: (x?: number), (x: number = 5), def f(x = 5)
558
+ 'default_parameter', // Python: def f(x=5)
559
+ 'typed_default_parameter', // Python: def f(x: int = 5)
560
+ 'optional_parameter_declaration', // C++: void f(int x = 5)
561
+ ]);
562
+ /** Check if a parameter node has a default value (handles Kotlin, C#, Swift, PHP
563
+ * where defaults are expressed as child nodes rather than distinct node types). */
564
+ const hasDefaultValue = (paramNode) => {
565
+ if (OPTIONAL_PARAM_TYPES.has(paramNode.type))
566
+ return true;
567
+ // C#, Swift, PHP: check for '=' token or equals_value_clause child
568
+ for (let i = 0; i < paramNode.childCount; i++) {
569
+ const c = paramNode.child(i);
570
+ if (!c)
571
+ continue;
572
+ if (c.type === '=' || c.type === 'equals_value_clause')
573
+ return true;
574
+ }
575
+ // Kotlin: default values are siblings of the parameter node, not children.
576
+ // The AST is: parameter, =, <literal> — all at function_value_parameters level.
577
+ // Check if the immediately following sibling is '=' (default value separator).
578
+ const sib = paramNode.nextSibling;
579
+ if (sib && sib.type === '=')
580
+ return true;
581
+ return false;
582
+ };
583
+ const findParameterList = (current) => {
584
+ for (const child of current.children) {
585
+ if (paramListTypes.has(child.type))
586
+ return child;
587
+ }
588
+ for (const child of current.children) {
589
+ const nested = findParameterList(child);
590
+ if (nested)
591
+ return nested;
592
+ }
593
+ return null;
594
+ };
595
+ const parameterList = (paramListTypes.has(node.type) ? node // node itself IS the parameter list (e.g. C# primary constructors)
596
+ : node.childForFieldName?.('parameters')
597
+ ?? findParameterList(node));
598
+ if (parameterList && paramListTypes.has(parameterList.type)) {
599
+ for (const param of parameterList.namedChildren) {
600
+ if (param.type === 'comment')
601
+ continue;
602
+ if (param.text === 'self' || param.text === '&self' || param.text === '&mut self' ||
603
+ param.type === 'self_parameter') {
604
+ continue;
605
+ }
606
+ // Kotlin: default values are siblings of the parameter node inside
607
+ // function_value_parameters, so they appear as named children (e.g.
608
+ // string_literal, integer_literal, boolean_literal, call_expression).
609
+ // Skip any named child that isn't a parameter-like or modifier node.
610
+ if (param.type.endsWith('_literal') || param.type === 'call_expression'
611
+ || param.type === 'navigation_expression' || param.type === 'prefix_expression'
612
+ || param.type === 'parenthesized_expression') {
613
+ continue;
614
+ }
615
+ // Check for variadic parameter types
616
+ if (VARIADIC_PARAM_TYPES.has(param.type)) {
617
+ isVariadic = true;
618
+ continue;
619
+ }
620
+ // TypeScript/JavaScript: rest parameter — required_parameter containing rest_pattern
621
+ if (param.type === 'required_parameter' || param.type === 'optional_parameter') {
622
+ for (const child of param.children) {
623
+ if (child.type === 'rest_pattern') {
624
+ isVariadic = true;
625
+ break;
626
+ }
627
+ }
628
+ if (isVariadic)
629
+ continue;
630
+ }
631
+ // Kotlin: vararg modifier on a regular parameter
632
+ if (param.type === 'parameter' || param.type === 'formal_parameter') {
633
+ const prev = param.previousSibling;
634
+ if (prev?.type === 'parameter_modifiers' && prev.text.includes('vararg')) {
635
+ isVariadic = true;
636
+ }
637
+ }
638
+ // Extract parameter type name for overload disambiguation.
639
+ // Works for Java (formal_parameter), Kotlin (parameter), C# (parameter),
640
+ // C++ (parameter_declaration). Uses childForFieldName('type') which is the
641
+ // standard tree-sitter field for typed parameters across these languages.
642
+ // Kotlin uses positional children instead of 'type' field — fall back to
643
+ // searching for user_type/nullable_type/predefined_type children.
644
+ const paramTypeNode = param.childForFieldName('type');
645
+ if (paramTypeNode) {
646
+ const typeName = extractSimpleTypeName(paramTypeNode);
647
+ paramTypes.push(typeName ?? 'unknown');
648
+ }
649
+ else {
650
+ // Kotlin: parameter → [simple_identifier, user_type|nullable_type]
651
+ let found = false;
652
+ for (const child of param.namedChildren) {
653
+ if (child.type === 'user_type' || child.type === 'nullable_type'
654
+ || child.type === 'type_identifier' || child.type === 'predefined_type') {
655
+ const typeName = extractSimpleTypeName(child);
656
+ paramTypes.push(typeName ?? 'unknown');
657
+ found = true;
658
+ break;
659
+ }
660
+ }
661
+ if (!found)
662
+ paramTypes.push('unknown');
663
+ }
664
+ if (!hasDefaultValue(param))
665
+ requiredCount++;
666
+ parameterCount++;
667
+ }
668
+ // C/C++: bare `...` token in parameter list (not a named child — check all children)
669
+ if (!isVariadic) {
670
+ for (const child of parameterList.children) {
671
+ if (!child.isNamed && child.text === '...') {
672
+ isVariadic = true;
673
+ break;
674
+ }
675
+ }
676
+ }
677
+ }
678
+ // Swift fallback: tree-sitter-swift places `parameter` nodes as direct children of
679
+ // function_declaration without a wrapping parameters/function_parameters list node.
680
+ // When no parameter list was found, count direct `parameter` children on the node.
681
+ if (!parameterList && parameterCount === 0) {
682
+ for (const child of node.namedChildren) {
683
+ if (child.type === 'parameter') {
684
+ if (!hasDefaultValue(child))
685
+ requiredCount++;
686
+ parameterCount++;
687
+ }
688
+ }
689
+ }
690
+ // Return type extraction — language-specific field names
691
+ // Go: 'result' field is either a type_identifier or parameter_list (multi-return)
692
+ const goResult = node.childForFieldName?.('result');
693
+ if (goResult) {
694
+ if (goResult.type === 'parameter_list') {
695
+ // Multi-return: extract first parameter's type only (e.g. (*User, error) → *User)
696
+ const firstParam = goResult.firstNamedChild;
697
+ if (firstParam?.type === 'parameter_declaration') {
698
+ const typeNode = firstParam.childForFieldName('type');
699
+ if (typeNode)
700
+ returnType = typeNode.text;
701
+ }
702
+ else if (firstParam) {
703
+ // Unnamed return types: (string, error) — first child is a bare type node
704
+ returnType = firstParam.text;
705
+ }
706
+ }
707
+ else {
708
+ returnType = goResult.text;
709
+ }
710
+ }
711
+ // Rust: 'return_type' field — the value IS the type node (e.g. primitive_type, type_identifier).
712
+ // Skip if the node is a type_annotation (TS/Python), which is handled by the generic loop below.
713
+ if (!returnType) {
714
+ const rustReturn = node.childForFieldName?.('return_type');
715
+ if (rustReturn && rustReturn.type !== 'type_annotation') {
716
+ returnType = rustReturn.text;
717
+ }
718
+ }
719
+ // C/C++: 'type' field on function_definition
720
+ if (!returnType) {
721
+ const cppType = node.childForFieldName?.('type');
722
+ if (cppType && cppType.text !== 'void') {
723
+ returnType = cppType.text;
724
+ }
725
+ }
726
+ // C#: 'returns' field on method_declaration
727
+ if (!returnType) {
728
+ const csReturn = node.childForFieldName?.('returns');
729
+ if (csReturn && csReturn.text !== 'void') {
730
+ returnType = csReturn.text;
731
+ }
732
+ }
733
+ // TS/Rust/Python/C#/Kotlin: type_annotation or return_type child
734
+ if (!returnType) {
735
+ for (const child of node.children) {
736
+ if (child.type === 'type_annotation' || child.type === 'return_type') {
737
+ const typeNode = child.children.find((c) => c.isNamed);
738
+ if (typeNode)
739
+ returnType = typeNode.text;
740
+ }
741
+ }
742
+ }
743
+ // Kotlin: fun getUser(): User — return type is a bare user_type child of
744
+ // function_declaration. The Kotlin grammar does NOT wrap it in type_annotation
745
+ // or return_type; it appears as a direct child after function_value_parameters.
746
+ // Note: Kotlin uses function_value_parameters (not a field), so we find it by type.
747
+ if (!returnType) {
748
+ let paramsEnd = -1;
749
+ for (let i = 0; i < node.childCount; i++) {
750
+ const child = node.child(i);
751
+ if (!child)
752
+ continue;
753
+ if (child.type === 'function_value_parameters' || child.type === 'value_parameters') {
754
+ paramsEnd = child.endIndex;
755
+ }
756
+ if (paramsEnd >= 0 && child.type === 'user_type' && child.startIndex > paramsEnd) {
757
+ returnType = child.text;
758
+ break;
759
+ }
760
+ }
761
+ }
762
+ if (isVariadic)
763
+ parameterCount = undefined;
764
+ // Only include parameterTypes when at least one type was successfully extracted.
765
+ // Use undefined (not []) to avoid empty array allocations for untyped parameters.
766
+ const hasTypes = paramTypes.length > 0 && paramTypes.some(t => t !== 'unknown');
767
+ // Only set requiredParameterCount when it differs from total — saves memory on the common case.
768
+ const requiredParameterCount = (!isVariadic && requiredCount < (parameterCount ?? 0))
769
+ ? requiredCount : undefined;
770
+ return { parameterCount, requiredParameterCount, parameterTypes: hasTypes ? paramTypes : undefined, returnType };
771
+ };
772
+ // ============================================================================
773
+ // Generic AST traversal helpers (shared by parse-worker + php-helpers)
774
+ // ============================================================================
775
+ /** Walk an AST node depth-first, returning the first descendant with the given type. */
776
+ export function findDescendant(node, type) {
777
+ if (node.type === type)
778
+ return node;
779
+ for (const child of (node.children ?? [])) {
780
+ const found = findDescendant(child, type);
781
+ if (found)
782
+ return found;
783
+ }
784
+ return null;
785
+ }
786
+ /** Extract the text content from a string or encapsed_string AST node. */
787
+ export function extractStringContent(node) {
788
+ if (!node)
789
+ return null;
790
+ const content = node.children?.find((c) => c.type === 'string_content');
791
+ if (content)
792
+ return content.text;
793
+ if (node.type === 'string_content')
794
+ return node.text;
795
+ return null;
796
+ }
797
+ /** Check if a C/C++ function_definition is inside a class or struct body.
798
+ * Used by the C/C++ labelOverride to skip duplicate function captures
799
+ * that are already covered by definition.method queries. */
800
+ export function isCppInsideClassOrStruct(functionNode) {
801
+ let ancestor = functionNode?.parent ?? null;
802
+ while (ancestor) {
803
+ if (ancestor.type === 'class_specifier' || ancestor.type === 'struct_specifier')
804
+ return true;
805
+ ancestor = ancestor.parent;
806
+ }
807
+ return false;
808
+ }
809
+ /** Find the first direct named child of a tree-sitter node matching the given type. */
810
+ export function findChild(node, type) {
811
+ for (let i = 0; i < node.namedChildCount; i++) {
812
+ const child = node.namedChild(i);
813
+ if (child?.type === type)
814
+ return child;
815
+ }
816
+ return null;
817
+ }