tree-sitter-analyzer 0.7.0__py3-none-any.whl → 0.8.1__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 +132 -132
- tree_sitter_analyzer/__main__.py +11 -11
- tree_sitter_analyzer/api.py +533 -533
- tree_sitter_analyzer/cli/__init__.py +39 -39
- tree_sitter_analyzer/cli/__main__.py +12 -12
- tree_sitter_analyzer/cli/commands/__init__.py +26 -26
- tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
- tree_sitter_analyzer/cli/commands/base_command.py +178 -160
- tree_sitter_analyzer/cli/commands/default_command.py +18 -18
- tree_sitter_analyzer/cli/commands/partial_read_command.py +141 -141
- tree_sitter_analyzer/cli/commands/query_command.py +88 -81
- tree_sitter_analyzer/cli/commands/structure_command.py +138 -138
- tree_sitter_analyzer/cli/commands/summary_command.py +101 -101
- tree_sitter_analyzer/cli/commands/table_command.py +235 -235
- tree_sitter_analyzer/cli/info_commands.py +121 -121
- tree_sitter_analyzer/cli_main.py +303 -297
- tree_sitter_analyzer/core/__init__.py +15 -15
- tree_sitter_analyzer/core/analysis_engine.py +580 -555
- tree_sitter_analyzer/core/cache_service.py +320 -320
- tree_sitter_analyzer/core/engine.py +566 -566
- tree_sitter_analyzer/core/parser.py +293 -293
- tree_sitter_analyzer/encoding_utils.py +459 -459
- tree_sitter_analyzer/exceptions.py +406 -337
- tree_sitter_analyzer/file_handler.py +210 -210
- tree_sitter_analyzer/formatters/__init__.py +1 -1
- tree_sitter_analyzer/formatters/base_formatter.py +167 -167
- tree_sitter_analyzer/formatters/formatter_factory.py +78 -78
- tree_sitter_analyzer/interfaces/__init__.py +9 -9
- tree_sitter_analyzer/interfaces/cli.py +528 -528
- tree_sitter_analyzer/interfaces/cli_adapter.py +343 -343
- tree_sitter_analyzer/interfaces/mcp_adapter.py +206 -206
- tree_sitter_analyzer/interfaces/mcp_server.py +425 -405
- tree_sitter_analyzer/languages/__init__.py +10 -10
- tree_sitter_analyzer/languages/javascript_plugin.py +446 -446
- tree_sitter_analyzer/languages/python_plugin.py +755 -755
- tree_sitter_analyzer/mcp/__init__.py +31 -31
- tree_sitter_analyzer/mcp/resources/__init__.py +44 -44
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +209 -209
- tree_sitter_analyzer/mcp/server.py +408 -333
- tree_sitter_analyzer/mcp/tools/__init__.py +30 -30
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +673 -654
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +247 -247
- tree_sitter_analyzer/mcp/tools/base_tool.py +54 -54
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +308 -300
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +379 -362
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +559 -543
- tree_sitter_analyzer/mcp/utils/__init__.py +107 -107
- tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
- tree_sitter_analyzer/output_manager.py +253 -253
- tree_sitter_analyzer/plugins/__init__.py +280 -280
- tree_sitter_analyzer/plugins/base.py +529 -529
- tree_sitter_analyzer/plugins/manager.py +379 -379
- tree_sitter_analyzer/project_detector.py +317 -0
- tree_sitter_analyzer/queries/__init__.py +26 -26
- tree_sitter_analyzer/queries/java.py +391 -391
- tree_sitter_analyzer/queries/javascript.py +148 -148
- tree_sitter_analyzer/queries/python.py +285 -285
- tree_sitter_analyzer/queries/typescript.py +229 -229
- tree_sitter_analyzer/query_loader.py +257 -257
- tree_sitter_analyzer/security/__init__.py +22 -0
- tree_sitter_analyzer/security/boundary_manager.py +237 -0
- tree_sitter_analyzer/security/regex_checker.py +292 -0
- tree_sitter_analyzer/security/validator.py +241 -0
- tree_sitter_analyzer/table_formatter.py +652 -589
- tree_sitter_analyzer/utils.py +277 -277
- {tree_sitter_analyzer-0.7.0.dist-info → tree_sitter_analyzer-0.8.1.dist-info}/METADATA +27 -1
- tree_sitter_analyzer-0.8.1.dist-info/RECORD +77 -0
- tree_sitter_analyzer-0.7.0.dist-info/RECORD +0 -72
- {tree_sitter_analyzer-0.7.0.dist-info → tree_sitter_analyzer-0.8.1.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-0.7.0.dist-info → tree_sitter_analyzer-0.8.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,343 +1,343 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
CLI Adapter for tree-sitter-analyzer
|
|
4
|
-
|
|
5
|
-
Adapter that uses the new unified analysis engine while maintaining compatibility with existing CLI API.
|
|
6
|
-
|
|
7
|
-
Roo Code compliance:
|
|
8
|
-
- Type hints: Required for all functions
|
|
9
|
-
- MCP logging: Log output at each step
|
|
10
|
-
- docstring: Google Style docstring
|
|
11
|
-
- Error handling: Proper exception handling
|
|
12
|
-
- Performance: Optimization through unified engine
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
import asyncio
|
|
16
|
-
import logging
|
|
17
|
-
import time
|
|
18
|
-
from pathlib import Path
|
|
19
|
-
from typing import Any
|
|
20
|
-
|
|
21
|
-
from ..core.analysis_engine import AnalysisRequest, UnifiedAnalysisEngine
|
|
22
|
-
from ..models import AnalysisResult
|
|
23
|
-
|
|
24
|
-
logger = logging.getLogger(__name__)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class CLIAdapter:
|
|
28
|
-
"""
|
|
29
|
-
CLI Adapter
|
|
30
|
-
|
|
31
|
-
Uses the new unified analysis engine while maintaining compatibility with existing CLI API.
|
|
32
|
-
Provides synchronous API and internally calls asynchronous engine.
|
|
33
|
-
|
|
34
|
-
Features:
|
|
35
|
-
- Maintaining existing API compatibility
|
|
36
|
-
- Utilizing unified analysis engine
|
|
37
|
-
- Sync/async conversion
|
|
38
|
-
- Performance monitoring
|
|
39
|
-
- Error handling
|
|
40
|
-
|
|
41
|
-
Example:
|
|
42
|
-
>>> adapter = CLIAdapter()
|
|
43
|
-
>>> result = adapter.analyze_file("example.java")
|
|
44
|
-
>>> print(result.classes)
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
def __init__(self) -> None:
|
|
48
|
-
"""
|
|
49
|
-
Initialize CLI adapter
|
|
50
|
-
|
|
51
|
-
Raises:
|
|
52
|
-
Exception: If unified analysis engine initialization fails
|
|
53
|
-
"""
|
|
54
|
-
try:
|
|
55
|
-
self._engine = UnifiedAnalysisEngine()
|
|
56
|
-
logger.info("CLIAdapter initialized successfully")
|
|
57
|
-
except Exception as e:
|
|
58
|
-
logger.error(f"Failed to initialize CLIAdapter: {e}")
|
|
59
|
-
raise
|
|
60
|
-
|
|
61
|
-
def analyze_file(self, file_path: str, **kwargs: Any) -> AnalysisResult:
|
|
62
|
-
"""
|
|
63
|
-
Analyze file (synchronous version)
|
|
64
|
-
|
|
65
|
-
Provides synchronous interface to maintain compatibility with existing CLI API.
|
|
66
|
-
Internally calls asynchronous methods of unified analysis engine.
|
|
67
|
-
|
|
68
|
-
Args:
|
|
69
|
-
file_path: Path to file to analyze
|
|
70
|
-
**kwargs: Analysis options
|
|
71
|
-
- language: Language specification (auto-detection possible)
|
|
72
|
-
- include_complexity: Include complexity calculation
|
|
73
|
-
- include_details: Include detailed information
|
|
74
|
-
- format_type: Output format ("standard", "structure", "summary")
|
|
75
|
-
|
|
76
|
-
Returns:
|
|
77
|
-
AnalysisResult: Analysis result
|
|
78
|
-
|
|
79
|
-
Raises:
|
|
80
|
-
ValueError: For invalid file path
|
|
81
|
-
FileNotFoundError: If file does not exist
|
|
82
|
-
UnsupportedLanguageError: For unsupported language
|
|
83
|
-
|
|
84
|
-
Example:
|
|
85
|
-
>>> adapter = CLIAdapter()
|
|
86
|
-
>>> result = adapter.analyze_file("example.java", include_complexity=True)
|
|
87
|
-
>>> print(f"Classes: {len(result.classes)}")
|
|
88
|
-
"""
|
|
89
|
-
start_time = time.time()
|
|
90
|
-
|
|
91
|
-
# Input validation
|
|
92
|
-
if not file_path or not file_path.strip():
|
|
93
|
-
raise ValueError("File path cannot be empty")
|
|
94
|
-
|
|
95
|
-
# File existence check
|
|
96
|
-
if not Path(file_path).exists():
|
|
97
|
-
raise FileNotFoundError(f"File not found: {file_path}")
|
|
98
|
-
|
|
99
|
-
try:
|
|
100
|
-
# Create AnalysisRequest
|
|
101
|
-
request = AnalysisRequest(
|
|
102
|
-
file_path=file_path,
|
|
103
|
-
language=kwargs.get("language"),
|
|
104
|
-
include_complexity=kwargs.get("include_complexity", False),
|
|
105
|
-
include_details=kwargs.get("include_details", True),
|
|
106
|
-
format_type=kwargs.get("format_type", "standard"),
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
# 非同期エンジンを同期的に実行
|
|
110
|
-
result = asyncio.run(self._engine.analyze(request))
|
|
111
|
-
|
|
112
|
-
# パフォーマンスログ
|
|
113
|
-
elapsed_time = time.time() - start_time
|
|
114
|
-
logger.info(f"CLI analysis completed: {file_path} in {elapsed_time:.3f}s")
|
|
115
|
-
|
|
116
|
-
return result
|
|
117
|
-
|
|
118
|
-
except Exception as e:
|
|
119
|
-
logger.error(f"CLI analysis failed for {file_path}: {e}")
|
|
120
|
-
raise
|
|
121
|
-
|
|
122
|
-
def analyze_structure(self, file_path: str, **kwargs: Any) -> dict[str, Any]:
|
|
123
|
-
"""
|
|
124
|
-
構造解析(既存API互換)
|
|
125
|
-
|
|
126
|
-
既存のCLI APIとの互換性を保つため、構造情報を辞書形式で返します。
|
|
127
|
-
|
|
128
|
-
Args:
|
|
129
|
-
file_path: 解析対象ファイルのパス
|
|
130
|
-
**kwargs: 解析オプション
|
|
131
|
-
|
|
132
|
-
Returns:
|
|
133
|
-
Dict[str, Any]: 構造情報の辞書
|
|
134
|
-
- file_path: ファイルパス
|
|
135
|
-
- classes: クラス情報のリスト
|
|
136
|
-
- methods: メソッド情報のリスト
|
|
137
|
-
- fields: フィールド情報のリスト
|
|
138
|
-
- imports: インポート情報のリスト
|
|
139
|
-
|
|
140
|
-
Example:
|
|
141
|
-
>>> adapter = CLIAdapter()
|
|
142
|
-
>>> structure = adapter.analyze_structure("example.java")
|
|
143
|
-
>>> print(structure["classes"])
|
|
144
|
-
"""
|
|
145
|
-
# 詳細情報を含めて解析
|
|
146
|
-
kwargs["include_details"] = True
|
|
147
|
-
kwargs["format_type"] = "structure"
|
|
148
|
-
|
|
149
|
-
result = self.analyze_file(file_path, **kwargs)
|
|
150
|
-
|
|
151
|
-
# 構造情報を辞書形式に変換
|
|
152
|
-
return {
|
|
153
|
-
"file_path": result.file_path,
|
|
154
|
-
"language": result.language,
|
|
155
|
-
"package": result.package,
|
|
156
|
-
"imports": [
|
|
157
|
-
{"name": imp.name, "type": str(type(imp).__name__)}
|
|
158
|
-
for imp in result.imports
|
|
159
|
-
],
|
|
160
|
-
"classes": [
|
|
161
|
-
{"name": cls.name, "type": str(type(cls).__name__)}
|
|
162
|
-
for cls in result.classes
|
|
163
|
-
],
|
|
164
|
-
"methods": [
|
|
165
|
-
{"name": method.name, "type": str(type(method).__name__)}
|
|
166
|
-
for method in result.methods
|
|
167
|
-
],
|
|
168
|
-
"fields": [
|
|
169
|
-
{"name": field.name, "type": str(type(field).__name__)}
|
|
170
|
-
for field in result.fields
|
|
171
|
-
],
|
|
172
|
-
"annotations": [
|
|
173
|
-
{
|
|
174
|
-
"name": getattr(ann, "name", str(ann)),
|
|
175
|
-
"type": str(type(ann).__name__),
|
|
176
|
-
}
|
|
177
|
-
for ann in getattr(result, "annotations", [])
|
|
178
|
-
],
|
|
179
|
-
"analysis_time": result.analysis_time,
|
|
180
|
-
"elements": [
|
|
181
|
-
{"name": elem.name, "type": str(type(elem).__name__)}
|
|
182
|
-
for elem in result.elements
|
|
183
|
-
],
|
|
184
|
-
"success": result.success,
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
def analyze_batch(
|
|
188
|
-
self, file_paths: list[str], **kwargs: Any
|
|
189
|
-
) -> list[AnalysisResult]:
|
|
190
|
-
"""
|
|
191
|
-
複数ファイルの一括解析
|
|
192
|
-
|
|
193
|
-
Args:
|
|
194
|
-
file_paths: 解析対象ファイルパスのリスト
|
|
195
|
-
**kwargs: 解析オプション
|
|
196
|
-
|
|
197
|
-
Returns:
|
|
198
|
-
list[AnalysisResult]: 解析結果のリスト
|
|
199
|
-
|
|
200
|
-
Example:
|
|
201
|
-
>>> adapter = CLIAdapter()
|
|
202
|
-
>>> results = adapter.analyze_batch(["file1.java", "file2.java"])
|
|
203
|
-
>>> print(f"Analyzed {len(results)} files")
|
|
204
|
-
"""
|
|
205
|
-
results = []
|
|
206
|
-
|
|
207
|
-
for file_path in file_paths:
|
|
208
|
-
try:
|
|
209
|
-
result = self.analyze_file(file_path, **kwargs)
|
|
210
|
-
results.append(result)
|
|
211
|
-
except Exception as e:
|
|
212
|
-
logger.warning(f"Failed to analyze {file_path}: {e}")
|
|
213
|
-
# エラーの場合も結果に含める(失敗情報付き)
|
|
214
|
-
error_result = AnalysisResult(
|
|
215
|
-
file_path=file_path,
|
|
216
|
-
package=None,
|
|
217
|
-
imports=[],
|
|
218
|
-
classes=[],
|
|
219
|
-
methods=[],
|
|
220
|
-
fields=[],
|
|
221
|
-
annotations=[],
|
|
222
|
-
analysis_time=0.0,
|
|
223
|
-
success=False,
|
|
224
|
-
error_message=str(e),
|
|
225
|
-
)
|
|
226
|
-
results.append(error_result)
|
|
227
|
-
|
|
228
|
-
return results
|
|
229
|
-
|
|
230
|
-
def get_supported_languages(self) -> list[str]:
|
|
231
|
-
"""
|
|
232
|
-
サポートされている言語のリストを取得
|
|
233
|
-
|
|
234
|
-
Returns:
|
|
235
|
-
list[str]: サポート言語のリスト
|
|
236
|
-
|
|
237
|
-
Example:
|
|
238
|
-
>>> adapter = CLIAdapter()
|
|
239
|
-
>>> languages = adapter.get_supported_languages()
|
|
240
|
-
>>> print(languages)
|
|
241
|
-
"""
|
|
242
|
-
return self._engine.get_supported_languages()
|
|
243
|
-
|
|
244
|
-
def clear_cache(self) -> None:
|
|
245
|
-
"""
|
|
246
|
-
キャッシュをクリア
|
|
247
|
-
|
|
248
|
-
Example:
|
|
249
|
-
>>> adapter = CLIAdapter()
|
|
250
|
-
>>> adapter.clear_cache()
|
|
251
|
-
"""
|
|
252
|
-
self._engine.clear_cache()
|
|
253
|
-
logger.info("CLI adapter cache cleared")
|
|
254
|
-
|
|
255
|
-
def get_cache_stats(self) -> dict[str, Any]:
|
|
256
|
-
"""
|
|
257
|
-
キャッシュ統計情報を取得
|
|
258
|
-
|
|
259
|
-
Returns:
|
|
260
|
-
Dict[str, Any]: キャッシュ統計情報
|
|
261
|
-
|
|
262
|
-
Example:
|
|
263
|
-
>>> adapter = CLIAdapter()
|
|
264
|
-
>>> stats = adapter.get_cache_stats()
|
|
265
|
-
>>> print(f"Cache hit rate: {stats['hit_rate']:.2%}")
|
|
266
|
-
"""
|
|
267
|
-
return self._engine.get_cache_stats()
|
|
268
|
-
|
|
269
|
-
def validate_file(self, file_path: str) -> bool:
|
|
270
|
-
"""
|
|
271
|
-
ファイルが解析可能かどうかを検証
|
|
272
|
-
|
|
273
|
-
Args:
|
|
274
|
-
file_path: 検証対象ファイルのパス
|
|
275
|
-
|
|
276
|
-
Returns:
|
|
277
|
-
bool: 解析可能な場合True
|
|
278
|
-
|
|
279
|
-
Example:
|
|
280
|
-
>>> adapter = CLIAdapter()
|
|
281
|
-
>>> if adapter.validate_file("example.java"):
|
|
282
|
-
... result = adapter.analyze_file("example.java")
|
|
283
|
-
"""
|
|
284
|
-
try:
|
|
285
|
-
# ファイル存在チェック
|
|
286
|
-
if not Path(file_path).exists():
|
|
287
|
-
return False
|
|
288
|
-
|
|
289
|
-
# 言語サポートチェック
|
|
290
|
-
supported_languages = self.get_supported_languages()
|
|
291
|
-
file_extension = Path(file_path).suffix.lower()
|
|
292
|
-
|
|
293
|
-
# 拡張子から言語を推定
|
|
294
|
-
extension_map = {
|
|
295
|
-
".java": "java",
|
|
296
|
-
".py": "python",
|
|
297
|
-
".js": "javascript",
|
|
298
|
-
".ts": "typescript",
|
|
299
|
-
".cpp": "cpp",
|
|
300
|
-
".c": "c",
|
|
301
|
-
".cs": "csharp",
|
|
302
|
-
".go": "go",
|
|
303
|
-
".rs": "rust",
|
|
304
|
-
".php": "php",
|
|
305
|
-
".rb": "ruby",
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
language = extension_map.get(file_extension)
|
|
309
|
-
return language in supported_languages if language else False
|
|
310
|
-
|
|
311
|
-
except Exception as e:
|
|
312
|
-
logger.warning(f"File validation failed for {file_path}: {e}")
|
|
313
|
-
return False
|
|
314
|
-
|
|
315
|
-
def get_engine_info(self) -> dict[str, Any]:
|
|
316
|
-
"""
|
|
317
|
-
エンジン情報を取得
|
|
318
|
-
|
|
319
|
-
Returns:
|
|
320
|
-
Dict[str, Any]: エンジン情報
|
|
321
|
-
|
|
322
|
-
Example:
|
|
323
|
-
>>> adapter = CLIAdapter()
|
|
324
|
-
>>> info = adapter.get_engine_info()
|
|
325
|
-
>>> print(f"Engine version: {info['version']}")
|
|
326
|
-
"""
|
|
327
|
-
return {
|
|
328
|
-
"adapter_type": "CLI",
|
|
329
|
-
"engine_type": "UnifiedAnalysisEngine",
|
|
330
|
-
"supported_languages": self.get_supported_languages(),
|
|
331
|
-
"cache_enabled": True,
|
|
332
|
-
"async_support": True,
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
# Legacy AdvancedAnalyzerAdapter removed - use UnifiedAnalysisEngine directly
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
def get_analysis_engine() -> "UnifiedAnalysisEngine":
|
|
340
|
-
"""Get analysis engine instance for testing compatibility."""
|
|
341
|
-
from ..core.analysis_engine import UnifiedAnalysisEngine
|
|
342
|
-
|
|
343
|
-
return UnifiedAnalysisEngine()
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
CLI Adapter for tree-sitter-analyzer
|
|
4
|
+
|
|
5
|
+
Adapter that uses the new unified analysis engine while maintaining compatibility with existing CLI API.
|
|
6
|
+
|
|
7
|
+
Roo Code compliance:
|
|
8
|
+
- Type hints: Required for all functions
|
|
9
|
+
- MCP logging: Log output at each step
|
|
10
|
+
- docstring: Google Style docstring
|
|
11
|
+
- Error handling: Proper exception handling
|
|
12
|
+
- Performance: Optimization through unified engine
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import asyncio
|
|
16
|
+
import logging
|
|
17
|
+
import time
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from ..core.analysis_engine import AnalysisRequest, UnifiedAnalysisEngine
|
|
22
|
+
from ..models import AnalysisResult
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CLIAdapter:
|
|
28
|
+
"""
|
|
29
|
+
CLI Adapter
|
|
30
|
+
|
|
31
|
+
Uses the new unified analysis engine while maintaining compatibility with existing CLI API.
|
|
32
|
+
Provides synchronous API and internally calls asynchronous engine.
|
|
33
|
+
|
|
34
|
+
Features:
|
|
35
|
+
- Maintaining existing API compatibility
|
|
36
|
+
- Utilizing unified analysis engine
|
|
37
|
+
- Sync/async conversion
|
|
38
|
+
- Performance monitoring
|
|
39
|
+
- Error handling
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> adapter = CLIAdapter()
|
|
43
|
+
>>> result = adapter.analyze_file("example.java")
|
|
44
|
+
>>> print(result.classes)
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self) -> None:
|
|
48
|
+
"""
|
|
49
|
+
Initialize CLI adapter
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
Exception: If unified analysis engine initialization fails
|
|
53
|
+
"""
|
|
54
|
+
try:
|
|
55
|
+
self._engine = UnifiedAnalysisEngine()
|
|
56
|
+
logger.info("CLIAdapter initialized successfully")
|
|
57
|
+
except Exception as e:
|
|
58
|
+
logger.error(f"Failed to initialize CLIAdapter: {e}")
|
|
59
|
+
raise
|
|
60
|
+
|
|
61
|
+
def analyze_file(self, file_path: str, **kwargs: Any) -> AnalysisResult:
|
|
62
|
+
"""
|
|
63
|
+
Analyze file (synchronous version)
|
|
64
|
+
|
|
65
|
+
Provides synchronous interface to maintain compatibility with existing CLI API.
|
|
66
|
+
Internally calls asynchronous methods of unified analysis engine.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
file_path: Path to file to analyze
|
|
70
|
+
**kwargs: Analysis options
|
|
71
|
+
- language: Language specification (auto-detection possible)
|
|
72
|
+
- include_complexity: Include complexity calculation
|
|
73
|
+
- include_details: Include detailed information
|
|
74
|
+
- format_type: Output format ("standard", "structure", "summary")
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
AnalysisResult: Analysis result
|
|
78
|
+
|
|
79
|
+
Raises:
|
|
80
|
+
ValueError: For invalid file path
|
|
81
|
+
FileNotFoundError: If file does not exist
|
|
82
|
+
UnsupportedLanguageError: For unsupported language
|
|
83
|
+
|
|
84
|
+
Example:
|
|
85
|
+
>>> adapter = CLIAdapter()
|
|
86
|
+
>>> result = adapter.analyze_file("example.java", include_complexity=True)
|
|
87
|
+
>>> print(f"Classes: {len(result.classes)}")
|
|
88
|
+
"""
|
|
89
|
+
start_time = time.time()
|
|
90
|
+
|
|
91
|
+
# Input validation
|
|
92
|
+
if not file_path or not file_path.strip():
|
|
93
|
+
raise ValueError("File path cannot be empty")
|
|
94
|
+
|
|
95
|
+
# File existence check
|
|
96
|
+
if not Path(file_path).exists():
|
|
97
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
# Create AnalysisRequest
|
|
101
|
+
request = AnalysisRequest(
|
|
102
|
+
file_path=file_path,
|
|
103
|
+
language=kwargs.get("language"),
|
|
104
|
+
include_complexity=kwargs.get("include_complexity", False),
|
|
105
|
+
include_details=kwargs.get("include_details", True),
|
|
106
|
+
format_type=kwargs.get("format_type", "standard"),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# 非同期エンジンを同期的に実行
|
|
110
|
+
result = asyncio.run(self._engine.analyze(request))
|
|
111
|
+
|
|
112
|
+
# パフォーマンスログ
|
|
113
|
+
elapsed_time = time.time() - start_time
|
|
114
|
+
logger.info(f"CLI analysis completed: {file_path} in {elapsed_time:.3f}s")
|
|
115
|
+
|
|
116
|
+
return result
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.error(f"CLI analysis failed for {file_path}: {e}")
|
|
120
|
+
raise
|
|
121
|
+
|
|
122
|
+
def analyze_structure(self, file_path: str, **kwargs: Any) -> dict[str, Any]:
|
|
123
|
+
"""
|
|
124
|
+
構造解析(既存API互換)
|
|
125
|
+
|
|
126
|
+
既存のCLI APIとの互換性を保つため、構造情報を辞書形式で返します。
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
file_path: 解析対象ファイルのパス
|
|
130
|
+
**kwargs: 解析オプション
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Dict[str, Any]: 構造情報の辞書
|
|
134
|
+
- file_path: ファイルパス
|
|
135
|
+
- classes: クラス情報のリスト
|
|
136
|
+
- methods: メソッド情報のリスト
|
|
137
|
+
- fields: フィールド情報のリスト
|
|
138
|
+
- imports: インポート情報のリスト
|
|
139
|
+
|
|
140
|
+
Example:
|
|
141
|
+
>>> adapter = CLIAdapter()
|
|
142
|
+
>>> structure = adapter.analyze_structure("example.java")
|
|
143
|
+
>>> print(structure["classes"])
|
|
144
|
+
"""
|
|
145
|
+
# 詳細情報を含めて解析
|
|
146
|
+
kwargs["include_details"] = True
|
|
147
|
+
kwargs["format_type"] = "structure"
|
|
148
|
+
|
|
149
|
+
result = self.analyze_file(file_path, **kwargs)
|
|
150
|
+
|
|
151
|
+
# 構造情報を辞書形式に変換
|
|
152
|
+
return {
|
|
153
|
+
"file_path": result.file_path,
|
|
154
|
+
"language": result.language,
|
|
155
|
+
"package": result.package,
|
|
156
|
+
"imports": [
|
|
157
|
+
{"name": imp.name, "type": str(type(imp).__name__)}
|
|
158
|
+
for imp in result.imports
|
|
159
|
+
],
|
|
160
|
+
"classes": [
|
|
161
|
+
{"name": cls.name, "type": str(type(cls).__name__)}
|
|
162
|
+
for cls in result.classes
|
|
163
|
+
],
|
|
164
|
+
"methods": [
|
|
165
|
+
{"name": method.name, "type": str(type(method).__name__)}
|
|
166
|
+
for method in result.methods
|
|
167
|
+
],
|
|
168
|
+
"fields": [
|
|
169
|
+
{"name": field.name, "type": str(type(field).__name__)}
|
|
170
|
+
for field in result.fields
|
|
171
|
+
],
|
|
172
|
+
"annotations": [
|
|
173
|
+
{
|
|
174
|
+
"name": getattr(ann, "name", str(ann)),
|
|
175
|
+
"type": str(type(ann).__name__),
|
|
176
|
+
}
|
|
177
|
+
for ann in getattr(result, "annotations", [])
|
|
178
|
+
],
|
|
179
|
+
"analysis_time": result.analysis_time,
|
|
180
|
+
"elements": [
|
|
181
|
+
{"name": elem.name, "type": str(type(elem).__name__)}
|
|
182
|
+
for elem in result.elements
|
|
183
|
+
],
|
|
184
|
+
"success": result.success,
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
def analyze_batch(
|
|
188
|
+
self, file_paths: list[str], **kwargs: Any
|
|
189
|
+
) -> list[AnalysisResult]:
|
|
190
|
+
"""
|
|
191
|
+
複数ファイルの一括解析
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
file_paths: 解析対象ファイルパスのリスト
|
|
195
|
+
**kwargs: 解析オプション
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
list[AnalysisResult]: 解析結果のリスト
|
|
199
|
+
|
|
200
|
+
Example:
|
|
201
|
+
>>> adapter = CLIAdapter()
|
|
202
|
+
>>> results = adapter.analyze_batch(["file1.java", "file2.java"])
|
|
203
|
+
>>> print(f"Analyzed {len(results)} files")
|
|
204
|
+
"""
|
|
205
|
+
results = []
|
|
206
|
+
|
|
207
|
+
for file_path in file_paths:
|
|
208
|
+
try:
|
|
209
|
+
result = self.analyze_file(file_path, **kwargs)
|
|
210
|
+
results.append(result)
|
|
211
|
+
except Exception as e:
|
|
212
|
+
logger.warning(f"Failed to analyze {file_path}: {e}")
|
|
213
|
+
# エラーの場合も結果に含める(失敗情報付き)
|
|
214
|
+
error_result = AnalysisResult(
|
|
215
|
+
file_path=file_path,
|
|
216
|
+
package=None,
|
|
217
|
+
imports=[],
|
|
218
|
+
classes=[],
|
|
219
|
+
methods=[],
|
|
220
|
+
fields=[],
|
|
221
|
+
annotations=[],
|
|
222
|
+
analysis_time=0.0,
|
|
223
|
+
success=False,
|
|
224
|
+
error_message=str(e),
|
|
225
|
+
)
|
|
226
|
+
results.append(error_result)
|
|
227
|
+
|
|
228
|
+
return results
|
|
229
|
+
|
|
230
|
+
def get_supported_languages(self) -> list[str]:
|
|
231
|
+
"""
|
|
232
|
+
サポートされている言語のリストを取得
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
list[str]: サポート言語のリスト
|
|
236
|
+
|
|
237
|
+
Example:
|
|
238
|
+
>>> adapter = CLIAdapter()
|
|
239
|
+
>>> languages = adapter.get_supported_languages()
|
|
240
|
+
>>> print(languages)
|
|
241
|
+
"""
|
|
242
|
+
return self._engine.get_supported_languages()
|
|
243
|
+
|
|
244
|
+
def clear_cache(self) -> None:
|
|
245
|
+
"""
|
|
246
|
+
キャッシュをクリア
|
|
247
|
+
|
|
248
|
+
Example:
|
|
249
|
+
>>> adapter = CLIAdapter()
|
|
250
|
+
>>> adapter.clear_cache()
|
|
251
|
+
"""
|
|
252
|
+
self._engine.clear_cache()
|
|
253
|
+
logger.info("CLI adapter cache cleared")
|
|
254
|
+
|
|
255
|
+
def get_cache_stats(self) -> dict[str, Any]:
|
|
256
|
+
"""
|
|
257
|
+
キャッシュ統計情報を取得
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Dict[str, Any]: キャッシュ統計情報
|
|
261
|
+
|
|
262
|
+
Example:
|
|
263
|
+
>>> adapter = CLIAdapter()
|
|
264
|
+
>>> stats = adapter.get_cache_stats()
|
|
265
|
+
>>> print(f"Cache hit rate: {stats['hit_rate']:.2%}")
|
|
266
|
+
"""
|
|
267
|
+
return self._engine.get_cache_stats()
|
|
268
|
+
|
|
269
|
+
def validate_file(self, file_path: str) -> bool:
|
|
270
|
+
"""
|
|
271
|
+
ファイルが解析可能かどうかを検証
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
file_path: 検証対象ファイルのパス
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
bool: 解析可能な場合True
|
|
278
|
+
|
|
279
|
+
Example:
|
|
280
|
+
>>> adapter = CLIAdapter()
|
|
281
|
+
>>> if adapter.validate_file("example.java"):
|
|
282
|
+
... result = adapter.analyze_file("example.java")
|
|
283
|
+
"""
|
|
284
|
+
try:
|
|
285
|
+
# ファイル存在チェック
|
|
286
|
+
if not Path(file_path).exists():
|
|
287
|
+
return False
|
|
288
|
+
|
|
289
|
+
# 言語サポートチェック
|
|
290
|
+
supported_languages = self.get_supported_languages()
|
|
291
|
+
file_extension = Path(file_path).suffix.lower()
|
|
292
|
+
|
|
293
|
+
# 拡張子から言語を推定
|
|
294
|
+
extension_map = {
|
|
295
|
+
".java": "java",
|
|
296
|
+
".py": "python",
|
|
297
|
+
".js": "javascript",
|
|
298
|
+
".ts": "typescript",
|
|
299
|
+
".cpp": "cpp",
|
|
300
|
+
".c": "c",
|
|
301
|
+
".cs": "csharp",
|
|
302
|
+
".go": "go",
|
|
303
|
+
".rs": "rust",
|
|
304
|
+
".php": "php",
|
|
305
|
+
".rb": "ruby",
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
language = extension_map.get(file_extension)
|
|
309
|
+
return language in supported_languages if language else False
|
|
310
|
+
|
|
311
|
+
except Exception as e:
|
|
312
|
+
logger.warning(f"File validation failed for {file_path}: {e}")
|
|
313
|
+
return False
|
|
314
|
+
|
|
315
|
+
def get_engine_info(self) -> dict[str, Any]:
|
|
316
|
+
"""
|
|
317
|
+
エンジン情報を取得
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
Dict[str, Any]: エンジン情報
|
|
321
|
+
|
|
322
|
+
Example:
|
|
323
|
+
>>> adapter = CLIAdapter()
|
|
324
|
+
>>> info = adapter.get_engine_info()
|
|
325
|
+
>>> print(f"Engine version: {info['version']}")
|
|
326
|
+
"""
|
|
327
|
+
return {
|
|
328
|
+
"adapter_type": "CLI",
|
|
329
|
+
"engine_type": "UnifiedAnalysisEngine",
|
|
330
|
+
"supported_languages": self.get_supported_languages(),
|
|
331
|
+
"cache_enabled": True,
|
|
332
|
+
"async_support": True,
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
# Legacy AdvancedAnalyzerAdapter removed - use UnifiedAnalysisEngine directly
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def get_analysis_engine() -> "UnifiedAnalysisEngine":
|
|
340
|
+
"""Get analysis engine instance for testing compatibility."""
|
|
341
|
+
from ..core.analysis_engine import UnifiedAnalysisEngine
|
|
342
|
+
|
|
343
|
+
return UnifiedAnalysisEngine()
|