@optave/codegraph 2.0.0 → 2.1.1-dev.3c12b64
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 +58 -22
- package/package.json +10 -10
- package/src/builder.js +14 -5
- package/src/cli.js +24 -8
- package/src/config.js +1 -1
- package/src/embedder.js +3 -3
- package/src/extractors/csharp.js +243 -0
- package/src/extractors/go.js +167 -0
- package/src/extractors/hcl.js +73 -0
- package/src/extractors/helpers.js +10 -0
- package/src/extractors/index.js +9 -0
- package/src/extractors/java.js +227 -0
- package/src/extractors/javascript.js +396 -0
- package/src/extractors/php.js +237 -0
- package/src/extractors/python.js +143 -0
- package/src/extractors/ruby.js +185 -0
- package/src/extractors/rust.js +215 -0
- package/src/index.js +1 -0
- package/src/mcp.js +2 -1
- package/src/parser.js +27 -1890
- package/src/queries.js +190 -4
- package/src/registry.js +24 -7
- package/src/resolve.js +4 -3
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { findChild, nodeEndLine } from './helpers.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract symbols from Python files.
|
|
5
|
+
*/
|
|
6
|
+
export function extractPythonSymbols(tree, _filePath) {
|
|
7
|
+
const definitions = [];
|
|
8
|
+
const calls = [];
|
|
9
|
+
const imports = [];
|
|
10
|
+
const classes = [];
|
|
11
|
+
const exports = [];
|
|
12
|
+
|
|
13
|
+
function walkPythonNode(node) {
|
|
14
|
+
switch (node.type) {
|
|
15
|
+
case 'function_definition': {
|
|
16
|
+
const nameNode = node.childForFieldName('name');
|
|
17
|
+
if (nameNode) {
|
|
18
|
+
const decorators = [];
|
|
19
|
+
if (node.previousSibling && node.previousSibling.type === 'decorator') {
|
|
20
|
+
decorators.push(node.previousSibling.text);
|
|
21
|
+
}
|
|
22
|
+
const parentClass = findPythonParentClass(node);
|
|
23
|
+
const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
|
|
24
|
+
const kind = parentClass ? 'method' : 'function';
|
|
25
|
+
definitions.push({
|
|
26
|
+
name: fullName,
|
|
27
|
+
kind,
|
|
28
|
+
line: node.startPosition.row + 1,
|
|
29
|
+
endLine: nodeEndLine(node),
|
|
30
|
+
decorators,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
case 'class_definition': {
|
|
37
|
+
const nameNode = node.childForFieldName('name');
|
|
38
|
+
if (nameNode) {
|
|
39
|
+
definitions.push({
|
|
40
|
+
name: nameNode.text,
|
|
41
|
+
kind: 'class',
|
|
42
|
+
line: node.startPosition.row + 1,
|
|
43
|
+
endLine: nodeEndLine(node),
|
|
44
|
+
});
|
|
45
|
+
const superclasses =
|
|
46
|
+
node.childForFieldName('superclasses') || findChild(node, 'argument_list');
|
|
47
|
+
if (superclasses) {
|
|
48
|
+
for (let i = 0; i < superclasses.childCount; i++) {
|
|
49
|
+
const child = superclasses.child(i);
|
|
50
|
+
if (child && child.type === 'identifier') {
|
|
51
|
+
classes.push({
|
|
52
|
+
name: nameNode.text,
|
|
53
|
+
extends: child.text,
|
|
54
|
+
line: node.startPosition.row + 1,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
case 'decorated_definition': {
|
|
64
|
+
for (let i = 0; i < node.childCount; i++) walkPythonNode(node.child(i));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
case 'call': {
|
|
69
|
+
const fn = node.childForFieldName('function');
|
|
70
|
+
if (fn) {
|
|
71
|
+
let callName = null;
|
|
72
|
+
if (fn.type === 'identifier') callName = fn.text;
|
|
73
|
+
else if (fn.type === 'attribute') {
|
|
74
|
+
const attr = fn.childForFieldName('attribute');
|
|
75
|
+
if (attr) callName = attr.text;
|
|
76
|
+
}
|
|
77
|
+
if (callName) calls.push({ name: callName, line: node.startPosition.row + 1 });
|
|
78
|
+
}
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
case 'import_statement': {
|
|
83
|
+
const names = [];
|
|
84
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
85
|
+
const child = node.child(i);
|
|
86
|
+
if (child && (child.type === 'dotted_name' || child.type === 'aliased_import')) {
|
|
87
|
+
const name =
|
|
88
|
+
child.type === 'aliased_import'
|
|
89
|
+
? (child.childForFieldName('alias') || child.childForFieldName('name'))?.text
|
|
90
|
+
: child.text;
|
|
91
|
+
if (name) names.push(name);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (names.length > 0)
|
|
95
|
+
imports.push({
|
|
96
|
+
source: names[0],
|
|
97
|
+
names,
|
|
98
|
+
line: node.startPosition.row + 1,
|
|
99
|
+
pythonImport: true,
|
|
100
|
+
});
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
case 'import_from_statement': {
|
|
105
|
+
let source = '';
|
|
106
|
+
const names = [];
|
|
107
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
108
|
+
const child = node.child(i);
|
|
109
|
+
if (!child) continue;
|
|
110
|
+
if (child.type === 'dotted_name' || child.type === 'relative_import') {
|
|
111
|
+
if (!source) source = child.text;
|
|
112
|
+
else names.push(child.text);
|
|
113
|
+
}
|
|
114
|
+
if (child.type === 'aliased_import') {
|
|
115
|
+
const n = child.childForFieldName('name') || child.child(0);
|
|
116
|
+
if (n) names.push(n.text);
|
|
117
|
+
}
|
|
118
|
+
if (child.type === 'wildcard_import') names.push('*');
|
|
119
|
+
}
|
|
120
|
+
if (source)
|
|
121
|
+
imports.push({ source, names, line: node.startPosition.row + 1, pythonImport: true });
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
for (let i = 0; i < node.childCount; i++) walkPythonNode(node.child(i));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function findPythonParentClass(node) {
|
|
130
|
+
let current = node.parent;
|
|
131
|
+
while (current) {
|
|
132
|
+
if (current.type === 'class_definition') {
|
|
133
|
+
const nameNode = current.childForFieldName('name');
|
|
134
|
+
return nameNode ? nameNode.text : null;
|
|
135
|
+
}
|
|
136
|
+
current = current.parent;
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
walkPythonNode(tree.rootNode);
|
|
142
|
+
return { definitions, calls, imports, classes, exports };
|
|
143
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { findChild, nodeEndLine } from './helpers.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract symbols from Ruby files.
|
|
5
|
+
*/
|
|
6
|
+
export function extractRubySymbols(tree, _filePath) {
|
|
7
|
+
const definitions = [];
|
|
8
|
+
const calls = [];
|
|
9
|
+
const imports = [];
|
|
10
|
+
const classes = [];
|
|
11
|
+
const exports = [];
|
|
12
|
+
|
|
13
|
+
function findRubyParentClass(node) {
|
|
14
|
+
let current = node.parent;
|
|
15
|
+
while (current) {
|
|
16
|
+
if (current.type === 'class') {
|
|
17
|
+
const nameNode = current.childForFieldName('name');
|
|
18
|
+
return nameNode ? nameNode.text : null;
|
|
19
|
+
}
|
|
20
|
+
if (current.type === 'module') {
|
|
21
|
+
const nameNode = current.childForFieldName('name');
|
|
22
|
+
return nameNode ? nameNode.text : null;
|
|
23
|
+
}
|
|
24
|
+
current = current.parent;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function walkRubyNode(node) {
|
|
30
|
+
switch (node.type) {
|
|
31
|
+
case 'class': {
|
|
32
|
+
const nameNode = node.childForFieldName('name');
|
|
33
|
+
if (nameNode) {
|
|
34
|
+
definitions.push({
|
|
35
|
+
name: nameNode.text,
|
|
36
|
+
kind: 'class',
|
|
37
|
+
line: node.startPosition.row + 1,
|
|
38
|
+
endLine: nodeEndLine(node),
|
|
39
|
+
});
|
|
40
|
+
const superclass = node.childForFieldName('superclass');
|
|
41
|
+
if (superclass) {
|
|
42
|
+
// superclass wraps the < token and class name
|
|
43
|
+
for (let i = 0; i < superclass.childCount; i++) {
|
|
44
|
+
const child = superclass.child(i);
|
|
45
|
+
if (child && (child.type === 'constant' || child.type === 'scope_resolution')) {
|
|
46
|
+
classes.push({
|
|
47
|
+
name: nameNode.text,
|
|
48
|
+
extends: child.text,
|
|
49
|
+
line: node.startPosition.row + 1,
|
|
50
|
+
});
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Direct superclass node may be a constant
|
|
55
|
+
if (superclass.type === 'superclass') {
|
|
56
|
+
for (let i = 0; i < superclass.childCount; i++) {
|
|
57
|
+
const child = superclass.child(i);
|
|
58
|
+
if (child && (child.type === 'constant' || child.type === 'scope_resolution')) {
|
|
59
|
+
classes.push({
|
|
60
|
+
name: nameNode.text,
|
|
61
|
+
extends: child.text,
|
|
62
|
+
line: node.startPosition.row + 1,
|
|
63
|
+
});
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
case 'module': {
|
|
74
|
+
const nameNode = node.childForFieldName('name');
|
|
75
|
+
if (nameNode) {
|
|
76
|
+
definitions.push({
|
|
77
|
+
name: nameNode.text,
|
|
78
|
+
kind: 'module',
|
|
79
|
+
line: node.startPosition.row + 1,
|
|
80
|
+
endLine: nodeEndLine(node),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
case 'method': {
|
|
87
|
+
const nameNode = node.childForFieldName('name');
|
|
88
|
+
if (nameNode) {
|
|
89
|
+
const parentClass = findRubyParentClass(node);
|
|
90
|
+
const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
|
|
91
|
+
definitions.push({
|
|
92
|
+
name: fullName,
|
|
93
|
+
kind: 'method',
|
|
94
|
+
line: node.startPosition.row + 1,
|
|
95
|
+
endLine: nodeEndLine(node),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
case 'singleton_method': {
|
|
102
|
+
const nameNode = node.childForFieldName('name');
|
|
103
|
+
if (nameNode) {
|
|
104
|
+
const parentClass = findRubyParentClass(node);
|
|
105
|
+
const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
|
|
106
|
+
definitions.push({
|
|
107
|
+
name: fullName,
|
|
108
|
+
kind: 'function',
|
|
109
|
+
line: node.startPosition.row + 1,
|
|
110
|
+
endLine: nodeEndLine(node),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
case 'call': {
|
|
117
|
+
const methodNode = node.childForFieldName('method');
|
|
118
|
+
if (methodNode) {
|
|
119
|
+
// Check for require/require_relative
|
|
120
|
+
if (methodNode.text === 'require' || methodNode.text === 'require_relative') {
|
|
121
|
+
const args = node.childForFieldName('arguments');
|
|
122
|
+
if (args) {
|
|
123
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
124
|
+
const arg = args.child(i);
|
|
125
|
+
if (arg && (arg.type === 'string' || arg.type === 'string_content')) {
|
|
126
|
+
const strContent = arg.text.replace(/^['"]|['"]$/g, '');
|
|
127
|
+
imports.push({
|
|
128
|
+
source: strContent,
|
|
129
|
+
names: [strContent.split('/').pop()],
|
|
130
|
+
line: node.startPosition.row + 1,
|
|
131
|
+
rubyRequire: true,
|
|
132
|
+
});
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
// Look inside string for string_content
|
|
136
|
+
if (arg && arg.type === 'string') {
|
|
137
|
+
const content = findChild(arg, 'string_content');
|
|
138
|
+
if (content) {
|
|
139
|
+
imports.push({
|
|
140
|
+
source: content.text,
|
|
141
|
+
names: [content.text.split('/').pop()],
|
|
142
|
+
line: node.startPosition.row + 1,
|
|
143
|
+
rubyRequire: true,
|
|
144
|
+
});
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} else if (
|
|
151
|
+
methodNode.text === 'include' ||
|
|
152
|
+
methodNode.text === 'extend' ||
|
|
153
|
+
methodNode.text === 'prepend'
|
|
154
|
+
) {
|
|
155
|
+
// Module inclusion — treated like implements
|
|
156
|
+
const parentClass = findRubyParentClass(node);
|
|
157
|
+
if (parentClass) {
|
|
158
|
+
const args = node.childForFieldName('arguments');
|
|
159
|
+
if (args) {
|
|
160
|
+
for (let i = 0; i < args.childCount; i++) {
|
|
161
|
+
const arg = args.child(i);
|
|
162
|
+
if (arg && (arg.type === 'constant' || arg.type === 'scope_resolution')) {
|
|
163
|
+
classes.push({
|
|
164
|
+
name: parentClass,
|
|
165
|
+
implements: arg.text,
|
|
166
|
+
line: node.startPosition.row + 1,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
calls.push({ name: methodNode.text, line: node.startPosition.row + 1 });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
for (let i = 0; i < node.childCount; i++) walkRubyNode(node.child(i));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
walkRubyNode(tree.rootNode);
|
|
184
|
+
return { definitions, calls, imports, classes, exports };
|
|
185
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { nodeEndLine } from './helpers.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extract symbols from Rust files.
|
|
5
|
+
*/
|
|
6
|
+
export function extractRustSymbols(tree, _filePath) {
|
|
7
|
+
const definitions = [];
|
|
8
|
+
const calls = [];
|
|
9
|
+
const imports = [];
|
|
10
|
+
const classes = [];
|
|
11
|
+
const exports = [];
|
|
12
|
+
|
|
13
|
+
function findCurrentImpl(node) {
|
|
14
|
+
let current = node.parent;
|
|
15
|
+
while (current) {
|
|
16
|
+
if (current.type === 'impl_item') {
|
|
17
|
+
const typeNode = current.childForFieldName('type');
|
|
18
|
+
return typeNode ? typeNode.text : null;
|
|
19
|
+
}
|
|
20
|
+
current = current.parent;
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function walkRustNode(node) {
|
|
26
|
+
switch (node.type) {
|
|
27
|
+
case 'function_item': {
|
|
28
|
+
const nameNode = node.childForFieldName('name');
|
|
29
|
+
if (nameNode) {
|
|
30
|
+
const implType = findCurrentImpl(node);
|
|
31
|
+
const fullName = implType ? `${implType}.${nameNode.text}` : nameNode.text;
|
|
32
|
+
const kind = implType ? 'method' : 'function';
|
|
33
|
+
definitions.push({
|
|
34
|
+
name: fullName,
|
|
35
|
+
kind,
|
|
36
|
+
line: node.startPosition.row + 1,
|
|
37
|
+
endLine: nodeEndLine(node),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
case 'struct_item': {
|
|
44
|
+
const nameNode = node.childForFieldName('name');
|
|
45
|
+
if (nameNode) {
|
|
46
|
+
definitions.push({
|
|
47
|
+
name: nameNode.text,
|
|
48
|
+
kind: 'struct',
|
|
49
|
+
line: node.startPosition.row + 1,
|
|
50
|
+
endLine: nodeEndLine(node),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
case 'enum_item': {
|
|
57
|
+
const nameNode = node.childForFieldName('name');
|
|
58
|
+
if (nameNode) {
|
|
59
|
+
definitions.push({
|
|
60
|
+
name: nameNode.text,
|
|
61
|
+
kind: 'enum',
|
|
62
|
+
line: node.startPosition.row + 1,
|
|
63
|
+
endLine: nodeEndLine(node),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
case 'trait_item': {
|
|
70
|
+
const nameNode = node.childForFieldName('name');
|
|
71
|
+
if (nameNode) {
|
|
72
|
+
definitions.push({
|
|
73
|
+
name: nameNode.text,
|
|
74
|
+
kind: 'trait',
|
|
75
|
+
line: node.startPosition.row + 1,
|
|
76
|
+
endLine: nodeEndLine(node),
|
|
77
|
+
});
|
|
78
|
+
const body = node.childForFieldName('body');
|
|
79
|
+
if (body) {
|
|
80
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
81
|
+
const child = body.child(i);
|
|
82
|
+
if (
|
|
83
|
+
child &&
|
|
84
|
+
(child.type === 'function_signature_item' || child.type === 'function_item')
|
|
85
|
+
) {
|
|
86
|
+
const methName = child.childForFieldName('name');
|
|
87
|
+
if (methName) {
|
|
88
|
+
definitions.push({
|
|
89
|
+
name: `${nameNode.text}.${methName.text}`,
|
|
90
|
+
kind: 'method',
|
|
91
|
+
line: child.startPosition.row + 1,
|
|
92
|
+
endLine: child.endPosition.row + 1,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
case 'impl_item': {
|
|
103
|
+
const typeNode = node.childForFieldName('type');
|
|
104
|
+
const traitNode = node.childForFieldName('trait');
|
|
105
|
+
if (typeNode && traitNode) {
|
|
106
|
+
classes.push({
|
|
107
|
+
name: typeNode.text,
|
|
108
|
+
implements: traitNode.text,
|
|
109
|
+
line: node.startPosition.row + 1,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
case 'use_declaration': {
|
|
116
|
+
const argNode = node.child(1);
|
|
117
|
+
if (argNode) {
|
|
118
|
+
const usePaths = extractRustUsePath(argNode);
|
|
119
|
+
for (const imp of usePaths) {
|
|
120
|
+
imports.push({
|
|
121
|
+
source: imp.source,
|
|
122
|
+
names: imp.names,
|
|
123
|
+
line: node.startPosition.row + 1,
|
|
124
|
+
rustUse: true,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
case 'call_expression': {
|
|
132
|
+
const fn = node.childForFieldName('function');
|
|
133
|
+
if (fn) {
|
|
134
|
+
if (fn.type === 'identifier') {
|
|
135
|
+
calls.push({ name: fn.text, line: node.startPosition.row + 1 });
|
|
136
|
+
} else if (fn.type === 'field_expression') {
|
|
137
|
+
const field = fn.childForFieldName('field');
|
|
138
|
+
if (field) calls.push({ name: field.text, line: node.startPosition.row + 1 });
|
|
139
|
+
} else if (fn.type === 'scoped_identifier') {
|
|
140
|
+
const name = fn.childForFieldName('name');
|
|
141
|
+
if (name) calls.push({ name: name.text, line: node.startPosition.row + 1 });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
case 'macro_invocation': {
|
|
148
|
+
const macroNode = node.child(0);
|
|
149
|
+
if (macroNode) {
|
|
150
|
+
calls.push({ name: `${macroNode.text}!`, line: node.startPosition.row + 1 });
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
for (let i = 0; i < node.childCount; i++) walkRustNode(node.child(i));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
walkRustNode(tree.rootNode);
|
|
160
|
+
return { definitions, calls, imports, classes, exports };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function extractRustUsePath(node) {
|
|
164
|
+
if (!node) return [];
|
|
165
|
+
|
|
166
|
+
if (node.type === 'use_list') {
|
|
167
|
+
const results = [];
|
|
168
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
169
|
+
results.push(...extractRustUsePath(node.child(i)));
|
|
170
|
+
}
|
|
171
|
+
return results;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (node.type === 'scoped_use_list') {
|
|
175
|
+
const pathNode = node.childForFieldName('path');
|
|
176
|
+
const listNode = node.childForFieldName('list');
|
|
177
|
+
const prefix = pathNode ? pathNode.text : '';
|
|
178
|
+
if (listNode) {
|
|
179
|
+
const names = [];
|
|
180
|
+
for (let i = 0; i < listNode.childCount; i++) {
|
|
181
|
+
const child = listNode.child(i);
|
|
182
|
+
if (
|
|
183
|
+
child &&
|
|
184
|
+
(child.type === 'identifier' || child.type === 'use_as_clause' || child.type === 'self')
|
|
185
|
+
) {
|
|
186
|
+
const name =
|
|
187
|
+
child.type === 'use_as_clause'
|
|
188
|
+
? (child.childForFieldName('alias') || child.childForFieldName('name'))?.text
|
|
189
|
+
: child.text;
|
|
190
|
+
if (name) names.push(name);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return [{ source: prefix, names }];
|
|
194
|
+
}
|
|
195
|
+
return [{ source: prefix, names: [] }];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (node.type === 'use_as_clause') {
|
|
199
|
+
const name = node.childForFieldName('alias') || node.childForFieldName('name');
|
|
200
|
+
return [{ source: node.text, names: name ? [name.text] : [] }];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (node.type === 'use_wildcard') {
|
|
204
|
+
const pathNode = node.childForFieldName('path');
|
|
205
|
+
return [{ source: pathNode ? pathNode.text : '*', names: ['*'] }];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (node.type === 'scoped_identifier' || node.type === 'identifier') {
|
|
209
|
+
const text = node.text;
|
|
210
|
+
const lastName = text.split('::').pop();
|
|
211
|
+
return [{ source: text, names: [lastName] }];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return [];
|
|
215
|
+
}
|
package/src/index.js
CHANGED
package/src/mcp.js
CHANGED
|
@@ -422,7 +422,8 @@ export async function startMCPServer(customDbPath, options = {}) {
|
|
|
422
422
|
break;
|
|
423
423
|
}
|
|
424
424
|
case 'list_repos': {
|
|
425
|
-
const { listRepos } = await import('./registry.js');
|
|
425
|
+
const { listRepos, pruneRegistry } = await import('./registry.js');
|
|
426
|
+
pruneRegistry();
|
|
426
427
|
let repos = listRepos();
|
|
427
428
|
if (allowedRepos) {
|
|
428
429
|
repos = repos.filter((r) => allowedRepos.includes(r.name));
|