@kentwynn/kgraph 0.1.26 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +3 -18
  2. package/dist/cli/commands/context.d.ts +2 -2
  3. package/dist/cli/commands/context.js +82 -23
  4. package/dist/cli/commands/init.js +2 -25
  5. package/dist/cli/commands/workflow.js +2 -2
  6. package/dist/cli/help.d.ts +1 -0
  7. package/dist/cli/help.js +4 -6
  8. package/dist/cli/index.js +0 -2
  9. package/dist/cli/init-prompt.d.ts +2 -7
  10. package/dist/cli/init-prompt.js +0 -63
  11. package/dist/cli/init-recommendations.d.ts +1 -12
  12. package/dist/cli/init-recommendations.js +0 -23
  13. package/dist/cli/init-summary.d.ts +2 -4
  14. package/dist/cli/init-summary.js +10 -35
  15. package/dist/config/config.js +0 -33
  16. package/dist/context/context-query.js +23 -0
  17. package/dist/scanner/c-symbol-extractor.d.ts +1 -1
  18. package/dist/scanner/c-symbol-extractor.js +108 -65
  19. package/dist/scanner/csharp-symbol-extractor.d.ts +1 -1
  20. package/dist/scanner/csharp-symbol-extractor.js +93 -67
  21. package/dist/scanner/go-symbol-extractor.d.ts +1 -1
  22. package/dist/scanner/go-symbol-extractor.js +75 -60
  23. package/dist/scanner/jvm-symbol-extractor.d.ts +1 -1
  24. package/dist/scanner/jvm-symbol-extractor.js +139 -71
  25. package/dist/scanner/python-symbol-extractor.d.ts +1 -1
  26. package/dist/scanner/python-symbol-extractor.js +92 -71
  27. package/dist/scanner/repo-scanner.js +65 -8
  28. package/dist/scanner/rust-symbol-extractor.d.ts +1 -1
  29. package/dist/scanner/rust-symbol-extractor.js +94 -89
  30. package/dist/scanner/tree-sitter-parser.d.ts +5 -0
  31. package/dist/scanner/tree-sitter-parser.js +55 -0
  32. package/dist/types/cognition.d.ts +3 -2
  33. package/dist/types/config.d.ts +0 -7
  34. package/dist/types/maps.d.ts +6 -5
  35. package/package.json +10 -1
  36. package/dist/cli/commands/extractor.d.ts +0 -2
  37. package/dist/cli/commands/extractor.js +0 -50
  38. package/dist/extractors/extractor-registry.d.ts +0 -11
  39. package/dist/extractors/extractor-registry.js +0 -70
  40. package/dist/extractors/extractor-store.d.ts +0 -10
  41. package/dist/extractors/extractor-store.js +0 -58
@@ -1,14 +1,19 @@
1
+ import { parseSource } from './tree-sitter-parser.js';
2
+ const CPP_EXTS = new Set(['.cpp', '.cc', '.cxx', '.hpp', '.hxx']);
1
3
  // Handles C (.c, .h) and C++ (.cpp, .cc, .cxx, .hpp, .hxx)
