tree-sitter-analyzer 0.1.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.
Potentially problematic release.
This version of tree-sitter-analyzer might be problematic. Click here for more details.
- tree_sitter_analyzer/__init__.py +121 -0
- tree_sitter_analyzer/__main__.py +12 -0
- tree_sitter_analyzer/api.py +539 -0
- tree_sitter_analyzer/cli/__init__.py +39 -0
- tree_sitter_analyzer/cli/__main__.py +13 -0
- tree_sitter_analyzer/cli/commands/__init__.py +27 -0
- tree_sitter_analyzer/cli/commands/advanced_command.py +88 -0
- tree_sitter_analyzer/cli/commands/base_command.py +155 -0
- tree_sitter_analyzer/cli/commands/default_command.py +19 -0
- tree_sitter_analyzer/cli/commands/partial_read_command.py +133 -0
- tree_sitter_analyzer/cli/commands/query_command.py +82 -0
- tree_sitter_analyzer/cli/commands/structure_command.py +121 -0
- tree_sitter_analyzer/cli/commands/summary_command.py +93 -0
- tree_sitter_analyzer/cli/commands/table_command.py +233 -0
- tree_sitter_analyzer/cli/info_commands.py +121 -0
- tree_sitter_analyzer/cli_main.py +276 -0
- tree_sitter_analyzer/core/__init__.py +20 -0
- tree_sitter_analyzer/core/analysis_engine.py +574 -0
- tree_sitter_analyzer/core/cache_service.py +330 -0
- tree_sitter_analyzer/core/engine.py +560 -0
- tree_sitter_analyzer/core/parser.py +288 -0
- tree_sitter_analyzer/core/query.py +502 -0
- tree_sitter_analyzer/encoding_utils.py +460 -0
- tree_sitter_analyzer/exceptions.py +340 -0
- tree_sitter_analyzer/file_handler.py +222 -0
- tree_sitter_analyzer/formatters/__init__.py +1 -0
- tree_sitter_analyzer/formatters/base_formatter.py +168 -0
- tree_sitter_analyzer/formatters/formatter_factory.py +74 -0
- tree_sitter_analyzer/formatters/java_formatter.py +270 -0
- tree_sitter_analyzer/formatters/python_formatter.py +235 -0
- tree_sitter_analyzer/interfaces/__init__.py +10 -0
- tree_sitter_analyzer/interfaces/cli.py +557 -0
- tree_sitter_analyzer/interfaces/cli_adapter.py +319 -0
- tree_sitter_analyzer/interfaces/mcp_adapter.py +170 -0
- tree_sitter_analyzer/interfaces/mcp_server.py +416 -0
- tree_sitter_analyzer/java_analyzer.py +219 -0
- tree_sitter_analyzer/language_detector.py +400 -0
- tree_sitter_analyzer/language_loader.py +228 -0
- tree_sitter_analyzer/languages/__init__.py +11 -0
- tree_sitter_analyzer/languages/java_plugin.py +1113 -0
- tree_sitter_analyzer/languages/python_plugin.py +712 -0
- tree_sitter_analyzer/mcp/__init__.py +32 -0
- tree_sitter_analyzer/mcp/resources/__init__.py +47 -0
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +213 -0
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +550 -0
- tree_sitter_analyzer/mcp/server.py +319 -0
- tree_sitter_analyzer/mcp/tools/__init__.py +36 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +558 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +245 -0
- tree_sitter_analyzer/mcp/tools/base_tool.py +55 -0
- tree_sitter_analyzer/mcp/tools/get_positions_tool.py +448 -0
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +302 -0
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +359 -0
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +476 -0
- tree_sitter_analyzer/mcp/utils/__init__.py +106 -0
- tree_sitter_analyzer/mcp/utils/error_handler.py +549 -0
- tree_sitter_analyzer/models.py +481 -0
- tree_sitter_analyzer/output_manager.py +264 -0
- tree_sitter_analyzer/plugins/__init__.py +334 -0
- tree_sitter_analyzer/plugins/base.py +446 -0
- tree_sitter_analyzer/plugins/java_plugin.py +625 -0
- tree_sitter_analyzer/plugins/javascript_plugin.py +439 -0
- tree_sitter_analyzer/plugins/manager.py +355 -0
- tree_sitter_analyzer/plugins/plugin_loader.py +83 -0
- tree_sitter_analyzer/plugins/python_plugin.py +598 -0
- tree_sitter_analyzer/plugins/registry.py +366 -0
- tree_sitter_analyzer/queries/__init__.py +27 -0
- tree_sitter_analyzer/queries/java.py +394 -0
- tree_sitter_analyzer/queries/javascript.py +149 -0
- tree_sitter_analyzer/queries/python.py +286 -0
- tree_sitter_analyzer/queries/typescript.py +230 -0
- tree_sitter_analyzer/query_loader.py +260 -0
- tree_sitter_analyzer/table_formatter.py +448 -0
- tree_sitter_analyzer/utils.py +201 -0
- tree_sitter_analyzer-0.1.0.dist-info/METADATA +581 -0
- tree_sitter_analyzer-0.1.0.dist-info/RECORD +78 -0
- tree_sitter_analyzer-0.1.0.dist-info/WHEEL +4 -0
- tree_sitter_analyzer-0.1.0.dist-info/entry_points.txt +8 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Output Manager for CLI
|
|
5
|
+
|
|
6
|
+
Handles different types of outputs: user information, errors, and structured data.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import sys
|
|
11
|
+
from typing import Any, Dict, List, Optional, Union
|
|
12
|
+
|
|
13
|
+
from .utils import log_error, log_warning
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class OutputManager:
|
|
17
|
+
"""Manages different types of output for CLI"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, quiet: bool = False, json_output: bool = False):
|
|
20
|
+
self.quiet = quiet
|
|
21
|
+
self.json_output = json_output
|
|
22
|
+
|
|
23
|
+
def info(self, message: str) -> None:
|
|
24
|
+
"""Output informational message to user"""
|
|
25
|
+
if not self.quiet:
|
|
26
|
+
print(message)
|
|
27
|
+
|
|
28
|
+
def warning(self, message: str) -> None:
|
|
29
|
+
"""Output warning message"""
|
|
30
|
+
if not self.quiet:
|
|
31
|
+
print(f"WARNING: {message}", file=sys.stderr)
|
|
32
|
+
log_warning(message)
|
|
33
|
+
|
|
34
|
+
def error(self, message: str) -> None:
|
|
35
|
+
"""Output error message"""
|
|
36
|
+
print(f"ERROR: {message}", file=sys.stderr)
|
|
37
|
+
log_error(message)
|
|
38
|
+
|
|
39
|
+
def success(self, message: str) -> None:
|
|
40
|
+
"""Output success message"""
|
|
41
|
+
if not self.quiet:
|
|
42
|
+
print(f"✓ {message}")
|
|
43
|
+
|
|
44
|
+
def output_info(self, message: str) -> None:
|
|
45
|
+
"""Output info message (alias for info)"""
|
|
46
|
+
self.info(message)
|
|
47
|
+
|
|
48
|
+
def output_warning(self, message: str) -> None:
|
|
49
|
+
"""Output warning message (alias for warning)"""
|
|
50
|
+
self.warning(message)
|
|
51
|
+
|
|
52
|
+
def output_error(self, message: str) -> None:
|
|
53
|
+
"""Output error message (alias for error)"""
|
|
54
|
+
self.error(message)
|
|
55
|
+
|
|
56
|
+
def output_success(self, message: str) -> None:
|
|
57
|
+
"""Output success message (alias for success)"""
|
|
58
|
+
self.success(message)
|
|
59
|
+
|
|
60
|
+
def data(self, data: Any, format_type: str = "json") -> None:
|
|
61
|
+
"""Output structured data"""
|
|
62
|
+
if self.json_output or format_type == "json":
|
|
63
|
+
print(json.dumps(data, indent=2, ensure_ascii=False))
|
|
64
|
+
else:
|
|
65
|
+
self._format_data(data)
|
|
66
|
+
|
|
67
|
+
def _format_data(self, data: Any) -> None:
|
|
68
|
+
"""Format data for human-readable output"""
|
|
69
|
+
if isinstance(data, dict):
|
|
70
|
+
for key, value in data.items():
|
|
71
|
+
print(f"{key}: {value}")
|
|
72
|
+
elif isinstance(data, list):
|
|
73
|
+
for i, item in enumerate(data, 1):
|
|
74
|
+
print(f"{i}. {item}")
|
|
75
|
+
else:
|
|
76
|
+
print(str(data))
|
|
77
|
+
|
|
78
|
+
def results_header(self, title: str) -> None:
|
|
79
|
+
"""Output results section header"""
|
|
80
|
+
if not self.quiet:
|
|
81
|
+
print(f"\n--- {title} ---")
|
|
82
|
+
|
|
83
|
+
def query_result(self, index: int, result: Dict[str, Any]) -> None:
|
|
84
|
+
"""Output query result in formatted way"""
|
|
85
|
+
if not self.quiet:
|
|
86
|
+
print(
|
|
87
|
+
f"\n{index}. {result.get('capture_name', 'Unknown')} ({result.get('node_type', 'Unknown')})"
|
|
88
|
+
)
|
|
89
|
+
print(
|
|
90
|
+
f" 位置: 行 {result.get('start_line', '?')}-{result.get('end_line', '?')}"
|
|
91
|
+
)
|
|
92
|
+
if "content" in result:
|
|
93
|
+
print(f" 内容:\n{result['content']}")
|
|
94
|
+
|
|
95
|
+
def analysis_summary(self, stats: Dict[str, Any]) -> None:
|
|
96
|
+
"""Output analysis summary"""
|
|
97
|
+
if self.json_output:
|
|
98
|
+
self.data(stats)
|
|
99
|
+
else:
|
|
100
|
+
self.results_header("統計情報")
|
|
101
|
+
for key, value in stats.items():
|
|
102
|
+
print(f"{key}: {value}")
|
|
103
|
+
|
|
104
|
+
def language_list(
|
|
105
|
+
self, languages: List[str], title: str = "サポートされている言語"
|
|
106
|
+
) -> None:
|
|
107
|
+
"""Output language list"""
|
|
108
|
+
if not self.quiet:
|
|
109
|
+
print(f"{title}:")
|
|
110
|
+
for lang in languages:
|
|
111
|
+
print(f" {lang}")
|
|
112
|
+
|
|
113
|
+
def query_list(self, queries: Dict[str, str], language: str) -> None:
|
|
114
|
+
"""Output query list for a language"""
|
|
115
|
+
if not self.quiet:
|
|
116
|
+
print(f"利用可能なクエリキー ({language}):")
|
|
117
|
+
for query_key, description in queries.items():
|
|
118
|
+
print(f" {query_key:<20} - {description}")
|
|
119
|
+
|
|
120
|
+
def extension_list(self, extensions: List[str]) -> None:
|
|
121
|
+
"""Output supported extensions"""
|
|
122
|
+
if not self.quiet:
|
|
123
|
+
print("サポートされている拡張子:")
|
|
124
|
+
for i in range(0, len(extensions), 10):
|
|
125
|
+
chunk = extensions[i : i + 10]
|
|
126
|
+
print(f" {' '.join(chunk)}")
|
|
127
|
+
print(f"合計 {len(extensions)} 個の拡張子をサポート")
|
|
128
|
+
|
|
129
|
+
def output_json(self, data: Any) -> None:
|
|
130
|
+
"""Output JSON data"""
|
|
131
|
+
print(json.dumps(data, indent=2, ensure_ascii=False))
|
|
132
|
+
|
|
133
|
+
def output_list(
|
|
134
|
+
self, items: Union[str, List[Any]], title: Optional[str] = None
|
|
135
|
+
) -> None:
|
|
136
|
+
"""Output a list of items"""
|
|
137
|
+
if title and not self.quiet:
|
|
138
|
+
print(f"{title}:")
|
|
139
|
+
# 文字列が単一要素として渡された場合の処理
|
|
140
|
+
if isinstance(items, str):
|
|
141
|
+
items = [items]
|
|
142
|
+
for item in items:
|
|
143
|
+
if not self.quiet:
|
|
144
|
+
print(f" {item}")
|
|
145
|
+
|
|
146
|
+
def output_section(self, title: str) -> None:
|
|
147
|
+
"""Output a section header"""
|
|
148
|
+
if not self.quiet:
|
|
149
|
+
print(f"\n--- {title} ---")
|
|
150
|
+
|
|
151
|
+
def output_query_results(self, results: Any) -> None:
|
|
152
|
+
"""Output query results"""
|
|
153
|
+
self.data(results)
|
|
154
|
+
|
|
155
|
+
def output_statistics(self, stats: Dict[str, Any]) -> None:
|
|
156
|
+
"""Output statistics"""
|
|
157
|
+
self.analysis_summary(stats)
|
|
158
|
+
|
|
159
|
+
def output_languages(self, languages: List[str]) -> None:
|
|
160
|
+
"""Output available languages"""
|
|
161
|
+
self.language_list(languages)
|
|
162
|
+
|
|
163
|
+
def output_queries(self, queries: List[str]) -> None:
|
|
164
|
+
"""Output available queries"""
|
|
165
|
+
if isinstance(queries, list):
|
|
166
|
+
query_dict = {q: f"Query {q}" for q in queries}
|
|
167
|
+
self.query_list(query_dict, "All")
|
|
168
|
+
else:
|
|
169
|
+
self.query_list(queries, "All")
|
|
170
|
+
|
|
171
|
+
def output_extensions(self, extensions: List[str]) -> None:
|
|
172
|
+
"""Output file extensions"""
|
|
173
|
+
self.extension_list(extensions)
|
|
174
|
+
|
|
175
|
+
def output_data(self, data: Any, format_type: str = "json") -> None:
|
|
176
|
+
"""Output data (alias for data)"""
|
|
177
|
+
self.data(data, format_type)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
# Default instance for backward compatibility
|
|
181
|
+
_output_manager = OutputManager()
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def set_output_mode(quiet: bool = False, json_output: bool = False) -> None:
|
|
185
|
+
"""Set global output mode"""
|
|
186
|
+
global _output_manager
|
|
187
|
+
_output_manager = OutputManager(quiet=quiet, json_output=json_output)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def get_output_manager() -> OutputManager:
|
|
191
|
+
"""Get current output manager"""
|
|
192
|
+
return _output_manager
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# Convenience functions
|
|
196
|
+
def output_info(message: str) -> None:
|
|
197
|
+
"""Output info message"""
|
|
198
|
+
_output_manager.info(message)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def output_warning(message: str) -> None:
|
|
202
|
+
"""Output warning message"""
|
|
203
|
+
_output_manager.warning(message)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def output_error(message: str) -> None:
|
|
207
|
+
"""Output error message using the global output manager"""
|
|
208
|
+
_output_manager.error(message)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def output_success(message: str) -> None:
|
|
212
|
+
"""Output success message using the global output manager"""
|
|
213
|
+
_output_manager.success(message)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def output_json(data: Any) -> None:
|
|
217
|
+
"""Output JSON data using the global output manager"""
|
|
218
|
+
_output_manager.output_json(data)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def output_list(items: Union[str, List[Any]], title: Optional[str] = None) -> None:
|
|
222
|
+
"""Output a list of items"""
|
|
223
|
+
_output_manager.output_list(items, title)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def output_section(title: str) -> None:
|
|
227
|
+
"""Output a section header"""
|
|
228
|
+
_output_manager.output_section(title)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def output_query_results(results: Any) -> None:
|
|
232
|
+
"""Output query results"""
|
|
233
|
+
_output_manager.output_query_results(results)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def output_statistics(stats: Dict[str, Any]) -> None:
|
|
237
|
+
"""Output statistics"""
|
|
238
|
+
_output_manager.output_statistics(stats)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def output_languages(
|
|
242
|
+
languages: List[str], title: str = "サポートされている言語"
|
|
243
|
+
) -> None:
|
|
244
|
+
"""Output available languages"""
|
|
245
|
+
_output_manager.language_list(languages, title)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def output_queries(queries: List[str], language: str = "All") -> None:
|
|
249
|
+
"""Output available queries"""
|
|
250
|
+
if isinstance(queries, list):
|
|
251
|
+
query_dict = {q: f"Query {q}" for q in queries}
|
|
252
|
+
_output_manager.query_list(query_dict, language)
|
|
253
|
+
else:
|
|
254
|
+
_output_manager.query_list(queries, language)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def output_extensions(extensions: List[str]) -> None:
|
|
258
|
+
"""Output file extensions"""
|
|
259
|
+
_output_manager.output_extensions(extensions)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def output_data(data: Any, format_type: str = "json") -> None:
|
|
263
|
+
"""Output structured data"""
|
|
264
|
+
_output_manager.data(data, format_type)
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Plugin System for Multi-Language Code Analysis
|
|
5
|
+
|
|
6
|
+
This package provides a plugin-based architecture for extending
|
|
7
|
+
the tree-sitter analyzer with language-specific parsers and extractors.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from abc import ABC, abstractmethod
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
import tree_sitter
|
|
15
|
+
|
|
16
|
+
from ..models import Class as ModelClass
|
|
17
|
+
from ..models import (
|
|
18
|
+
CodeElement,
|
|
19
|
+
)
|
|
20
|
+
from ..models import Function as ModelFunction
|
|
21
|
+
from ..models import Import as ModelImport
|
|
22
|
+
from ..models import Variable as ModelVariable
|
|
23
|
+
from ..utils import log_debug, log_error, log_warning
|
|
24
|
+
|
|
25
|
+
__all__ = ["LanguagePlugin", "ElementExtractor", "PluginRegistry", "plugin_registry"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ElementExtractor(ABC):
|
|
29
|
+
"""Abstract base class for language-specific element extractors"""
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def extract_functions(
|
|
33
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
34
|
+
) -> List[ModelFunction]:
|
|
35
|
+
"""Extract function definitions from the syntax tree"""
|
|
36
|
+
log_warning("extract_functions not implemented in subclass")
|
|
37
|
+
return []
|
|
38
|
+
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def extract_classes(
|
|
41
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
42
|
+
) -> List[ModelClass]:
|
|
43
|
+
"""Extract class definitions from the syntax tree"""
|
|
44
|
+
log_warning("extract_classes not implemented in subclass")
|
|
45
|
+
return []
|
|
46
|
+
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def extract_variables(
|
|
49
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
50
|
+
) -> List[ModelVariable]:
|
|
51
|
+
"""Extract variable declarations from the syntax tree"""
|
|
52
|
+
log_warning("extract_variables not implemented in subclass")
|
|
53
|
+
return []
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
def extract_imports(
|
|
57
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
58
|
+
) -> List[ModelImport]:
|
|
59
|
+
"""Extract import statements from the syntax tree"""
|
|
60
|
+
log_warning("extract_imports not implemented in subclass")
|
|
61
|
+
return []
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class LanguagePlugin(ABC):
|
|
65
|
+
"""Abstract base class for language-specific plugins"""
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def get_language_name(self) -> str:
|
|
69
|
+
"""Return the name of the programming language this plugin supports"""
|
|
70
|
+
return "unknown"
|
|
71
|
+
|
|
72
|
+
@abstractmethod
|
|
73
|
+
def get_file_extensions(self) -> List[str]:
|
|
74
|
+
"""Return list of file extensions this plugin supports"""
|
|
75
|
+
return []
|
|
76
|
+
|
|
77
|
+
@abstractmethod
|
|
78
|
+
def create_extractor(self) -> ElementExtractor:
|
|
79
|
+
"""Create and return an element extractor for this language"""
|
|
80
|
+
return DefaultExtractor()
|
|
81
|
+
|
|
82
|
+
def is_applicable(self, file_path: str) -> bool:
|
|
83
|
+
"""Check if this plugin is applicable for the given file"""
|
|
84
|
+
extensions = self.get_file_extensions()
|
|
85
|
+
return any(file_path.lower().endswith(ext.lower()) for ext in extensions)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class DefaultExtractor(ElementExtractor):
|
|
89
|
+
"""Default implementation of ElementExtractor with basic functionality"""
|
|
90
|
+
|
|
91
|
+
def extract_functions(
|
|
92
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
93
|
+
) -> List[ModelFunction]:
|
|
94
|
+
"""Basic function extraction implementation"""
|
|
95
|
+
functions: List[ModelFunction] = []
|
|
96
|
+
try:
|
|
97
|
+
if hasattr(tree, "root_node"):
|
|
98
|
+
# Generic function extraction logic
|
|
99
|
+
self._traverse_for_functions(
|
|
100
|
+
tree.root_node, functions, source_code.splitlines()
|
|
101
|
+
)
|
|
102
|
+
except Exception as e:
|
|
103
|
+
log_error(f"Error in function extraction: {e}")
|
|
104
|
+
return functions
|
|
105
|
+
|
|
106
|
+
def extract_classes(
|
|
107
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
108
|
+
) -> List[ModelClass]:
|
|
109
|
+
"""Basic class extraction implementation"""
|
|
110
|
+
classes: List[ModelClass] = []
|
|
111
|
+
try:
|
|
112
|
+
if hasattr(tree, "root_node"):
|
|
113
|
+
# Generic class extraction logic
|
|
114
|
+
self._traverse_for_classes(
|
|
115
|
+
tree.root_node, classes, source_code.splitlines()
|
|
116
|
+
)
|
|
117
|
+
except Exception as e:
|
|
118
|
+
log_error(f"Error in class extraction: {e}")
|
|
119
|
+
return classes
|
|
120
|
+
|
|
121
|
+
def extract_variables(
|
|
122
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
123
|
+
) -> List[ModelVariable]:
|
|
124
|
+
"""Basic variable extraction implementation"""
|
|
125
|
+
variables: List[ModelVariable] = []
|
|
126
|
+
try:
|
|
127
|
+
if hasattr(tree, "root_node"):
|
|
128
|
+
# Generic variable extraction logic
|
|
129
|
+
self._traverse_for_variables(
|
|
130
|
+
tree.root_node, variables, source_code.splitlines()
|
|
131
|
+
)
|
|
132
|
+
except Exception as e:
|
|
133
|
+
log_error(f"Error in variable extraction: {e}")
|
|
134
|
+
return variables
|
|
135
|
+
|
|
136
|
+
def extract_imports(
|
|
137
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
138
|
+
) -> List[ModelImport]:
|
|
139
|
+
"""Basic import extraction implementation"""
|
|
140
|
+
imports: List[ModelImport] = []
|
|
141
|
+
try:
|
|
142
|
+
if hasattr(tree, "root_node"):
|
|
143
|
+
# Generic import extraction logic
|
|
144
|
+
self._traverse_for_imports(
|
|
145
|
+
tree.root_node, imports, source_code.splitlines()
|
|
146
|
+
)
|
|
147
|
+
except Exception as e:
|
|
148
|
+
log_error(f"Error in import extraction: {e}")
|
|
149
|
+
return imports
|
|
150
|
+
|
|
151
|
+
def _traverse_for_functions(
|
|
152
|
+
self, node: "tree_sitter.Node", functions: List[ModelFunction], lines: List[str]
|
|
153
|
+
) -> None:
|
|
154
|
+
"""Traverse tree to find function-like nodes"""
|
|
155
|
+
if hasattr(node, "type") and "function" in node.type.lower():
|
|
156
|
+
try:
|
|
157
|
+
func = ModelFunction(
|
|
158
|
+
name=self._extract_node_name(node) or "unknown",
|
|
159
|
+
start_line=(
|
|
160
|
+
node.start_point[0] + 1 if hasattr(node, "start_point") else 0
|
|
161
|
+
),
|
|
162
|
+
end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
|
|
163
|
+
raw_text="",
|
|
164
|
+
language="unknown",
|
|
165
|
+
)
|
|
166
|
+
functions.append(func)
|
|
167
|
+
except Exception as e:
|
|
168
|
+
log_debug(f"Failed to extract function: {e}")
|
|
169
|
+
|
|
170
|
+
if hasattr(node, "children"):
|
|
171
|
+
for child in node.children:
|
|
172
|
+
self._traverse_for_functions(child, functions, lines)
|
|
173
|
+
|
|
174
|
+
def _traverse_for_classes(
|
|
175
|
+
self, node: "tree_sitter.Node", classes: List[ModelClass], lines: List[str]
|
|
176
|
+
) -> None:
|
|
177
|
+
"""Traverse tree to find class-like nodes"""
|
|
178
|
+
if hasattr(node, "type") and "class" in node.type.lower():
|
|
179
|
+
try:
|
|
180
|
+
cls = ModelClass(
|
|
181
|
+
name=self._extract_node_name(node) or "unknown",
|
|
182
|
+
start_line=(
|
|
183
|
+
node.start_point[0] + 1 if hasattr(node, "start_point") else 0
|
|
184
|
+
),
|
|
185
|
+
end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
|
|
186
|
+
raw_text="",
|
|
187
|
+
language="unknown",
|
|
188
|
+
)
|
|
189
|
+
classes.append(cls)
|
|
190
|
+
except Exception as e:
|
|
191
|
+
log_debug(f"Failed to extract class: {e}")
|
|
192
|
+
|
|
193
|
+
if hasattr(node, "children"):
|
|
194
|
+
for child in node.children:
|
|
195
|
+
self._traverse_for_classes(child, classes, lines)
|
|
196
|
+
|
|
197
|
+
def _traverse_for_variables(
|
|
198
|
+
self, node: "tree_sitter.Node", variables: List[ModelVariable], lines: List[str]
|
|
199
|
+
) -> None:
|
|
200
|
+
"""Traverse tree to find variable declarations"""
|
|
201
|
+
if hasattr(node, "type") and (
|
|
202
|
+
"variable" in node.type.lower() or "declaration" in node.type.lower()
|
|
203
|
+
):
|
|
204
|
+
try:
|
|
205
|
+
var = ModelVariable(
|
|
206
|
+
name=self._extract_node_name(node) or "unknown",
|
|
207
|
+
start_line=(
|
|
208
|
+
node.start_point[0] + 1 if hasattr(node, "start_point") else 0
|
|
209
|
+
),
|
|
210
|
+
end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
|
|
211
|
+
raw_text="",
|
|
212
|
+
language="unknown",
|
|
213
|
+
)
|
|
214
|
+
variables.append(var)
|
|
215
|
+
except Exception as e:
|
|
216
|
+
log_debug(f"Failed to extract variable: {e}")
|
|
217
|
+
|
|
218
|
+
if hasattr(node, "children"):
|
|
219
|
+
for child in node.children:
|
|
220
|
+
self._traverse_for_variables(child, variables, lines)
|
|
221
|
+
|
|
222
|
+
def _traverse_for_imports(
|
|
223
|
+
self, node: "tree_sitter.Node", imports: List[ModelImport], lines: List[str]
|
|
224
|
+
) -> None:
|
|
225
|
+
"""Traverse tree to find import statements"""
|
|
226
|
+
if hasattr(node, "type") and "import" in node.type.lower():
|
|
227
|
+
try:
|
|
228
|
+
imp = ModelImport(
|
|
229
|
+
name=self._extract_node_name(node) or "unknown",
|
|
230
|
+
start_line=(
|
|
231
|
+
node.start_point[0] + 1 if hasattr(node, "start_point") else 0
|
|
232
|
+
),
|
|
233
|
+
end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
|
|
234
|
+
raw_text="",
|
|
235
|
+
language="unknown",
|
|
236
|
+
)
|
|
237
|
+
imports.append(imp)
|
|
238
|
+
except Exception as e:
|
|
239
|
+
log_debug(f"Failed to extract import: {e}")
|
|
240
|
+
|
|
241
|
+
if hasattr(node, "children"):
|
|
242
|
+
for child in node.children:
|
|
243
|
+
self._traverse_for_imports(child, imports, lines)
|
|
244
|
+
|
|
245
|
+
def _extract_node_name(self, node: "tree_sitter.Node") -> Optional[str]:
|
|
246
|
+
"""Extract name from a tree-sitter node"""
|
|
247
|
+
try:
|
|
248
|
+
# Look for identifier children
|
|
249
|
+
if hasattr(node, "children"):
|
|
250
|
+
for child in node.children:
|
|
251
|
+
if hasattr(child, "type") and child.type == "identifier":
|
|
252
|
+
# This would need actual text extraction in real implementation
|
|
253
|
+
return f"element_{child.start_point[0]}_{child.start_point[1]}"
|
|
254
|
+
return None
|
|
255
|
+
except Exception:
|
|
256
|
+
return None
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class PluginRegistry:
|
|
260
|
+
"""Registry for managing language plugins"""
|
|
261
|
+
|
|
262
|
+
def __init__(self) -> None:
|
|
263
|
+
self._plugins: Dict[str, LanguagePlugin] = {}
|
|
264
|
+
self._extension_map: Dict[str, LanguagePlugin] = {}
|
|
265
|
+
self._default_plugin = DefaultLanguagePlugin()
|
|
266
|
+
|
|
267
|
+
def register_plugin(self, plugin: LanguagePlugin) -> None:
|
|
268
|
+
"""Register a language plugin"""
|
|
269
|
+
try:
|
|
270
|
+
language = plugin.get_language_name()
|
|
271
|
+
self._plugins[language] = plugin
|
|
272
|
+
|
|
273
|
+
# Register file extensions
|
|
274
|
+
for ext in plugin.get_file_extensions():
|
|
275
|
+
self._extension_map[ext] = plugin
|
|
276
|
+
|
|
277
|
+
log_debug(f"Registered plugin for language: {language}")
|
|
278
|
+
except Exception as e:
|
|
279
|
+
log_error(f"Failed to register plugin: {e}")
|
|
280
|
+
|
|
281
|
+
def get_plugin(self, language: str) -> Optional[LanguagePlugin]:
|
|
282
|
+
"""Get plugin for specified language"""
|
|
283
|
+
return self._plugins.get(language)
|
|
284
|
+
|
|
285
|
+
def get_plugin_by_extension(self, extension: str) -> Optional[LanguagePlugin]:
|
|
286
|
+
"""Get plugin for specified file extension"""
|
|
287
|
+
return self._extension_map.get(extension)
|
|
288
|
+
|
|
289
|
+
def get_plugin_for_file(self, file_path: str) -> LanguagePlugin:
|
|
290
|
+
"""Get appropriate plugin for a file"""
|
|
291
|
+
for plugin in self._plugins.values():
|
|
292
|
+
if plugin.is_applicable(file_path):
|
|
293
|
+
return plugin
|
|
294
|
+
return self._default_plugin
|
|
295
|
+
|
|
296
|
+
def list_supported_languages(self) -> List[str]:
|
|
297
|
+
"""List all supported languages"""
|
|
298
|
+
return list(self._plugins.keys())
|
|
299
|
+
|
|
300
|
+
def list_supported_extensions(self) -> List[str]:
|
|
301
|
+
"""List all supported file extensions"""
|
|
302
|
+
return list(self._extension_map.keys())
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
class DefaultLanguagePlugin(LanguagePlugin):
|
|
306
|
+
"""Default plugin that provides basic functionality for any language"""
|
|
307
|
+
|
|
308
|
+
def get_language_name(self) -> str:
|
|
309
|
+
return "generic"
|
|
310
|
+
|
|
311
|
+
def get_file_extensions(self) -> List[str]:
|
|
312
|
+
return [".txt", ".md"] # Fallback extensions
|
|
313
|
+
|
|
314
|
+
def create_extractor(self) -> ElementExtractor:
|
|
315
|
+
return DefaultExtractor()
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
# Global plugin registry instance
|
|
319
|
+
plugin_registry = PluginRegistry()
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
# Auto-load all plugins when the package is imported
|
|
323
|
+
try:
|
|
324
|
+
from .plugin_loader import (
|
|
325
|
+
get_supported_extensions,
|
|
326
|
+
get_supported_languages,
|
|
327
|
+
load_all_plugins,
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
# Plugins are automatically loaded when plugin_loader is imported
|
|
331
|
+
except ImportError as e:
|
|
332
|
+
from ..utils import log_warning
|
|
333
|
+
|
|
334
|
+
log_warning(f"Could not load plugin loader: {e}")
|