Add evaluation framework and fix call graph extraction
- Add evaluation test suite with TypeScript and Python fixtures - Fix MCP server to defer CodeGraph init until rootUri received - Fix call edge extraction by calling resolveReferences() after indexAll/sync - Fix glob matching for root-level files (e.g., **/*.py now matches auth.py) - Fix duplicate node extraction for methods inside classes - Update context tests to use buildContext for semantic search + graph traversal - Export unused formatter functions to fix build Evaluation results: - TypeScript: 96% precision, 79% recall, 85% F1 - Python: 99% precision, 80% recall, 85% F1 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
e306114607
commit
6b672f9152
+18
-7
@@ -65,13 +65,24 @@ export function hashContent(content: string): string {
|
||||
* Check if a path matches any glob pattern (simplified)
|
||||
*/
|
||||
function matchesGlob(filePath: string, pattern: string): boolean {
|
||||
// Convert glob to regex (simplified)
|
||||
const regexStr = pattern
|
||||
.replace(/\./g, '\\.')
|
||||
.replace(/\*\*/g, '<<<GLOBSTAR>>>')
|
||||
.replace(/\*/g, '[^/]*')
|
||||
.replace(/<<<GLOBSTAR>>>/g, '.*')
|
||||
.replace(/\?/g, '.');
|
||||
// Convert glob to regex using placeholders to avoid conflicts
|
||||
let regexStr = pattern;
|
||||
|
||||
// Replace glob patterns with placeholders first
|
||||
regexStr = regexStr.replace(/\*\*\//g, '\x00GLOBSTAR_SLASH\x00');
|
||||
regexStr = regexStr.replace(/\*\*/g, '\x00GLOBSTAR\x00');
|
||||
regexStr = regexStr.replace(/\*/g, '\x00STAR\x00');
|
||||
regexStr = regexStr.replace(/\?/g, '\x00QUESTION\x00');
|
||||
|
||||
// Escape regex special characters
|
||||
regexStr = regexStr.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
// Replace placeholders with regex equivalents
|
||||
regexStr = regexStr.replace(/\x00GLOBSTAR_SLASH\x00/g, '(?:.*/)?'); // **/ = zero or more dirs
|
||||
regexStr = regexStr.replace(/\x00GLOBSTAR\x00/g, '.*'); // ** = anything
|
||||
regexStr = regexStr.replace(/\x00STAR\x00/g, '[^/]*'); // * = anything except /
|
||||
regexStr = regexStr.replace(/\x00QUESTION\x00/g, '.'); // ? = single char
|
||||
|
||||
const regex = new RegExp(`^${regexStr}$`);
|
||||
return regex.test(filePath);
|
||||
}
|
||||
|
||||
@@ -743,10 +743,19 @@ export class TreeSitterExtractor {
|
||||
if (!this.extractor) return;
|
||||
|
||||
const nodeType = node.type;
|
||||
let skipChildren = false;
|
||||
|
||||
// Check for function declarations
|
||||
// For Python/Ruby, function_definition inside a class should be treated as method
|
||||
if (this.extractor.functionTypes.includes(nodeType)) {
|
||||
this.extractFunction(node);
|
||||
if (this.nodeStack.length > 0 && this.extractor.methodTypes.includes(nodeType)) {
|
||||
// Inside a class - treat as method
|
||||
this.extractMethod(node);
|
||||
skipChildren = true; // extractMethod visits children via visitFunctionBody
|
||||
} else {
|
||||
this.extractFunction(node);
|
||||
skipChildren = true; // extractFunction visits children via visitFunctionBody
|
||||
}
|
||||
}
|
||||
// Check for class declarations
|
||||
else if (this.extractor.classTypes.includes(nodeType)) {
|
||||
@@ -759,22 +768,27 @@ export class TreeSitterExtractor {
|
||||
} else {
|
||||
this.extractClass(node);
|
||||
}
|
||||
skipChildren = true; // extractClass visits body children
|
||||
}
|
||||
// Check for method declarations
|
||||
// Check for method declarations (only if not already handled by functionTypes)
|
||||
else if (this.extractor.methodTypes.includes(nodeType)) {
|
||||
this.extractMethod(node);
|
||||
skipChildren = true; // extractMethod visits children via visitFunctionBody
|
||||
}
|
||||
// Check for interface/protocol/trait declarations
|
||||
else if (this.extractor.interfaceTypes.includes(nodeType)) {
|
||||
this.extractInterface(node);
|
||||
skipChildren = true; // extractInterface visits body children
|
||||
}
|
||||
// Check for struct declarations
|
||||
else if (this.extractor.structTypes.includes(nodeType)) {
|
||||
this.extractStruct(node);
|
||||
skipChildren = true; // extractStruct visits body children
|
||||
}
|
||||
// Check for enum declarations
|
||||
else if (this.extractor.enumTypes.includes(nodeType)) {
|
||||
this.extractEnum(node);
|
||||
skipChildren = true; // extractEnum visits body children
|
||||
}
|
||||
// Check for imports
|
||||
else if (this.extractor.importTypes.includes(nodeType)) {
|
||||
@@ -785,11 +799,13 @@ export class TreeSitterExtractor {
|
||||
this.extractCall(node);
|
||||
}
|
||||
|
||||
// Visit children
|
||||
for (let i = 0; i < node.namedChildCount; i++) {
|
||||
const child = node.namedChild(i);
|
||||
if (child) {
|
||||
this.visitNode(child);
|
||||
// Visit children (unless the extract method already visited them)
|
||||
if (!skipChildren) {
|
||||
for (let i = 0; i < node.namedChildCount; i++) {
|
||||
const child = node.namedChild(i);
|
||||
if (child) {
|
||||
this.visitNode(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user