2
- export function extractCSymbols(sourceText, filePath) {
3
- const lines = sourceText.split('\n');
4
+ export async function extractCSymbols(sourceText, filePath) {
4
5
  const symbols = [];
5
6
  const dependencies = [];
6
7
  const relationships = [];
7
8
  const warnings = [];
8
- const typeStack = [];
9
- let braceDepth = 0;
10
- const addSymbol = (name, kind, lineNum, parentName) => {
11
- const id = [filePath, kind, parentName, name, lineNum]
9
+ if (!sourceText.trim()) {
10
+ return { symbols, dependencies, relationships, warnings };
11
+ }
12
+ const ext = filePath.substring(filePath.lastIndexOf('.'));
13
+ const grammar = CPP_EXTS.has(ext) ? 'cpp' : 'c';
14
+ const tree = await parseSource(sourceText, grammar);
15
+ const addSymbol = (name, kind, startLine, endLine, parentName) => {
16
+ const id = [filePath, kind, parentName, name, startLine]
12
17
  .filter(Boolean)
13
18
  .join('#');
14
19
  symbols.push({
@@ -16,8 +21,8 @@ export function extractCSymbols(sourceText, filePath) {
16
21
  name,
17
22
  kind,
18
23
  filePath,
19
- startLine: lineNum,
20
- endLine: lineNum,
24
+ startLine,
25
+ endLine,
21
26
  exported: false,
22
27
  parentName,
23
28
  });
@@ -30,67 +35,105 @@ export function extractCSymbols(sourceText, filePath) {
30
35
  confidence: 'high',
31
36
  });
32
37
  };
33
- for (let i = 0; i < lines.length; i++) {
34
- const line = lines[i];
35
- const lineNum = i + 1;
36
- const trimmed = line.trim();
37
- if (!trimmed || trimmed.startsWith('//') || trimmed.startsWith('*'))
38
- continue;
39
- braceDepth +=
40
- (line.match(/\{/g) ?? []).length - (line.match(/\}/g) ?? []).length;
41
- while (typeStack.length > 0 &&
42
- braceDepth < typeStack[typeStack.length - 1].braceDepth) {
43
- typeStack.pop();
44
- }
45
- // #include <...> or #include "..."
46
- const includeMatch = trimmed.match(/^#include\s+[<"]([^>"]+)[>"]/);
47
- if (includeMatch) {
48
- const specifier = includeMatch[1];
49
- const kind = trimmed.includes('"') ? 'local' : 'package';
50
- dependencies.push({ fromFile: filePath, specifier, kind });
51
- relationships.push({
52
- sourceType: 'file',
53
- sourceId: filePath,
54
- targetType: kind === 'local' ? 'file' : 'package',
55
- targetId: specifier,
56
- relationshipType: 'import',
57
- confidence: 'high',
58
- });
59
- continue;
38
+ function getFuncName(node) {
39
+ // function_definition has a function_declarator child which contains the identifier
40
+ const declarator = node.childForFieldName('declarator');
41
+ if (!declarator)
42
+ return null;
43
+ if (declarator.type === 'function_declarator') {
44
+ const nameNode = declarator.childForFieldName('declarator');
45
+ return nameNode?.text ?? null;
60
46
  }
61
- // class / struct (C++ with body — has name before {)
62
- const classMatch = trimmed.match(/\b(?:class|struct)\s+(\w+)\s*(?::[^{]*)?\s*\{/);
63
- if (classMatch) {
64
- addSymbol(classMatch[1], 'class', lineNum, typeStack[typeStack.length - 1]?.name);
65
- typeStack.push({ name: classMatch[1], braceDepth });
66
- continue;
47
+ // For pointer_declarator wrapping function_declarator
48
+ const funcDecl = declarator.descendantsOfType('function_declarator')[0];
49
+ if (funcDecl) {
50
+ const nameNode = funcDecl.childForFieldName('declarator');
51
+ return nameNode?.text ?? null;
67
52
  }
68
- // Function definition: returnType funcName( — must have ( and no ; on same line
69
- // Exclude preprocessor, declarations without body
70
- if (!trimmed.endsWith(';') && !trimmed.startsWith('#')) {
71
- const funcMatch = trimmed.match(/\b(\w+)\s*\((?:[^)]*)?\)\s*(?:const\s*)?(?:noexcept\s*)?(?:override\s*)?(?:final\s*)?\{?$/);
72
- // Filter out common false positives: if/for/while/switch/catch/else
73
- const CONTROL_FLOW = new Set([
74
- 'if',
75
- 'for',
76
- 'while',
77
- 'switch',
78
- 'catch',
79
- 'else',
80
- 'return',
81
- 'sizeof',
82
- 'typeof',
83
- ]);
84
- if (funcMatch &&
85
- !CONTROL_FLOW.has(funcMatch[1]) &&
86
- funcMatch[1] !== 'class' &&
87
- funcMatch[1] !== 'struct') {
88
- const parent = typeStack[typeStack.length - 1];
89
- const kind = parent ? 'method' : 'function';
90
- addSymbol(funcMatch[1], kind, lineNum, parent?.name);
91
- continue;
53
+ return null;
54
+ }
55
+ function walk(node, parentClassName) {
56
+ switch (node.type) {
57
+ case 'preproc_include': {
58
+ // #include <...> or #include "..."
59
+ const pathNode = node.namedChildren.find((c) => c.type === 'system_lib_string') ??
60
+ node.namedChildren.find((c) => c.type === 'string_literal');
61
+ if (pathNode) {
62
+ let specifier;
63
+ let kind;
64
+ if (pathNode.type === 'system_lib_string') {
65
+ // <iostream> — strip angle brackets
66
+ specifier = pathNode.text.replace(/^<|>$/g, '');
67
+ kind = 'package';
68
+ }
69
+ else {
70
+ // "myheader.h" — extract string content
71
+ const content = pathNode.namedChildren.find((c) => c.type === 'string_content');
72
+ specifier = content?.text ?? pathNode.text.replace(/^"|"$/g, '');
73
+ kind = 'local';
74
+ }
75
+ dependencies.push({ fromFile: filePath, specifier, kind });
76
+ relationships.push({
77
+ sourceType: 'file',
78
+ sourceId: filePath,
79
+ targetType: kind === 'local' ? 'file' : 'package',
80
+ targetId: specifier,
81
+ relationshipType: 'import',
82
+ confidence: 'high',
83
+ });
84
+ }
85
+ return;
86
+ }
87
+ case 'class_specifier':
88
+ case 'struct_specifier': {
89
+ const nameNode = node.childForFieldName('name');
90
+ if (nameNode) {
91
+ addSymbol(nameNode.text, 'class', node.startPosition.row + 1, node.endPosition.row + 1, parentClassName);
92
+ // Walk body for methods
93
+ const body = node.childForFieldName('body');
94
+ if (body) {
95
+ for (const child of body.namedChildren) {
96
+ walk(child, nameNode.text);
97
+ }
98
+ }
99
+ }
100
+ return;
92
101
  }
102
+ case 'function_definition': {
103
+ const name = getFuncName(node);
104
+ if (name) {
105
+ const kind = parentClassName
106
+ ? 'method'
107
+ : 'function';
108
+ addSymbol(name, kind, node.startPosition.row + 1, node.endPosition.row + 1, parentClassName);
109
+ }
110
+ return;
111
+ }
112
+ case 'declaration': {
113
+ // Could be a function declaration (prototype) inside a class
114
+ if (parentClassName) {
115
+ const declarator = node.childForFieldName('declarator');
116
+ if (declarator) {
117
+ const funcDecl = declarator.type === 'function_declarator'
118
+ ? declarator
119
+ : declarator.descendantsOfType('function_declarator')[0];
120
+ if (funcDecl) {
121
+ const nameNode = funcDecl.childForFieldName('declarator');
122
+ if (nameNode) {
123
+ addSymbol(nameNode.text, 'method', node.startPosition.row + 1, node.endPosition.row + 1, parentClassName);
124
+ }
125
+ return;
126
+ }
127
+ }
128
+ }
129
+ break;
130
+ }
131
+ }
132
+ for (const child of node.namedChildren) {
133
+ walk(child, parentClassName);
93
134
  }
94
135
  }
136
+ walk(tree.rootNode);
137
+ tree.delete();
95
138
  return { symbols, dependencies, relationships, warnings };
96
139
  }
@@ -1,2 +1,2 @@
1
1
  import type { SymbolExtractionResult } from './ts-symbol-extractor.js';
2
- export declare function extractCSharpSymbols(sourceText: string, filePath: string): SymbolExtractionResult;
2
+ export declare function extractCSharpSymbols(sourceText: string, filePath: string): Promise<SymbolExtractionResult>;
@@ -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>;