@kentwynn/kgraph 0.1.26 → 0.1.27
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 +3 -18
- package/dist/cli/commands/init.js +2 -25
- package/dist/cli/help.js +0 -5
- package/dist/cli/index.js +0 -2
- package/dist/cli/init-prompt.d.ts +2 -7
- package/dist/cli/init-prompt.js +0 -63
- package/dist/cli/init-recommendations.d.ts +1 -12
- package/dist/cli/init-recommendations.js +0 -23
- package/dist/cli/init-summary.d.ts +2 -4
- package/dist/cli/init-summary.js +10 -35
- package/dist/config/config.js +0 -33
- package/dist/scanner/c-symbol-extractor.d.ts +1 -1
- package/dist/scanner/c-symbol-extractor.js +108 -65
- package/dist/scanner/csharp-symbol-extractor.d.ts +1 -1
- package/dist/scanner/csharp-symbol-extractor.js +93 -67
- package/dist/scanner/go-symbol-extractor.d.ts +1 -1
- package/dist/scanner/go-symbol-extractor.js +75 -60
- package/dist/scanner/jvm-symbol-extractor.d.ts +1 -1
- package/dist/scanner/jvm-symbol-extractor.js +139 -71
- package/dist/scanner/python-symbol-extractor.d.ts +1 -1
- package/dist/scanner/python-symbol-extractor.js +92 -71
- package/dist/scanner/repo-scanner.js +3 -3
- package/dist/scanner/rust-symbol-extractor.d.ts +1 -1
- package/dist/scanner/rust-symbol-extractor.js +94 -89
- package/dist/scanner/tree-sitter-parser.d.ts +5 -0
- package/dist/scanner/tree-sitter-parser.js +55 -0
- package/dist/types/config.d.ts +0 -7
- package/package.json +10 -1
- package/dist/cli/commands/extractor.d.ts +0 -2
- package/dist/cli/commands/extractor.js +0 -50
- package/dist/extractors/extractor-registry.d.ts +0 -11
- package/dist/extractors/extractor-registry.js +0 -70
- package/dist/extractors/extractor-store.d.ts +0 -10
- package/dist/extractors/extractor-store.js +0 -58
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { parseSource } from './tree-sitter-parser.js';
|
|
2
|
+
export async function extractCSharpSymbols(sourceText, filePath) {
|
|
3
3
|
const symbols = [];
|
|
4
4
|
const dependencies = [];
|
|
5
5
|
const relationships = [];
|
|
6
6
|
const warnings = [];
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
if (!sourceText.trim()) {
|
|
8
|
+
return { symbols, dependencies, relationships, warnings };
|
|
9
|
+
}
|
|
10
|
+
const tree = await parseSource(sourceText, 'c_sharp');
|
|
11
|
+
const addSymbol = (name, kind, startLine, endLine, exported, parentName) => {
|
|
12
|
+
const id = [filePath, kind, parentName, name, startLine]
|
|
11
13
|
.filter(Boolean)
|
|
12
14
|
.join('#');
|
|
13
15
|
symbols.push({
|
|
@@ -15,8 +17,8 @@ export function extractCSharpSymbols(sourceText, filePath) {
|
|
|
15
17
|
name,
|
|
16
18
|
kind,
|
|
17
19
|
filePath,
|
|
18
|
-
startLine
|
|
19
|
-
endLine
|
|
20
|
+
startLine,
|
|
21
|
+
endLine,
|
|
20
22
|
exported,
|
|
21
23
|
parentName,
|
|
22
24
|
});
|
|
@@ -29,68 +31,92 @@ export function extractCSharpSymbols(sourceText, filePath) {
|
|
|
29
31
|
confidence: 'high',
|
|
30
32
|
});
|
|
31
33
|
};
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (!trimmed || trimmed.startsWith('//') || trimmed.startsWith('*'))
|
|
37
|
-
continue;
|
|
38
|
-
braceDepth +=
|
|
39
|
-
(line.match(/\{/g) ?? []).length - (line.match(/\}/g) ?? []).length;
|
|
40
|
-
while (typeStack.length > 0 &&
|
|
41
|
-
braceDepth < typeStack[typeStack.length - 1].braceDepth) {
|
|
42
|
-
typeStack.pop();
|
|
43
|
-
}
|
|
44
|
-
// using statement
|
|
45
|
-
const usingMatch = trimmed.match(/^using\s+([\w.]+)\s*;/);
|
|
46
|
-
if (usingMatch) {
|
|
47
|
-
const specifier = usingMatch[1];
|
|
48
|
-
dependencies.push({ fromFile: filePath, specifier, kind: 'package' });
|
|
49
|
-
relationships.push({
|
|
50
|
-
sourceType: 'file',
|
|
51
|
-
sourceId: filePath,
|
|
52
|
-
targetType: 'package',
|
|
53
|
-
targetId: specifier,
|
|
54
|
-
relationshipType: 'import',
|
|
55
|
-
confidence: 'high',
|
|
56
|
-
});
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
// class / interface / struct / enum / record
|
|
60
|
-
const typeMatch = trimmed.match(/\b(?:public|private|protected|internal|static|abstract|sealed|partial|readonly)?\s*(?:public|private|protected|internal|static|abstract|sealed|partial|readonly)?\s*(?:class|interface|struct|enum|record)\s+(\w+)/);
|
|
61
|
-
if (typeMatch && typeMatch[1]) {
|
|
62
|
-
const parent = typeStack[typeStack.length - 1];
|
|
63
|
-
const exported = trimmed.includes('public') || trimmed.includes('internal');
|
|
64
|
-
addSymbol(typeMatch[1], 'class', lineNum, exported, parent?.name);
|
|
65
|
-
typeStack.push({ name: typeMatch[1], braceDepth });
|
|
66
|
-
continue;
|
|
34
|
+
function hasVisibility(node, vis) {
|
|
35
|
+
for (const child of node.children) {
|
|
36
|
+
if (child.type === 'modifier' && child.text === vis)
|
|
37
|
+
return true;
|
|
67
38
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
'
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
function isExported(node) {
|
|
42
|
+
return hasVisibility(node, 'public') || hasVisibility(node, 'internal');
|
|
43
|
+
}
|
|
44
|
+
function walk(node, parentClassName) {
|
|
45
|
+
switch (node.type) {
|
|
46
|
+
case 'using_directive': {
|
|
47
|
+
// using System; or using System.Collections.Generic;
|
|
48
|
+
const nameNode = node.namedChildren.find((c) => c.type === 'qualified_name') ??
|
|
49
|
+
node.namedChildren.find((c) => c.type === 'identifier');
|
|
50
|
+
if (nameNode) {
|
|
51
|
+
const specifier = nameNode.text;
|
|
52
|
+
dependencies.push({ fromFile: filePath, specifier, kind: 'package' });
|
|
53
|
+
relationships.push({
|
|
54
|
+
sourceType: 'file',
|
|
55
|
+
sourceId: filePath,
|
|
56
|
+
targetType: 'package',
|
|
57
|
+
targetId: specifier,
|
|
58
|
+
relationshipType: 'import',
|
|
59
|
+
confidence: 'high',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
92
63
|
}
|
|
64
|
+
case 'namespace_declaration':
|
|
65
|
+
case 'file_scoped_namespace_declaration': {
|
|
66
|
+
// Recurse into namespace body
|
|
67
|
+
const body = node.childForFieldName('body') ??
|
|
68
|
+
node.namedChildren.find((c) => c.type === 'declaration_list');
|
|
69
|
+
if (body) {
|
|
70
|
+
for (const child of body.namedChildren) {
|
|
71
|
+
walk(child, parentClassName);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// file-scoped namespace: declarations are siblings
|
|
76
|
+
for (const child of node.namedChildren) {
|
|
77
|
+
walk(child, parentClassName);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
case 'class_declaration':
|
|
83
|
+
case 'interface_declaration':
|
|
84
|
+
case 'struct_declaration':
|
|
85
|
+
case 'enum_declaration':
|
|
86
|
+
case 'record_declaration': {
|
|
87
|
+
const nameNode = node.childForFieldName('name');
|
|
88
|
+
if (nameNode) {
|
|
89
|
+
const exported = isExported(node);
|
|
90
|
+
addSymbol(nameNode.text, 'class', node.startPosition.row + 1, node.endPosition.row + 1, exported, parentClassName);
|
|
91
|
+
// Walk body for methods and nested types
|
|
92
|
+
const body = node.childForFieldName('body') ??
|
|
93
|
+
node.namedChildren.find((c) => c.type === 'declaration_list');
|
|
94
|
+
if (body) {
|
|
95
|
+
for (const child of body.namedChildren) {
|
|
96
|
+
walk(child, nameNode.text);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
case 'method_declaration':
|
|
103
|
+
case 'constructor_declaration': {
|
|
104
|
+
const nameNode = node.childForFieldName('name');
|
|
105
|
+
if (nameNode) {
|
|
106
|
+
const exported = isExported(node);
|
|
107
|
+
const kind = parentClassName
|
|
108
|
+
? 'method'
|
|
109
|
+
: 'function';
|
|
110
|
+
addSymbol(nameNode.text, kind, node.startPosition.row + 1, node.endPosition.row + 1, exported, parentClassName);
|
|
111
|
+
}
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
for (const child of node.namedChildren) {
|
|
116
|
+
walk(child, parentClassName);
|
|
93
117
|
}
|
|
94
118
|
}
|
|
119
|
+
walk(tree.rootNode);
|
|
120
|
+
tree.delete();
|
|
95
121
|
return { symbols, dependencies, relationships, warnings };
|
|
96
122
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { SymbolExtractionResult } from './ts-symbol-extractor.js';
|
|
2
|
-
export declare function extractGoSymbols(sourceText: string, filePath: string): SymbolExtractionResult
|
|
2
|
+
export declare function extractGoSymbols(sourceText: string, filePath: string): Promise<SymbolExtractionResult>;
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { parseSource } from './tree-sitter-parser.js';
|
|
2
|
+
export async function extractGoSymbols(sourceText, filePath) {
|
|
3
3
|
const symbols = [];
|
|
4
4
|
const dependencies = [];
|
|
5
5
|
const relationships = [];
|
|
6
6
|
const warnings = [];
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
if (!sourceText.trim()) {
|
|
8
|
+
return { symbols, dependencies, relationships, warnings };
|
|
9
|
+
}
|
|
10
|
+
const tree = await parseSource(sourceText, 'go');
|
|
11
|
+
const addSymbol = (name, kind, startLine, endLine, parentName) => {
|
|
12
|
+
const id = [filePath, kind, parentName, name, startLine]
|
|
9
13
|
.filter(Boolean)
|
|
10
14
|
.join('#');
|
|
11
15
|
symbols.push({
|
|
@@ -13,8 +17,8 @@ export function extractGoSymbols(sourceText, filePath) {
|
|
|
13
17
|
name,
|
|
14
18
|
kind,
|
|
15
19
|
filePath,
|
|
16
|
-
startLine
|
|
17
|
-
endLine
|
|
20
|
+
startLine,
|
|
21
|
+
endLine,
|
|
18
22
|
exported: /^[A-Z]/.test(name), // Go: exported = starts with uppercase
|
|
19
23
|
parentName,
|
|
20
24
|
});
|
|
@@ -27,43 +31,12 @@ export function extractGoSymbols(sourceText, filePath) {
|
|
|
27
31
|
confidence: 'high',
|
|
28
32
|
});
|
|
29
33
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
continue;
|
|
37
|
-
// import block: import ( ... )
|
|
38
|
-
if (trimmed === 'import (') {
|
|
39
|
-
inImportBlock = true;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (inImportBlock) {
|
|
43
|
-
if (trimmed === ')') {
|
|
44
|
-
inImportBlock = false;
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
// e.g. "fmt" or aliased: log "log/slog"
|
|
48
|
-
const specMatch = trimmed.match(/"([^"]+)"/);
|
|
49
|
-
if (specMatch) {
|
|
50
|
-
const specifier = specMatch[1];
|
|
51
|
-
dependencies.push({ fromFile: filePath, specifier, kind: 'package' });
|
|
52
|
-
relationships.push({
|
|
53
|
-
sourceType: 'file',
|
|
54
|
-
sourceId: filePath,
|
|
55
|
-
targetType: 'package',
|
|
56
|
-
targetId: specifier,
|
|
57
|
-
relationshipType: 'import',
|
|
58
|
-
confidence: 'high',
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
// single import: import "fmt"
|
|
64
|
-
const singleImport = trimmed.match(/^import\s+"([^"]+)"/);
|
|
65
|
-
if (singleImport) {
|
|
66
|
-
const specifier = singleImport[1];
|
|
34
|
+
function extractImportSpec(node) {
|
|
35
|
+
// import_spec contains an interpreted_string_literal with the path
|
|
36
|
+
const pathNode = node.namedChildren.find((c) => c.type === 'interpreted_string_literal');
|
|
37
|
+
if (pathNode) {
|
|
38
|
+
// Strip quotes from the string literal
|
|
39
|
+
const specifier = pathNode.text.replace(/^"|"$/g, '');
|
|
67
40
|
dependencies.push({ fromFile: filePath, specifier, kind: 'package' });
|
|
68
41
|
relationships.push({
|
|
69
42
|
sourceType: 'file',
|
|
@@ -73,26 +46,68 @@ export function extractGoSymbols(sourceText, filePath) {
|
|
|
73
46
|
relationshipType: 'import',
|
|
74
47
|
confidence: 'high',
|
|
75
48
|
});
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
// method with receiver: func (r ReceiverType) MethodName(
|
|
79
|
-
const methodMatch = trimmed.match(/^func\s+\(\s*\w+\s+\*?(\w+)\s*\)\s+(\w+)\s*\(/);
|
|
80
|
-
if (methodMatch) {
|
|
81
|
-
addSymbol(methodMatch[2], 'method', lineNum, methodMatch[1]);
|
|
82
|
-
continue;
|
|
83
49
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
50
|
+
}
|
|
51
|
+
function walk(node) {
|
|
52
|
+
switch (node.type) {
|
|
53
|
+
case 'import_declaration': {
|
|
54
|
+
// Could contain import_spec_list or a single import_spec
|
|
55
|
+
for (const child of node.namedChildren) {
|
|
56
|
+
if (child.type === 'import_spec_list') {
|
|
57
|
+
for (const spec of child.namedChildren) {
|
|
58
|
+
if (spec.type === 'import_spec') {
|
|
59
|
+
extractImportSpec(spec);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else if (child.type === 'import_spec') {
|
|
64
|
+
extractImportSpec(child);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
case 'type_declaration': {
|
|
70
|
+
// type_declaration contains type_spec children
|
|
71
|
+
for (const child of node.namedChildren) {
|
|
72
|
+
if (child.type === 'type_spec') {
|
|
73
|
+
const nameNode = child.childForFieldName('name');
|
|
74
|
+
if (nameNode) {
|
|
75
|
+
addSymbol(nameNode.text, 'class', node.startPosition.row + 1, node.endPosition.row + 1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
case 'function_declaration': {
|
|
82
|
+
const nameNode = node.childForFieldName('name');
|
|
83
|
+
if (nameNode) {
|
|
84
|
+
addSymbol(nameNode.text, 'function', node.startPosition.row + 1, node.endPosition.row + 1);
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
case 'method_declaration': {
|
|
89
|
+
const nameNode = node.childForFieldName('name');
|
|
90
|
+
const receiverNode = node.childForFieldName('receiver');
|
|
91
|
+
let parentName;
|
|
92
|
+
if (receiverNode) {
|
|
93
|
+
// receiver is a parameter_list with a parameter_declaration inside
|
|
94
|
+
// that contains the type (possibly pointer_type wrapping type_identifier)
|
|
95
|
+
const typeNode = receiverNode.descendantsOfType('type_identifier')[0];
|
|
96
|
+
if (typeNode) {
|
|
97
|
+
parentName = typeNode.text;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (nameNode) {
|
|
101
|
+
addSymbol(nameNode.text, 'method', node.startPosition.row + 1, node.endPosition.row + 1, parentName);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
89
105
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (typeMatch) {
|
|
93
|
-
addSymbol(typeMatch[1], 'class', lineNum); // 'class' is closest kind for struct/interface
|
|
94
|
-
continue;
|
|
106
|
+
for (const child of node.namedChildren) {
|
|
107
|
+
walk(child);
|
|
95
108
|
}
|
|
96
109
|
}
|
|
110
|
+
walk(tree.rootNode);
|
|
111
|
+
tree.delete();
|
|
97
112
|
return { symbols, dependencies, relationships, warnings };
|
|
98
113
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { SymbolExtractionResult } from './ts-symbol-extractor.js';
|
|
2
|
-
export declare function extractJvmSymbols(sourceText: string, filePath: string): SymbolExtractionResult
|
|
2
|
+
export declare function extractJvmSymbols(sourceText: string, filePath: string): Promise<SymbolExtractionResult>;
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
import { parseSource } from './tree-sitter-parser.js';
|
|
1
2
|
// Handles Java (.java) and Kotlin (.kt, .kts)
|
|
2
|
-
export function extractJvmSymbols(sourceText, filePath) {
|
|
3
|
-
const ext = filePath.endsWith('.kt') || filePath.endsWith('.kts') ? 'kotlin' : 'java';
|
|
4
|
-
const lines = sourceText.split('\n');
|
|
3
|
+
export async function extractJvmSymbols(sourceText, filePath) {
|
|
5
4
|
const symbols = [];
|
|
6
5
|
const dependencies = [];
|
|
7
6
|
const relationships = [];
|
|
8
7
|
const warnings = [];
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
8
|
+
if (!sourceText.trim()) {
|
|
9
|
+
return { symbols, dependencies, relationships, warnings };
|
|
10
|
+
}
|
|
11
|
+
const lang = filePath.endsWith('.kt') || filePath.endsWith('.kts') ? 'kotlin' : 'java';
|
|
12
|
+
const tree = await parseSource(sourceText, lang);
|
|
13
|
+
const addSymbol = (name, kind, startLine, endLine, exported, parentName) => {
|
|
14
|
+
const id = [filePath, kind, parentName, name, startLine]
|
|
14
15
|
.filter(Boolean)
|
|
15
16
|
.join('#');
|
|
16
17
|
symbols.push({
|
|
@@ -18,8 +19,8 @@ export function extractJvmSymbols(sourceText, filePath) {
|
|
|
18
19
|
name,
|
|
19
20
|
kind,
|
|
20
21
|
filePath,
|
|
21
|
-
startLine
|
|
22
|
-
endLine
|
|
22
|
+
startLine,
|
|
23
|
+
endLine,
|
|
23
24
|
exported,
|
|
24
25
|
parentName,
|
|
25
26
|
});
|
|
@@ -32,74 +33,141 @@ export function extractJvmSymbols(sourceText, filePath) {
|
|
|
32
33
|
confidence: 'high',
|
|
33
34
|
});
|
|
34
35
|
};
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
function hasModifier(node, modifier) {
|
|
37
|
+
const mods = node.childForFieldName('modifiers') ??
|
|
38
|
+
node.namedChildren.find((c) => c.type === 'modifiers');
|
|
39
|
+
if (!mods)
|
|
40
|
+
return false;
|
|
41
|
+
return mods.text.includes(modifier);
|
|
42
|
+
}
|
|
43
|
+
function walkJava(node, parentClassName) {
|
|
44
|
+
switch (node.type) {
|
|
45
|
+
case 'import_declaration': {
|
|
46
|
+
// Java import: scoped_identifier child
|
|
47
|
+
const scopedId = node.namedChildren.find((c) => c.type === 'scoped_identifier' || c.type === 'identifier');
|
|
48
|
+
if (scopedId) {
|
|
49
|
+
const specifier = scopedId.text.replace(/\.\*$/, '');
|
|
50
|
+
dependencies.push({ fromFile: filePath, specifier, kind: 'package' });
|
|
51
|
+
relationships.push({
|
|
52
|
+
sourceType: 'file',
|
|
53
|
+
sourceId: filePath,
|
|
54
|
+
targetType: 'package',
|
|
55
|
+
targetId: specifier,
|
|
56
|
+
relationshipType: 'import',
|
|
57
|
+
confidence: 'high',
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
case 'class_declaration':
|
|
63
|
+
case 'interface_declaration':
|
|
64
|
+
case 'enum_declaration':
|
|
65
|
+
case 'annotation_type_declaration': {
|
|
66
|
+
const nameNode = node.childForFieldName('name');
|
|
67
|
+
if (nameNode) {
|
|
68
|
+
const exported = hasModifier(node, 'public');
|
|
69
|
+
addSymbol(nameNode.text, 'class', node.startPosition.row + 1, node.endPosition.row + 1, exported, parentClassName);
|
|
70
|
+
// Walk class body for methods and nested types
|
|
71
|
+
const body = node.childForFieldName('body') ??
|
|
72
|
+
node.namedChildren.find((c) => c.type === 'class_body' ||
|
|
73
|
+
c.type === 'interface_body' ||
|
|
74
|
+
c.type === 'enum_body');
|
|
75
|
+
if (body) {
|
|
76
|
+
for (const child of body.namedChildren) {
|
|
77
|
+
walkJava(child, nameNode.text);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
case 'method_declaration':
|
|
84
|
+
case 'constructor_declaration': {
|
|
85
|
+
const nameNode = node.childForFieldName('name');
|
|
86
|
+
if (nameNode) {
|
|
87
|
+
const exported = hasModifier(node, 'public');
|
|
88
|
+
const kind = parentClassName
|
|
89
|
+
? 'method'
|
|
90
|
+
: 'function';
|
|
91
|
+
addSymbol(nameNode.text, kind, node.startPosition.row + 1, node.endPosition.row + 1, exported, parentClassName);
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
46
95
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (importMatch) {
|
|
50
|
-
const specifier = importMatch[1].replace(/\.\*$/, '');
|
|
51
|
-
dependencies.push({ fromFile: filePath, specifier, kind: 'package' });
|
|
52
|
-
relationships.push({
|
|
53
|
-
sourceType: 'file',
|
|
54
|
-
sourceId: filePath,
|
|
55
|
-
targetType: 'package',
|
|
56
|
-
targetId: specifier,
|
|
57
|
-
relationshipType: 'import',
|
|
58
|
-
confidence: 'high',
|
|
59
|
-
});
|
|
60
|
-
continue;
|
|
96
|
+
for (const child of node.namedChildren) {
|
|
97
|
+
walkJava(child, parentClassName);
|
|
61
98
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
99
|
+
}
|
|
100
|
+
function walkKotlin(node, parentClassName) {
|
|
101
|
+
switch (node.type) {
|
|
102
|
+
case 'import': {
|
|
103
|
+
// Kotlin import: qualified_identifier child
|
|
104
|
+
const qualId = node.namedChildren.find((c) => c.type === 'qualified_identifier' || c.type === 'identifier');
|
|
105
|
+
if (qualId) {
|
|
106
|
+
const specifier = qualId.text;
|
|
107
|
+
dependencies.push({ fromFile: filePath, specifier, kind: 'package' });
|
|
108
|
+
relationships.push({
|
|
109
|
+
sourceType: 'file',
|
|
110
|
+
sourceId: filePath,
|
|
111
|
+
targetType: 'package',
|
|
112
|
+
targetId: specifier,
|
|
113
|
+
relationshipType: 'import',
|
|
114
|
+
confidence: 'high',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
71
118
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
119
|
+
case 'class_declaration': {
|
|
120
|
+
const nameNode = node.childForFieldName('name') ??
|
|
121
|
+
node.namedChildren.find((c) => c.type === 'identifier');
|
|
122
|
+
if (nameNode) {
|
|
123
|
+
const exported = !hasModifier(node, 'private') && !hasModifier(node, 'internal');
|
|
124
|
+
addSymbol(nameNode.text, 'class', node.startPosition.row + 1, node.endPosition.row + 1, exported, parentClassName);
|
|
125
|
+
// Walk class body
|
|
126
|
+
const body = node.namedChildren.find((c) => c.type === 'class_body');
|
|
127
|
+
if (body) {
|
|
128
|
+
for (const child of body.namedChildren) {
|
|
129
|
+
walkKotlin(child, nameNode.text);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return;
|
|
81
134
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
135
|
+
case 'object_declaration': {
|
|
136
|
+
const nameNode = node.namedChildren.find((c) => c.type === 'identifier');
|
|
137
|
+
if (nameNode) {
|
|
138
|
+
const exported = !hasModifier(node, 'private') && !hasModifier(node, 'internal');
|
|
139
|
+
addSymbol(nameNode.text, 'class', node.startPosition.row + 1, node.endPosition.row + 1, exported, parentClassName);
|
|
140
|
+
const body = node.namedChildren.find((c) => c.type === 'class_body');
|
|
141
|
+
if (body) {
|
|
142
|
+
for (const child of body.namedChildren) {
|
|
143
|
+
walkKotlin(child, nameNode.text);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
92
148
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
149
|
+
case 'function_declaration': {
|
|
150
|
+
const nameNode = node.namedChildren.find((c) => c.type === 'identifier');
|
|
151
|
+
if (nameNode) {
|
|
152
|
+
const exported = !hasModifier(node, 'private') && !hasModifier(node, 'internal');
|
|
153
|
+
const kind = parentClassName
|
|
154
|
+
? 'method'
|
|
155
|
+
: 'function';
|
|
156
|
+
addSymbol(nameNode.text, kind, node.startPosition.row + 1, node.endPosition.row + 1, exported, parentClassName);
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
101
159
|
}
|
|
102
160
|
}
|
|
161
|
+
for (const child of node.namedChildren) {
|
|
162
|
+
walkKotlin(child, parentClassName);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (lang === 'java') {
|
|
166
|
+
walkJava(tree.rootNode);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
walkKotlin(tree.rootNode);
|
|
103
170
|
}
|
|
171
|
+
tree.delete();
|
|
104
172
|
return { symbols, dependencies, relationships, warnings };
|
|
105
173
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { SymbolExtractionResult } from './ts-symbol-extractor.js';
|
|
2
|
-
export declare function extractPythonSymbols(sourceText: string, filePath: string): SymbolExtractionResult
|
|
2
|
+
export declare function extractPythonSymbols(sourceText: string, filePath: string): Promise<SymbolExtractionResult>;
|