diffninja 0.1.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 (154) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +259 -0
  3. package/dist/calltree.d.ts +47 -0
  4. package/dist/calltree.js +296 -0
  5. package/dist/cli.d.ts +57 -0
  6. package/dist/cli.js +340 -0
  7. package/dist/diff.d.ts +7 -0
  8. package/dist/diff.js +114 -0
  9. package/dist/extract.d.ts +26 -0
  10. package/dist/extract.js +152 -0
  11. package/dist/git.d.ts +40 -0
  12. package/dist/git.js +288 -0
  13. package/dist/index.d.ts +9 -0
  14. package/dist/index.js +8 -0
  15. package/dist/infer.d.ts +21 -0
  16. package/dist/infer.js +189 -0
  17. package/dist/languages/bash.d.ts +2 -0
  18. package/dist/languages/bash.js +208 -0
  19. package/dist/languages/c.d.ts +2 -0
  20. package/dist/languages/c.js +218 -0
  21. package/dist/languages/call-syntax.d.ts +125 -0
  22. package/dist/languages/call-syntax.js +997 -0
  23. package/dist/languages/cpp.d.ts +2 -0
  24. package/dist/languages/cpp.js +321 -0
  25. package/dist/languages/csharp.d.ts +2 -0
  26. package/dist/languages/csharp.js +324 -0
  27. package/dist/languages/elixir.d.ts +2 -0
  28. package/dist/languages/elixir.js +331 -0
  29. package/dist/languages/go.d.ts +2 -0
  30. package/dist/languages/go.js +299 -0
  31. package/dist/languages/grammars.d.ts +50 -0
  32. package/dist/languages/grammars.js +351 -0
  33. package/dist/languages/haskell.d.ts +2 -0
  34. package/dist/languages/haskell.js +250 -0
  35. package/dist/languages/java.d.ts +2 -0
  36. package/dist/languages/java.js +351 -0
  37. package/dist/languages/javascript.d.ts +4 -0
  38. package/dist/languages/javascript.js +648 -0
  39. package/dist/languages/kotlin.d.ts +2 -0
  40. package/dist/languages/kotlin.js +368 -0
  41. package/dist/languages/lua.d.ts +2 -0
  42. package/dist/languages/lua.js +212 -0
  43. package/dist/languages/ocaml.d.ts +2 -0
  44. package/dist/languages/ocaml.js +291 -0
  45. package/dist/languages/perl.d.ts +2 -0
  46. package/dist/languages/perl.js +418 -0
  47. package/dist/languages/php.d.ts +2 -0
  48. package/dist/languages/php.js +397 -0
  49. package/dist/languages/python.d.ts +2 -0
  50. package/dist/languages/python.js +376 -0
  51. package/dist/languages/registry.d.ts +7 -0
  52. package/dist/languages/registry.js +69 -0
  53. package/dist/languages/ruby.d.ts +2 -0
  54. package/dist/languages/ruby.js +391 -0
  55. package/dist/languages/rust.d.ts +2 -0
  56. package/dist/languages/rust.js +261 -0
  57. package/dist/languages/scala.d.ts +2 -0
  58. package/dist/languages/scala.js +307 -0
  59. package/dist/languages/solidity.d.ts +2 -0
  60. package/dist/languages/solidity.js +240 -0
  61. package/dist/languages/swift.d.ts +2 -0
  62. package/dist/languages/swift.js +268 -0
  63. package/dist/languages/types.d.ts +36 -0
  64. package/dist/languages/types.js +74 -0
  65. package/dist/languages/typescript-contracts.d.ts +57 -0
  66. package/dist/languages/typescript-contracts.js +528 -0
  67. package/dist/languages/typescript-dispatch.d.ts +68 -0
  68. package/dist/languages/typescript-dispatch.js +710 -0
  69. package/dist/languages/typescript.d.ts +4 -0
  70. package/dist/languages/typescript.js +722 -0
  71. package/dist/languages/zig.d.ts +2 -0
  72. package/dist/languages/zig.js +243 -0
  73. package/dist/loc.d.ts +17 -0
  74. package/dist/loc.js +34 -0
  75. package/dist/reach.d.ts +17 -0
  76. package/dist/reach.js +65 -0
  77. package/dist/render.d.ts +18 -0
  78. package/dist/render.js +83 -0
  79. package/dist/review/brand.d.ts +8 -0
  80. package/dist/review/brand.js +25 -0
  81. package/dist/review/call-context.d.ts +27 -0
  82. package/dist/review/call-context.js +446 -0
  83. package/dist/review/call-flow-html.d.ts +32 -0
  84. package/dist/review/call-flow-html.js +1870 -0
  85. package/dist/review/call-flow-nav.d.ts +151 -0
  86. package/dist/review/call-flow-nav.js +317 -0
  87. package/dist/review/call-flow.d.ts +47 -0
  88. package/dist/review/call-flow.js +229 -0
  89. package/dist/review/change-facts.d.ts +69 -0
  90. package/dist/review/change-facts.js +729 -0
  91. package/dist/review/cli.d.ts +2 -0
  92. package/dist/review/cli.js +50 -0
  93. package/dist/review/connected-analysis.d.ts +100 -0
  94. package/dist/review/connected-analysis.js +163 -0
  95. package/dist/review/connected-html.d.ts +17 -0
  96. package/dist/review/connected-html.js +2853 -0
  97. package/dist/review/connected.d.ts +23 -0
  98. package/dist/review/connected.js +141 -0
  99. package/dist/review/escape-html.d.ts +2 -0
  100. package/dist/review/escape-html.js +9 -0
  101. package/dist/review/evidence-html.d.ts +21 -0
  102. package/dist/review/evidence-html.js +521 -0
  103. package/dist/review/evidence-syntax.d.ts +132 -0
  104. package/dist/review/evidence-syntax.js +478 -0
  105. package/dist/review/evidence-types.d.ts +62 -0
  106. package/dist/review/evidence-types.js +1 -0
  107. package/dist/review/evidence.d.ts +31 -0
  108. package/dist/review/evidence.js +1603 -0
  109. package/dist/review/file-role.d.ts +9 -0
  110. package/dist/review/file-role.js +29 -0
  111. package/dist/review/github.d.ts +204 -0
  112. package/dist/review/github.js +1245 -0
  113. package/dist/review/history.d.ts +101 -0
  114. package/dist/review/history.js +412 -0
  115. package/dist/review/html.d.ts +34 -0
  116. package/dist/review/html.js +1104 -0
  117. package/dist/review/input.d.ts +10 -0
  118. package/dist/review/input.js +113 -0
  119. package/dist/review/intent.d.ts +4 -0
  120. package/dist/review/intent.js +75 -0
  121. package/dist/review/mcp-cli.d.ts +2 -0
  122. package/dist/review/mcp-cli.js +25 -0
  123. package/dist/review/mcp.d.ts +12 -0
  124. package/dist/review/mcp.js +414 -0
  125. package/dist/review/module-resolution.d.ts +2 -0
  126. package/dist/review/module-resolution.js +86 -0
  127. package/dist/review/palette.d.ts +7 -0
  128. package/dist/review/palette.js +104 -0
  129. package/dist/review/pipeline.d.ts +77 -0
  130. package/dist/review/pipeline.js +227 -0
  131. package/dist/review/pr-input.d.ts +19 -0
  132. package/dist/review/pr-input.js +130 -0
  133. package/dist/review/questions.d.ts +201 -0
  134. package/dist/review/questions.js +174 -0
  135. package/dist/review/reference-check.d.ts +7 -0
  136. package/dist/review/reference-check.js +733 -0
  137. package/dist/review/report-pages.d.ts +109 -0
  138. package/dist/review/report-pages.js +328 -0
  139. package/dist/review/service.d.ts +23 -0
  140. package/dist/review/service.js +198 -0
  141. package/dist/review/setup.d.ts +112 -0
  142. package/dist/review/setup.js +549 -0
  143. package/dist/review/source.d.ts +26 -0
  144. package/dist/review/source.js +276 -0
  145. package/dist/review/toml.d.ts +38 -0
  146. package/dist/review/toml.js +565 -0
  147. package/dist/review/types.d.ts +179 -0
  148. package/dist/review/types.js +1 -0
  149. package/dist/run.d.ts +49 -0
  150. package/dist/run.js +311 -0
  151. package/dist/types.d.ts +366 -0
  152. package/dist/types.js +83 -0
  153. package/package.json +88 -0
  154. package/scripts/ensure-native-grammar.mjs +188 -0
