tree-sitter-analyzer 0.8.3__py3-none-any.whl → 0.9.2__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 (62) hide show
  1. tree_sitter_analyzer/__init__.py +132 -132
  2. tree_sitter_analyzer/__main__.py +11 -11
  3. tree_sitter_analyzer/api.py +533 -533
  4. tree_sitter_analyzer/cli/__init__.py +39 -39
  5. tree_sitter_analyzer/cli/__main__.py +12 -12
  6. tree_sitter_analyzer/cli/commands/__init__.py +26 -26
  7. tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
  8. tree_sitter_analyzer/cli/commands/base_command.py +182 -180
  9. tree_sitter_analyzer/cli/commands/structure_command.py +138 -138
  10. tree_sitter_analyzer/cli/commands/summary_command.py +101 -101
  11. tree_sitter_analyzer/core/__init__.py +15 -15
  12. tree_sitter_analyzer/core/analysis_engine.py +74 -78
  13. tree_sitter_analyzer/core/cache_service.py +320 -320
  14. tree_sitter_analyzer/core/engine.py +566 -566
  15. tree_sitter_analyzer/core/parser.py +293 -293
  16. tree_sitter_analyzer/encoding_utils.py +459 -459
  17. tree_sitter_analyzer/file_handler.py +210 -210
  18. tree_sitter_analyzer/formatters/__init__.py +1 -1
  19. tree_sitter_analyzer/formatters/base_formatter.py +167 -167
  20. tree_sitter_analyzer/formatters/formatter_factory.py +78 -78
  21. tree_sitter_analyzer/formatters/java_formatter.py +18 -18
  22. tree_sitter_analyzer/formatters/python_formatter.py +19 -19
  23. tree_sitter_analyzer/interfaces/__init__.py +9 -9
  24. tree_sitter_analyzer/interfaces/cli.py +528 -528
  25. tree_sitter_analyzer/interfaces/cli_adapter.py +344 -343
  26. tree_sitter_analyzer/interfaces/mcp_adapter.py +206 -206
  27. tree_sitter_analyzer/language_detector.py +53 -53
  28. tree_sitter_analyzer/languages/__init__.py +10 -10
  29. tree_sitter_analyzer/languages/java_plugin.py +1 -1
  30. tree_sitter_analyzer/languages/javascript_plugin.py +446 -446
  31. tree_sitter_analyzer/languages/python_plugin.py +755 -755
  32. tree_sitter_analyzer/mcp/__init__.py +34 -31
  33. tree_sitter_analyzer/mcp/resources/__init__.py +44 -44
  34. tree_sitter_analyzer/mcp/resources/code_file_resource.py +209 -209
  35. tree_sitter_analyzer/mcp/server.py +623 -436
  36. tree_sitter_analyzer/mcp/tools/__init__.py +30 -30
  37. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +10 -6
  38. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +247 -242
  39. tree_sitter_analyzer/mcp/tools/base_tool.py +54 -54
  40. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +310 -308
  41. tree_sitter_analyzer/mcp/tools/table_format_tool.py +386 -379
  42. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +563 -559
  43. tree_sitter_analyzer/mcp/utils/__init__.py +107 -107
  44. tree_sitter_analyzer/models.py +10 -10
  45. tree_sitter_analyzer/output_manager.py +253 -253
  46. tree_sitter_analyzer/plugins/__init__.py +280 -280
  47. tree_sitter_analyzer/plugins/base.py +529 -529
  48. tree_sitter_analyzer/plugins/manager.py +379 -379
  49. tree_sitter_analyzer/queries/__init__.py +26 -26
  50. tree_sitter_analyzer/queries/java.py +391 -391
  51. tree_sitter_analyzer/queries/javascript.py +148 -148
  52. tree_sitter_analyzer/queries/python.py +285 -285
  53. tree_sitter_analyzer/queries/typescript.py +229 -229
  54. tree_sitter_analyzer/query_loader.py +257 -257
  55. tree_sitter_analyzer/security/boundary_manager.py +237 -279
  56. tree_sitter_analyzer/security/validator.py +60 -58
  57. tree_sitter_analyzer/utils.py +294 -277
  58. {tree_sitter_analyzer-0.8.3.dist-info → tree_sitter_analyzer-0.9.2.dist-info}/METADATA +28 -19
  59. tree_sitter_analyzer-0.9.2.dist-info/RECORD +77 -0
  60. {tree_sitter_analyzer-0.8.3.dist-info → tree_sitter_analyzer-0.9.2.dist-info}/entry_points.txt +1 -0
  61. tree_sitter_analyzer-0.8.3.dist-info/RECORD +0 -77
  62. {tree_sitter_analyzer-0.8.3.dist-info → tree_sitter_analyzer-0.9.2.dist-info}/WHEEL +0 -0
