tree-sitter-analyzer 1.9.17.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.
- tree_sitter_analyzer/__init__.py +132 -0
- tree_sitter_analyzer/__main__.py +11 -0
- tree_sitter_analyzer/api.py +853 -0
- tree_sitter_analyzer/cli/__init__.py +39 -0
- tree_sitter_analyzer/cli/__main__.py +12 -0
- tree_sitter_analyzer/cli/argument_validator.py +89 -0
- tree_sitter_analyzer/cli/commands/__init__.py +26 -0
- tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
- tree_sitter_analyzer/cli/commands/base_command.py +181 -0
- tree_sitter_analyzer/cli/commands/default_command.py +18 -0
- tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
- tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
- tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
- tree_sitter_analyzer/cli/commands/query_command.py +109 -0
- tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
- tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
- tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
- tree_sitter_analyzer/cli/commands/table_command.py +414 -0
- tree_sitter_analyzer/cli/info_commands.py +124 -0
- tree_sitter_analyzer/cli_main.py +472 -0
- tree_sitter_analyzer/constants.py +85 -0
- tree_sitter_analyzer/core/__init__.py +15 -0
- tree_sitter_analyzer/core/analysis_engine.py +580 -0
- tree_sitter_analyzer/core/cache_service.py +333 -0
- tree_sitter_analyzer/core/engine.py +585 -0
- tree_sitter_analyzer/core/parser.py +293 -0
- tree_sitter_analyzer/core/query.py +605 -0
- tree_sitter_analyzer/core/query_filter.py +200 -0
- tree_sitter_analyzer/core/query_service.py +340 -0
- tree_sitter_analyzer/encoding_utils.py +530 -0
- tree_sitter_analyzer/exceptions.py +747 -0
- tree_sitter_analyzer/file_handler.py +246 -0
- tree_sitter_analyzer/formatters/__init__.py +1 -0
- tree_sitter_analyzer/formatters/base_formatter.py +201 -0
- tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
- tree_sitter_analyzer/formatters/formatter_config.py +197 -0
- tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
- tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
- tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
- tree_sitter_analyzer/formatters/go_formatter.py +368 -0
- tree_sitter_analyzer/formatters/html_formatter.py +498 -0
- tree_sitter_analyzer/formatters/java_formatter.py +423 -0
- tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
- tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
- tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
- tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
- tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
- tree_sitter_analyzer/formatters/php_formatter.py +301 -0
- tree_sitter_analyzer/formatters/python_formatter.py +830 -0
- tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
- tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
- tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
- tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
- tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
- tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
- tree_sitter_analyzer/interfaces/__init__.py +9 -0
- tree_sitter_analyzer/interfaces/cli.py +535 -0
- tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
- tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
- tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
- tree_sitter_analyzer/language_detector.py +553 -0
- tree_sitter_analyzer/language_loader.py +271 -0
- tree_sitter_analyzer/languages/__init__.py +10 -0
- tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
- tree_sitter_analyzer/languages/css_plugin.py +449 -0
- tree_sitter_analyzer/languages/go_plugin.py +836 -0
- tree_sitter_analyzer/languages/html_plugin.py +496 -0
- tree_sitter_analyzer/languages/java_plugin.py +1299 -0
- tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
- tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
- tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
- tree_sitter_analyzer/languages/php_plugin.py +862 -0
- tree_sitter_analyzer/languages/python_plugin.py +1636 -0
- tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
- tree_sitter_analyzer/languages/rust_plugin.py +673 -0
- tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
- tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
- tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
- tree_sitter_analyzer/legacy_table_formatter.py +860 -0
- tree_sitter_analyzer/mcp/__init__.py +34 -0
- tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
- tree_sitter_analyzer/mcp/server.py +869 -0
- tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
- tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
- tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
- tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
- tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
- tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
- tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
- tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
- tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
- tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
- tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
- tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
- tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
- tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
- tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
- tree_sitter_analyzer/models.py +840 -0
- tree_sitter_analyzer/mypy_current_errors.txt +2 -0
- tree_sitter_analyzer/output_manager.py +255 -0
- tree_sitter_analyzer/platform_compat/__init__.py +3 -0
- tree_sitter_analyzer/platform_compat/adapter.py +324 -0
- tree_sitter_analyzer/platform_compat/compare.py +224 -0
- tree_sitter_analyzer/platform_compat/detector.py +67 -0
- tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
- tree_sitter_analyzer/platform_compat/profiles.py +217 -0
- tree_sitter_analyzer/platform_compat/record.py +55 -0
- tree_sitter_analyzer/platform_compat/recorder.py +155 -0
- tree_sitter_analyzer/platform_compat/report.py +92 -0
- tree_sitter_analyzer/plugins/__init__.py +280 -0
- tree_sitter_analyzer/plugins/base.py +647 -0
- tree_sitter_analyzer/plugins/manager.py +384 -0
- tree_sitter_analyzer/project_detector.py +328 -0
- tree_sitter_analyzer/queries/__init__.py +27 -0
- tree_sitter_analyzer/queries/csharp.py +216 -0
- tree_sitter_analyzer/queries/css.py +615 -0
- tree_sitter_analyzer/queries/go.py +275 -0
- tree_sitter_analyzer/queries/html.py +543 -0
- tree_sitter_analyzer/queries/java.py +402 -0
- tree_sitter_analyzer/queries/javascript.py +724 -0
- tree_sitter_analyzer/queries/kotlin.py +192 -0
- tree_sitter_analyzer/queries/markdown.py +258 -0
- tree_sitter_analyzer/queries/php.py +95 -0
- tree_sitter_analyzer/queries/python.py +859 -0
- tree_sitter_analyzer/queries/ruby.py +92 -0
- tree_sitter_analyzer/queries/rust.py +223 -0
- tree_sitter_analyzer/queries/sql.py +555 -0
- tree_sitter_analyzer/queries/typescript.py +871 -0
- tree_sitter_analyzer/queries/yaml.py +236 -0
- tree_sitter_analyzer/query_loader.py +272 -0
- tree_sitter_analyzer/security/__init__.py +22 -0
- tree_sitter_analyzer/security/boundary_manager.py +277 -0
- tree_sitter_analyzer/security/regex_checker.py +297 -0
- tree_sitter_analyzer/security/validator.py +599 -0
- tree_sitter_analyzer/table_formatter.py +782 -0
- tree_sitter_analyzer/utils/__init__.py +53 -0
- tree_sitter_analyzer/utils/logging.py +433 -0
- tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/entry_points.txt +25 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Ruby-specific table formatter.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from .base_formatter import BaseTableFormatter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RubyTableFormatter(BaseTableFormatter):
|
|
12
|
+
"""Table formatter specialized for Ruby"""
|
|
13
|
+
|
|
14
|
+
def _format_full_table(self, data: dict[str, Any]) -> str:
|
|
15
|
+
"""Full table format for Ruby"""
|
|
16
|
+
lines = []
|
|
17
|
+
|
|
18
|
+
# Header - Ruby (multi-class supported)
|
|
19
|
+
classes = data.get("classes", [])
|
|
20
|
+
|
|
21
|
+
if len(classes) > 1:
|
|
22
|
+
# If multiple classes exist, use filename
|
|
23
|
+
file_name = data.get("file_path", "Unknown").split("/")[-1].split("\\")[-1]
|
|
24
|
+
lines.append(f"# {file_name}")
|
|
25
|
+
else:
|
|
26
|
+
# Single class: use class name
|
|
27
|
+
class_name = classes[0].get("name", "Unknown") if classes else "Unknown"
|
|
28
|
+
lines.append(f"# {class_name}")
|
|
29
|
+
lines.append("")
|
|
30
|
+
|
|
31
|
+
# Require statements (imports)
|
|
32
|
+
imports = data.get("imports", [])
|
|
33
|
+
if imports:
|
|
34
|
+
lines.append("## Imports")
|
|
35
|
+
lines.append("```ruby")
|
|
36
|
+
for imp in imports:
|
|
37
|
+
import_text = imp.get("raw_text", "").strip()
|
|
38
|
+
if import_text:
|
|
39
|
+
lines.append(import_text)
|
|
40
|
+
lines.append("```")
|
|
41
|
+
lines.append("")
|
|
42
|
+
|
|
43
|
+
# Class Info - Ruby (multi-class aware)
|
|
44
|
+
if len(classes) > 1:
|
|
45
|
+
lines.append("## Classes Overview")
|
|
46
|
+
lines.append("| Class | Type | Lines | Methods | Constants |")
|
|
47
|
+
lines.append("|-------|------|-------|---------|-----------|")
|
|
48
|
+
|
|
49
|
+
for class_info in classes:
|
|
50
|
+
name = str(class_info.get("name", "Unknown"))
|
|
51
|
+
class_type = str(class_info.get("class_type", "class"))
|
|
52
|
+
line_range = class_info.get("line_range", {})
|
|
53
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
54
|
+
|
|
55
|
+
# Count methods/constants within the class range
|
|
56
|
+
class_methods = [
|
|
57
|
+
m
|
|
58
|
+
for m in data.get("methods", [])
|
|
59
|
+
if line_range.get("start", 0)
|
|
60
|
+
<= m.get("line_range", {}).get("start", 0)
|
|
61
|
+
<= line_range.get("end", 0)
|
|
62
|
+
]
|
|
63
|
+
class_constants = [
|
|
64
|
+
f
|
|
65
|
+
for f in data.get("fields", [])
|
|
66
|
+
if f.get("is_constant", False)
|
|
67
|
+
and line_range.get("start", 0)
|
|
68
|
+
<= f.get("line_range", {}).get("start", 0)
|
|
69
|
+
<= line_range.get("end", 0)
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
lines.append(
|
|
73
|
+
f"| {name} | {class_type} | {lines_str} | {len(class_methods)} | {len(class_constants)} |"
|
|
74
|
+
)
|
|
75
|
+
else:
|
|
76
|
+
# Single class details
|
|
77
|
+
lines.append("## Info")
|
|
78
|
+
lines.append("| Property | Value |")
|
|
79
|
+
lines.append("|----------|-------|")
|
|
80
|
+
|
|
81
|
+
class_info = data.get("classes", [{}])[0] if data.get("classes") else {}
|
|
82
|
+
stats = data.get("statistics") or {}
|
|
83
|
+
|
|
84
|
+
lines.append(f"| Type | {str(class_info.get('class_type', 'class'))} |")
|
|
85
|
+
lines.append(f"| Methods | {stats.get('method_count', 0)} |")
|
|
86
|
+
lines.append(
|
|
87
|
+
f"| Constants | {sum(1 for f in data.get('fields', []) if f.get('is_constant', False))} |"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
lines.append("")
|
|
91
|
+
|
|
92
|
+
# Methods
|
|
93
|
+
methods = data.get("methods", [])
|
|
94
|
+
if methods:
|
|
95
|
+
lines.append("## Methods")
|
|
96
|
+
lines.append("| Name | Type | Lines | Parameters |")
|
|
97
|
+
lines.append("|------|------|-------|------------|")
|
|
98
|
+
|
|
99
|
+
for method in methods:
|
|
100
|
+
name = str(method.get("name", "Unknown"))
|
|
101
|
+
metadata = method.get("metadata", {})
|
|
102
|
+
method_type = metadata.get("method_type", "instance")
|
|
103
|
+
attr_type = metadata.get("attr_type", "")
|
|
104
|
+
|
|
105
|
+
# Determine display type
|
|
106
|
+
if attr_type:
|
|
107
|
+
display_type = attr_type
|
|
108
|
+
elif method_type == "class":
|
|
109
|
+
display_type = "class method"
|
|
110
|
+
else:
|
|
111
|
+
display_type = "instance method"
|
|
112
|
+
|
|
113
|
+
line_range = method.get("line_range", {})
|
|
114
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
115
|
+
params = ", ".join(method.get("parameters", []))
|
|
116
|
+
|
|
117
|
+
lines.append(f"| {name} | {display_type} | {lines_str} | {params} |")
|
|
118
|
+
|
|
119
|
+
lines.append("")
|
|
120
|
+
|
|
121
|
+
# Variables (Constants, Instance Variables, Class Variables)
|
|
122
|
+
fields = data.get("fields", [])
|
|
123
|
+
if fields:
|
|
124
|
+
lines.append("## Variables")
|
|
125
|
+
lines.append("| Name | Type | Visibility | Lines |")
|
|
126
|
+
lines.append("|------|------|------------|-------|")
|
|
127
|
+
|
|
128
|
+
for field in fields:
|
|
129
|
+
name = str(field.get("name", "Unknown"))
|
|
130
|
+
metadata = field.get("metadata", {})
|
|
131
|
+
|
|
132
|
+
# Determine variable type
|
|
133
|
+
if field.get("is_constant", False):
|
|
134
|
+
var_type = "constant"
|
|
135
|
+
elif metadata.get("is_class_variable", False):
|
|
136
|
+
var_type = "class variable"
|
|
137
|
+
elif metadata.get("is_instance_variable", False):
|
|
138
|
+
var_type = "instance variable"
|
|
139
|
+
elif metadata.get("is_global", False):
|
|
140
|
+
var_type = "global variable"
|
|
141
|
+
else:
|
|
142
|
+
var_type = "variable"
|
|
143
|
+
|
|
144
|
+
visibility = str(field.get("visibility", "public"))
|
|
145
|
+
line_range = field.get("line_range", {})
|
|
146
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
147
|
+
|
|
148
|
+
lines.append(f"| {name} | {var_type} | {visibility} | {lines_str} |")
|
|
149
|
+
|
|
150
|
+
lines.append("")
|
|
151
|
+
|
|
152
|
+
return "\n".join(lines)
|
|
153
|
+
|
|
154
|
+
def _format_compact_table(self, data: dict[str, Any]) -> str:
|
|
155
|
+
"""Compact table format for Ruby"""
|
|
156
|
+
lines = []
|
|
157
|
+
|
|
158
|
+
# Header
|
|
159
|
+
classes = data.get("classes", [])
|
|
160
|
+
|
|
161
|
+
if len(classes) > 1:
|
|
162
|
+
file_name = data.get("file_path", "Unknown").split("/")[-1].split("\\")[-1]
|
|
163
|
+
lines.append(f"# {file_name}")
|
|
164
|
+
else:
|
|
165
|
+
class_name = classes[0].get("name", "Unknown") if classes else "Unknown"
|
|
166
|
+
lines.append(f"# {class_name}")
|
|
167
|
+
lines.append("")
|
|
168
|
+
|
|
169
|
+
# Statistics
|
|
170
|
+
stats = data.get("statistics") or {}
|
|
171
|
+
lines.append("## Statistics")
|
|
172
|
+
lines.append(f"- Classes: {stats.get('class_count', 0)}")
|
|
173
|
+
lines.append(f"- Methods: {stats.get('method_count', 0)}")
|
|
174
|
+
lines.append(
|
|
175
|
+
f"- Constants: {sum(1 for f in data.get('fields', []) if f.get('is_constant', False))}"
|
|
176
|
+
)
|
|
177
|
+
lines.append(f"- Imports: {len(data.get('imports', []))}")
|
|
178
|
+
lines.append("")
|
|
179
|
+
|
|
180
|
+
# Classes
|
|
181
|
+
if classes:
|
|
182
|
+
lines.append("## Classes")
|
|
183
|
+
for class_info in classes:
|
|
184
|
+
name = str(class_info.get("name", "Unknown"))
|
|
185
|
+
class_type = str(class_info.get("class_type", "class"))
|
|
186
|
+
line_range = class_info.get("line_range", {})
|
|
187
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
188
|
+
lines.append(f"- **{name}** ({class_type}) - Lines {lines_str}")
|
|
189
|
+
lines.append("")
|
|
190
|
+
|
|
191
|
+
# Methods
|
|
192
|
+
methods = data.get("methods", [])
|
|
193
|
+
if methods:
|
|
194
|
+
lines.append("## Methods")
|
|
195
|
+
for method in methods:
|
|
196
|
+
name = str(method.get("name", "Unknown"))
|
|
197
|
+
metadata = method.get("metadata", {})
|
|
198
|
+
method_type = metadata.get("method_type", "instance")
|
|
199
|
+
line_range = method.get("line_range", {})
|
|
200
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
201
|
+
|
|
202
|
+
type_prefix = "." if method_type == "class" else "#"
|
|
203
|
+
lines.append(f"- {type_prefix}{name} - Lines {lines_str}")
|
|
204
|
+
lines.append("")
|
|
205
|
+
|
|
206
|
+
return "\n".join(lines)
|
|
207
|
+
|
|
208
|
+
def _format_csv(self, data: dict[str, Any]) -> str:
|
|
209
|
+
"""CSV format for Ruby"""
|
|
210
|
+
lines = []
|
|
211
|
+
|
|
212
|
+
# Header
|
|
213
|
+
lines.append("Type,Name,Additional,Lines")
|
|
214
|
+
|
|
215
|
+
# Classes
|
|
216
|
+
for class_info in data.get("classes", []):
|
|
217
|
+
name = str(class_info.get("name", "Unknown"))
|
|
218
|
+
class_type = str(class_info.get("class_type", "class"))
|
|
219
|
+
line_range = class_info.get("line_range", {})
|
|
220
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
221
|
+
lines.append(f"{class_type},{name},,{lines_str}")
|
|
222
|
+
|
|
223
|
+
# Methods
|
|
224
|
+
for method in data.get("methods", []):
|
|
225
|
+
name = str(method.get("name", "Unknown"))
|
|
226
|
+
metadata = method.get("metadata", {})
|
|
227
|
+
method_type = metadata.get("method_type", "instance")
|
|
228
|
+
attr_type = metadata.get("attr_type", "")
|
|
229
|
+
line_range = method.get("line_range", {})
|
|
230
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
231
|
+
|
|
232
|
+
additional = attr_type if attr_type else method_type
|
|
233
|
+
lines.append(f"method,{name},{additional},{lines_str}")
|
|
234
|
+
|
|
235
|
+
# Variables
|
|
236
|
+
for field in data.get("fields", []):
|
|
237
|
+
name = str(field.get("name", "Unknown"))
|
|
238
|
+
metadata = field.get("metadata", {})
|
|
239
|
+
|
|
240
|
+
# Determine variable type
|
|
241
|
+
if field.get("is_constant", False):
|
|
242
|
+
var_type = "constant"
|
|
243
|
+
elif metadata.get("is_class_variable", False):
|
|
244
|
+
var_type = "class_variable"
|
|
245
|
+
elif metadata.get("is_instance_variable", False):
|
|
246
|
+
var_type = "instance_variable"
|
|
247
|
+
else:
|
|
248
|
+
var_type = "variable"
|
|
249
|
+
|
|
250
|
+
line_range = field.get("line_range", {})
|
|
251
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
252
|
+
lines.append(f"{var_type},{name},,{lines_str}")
|
|
253
|
+
|
|
254
|
+
return "\n".join(lines)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class RubyFullFormatter(RubyTableFormatter):
|
|
258
|
+
"""Full table formatter for Ruby"""
|
|
259
|
+
|
|
260
|
+
def format(self, data: dict[str, Any]) -> str:
|
|
261
|
+
"""Format data as full table"""
|
|
262
|
+
return self._format_full_table(data)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class RubyCompactFormatter(RubyTableFormatter):
|
|
266
|
+
"""Compact table formatter for Ruby"""
|
|
267
|
+
|
|
268
|
+
def format(self, data: dict[str, Any]) -> str:
|
|
269
|
+
"""Format data as compact table"""
|
|
270
|
+
return self._format_compact_table(data)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class RubyCSVFormatter(RubyTableFormatter):
|
|
274
|
+
"""CSV formatter for Ruby"""
|
|
275
|
+
|
|
276
|
+
def format(self, data: dict[str, Any]) -> str:
|
|
277
|
+
"""Format data as CSV"""
|
|
278
|
+
return self._format_csv(data)
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Rust-specific table formatter.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from .base_formatter import BaseTableFormatter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RustTableFormatter(BaseTableFormatter):
|
|
12
|
+
"""Table formatter specialized for Rust"""
|
|
13
|
+
|
|
14
|
+
def _format_full_table(self, data: dict[str, Any]) -> str:
|
|
15
|
+
"""Full table format for Rust"""
|
|
16
|
+
lines = []
|
|
17
|
+
|
|
18
|
+
# Header - Rust (module-centric)
|
|
19
|
+
file_name = data.get("file_path", "Unknown").split("/")[-1].split("\\")[-1]
|
|
20
|
+
lines.append(f"# {file_name}")
|
|
21
|
+
lines.append("")
|
|
22
|
+
|
|
23
|
+
# Module Info
|
|
24
|
+
modules = data.get("modules", [])
|
|
25
|
+
if modules:
|
|
26
|
+
lines.append("## Modules")
|
|
27
|
+
lines.append("| Name | Visibility | Lines |")
|
|
28
|
+
lines.append("|------|------------|-------|")
|
|
29
|
+
for mod in modules:
|
|
30
|
+
lines.append(
|
|
31
|
+
f"| {mod.get('name')} | {mod.get('visibility')} | {mod.get('line_range', {}).get('start')}-{mod.get('line_range', {}).get('end')} |"
|
|
32
|
+
)
|
|
33
|
+
lines.append("")
|
|
34
|
+
|
|
35
|
+
# Structs (mapped from classes)
|
|
36
|
+
structs = data.get("classes", [])
|
|
37
|
+
if structs:
|
|
38
|
+
lines.append("## Structs")
|
|
39
|
+
lines.append("| Name | Type | Visibility | Lines | Fields | Traits |")
|
|
40
|
+
lines.append("|------|------|------------|-------|--------|--------|")
|
|
41
|
+
|
|
42
|
+
for struct in structs:
|
|
43
|
+
name = str(struct.get("name", "Unknown"))
|
|
44
|
+
struct_type = str(struct.get("type", "struct")) # struct, enum, trait
|
|
45
|
+
visibility = str(struct.get("visibility", "private"))
|
|
46
|
+
line_range = struct.get("line_range", {})
|
|
47
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
48
|
+
|
|
49
|
+
# Count fields
|
|
50
|
+
fields_count = len(
|
|
51
|
+
[
|
|
52
|
+
f
|
|
53
|
+
for f in data.get("fields", [])
|
|
54
|
+
if line_range.get("start", 0)
|
|
55
|
+
<= f.get("line_range", {}).get("start", 0)
|
|
56
|
+
<= line_range.get("end", 0)
|
|
57
|
+
]
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Traits (from interfaces field)
|
|
61
|
+
traits = struct.get("implements_interfaces", [])
|
|
62
|
+
traits_str = ", ".join(traits) if traits else "-"
|
|
63
|
+
|
|
64
|
+
lines.append(
|
|
65
|
+
f"| {name} | {struct_type} | {visibility} | {lines_str} | {fields_count} | {traits_str} |"
|
|
66
|
+
)
|
|
67
|
+
lines.append("")
|
|
68
|
+
|
|
69
|
+
# Functions (mapped from methods)
|
|
70
|
+
fns = data.get("methods", [])
|
|
71
|
+
if fns:
|
|
72
|
+
lines.append("## Functions")
|
|
73
|
+
lines.append("| Function | Signature | Vis | Async | Lines | Doc |")
|
|
74
|
+
lines.append("|----------|-----------|-----|-------|-------|-----|")
|
|
75
|
+
|
|
76
|
+
for fn in fns:
|
|
77
|
+
lines.append(self._format_fn_row(fn))
|
|
78
|
+
lines.append("")
|
|
79
|
+
|
|
80
|
+
# Traits (if mapped separately or included in classes)
|
|
81
|
+
# Note: The extractor maps traits to classes with type="trait"
|
|
82
|
+
|
|
83
|
+
# Impl blocks
|
|
84
|
+
impls = data.get("impls", [])
|
|
85
|
+
if impls:
|
|
86
|
+
lines.append("## Implementations")
|
|
87
|
+
lines.append("| Type | Trait | Lines |")
|
|
88
|
+
lines.append("|------|-------|-------|")
|
|
89
|
+
for impl in impls:
|
|
90
|
+
trait_name = impl.get("trait", "-")
|
|
91
|
+
type_name = impl.get("type", "Unknown")
|
|
92
|
+
line_range = impl.get("line_range", {})
|
|
93
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
94
|
+
lines.append(f"| {type_name} | {trait_name} | {lines_str} |")
|
|
95
|
+
lines.append("")
|
|
96
|
+
|
|
97
|
+
return "\n".join(lines)
|
|
98
|
+
|
|
99
|
+
def _format_compact_table(self, data: dict[str, Any]) -> str:
|
|
100
|
+
"""Compact table format for Rust"""
|
|
101
|
+
lines = []
|
|
102
|
+
|
|
103
|
+
# Header
|
|
104
|
+
file_name = data.get("file_path", "Unknown").split("/")[-1].split("\\")[-1]
|
|
105
|
+
lines.append(f"# {file_name}")
|
|
106
|
+
lines.append("")
|
|
107
|
+
|
|
108
|
+
# Info
|
|
109
|
+
stats = data.get("statistics") or {}
|
|
110
|
+
lines.append("## Info")
|
|
111
|
+
lines.append("| Property | Value |")
|
|
112
|
+
lines.append("|----------|-------|")
|
|
113
|
+
lines.append(f"| Structs | {len(data.get('classes', []))} |")
|
|
114
|
+
lines.append(f"| Functions | {stats.get('method_count', 0)} |")
|
|
115
|
+
lines.append(f"| Lines | {stats.get('lines', 0)} |")
|
|
116
|
+
lines.append("")
|
|
117
|
+
|
|
118
|
+
# Functions (compact)
|
|
119
|
+
fns = data.get("methods", [])
|
|
120
|
+
if fns:
|
|
121
|
+
lines.append("## Functions")
|
|
122
|
+
lines.append("| Fn | Sig | V | A | L | Doc |")
|
|
123
|
+
lines.append("|----|-----|---|---|---|-----|")
|
|
124
|
+
|
|
125
|
+
for fn in fns:
|
|
126
|
+
name = str(fn.get("name", ""))
|
|
127
|
+
signature = self._create_compact_signature(fn)
|
|
128
|
+
visibility = self._convert_visibility(str(fn.get("visibility", "")))
|
|
129
|
+
is_async = "Y" if fn.get("is_async", False) else "-"
|
|
130
|
+
line_range = fn.get("line_range", {})
|
|
131
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
132
|
+
doc = self._clean_csv_text(
|
|
133
|
+
self._extract_doc_summary(str(fn.get("docstring", "") or ""))
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
lines.append(
|
|
137
|
+
f"| {name} | {signature} | {visibility} | {is_async} | {lines_str} | {doc} |"
|
|
138
|
+
)
|
|
139
|
+
lines.append("")
|
|
140
|
+
|
|
141
|
+
return "\n".join(lines)
|
|
142
|
+
|
|
143
|
+
def _format_fn_row(self, fn: dict[str, Any]) -> str:
|
|
144
|
+
"""Format a function table row for Rust"""
|
|
145
|
+
name = str(fn.get("name", ""))
|
|
146
|
+
signature = self._create_full_signature(fn)
|
|
147
|
+
visibility = self._convert_visibility(str(fn.get("visibility", "")))
|
|
148
|
+
is_async = "Yes" if fn.get("is_async", False) else "-"
|
|
149
|
+
line_range = fn.get("line_range", {})
|
|
150
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
151
|
+
doc = self._clean_csv_text(
|
|
152
|
+
self._extract_doc_summary(str(fn.get("docstring", "") or ""))
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
return f"| {name} | {signature} | {visibility} | {is_async} | {lines_str} | {doc} |"
|
|
156
|
+
|
|
157
|
+
def _create_full_signature(self, fn: dict[str, Any]) -> str:
|
|
158
|
+
"""Create full function signature for Rust"""
|
|
159
|
+
params = fn.get("parameters", [])
|
|
160
|
+
# Rust parameters are usually strings like "x: i32", keep them as is or simplify
|
|
161
|
+
params_str = ", ".join([str(p) for p in params])
|
|
162
|
+
return_type = fn.get("return_type", "")
|
|
163
|
+
ret_str = f" -> {return_type}" if return_type and return_type != "()" else ""
|
|
164
|
+
|
|
165
|
+
return f"fn({params_str}){ret_str}"
|
|
166
|
+
|
|
167
|
+
def _create_compact_signature(self, fn: dict[str, Any]) -> str:
|
|
168
|
+
"""Create compact function signature for Rust"""
|
|
169
|
+
params = fn.get("parameters", [])
|
|
170
|
+
# Simply count parameters or use short types if available
|
|
171
|
+
params_summary = f"({len(params)})"
|
|
172
|
+
return_type = fn.get("return_type", "")
|
|
173
|
+
ret_str = f"->{return_type}" if return_type and return_type != "()" else ""
|
|
174
|
+
|
|
175
|
+
return f"{params_summary}{ret_str}"
|
|
176
|
+
|
|
177
|
+
def _convert_visibility(self, visibility: str) -> str:
|
|
178
|
+
"""Convert visibility to short symbol"""
|
|
179
|
+
if visibility == "pub":
|
|
180
|
+
return "pub"
|
|
181
|
+
elif visibility == "pub(crate)":
|
|
182
|
+
return "crate"
|
|
183
|
+
elif visibility == "private": # Default
|
|
184
|
+
return "priv"
|
|
185
|
+
return visibility
|
|
186
|
+
|
|
187
|
+
def format_table(
|
|
188
|
+
self, analysis_result: dict[str, Any], table_type: str = "full"
|
|
189
|
+
) -> str:
|
|
190
|
+
"""Format table output for Rust"""
|
|
191
|
+
# Set the format type based on table_type parameter
|
|
192
|
+
original_format_type = self.format_type
|
|
193
|
+
self.format_type = table_type
|
|
194
|
+
|
|
195
|
+
try:
|
|
196
|
+
# Handle json format separately
|
|
197
|
+
if table_type == "json":
|
|
198
|
+
return self._format_json(analysis_result)
|
|
199
|
+
# Use the existing format_structure method
|
|
200
|
+
return self.format_structure(analysis_result)
|
|
201
|
+
finally:
|
|
202
|
+
# Restore original format type
|
|
203
|
+
self.format_type = original_format_type
|
|
204
|
+
|
|
205
|
+
def format_summary(self, analysis_result: dict[str, Any]) -> str:
|
|
206
|
+
"""Format summary output for Rust"""
|
|
207
|
+
return self._format_compact_table(analysis_result)
|
|
208
|
+
|
|
209
|
+
def format_structure(self, analysis_result: dict[str, Any]) -> str:
|
|
210
|
+
"""Format structure analysis output for Rust"""
|
|
211
|
+
if self.format_type == "compact":
|
|
212
|
+
return self._format_compact_table(analysis_result)
|
|
213
|
+
return self._format_full_table(analysis_result)
|
|
214
|
+
|
|
215
|
+
def format_advanced(
|
|
216
|
+
self, analysis_result: dict[str, Any], output_format: str = "json"
|
|
217
|
+
) -> str:
|
|
218
|
+
"""Format advanced analysis output for Rust"""
|
|
219
|
+
if output_format == "json":
|
|
220
|
+
return self._format_json(analysis_result)
|
|
221
|
+
elif output_format == "csv":
|
|
222
|
+
return self._format_csv(analysis_result)
|
|
223
|
+
else:
|
|
224
|
+
return self._format_full_table(analysis_result)
|
|
225
|
+
|
|
226
|
+
def _format_json(self, data: dict[str, Any]) -> str:
|
|
227
|
+
"""Format data as JSON"""
|
|
228
|
+
import json
|
|
229
|
+
|
|
230
|
+
try:
|
|
231
|
+
return json.dumps(data, indent=2, ensure_ascii=False)
|
|
232
|
+
except (TypeError, ValueError) as e:
|
|
233
|
+
return f"# JSON serialization error: {e}\n"
|