@kentwynn/kgraph 0.1.25 → 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.
@@ -1,13 +1,15 @@
1
- export function extractCSharpSymbols(sourceText, filePath) {
2
- const lines = sourceText.split('\n');
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
- const typeStack = [];
8
- let braceDepth = 0;
9
- const addSymbol = (name, kind, lineNum, exported, parentName) => {
10
- const id = [filePath, kind, parentName, name, lineNum]
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: lineNum,
19
- endLine: lineNum,
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
- for (let i = 0; i < lines.length; i++) {
33
- const line = lines[i];
34
- const lineNum = i + 1;
35
- const trimmed = line.trim();
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
- // method: visibility [modifiers] returnType MethodName(
69
- // Must have parens, no semicolon (not a field), not a control-flow keyword
70
- const CONTROL_FLOW = new Set([
71
- 'if',
72
- 'for',
73
- 'foreach',
74
- 'while',
75
- 'switch',
76
- 'catch',
77
- 'else',
78
- 'using',
79
- 'lock',
80
- 'return',
81
- ]);
82
- if (!trimmed.endsWith(';')) {
83
- // Strip generic type parameters (e.g. Task<string> → Task) before matching method name
84
- const normalizedForMethod = trimmed.replace(/<[^>]*>/g, '');
85
- const methodMatch = normalizedForMethod.match(/\b(?:public|private|protected|internal|static|virtual|override|abstract|async|new|sealed)[\w\s]*\s+(\w+)\s*\(/);
86
- if (methodMatch && !CONTROL_FLOW.has(methodMatch[1])) {
87
- const parent = typeStack[typeStack.length - 1];
88
- const exported = trimmed.includes('public') || trimmed.includes('internal');
89
- const kind = parent ? 'method' : 'function';
90
- addSymbol(methodMatch[1], kind, lineNum, exported, parent?.name);
91
- continue;
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
- export function extractGoSymbols(sourceText, filePath) {
2
- const lines = sourceText.split('\n');
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
- const addSymbol = (name, kind, lineNum, parentName) => {
8
- const id = [filePath, kind, parentName, name, lineNum]
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: lineNum,
17
- endLine: lineNum,
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
- let inImportBlock = false;
31
- for (let i = 0; i < lines.length; i++) {
32
- const line = lines[i];
33
- const lineNum = i + 1;
34
- const trimmed = line.trim();
35
- if (!trimmed || trimmed.startsWith('//'))
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
- // top-level function: func FuncName(
85
- const funcMatch = trimmed.match(/^func\s+(\w+)\s*\(/);
86
- if (funcMatch) {
87
- addSymbol(funcMatch[1], 'function', lineNum);
88
- continue;
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
- // type declaration: type Name struct / type Name interface / type Name ...
91
- const typeMatch = trimmed.match(/^type\s+(\w+)\s+/);
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
- // Stack tracks class/object/interface scope
10
- const typeStack = [];
11
- let braceDepth = 0;
12
- const addSymbol = (name, kind, lineNum, exported, parentName) => {
13
- const id = [filePath, kind, parentName, name, lineNum]
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: lineNum,
22
- endLine: lineNum,
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
- for (let i = 0; i < lines.length; i++) {
36
- const line = lines[i];
37
- const lineNum = i + 1;
38
- const trimmed = line.trim();
39
- if (!trimmed || trimmed.startsWith('//') || trimmed.startsWith('*'))
40
- continue;
41
- braceDepth +=
42
- (line.match(/\{/g) ?? []).length - (line.match(/\}/g) ?? []).length;
43
- while (typeStack.length > 0 &&
44
- braceDepth < typeStack[typeStack.length - 1].braceDepth) {
45
- typeStack.pop();
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
- // import statement
48
- const importMatch = trimmed.match(/^import\s+([\w.]+(?:\.\*)?)/);
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
- if (ext === 'java') {
63
- // class / interface / enum / @interface
64
- const typeMatch = trimmed.match(/\b(?:public\s+|private\s+|protected\s+|abstract\s+|final\s+)*(?:class|interface|enum|@interface)\s+(\w+)/);
65
- if (typeMatch) {
66
- const parent = typeStack[typeStack.length - 1];
67
- const exported = trimmed.includes('public');
68
- addSymbol(typeMatch[1], 'class', lineNum, exported, parent?.name);
69
- typeStack.push({ name: typeMatch[1], braceDepth });
70
- continue;
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
- // method: visibility returnType methodName(
73
- // Avoid matching field declarations (no parenthesis)
74
- const methodMatch = trimmed.match(/\b(?:public|private|protected|static|final|synchronized|abstract|native|default|void|@Override\s+(?:public|protected))\b.*\s+(\w+)\s*\(/);
75
- if (methodMatch && !trimmed.startsWith('//')) {
76
- const parent = typeStack[typeStack.length - 1];
77
- const exported = trimmed.includes('public');
78
- const kind = parent ? 'method' : 'function';
79
- addSymbol(methodMatch[1], kind, lineNum, exported, parent?.name);
80
- continue;
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
- if (ext === 'kotlin') {
84
- // class / interface / object / data class / sealed class
85
- const typeMatch = trimmed.match(/\b(?:data\s+|sealed\s+|abstract\s+|open\s+|inner\s+)?(?:class|interface|object|enum\s+class)\s+(\w+)/);
86
- if (typeMatch) {
87
- const parent = typeStack[typeStack.length - 1];
88
- const exported = !trimmed.startsWith('private') && !trimmed.startsWith('internal');
89
- addSymbol(typeMatch[1], 'class', lineNum, exported, parent?.name);
90
- typeStack.push({ name: typeMatch[1], braceDepth });
91
- continue;
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
- // fun
94
- const funcMatch = trimmed.match(/\bfun\s+(\w+)\s*[(<]/);
95
- if (funcMatch) {
96
- const parent = typeStack[typeStack.length - 1];
97
- const exported = !trimmed.startsWith('private') && !trimmed.startsWith('internal');
98
- const kind = parent ? 'method' : 'function';
99
- addSymbol(funcMatch[1], kind, lineNum, exported, parent?.name);
100
- continue;
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>;