@@ -1,180 +1,182 @@
1
- #!/usr/bin/env python3
2
- """
3
- Base Command Class
4
-
5
- Abstract base class for all CLI commands implementing the Command Pattern.
6
- """
7
-
8
- import asyncio
9
- from abc import ABC, abstractmethod
10
- from argparse import Namespace
11
- from typing import Optional
12
-
13
- from ...core.analysis_engine import AnalysisRequest, get_analysis_engine
14
- from ...file_handler import read_file_partial
15
- from ...language_detector import detect_language_from_file, is_language_supported
16
- from ...models import AnalysisResult
17
- from ...output_manager import output_error, output_info
18
- from ...project_detector import detect_project_root
19
- from ...security import SecurityValidator
20
-
21
-
22
- class BaseCommand(ABC):
23
- """
24
- Base class for all CLI commands.
25
-
26
- Implements common functionality like file validation, language detection,
27
- and analysis engine interaction.
28
- """
29
-
30
- def __init__(self, args: Namespace):
31
- """Initialize command with parsed arguments."""
32
- self.args = args
33
-
34
- # Detect project root with priority handling
35
- file_path = getattr(args, 'file_path', None)
36
- explicit_root = getattr(args, 'project_root', None)
37
- self.project_root = detect_project_root(file_path, explicit_root)
38
-
39
- # Initialize components with project root
40
- self.analysis_engine = get_analysis_engine(self.project_root)
41
- self.security_validator = SecurityValidator(self.project_root)
42
-
43
- def validate_file(self) -> bool:
44
- """Validate input file exists and is accessible."""
45
- if not hasattr(self.args, "file_path") or not self.args.file_path:
46
- output_error("File path not specified.")
47
- return False
48
-
49
- # Security validation
50
- is_valid, error_msg = self.security_validator.validate_file_path(
51
- self.args.file_path, base_path=self.project_root
52
- )
53
- if not is_valid:
54
- output_error(f"Invalid file path: {error_msg}")
55
- return False
56
-
57
- import os
58
-
59
- if not os.path.exists(self.args.file_path):
60
- output_error(f"File not found: {self.args.file_path}")
61
- return False
62
-
63
- return True
64
-
65
- def detect_language(self) -> str | None:
66
- """Detect or validate the target language."""
67
- if hasattr(self.args, "language") and self.args.language:
68
- # Sanitize language input
69
- sanitized_language = self.security_validator.sanitize_input(self.args.language, max_length=50)
70
- target_language = sanitized_language.lower()
71
- if (not hasattr(self.args, "table") or not self.args.table) and (
72
- not hasattr(self.args, "quiet") or not self.args.quiet
73
- ):
74
- output_info(f"INFO: Language explicitly specified: {target_language}")
75
- else:
76
- target_language = detect_language_from_file(self.args.file_path)
77
- if target_language == "unknown":
78
- output_error(
79
- f"ERROR: Could not determine language for file '{self.args.file_path}'."
80
- )
81
- return None
82
- else:
83
- if (not hasattr(self.args, "table") or not self.args.table) and (
84
- not hasattr(self.args, "quiet") or not self.args.quiet
85
- ):
86
- output_info(
87
- f"INFO: Language auto-detected from extension: {target_language}"
88
- )
89
-
90
- # Language support validation
91
- if not is_language_supported(target_language):
92
- if target_language != "java":
93
- if (not hasattr(self.args, "table") or not self.args.table) and (
94
- not hasattr(self.args, "quiet") or not self.args.quiet
95
- ):
96
- output_info(
97
- "INFO: Trying with Java analysis engine. May not work correctly."
98
- )
99
- target_language = "java" # Fallback
100
-
101
- return str(target_language) if target_language else None
102
-
103
- async def analyze_file(self, language: str) -> Optional["AnalysisResult"]:
104
- """Perform file analysis using the unified analysis engine."""
105
- try:
106
- # Handle partial read if enabled
107
- if hasattr(self.args, "partial_read") and self.args.partial_read:
108
- try:
109
- partial_content = read_file_partial(
110
- self.args.file_path,
111
- start_line=self.args.start_line,
112
- end_line=getattr(self.args, "end_line", None),
113
- start_column=getattr(self.args, "start_column", None),
114
- end_column=getattr(self.args, "end_column", None),
115
- )
116
- if partial_content is None:
117
- output_error("Failed to read file partially")
118
- return None
119
- except Exception as e:
120
- output_error(f"Failed to read file partially: {e}")
121
- return None
122
-
123
- request = AnalysisRequest(
124
- file_path=self.args.file_path,
125
- language=language,
126
- include_complexity=True,
127
- include_details=True,
128
- )
129
- analysis_result = await self.analysis_engine.analyze(request)
130
-
131
- if not analysis_result or not analysis_result.success:
132
- error_msg = (
133
- analysis_result.error_message
134
- if analysis_result
135
- else "Unknown error"
136
- )
137
- output_error(f"Analysis failed: {error_msg}")
138
- return None
139
-
140
- return analysis_result
141
-
142
- except Exception as e:
143
- output_error(f"An error occurred during analysis: {e}")
144
- return None
145
-
146
- def execute(self) -> int:
147
- """
148
- Execute the command.
149
-
150
- Returns:
151
- int: Exit code (0 for success, 1 for failure)
152
- """
153
- # Validate inputs
154
- if not self.validate_file():
155
- return 1
156
-
157
- # Detect language
158
- language = self.detect_language()
159
- if not language:
160
- return 1
161
-
162
- # Execute the specific command
163
- try:
164
- return asyncio.run(self.execute_async(language))
165
- except Exception as e:
166
- output_error(f"An error occurred during command execution: {e}")
167
- return 1
168
-
169
- @abstractmethod
170
- async def execute_async(self, language: str) -> int:
171
- """
172
- Execute the command asynchronously.
173
-
174
- Args:
175
- language: Detected/specified target language
176
-
177
- Returns:
178
- int: Exit code (0 for success, 1 for failure)
179
- """
180
- pass
1
+ #!/usr/bin/env python3
2
+ """
3
+ Base Command Class
4
+
5
+ Abstract base class for all CLI commands implementing the Command Pattern.
6
+ """
7
+
8
+ import asyncio
9
+ from abc import ABC, abstractmethod
10
+ from argparse import Namespace
11
+ from typing import Optional
12
+
13
+ from ...core.analysis_engine import AnalysisRequest, get_analysis_engine
14
+ from ...file_handler import read_file_partial
15
+ from ...language_detector import detect_language_from_file, is_language_supported
16
+ from ...models import AnalysisResult
17
+ from ...output_manager import output_error, output_info
18
+ from ...project_detector import detect_project_root
19
+ from ...security import SecurityValidator
20
+
21
+
22
+ class BaseCommand(ABC):
23
+ """
24
+ Base class for all CLI commands.
25
+
26
+ Implements common functionality like file validation, language detection,
27
+ and analysis engine interaction.
28
+ """
29
+
30
+ def __init__(self, args: Namespace):
31
+ """Initialize command with parsed arguments."""
32
+ self.args = args
33
+
34
+ # Detect project root with priority handling
35
+ file_path = getattr(args, "file_path", None)
36
+ explicit_root = getattr(args, "project_root", None)
37
+ self.project_root = detect_project_root(file_path, explicit_root)
38
+
39
+ # Initialize components with project root
40
+ self.analysis_engine = get_analysis_engine(self.project_root)
41
+ self.security_validator = SecurityValidator(self.project_root)
42
+
43
+ def validate_file(self) -> bool:
44
+ """Validate input file exists and is accessible."""
45
+ if not hasattr(self.args, "file_path") or not self.args.file_path:
46
+ output_error("File path not specified.")
47
+ return False
48
+
49
+ # Security validation
50
+ is_valid, error_msg = self.security_validator.validate_file_path(
51
+ self.args.file_path
52
+ )
53
+ if not is_valid:
54
+ output_error(f"Invalid file path: {error_msg}")
55
+ return False
56
+
57
+ import os
58
+
59
+ if not os.path.exists(self.args.file_path):
60
+ output_error("Invalid file path: file does not exist")
61
+ return False
62
+
63
+ return True
64
+
65
+ def detect_language(self) -> str | None:
66
+ """Detect or validate the target language."""
67
+ if hasattr(self.args, "language") and self.args.language:
68
+ # Sanitize language input
69
+ sanitized_language = self.security_validator.sanitize_input(
70
+ self.args.language, max_length=50
71
+ )
72
+ target_language = sanitized_language.lower()
73
+ if (not hasattr(self.args, "table") or not self.args.table) and (
74
+ not hasattr(self.args, "quiet") or not self.args.quiet
75
+ ):
76
+ output_info(f"INFO: Language explicitly specified: {target_language}")
77
+ else:
78
+ target_language = detect_language_from_file(self.args.file_path)
79
+ if target_language == "unknown":
80
+ output_error(
81
+ f"ERROR: Could not determine language for file '{self.args.file_path}'."
82
+ )
83
+ return None
84
+ else:
85
+ if (not hasattr(self.args, "table") or not self.args.table) and (
86
+ not hasattr(self.args, "quiet") or not self.args.quiet
87
+ ):
88
+ output_info(
89
+ f"INFO: Language auto-detected from extension: {target_language}"
90
+ )
91
+
92
+ # Language support validation
93
+ if not is_language_supported(target_language):
94
+ if target_language != "java":
95
+ if (not hasattr(self.args, "table") or not self.args.table) and (
96
+ not hasattr(self.args, "quiet") or not self.args.quiet
97
+ ):
98
+ output_info(
99
+ "INFO: Trying with Java analysis engine. May not work correctly."
100
+ )
101
+ target_language = "java" # Fallback
102
+
103
+ return str(target_language) if target_language else None
104
+
105
+ async def analyze_file(self, language: str) -> Optional["AnalysisResult"]:
106
+ """Perform file analysis using the unified analysis engine."""
107
+ try:
108
+ # Handle partial read if enabled
109
+ if hasattr(self.args, "partial_read") and self.args.partial_read:
110
+ try:
111
+ partial_content = read_file_partial(
112
+ self.args.file_path,
113
+ start_line=self.args.start_line,
114
+ end_line=getattr(self.args, "end_line", None),
115
+ start_column=getattr(self.args, "start_column", None),
116
+ end_column=getattr(self.args, "end_column", None),
117
+ )
118
+ if partial_content is None:
119
+ output_error("Failed to read file partially")
120
+ return None
121
+ except Exception as e:
122
+ output_error(f"Failed to read file partially: {e}")
123
+ return None
124
+
125
+ request = AnalysisRequest(
126
+ file_path=self.args.file_path,
127
+ language=language,
128
+ include_complexity=True,
129
+ include_details=True,
130
+ )
131
+ analysis_result = await self.analysis_engine.analyze(request)
132
+
133
+ if not analysis_result or not analysis_result.success:
134
+ error_msg = (
135
+ analysis_result.error_message
136
+ if analysis_result
137
+ else "Unknown error"
138
+ )
139
+ output_error(f"Analysis failed: {error_msg}")
140
+ return None
141
+
142
+ return analysis_result
143
+
144
+ except Exception as e:
145
+ output_error(f"An error occurred during analysis: {e}")
146
+ return None
147
+
148
+ def execute(self) -> int:
149
+ """
150
+ Execute the command.
151
+
152
+ Returns:
153
+ int: Exit code (0 for success, 1 for failure)
154
+ """
155
+ # Validate inputs
156
+ if not self.validate_file():
157
+ return 1
158
+
159
+ # Detect language
160
+ language = self.detect_language()
161
+ if not language:
162
+ return 1
163
+
164
+ # Execute the specific command
165
+ try:
166
+ return asyncio.run(self.execute_async(language))
167
+ except Exception as e:
168
+ output_error(f"An error occurred during command execution: {e}")
169
+ return 1
170
+
171
+ @abstractmethod
172
+ async def execute_async(self, language: str) -> int:
173
+ """
174
+ Execute the command asynchronously.
175
+
176
+ Args:
177
+ language: Detected/specified target language
178
+
179
+ Returns:
180
+ int: Exit code (0 for success, 1 for failure)
181
+ """
182
+ pass
@@ -1,138 +1,138 @@
1
- #!/usr/bin/env python3
2
- """
3
- Structure Command
4
-
5
- Handles structure analysis functionality with appropriate Japanese output.
6
- """
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("Structure Analysis Results")
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 = [
46
- e for e in analysis_result.elements if e.__class__.__name__ == "Class"
47
- ]
48
- methods = [
49
- e for e in analysis_result.elements if e.__class__.__name__ == "Function"
50
- ]
51
- fields = [
52
- e for e in analysis_result.elements if e.__class__.__name__ == "Variable"
53
- ]
54
- imports = [
55
- e for e in analysis_result.elements if e.__class__.__name__ == "Import"
56
- ]
57
- packages = [
58
- e for e in analysis_result.elements if e.__class__.__name__ == "Package"
59
- ]
60
-
61
- return {
62
- "file_path": analysis_result.file_path,
63
- "language": analysis_result.language,
64
- "package": (
65
- {
66
- "name": packages[0].name,
67
- "line_range": {
68
- "start": packages[0].start_line,
69
- "end": packages[0].end_line,
70
- },
71
- }
72
- if packages
73
- else None
74
- ),
75
- "classes": [{"name": getattr(c, "name", "unknown")} for c in classes],
76
- "methods": [{"name": getattr(m, "name", "unknown")} for m in methods],
77
- "fields": [{"name": getattr(f, "name", "unknown")} for f in fields],
78
- "imports": [
79
- {
80
- "name": getattr(i, "name", "unknown"),
81
- "is_static": getattr(i, "is_static", False),
82
- "is_wildcard": getattr(i, "is_wildcard", False),
83
- "statement": getattr(i, "import_statement", ""),
84
- "line_range": {
85
- "start": getattr(i, "start_line", 0),
86
- "end": getattr(i, "end_line", 0),
87
- },
88
- }
89
- for i in imports
90
- ],
91
- "annotations": [],
92
- "statistics": {
93
- "class_count": len(classes),
94
- "method_count": len(methods),
95
- "field_count": len(fields),
96
- "import_count": len(imports),
97
- "total_lines": analysis_result.line_count,
98
- "annotation_count": 0,
99
- },
100
- "analysis_metadata": {
101
- "analysis_time": getattr(analysis_result, "analysis_time", 0.0),
102
- "language": analysis_result.language,
103
- "file_path": analysis_result.file_path,
104
- "analyzer_version": "2.0.0",
105
- "timestamp": time.time(),
106
- },
107
- }
108
-
109
- def _output_text_format(self, structure_dict: dict) -> None:
110
- """Output structure analysis in human-readable text format."""
111
- output_data(f"File: {structure_dict['file_path']}")
112
- output_data(f"Language: {structure_dict['language']}")
113
-
114
- if structure_dict["package"]:
115
- output_data(f"Package: {structure_dict['package']['name']}")
116
-
117
- stats = structure_dict["statistics"]
118
- output_data("Statistics:")
119
- output_data(f" Classes: {stats['class_count']}")
120
- output_data(f" Methods: {stats['method_count']}")
121
- output_data(f" Fields: {stats['field_count']}")
122
- output_data(f" Imports: {stats['import_count']}")
123
- output_data(f" Total lines: {stats['total_lines']}")
124
-
125
- if structure_dict["classes"]:
126
- output_data("Classes:")
127
- for cls in structure_dict["classes"]:
128
- output_data(f" - {cls['name']}")
129
-
130
- if structure_dict["methods"]:
131
- output_data("Methods:")
132
- for method in structure_dict["methods"]:
133
- output_data(f" - {method['name']}")
134
-
135
- if structure_dict["fields"]:
136
- output_data("Fields:")
137
- for field in structure_dict["fields"]:
138
- output_data(f" - {field['name']}")
1
+ #!/usr/bin/env python3
2
+ """
3
+ Structure Command
4
+
5
+ Handles structure analysis functionality with appropriate Japanese output.
6
+ """
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("Structure Analysis Results")
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 = [
46
+ e for e in analysis_result.elements if e.__class__.__name__ == "Class"
47
+ ]
48
+ methods = [
49
+ e for e in analysis_result.elements if e.__class__.__name__ == "Function"
50
+ ]
51
+ fields = [
52
+ e for e in analysis_result.elements if e.__class__.__name__ == "Variable"
53
+ ]
54
+ imports = [
55
+ e for e in analysis_result.elements if e.__class__.__name__ == "Import"
56
+ ]
57
+ packages = [
58
+ e for e in analysis_result.elements if e.__class__.__name__ == "Package"
59
+ ]
60
+
61
+ return {
62
+ "file_path": analysis_result.file_path,
63
+ "language": analysis_result.language,
64
+ "package": (
65
+ {
66
+ "name": packages[0].name,
67
+ "line_range": {
68
+ "start": packages[0].start_line,
69
+ "end": packages[0].end_line,
70
+ },
71
+ }
72
+ if packages
73
+ else None
74
+ ),
75
+ "classes": [{"name": getattr(c, "name", "unknown")} for c in classes],
76
+ "methods": [{"name": getattr(m, "name", "unknown")} for m in methods],
77
+ "fields": [{"name": getattr(f, "name", "unknown")} for f in fields],
78
+ "imports": [
79
+ {
80
+ "name": getattr(i, "name", "unknown"),
81
+ "is_static": getattr(i, "is_static", False),
82
+ "is_wildcard": getattr(i, "is_wildcard", False),
83
+ "statement": getattr(i, "import_statement", ""),
84
+ "line_range": {
85
+ "start": getattr(i, "start_line", 0),
86
+ "end": getattr(i, "end_line", 0),
87
+ },
88
+ }
89
+ for i in imports
90
+ ],
91
+ "annotations": [],
92
+ "statistics": {
93
+ "class_count": len(classes),
94
+ "method_count": len(methods),
95
+ "field_count": len(fields),
96
+ "import_count": len(imports),
97
+ "total_lines": analysis_result.line_count,
98
+ "annotation_count": 0,
99
+ },
100
+ "analysis_metadata": {
101
+ "analysis_time": getattr(analysis_result, "analysis_time", 0.0),
102
+ "language": analysis_result.language,
103
+ "file_path": analysis_result.file_path,
104
+ "analyzer_version": "2.0.0",
105
+ "timestamp": time.time(),
106
+ },
107
+ }
108
+
109
+ def _output_text_format(self, structure_dict: dict) -> None:
110
+ """Output structure analysis in human-readable text format."""
111
+ output_data(f"File: {structure_dict['file_path']}")
112
+ output_data(f"Language: {structure_dict['language']}")
113
+
114
+ if structure_dict["package"]:
115
+ output_data(f"Package: {structure_dict['package']['name']}")
116
+
117
+ stats = structure_dict["statistics"]
118
+ output_data("Statistics:")
119
+ output_data(f" Classes: {stats['class_count']}")
120
+ output_data(f" Methods: {stats['method_count']}")
121
+ output_data(f" Fields: {stats['field_count']}")
122
+ output_data(f" Imports: {stats['import_count']}")
123
+ output_data(f" Total lines: {stats['total_lines']}")
124
+
125
+ if structure_dict["classes"]:
126
+ output_data("Classes:")
127
+ for cls in structure_dict["classes"]:
128
+ output_data(f" - {cls['name']}")
129
+
130
+ if structure_dict["methods"]:
131
+ output_data("Methods:")
132
+ for method in structure_dict["methods"]:
133
+ output_data(f" - {method['name']}")
134
+
135
+ if structure_dict["fields"]:
136
+ output_data("Fields:")
137
+ for field in structure_dict["fields"]:
138
+ output_data(f" - {field['name']}")