22from ...entities .entity import Entity
33from ...entities .file import File
44from typing import Optional
5- from ..analyzer import AbstractAnalyzer
5+ from ..tree_sitter_base import TreeSitterAnalyzer
66
77from multilspy import SyncLanguageServer
88
1212import logging
1313logger = logging .getLogger ('code_graph' )
1414
15- class KotlinAnalyzer (AbstractAnalyzer ):
15+ class KotlinAnalyzer (TreeSitterAnalyzer ):
16+ entity_node_types = {
17+ 'class_declaration' : "Class" ,
18+ 'object_declaration' : "Object" ,
19+ 'function_declaration' : "Function" ,
20+ }
21+ type_definition_node_types = ('class_declaration' , 'object_declaration' )
22+ callable_definition_node_types = (
23+ 'function_declaration' ,
24+ 'class_declaration' ,
25+ 'object_declaration' ,
26+ )
27+ callable_exclude_node_types = ('class_declaration' , 'object_declaration' )
28+ type_resolution_keys = (
29+ "implement_interface" ,
30+ "base_class" ,
31+ "parameters" ,
32+ "return_type" ,
33+ )
34+ method_resolution_keys = ("call" ,)
35+
1636 def __init__ (self ) -> None :
1737 super ().__init__ (Language (tskotlin .language ()))
1838
@@ -44,7 +64,7 @@ def get_entity_name(self, node: Node) -> str:
4464 if child .type == 'identifier' :
4565 return child .text .decode ('utf-8' )
4666 raise ValueError (f"Cannot extract name from entity type: { node .type } " )
47-
67+
4868 def get_entity_docstring (self , node : Node ) -> Optional [str ]:
4969 if node .type in ['class_declaration' , 'object_declaration' , 'function_declaration' ]:
5070 # Check for KDoc comment (/** ... */) before the node
@@ -54,14 +74,11 @@ def get_entity_docstring(self, node: Node) -> Optional[str]:
5474 if comment_text .startswith ('/**' ):
5575 return comment_text
5676 return None
57- raise ValueError (f"Unknown entity type: { node .type } " )
77+ raise ValueError (f"Unknown entity type: { node .type } " )
5878
59- def get_entity_types (self ) -> list [str ]:
60- return ['class_declaration' , 'object_declaration' , 'function_declaration' ]
61-
6279 def _get_delegation_types (self , entity : Entity ) -> list [tuple ]:
6380 """Extract type identifiers from delegation specifiers in order.
64-
81+
6582 Returns list of (node, is_constructor_invocation) tuples.
6683 constructor_invocation indicates a superclass; plain user_type indicates an interface.
6784 """
@@ -91,25 +108,25 @@ def add_symbols(self, entity: Entity) -> None:
91108 entity .add_symbol ("base_class" , node )
92109 else :
93110 entity .add_symbol ("implement_interface" , node )
94-
111+
95112 elif entity .node .type == 'object_declaration' :
96113 types = self ._get_delegation_types (entity )
97114 for node , _ in types :
98115 entity .add_symbol ("implement_interface" , node )
99-
116+
100117 elif entity .node .type == 'function_declaration' :
101118 # Find function calls
102119 captures = self ._captures ("(call_expression) @reference.call" , entity .node )
103120 if 'reference.call' in captures :
104121 for caller in captures ['reference.call' ]:
105122 entity .add_symbol ("call" , caller )
106-
123+
107124 # Find parameters with types
108125 captures = self ._captures ("(parameter (user_type (identifier) @parameter))" , entity .node )
109126 if 'parameter' in captures :
110127 for parameter in captures ['parameter' ]:
111128 entity .add_symbol ("parameters" , parameter )
112-
129+
113130 # Find return type
114131 captures = self ._captures ("(function_declaration (user_type (identifier) @return_type))" , entity .node )
115132 if 'return_type' in captures :
@@ -120,18 +137,6 @@ def is_dependency(self, file_path: str) -> bool:
120137 # Check if file is in a dependency directory (e.g., build, .gradle cache)
121138 return "build/" in file_path or ".gradle/" in file_path or "/cache/" in file_path
122139
123- def resolve_path (self , file_path : str , path : Path ) -> str :
124- # For Kotlin, just return the file path as-is for now
125- return file_path
126-
127- def resolve_type (self , files : dict [Path , File ], lsp : SyncLanguageServer , file_path : Path , path : Path , node : Node ) -> list [Entity ]:
128- res = []
129- for file , resolved_node in self .resolve (files , lsp , file_path , path , node ):
130- type_dec = self .find_parent (resolved_node , ['class_declaration' , 'object_declaration' ])
131- if type_dec in file .entities :
132- res .append (file .entities [type_dec ])
133- return res
134-
135140 def resolve_method (self , files : dict [Path , File ], lsp : SyncLanguageServer , file_path : Path , path : Path , node : Node ) -> list [Entity ]:
136141 res = []
137142 # For call expressions, we need to extract the function name
@@ -147,11 +152,3 @@ def resolve_method(self, files: dict[Path, File], lsp: SyncLanguageServer, file_
147152 res .append (file .entities [method_dec ])
148153 break
149154 return res
150-
151- def resolve_symbol (self , files : dict [Path , File ], lsp : SyncLanguageServer , file_path : Path , path : Path , key : str , symbol : Node ) -> list [Entity ]:
152- if key in ["implement_interface" , "base_class" , "parameters" , "return_type" ]:
153- return self .resolve_type (files , lsp , file_path , path , symbol )
154- elif key in ["call" ]:
155- return self .resolve_method (files , lsp , file_path , path , symbol )
156- else :
157- raise ValueError (f"Unknown key { key } " )
0 commit comments