codegraph-py 1.0.0__py3-none-any.whl

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 (44) hide show
  1. codegraph/__init__.py +12 -0
  2. codegraph/__main__.py +6 -0
  3. codegraph/cli.py +911 -0
  4. codegraph/codegraph.py +618 -0
  5. codegraph/context/__init__.py +216 -0
  6. codegraph/db/__init__.py +11 -0
  7. codegraph/db/connection.py +163 -0
  8. codegraph/db/queries.py +752 -0
  9. codegraph/db/schema.sql +151 -0
  10. codegraph/directory.py +160 -0
  11. codegraph/errors.py +101 -0
  12. codegraph/extraction/__init__.py +956 -0
  13. codegraph/extraction/languages/__init__.py +64 -0
  14. codegraph/extraction/languages/base.py +132 -0
  15. codegraph/extraction/languages/c_cfg.py +53 -0
  16. codegraph/extraction/languages/cpp_cfg.py +60 -0
  17. codegraph/extraction/languages/dart_cfg.py +53 -0
  18. codegraph/extraction/languages/go_cfg.py +70 -0
  19. codegraph/extraction/languages/java_cfg.py +62 -0
  20. codegraph/extraction/languages/javascript_cfg.py +84 -0
  21. codegraph/extraction/languages/kotlin_cfg.py +52 -0
  22. codegraph/extraction/languages/lua_cfg.py +65 -0
  23. codegraph/extraction/languages/python_cfg.py +75 -0
  24. codegraph/extraction/languages/ruby_cfg.py +48 -0
  25. codegraph/extraction/languages/rust_cfg.py +68 -0
  26. codegraph/extraction/languages/scala_cfg.py +59 -0
  27. codegraph/extraction/languages/swift_cfg.py +64 -0
  28. codegraph/extraction/languages/typescript_cfg.py +89 -0
  29. codegraph/extraction/tree_sitter_extractor.py +689 -0
  30. codegraph/graph/__init__.py +685 -0
  31. codegraph/installer/__init__.py +6 -0
  32. codegraph/mcp/__init__.py +668 -0
  33. codegraph/project_config.py +191 -0
  34. codegraph/resolution/__init__.py +337 -0
  35. codegraph/search/__init__.py +653 -0
  36. codegraph/sync/__init__.py +204 -0
  37. codegraph/types.py +334 -0
  38. codegraph/ui/__init__.py +11 -0
  39. codegraph/utils.py +218 -0
  40. codegraph_py-1.0.0.dist-info/METADATA +238 -0
  41. codegraph_py-1.0.0.dist-info/RECORD +44 -0
  42. codegraph_py-1.0.0.dist-info/WHEEL +5 -0
  43. codegraph_py-1.0.0.dist-info/entry_points.txt +2 -0
  44. codegraph_py-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,64 @@
