@optave/codegraph 2.6.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -54
- package/package.json +5 -5
- package/src/ast.js +418 -0
- package/src/batch.js +93 -3
- package/src/builder.js +371 -103
- package/src/cfg.js +1452 -0
- package/src/change-journal.js +130 -0
- package/src/cli.js +415 -139
- package/src/complexity.js +8 -8
- package/src/dataflow.js +1190 -0
- package/src/db.js +96 -0
- package/src/embedder.js +16 -16
- 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 +193 -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 +5 -2
- package/src/index.js +52 -4
- package/src/mcp.js +403 -222
- package/src/paginate.js +3 -3
- package/src/parser.js +24 -0
- package/src/queries.js +362 -36
- package/src/structure.js +64 -8
- package/src/viewer.js +948 -0
- package/src/watcher.js +36 -1
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) {
|
package/src/extractors/ruby.js
CHANGED
|
@@ -31,11 +31,13 @@ export function extractRubySymbols(tree, _filePath) {
|
|
|
31
31
|
case 'class': {
|
|
32
32
|
const nameNode = node.childForFieldName('name');
|
|
33
33
|
if (nameNode) {
|
|
34
|
+
const classChildren = extractRubyClassChildren(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
|
const superclass = node.childForFieldName('superclass');
|
|
41
43
|
if (superclass) {
|
|
@@ -73,11 +75,13 @@ export function extractRubySymbols(tree, _filePath) {
|
|
|
73
75
|
case 'module': {
|
|
74
76
|
const nameNode = node.childForFieldName('name');
|
|
75
77
|
if (nameNode) {
|
|
78
|
+
const moduleChildren = extractRubyBodyConstants(node);
|
|
76
79
|
definitions.push({
|
|
77
80
|
name: nameNode.text,
|
|
78
81
|
kind: 'module',
|
|
79
82
|
line: node.startPosition.row + 1,
|
|
80
83
|
endLine: nodeEndLine(node),
|
|
84
|
+
children: moduleChildren.length > 0 ? moduleChildren : undefined,
|
|
81
85
|
});
|
|
82
86
|
}
|
|
83
87
|
break;
|
|
@@ -88,11 +92,13 @@ export function extractRubySymbols(tree, _filePath) {
|
|
|
88
92
|
if (nameNode) {
|
|
89
93
|
const parentClass = findRubyParentClass(node);
|
|
90
94
|
const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
|
|
95
|
+
const params = extractRubyParameters(node);
|
|
91
96
|
definitions.push({
|
|
92
97
|
name: fullName,
|
|
93
98
|
kind: 'method',
|
|
94
99
|
line: node.startPosition.row + 1,
|
|
95
100
|
endLine: nodeEndLine(node),
|
|
101
|
+
children: params.length > 0 ? params : undefined,
|
|
96
102
|
});
|
|
97
103
|
}
|
|
98
104
|
break;
|
|
@@ -103,16 +109,34 @@ export function extractRubySymbols(tree, _filePath) {
|
|
|
103
109
|
if (nameNode) {
|
|
104
110
|
const parentClass = findRubyParentClass(node);
|
|
105
111
|
const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
|
|
112
|
+
const params = extractRubyParameters(node);
|
|
106
113
|
definitions.push({
|
|
107
114
|
name: fullName,
|
|
108
115
|
kind: 'function',
|
|
109
116
|
line: node.startPosition.row + 1,
|
|
110
117
|
endLine: nodeEndLine(node),
|
|
118
|
+
children: params.length > 0 ? params : undefined,
|
|
111
119
|
});
|
|
112
120
|
}
|
|
113
121
|
break;
|
|
114
122
|
}
|
|
115
123
|
|
|
124
|
+
case 'assignment': {
|
|
125
|
+
// Top-level constant assignments (parent is program)
|
|
126
|
+
if (node.parent && node.parent.type === 'program') {
|
|
127
|
+
const left = node.childForFieldName('left');
|
|
128
|
+
if (left && left.type === 'constant') {
|
|
129
|
+
definitions.push({
|
|
130
|
+
name: left.text,
|
|
131
|
+
kind: 'constant',
|
|
132
|
+
line: node.startPosition.row + 1,
|
|
133
|
+
endLine: nodeEndLine(node),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
|
|
116
140
|
case 'call': {
|
|
117
141
|
const methodNode = node.childForFieldName('method');
|
|
118
142
|
if (methodNode) {
|
|
@@ -186,3 +210,68 @@ export function extractRubySymbols(tree, _filePath) {
|
|
|
186
210
|
walkRubyNode(tree.rootNode);
|
|
187
211
|
return { definitions, calls, imports, classes, exports };
|
|
188
212
|
}
|
|
213
|
+
|
|
214
|
+
// ── Child extraction helpers ────────────────────────────────────────────────
|
|
215
|
+
|
|
216
|
+
const RUBY_PARAM_TYPES = new Set([
|
|
217
|
+
'identifier',
|
|
218
|
+
'optional_parameter',
|
|
219
|
+
'splat_parameter',
|
|
220
|
+
'hash_splat_parameter',
|
|
221
|
+
'block_parameter',
|
|
222
|
+
'keyword_parameter',
|
|
223
|
+
]);
|
|
224
|
+
|
|
225
|
+
function extractRubyParameters(methodNode) {
|
|
226
|
+
const params = [];
|
|
227
|
+
const paramList =
|
|
228
|
+
methodNode.childForFieldName('parameters') || findChild(methodNode, 'method_parameters');
|
|
229
|
+
if (!paramList) return params;
|
|
230
|
+
for (let i = 0; i < paramList.childCount; i++) {
|
|
231
|
+
const param = paramList.child(i);
|
|
232
|
+
if (!param || !RUBY_PARAM_TYPES.has(param.type)) continue;
|
|
233
|
+
let name;
|
|
234
|
+
if (param.type === 'identifier') {
|
|
235
|
+
name = param.text;
|
|
236
|
+
} else {
|
|
237
|
+
// Compound parameter types have an identifier child for the name
|
|
238
|
+
const id = findChild(param, 'identifier');
|
|
239
|
+
name = id ? id.text : param.text;
|
|
240
|
+
}
|
|
241
|
+
params.push({ name, kind: 'parameter', line: param.startPosition.row + 1 });
|
|
242
|
+
}
|
|
243
|
+
return params;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function extractRubyBodyConstants(containerNode) {
|
|
247
|
+
const children = [];
|
|
248
|
+
const body = containerNode.childForFieldName('body') || findChild(containerNode, 'body');
|
|
249
|
+
if (!body) return children;
|
|
250
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
251
|
+
const child = body.child(i);
|
|
252
|
+
if (!child || child.type !== 'assignment') continue;
|
|
253
|
+
const left = child.childForFieldName('left');
|
|
254
|
+
if (left && left.type === 'constant') {
|
|
255
|
+
children.push({ name: left.text, kind: 'constant', line: child.startPosition.row + 1 });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return children;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function extractRubyClassChildren(classNode) {
|
|
262
|
+
const children = [];
|
|
263
|
+
const body = classNode.childForFieldName('body') || findChild(classNode, 'body');
|
|
264
|
+
if (!body) return children;
|
|
265
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
266
|
+
const child = body.child(i);
|
|
267
|
+
if (!child || child.type !== 'assignment') continue;
|
|
268
|
+
const left = child.childForFieldName('left');
|
|
269
|
+
if (!left) continue;
|
|
270
|
+
if (left.type === 'instance_variable') {
|
|
271
|
+
children.push({ name: left.text, kind: 'property', line: child.startPosition.row + 1 });
|
|
272
|
+
} else if (left.type === 'constant') {
|
|
273
|
+
children.push({ name: left.text, kind: 'constant', line: child.startPosition.row + 1 });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return children;
|
|
277
|
+
}
|
package/src/extractors/rust.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 Rust files.
|
|
@@ -30,11 +30,13 @@ export function extractRustSymbols(tree, _filePath) {
|
|
|
30
30
|
const implType = findCurrentImpl(node);
|
|
31
31
|
const fullName = implType ? `${implType}.${nameNode.text}` : nameNode.text;
|
|
32
32
|
const kind = implType ? 'method' : 'function';
|
|
33
|
+
const params = extractRustParameters(node.childForFieldName('parameters'));
|
|
33
34
|
definitions.push({
|
|
34
35
|
name: fullName,
|
|
35
36
|
kind,
|
|
36
37
|
line: node.startPosition.row + 1,
|
|
37
38
|
endLine: nodeEndLine(node),
|
|
39
|
+
children: params.length > 0 ? params : undefined,
|
|
38
40
|
});
|
|
39
41
|
}
|
|
40
42
|
break;
|
|
@@ -43,11 +45,13 @@ export function extractRustSymbols(tree, _filePath) {
|
|
|
43
45
|
case 'struct_item': {
|
|
44
46
|
const nameNode = node.childForFieldName('name');
|
|
45
47
|
if (nameNode) {
|
|
48
|
+
const fields = extractStructFields(node);
|
|
46
49
|
definitions.push({
|
|
47
50
|
name: nameNode.text,
|
|
48
51
|
kind: 'struct',
|
|
49
52
|
line: node.startPosition.row + 1,
|
|
50
53
|
endLine: nodeEndLine(node),
|
|
54
|
+
children: fields.length > 0 ? fields : undefined,
|
|
51
55
|
});
|
|
52
56
|
}
|
|
53
57
|
break;
|
|
@@ -56,11 +60,26 @@ export function extractRustSymbols(tree, _filePath) {
|
|
|
56
60
|
case 'enum_item': {
|
|
57
61
|
const nameNode = node.childForFieldName('name');
|
|
58
62
|
if (nameNode) {
|
|
63
|
+
const variants = extractEnumVariants(node);
|
|
59
64
|
definitions.push({
|
|
60
65
|
name: nameNode.text,
|
|
61
66
|
kind: 'enum',
|
|
62
67
|
line: node.startPosition.row + 1,
|
|
63
68
|
endLine: nodeEndLine(node),
|
|
69
|
+
children: variants.length > 0 ? variants : undefined,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
case 'const_item': {
|
|
76
|
+
const nameNode = node.childForFieldName('name');
|
|
77
|
+
if (nameNode) {
|
|
78
|
+
definitions.push({
|
|
79
|
+
name: nameNode.text,
|
|
80
|
+
kind: 'constant',
|
|
81
|
+
line: node.startPosition.row + 1,
|
|
82
|
+
endLine: nodeEndLine(node),
|
|
64
83
|
});
|
|
65
84
|
}
|
|
66
85
|
break;
|
|
@@ -170,6 +189,57 @@ export function extractRustSymbols(tree, _filePath) {
|
|
|
170
189
|
return { definitions, calls, imports, classes, exports };
|
|
171
190
|
}
|
|
172
191
|
|
|
192
|
+
// ── Child extraction helpers ────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
function extractRustParameters(paramListNode) {
|
|
195
|
+
const params = [];
|
|
196
|
+
if (!paramListNode) return params;
|
|
197
|
+
for (let i = 0; i < paramListNode.childCount; i++) {
|
|
198
|
+
const param = paramListNode.child(i);
|
|
199
|
+
if (!param) continue;
|
|
200
|
+
if (param.type === 'self_parameter') {
|
|
201
|
+
params.push({ name: 'self', kind: 'parameter', line: param.startPosition.row + 1 });
|
|
202
|
+
} else if (param.type === 'parameter') {
|
|
203
|
+
const pattern = param.childForFieldName('pattern');
|
|
204
|
+
if (pattern) {
|
|
205
|
+
params.push({ name: pattern.text, kind: 'parameter', line: param.startPosition.row + 1 });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return params;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function extractStructFields(structNode) {
|
|
213
|
+
const fields = [];
|
|
214
|
+
const fieldList =
|
|
215
|
+
structNode.childForFieldName('body') || findChild(structNode, 'field_declaration_list');
|
|
216
|
+
if (!fieldList) return fields;
|
|
217
|
+
for (let i = 0; i < fieldList.childCount; i++) {
|
|
218
|
+
const field = fieldList.child(i);
|
|
219
|
+
if (!field || field.type !== 'field_declaration') continue;
|
|
220
|
+
const nameNode = field.childForFieldName('name');
|
|
221
|
+
if (nameNode) {
|
|
222
|
+
fields.push({ name: nameNode.text, kind: 'property', line: field.startPosition.row + 1 });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return fields;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function extractEnumVariants(enumNode) {
|
|
229
|
+
const variants = [];
|
|
230
|
+
const body = enumNode.childForFieldName('body') || findChild(enumNode, 'enum_variant_list');
|
|
231
|
+
if (!body) return variants;
|
|
232
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
233
|
+
const variant = body.child(i);
|
|
234
|
+
if (!variant || variant.type !== 'enum_variant') continue;
|
|
235
|
+
const nameNode = variant.childForFieldName('name');
|
|
236
|
+
if (nameNode) {
|
|
237
|
+
variants.push({ name: nameNode.text, kind: 'constant', line: variant.startPosition.row + 1 });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return variants;
|
|
241
|
+
}
|
|
242
|
+
|
|
173
243
|
function extractRustUsePath(node) {
|
|
174
244
|
if (!node) return [];
|
|
175
245
|
|
package/src/flow.js
CHANGED
|
@@ -45,7 +45,10 @@ export function listEntryPointsData(dbPath, opts = {}) {
|
|
|
45
45
|
.prepare(
|
|
46
46
|
`SELECT n.name, n.kind, n.file, n.line, n.role
|
|
47
47
|
FROM nodes n
|
|
48
|
-
WHERE (
|
|
48
|
+
WHERE (
|
|
49
|
+
(${prefixConditions})
|
|
50
|
+
OR n.role = 'entry'
|
|
51
|
+
)
|
|
49
52
|
AND n.kind NOT IN ('file', 'directory')
|
|
50
53
|
ORDER BY n.name`,
|
|
51
54
|
)
|
|
@@ -59,7 +62,7 @@ export function listEntryPointsData(dbPath, opts = {}) {
|
|
|
59
62
|
file: r.file,
|
|
60
63
|
line: r.line,
|
|
61
64
|
role: r.role,
|
|
62
|
-
type: entryPointType(r.name),
|
|
65
|
+
type: entryPointType(r.name) || (r.role === 'entry' ? 'exported' : null),
|
|
63
66
|
}));
|
|
64
67
|
|
|
65
68
|
const byType = {};
|