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.

Files changed (78) hide show
  1. tree_sitter_analyzer/__init__.py +121 -0
  2. tree_sitter_analyzer/__main__.py +12 -0
  3. tree_sitter_analyzer/api.py +539 -0
  4. tree_sitter_analyzer/cli/__init__.py +39 -0
  5. tree_sitter_analyzer/cli/__main__.py +13 -0
  6. tree_sitter_analyzer/cli/commands/__init__.py +27 -0
  7. tree_sitter_analyzer/cli/commands/advanced_command.py +88 -0
  8. tree_sitter_analyzer/cli/commands/base_command.py +155 -0
  9. tree_sitter_analyzer/cli/commands/default_command.py +19 -0
  10. tree_sitter_analyzer/cli/commands/partial_read_command.py +133 -0
  11. tree_sitter_analyzer/cli/commands/query_command.py +82 -0
  12. tree_sitter_analyzer/cli/commands/structure_command.py +121 -0
  13. tree_sitter_analyzer/cli/commands/summary_command.py +93 -0
  14. tree_sitter_analyzer/cli/commands/table_command.py +233 -0
  15. tree_sitter_analyzer/cli/info_commands.py +121 -0
  16. tree_sitter_analyzer/cli_main.py +276 -0
  17. tree_sitter_analyzer/core/__init__.py +20 -0
  18. tree_sitter_analyzer/core/analysis_engine.py +574 -0
  19. tree_sitter_analyzer/core/cache_service.py +330 -0
  20. tree_sitter_analyzer/core/engine.py +560 -0
  21. tree_sitter_analyzer/core/parser.py +288 -0
  22. tree_sitter_analyzer/core/query.py +502 -0
  23. tree_sitter_analyzer/encoding_utils.py +460 -0
  24. tree_sitter_analyzer/exceptions.py +340 -0
  25. tree_sitter_analyzer/file_handler.py +222 -0
  26. tree_sitter_analyzer/formatters/__init__.py +1 -0
  27. tree_sitter_analyzer/formatters/base_formatter.py +168 -0
  28. tree_sitter_analyzer/formatters/formatter_factory.py +74 -0
  29. tree_sitter_analyzer/formatters/java_formatter.py +270 -0
  30. tree_sitter_analyzer/formatters/python_formatter.py +235 -0
  31. tree_sitter_analyzer/interfaces/__init__.py +10 -0
  32. tree_sitter_analyzer/interfaces/cli.py +557 -0
  33. tree_sitter_analyzer/interfaces/cli_adapter.py +319 -0
  34. tree_sitter_analyzer/interfaces/mcp_adapter.py +170 -0
  35. tree_sitter_analyzer/interfaces/mcp_server.py +416 -0
  36. tree_sitter_analyzer/java_analyzer.py +219 -0
  37. tree_sitter_analyzer/language_detector.py +400 -0
  38. tree_sitter_analyzer/language_loader.py +228 -0
  39. tree_sitter_analyzer/languages/__init__.py +11 -0
  40. tree_sitter_analyzer/languages/java_plugin.py +1113 -0
  41. tree_sitter_analyzer/languages/python_plugin.py +712 -0
  42. tree_sitter_analyzer/mcp/__init__.py +32 -0
  43. tree_sitter_analyzer/mcp/resources/__init__.py +47 -0
  44. tree_sitter_analyzer/mcp/resources/code_file_resource.py +213 -0
  45. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +550 -0
  46. tree_sitter_analyzer/mcp/server.py +319 -0
  47. tree_sitter_analyzer/mcp/tools/__init__.py +36 -0
  48. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +558 -0
  49. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +245 -0
  50. tree_sitter_analyzer/mcp/tools/base_tool.py +55 -0
  51. tree_sitter_analyzer/mcp/tools/get_positions_tool.py +448 -0
  52. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +302 -0
  53. tree_sitter_analyzer/mcp/tools/table_format_tool.py +359 -0
  54. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +476 -0
  55. tree_sitter_analyzer/mcp/utils/__init__.py +106 -0
  56. tree_sitter_analyzer/mcp/utils/error_handler.py +549 -0
  57. tree_sitter_analyzer/models.py +481 -0
  58. tree_sitter_analyzer/output_manager.py +264 -0
  59. tree_sitter_analyzer/plugins/__init__.py +334 -0
  60. tree_sitter_analyzer/plugins/base.py +446 -0
  61. tree_sitter_analyzer/plugins/java_plugin.py +625 -0
  62. tree_sitter_analyzer/plugins/javascript_plugin.py +439 -0
  63. tree_sitter_analyzer/plugins/manager.py +355 -0
  64. tree_sitter_analyzer/plugins/plugin_loader.py +83 -0
  65. tree_sitter_analyzer/plugins/python_plugin.py +598 -0
  66. tree_sitter_analyzer/plugins/registry.py +366 -0
  67. tree_sitter_analyzer/queries/__init__.py +27 -0
  68. tree_sitter_analyzer/queries/java.py +394 -0
  69. tree_sitter_analyzer/queries/javascript.py +149 -0
  70. tree_sitter_analyzer/queries/python.py +286 -0
  71. tree_sitter_analyzer/queries/typescript.py +230 -0
  72. tree_sitter_analyzer/query_loader.py +260 -0
  73. tree_sitter_analyzer/table_formatter.py +448 -0
  74. tree_sitter_analyzer/utils.py +201 -0
  75. tree_sitter_analyzer-0.1.0.dist-info/METADATA +581 -0
  76. tree_sitter_analyzer-0.1.0.dist-info/RECORD +78 -0
  77. tree_sitter_analyzer-0.1.0.dist-info/WHEEL +4 -0
  78. 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']}")