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.
- codegraph/__init__.py +12 -0
- codegraph/__main__.py +6 -0
- codegraph/cli.py +911 -0
- codegraph/codegraph.py +618 -0
- codegraph/context/__init__.py +216 -0
- codegraph/db/__init__.py +11 -0
- codegraph/db/connection.py +163 -0
- codegraph/db/queries.py +752 -0
- codegraph/db/schema.sql +151 -0
- codegraph/directory.py +160 -0
- codegraph/errors.py +101 -0
- codegraph/extraction/__init__.py +956 -0
- codegraph/extraction/languages/__init__.py +64 -0
- codegraph/extraction/languages/base.py +132 -0
- codegraph/extraction/languages/c_cfg.py +53 -0
- codegraph/extraction/languages/cpp_cfg.py +60 -0
- codegraph/extraction/languages/dart_cfg.py +53 -0
- codegraph/extraction/languages/go_cfg.py +70 -0
- codegraph/extraction/languages/java_cfg.py +62 -0
- codegraph/extraction/languages/javascript_cfg.py +84 -0
- codegraph/extraction/languages/kotlin_cfg.py +52 -0
- codegraph/extraction/languages/lua_cfg.py +65 -0
- codegraph/extraction/languages/python_cfg.py +75 -0
- codegraph/extraction/languages/ruby_cfg.py +48 -0
- codegraph/extraction/languages/rust_cfg.py +68 -0
- codegraph/extraction/languages/scala_cfg.py +59 -0
- codegraph/extraction/languages/swift_cfg.py +64 -0
- codegraph/extraction/languages/typescript_cfg.py +89 -0
- codegraph/extraction/tree_sitter_extractor.py +689 -0
- codegraph/graph/__init__.py +685 -0
- codegraph/installer/__init__.py +6 -0
- codegraph/mcp/__init__.py +668 -0
- codegraph/project_config.py +191 -0
- codegraph/resolution/__init__.py +337 -0
- codegraph/search/__init__.py +653 -0
- codegraph/sync/__init__.py +204 -0
- codegraph/types.py +334 -0
- codegraph/ui/__init__.py +11 -0
- codegraph/utils.py +218 -0
- codegraph_py-1.0.0.dist-info/METADATA +238 -0
- codegraph_py-1.0.0.dist-info/RECORD +44 -0
- codegraph_py-1.0.0.dist-info/WHEEL +5 -0
- codegraph_py-1.0.0.dist-info/entry_points.txt +2 -0
- 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
|