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,13 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
CLI Module Entry Point
|
|
5
|
+
|
|
6
|
+
This module allows the CLI to be executed as a module with:
|
|
7
|
+
python -m tree_sitter_analyzer.cli
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from ..cli_main import main
|
|
11
|
+
|
|
12
|
+
if __name__ == "__main__":
|
|
13
|
+
main()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
CLI Commands Package
|
|
5
|
+
|
|
6
|
+
This package contains all command implementations for the CLI interface.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .advanced_command import AdvancedCommand
|
|
10
|
+
from .base_command import BaseCommand
|
|
11
|
+
from .default_command import DefaultCommand
|
|
12
|
+
from .partial_read_command import PartialReadCommand
|
|
13
|
+
from .query_command import QueryCommand
|
|
14
|
+
from .structure_command import StructureCommand
|
|
15
|
+
from .summary_command import SummaryCommand
|
|
16
|
+
from .table_command import TableCommand
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"BaseCommand",
|
|
20
|
+
"AdvancedCommand",
|
|
21
|
+
"DefaultCommand",
|
|
22
|
+
"PartialReadCommand",
|
|
23
|
+
"QueryCommand",
|
|
24
|
+
"StructureCommand",
|
|
25
|
+
"SummaryCommand",
|
|
26
|
+
"TableCommand",
|
|
27
|
+
]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Advanced Command
|
|
5
|
+
|
|
6
|
+
Handles advanced analysis functionality.
|
|
7
|
+
"""
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from ...output_manager import output_data, output_json, output_section
|
|
11
|
+
from .base_command import BaseCommand
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ...models import AnalysisResult
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AdvancedCommand(BaseCommand):
|
|
18
|
+
"""Command for advanced analysis."""
|
|
19
|
+
|
|
20
|
+
async def execute_async(self, language: str) -> int:
|
|
21
|
+
analysis_result = await self.analyze_file(language)
|
|
22
|
+
if not analysis_result:
|
|
23
|
+
return 1
|
|
24
|
+
|
|
25
|
+
if hasattr(self.args, "statistics") and self.args.statistics:
|
|
26
|
+
self._output_statistics(analysis_result)
|
|
27
|
+
else:
|
|
28
|
+
self._output_full_analysis(analysis_result)
|
|
29
|
+
|
|
30
|
+
return 0
|
|
31
|
+
|
|
32
|
+
def _output_statistics(self, analysis_result: "AnalysisResult") -> None:
|
|
33
|
+
"""Output statistics only."""
|
|
34
|
+
stats = {
|
|
35
|
+
"line_count": analysis_result.line_count,
|
|
36
|
+
"element_count": len(analysis_result.elements),
|
|
37
|
+
"node_count": analysis_result.node_count,
|
|
38
|
+
"language": analysis_result.language,
|
|
39
|
+
}
|
|
40
|
+
output_section("統計情報")
|
|
41
|
+
if self.args.output_format == "json":
|
|
42
|
+
output_json(stats)
|
|
43
|
+
else:
|
|
44
|
+
for key, value in stats.items():
|
|
45
|
+
output_data(f"{key}: {value}")
|
|
46
|
+
|
|
47
|
+
def _output_full_analysis(self, analysis_result: "AnalysisResult") -> None:
|
|
48
|
+
"""Output full analysis results."""
|
|
49
|
+
output_section("高度な解析結果")
|
|
50
|
+
if self.args.output_format == "json":
|
|
51
|
+
result_dict = {
|
|
52
|
+
"file_path": analysis_result.file_path,
|
|
53
|
+
"language": analysis_result.language,
|
|
54
|
+
"line_count": analysis_result.line_count,
|
|
55
|
+
"element_count": len(analysis_result.elements),
|
|
56
|
+
"node_count": analysis_result.node_count,
|
|
57
|
+
"elements": [
|
|
58
|
+
{
|
|
59
|
+
"name": getattr(element, "name", str(element)),
|
|
60
|
+
"type": getattr(element, "__class__", type(element)).__name__,
|
|
61
|
+
"start_line": getattr(element, "start_line", 0),
|
|
62
|
+
"end_line": getattr(element, "end_line", 0),
|
|
63
|
+
}
|
|
64
|
+
for element in analysis_result.elements
|
|
65
|
+
],
|
|
66
|
+
"success": analysis_result.success,
|
|
67
|
+
"analysis_time": analysis_result.analysis_time,
|
|
68
|
+
}
|
|
69
|
+
output_json(result_dict)
|
|
70
|
+
else:
|
|
71
|
+
self._output_text_analysis(analysis_result)
|
|
72
|
+
|
|
73
|
+
def _output_text_analysis(self, analysis_result: "AnalysisResult") -> None:
|
|
74
|
+
"""Output analysis in text format."""
|
|
75
|
+
output_data(f"ファイル: {analysis_result.file_path}")
|
|
76
|
+
output_data(f"パッケージ: (default)")
|
|
77
|
+
output_data(f"行数: {analysis_result.line_count}")
|
|
78
|
+
|
|
79
|
+
element_counts = {}
|
|
80
|
+
for element in analysis_result.elements:
|
|
81
|
+
element_type = getattr(element, "__class__", type(element)).__name__
|
|
82
|
+
element_counts[element_type] = element_counts.get(element_type, 0) + 1
|
|
83
|
+
|
|
84
|
+
output_data(f"\nクラス数: {element_counts.get('Class', 0)}")
|
|
85
|
+
output_data(f"メソッド数: {element_counts.get('Function', 0)}")
|
|
86
|
+
output_data(f"フィールド数: {element_counts.get('Variable', 0)}")
|
|
87
|
+
output_data(f"インポート数: {element_counts.get('Import', 0)}")
|
|
88
|
+
output_data(f"アノテーション数: 0")
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Base Command Class
|
|
5
|
+
|
|
6
|
+
Abstract base class for all CLI commands implementing the Command Pattern.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
from abc import ABC, abstractmethod
|
|
11
|
+
from argparse import Namespace
|
|
12
|
+
from typing import Any, Optional
|
|
13
|
+
|
|
14
|
+
from ...core.analysis_engine import AnalysisRequest, get_analysis_engine
|
|
15
|
+
from ...file_handler import read_file_partial
|
|
16
|
+
from ...language_detector import detect_language_from_file, is_language_supported
|
|
17
|
+
from ...models import AnalysisResult
|
|
18
|
+
from ...output_manager import output_error, output_info
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseCommand(ABC):
|
|
22
|
+
"""
|
|
23
|
+
Base class for all CLI commands.
|
|
24
|
+
|
|
25
|
+
Implements common functionality like file validation, language detection,
|
|
26
|
+
and analysis engine interaction.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, args: Namespace):
|
|
30
|
+
"""Initialize command with parsed arguments."""
|
|
31
|
+
self.args = args
|
|
32
|
+
self.analysis_engine = get_analysis_engine()
|
|
33
|
+
|
|
34
|
+
def validate_file(self) -> bool:
|
|
35
|
+
"""Validate input file exists and is accessible."""
|
|
36
|
+
if not hasattr(self.args, "file_path") or not self.args.file_path:
|
|
37
|
+
output_error("ERROR: ファイルパスが指定されていません。")
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
import os
|
|
41
|
+
|
|
42
|
+
if not os.path.exists(self.args.file_path):
|
|
43
|
+
output_error(f"ERROR: ファイルが見つかりません: {self.args.file_path}")
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
def detect_language(self) -> Optional[str]:
|
|
49
|
+
"""Detect or validate the target language."""
|
|
50
|
+
if hasattr(self.args, "language") and self.args.language:
|
|
51
|
+
target_language = self.args.language.lower()
|
|
52
|
+
if (not hasattr(self.args, "table") or not self.args.table) and (not hasattr(self.args, "quiet") or not self.args.quiet):
|
|
53
|
+
output_info(f"INFO: 言語が明示的に指定されました: {target_language}")
|
|
54
|
+
else:
|
|
55
|
+
target_language = detect_language_from_file(self.args.file_path)
|
|
56
|
+
if target_language == "unknown":
|
|
57
|
+
output_error(
|
|
58
|
+
f"ERROR: ファイル '{self.args.file_path}' の言語を判定できませんでした。"
|
|
59
|
+
)
|
|
60
|
+
return None
|
|
61
|
+
else:
|
|
62
|
+
if (not hasattr(self.args, "table") or not self.args.table) and (not hasattr(self.args, "quiet") or not self.args.quiet):
|
|
63
|
+
output_info(
|
|
64
|
+
f"INFO: 拡張子から言語を自動判定しました: {target_language}"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Language support validation
|
|
68
|
+
if not is_language_supported(target_language):
|
|
69
|
+
if target_language != "java":
|
|
70
|
+
if (not hasattr(self.args, "table") or not self.args.table) and (not hasattr(self.args, "quiet") or not self.args.quiet):
|
|
71
|
+
output_info(
|
|
72
|
+
"INFO: Java解析エンジンで試行します。正しく動作しない可能性があります。"
|
|
73
|
+
)
|
|
74
|
+
target_language = "java" # Fallback
|
|
75
|
+
|
|
76
|
+
return target_language
|
|
77
|
+
|
|
78
|
+
async def analyze_file(self, language: str) -> Optional["AnalysisResult"]:
|
|
79
|
+
"""Perform file analysis using the unified analysis engine."""
|
|
80
|
+
try:
|
|
81
|
+
# Handle partial read if enabled
|
|
82
|
+
if hasattr(self.args, 'partial_read') and self.args.partial_read:
|
|
83
|
+
try:
|
|
84
|
+
partial_content = read_file_partial(
|
|
85
|
+
self.args.file_path,
|
|
86
|
+
start_line=self.args.start_line,
|
|
87
|
+
end_line=getattr(self.args, 'end_line', None),
|
|
88
|
+
start_column=getattr(self.args, 'start_column', None),
|
|
89
|
+
end_column=getattr(self.args, 'end_column', None)
|
|
90
|
+
)
|
|
91
|
+
if partial_content is None:
|
|
92
|
+
output_error("ERROR: ファイルの部分読み込みに失敗しました")
|
|
93
|
+
return None
|
|
94
|
+
except Exception as e:
|
|
95
|
+
output_error(f"ERROR: ファイルの部分読み込みに失敗しました: {e}")
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
request = AnalysisRequest(
|
|
99
|
+
file_path=self.args.file_path,
|
|
100
|
+
language=language,
|
|
101
|
+
include_complexity=True,
|
|
102
|
+
include_details=True,
|
|
103
|
+
)
|
|
104
|
+
analysis_result = await self.analysis_engine.analyze(request)
|
|
105
|
+
|
|
106
|
+
if not analysis_result or not analysis_result.success:
|
|
107
|
+
error_msg = (
|
|
108
|
+
analysis_result.error_message
|
|
109
|
+
if analysis_result
|
|
110
|
+
else "Unknown error"
|
|
111
|
+
)
|
|
112
|
+
output_error(f"ERROR: 解析に失敗しました: {error_msg}")
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
return analysis_result
|
|
116
|
+
|
|
117
|
+
except Exception as e:
|
|
118
|
+
output_error(f"ERROR: 解析でエラーが発生しました: {e}")
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
def execute(self) -> int:
|
|
122
|
+
"""
|
|
123
|
+
Execute the command.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
int: Exit code (0 for success, 1 for failure)
|
|
127
|
+
"""
|
|
128
|
+
# Validate inputs
|
|
129
|
+
if not self.validate_file():
|
|
130
|
+
return 1
|
|
131
|
+
|
|
132
|
+
# Detect language
|
|
133
|
+
language = self.detect_language()
|
|
134
|
+
if not language:
|
|
135
|
+
return 1
|
|
136
|
+
|
|
137
|
+
# Execute the specific command
|
|
138
|
+
try:
|
|
139
|
+
return asyncio.run(self.execute_async(language))
|
|
140
|
+
except Exception as e:
|
|
141
|
+
output_error(f"ERROR: コマンド実行中にエラーが発生しました: {e}")
|
|
142
|
+
return 1
|
|
143
|
+
|
|
144
|
+
@abstractmethod
|
|
145
|
+
async def execute_async(self, language: str) -> int:
|
|
146
|
+
"""
|
|
147
|
+
Execute the command asynchronously.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
language: Detected/specified target language
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
int: Exit code (0 for success, 1 for failure)
|
|
154
|
+
"""
|
|
155
|
+
pass
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Default Command
|
|
5
|
+
|
|
6
|
+
Handles default analysis when no specific command is specified.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from ...output_manager import output_error
|
|
10
|
+
from .base_command import BaseCommand
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DefaultCommand(BaseCommand):
|
|
14
|
+
"""Default command that shows error when no specific command is given."""
|
|
15
|
+
|
|
16
|
+
async def execute_async(self, language: str) -> int:
|
|
17
|
+
"""Execute default command - show error for missing options."""
|
|
18
|
+
output_error("ERROR: クエリまたは--advancedオプションを指定してください")
|
|
19
|
+
return 1
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Partial Read Command
|
|
5
|
+
|
|
6
|
+
Handles partial file reading functionality, extracting specified line ranges.
|
|
7
|
+
"""
|
|
8
|
+
import json
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from ...file_handler import read_file_partial
|
|
12
|
+
from ...output_manager import output_data, output_json, output_section
|
|
13
|
+
from .base_command import BaseCommand
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PartialReadCommand(BaseCommand):
|
|
20
|
+
"""Command for reading partial file content by line range."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, args):
|
|
23
|
+
"""Initialize with arguments but skip base class analysis engine setup."""
|
|
24
|
+
self.args = args
|
|
25
|
+
# Don't call super().__init__() to avoid unnecessary analysis engine setup
|
|
26
|
+
|
|
27
|
+
def validate_file(self) -> bool:
|
|
28
|
+
"""Validate input file exists and is accessible."""
|
|
29
|
+
if not hasattr(self.args, "file_path") or not self.args.file_path:
|
|
30
|
+
from ...output_manager import output_error
|
|
31
|
+
output_error("ERROR: ファイルパスが指定されていません。")
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
import os
|
|
35
|
+
|
|
36
|
+
if not os.path.exists(self.args.file_path):
|
|
37
|
+
from ...output_manager import output_error
|
|
38
|
+
output_error(f"ERROR: ファイルが見つかりません: {self.args.file_path}")
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
def execute(self) -> int:
|
|
44
|
+
"""
|
|
45
|
+
Execute partial read command.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
int: Exit code (0 for success, 1 for failure)
|
|
49
|
+
"""
|
|
50
|
+
# Validate inputs
|
|
51
|
+
if not self.validate_file():
|
|
52
|
+
return 1
|
|
53
|
+
|
|
54
|
+
# Validate partial read arguments
|
|
55
|
+
if not self.args.start_line:
|
|
56
|
+
from ...output_manager import output_error
|
|
57
|
+
output_error("ERROR: --start-lineが必須です")
|
|
58
|
+
return 1
|
|
59
|
+
|
|
60
|
+
if self.args.start_line < 1:
|
|
61
|
+
from ...output_manager import output_error
|
|
62
|
+
output_error("ERROR: --start-lineは1以上である必要があります")
|
|
63
|
+
return 1
|
|
64
|
+
|
|
65
|
+
if self.args.end_line and self.args.end_line < self.args.start_line:
|
|
66
|
+
from ...output_manager import output_error
|
|
67
|
+
output_error("ERROR: --end-lineは--start-line以上である必要があります")
|
|
68
|
+
return 1
|
|
69
|
+
|
|
70
|
+
# Read partial content
|
|
71
|
+
try:
|
|
72
|
+
partial_content = read_file_partial(
|
|
73
|
+
self.args.file_path,
|
|
74
|
+
start_line=self.args.start_line,
|
|
75
|
+
end_line=getattr(self.args, 'end_line', None),
|
|
76
|
+
start_column=getattr(self.args, 'start_column', None),
|
|
77
|
+
end_column=getattr(self.args, 'end_column', None)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if partial_content is None:
|
|
81
|
+
from ...output_manager import output_error
|
|
82
|
+
output_error("ERROR: ファイルの部分読み込みに失敗しました")
|
|
83
|
+
return 1
|
|
84
|
+
|
|
85
|
+
# Output the result
|
|
86
|
+
self._output_partial_content(partial_content)
|
|
87
|
+
return 0
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
from ...output_manager import output_error
|
|
91
|
+
output_error(f"ERROR: ファイルの部分読み込みに失敗しました: {e}")
|
|
92
|
+
return 1
|
|
93
|
+
|
|
94
|
+
def _output_partial_content(self, content: str) -> None:
|
|
95
|
+
"""Output the partial content in the specified format."""
|
|
96
|
+
# Build result data
|
|
97
|
+
result_data = {
|
|
98
|
+
"file_path": self.args.file_path,
|
|
99
|
+
"range": {
|
|
100
|
+
"start_line": self.args.start_line,
|
|
101
|
+
"end_line": getattr(self.args, 'end_line', None),
|
|
102
|
+
"start_column": getattr(self.args, 'start_column', None),
|
|
103
|
+
"end_column": getattr(self.args, 'end_column', None),
|
|
104
|
+
},
|
|
105
|
+
"content": content,
|
|
106
|
+
"content_length": len(content),
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
# Build range info for header
|
|
110
|
+
range_info = f"行 {self.args.start_line}"
|
|
111
|
+
if hasattr(self.args, 'end_line') and self.args.end_line:
|
|
112
|
+
range_info += f"-{self.args.end_line}"
|
|
113
|
+
|
|
114
|
+
# Output format selection
|
|
115
|
+
output_format = getattr(self.args, 'output_format', 'text')
|
|
116
|
+
|
|
117
|
+
if output_format == 'json':
|
|
118
|
+
# Pure JSON output
|
|
119
|
+
output_json(result_data)
|
|
120
|
+
else:
|
|
121
|
+
# Human-readable format with header
|
|
122
|
+
output_section("部分読み込み結果")
|
|
123
|
+
output_data(f"ファイル: {self.args.file_path}")
|
|
124
|
+
output_data(f"範囲: {range_info}")
|
|
125
|
+
output_data(f"読み込み文字数: {len(content)}")
|
|
126
|
+
output_data("") # Empty line for separation
|
|
127
|
+
|
|
128
|
+
# Output the actual content
|
|
129
|
+
print(content, end='') # Use print to avoid extra formatting
|
|
130
|
+
|
|
131
|
+
async def execute_async(self, language: str) -> int:
|
|
132
|
+
"""Not used for partial read command."""
|
|
133
|
+
return self.execute()
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Query Command
|
|
5
|
+
|
|
6
|
+
Handles query execution functionality.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from ...output_manager import output_data, output_error, output_info, output_json
|
|
10
|
+
from ...query_loader import query_loader
|
|
11
|
+
from .base_command import BaseCommand
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class QueryCommand(BaseCommand):
|
|
15
|
+
"""Command for executing queries."""
|
|
16
|
+
|
|
17
|
+
async def execute_async(self, language: str) -> int:
|
|
18
|
+
# Get the query to execute
|
|
19
|
+
query_to_execute = None
|
|
20
|
+
|
|
21
|
+
if hasattr(self.args, "query_key") and self.args.query_key:
|
|
22
|
+
try:
|
|
23
|
+
query_to_execute = query_loader.get_query(language, self.args.query_key)
|
|
24
|
+
if query_to_execute is None:
|
|
25
|
+
output_error(
|
|
26
|
+
f"ERROR: クエリ '{self.args.query_key}' が言語 '{language}' で見つかりません"
|
|
27
|
+
)
|
|
28
|
+
return 1
|
|
29
|
+
except ValueError as e:
|
|
30
|
+
output_error(f"ERROR: {e}")
|
|
31
|
+
return 1
|
|
32
|
+
elif hasattr(self.args, "query_string") and self.args.query_string:
|
|
33
|
+
query_to_execute = self.args.query_string
|
|
34
|
+
|
|
35
|
+
if not query_to_execute:
|
|
36
|
+
output_error("ERROR: クエリが指定されていません。")
|
|
37
|
+
return 1
|
|
38
|
+
|
|
39
|
+
# Perform analysis
|
|
40
|
+
analysis_result = await self.analyze_file(language)
|
|
41
|
+
if not analysis_result:
|
|
42
|
+
return 1
|
|
43
|
+
|
|
44
|
+
# Process query results
|
|
45
|
+
results = []
|
|
46
|
+
if hasattr(analysis_result, "query_results") and analysis_result.query_results:
|
|
47
|
+
results = analysis_result.query_results.get("captures", [])
|
|
48
|
+
else:
|
|
49
|
+
# Create basic results from elements
|
|
50
|
+
if hasattr(analysis_result, "elements") and analysis_result.elements:
|
|
51
|
+
for element in analysis_result.elements:
|
|
52
|
+
results.append(
|
|
53
|
+
{
|
|
54
|
+
"capture_name": getattr(
|
|
55
|
+
element, "__class__", type(element)
|
|
56
|
+
).__name__.lower(),
|
|
57
|
+
"node_type": getattr(
|
|
58
|
+
element, "__class__", type(element)
|
|
59
|
+
).__name__,
|
|
60
|
+
"start_line": getattr(element, "start_line", 0),
|
|
61
|
+
"end_line": getattr(element, "end_line", 0),
|
|
62
|
+
"content": getattr(element, "name", str(element)),
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Output results
|
|
67
|
+
if results:
|
|
68
|
+
if self.args.output_format == "json":
|
|
69
|
+
output_json(results)
|
|
70
|
+
else:
|
|
71
|
+
for i, query_result in enumerate(results, 1):
|
|
72
|
+
output_data(
|
|
73
|
+
f"\n{i}. {query_result['capture_name']} ({query_result['node_type']})"
|
|
74
|
+
)
|
|
75
|
+
output_data(
|
|
76
|
+
f" 位置: 行 {query_result['start_line']}-{query_result['end_line']}"
|
|
77
|
+
)
|
|
78
|
+
output_data(f" 内容:\n{query_result['content']}")
|
|
79
|
+
else:
|
|
80
|
+
output_info(f"\nINFO: クエリにマッチする結果は見つかりませんでした。")
|
|
81
|
+
|
|
82
|
+
return 0
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Structure Command
|
|
5
|
+
|
|
6
|
+
Handles structure analysis functionality with appropriate Japanese output.
|
|
7
|
+
"""
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from ...output_manager import output_data, output_json, output_section
|
|
11
|
+
from .base_command import BaseCommand
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ...models import AnalysisResult
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class StructureCommand(BaseCommand):
|
|
18
|
+
"""Command for structure analysis with Japanese output."""
|
|
19
|
+
|
|
20
|
+
async def execute_async(self, language: str) -> int:
|
|
21
|
+
analysis_result = await self.analyze_file(language)
|
|
22
|
+
if not analysis_result:
|
|
23
|
+
return 1
|
|
24
|
+
|
|
25
|
+
self._output_structure_analysis(analysis_result)
|
|
26
|
+
return 0
|
|
27
|
+
|
|
28
|
+
def _output_structure_analysis(self, analysis_result: "AnalysisResult") -> None:
|
|
29
|
+
"""Output structure analysis results with appropriate Japanese header."""
|
|
30
|
+
output_section("構造解析結果")
|
|
31
|
+
|
|
32
|
+
# Convert to legacy structure format expected by tests
|
|
33
|
+
structure_dict = self._convert_to_legacy_format(analysis_result)
|
|
34
|
+
|
|
35
|
+
if self.args.output_format == "json":
|
|
36
|
+
output_json(structure_dict)
|
|
37
|
+
else:
|
|
38
|
+
self._output_text_format(structure_dict)
|
|
39
|
+
|
|
40
|
+
def _convert_to_legacy_format(self, analysis_result: "AnalysisResult") -> dict:
|
|
41
|
+
"""Convert AnalysisResult to legacy structure format expected by tests."""
|
|
42
|
+
import time
|
|
43
|
+
|
|
44
|
+
# Extract elements by type
|
|
45
|
+
classes = [e for e in analysis_result.elements if e.__class__.__name__ == 'Class']
|
|
46
|
+
methods = [e for e in analysis_result.elements if e.__class__.__name__ == 'Function']
|
|
47
|
+
fields = [e for e in analysis_result.elements if e.__class__.__name__ == 'Variable']
|
|
48
|
+
imports = [e for e in analysis_result.elements if e.__class__.__name__ == 'Import']
|
|
49
|
+
packages = [e for e in analysis_result.elements if e.__class__.__name__ == 'Package']
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
'file_path': analysis_result.file_path,
|
|
53
|
+
'language': analysis_result.language,
|
|
54
|
+
'package': {
|
|
55
|
+
'name': packages[0].name,
|
|
56
|
+
'line_range': {
|
|
57
|
+
'start': packages[0].start_line,
|
|
58
|
+
'end': packages[0].end_line
|
|
59
|
+
}
|
|
60
|
+
} if packages else None,
|
|
61
|
+
'classes': [{'name': getattr(c, 'name', 'unknown')} for c in classes],
|
|
62
|
+
'methods': [{'name': getattr(m, 'name', 'unknown')} for m in methods],
|
|
63
|
+
'fields': [{'name': getattr(f, 'name', 'unknown')} for f in fields],
|
|
64
|
+
'imports': [{
|
|
65
|
+
'name': getattr(i, 'name', 'unknown'),
|
|
66
|
+
'is_static': getattr(i, 'is_static', False),
|
|
67
|
+
'is_wildcard': getattr(i, 'is_wildcard', False),
|
|
68
|
+
'statement': getattr(i, 'import_statement', ''),
|
|
69
|
+
'line_range': {
|
|
70
|
+
'start': getattr(i, 'start_line', 0),
|
|
71
|
+
'end': getattr(i, 'end_line', 0)
|
|
72
|
+
}
|
|
73
|
+
} for i in imports],
|
|
74
|
+
'annotations': [],
|
|
75
|
+
'statistics': {
|
|
76
|
+
'class_count': len(classes),
|
|
77
|
+
'method_count': len(methods),
|
|
78
|
+
'field_count': len(fields),
|
|
79
|
+
'import_count': len(imports),
|
|
80
|
+
'total_lines': analysis_result.line_count,
|
|
81
|
+
'annotation_count': 0
|
|
82
|
+
},
|
|
83
|
+
'analysis_metadata': {
|
|
84
|
+
'analysis_time': getattr(analysis_result, 'analysis_time', 0.0),
|
|
85
|
+
'language': analysis_result.language,
|
|
86
|
+
'file_path': analysis_result.file_path,
|
|
87
|
+
'analyzer_version': '2.0.0',
|
|
88
|
+
'timestamp': time.time()
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
def _output_text_format(self, structure_dict: dict) -> None:
|
|
93
|
+
"""Output structure analysis in human-readable text format."""
|
|
94
|
+
output_data(f"ファイル: {structure_dict['file_path']}")
|
|
95
|
+
output_data(f"言語: {structure_dict['language']}")
|
|
96
|
+
|
|
97
|
+
if structure_dict['package']:
|
|
98
|
+
output_data(f"パッケージ: {structure_dict['package']['name']}")
|
|
99
|
+
|
|
100
|
+
stats = structure_dict['statistics']
|
|
101
|
+
output_data(f"統計:")
|
|
102
|
+
output_data(f" クラス数: {stats['class_count']}")
|
|
103
|
+
output_data(f" メソッド数: {stats['method_count']}")
|
|
104
|
+
output_data(f" フィールド数: {stats['field_count']}")
|
|
105
|
+
output_data(f" インポート数: {stats['import_count']}")
|
|
106
|
+
output_data(f" 総行数: {stats['total_lines']}")
|
|
107
|
+
|
|
108
|
+
if structure_dict['classes']:
|
|
109
|
+
output_data("クラス:")
|
|
110
|
+
for cls in structure_dict['classes']:
|
|
111
|
+
output_data(f" - {cls['name']}")
|
|
112
|
+
|
|
113
|
+
if structure_dict['methods']:
|
|
114
|
+
output_data("メソッド:")
|
|
115
|
+
for method in structure_dict['methods']:
|
|
116
|
+
output_data(f" - {method['name']}")
|
|
117
|
+
|
|
118
|
+
if structure_dict['fields']:
|
|
119
|
+
output_data("フィールド:")
|
|
120
|
+
for field in structure_dict['fields']:
|
|
121
|
+
output_data(f" - {field['name']}")
|