Module for finding relevant code snippets and analyzing relationships.
| 142 | |
| 143 | |
| 144 | class CodeFinder: |
| 145 | """Module for finding relevant code snippets and analyzing relationships.""" |
| 146 | |
| 147 | def __init__(self, db_manager: DatabaseManager): |
| 148 | self.db_manager = db_manager |
| 149 | self.driver = self.db_manager.get_driver() |
| 150 | self._lacks_native_fulltext = getattr(db_manager, 'get_backend_type', lambda: 'neo4j')() != 'neo4j' |
| 151 | |
| 152 | def audit_kotlin_call_ambiguity( |
| 153 | self, |
| 154 | repo_path: Optional[str] = None, |
| 155 | limit: int = 20, |
| 156 | ) -> Dict[str, Any]: |
| 157 | """Audit Kotlin function-to-function CALLS edges for multi-target callsites.""" |
| 158 | repo_path = Path(repo_path).resolve().as_posix() if repo_path else None |
| 159 | repo_filter = "AND a.path STARTS WITH $repo_path" if repo_path else "" |
| 160 | query = f""" |
| 161 | MATCH (a:Function)-[r:CALLS]->(b:Function) |
| 162 | WHERE a.path ENDS WITH '.kt' |
| 163 | AND b.path ENDS WITH '.kt' |
| 164 | {repo_filter} |
| 165 | RETURN a.name as caller_name, |
| 166 | a.path as caller_path, |
| 167 | a.line_number as caller_line, |
| 168 | a.end_line as caller_end_line, |
| 169 | r.line_number as call_line, |
| 170 | r.full_call_name as full_call_name, |
| 171 | b.name as target_name, |
| 172 | b.path as target_path, |
| 173 | b.line_number as target_line, |
| 174 | b.context as target_context, |
| 175 | r.args as args |
| 176 | """ |
| 177 | with self.driver.session() as session: |
| 178 | rows = session.run(query, repo_path=repo_path).data() |
| 179 | return summarize_kotlin_call_ambiguity(rows, limit=limit) |
| 180 | |
| 181 | def format_query(self, find_by: Literal["Class", "Function"], fuzzy_search:bool, repo_path: Optional[str] = None) -> str: |
| 182 | """Format the search query based on the search type and fuzzy search settings.""" |
| 183 | repo_filter = "AND node.path STARTS WITH $repo_path" if repo_path else "" |
| 184 | if self._lacks_native_fulltext: |
| 185 | # FalkorDB does not support CALL db.idx.fulltext.queryNodes. |
| 186 | # Fall back to a pure Cypher CONTAINS/toLower match on node name. |
| 187 | name_filter = "toLower(node.name) CONTAINS toLower($search_term)" |
| 188 | return f""" |
| 189 | MATCH (node:{find_by}) |
| 190 | WHERE {name_filter} {repo_filter} |
| 191 | RETURN node.name as name, node.path as path, node.line_number as line_number, |
| 192 | node.source as source, node.docstring as docstring, node.is_dependency as is_dependency |
| 193 | ORDER BY node.is_dependency ASC, node.name |
| 194 | LIMIT 20 |
| 195 | """ |
| 196 | return f""" |
| 197 | CALL db.index.fulltext.queryNodes("code_search_index", $search_term) YIELD node, score |
| 198 | WITH node, score |
| 199 | WHERE node:{find_by} {'AND node.name CONTAINS $search_term' if not fuzzy_search else ''} {repo_filter} |
| 200 | RETURN node.name as name, node.path as path, node.line_number as line_number, |
| 201 | node.source as source, node.docstring as docstring, node.is_dependency as is_dependency |
no outgoing calls