@optave/codegraph 2.5.1 → 3.0.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.
- package/README.md +216 -89
- package/package.json +8 -7
- package/src/ast.js +392 -0
- package/src/audit.js +423 -0
- package/src/batch.js +180 -0
- package/src/boundaries.js +346 -0
- package/src/builder.js +375 -92
- package/src/cfg.js +1451 -0
- package/src/change-journal.js +130 -0
- package/src/check.js +432 -0
- package/src/cli.js +734 -107
- package/src/cochange.js +5 -2
- package/src/communities.js +7 -1
- package/src/complexity.js +124 -17
- package/src/config.js +10 -0
- package/src/dataflow.js +1187 -0
- package/src/db.js +96 -0
- package/src/embedder.js +359 -47
- package/src/export.js +305 -0
- package/src/extractors/csharp.js +64 -1
- package/src/extractors/go.js +66 -1
- package/src/extractors/hcl.js +22 -0
- package/src/extractors/java.js +61 -1
- package/src/extractors/javascript.js +142 -0
- package/src/extractors/php.js +79 -0
- package/src/extractors/python.js +134 -0
- package/src/extractors/ruby.js +89 -0
- package/src/extractors/rust.js +71 -1
- package/src/flow.js +4 -4
- package/src/index.js +78 -3
- package/src/manifesto.js +69 -1
- package/src/mcp.js +702 -193
- package/src/owners.js +359 -0
- package/src/paginate.js +37 -2
- package/src/parser.js +8 -0
- package/src/queries.js +590 -50
- package/src/snapshot.js +149 -0
- package/src/structure.js +9 -3
- package/src/triage.js +273 -0
- package/src/viewer.js +948 -0
- package/src/watcher.js +36 -1
package/src/extractors/java.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { nodeEndLine } from './helpers.js';
|
|
1
|
+
import { findChild, nodeEndLine } from './helpers.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Extract symbols from Java files.
|
|
@@ -31,11 +31,13 @@ export function extractJavaSymbols(tree, _filePath) {
|
|
|
31
31
|
case 'class_declaration': {
|
|
32
32
|
const nameNode = node.childForFieldName('name');
|
|
33
33
|
if (nameNode) {
|
|
34
|
+
const classChildren = extractClassFields(node);
|
|
34
35
|
definitions.push({
|
|
35
36
|
name: nameNode.text,
|
|
36
37
|
kind: 'class',
|
|
37
38
|
line: node.startPosition.row + 1,
|
|
38
39
|
endLine: nodeEndLine(node),
|
|
40
|
+
children: classChildren.length > 0 ? classChildren : undefined,
|
|
39
41
|
});
|
|
40
42
|
|
|
41
43
|
const superclass = node.childForFieldName('superclass');
|
|
@@ -139,11 +141,13 @@ export function extractJavaSymbols(tree, _filePath) {
|
|
|
139
141
|
case 'enum_declaration': {
|
|
140
142
|
const nameNode = node.childForFieldName('name');
|
|
141
143
|
if (nameNode) {
|
|
144
|
+
const enumChildren = extractEnumConstants(node);
|
|
142
145
|
definitions.push({
|
|
143
146
|
name: nameNode.text,
|
|
144
147
|
kind: 'enum',
|
|
145
148
|
line: node.startPosition.row + 1,
|
|
146
149
|
endLine: nodeEndLine(node),
|
|
150
|
+
children: enumChildren.length > 0 ? enumChildren : undefined,
|
|
147
151
|
});
|
|
148
152
|
}
|
|
149
153
|
break;
|
|
@@ -154,11 +158,13 @@ export function extractJavaSymbols(tree, _filePath) {
|
|
|
154
158
|
if (nameNode) {
|
|
155
159
|
const parentClass = findJavaParentClass(node);
|
|
156
160
|
const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
|
|
161
|
+
const params = extractJavaParameters(node.childForFieldName('parameters'));
|
|
157
162
|
definitions.push({
|
|
158
163
|
name: fullName,
|
|
159
164
|
kind: 'method',
|
|
160
165
|
line: node.startPosition.row + 1,
|
|
161
166
|
endLine: nodeEndLine(node),
|
|
167
|
+
children: params.length > 0 ? params : undefined,
|
|
162
168
|
});
|
|
163
169
|
}
|
|
164
170
|
break;
|
|
@@ -169,11 +175,13 @@ export function extractJavaSymbols(tree, _filePath) {
|
|
|
169
175
|
if (nameNode) {
|
|
170
176
|
const parentClass = findJavaParentClass(node);
|
|
171
177
|
const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
|
|
178
|
+
const params = extractJavaParameters(node.childForFieldName('parameters'));
|
|
172
179
|
definitions.push({
|
|
173
180
|
name: fullName,
|
|
174
181
|
kind: 'method',
|
|
175
182
|
line: node.startPosition.row + 1,
|
|
176
183
|
endLine: nodeEndLine(node),
|
|
184
|
+
children: params.length > 0 ? params : undefined,
|
|
177
185
|
});
|
|
178
186
|
}
|
|
179
187
|
break;
|
|
@@ -228,3 +236,55 @@ export function extractJavaSymbols(tree, _filePath) {
|
|
|
228
236
|
walkJavaNode(tree.rootNode);
|
|
229
237
|
return { definitions, calls, imports, classes, exports };
|
|
230
238
|
}
|
|
239
|
+
|
|
240
|
+
// ── Child extraction helpers ────────────────────────────────────────────────
|
|
241
|
+
|
|
242
|
+
function extractJavaParameters(paramListNode) {
|
|
243
|
+
const params = [];
|
|
244
|
+
if (!paramListNode) return params;
|
|
245
|
+
for (let i = 0; i < paramListNode.childCount; i++) {
|
|
246
|
+
const param = paramListNode.child(i);
|
|
247
|
+
if (!param) continue;
|
|
248
|
+
if (param.type === 'formal_parameter' || param.type === 'spread_parameter') {
|
|
249
|
+
const nameNode = param.childForFieldName('name');
|
|
250
|
+
if (nameNode) {
|
|
251
|
+
params.push({ name: nameNode.text, kind: 'parameter', line: param.startPosition.row + 1 });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return params;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function extractClassFields(classNode) {
|
|
259
|
+
const fields = [];
|
|
260
|
+
const body = classNode.childForFieldName('body') || findChild(classNode, 'class_body');
|
|
261
|
+
if (!body) return fields;
|
|
262
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
263
|
+
const member = body.child(i);
|
|
264
|
+
if (!member || member.type !== 'field_declaration') continue;
|
|
265
|
+
for (let j = 0; j < member.childCount; j++) {
|
|
266
|
+
const child = member.child(j);
|
|
267
|
+
if (!child || child.type !== 'variable_declarator') continue;
|
|
268
|
+
const nameNode = child.childForFieldName('name');
|
|
269
|
+
if (nameNode) {
|
|
270
|
+
fields.push({ name: nameNode.text, kind: 'property', line: member.startPosition.row + 1 });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return fields;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function extractEnumConstants(enumNode) {
|
|
278
|
+
const constants = [];
|
|
279
|
+
const body = enumNode.childForFieldName('body') || findChild(enumNode, 'enum_body');
|
|
280
|
+
if (!body) return constants;
|
|
281
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
282
|
+
const member = body.child(i);
|
|
283
|
+
if (!member || member.type !== 'enum_constant') continue;
|
|
284
|
+
const nameNode = member.childForFieldName('name');
|
|
285
|
+
if (nameNode) {
|
|
286
|
+
constants.push({ name: nameNode.text, kind: 'constant', line: member.startPosition.row + 1 });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return constants;
|
|
290
|
+
}
|
|
@@ -28,31 +28,37 @@ function extractSymbolsQuery(tree, query) {
|
|
|
28
28
|
|
|
29
29
|
if (c.fn_node) {
|
|
30
30
|
// function_declaration
|
|
31
|
+
const fnChildren = extractParameters(c.fn_node);
|
|
31
32
|
definitions.push({
|
|
32
33
|
name: c.fn_name.text,
|
|
33
34
|
kind: 'function',
|
|
34
35
|
line: c.fn_node.startPosition.row + 1,
|
|
35
36
|
endLine: nodeEndLine(c.fn_node),
|
|
37
|
+
children: fnChildren.length > 0 ? fnChildren : undefined,
|
|
36
38
|
});
|
|
37
39
|
} else if (c.varfn_name) {
|
|
38
40
|
// variable_declarator with arrow_function / function_expression
|
|
39
41
|
const declNode = c.varfn_name.parent?.parent;
|
|
40
42
|
const line = declNode ? declNode.startPosition.row + 1 : c.varfn_name.startPosition.row + 1;
|
|
43
|
+
const varFnChildren = extractParameters(c.varfn_value);
|
|
41
44
|
definitions.push({
|
|
42
45
|
name: c.varfn_name.text,
|
|
43
46
|
kind: 'function',
|
|
44
47
|
line,
|
|
45
48
|
endLine: nodeEndLine(c.varfn_value),
|
|
49
|
+
children: varFnChildren.length > 0 ? varFnChildren : undefined,
|
|
46
50
|
});
|
|
47
51
|
} else if (c.cls_node) {
|
|
48
52
|
// class_declaration
|
|
49
53
|
const className = c.cls_name.text;
|
|
50
54
|
const startLine = c.cls_node.startPosition.row + 1;
|
|
55
|
+
const clsChildren = extractClassProperties(c.cls_node);
|
|
51
56
|
definitions.push({
|
|
52
57
|
name: className,
|
|
53
58
|
kind: 'class',
|
|
54
59
|
line: startLine,
|
|
55
60
|
endLine: nodeEndLine(c.cls_node),
|
|
61
|
+
children: clsChildren.length > 0 ? clsChildren : undefined,
|
|
56
62
|
});
|
|
57
63
|
const heritage =
|
|
58
64
|
c.cls_node.childForFieldName('heritage') || findChild(c.cls_node, 'class_heritage');
|
|
@@ -69,11 +75,13 @@ function extractSymbolsQuery(tree, query) {
|
|
|
69
75
|
const methName = c.meth_name.text;
|
|
70
76
|
const parentClass = findParentClass(c.meth_node);
|
|
71
77
|
const fullName = parentClass ? `${parentClass}.${methName}` : methName;
|
|
78
|
+
const methChildren = extractParameters(c.meth_node);
|
|
72
79
|
definitions.push({
|
|
73
80
|
name: fullName,
|
|
74
81
|
kind: 'method',
|
|
75
82
|
line: c.meth_node.startPosition.row + 1,
|
|
76
83
|
endLine: nodeEndLine(c.meth_node),
|
|
84
|
+
children: methChildren.length > 0 ? methChildren : undefined,
|
|
77
85
|
});
|
|
78
86
|
} else if (c.iface_node) {
|
|
79
87
|
// interface_declaration (TS/TSX only)
|
|
@@ -231,11 +239,13 @@ function extractSymbolsWalk(tree) {
|
|
|
231
239
|
case 'function_declaration': {
|
|
232
240
|
const nameNode = node.childForFieldName('name');
|
|
233
241
|
if (nameNode) {
|
|
242
|
+
const fnChildren = extractParameters(node);
|
|
234
243
|
definitions.push({
|
|
235
244
|
name: nameNode.text,
|
|
236
245
|
kind: 'function',
|
|
237
246
|
line: node.startPosition.row + 1,
|
|
238
247
|
endLine: nodeEndLine(node),
|
|
248
|
+
children: fnChildren.length > 0 ? fnChildren : undefined,
|
|
239
249
|
});
|
|
240
250
|
}
|
|
241
251
|
break;
|
|
@@ -246,11 +256,13 @@ function extractSymbolsWalk(tree) {
|
|
|
246
256
|
if (nameNode) {
|
|
247
257
|
const className = nameNode.text;
|
|
248
258
|
const startLine = node.startPosition.row + 1;
|
|
259
|
+
const clsChildren = extractClassProperties(node);
|
|
249
260
|
definitions.push({
|
|
250
261
|
name: className,
|
|
251
262
|
kind: 'class',
|
|
252
263
|
line: startLine,
|
|
253
264
|
endLine: nodeEndLine(node),
|
|
265
|
+
children: clsChildren.length > 0 ? clsChildren : undefined,
|
|
254
266
|
});
|
|
255
267
|
const heritage = node.childForFieldName('heritage') || findChild(node, 'class_heritage');
|
|
256
268
|
if (heritage) {
|
|
@@ -272,11 +284,13 @@ function extractSymbolsWalk(tree) {
|
|
|
272
284
|
if (nameNode) {
|
|
273
285
|
const parentClass = findParentClass(node);
|
|
274
286
|
const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
|
|
287
|
+
const methChildren = extractParameters(node);
|
|
275
288
|
definitions.push({
|
|
276
289
|
name: fullName,
|
|
277
290
|
kind: 'method',
|
|
278
291
|
line: node.startPosition.row + 1,
|
|
279
292
|
endLine: nodeEndLine(node),
|
|
293
|
+
children: methChildren.length > 0 ? methChildren : undefined,
|
|
280
294
|
});
|
|
281
295
|
}
|
|
282
296
|
break;
|
|
@@ -317,6 +331,7 @@ function extractSymbolsWalk(tree) {
|
|
|
317
331
|
|
|
318
332
|
case 'lexical_declaration':
|
|
319
333
|
case 'variable_declaration': {
|
|
334
|
+
const isConst = node.text.startsWith('const ');
|
|
320
335
|
for (let i = 0; i < node.childCount; i++) {
|
|
321
336
|
const declarator = node.child(i);
|
|
322
337
|
if (declarator && declarator.type === 'variable_declarator') {
|
|
@@ -329,15 +344,59 @@ function extractSymbolsWalk(tree) {
|
|
|
329
344
|
valType === 'function_expression' ||
|
|
330
345
|
valType === 'function'
|
|
331
346
|
) {
|
|
347
|
+
const varFnChildren = extractParameters(valueN);
|
|
332
348
|
definitions.push({
|
|
333
349
|
name: nameN.text,
|
|
334
350
|
kind: 'function',
|
|
335
351
|
line: node.startPosition.row + 1,
|
|
336
352
|
endLine: nodeEndLine(valueN),
|
|
353
|
+
children: varFnChildren.length > 0 ? varFnChildren : undefined,
|
|
337
354
|
});
|
|
355
|
+
} else if (isConst && nameN.type === 'identifier' && isConstantValue(valueN)) {
|
|
356
|
+
definitions.push({
|
|
357
|
+
name: nameN.text,
|
|
358
|
+
kind: 'constant',
|
|
359
|
+
line: node.startPosition.row + 1,
|
|
360
|
+
endLine: nodeEndLine(node),
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
} else if (isConst && nameN && nameN.type === 'identifier' && !valueN) {
|
|
364
|
+
// const with no value (shouldn't happen but be safe)
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
case 'enum_declaration': {
|
|
372
|
+
// TypeScript enum
|
|
373
|
+
const nameNode = node.childForFieldName('name');
|
|
374
|
+
if (nameNode) {
|
|
375
|
+
const enumChildren = [];
|
|
376
|
+
const body = node.childForFieldName('body') || findChild(node, 'enum_body');
|
|
377
|
+
if (body) {
|
|
378
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
379
|
+
const member = body.child(i);
|
|
380
|
+
if (!member) continue;
|
|
381
|
+
if (member.type === 'enum_assignment' || member.type === 'property_identifier') {
|
|
382
|
+
const mName = member.childForFieldName('name') || member.child(0);
|
|
383
|
+
if (mName) {
|
|
384
|
+
enumChildren.push({
|
|
385
|
+
name: mName.text,
|
|
386
|
+
kind: 'constant',
|
|
387
|
+
line: member.startPosition.row + 1,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
338
390
|
}
|
|
339
391
|
}
|
|
340
392
|
}
|
|
393
|
+
definitions.push({
|
|
394
|
+
name: nameNode.text,
|
|
395
|
+
kind: 'enum',
|
|
396
|
+
line: node.startPosition.row + 1,
|
|
397
|
+
endLine: nodeEndLine(node),
|
|
398
|
+
children: enumChildren.length > 0 ? enumChildren : undefined,
|
|
399
|
+
});
|
|
341
400
|
}
|
|
342
401
|
break;
|
|
343
402
|
}
|
|
@@ -471,6 +530,89 @@ function extractSymbolsWalk(tree) {
|
|
|
471
530
|
return { definitions, calls, imports, classes, exports };
|
|
472
531
|
}
|
|
473
532
|
|
|
533
|
+
// ── Child extraction helpers ────────────────────────────────────────────────
|
|
534
|
+
|
|
535
|
+
function extractParameters(node) {
|
|
536
|
+
const params = [];
|
|
537
|
+
const paramsNode = node.childForFieldName('parameters') || findChild(node, 'formal_parameters');
|
|
538
|
+
if (!paramsNode) return params;
|
|
539
|
+
for (let i = 0; i < paramsNode.childCount; i++) {
|
|
540
|
+
const child = paramsNode.child(i);
|
|
541
|
+
if (!child) continue;
|
|
542
|
+
const t = child.type;
|
|
543
|
+
if (t === 'identifier') {
|
|
544
|
+
params.push({ name: child.text, kind: 'parameter', line: child.startPosition.row + 1 });
|
|
545
|
+
} else if (
|
|
546
|
+
t === 'required_parameter' ||
|
|
547
|
+
t === 'optional_parameter' ||
|
|
548
|
+
t === 'assignment_pattern'
|
|
549
|
+
) {
|
|
550
|
+
const nameNode =
|
|
551
|
+
child.childForFieldName('pattern') || child.childForFieldName('left') || child.child(0);
|
|
552
|
+
if (
|
|
553
|
+
nameNode &&
|
|
554
|
+
(nameNode.type === 'identifier' ||
|
|
555
|
+
nameNode.type === 'shorthand_property_identifier_pattern')
|
|
556
|
+
) {
|
|
557
|
+
params.push({ name: nameNode.text, kind: 'parameter', line: child.startPosition.row + 1 });
|
|
558
|
+
}
|
|
559
|
+
} else if (t === 'rest_pattern' || t === 'rest_element') {
|
|
560
|
+
const nameNode = child.child(1) || child.childForFieldName('name');
|
|
561
|
+
if (nameNode && nameNode.type === 'identifier') {
|
|
562
|
+
params.push({ name: nameNode.text, kind: 'parameter', line: child.startPosition.row + 1 });
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return params;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function extractClassProperties(classNode) {
|
|
570
|
+
const props = [];
|
|
571
|
+
const body = classNode.childForFieldName('body') || findChild(classNode, 'class_body');
|
|
572
|
+
if (!body) return props;
|
|
573
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
574
|
+
const child = body.child(i);
|
|
575
|
+
if (!child) continue;
|
|
576
|
+
if (
|
|
577
|
+
child.type === 'field_definition' ||
|
|
578
|
+
child.type === 'public_field_definition' ||
|
|
579
|
+
child.type === 'property_definition'
|
|
580
|
+
) {
|
|
581
|
+
const nameNode =
|
|
582
|
+
child.childForFieldName('name') || child.childForFieldName('property') || child.child(0);
|
|
583
|
+
if (
|
|
584
|
+
nameNode &&
|
|
585
|
+
(nameNode.type === 'property_identifier' ||
|
|
586
|
+
nameNode.type === 'identifier' ||
|
|
587
|
+
nameNode.type === 'private_property_identifier')
|
|
588
|
+
) {
|
|
589
|
+
props.push({ name: nameNode.text, kind: 'property', line: child.startPosition.row + 1 });
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return props;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function isConstantValue(valueNode) {
|
|
597
|
+
if (!valueNode) return false;
|
|
598
|
+
const t = valueNode.type;
|
|
599
|
+
return (
|
|
600
|
+
t === 'number' ||
|
|
601
|
+
t === 'string' ||
|
|
602
|
+
t === 'template_string' ||
|
|
603
|
+
t === 'true' ||
|
|
604
|
+
t === 'false' ||
|
|
605
|
+
t === 'null' ||
|
|
606
|
+
t === 'undefined' ||
|
|
607
|
+
t === 'array' ||
|
|
608
|
+
t === 'object' ||
|
|
609
|
+
t === 'regex' ||
|
|
610
|
+
t === 'unary_expression' ||
|
|
611
|
+
t === 'binary_expression' ||
|
|
612
|
+
t === 'new_expression'
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
|
|
474
616
|
// ── Shared helpers ──────────────────────────────────────────────────────────
|
|
475
617
|
|
|
476
618
|
function extractInterfaceMethods(bodyNode, interfaceName, definitions) {
|
package/src/extractors/php.js
CHANGED
|
@@ -1,5 +1,76 @@
|
|
|
1
1
|
import { findChild, nodeEndLine } from './helpers.js';
|
|
2
2
|
|
|
3
|
+
function extractPhpParameters(fnNode) {
|
|
4
|
+
const params = [];
|
|
5
|
+
const paramsNode =
|
|
6
|
+
fnNode.childForFieldName('parameters') || findChild(fnNode, 'formal_parameters');
|
|
7
|
+
if (!paramsNode) return params;
|
|
8
|
+
for (let i = 0; i < paramsNode.childCount; i++) {
|
|
9
|
+
const param = paramsNode.child(i);
|
|
10
|
+
if (!param) continue;
|
|
11
|
+
if (param.type === 'simple_parameter' || param.type === 'variadic_parameter') {
|
|
12
|
+
const nameNode = param.childForFieldName('name') || findChild(param, 'variable_name');
|
|
13
|
+
if (nameNode) {
|
|
14
|
+
params.push({ name: nameNode.text, kind: 'parameter', line: param.startPosition.row + 1 });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return params;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function extractPhpClassChildren(classNode) {
|
|
22
|
+
const children = [];
|
|
23
|
+
const body = classNode.childForFieldName('body') || findChild(classNode, 'declaration_list');
|
|
24
|
+
if (!body) return children;
|
|
25
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
26
|
+
const member = body.child(i);
|
|
27
|
+
if (!member) continue;
|
|
28
|
+
if (member.type === 'property_declaration') {
|
|
29
|
+
for (let j = 0; j < member.childCount; j++) {
|
|
30
|
+
const el = member.child(j);
|
|
31
|
+
if (!el || el.type !== 'property_element') continue;
|
|
32
|
+
const varNode = findChild(el, 'variable_name');
|
|
33
|
+
if (varNode) {
|
|
34
|
+
children.push({
|
|
35
|
+
name: varNode.text,
|
|
36
|
+
kind: 'property',
|
|
37
|
+
line: member.startPosition.row + 1,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} else if (member.type === 'const_declaration') {
|
|
42
|
+
for (let j = 0; j < member.childCount; j++) {
|
|
43
|
+
const el = member.child(j);
|
|
44
|
+
if (!el || el.type !== 'const_element') continue;
|
|
45
|
+
const nameNode = el.childForFieldName('name') || findChild(el, 'name');
|
|
46
|
+
if (nameNode) {
|
|
47
|
+
children.push({
|
|
48
|
+
name: nameNode.text,
|
|
49
|
+
kind: 'constant',
|
|
50
|
+
line: member.startPosition.row + 1,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return children;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function extractPhpEnumCases(enumNode) {
|
|
60
|
+
const children = [];
|
|
61
|
+
const body = enumNode.childForFieldName('body') || findChild(enumNode, 'enum_declaration_list');
|
|
62
|
+
if (!body) return children;
|
|
63
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
64
|
+
const member = body.child(i);
|
|
65
|
+
if (!member || member.type !== 'enum_case') continue;
|
|
66
|
+
const nameNode = member.childForFieldName('name');
|
|
67
|
+
if (nameNode) {
|
|
68
|
+
children.push({ name: nameNode.text, kind: 'constant', line: member.startPosition.row + 1 });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return children;
|
|
72
|
+
}
|
|
73
|
+
|
|
3
74
|
/**
|
|
4
75
|
* Extract symbols from PHP files.
|
|
5
76
|
*/
|
|
@@ -31,11 +102,13 @@ export function extractPHPSymbols(tree, _filePath) {
|
|
|
31
102
|
case 'function_definition': {
|
|
32
103
|
const nameNode = node.childForFieldName('name');
|
|
33
104
|
if (nameNode) {
|
|
105
|
+
const params = extractPhpParameters(node);
|
|
34
106
|
definitions.push({
|
|
35
107
|
name: nameNode.text,
|
|
36
108
|
kind: 'function',
|
|
37
109
|
line: node.startPosition.row + 1,
|
|
38
110
|
endLine: nodeEndLine(node),
|
|
111
|
+
children: params.length > 0 ? params : undefined,
|
|
39
112
|
});
|
|
40
113
|
}
|
|
41
114
|
break;
|
|
@@ -44,11 +117,13 @@ export function extractPHPSymbols(tree, _filePath) {
|
|
|
44
117
|
case 'class_declaration': {
|
|
45
118
|
const nameNode = node.childForFieldName('name');
|
|
46
119
|
if (nameNode) {
|
|
120
|
+
const classChildren = extractPhpClassChildren(node);
|
|
47
121
|
definitions.push({
|
|
48
122
|
name: nameNode.text,
|
|
49
123
|
kind: 'class',
|
|
50
124
|
line: node.startPosition.row + 1,
|
|
51
125
|
endLine: nodeEndLine(node),
|
|
126
|
+
children: classChildren.length > 0 ? classChildren : undefined,
|
|
52
127
|
});
|
|
53
128
|
|
|
54
129
|
// Check base clause (extends)
|
|
@@ -132,11 +207,13 @@ export function extractPHPSymbols(tree, _filePath) {
|
|
|
132
207
|
case 'enum_declaration': {
|
|
133
208
|
const nameNode = node.childForFieldName('name');
|
|
134
209
|
if (nameNode) {
|
|
210
|
+
const enumChildren = extractPhpEnumCases(node);
|
|
135
211
|
definitions.push({
|
|
136
212
|
name: nameNode.text,
|
|
137
213
|
kind: 'enum',
|
|
138
214
|
line: node.startPosition.row + 1,
|
|
139
215
|
endLine: nodeEndLine(node),
|
|
216
|
+
children: enumChildren.length > 0 ? enumChildren : undefined,
|
|
140
217
|
});
|
|
141
218
|
}
|
|
142
219
|
break;
|
|
@@ -147,11 +224,13 @@ export function extractPHPSymbols(tree, _filePath) {
|
|
|
147
224
|
if (nameNode) {
|
|
148
225
|
const parentClass = findPHPParentClass(node);
|
|
149
226
|
const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
|
|
227
|
+
const params = extractPhpParameters(node);
|
|
150
228
|
definitions.push({
|
|
151
229
|
name: fullName,
|
|
152
230
|
kind: 'method',
|
|
153
231
|
line: node.startPosition.row + 1,
|
|
154
232
|
endLine: nodeEndLine(node),
|
|
233
|
+
children: params.length > 0 ? params : undefined,
|
|
155
234
|
});
|
|
156
235
|
}
|
|
157
236
|
break;
|
package/src/extractors/python.js
CHANGED
|
@@ -22,12 +22,14 @@ export function extractPythonSymbols(tree, _filePath) {
|
|
|
22
22
|
const parentClass = findPythonParentClass(node);
|
|
23
23
|
const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
|
|
24
24
|
const kind = parentClass ? 'method' : 'function';
|
|
25
|
+
const fnChildren = extractPythonParameters(node);
|
|
25
26
|
definitions.push({
|
|
26
27
|
name: fullName,
|
|
27
28
|
kind,
|
|
28
29
|
line: node.startPosition.row + 1,
|
|
29
30
|
endLine: nodeEndLine(node),
|
|
30
31
|
decorators,
|
|
32
|
+
children: fnChildren.length > 0 ? fnChildren : undefined,
|
|
31
33
|
});
|
|
32
34
|
}
|
|
33
35
|
break;
|
|
@@ -36,11 +38,13 @@ export function extractPythonSymbols(tree, _filePath) {
|
|
|
36
38
|
case 'class_definition': {
|
|
37
39
|
const nameNode = node.childForFieldName('name');
|
|
38
40
|
if (nameNode) {
|
|
41
|
+
const clsChildren = extractPythonClassProperties(node);
|
|
39
42
|
definitions.push({
|
|
40
43
|
name: nameNode.text,
|
|
41
44
|
kind: 'class',
|
|
42
45
|
line: node.startPosition.row + 1,
|
|
43
46
|
endLine: nodeEndLine(node),
|
|
47
|
+
children: clsChildren.length > 0 ? clsChildren : undefined,
|
|
44
48
|
});
|
|
45
49
|
const superclasses =
|
|
46
50
|
node.childForFieldName('superclasses') || findChild(node, 'argument_list');
|
|
@@ -108,6 +112,24 @@ export function extractPythonSymbols(tree, _filePath) {
|
|
|
108
112
|
break;
|
|
109
113
|
}
|
|
110
114
|
|
|
115
|
+
case 'expression_statement': {
|
|
116
|
+
// Module-level UPPER_CASE assignments → constants
|
|
117
|
+
if (node.parent && node.parent.type === 'module') {
|
|
118
|
+
const assignment = findChild(node, 'assignment');
|
|
119
|
+
if (assignment) {
|
|
120
|
+
const left = assignment.childForFieldName('left');
|
|
121
|
+
if (left && left.type === 'identifier' && /^[A-Z_][A-Z0-9_]*$/.test(left.text)) {
|
|
122
|
+
definitions.push({
|
|
123
|
+
name: left.text,
|
|
124
|
+
kind: 'constant',
|
|
125
|
+
line: node.startPosition.row + 1,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
|
|
111
133
|
case 'import_from_statement': {
|
|
112
134
|
let source = '';
|
|
113
135
|
const names = [];
|
|
@@ -133,6 +155,118 @@ export function extractPythonSymbols(tree, _filePath) {
|
|
|
133
155
|
for (let i = 0; i < node.childCount; i++) walkPythonNode(node.child(i));
|
|
134
156
|
}
|
|
135
157
|
|
|
158
|
+
function extractPythonParameters(fnNode) {
|
|
159
|
+
const params = [];
|
|
160
|
+
const paramsNode = fnNode.childForFieldName('parameters') || findChild(fnNode, 'parameters');
|
|
161
|
+
if (!paramsNode) return params;
|
|
162
|
+
for (let i = 0; i < paramsNode.childCount; i++) {
|
|
163
|
+
const child = paramsNode.child(i);
|
|
164
|
+
if (!child) continue;
|
|
165
|
+
const t = child.type;
|
|
166
|
+
if (t === 'identifier') {
|
|
167
|
+
params.push({ name: child.text, kind: 'parameter', line: child.startPosition.row + 1 });
|
|
168
|
+
} else if (
|
|
169
|
+
t === 'typed_parameter' ||
|
|
170
|
+
t === 'default_parameter' ||
|
|
171
|
+
t === 'typed_default_parameter'
|
|
172
|
+
) {
|
|
173
|
+
const nameNode = child.childForFieldName('name') || child.child(0);
|
|
174
|
+
if (nameNode && nameNode.type === 'identifier') {
|
|
175
|
+
params.push({
|
|
176
|
+
name: nameNode.text,
|
|
177
|
+
kind: 'parameter',
|
|
178
|
+
line: child.startPosition.row + 1,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
} else if (t === 'list_splat_pattern' || t === 'dictionary_splat_pattern') {
|
|
182
|
+
// *args, **kwargs
|
|
183
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
184
|
+
const inner = child.child(j);
|
|
185
|
+
if (inner && inner.type === 'identifier') {
|
|
186
|
+
params.push({ name: inner.text, kind: 'parameter', line: child.startPosition.row + 1 });
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return params;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function extractPythonClassProperties(classNode) {
|
|
196
|
+
const props = [];
|
|
197
|
+
const seen = new Set();
|
|
198
|
+
const body = classNode.childForFieldName('body') || findChild(classNode, 'block');
|
|
199
|
+
if (!body) return props;
|
|
200
|
+
|
|
201
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
202
|
+
const child = body.child(i);
|
|
203
|
+
if (!child) continue;
|
|
204
|
+
|
|
205
|
+
// Direct class attribute assignments: x = 5
|
|
206
|
+
if (child.type === 'expression_statement') {
|
|
207
|
+
const assignment = findChild(child, 'assignment');
|
|
208
|
+
if (assignment) {
|
|
209
|
+
const left = assignment.childForFieldName('left');
|
|
210
|
+
if (left && left.type === 'identifier' && !seen.has(left.text)) {
|
|
211
|
+
seen.add(left.text);
|
|
212
|
+
props.push({ name: left.text, kind: 'property', line: child.startPosition.row + 1 });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// __init__ method: self.x = ... assignments
|
|
218
|
+
if (child.type === 'function_definition') {
|
|
219
|
+
const fnName = child.childForFieldName('name');
|
|
220
|
+
if (fnName && fnName.text === '__init__') {
|
|
221
|
+
const initBody = child.childForFieldName('body') || findChild(child, 'block');
|
|
222
|
+
if (initBody) {
|
|
223
|
+
walkInitBody(initBody, seen, props);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// decorated __init__
|
|
229
|
+
if (child.type === 'decorated_definition') {
|
|
230
|
+
for (let j = 0; j < child.childCount; j++) {
|
|
231
|
+
const inner = child.child(j);
|
|
232
|
+
if (inner && inner.type === 'function_definition') {
|
|
233
|
+
const fnName = inner.childForFieldName('name');
|
|
234
|
+
if (fnName && fnName.text === '__init__') {
|
|
235
|
+
const initBody = inner.childForFieldName('body') || findChild(inner, 'block');
|
|
236
|
+
if (initBody) {
|
|
237
|
+
walkInitBody(initBody, seen, props);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return props;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function walkInitBody(bodyNode, seen, props) {
|
|
248
|
+
for (let i = 0; i < bodyNode.childCount; i++) {
|
|
249
|
+
const stmt = bodyNode.child(i);
|
|
250
|
+
if (!stmt || stmt.type !== 'expression_statement') continue;
|
|
251
|
+
const assignment = findChild(stmt, 'assignment');
|
|
252
|
+
if (!assignment) continue;
|
|
253
|
+
const left = assignment.childForFieldName('left');
|
|
254
|
+
if (!left || left.type !== 'attribute') continue;
|
|
255
|
+
const obj = left.childForFieldName('object');
|
|
256
|
+
const attr = left.childForFieldName('attribute');
|
|
257
|
+
if (
|
|
258
|
+
obj &&
|
|
259
|
+
obj.text === 'self' &&
|
|
260
|
+
attr &&
|
|
261
|
+
attr.type === 'identifier' &&
|
|
262
|
+
!seen.has(attr.text)
|
|
263
|
+
) {
|
|
264
|
+
seen.add(attr.text);
|
|
265
|
+
props.push({ name: attr.text, kind: 'property', line: stmt.startPosition.row + 1 });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
136
270
|
function findPythonParentClass(node) {
|
|
137
271
|
let current = node.parent;
|
|
138
272
|
while (current) {
|