Spaces:
Sleeping
Sleeping
File size: 4,475 Bytes
71b4454 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | import ast
import re
from typing import Dict, List, Any, Optional
from backend.indexer.manager import WorkspaceIndexer
from backend.indexer.incremental import IncrementalIndexerCache
class CodeIntelligenceEngine:
"""Provides navigation, symbol refactoring, and code diagnostics."""
def __init__(self, indexer: WorkspaceIndexer):
self.indexer = indexer
self.cache = IncrementalIndexerCache()
self.file_contents: Dict[str, str] = {}
def index_workspace_file(self, filepath: str, content: str):
if self.cache.should_reindex(filepath, content):
self.file_contents[filepath] = content
self.indexer.index_file(filepath, content)
def go_to_definition(self, symbol: str) -> Optional[Dict[str, Any]]:
"""Finds definition target (filepath and line number) for a given symbol."""
for filepath, content in self.file_contents.items():
try:
tree = ast.parse(content)
for node in ast.walk(tree):
if isinstance(node, (ast.FunctionDef, ast.ClassDef)) and node.name == symbol:
return {
"filepath": filepath,
"line": node.lineno,
"col": node.col_offset,
"type": "class" if isinstance(node, ast.ClassDef) else "function"
}
except SyntaxError:
continue
return None
def find_references(self, symbol: str) -> List[Dict[str, Any]]:
"""Finds all occurrences/usages of a symbol across the workspace."""
refs = []
pattern = re.compile(r'\b' + re.escape(symbol) + r'\b')
for filepath, content in self.file_contents.items():
for line_no, line in enumerate(content.splitlines(), start=1):
for match in pattern.finditer(line):
refs.append({
"filepath": filepath,
"line": line_no,
"col": match.start(),
"text": line.strip()
})
return refs
def rename_symbol(self, old_name: str, new_name: str) -> Dict[str, str]:
"""Performs refactoring by renaming symbols across workspace files."""
pattern = re.compile(r'\b' + re.escape(old_name) + r'\b')
modified_files = {}
for filepath, content in self.file_contents.items():
if pattern.search(content):
new_content = pattern.sub(new_name, content)
modified_files[filepath] = new_content
self.file_contents[filepath] = new_content
self.cache.invalidate(filepath)
self.indexer.index_file(filepath, new_content)
return modified_files
def get_diagnostics(self, filepath: str) -> List[Dict[str, Any]]:
"""Analyzes AST syntax errors and basic dead-code/unused imports."""
diagnostics = []
content = self.file_contents.get(filepath, "")
if not content:
return diagnostics
try:
tree = ast.parse(content)
except SyntaxError as err:
diagnostics.append({
"severity": "error",
"message": f"SyntaxError: {err.msg}",
"line": err.lineno,
"col": err.offset
})
return diagnostics
# Check unused imports / unused names
imported_names = set()
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for alias in node.names:
imported_names.add(alias.asname or alias.name)
elif isinstance(node, ast.ImportFrom):
for alias in node.names:
imported_names.add(alias.asname or alias.name)
full_text = content
for name in imported_names:
# Simple heuristic check if name occurs only once (the import statement itself)
occurrences = len(re.findall(r'\b' + re.escape(name) + r'\b', full_text))
if occurrences <= 1:
diagnostics.append({
"severity": "warning",
"message": f"Potentially unused import: '{name}'",
"line": 1
})
return diagnostics
|