1
+ """
2
+ Language extraction configuration registry.
3
+
4
+ Maps Language enum values to their corresponding LanguageConfig instances.
5
+ """
6
+
7
+ from ...types import Language as LangEnum
8
+ from .base import LanguageConfig
9
+
10
+ # Import all language configs
11
+ from .python_cfg import PythonConfig
12
+ from .javascript_cfg import JavaScriptConfig
13
+ from .typescript_cfg import TypeScriptConfig
14
+ from .go_cfg import GoConfig
15
+ from .java_cfg import JavaConfig
16
+ from .rust_cfg import RustConfig
17
+ from .c_cfg import CConfig
18
+ from .cpp_cfg import CPPConfig
19
+ from .ruby_cfg import RubyConfig
20
+ from .swift_cfg import SwiftConfig
21
+ from .kotlin_cfg import KotlinConfig
22
+ from .dart_cfg import DartConfig
23
+ from .scala_cfg import ScalaConfig
24
+ from .lua_cfg import LuaConfig
25
+
26
+ # Registry: maps Language enum -> LanguageConfig instance
27
+ _REGISTRY: dict[LangEnum, LanguageConfig] = {}
28
+
29
+ def _register(config_cls: type[LanguageConfig], *langs: LangEnum):
30
+ """Register a config class for one or more language enums."""
31
+ instance = config_cls()
32
+ for lang in langs:
33
+ _REGISTRY[lang] = instance
34
+
35
+ # Register all supported languages
36
+ _register(PythonConfig, LangEnum.PYTHON)
37
+ _register(JavaScriptConfig, LangEnum.JAVASCRIPT, LangEnum.JSX)
38
+ _register(TypeScriptConfig, LangEnum.TYPESCRIPT, LangEnum.TSX)
39
+ _register(GoConfig, LangEnum.GO)
40
+ _register(JavaConfig, LangEnum.JAVA)
41
+ _register(RustConfig, LangEnum.RUST)
42
+ _register(CConfig, LangEnum.C)
43
+ _register(CPPConfig, LangEnum.CPP)
44
+ _register(RubyConfig, LangEnum.RUBY)
45
+ _register(SwiftConfig, LangEnum.SWIFT)
46
+ _register(KotlinConfig, LangEnum.KOTLIN)
47
+ _register(DartConfig, LangEnum.DART)
48
+ _register(ScalaConfig, LangEnum.SCALA)
49
+ _register(LuaConfig, LangEnum.LUA, LangEnum.LUAU)
50
+
51
+
52
+ def get_config(lang: LangEnum) -> LanguageConfig | None:
53
+ """Get the LanguageConfig for a given Language enum value."""
54
+ return _REGISTRY.get(lang)
55
+
56
+
57
+ def has_config(lang: LangEnum) -> bool:
58
+ """Check if a language has a tree-sitter extraction config."""
59
+ return lang in _REGISTRY
60
+
61
+
62
+ def supported_languages() -> list[LangEnum]:
63
+ """Return list of languages with tree-sitter extraction support."""
64
+ return list(_REGISTRY.keys())
@@ -0,0 +1,132 @@
1
+ """
2
+ Language extraction configuration base types.
3
+
4
+ Mirrors the TypeScript LanguageExtractor interface from the original CodeGraph,
5
+ adapted for Python tree-sitter bindings.
6
+ """
7
+
8
+ from typing import Optional, ClassVar
9
+ from tree_sitter import Node as TSNode
10
+
11
+ from ...types import NodeKind
12
+
13
+
14
+ class ImportInfo:
15
+ """Information returned by a language's extract_import hook."""
16
+ __slots__ = ('module_name', 'signature', 'handled_refs')
17
+
18
+ def __init__(self, module_name: str, signature: str, handled_refs: bool = False):
19
+ self.module_name = module_name
20
+ self.signature = signature
21
+ self.handled_refs = handled_refs
22
+
23
+
24
+ class VariableInfo:
25
+ """Information about a variable within a declaration."""
26
+ __slots__ = ('name', 'kind', 'signature')
27
+
28
+ def __init__(self, name: str, kind: str, signature: Optional[str] = None):
29
+ self.name = name
30
+ self.kind = kind
31
+ self.signature = signature
32
+
33
+
34
+ class LanguageConfig:
35
+ """
36
+ Language-specific extraction configuration.
37
+
38
+ Each supported language defines class-level attributes for node type
39
+ mappings and optional hooks. Subclasses automatically inherit and
40
+ override parent config.
41
+ """
42
+
43
+ # --- Node type mappings ---
44
+ function_types: ClassVar[list[str]] = []
45
+ class_types: ClassVar[list[str]] = []
46
+ method_types: ClassVar[list[str]] = []
47
+ interface_types: ClassVar[list[str]] = []
48
+ struct_types: ClassVar[list[str]] = []
49
+ enum_types: ClassVar[list[str]] = []
50
+ enum_member_types: ClassVar[list[str]] = []
51
+ type_alias_types: ClassVar[list[str]] = []
52
+ import_types: ClassVar[list[str]] = []
53
+ call_types: ClassVar[list[str]] = []
54
+ variable_types: ClassVar[list[str]] = []
55
+ field_types: ClassVar[list[str]] = []
56
+ property_types: ClassVar[list[str]] = []
57
+ statement_types: ClassVar[list[str]] = []
58
+
59
+ # --- Field name mappings ---
60
+ name_field: ClassVar[str] = 'name'
61
+ body_field: ClassVar[str] = 'body'
62
+ params_field: ClassVar[str] = 'parameters'
63
+ return_field: ClassVar[Optional[str]] = None
64
+
65
+ # --- Language identity ---
66
+ language_id: ClassVar[str] = ''
67
+ """The tree-sitter language identifier (e.g. 'python', 'javascript')."""
68
+
69
+ # ============================================================
70
+ # Hooks — override in subclass for language-specific behaviour
71
+ # ============================================================
72
+
73
+ def get_signature(self, node: TSNode, source: bytes) -> Optional[str]:
74
+ """Extract function/method signature."""
75
+ return None
76
+
77
+ def get_visibility(self, node: TSNode) -> Optional[str]:
78
+ """Extract visibility (public/private/protected)."""
79
+ return None
80
+
81
+ def is_exported(self, node: TSNode, source: bytes) -> bool:
82
+ """Check if node is exported."""
83
+ return False
84
+
85
+ def is_async(self, node: TSNode) -> bool:
86
+ """Check if node is async."""
87
+ return False
88
+
89
+ def is_static(self, node: TSNode) -> bool:
90
+ """Check if node is static."""
91
+ return False
92
+
93
+ def is_const(self, node: TSNode) -> bool:
94
+ """Check if variable declaration is a constant."""
95
+ return False
96
+
97
+ def extract_import(self, node: TSNode, source: bytes) -> Optional[ImportInfo]:
98
+ """Extract import information from an import node."""
99
+ return None
100
+
101
+ def extract_variables(self, node: TSNode, source: bytes) -> list:
102
+ """Extract variable declarations from a variable node."""
103
+ return []
104
+
105
+ def extract_modifiers(self, node: TSNode) -> list[str]:
106
+ """Extract extra modifier keywords."""
107
+ return []
108
+
109
+ def classify_class_node(self, node: TSNode) -> str:
110
+ """Classify a class_declaration node: 'class', 'struct', 'enum', 'interface', 'trait'."""
111
+ return 'class'
112
+
113
+ @staticmethod
114
+ def should_skip_type(node_type: str) -> bool:
115
+ """Check if a node type should be skipped during traversal."""
116
+ return node_type in (
117
+ 'comment', 'string', 'string_content', 'escape_sequence',
118
+ 'interpolation',
119
+ )
120
+
121
+ # ============================================================
122
+ # Convenience
123
+ # ============================================================
124
+
125
+ def all_symbol_types(self) -> set[str]:
126
+ """Return the set of all AST node types that represent symbols."""
127
+ return set(
128
+ self.function_types + self.class_types + self.method_types
129
+ + self.interface_types + self.struct_types + self.enum_types
130
+ + self.type_alias_types + self.import_types + self.call_types
131
+ + self.variable_types
132
+ )
@@ -0,0 +1,53 @@
1
+ """
2
+ C language extraction configuration.
3
+ """
4
+ from tree_sitter import Node as TSNode
5
+ from .base import LanguageConfig, ImportInfo
6
+
7
+
8
+ class CConfig(LanguageConfig):
9
+ """C extraction configuration."""
10
+
11
+ language_id = 'c'
12
+
13
+ function_types = ['function_definition']
14
+ class_types = []
15
+ method_types = []
16
+ interface_types = []
17
+ struct_types = ['struct_specifier']
18
+ enum_types = ['enum_specifier']
19
+ type_alias_types = ['type_definition']
20
+ import_types = ['preproc_include', 'include_directive']
21
+ call_types = ['call_expression']
22
+ variable_types = ['declaration']
23
+ field_types = ['field_declaration']
24
+ property_types = []
25
+
26
+ name_field = 'name'
27
+ body_field = 'body'
28
+ params_field = 'parameters'
29
+ return_field = 'type'
30
+
31
+ def get_signature(self, node: TSNode, source: bytes) -> str | None:
32
+ params = node.child_by_field_name('parameters')
33
+ if not params:
34
+ return None
35
+ sig = source[params.start_byte:params.end_byte].decode('utf-8', errors='replace')
36
+ return_type = node.child_by_field_name('type')
37
+ if return_type:
38
+ ret = source[return_type.start_byte:return_type.end_byte].decode('utf-8', errors='replace')
39
+ sig = f'{ret} {sig}'
40
+ return sig
41
+
42
+ def extract_import(self, node: TSNode, source: bytes) -> ImportInfo | None:
43
+ if node.type in ('preproc_include', 'include_directive'):
44
+ text = source[node.start_byte:node.end_byte].decode('utf-8', errors='replace').strip()
45
+ path_node = node.child_by_field_name('path')
46
+ if path_node:
47
+ module_name = source[path_node.start_byte:path_node.end_byte].decode('utf-8', errors='replace')
48
+ module_name = module_name.strip('<>"')
49
+ return ImportInfo(module_name=module_name, signature=text)
50
+ return None
51
+
52
+ def is_exported(self, node: TSNode, source: bytes) -> bool:
53
+ return True # All C functions at file scope are effectively exported
@@ -0,0 +1,60 @@
1
+ """
2
+ C++ language extraction configuration.
3
+ """
4
+ from tree_sitter import Node as TSNode
5
+ from .base import LanguageConfig, ImportInfo
6
+
7
+
8
+ class CPPConfig(LanguageConfig):
9
+ """C++ extraction configuration."""
10
+
11
+ language_id = 'cpp'
12
+
13
+ function_types = ['function_definition']
14
+ class_types = ['class_specifier']
15
+ method_types = ['function_definition'] # Functions inside class bodies
16
+ interface_types = []
17
+ struct_types = ['struct_specifier']
18
+ enum_types = ['enum_specifier']
19
+ type_alias_types = ['type_definition', 'alias_declaration']
20
+ import_types = ['preproc_include', 'include_directive', 'using_declaration']
21
+ call_types = ['call_expression']
22
+ variable_types = ['declaration']
23
+ field_types = ['field_declaration']
24
+ property_types = []
25
+
26
+ name_field = 'name'
27
+ body_field = 'body'
28
+ params_field = 'parameters'
29
+ return_field = 'type'
30
+
31
+ def get_signature(self, node: TSNode, source: bytes) -> str | None:
32
+ params = node.child_by_field_name('parameters')
33
+ if not params:
34
+ return None
35
+ sig = source[params.start_byte:params.end_byte].decode('utf-8', errors='replace')
36
+ return_type = node.child_by_field_name('type')
37
+ if return_type:
38
+ ret = source[return_type.start_byte:return_type.end_byte].decode('utf-8', errors='replace')
39
+ sig = f'{ret} {sig}'
40
+ return sig
41
+
42
+ def extract_import(self, node: TSNode, source: bytes) -> ImportInfo | None:
43
+ if node.type in ('preproc_include', 'include_directive'):
44
+ text = source[node.start_byte:node.end_byte].decode('utf-8', errors='replace').strip()
45
+ path_node = node.child_by_field_name('path')
46
+ if path_node:
47
+ module_name = source[path_node.start_byte:path_node.end_byte].decode('utf-8', errors='replace')
48
+ module_name = module_name.strip('<>"')
49
+ return ImportInfo(module_name=module_name, signature=text)
50
+ elif node.type == 'using_declaration':
51
+ text = source[node.start_byte:node.end_byte].decode('utf-8', errors='replace').strip()
52
+ # Extract namespace from 'using namespace X;'
53
+ args = node.child_by_field_name('argument')
54
+ if args:
55
+ module_name = source[args.start_byte:args.end_byte].decode('utf-8', errors='replace')
56
+ return ImportInfo(module_name=module_name, signature=text)
57
+ return None
58
+
59
+ def is_exported(self, node: TSNode, source: bytes) -> bool:
60
+ return True # C++ functions at file scope are exported
@@ -0,0 +1,53 @@
1
+ """
2
+ Dart language extraction configuration.
3
+ """
4
+ from tree_sitter import Node as TSNode
5
+ from .base import LanguageConfig, ImportInfo
6
+
7
+
8
+ class DartConfig(LanguageConfig):
9
+ """Dart extraction configuration."""
10
+
11
+ language_id = 'dart'
12
+
13
+ function_types = ['function_declaration']
14
+ class_types = ['class_declaration']
15
+ method_types = ['method_declaration']
16
+ interface_types = ['interface_type'] # Dart uses abstract classes + implements
17
+ struct_types = []
18
+ enum_types = ['enum_declaration']
19
+ type_alias_types = ['type_alias']
20
+ import_types = ['import_directive', 'export_directive']
21
+ call_types = ['function_expression']
22
+ variable_types = ['variable_declaration']
23
+ field_types = ['field_declaration']
24
+ property_types = []
25
+
26
+ name_field = 'name'
27
+ body_field = 'body'
28
+ params_field = 'parameters'
29
+ return_field = 'return_type'
30
+
31
+ def get_signature(self, node: TSNode, source: bytes) -> str | None:
32
+ params = node.child_by_field_name('parameters')
33
+ if not params:
34
+ return None
35
+ sig = source[params.start_byte:params.end_byte].decode('utf-8', errors='replace')
36
+ return_type = node.child_by_field_name('return_type')
37
+ if return_type:
38
+ ret = source[return_type.start_byte:return_type.end_byte].decode('utf-8', errors='replace')
39
+ sig += f' -> {ret}'
40
+ return sig
41
+
42
+ def extract_import(self, node: TSNode, source: bytes) -> ImportInfo | None:
43
+ if node.type in ('import_directive', 'export_directive'):
44
+ text = source[node.start_byte:node.end_byte].decode('utf-8', errors='replace').strip()
45
+ uri_node = node.child_by_field_name('uri')
46
+ if uri_node:
47
+ module_name = source[uri_node.start_byte:uri_node.end_byte].decode('utf-8', errors='replace')
48
+ module_name = module_name.strip('\'"')
49
+ return ImportInfo(module_name=module_name, signature=text)
50
+ return None
51
+
52
+ def is_exported(self, node: TSNode, source: bytes) -> bool:
53
+ return True # Exported via library/part directives
@@ -0,0 +1,70 @@
1
+ """
2
+ Go language extraction configuration.
3
+ """
4
+ from tree_sitter import Node as TSNode
5
+ from .base import LanguageConfig, ImportInfo
6
+
7
+
8
+ class GoConfig(LanguageConfig):
9
+ """Go extraction configuration."""
10
+
11
+ language_id = 'go'
12
+
13
+ function_types = ['function_declaration']
14
+ class_types = []
15
+ method_types = ['method_declaration']
16
+ interface_types = ['interface_type']
17
+ struct_types = ['struct_type']
18
+ enum_types = []
19
+ type_alias_types = ['type_spec'] # Go uses type_spec for type aliases and type definitions
20
+ import_types = ['import_declaration', 'import_spec']
21
+ call_types = ['call_expression']
22
+ variable_types = ['var_declaration', 'short_var_declaration']
23
+ field_types = ['field_declaration']
24
+ property_types = []
25
+
26
+ name_field = 'name'
27
+ body_field = 'body'
28
+ params_field = 'parameters'
29
+ return_field = 'result'
30
+
31
+ def get_signature(self, node: TSNode, source: bytes) -> str | None:
32
+ params = node.child_by_field_name('parameters')
33
+ if not params:
34
+ return None
35
+ sig = source[params.start_byte:params.end_byte].decode('utf-8', errors='replace')
36
+ result = node.child_by_field_name('result')
37
+ if result:
38
+ sig += ' ' + source[result.start_byte:result.end_byte].decode('utf-8', errors='replace')
39
+ return sig
40
+
41
+ def extract_import(self, node: TSNode, source: bytes) -> ImportInfo | None:
42
+ if node.type == 'import_spec':
43
+ path_node = node.child_by_field_name('path')
44
+ if path_node:
45
+ module_name = source[path_node.start_byte:path_node.end_byte].decode('utf-8', errors='replace')
46
+ module_name = module_name.strip('"')
47
+ return ImportInfo(
48
+ module_name=module_name,
49
+ signature=source[node.start_byte:node.end_byte].decode('utf-8', errors='replace').strip(),
50
+ )
51
+ elif node.type == 'import_declaration':
52
+ # Return parent container info
53
+ return ImportInfo(module_name='', signature='import (...)')
54
+ return None
55
+
56
+ def is_exported(self, node: TSNode, source: bytes) -> bool:
57
+ name = self._extract_name(node)
58
+ return name is not None and name[0].isupper() if name else False
59
+
60
+ def _extract_name(self, node: TSNode) -> str | None:
61
+ name_node = node.child_by_field_name('name')
62
+ if name_node:
63
+ try:
64
+ t = name_node.text
65
+ if isinstance(t, bytes):
66
+ return t.decode('utf-8', errors='replace')
67
+ return str(t)
68
+ except Exception:
69
+ pass
70
+ return None
@@ -0,0 +1,62 @@
1
+ """
2
+ Java language extraction configuration.
3
+ """
4
+ from tree_sitter import Node as TSNode
5
+ from .base import LanguageConfig, ImportInfo
6
+
7
+
8
+ class JavaConfig(LanguageConfig):
9
+ """Java extraction configuration."""
10
+
11
+ language_id = 'java'
12
+
13
+ function_types = []
14
+ class_types = ['class_declaration']
15
+ method_types = ['method_declaration']
16
+ interface_types = ['interface_declaration']
17
+ struct_types = []
18
+ enum_types = ['enum_declaration']
19
+ type_alias_types = []
20
+ import_types = ['import_declaration']
21
+ call_types = ['method_invocation']
22
+ variable_types = ['variable_declaration', 'local_variable_declaration']
23
+ field_types = ['field_declaration']
24
+ property_types = []
25
+
26
+ name_field = 'name'
27
+ body_field = 'body'
28
+ params_field = 'parameters'
29
+ return_field = 'type'
30
+
31
+ def get_signature(self, node: TSNode, source: bytes) -> str | None:
32
+ params = node.child_by_field_name('parameters')
33
+ if not params:
34
+ return None
35
+ sig = source[params.start_byte:params.end_byte].decode('utf-8', errors='replace')
36
+ return_type = node.child_by_field_name('type')
37
+ if return_type:
38
+ type_text = source[return_type.start_byte:return_type.end_byte].decode('utf-8', errors='replace')
39
+ sig = type_text + ' ' + sig
40
+ return sig
41
+
42
+ def extract_import(self, node: TSNode, source: bytes) -> ImportInfo | None:
43
+ if node.type == 'import_declaration':
44
+ text = source[node.start_byte:node.end_byte].decode('utf-8', errors='replace').strip()
45
+ name = text.replace('import ', '').replace(';', '').strip()
46
+ return ImportInfo(module_name=name, signature=text) if name else None
47
+ return None
48
+
49
+ def get_visibility(self, node: TSNode) -> str | None:
50
+ # Walk previous siblings looking for modifier keywords
51
+ prev = node.prev_sibling
52
+ while prev is not None:
53
+ if prev.type == 'public':
54
+ return 'public'
55
+ if prev.type == 'private':
56
+ return 'private'
57
+ if prev.type == 'protected':
58
+ return 'protected'
59
+ if prev.is_named:
60
+ break
61
+ prev = prev.prev_sibling
62
+ return None
@@ -0,0 +1,84 @@
1
+ """
2
+ JavaScript/JSX language extraction configuration.
3
+ """
4
+ from tree_sitter import Node as TSNode
5
+ from .base import LanguageConfig, ImportInfo
6
+
7
+
8
+ def _node_text(node: TSNode) -> str:
9
+ try:
10
+ t = node.text
11
+ if isinstance(t, bytes):
12
+ return t.decode('utf-8', errors='replace')
13
+ return str(t)
14
+ except Exception:
15
+ return ''
16
+
17
+
18
+ class JavaScriptConfig(LanguageConfig):
19
+ """JavaScript/JSX extraction configuration."""
20
+
21
+ language_id = 'javascript'
22
+
23
+ function_types = ['function_declaration', 'arrow_function', 'generator_function_declaration']
24
+ class_types = ['class_declaration']
25
+ method_types = ['method_definition']
26
+ interface_types = []
27
+ struct_types = []
28
+ enum_types = []
29
+ type_alias_types = []
30
+ import_types = ['import_statement', 'import_specifier']
31
+ call_types = ['call_expression']
32
+ variable_types = ['variable_declaration', 'lexical_declaration']
33
+ field_types = ['field_definition']
34
+ property_types = ['property_identifier']
35
+
36
+ name_field = 'name'
37
+ body_field = 'body'
38
+ params_field = 'parameters'
39
+ return_field = 'return_type'
40
+
41
+ def get_signature(self, node: TSNode, source: bytes) -> str | None:
42
+ params = node.child_by_field_name('parameters')
43
+ if not params:
44
+ return None
45
+ return source[params.start_byte:params.end_byte].decode('utf-8', errors='replace')
46
+
47
+ def extract_import(self, node: TSNode, source: bytes) -> ImportInfo | None:
48
+ text = source[node.start_byte:node.end_byte].decode('utf-8', errors='replace').strip()
49
+ if node.type == 'import_statement':
50
+ source_node = node.child_by_field_name('source')
51
+ if source_node:
52
+ module_name = source[source_node.start_byte:source_node.end_byte].decode('utf-8', errors='replace')
53
+ module_name = module_name.strip('\'"')
54
+ return ImportInfo(module_name=module_name, signature=text)
55
+ return None
56
+
57
+ def is_exported(self, node: TSNode, source: bytes) -> bool:
58
+ # Check if preceded by 'export' keyword
59
+ prev = node.prev_sibling
60
+ while prev is not None:
61
+ if prev.type == 'export':
62
+ return True
63
+ if prev.is_named or prev.type in (';', '\n'):
64
+ break
65
+ prev = prev.prev_sibling
66
+ return False
67
+
68
+ def is_async(self, node: TSNode) -> bool:
69
+ prev = node.prev_sibling
70
+ while prev is not None:
71
+ if prev.type == 'async':
72
+ return True
73
+ if prev.is_named:
74
+ break
75
+ prev = prev.prev_sibling
76
+ return False
77
+
78
+ def is_const(self, node: TSNode) -> bool:
79
+ # Check variable_declarator for const keyword
80
+ parent = node.parent
81
+ if parent and parent.type == 'variable_declaration':
82
+ kind = _node_text(parent.child(0)) if parent.child(0) else ''
83
+ return kind == 'const'
84
+ return False
@@ -0,0 +1,52 @@
1
+ """
2
+ Kotlin language extraction configuration.
3
+ """
4
+ from tree_sitter import Node as TSNode
5
+ from .base import LanguageConfig, ImportInfo
6
+
7
+
8
+ class KotlinConfig(LanguageConfig):
9
+ """Kotlin extraction configuration."""
10
+
11
+ language_id = 'kotlin'
12
+
13
+ function_types = ['function_declaration']
14
+ class_types = ['class_declaration']
15
+ method_types = ['function_declaration'] # Functions inside class bodies
16
+ interface_types = ['interface_declaration']
17
+ struct_types = []
18
+ enum_types = ['enum_entry']
19
+ type_alias_types = ['type_alias']
20
+ import_types = ['import_header']
21
+ call_types = ['call_expression']
22
+ variable_types = ['property_declaration', 'variable_declaration']
23
+ field_types = ['property_declaration']
24
+ property_types = []
25
+
26
+ name_field = 'name'
27
+ body_field = 'body'
28
+ params_field = 'parameters'
29
+ return_field = 'type'
30
+
31
+ def get_signature(self, node: TSNode, source: bytes) -> str | None:
32
+ params = node.child_by_field_name('parameters')
33
+ if not params:
34
+ return None
35
+ sig = source[params.start_byte:params.end_byte].decode('utf-8', errors='replace')
36
+ return_type = node.child_by_field_name('type')
37
+ if return_type:
38
+ ret = source[return_type.start_byte:return_type.end_byte].decode('utf-8', errors='replace')
39
+ sig += f': {ret}'
40
+ return sig
41
+
42
+ def extract_import(self, node: TSNode, source: bytes) -> ImportInfo | None:
43
+ if node.type == 'import_header':
44
+ text = source[node.start_byte:node.end_byte].decode('utf-8', errors='replace').strip()
45
+ path_node = node.child_by_field_name('path')
46
+ if path_node:
47
+ module_name = source[path_node.start_byte:path_node.end_byte].decode('utf-8', errors='replace')
48
+ return ImportInfo(module_name=module_name, signature=text)
49
+ return None
50
+
51
+ def is_exported(self, node: TSNode, source: bytes) -> bool:
52
+ return True # Kotlin functions are public by default