@@ -0,0 +1,2 @@
1
+ import { type LanguageExtractor } from "./types.js";
2
+ export declare const cppExtractor: LanguageExtractor;
@@ -0,0 +1,321 @@
1
+ import { heuristicCallSyntax } from "./call-syntax.js";
2
+ import { childByType, declaredParamsOf, collapseWs, locFromNode, namedChildren, sameNode, } from "./types.js";
3
+ function unwrapDeclarator(node) {
4
+ let cur = node;
5
+ while (cur) {
6
+ if (cur.type === "function_declarator")
7
+ return cur;
8
+ const next = childByType(cur, "function_declarator") ??
9
+ childByType(cur, "pointer_declarator") ??
10
+ childByType(cur, "reference_declarator") ??
11
+ childByType(cur, "parenthesized_declarator") ??
12
+ null;
13
+ if (!next || sameNode(next, cur))
14
+ break;
15
+ cur = next;
16
+ }
17
+ return cur?.type === "function_declarator" ? cur : null;
18
+ }
19
+ function getParamsLabel(declarator) {
20
+ const params = declarator ? childByType(declarator, "parameter_list") : null;
21
+ if (!params)
22
+ return "()";
23
+ const names = [];
24
+ for (const p of namedChildren(params)) {
25
+ if (p.type !== "parameter_declaration")
26
+ continue;
27
+ if (p.text === "void")
28
+ continue;
29
+ const id = childByType(p, "identifier") ??
30
+ childByType(childByType(p, "pointer_declarator") ?? p, "identifier") ??
31
+ childByType(childByType(p, "reference_declarator") ?? p, "identifier");
32
+ names.push(id?.text ?? "_");
33
+ }
34
+ return names.length === 0 ? "()" : `(${names.join(", ")})`;
35
+ }
36
+ function calleeKey(node, className) {
37
+ if (node.type === "identifier") {
38
+ return node.text;
39
+ }
40
+ if (node.type === "type_identifier") {
41
+ // Rare as a bare callee; prefer not inventing `new`
42
+ return node.text;
43
+ }
44
+ if (node.type === "qualified_identifier") {
45
+ // std::foo / Foo::bar — normalize :: to . for Class.method keys
46
+ return collapseWs(node.text).replace(/\s+/g, "").replace(/::/g, ".");
47
+ }
48
+ if (node.type === "field_expression") {
49
+ const object = node.namedChild(0);
50
+ const field = childByType(node, "field_identifier");
51
+ if (!object || !field)
52
+ return null;
53
+ const prop = field.text;
54
+ if (object.type === "this" && className) {
55
+ return `${className}.${prop}`;
56
+ }
57
+ if (object.type === "identifier") {
58
+ return `${object.text}.${prop}`;
59
+ }
60
+ if (className)
61
+ return `${className}.${prop}`;
62
+ return prop;
63
+ }
64
+ return null;
65
+ }
66
+ function condText(node) {
67
+ if (!node)
68
+ return "";
69
+ if (node.type === "condition_clause" || node.type === "parenthesized_expression") {
70
+ const inner = node.namedChild(0);
71
+ if (inner)
72
+ return collapseWs(inner.text);
73
+ }
74
+ return collapseWs(node.text);
75
+ }
76
+ function collectStatements(file, statements, className) {
77
+ const steps = [];
78
+ const seen = new Set();
79
+ const addCall = (key, node) => {
80
+ const mark = `${key}:${node.startIndex}`;
81
+ if (seen.has(mark))
82
+ return;
83
+ seen.add(mark);
84
+ const step = { type: "call", key, ...locFromNode(file, node) };
85
+ step.syntax = heuristicCallSyntax(node, key);
86
+ steps.push(step);
87
+ };
88
+ const walk = (node) => {
89
+ if (node.type === "function_definition" ||
90
+ node.type === "lambda_expression" ||
91
+ node.type === "class_specifier" ||
92
+ node.type === "struct_specifier") {
93
+ return;
94
+ }
95
+ if (node.type === "if_statement") {
96
+ const cond = node.childForFieldName("condition") ??
97
+ childByType(node, "condition_clause") ??
98
+ childByType(node, "parenthesized_expression");
99
+ const consequent = node.childForFieldName("consequence") ??
100
+ namedChildren(node).find((c) => c.type !== "condition_clause" &&
101
+ c.type !== "parenthesized_expression" &&
102
+ c.type !== "else_clause") ??
103
+ null;
104
+ const text = condText(cond);
105
+ steps.push({
106
+ type: "branch",
107
+ key: text ? `if:${text}` : "if",
108
+ label: text ? `if ${text}` : "if",
109
+ ...locFromNode(file, cond ?? node),
110
+ children: consequent
111
+ ? collectStatements(file, [consequent], className)
112
+ : [],
113
+ });
114
+ let elseClause = childByType(node, "else_clause");
115
+ while (elseClause) {
116
+ const inner = elseClause.namedChild(0);
117
+ if (!inner)
118
+ break;
119
+ if (inner.type === "if_statement") {
120
+ const elifCond = inner.childForFieldName("condition") ??
121
+ childByType(inner, "condition_clause");
122
+ const elifText = condText(elifCond);
123
+ const elifCons = inner.childForFieldName("consequence") ??
124
+ namedChildren(inner).find((c) => c.type !== "condition_clause" &&
125
+ c.type !== "parenthesized_expression" &&
126
+ c.type !== "else_clause") ??
127
+ null;
128
+ steps.push({
129
+ type: "branch",
130
+ key: elifText ? `else-if:${elifText}` : "else-if",
131
+ label: elifText ? `else if ${elifText}` : "else if",
132
+ ...locFromNode(file, elifCond ?? elseClause),
133
+ children: elifCons
134
+ ? collectStatements(file, [elifCons], className)
135
+ : [],
136
+ });
137
+ elseClause = childByType(inner, "else_clause");
138
+ continue;
139
+ }
140
+ steps.push({
141
+ type: "branch",
142
+ key: "else",
143
+ label: "else",
144
+ ...locFromNode(file, elseClause),
145
+ children: collectStatements(file, [inner], className),
146
+ });
147
+ break;
148
+ }
149
+ return;
150
+ }
151
+ if (node.type === "try_statement") {
152
+ const body = node.childForFieldName("body") ??
153
+ childByType(node, "compound_statement");
154
+ steps.push({
155
+ type: "branch",
156
+ key: "try",
157
+ label: "try",
158
+ ...locFromNode(file, node),
159
+ children: body
160
+ ? collectStatements(file, namedChildren(body), className)
161
+ : [],
162
+ });
163
+ for (const clause of namedChildren(node)) {
164
+ if (clause.type !== "catch_clause")
165
+ continue;
166
+ const params = childByType(clause, "parameter_list");
167
+ const text = params ? collapseWs(params.text) : "";
168
+ const catchBody = clause.childForFieldName("body") ??
169
+ childByType(clause, "compound_statement");
170
+ steps.push({
171
+ type: "branch",
172
+ key: text ? `catch:${text}` : "catch",
173
+ label: text ? `catch ${text}` : "catch",
174
+ ...locFromNode(file, params ?? clause),
175
+ children: catchBody
176
+ ? collectStatements(file, namedChildren(catchBody), className)
177
+ : [],
178
+ });
179
+ }
180
+ return;
181
+ }
182
+ if (node.type === "call_expression") {
183
+ const callee = node.namedChild(0);
184
+ if (callee) {
185
+ const key = calleeKey(callee, className);
186
+ if (key)
187
+ addCall(key, node);
188
+ }
189
+ }
190
+ else if (node.type === "new_expression") {
191
+ const typeId = childByType(node, "type_identifier") ??
192
+ namedChildren(node).find((c) => c.type === "type_identifier" || c.type === "qualified_identifier") ??
193
+ null;
194
+ if (typeId) {
195
+ const name = collapseWs(typeId.text);
196
+ addCall(`new ${name}`, node);
197
+ }
198
+ }
199
+ for (const child of namedChildren(node))
200
+ walk(child);
201
+ };
202
+ for (const stmt of statements)
203
+ walk(stmt);
204
+ return steps;
205
+ }
206
+ function pushFunction(file, node, key, label, declarator, body, className, exported, functions) {
207
+ const info = {
208
+ key,
209
+ label: `${label}${getParamsLabel(declarator)}`,
210
+ params: declarator ? declaredParamsOf(declarator) : undefined,
211
+ file,
212
+ steps: body ? collectStatements(file, namedChildren(body), className) : [],
213
+ exported,
214
+ start: node.startIndex,
215
+ end: node.endIndex,
216
+ };
217
+ functions.push(info);
218
+ }
219
+ function handleFreeFunction(file, node, functions) {
220
+ const rawDecl = namedChildren(node).find((c) => c.type === "function_declarator" ||
221
+ c.type === "pointer_declarator" ||
222
+ c.type === "reference_declarator") ?? null;
223
+ const declarator = unwrapDeclarator(rawDecl);
224
+ if (!declarator)
225
+ return;
226
+ if (childByType(declarator, "destructor_name"))
227
+ return;
228
+ const name = childByType(declarator, "identifier")?.text ??
229
+ childByType(declarator, "field_identifier")?.text ??
230
+ null;
231
+ if (!name)
232
+ return;
233
+ const body = childByType(node, "compound_statement");
234
+ pushFunction(file, node, name, name, declarator, body, null, true, functions);
235
+ }
236
+ function handleClassMethod(file, node, className, functions) {
237
+ const rawDecl = namedChildren(node).find((c) => c.type === "function_declarator" ||
238
+ c.type === "pointer_declarator" ||
239
+ c.type === "reference_declarator") ?? null;
240
+ const declarator = unwrapDeclarator(rawDecl);
241
+ if (!declarator)
242
+ return;
243
+ const dtor = childByType(declarator, "destructor_name");
244
+ if (dtor) {
245
+ // Skip destructors for callgraph indexing
246
+ return;
247
+ }
248
+ const nameNode = childByType(declarator, "identifier") ??
249
+ childByType(declarator, "field_identifier");
250
+ const name = nameNode?.text ?? null;
251
+ if (!name)
252
+ return;
253
+ const body = childByType(node, "compound_statement");
254
+ const isCtor = name === className;
255
+ const key = isCtor ? `${className}.constructor` : `${className}.${name}`;
256
+ const label = isCtor ? `new ${className}` : key;
257
+ pushFunction(file, node, key, label, declarator, body, className, true, functions);
258
+ if (isCtor) {
259
+ const ctor = functions[functions.length - 1];
260
+ functions.push({
261
+ ...ctor,
262
+ key: `new ${className}`,
263
+ label: `new ${className}${getParamsLabel(declarator)}`,
264
+ params: declaredParamsOf(declarator),
265
+ });
266
+ }
267
+ }
268
+ function handleClass(file, node, functions) {
269
+ const className = childByType(node, "type_identifier")?.text ??
270
+ childByType(node, "identifier")?.text ??
271
+ null;
272
+ if (!className)
273
+ return;
274
+ const body = childByType(node, "field_declaration_list");
275
+ if (!body)
276
+ return;
277
+ for (const member of namedChildren(body)) {
278
+ if (member.type === "function_definition") {
279
+ handleClassMethod(file, member, className, functions);
280
+ }
281
+ }
282
+ }
283
+ function visit(file, node, functions) {
284
+ if (node.type === "function_definition") {
285
+ handleFreeFunction(file, node, functions);
286
+ return;
287
+ }
288
+ if (node.type === "class_specifier" ||
289
+ node.type === "struct_specifier") {
290
+ handleClass(file, node, functions);
291
+ return;
292
+ }
293
+ if (node.type === "namespace_definition" || node.type === "declaration_list") {
294
+ for (const child of namedChildren(node))
295
+ visit(file, child, functions);
296
+ return;
297
+ }
298
+ // `class Foo { ... };` may wrap as type declaration — walk named children lightly
299
+ if (node.type === "declaration") {
300
+ for (const child of namedChildren(node)) {
301
+ if (child.type === "class_specifier" ||
302
+ child.type === "struct_specifier" ||
303
+ child.type === "namespace_definition") {
304
+ visit(file, child, functions);
305
+ }
306
+ }
307
+ }
308
+ }
309
+ function extractFromTree(file, _source, tree) {
310
+ const functions = [];
311
+ for (const stmt of namedChildren(tree.rootNode)) {
312
+ visit(file, stmt, functions);
313
+ }
314
+ return functions;
315
+ }
316
+ export const cppExtractor = {
317
+ id: "cpp",
318
+ extensions: [".cc", ".cpp", ".cu", ".cuh", ".cxx", ".hpp", ".hh"],
319
+ grammarPackage: "tree-sitter-cpp",
320
+ extract: extractFromTree,
321
+ };
@@ -0,0 +1,2 @@
1
+ import { type LanguageExtractor } from "./types.js";
2
+ export declare const csharpExtractor: LanguageExtractor;
@@ -0,0 +1,324 @@
1
+ import { heuristicCallSyntax } from "./call-syntax.js";
2
+ import { childByType, declaredParamsOf, collapseWs, locFromNode, namedChildren, sameNode, } from "./types.js";
3
+ function isPrivate(node) {
4
+ return namedChildren(node).some((c) => c.type === "modifier" && c.text === "private");
5
+ }
6
+ function getParamsLabel(params) {
7
+ if (!params || params.type !== "parameter_list")
8
+ return "()";
9
+ const names = [];
10
+ for (const p of namedChildren(params)) {
11
+ if (p.type !== "parameter")
12
+ continue;
13
+ const id = childByType(p, "identifier");
14
+ names.push(id?.text ?? "_");
15
+ }
16
+ return names.length === 0 ? "()" : `(${names.join(", ")})`;
17
+ }
18
+ function memberAccessKey(node, className) {
19
+ // this.Foo → unnamed "this" child + named property identifier
20
+ const first = node.child(0);
21
+ const ids = namedChildren(node).filter((c) => c.type === "identifier");
22
+ if (first?.type === "this" || first?.text === "this") {
23
+ const prop = ids[0]?.text;
24
+ if (!prop)
25
+ return null;
26
+ return className ? `${className}.${prop}` : prop;
27
+ }
28
+ if (ids.length >= 2) {
29
+ return `${ids[0].text}.${ids[1].text}`;
30
+ }
31
+ if (ids.length === 1) {
32
+ // Unusual single-identifier member access
33
+ return ids[0].text;
34
+ }
35
+ return null;
36
+ }
37
+ function calleeKey(node, className) {
38
+ if (node.type === "identifier")
39
+ return node.text;
40
+ if (node.type === "member_access_expression") {
41
+ return memberAccessKey(node, className);
42
+ }
43
+ return null;
44
+ }
45
+ function condText(node) {
46
+ if (!node)
47
+ return "";
48
+ if (node.type === "parenthesized_expression") {
49
+ const inner = node.namedChild(0);
50
+ if (inner)
51
+ return collapseWs(inner.text);
52
+ }
53
+ return collapseWs(node.text);
54
+ }
55
+ function collectStatements(file, statements, className) {
56
+ const steps = [];
57
+ const seen = new Set();
58
+ const addCall = (key, node) => {
59
+ const mark = `${key}:${node.startIndex}`;
60
+ if (seen.has(mark))
61
+ return;
62
+ seen.add(mark);
63
+ const step = { type: "call", key, ...locFromNode(file, node) };
64
+ step.syntax = heuristicCallSyntax(node, key);
65
+ steps.push(step);
66
+ };
67
+ const walk = (node) => {
68
+ if (node.type === "method_declaration" ||
69
+ node.type === "constructor_declaration" ||
70
+ node.type === "local_function_statement" ||
71
+ node.type === "anonymous_method_expression" ||
72
+ node.type === "lambda_expression" ||
73
+ node.type === "class_declaration" ||
74
+ node.type === "struct_declaration" ||
75
+ node.type === "record_declaration") {
76
+ return;
77
+ }
78
+ if (node.type === "if_statement") {
79
+ const cond = node.childForFieldName("condition") ?? node.namedChild(0) ?? null;
80
+ const consequent = node.childForFieldName("consequence") ??
81
+ namedChildren(node).find((c, i) => i > 0) ??
82
+ null;
83
+ const text = condText(cond);
84
+ steps.push({
85
+ type: "branch",
86
+ key: text ? `if:${text}` : "if",
87
+ label: text ? `if ${text}` : "if",
88
+ ...locFromNode(file, cond ?? node),
89
+ children: consequent
90
+ ? collectStatements(file, consequent.type === "block"
91
+ ? namedChildren(consequent)
92
+ : [consequent], className)
93
+ : [],
94
+ });
95
+ let alternative = node.childForFieldName("alternative");
96
+ while (alternative) {
97
+ if (alternative.type === "if_statement") {
98
+ const elifCond = alternative.childForFieldName("condition") ??
99
+ alternative.namedChild(0) ??
100
+ null;
101
+ const elifText = condText(elifCond);
102
+ const elifCons = alternative.childForFieldName("consequence") ?? null;
103
+ steps.push({
104
+ type: "branch",
105
+ key: elifText ? `else-if:${elifText}` : "else-if",
106
+ label: elifText ? `else if ${elifText}` : "else if",
107
+ ...locFromNode(file, elifCond ?? alternative),
108
+ children: elifCons
109
+ ? collectStatements(file, elifCons.type === "block"
110
+ ? namedChildren(elifCons)
111
+ : [elifCons], className)
112
+ : [],
113
+ });
114
+ alternative = alternative.childForFieldName("alternative");
115
+ continue;
116
+ }
117
+ steps.push({
118
+ type: "branch",
119
+ key: "else",
120
+ label: "else",
121
+ ...locFromNode(file, alternative),
122
+ children: collectStatements(file, alternative.type === "block"
123
+ ? namedChildren(alternative)
124
+ : [alternative], className),
125
+ });
126
+ break;
127
+ }
128
+ return;
129
+ }
130
+ if (node.type === "try_statement") {
131
+ const body = node.childForFieldName("body") ?? childByType(node, "block");
132
+ steps.push({
133
+ type: "branch",
134
+ key: "try",
135
+ label: "try",
136
+ ...locFromNode(file, node),
137
+ children: body
138
+ ? collectStatements(file, namedChildren(body), className)
139
+ : [],
140
+ });
141
+ for (const clause of namedChildren(node)) {
142
+ if (clause.type === "catch_clause") {
143
+ const decl = childByType(clause, "catch_declaration");
144
+ const text = decl ? collapseWs(decl.text) : "";
145
+ const catchBody = clause.childForFieldName("body") ?? childByType(clause, "block");
146
+ steps.push({
147
+ type: "branch",
148
+ key: text ? `catch:${text}` : "catch",
149
+ label: text ? `catch ${text}` : "catch",
150
+ ...locFromNode(file, decl ?? clause),
151
+ children: catchBody
152
+ ? collectStatements(file, namedChildren(catchBody), className)
153
+ : [],
154
+ });
155
+ }
156
+ if (clause.type === "finally_clause") {
157
+ const finallyBody = clause.childForFieldName("body") ?? childByType(clause, "block");
158
+ steps.push({
159
+ type: "branch",
160
+ key: "finally",
161
+ label: "finally",
162
+ ...locFromNode(file, clause),
163
+ children: finallyBody
164
+ ? collectStatements(file, namedChildren(finallyBody), className)
165
+ : [],
166
+ });
167
+ }
168
+ }
169
+ return;
170
+ }
171
+ if (node.type === "switch_statement") {
172
+ const body = node.childForFieldName("body") ?? childByType(node, "switch_body");
173
+ if (body) {
174
+ for (const section of namedChildren(body)) {
175
+ if (section.type !== "switch_section")
176
+ continue;
177
+ const pattern = namedChildren(section).find((c) => c.type === "constant_pattern" ||
178
+ c.type === "declaration_pattern" ||
179
+ c.type === "var_pattern" ||
180
+ c.type === "discard_pattern") ?? null;
181
+ const isDefault = [...Array(section.childCount)]
182
+ .map((_, i) => section.child(i))
183
+ .some((c) => c?.type === "default" || c?.text === "default");
184
+ const stmts = namedChildren(section).filter((c) => !sameNode(c, pattern) &&
185
+ c.type !== "break_statement" &&
186
+ c.type !== "constant_pattern" &&
187
+ c.type !== "declaration_pattern" &&
188
+ c.type !== "var_pattern" &&
189
+ c.type !== "discard_pattern");
190
+ if (isDefault && !pattern) {
191
+ steps.push({
192
+ type: "branch",
193
+ key: "default",
194
+ label: "default",
195
+ ...locFromNode(file, section),
196
+ children: collectStatements(file, stmts, className),
197
+ });
198
+ }
199
+ else {
200
+ const text = pattern ? collapseWs(pattern.text) : "";
201
+ steps.push({
202
+ type: "branch",
203
+ key: text ? `case:${text}` : "case",
204
+ label: text ? `case ${text}` : "case",
205
+ ...locFromNode(file, pattern ?? section),
206
+ children: collectStatements(file, stmts, className),
207
+ });
208
+ }
209
+ }
210
+ }
211
+ return;
212
+ }
213
+ if (node.type === "invocation_expression") {
214
+ const callee = node.namedChild(0);
215
+ if (callee) {
216
+ const key = calleeKey(callee, className);
217
+ if (key)
218
+ addCall(key, node);
219
+ }
220
+ }
221
+ else if (node.type === "object_creation_expression") {
222
+ const typeId = childByType(node, "identifier") ??
223
+ childByType(node, "qualified_name") ??
224
+ namedChildren(node).find((c) => c.type === "identifier" || c.type === "qualified_name") ??
225
+ null;
226
+ if (typeId) {
227
+ addCall(`new ${collapseWs(typeId.text)}`, node);
228
+ }
229
+ }
230
+ for (const child of namedChildren(node))
231
+ walk(child);
232
+ };
233
+ for (const stmt of statements)
234
+ walk(stmt);
235
+ return steps;
236
+ }
237
+ function handleMethod(file, node, className, functions) {
238
+ const name = node.childForFieldName("name")?.text ??
239
+ // Fallback: last identifier (return type may precede the name)
240
+ [...namedChildren(node)].filter((c) => c.type === "identifier").at(-1)
241
+ ?.text ??
242
+ null;
243
+ if (!name)
244
+ return;
245
+ const params = childByType(node, "parameter_list");
246
+ const body = childByType(node, "block");
247
+ const key = `${className}.${name}`;
248
+ functions.push({
249
+ key,
250
+ label: `${key}${getParamsLabel(params)}`,
251
+ params: declaredParamsOf(node),
252
+ file,
253
+ steps: body ? collectStatements(file, namedChildren(body), className) : [],
254
+ exported: !isPrivate(node),
255
+ start: node.startIndex,
256
+ end: node.endIndex,
257
+ });
258
+ }
259
+ function handleConstructor(file, node, className, functions) {
260
+ const params = childByType(node, "parameter_list");
261
+ const body = childByType(node, "block");
262
+ const info = {
263
+ key: `${className}.constructor`,
264
+ label: `new ${className}${getParamsLabel(params)}`,
265
+ params: declaredParamsOf(node),
266
+ file,
267
+ steps: body ? collectStatements(file, namedChildren(body), className) : [],
268
+ exported: !isPrivate(node),
269
+ start: node.startIndex,
270
+ end: node.endIndex,
271
+ };
272
+ functions.push(info);
273
+ functions.push({
274
+ ...info,
275
+ key: `new ${className}`,
276
+ });
277
+ }
278
+ function handleClass(file, node, functions) {
279
+ const className = childByType(node, "identifier")?.text ?? null;
280
+ if (!className)
281
+ return;
282
+ const body = childByType(node, "declaration_list");
283
+ if (!body)
284
+ return;
285
+ for (const member of namedChildren(body)) {
286
+ if (member.type === "method_declaration") {
287
+ handleMethod(file, member, className, functions);
288
+ }
289
+ else if (member.type === "constructor_declaration") {
290
+ handleConstructor(file, member, className, functions);
291
+ }
292
+ }
293
+ }
294
+ function visit(file, node, functions) {
295
+ if (node.type === "class_declaration" ||
296
+ node.type === "struct_declaration" ||
297
+ node.type === "record_declaration") {
298
+ handleClass(file, node, functions);
299
+ return;
300
+ }
301
+ if (node.type === "namespace_declaration") {
302
+ const list = childByType(node, "declaration_list") ?? node;
303
+ for (const child of namedChildren(list))
304
+ visit(file, child, functions);
305
+ return;
306
+ }
307
+ if (node.type === "file_scoped_namespace_declaration") {
308
+ for (const child of namedChildren(node))
309
+ visit(file, child, functions);
310
+ }
311
+ }
312
+ function extractFromTree(file, _source, tree) {
313
+ const functions = [];
314
+ for (const stmt of namedChildren(tree.rootNode)) {
315
+ visit(file, stmt, functions);
316
+ }
317
+ return functions;
318
+ }
319
+ export const csharpExtractor = {
320
+ id: "csharp",
321
+ extensions: [".cs"],
322
+ grammarPackage: "tree-sitter-c-sharp",
323
+ extract: extractFromTree,
324
+ };
@@ -0,0 +1,2 @@
1
+ import { type LanguageExtractor } from "./types.js";
2
+ export declare const elixirExtractor: LanguageExtractor;