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,414 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Table Command
|
|
4
|
+
|
|
5
|
+
Handles table format output generation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from ...constants import (
|
|
12
|
+
ELEMENT_TYPE_CLASS,
|
|
13
|
+
ELEMENT_TYPE_FUNCTION,
|
|
14
|
+
ELEMENT_TYPE_IMPORT,
|
|
15
|
+
ELEMENT_TYPE_PACKAGE,
|
|
16
|
+
ELEMENT_TYPE_SQL_FUNCTION,
|
|
17
|
+
ELEMENT_TYPE_SQL_INDEX,
|
|
18
|
+
ELEMENT_TYPE_SQL_PROCEDURE,
|
|
19
|
+
ELEMENT_TYPE_SQL_TABLE,
|
|
20
|
+
ELEMENT_TYPE_SQL_TRIGGER,
|
|
21
|
+
ELEMENT_TYPE_SQL_VIEW,
|
|
22
|
+
ELEMENT_TYPE_VARIABLE,
|
|
23
|
+
get_element_type,
|
|
24
|
+
)
|
|
25
|
+
from ...output_manager import output_error
|
|
26
|
+
from .base_command import BaseCommand
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TableCommand(BaseCommand):
|
|
30
|
+
"""Command for generating table format output."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, args: Any) -> None:
|
|
33
|
+
"""Initialize the table command."""
|
|
34
|
+
super().__init__(args)
|
|
35
|
+
|
|
36
|
+
async def execute_async(self, language: str) -> int:
|
|
37
|
+
"""Execute table format generation."""
|
|
38
|
+
try:
|
|
39
|
+
# Perform standard analysis
|
|
40
|
+
analysis_result = await self.analyze_file(language)
|
|
41
|
+
if not analysis_result:
|
|
42
|
+
return 1
|
|
43
|
+
|
|
44
|
+
# Get appropriate formatter using FormatterSelector (explicit configuration)
|
|
45
|
+
from ...formatters.formatter_selector import FormatterSelector
|
|
46
|
+
|
|
47
|
+
table_type = getattr(self.args, "table", "full")
|
|
48
|
+
formatter = FormatterSelector.get_formatter(
|
|
49
|
+
analysis_result.language,
|
|
50
|
+
table_type,
|
|
51
|
+
include_javadoc=getattr(self.args, "include_javadoc", False),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Check if formatter has a method to handle AnalysisResult directly
|
|
55
|
+
if hasattr(formatter, "format_analysis_result"):
|
|
56
|
+
formatted_output = formatter.format_analysis_result(
|
|
57
|
+
analysis_result, table_type
|
|
58
|
+
)
|
|
59
|
+
else:
|
|
60
|
+
# Convert to structure format that the formatter expects
|
|
61
|
+
formatted_data = self._convert_to_structure_format(
|
|
62
|
+
analysis_result, language
|
|
63
|
+
)
|
|
64
|
+
formatted_output = formatter.format_structure(formatted_data)
|
|
65
|
+
|
|
66
|
+
self._output_table(formatted_output)
|
|
67
|
+
return 0
|
|
68
|
+
|
|
69
|
+
except Exception as e:
|
|
70
|
+
output_error(f"An error occurred during table format analysis: {e}")
|
|
71
|
+
return 1
|
|
72
|
+
|
|
73
|
+
def _convert_to_formatter_format(self, analysis_result: Any) -> dict[str, Any]:
|
|
74
|
+
"""Convert AnalysisResult to format expected by formatters."""
|
|
75
|
+
return {
|
|
76
|
+
"file_path": analysis_result.file_path,
|
|
77
|
+
"language": analysis_result.language,
|
|
78
|
+
"line_count": analysis_result.line_count,
|
|
79
|
+
"elements": [
|
|
80
|
+
{
|
|
81
|
+
"name": getattr(element, "name", str(element)),
|
|
82
|
+
"type": get_element_type(element),
|
|
83
|
+
"start_line": getattr(element, "start_line", 0),
|
|
84
|
+
"end_line": getattr(element, "end_line", 0),
|
|
85
|
+
"text": getattr(element, "text", ""),
|
|
86
|
+
"level": getattr(element, "level", 1),
|
|
87
|
+
"url": getattr(element, "url", ""),
|
|
88
|
+
"alt": getattr(element, "alt", ""),
|
|
89
|
+
"language": getattr(element, "language", ""),
|
|
90
|
+
"line_count": getattr(element, "line_count", 0),
|
|
91
|
+
"list_type": getattr(element, "list_type", ""),
|
|
92
|
+
"item_count": getattr(element, "item_count", 0),
|
|
93
|
+
"column_count": getattr(element, "column_count", 0),
|
|
94
|
+
"row_count": getattr(element, "row_count", 0),
|
|
95
|
+
"line_range": {
|
|
96
|
+
"start": getattr(element, "start_line", 0),
|
|
97
|
+
"end": getattr(element, "end_line", 0),
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
for element in analysis_result.elements
|
|
101
|
+
],
|
|
102
|
+
"analysis_metadata": {
|
|
103
|
+
"analysis_time": getattr(analysis_result, "analysis_time", 0.0),
|
|
104
|
+
"language": analysis_result.language,
|
|
105
|
+
"file_path": analysis_result.file_path,
|
|
106
|
+
"analyzer_version": "2.0.0",
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
def _get_default_package_name(self, language: str) -> str:
|
|
111
|
+
"""
|
|
112
|
+
Get default package name for language.
|
|
113
|
+
|
|
114
|
+
Only Java-like languages have package concept.
|
|
115
|
+
Other languages (JS, TS, Python) don't need package prefix.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
language: Programming language name
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Default package name ("unknown" for Java-like, "" for others)
|
|
122
|
+
"""
|
|
123
|
+
# Languages with package/namespace concept
|
|
124
|
+
PACKAGED_LANGUAGES = {"java", "kotlin", "scala", "csharp", "cpp", "c++"}
|
|
125
|
+
|
|
126
|
+
if language.lower() in PACKAGED_LANGUAGES:
|
|
127
|
+
return "unknown"
|
|
128
|
+
|
|
129
|
+
return "" # No package for JS, TS, Python, etc.
|
|
130
|
+
|
|
131
|
+
def _convert_to_structure_format(
|
|
132
|
+
self, analysis_result: Any, language: str
|
|
133
|
+
) -> dict[str, Any]:
|
|
134
|
+
"""Convert AnalysisResult to the format expected by table formatter."""
|
|
135
|
+
classes = []
|
|
136
|
+
methods = []
|
|
137
|
+
fields = []
|
|
138
|
+
imports = []
|
|
139
|
+
|
|
140
|
+
# Try to get package from analysis_result.package attribute first
|
|
141
|
+
package_obj = getattr(analysis_result, "package", None)
|
|
142
|
+
if package_obj and hasattr(package_obj, "name"):
|
|
143
|
+
package_name = str(package_obj.name)
|
|
144
|
+
else:
|
|
145
|
+
# Fall back to default or scanning elements
|
|
146
|
+
package_name = self._get_default_package_name(language)
|
|
147
|
+
|
|
148
|
+
# Process each element
|
|
149
|
+
for i, element in enumerate(analysis_result.elements):
|
|
150
|
+
try:
|
|
151
|
+
element_type = get_element_type(element)
|
|
152
|
+
element_name = getattr(element, "name", None)
|
|
153
|
+
|
|
154
|
+
if element_type == ELEMENT_TYPE_PACKAGE:
|
|
155
|
+
package_name = str(element_name)
|
|
156
|
+
elif element_type == ELEMENT_TYPE_CLASS:
|
|
157
|
+
classes.append(self._convert_class_element(element, i, language))
|
|
158
|
+
elif element_type == ELEMENT_TYPE_FUNCTION:
|
|
159
|
+
methods.append(self._convert_function_element(element, language))
|
|
160
|
+
elif element_type == ELEMENT_TYPE_VARIABLE:
|
|
161
|
+
fields.append(self._convert_variable_element(element, language))
|
|
162
|
+
elif element_type == ELEMENT_TYPE_IMPORT:
|
|
163
|
+
imports.append(self._convert_import_element(element))
|
|
164
|
+
# SQL element types
|
|
165
|
+
elif element_type in [
|
|
166
|
+
ELEMENT_TYPE_SQL_TABLE,
|
|
167
|
+
ELEMENT_TYPE_SQL_VIEW,
|
|
168
|
+
ELEMENT_TYPE_SQL_PROCEDURE,
|
|
169
|
+
ELEMENT_TYPE_SQL_FUNCTION,
|
|
170
|
+
ELEMENT_TYPE_SQL_TRIGGER,
|
|
171
|
+
ELEMENT_TYPE_SQL_INDEX,
|
|
172
|
+
]:
|
|
173
|
+
methods.append(self._convert_sql_element(element, language))
|
|
174
|
+
|
|
175
|
+
except Exception as element_error:
|
|
176
|
+
output_error(f"ERROR: Element {i} processing failed: {element_error}")
|
|
177
|
+
continue
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
"file_path": analysis_result.file_path,
|
|
181
|
+
"language": analysis_result.language,
|
|
182
|
+
"line_count": analysis_result.line_count,
|
|
183
|
+
"package": {"name": package_name},
|
|
184
|
+
"classes": classes,
|
|
185
|
+
"methods": methods,
|
|
186
|
+
"fields": fields,
|
|
187
|
+
"imports": imports,
|
|
188
|
+
"statistics": {
|
|
189
|
+
"method_count": len(methods),
|
|
190
|
+
"field_count": len(fields),
|
|
191
|
+
"class_count": len(classes),
|
|
192
|
+
"import_count": len(imports),
|
|
193
|
+
},
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
def _convert_class_element(
|
|
197
|
+
self, element: Any, index: int, language: str
|
|
198
|
+
) -> dict[str, Any]:
|
|
199
|
+
"""Convert class element to table format."""
|
|
200
|
+
element_name = getattr(element, "name", None)
|
|
201
|
+
final_name = element_name if element_name else f"UnknownClass_{index}"
|
|
202
|
+
|
|
203
|
+
# Get class type from element (interface, enum, or class)
|
|
204
|
+
class_type = getattr(element, "class_type", "class")
|
|
205
|
+
|
|
206
|
+
# Get visibility from element with language-specific default
|
|
207
|
+
# Java and C++ have package-private/private default, others have public default
|
|
208
|
+
default_visibility = "package" if language in ["java", "cpp", "c"] else "public"
|
|
209
|
+
visibility = getattr(element, "visibility", default_visibility)
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
"name": final_name,
|
|
213
|
+
"type": class_type,
|
|
214
|
+
"visibility": visibility,
|
|
215
|
+
"line_range": {
|
|
216
|
+
"start": getattr(element, "start_line", 0),
|
|
217
|
+
"end": getattr(element, "end_line", 0),
|
|
218
|
+
},
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
def _convert_function_element(self, element: Any, language: str) -> dict[str, Any]:
|
|
222
|
+
"""Convert function element to table format."""
|
|
223
|
+
# Process parameters based on language
|
|
224
|
+
params = getattr(element, "parameters", [])
|
|
225
|
+
processed_params = self._process_parameters(params, language)
|
|
226
|
+
|
|
227
|
+
# Get visibility
|
|
228
|
+
visibility = self._get_element_visibility(element)
|
|
229
|
+
|
|
230
|
+
# Get JavaDoc if enabled
|
|
231
|
+
include_javadoc = getattr(self.args, "include_javadoc", False)
|
|
232
|
+
javadoc = getattr(element, "docstring", "") or "" if include_javadoc else ""
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
"name": getattr(element, "name", str(element)),
|
|
236
|
+
"visibility": visibility,
|
|
237
|
+
"return_type": getattr(element, "return_type", "Any"),
|
|
238
|
+
"parameters": processed_params,
|
|
239
|
+
"is_constructor": getattr(element, "is_constructor", False),
|
|
240
|
+
"is_static": getattr(element, "is_static", False),
|
|
241
|
+
"complexity_score": getattr(element, "complexity_score", 1),
|
|
242
|
+
"line_range": {
|
|
243
|
+
"start": getattr(element, "start_line", 0),
|
|
244
|
+
"end": getattr(element, "end_line", 0),
|
|
245
|
+
},
|
|
246
|
+
"javadoc": javadoc,
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
def _convert_variable_element(self, element: Any, language: str) -> dict[str, Any]:
|
|
250
|
+
"""Convert variable element to table format."""
|
|
251
|
+
# Get field type based on language
|
|
252
|
+
if language == "python":
|
|
253
|
+
field_type = getattr(element, "variable_type", "") or ""
|
|
254
|
+
else:
|
|
255
|
+
field_type = getattr(element, "variable_type", "") or getattr(
|
|
256
|
+
element, "field_type", ""
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Get visibility
|
|
260
|
+
field_visibility = self._get_element_visibility(element)
|
|
261
|
+
|
|
262
|
+
# Get JavaDoc if enabled
|
|
263
|
+
include_javadoc = getattr(self.args, "include_javadoc", False)
|
|
264
|
+
javadoc = getattr(element, "docstring", "") or "" if include_javadoc else ""
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
"name": getattr(element, "name", str(element)),
|
|
268
|
+
"type": field_type,
|
|
269
|
+
"visibility": field_visibility,
|
|
270
|
+
"modifiers": getattr(element, "modifiers", []),
|
|
271
|
+
"line_range": {
|
|
272
|
+
"start": getattr(element, "start_line", 0),
|
|
273
|
+
"end": getattr(element, "end_line", 0),
|
|
274
|
+
},
|
|
275
|
+
"javadoc": javadoc,
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
def _convert_import_element(self, element: Any) -> dict[str, Any]:
|
|
279
|
+
"""Convert import element to table format."""
|
|
280
|
+
# Try to get the full import statement from raw_text
|
|
281
|
+
raw_text = getattr(element, "raw_text", "")
|
|
282
|
+
if raw_text:
|
|
283
|
+
statement = raw_text
|
|
284
|
+
else:
|
|
285
|
+
# Fallback to constructing from name
|
|
286
|
+
statement = f"import {getattr(element, 'name', str(element))}"
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
"statement": statement,
|
|
290
|
+
"raw_text": statement, # PythonTableFormatter expects raw_text
|
|
291
|
+
"name": getattr(element, "name", str(element)),
|
|
292
|
+
"module_name": getattr(element, "module_name", ""),
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
def _convert_sql_element(self, element: Any, language: str) -> dict[str, Any]:
|
|
296
|
+
"""Convert SQL element to table format."""
|
|
297
|
+
element_name = getattr(element, "name", str(element))
|
|
298
|
+
element_type = get_element_type(element)
|
|
299
|
+
|
|
300
|
+
# Get SQL-specific attributes
|
|
301
|
+
columns = getattr(element, "columns", [])
|
|
302
|
+
parameters = getattr(element, "parameters", [])
|
|
303
|
+
dependencies = getattr(element, "dependencies", [])
|
|
304
|
+
source_tables = getattr(element, "source_tables", [])
|
|
305
|
+
return_type = getattr(element, "return_type", "")
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
"name": element_name,
|
|
309
|
+
"visibility": "public", # SQL elements are typically public
|
|
310
|
+
"return_type": (
|
|
311
|
+
return_type if return_type else ""
|
|
312
|
+
), # Don't fallback to element_type
|
|
313
|
+
"parameters": self._process_sql_parameters(parameters),
|
|
314
|
+
"is_constructor": False,
|
|
315
|
+
"is_static": False,
|
|
316
|
+
"complexity_score": 1,
|
|
317
|
+
"line_range": {
|
|
318
|
+
"start": getattr(element, "start_line", 0),
|
|
319
|
+
"end": getattr(element, "end_line", 0),
|
|
320
|
+
},
|
|
321
|
+
"javadoc": "",
|
|
322
|
+
"sql_type": element_type,
|
|
323
|
+
"columns": columns,
|
|
324
|
+
"dependencies": dependencies,
|
|
325
|
+
"source_tables": source_tables,
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
def _process_sql_parameters(self, params: Any) -> list[dict[str, str]]:
|
|
329
|
+
"""Process SQL parameters."""
|
|
330
|
+
if not params:
|
|
331
|
+
return []
|
|
332
|
+
|
|
333
|
+
if isinstance(params, list):
|
|
334
|
+
param_list = []
|
|
335
|
+
for param in params:
|
|
336
|
+
if isinstance(param, dict):
|
|
337
|
+
param_list.append(param)
|
|
338
|
+
else:
|
|
339
|
+
param_list.append({"name": str(param), "type": "Any"})
|
|
340
|
+
return param_list
|
|
341
|
+
else:
|
|
342
|
+
return [{"name": str(params), "type": "Any"}]
|
|
343
|
+
|
|
344
|
+
def _process_parameters(self, params: Any, language: str) -> list[dict[str, str]]:
|
|
345
|
+
"""Process parameters based on language syntax."""
|
|
346
|
+
if isinstance(params, str):
|
|
347
|
+
param_list = []
|
|
348
|
+
if params.strip():
|
|
349
|
+
param_names = [p.strip() for p in params.split(",") if p.strip()]
|
|
350
|
+
param_list = [{"name": name, "type": "Any"} for name in param_names]
|
|
351
|
+
return param_list
|
|
352
|
+
elif isinstance(params, list):
|
|
353
|
+
param_list = []
|
|
354
|
+
for param in params:
|
|
355
|
+
if isinstance(param, str):
|
|
356
|
+
param = param.strip()
|
|
357
|
+
# Languages using "name: type" syntax
|
|
358
|
+
TYPE_SUFFIX_LANGUAGES = {
|
|
359
|
+
"python",
|
|
360
|
+
"rust",
|
|
361
|
+
"kotlin",
|
|
362
|
+
"typescript",
|
|
363
|
+
"ts",
|
|
364
|
+
"scala",
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if language.lower() in TYPE_SUFFIX_LANGUAGES:
|
|
368
|
+
# Format: "name: type"
|
|
369
|
+
if ":" in param:
|
|
370
|
+
parts = param.split(":", 1)
|
|
371
|
+
param_name = parts[0].strip()
|
|
372
|
+
param_type = parts[1].strip() if len(parts) > 1 else "Any"
|
|
373
|
+
param_list.append({"name": param_name, "type": param_type})
|
|
374
|
+
else:
|
|
375
|
+
param_list.append({"name": param, "type": "Any"})
|
|
376
|
+
else:
|
|
377
|
+
# Java format: "Type name"
|
|
378
|
+
last_space_idx = param.rfind(" ")
|
|
379
|
+
if last_space_idx != -1:
|
|
380
|
+
param_type = param[:last_space_idx].strip()
|
|
381
|
+
param_name = param[last_space_idx + 1 :].strip()
|
|
382
|
+
if param_type and param_name:
|
|
383
|
+
param_list.append(
|
|
384
|
+
{"name": param_name, "type": param_type}
|
|
385
|
+
)
|
|
386
|
+
else:
|
|
387
|
+
param_list.append({"name": param, "type": "Any"})
|
|
388
|
+
else:
|
|
389
|
+
param_list.append({"name": param, "type": "Any"})
|
|
390
|
+
elif isinstance(param, dict):
|
|
391
|
+
param_list.append(param)
|
|
392
|
+
else:
|
|
393
|
+
param_list.append({"name": str(param), "type": "Any"})
|
|
394
|
+
return param_list
|
|
395
|
+
else:
|
|
396
|
+
return []
|
|
397
|
+
|
|
398
|
+
def _get_element_visibility(self, element: Any) -> str:
|
|
399
|
+
"""Get element visibility."""
|
|
400
|
+
visibility = getattr(element, "visibility", "public")
|
|
401
|
+
if hasattr(element, "is_private") and getattr(element, "is_private", False):
|
|
402
|
+
visibility = "private"
|
|
403
|
+
elif hasattr(element, "is_public") and getattr(element, "is_public", False):
|
|
404
|
+
visibility = "public"
|
|
405
|
+
return visibility
|
|
406
|
+
|
|
407
|
+
def _output_table(self, table_output: str) -> None:
|
|
408
|
+
"""Output the table with proper encoding."""
|
|
409
|
+
try:
|
|
410
|
+
# Windows support: Output with UTF-8 encoding
|
|
411
|
+
sys.stdout.buffer.write(table_output.encode("utf-8"))
|
|
412
|
+
except (AttributeError, UnicodeEncodeError):
|
|
413
|
+
# Fallback: Normal print
|
|
414
|
+
print(table_output, end="")
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Information Commands for CLI
|
|
4
|
+
|
|
5
|
+
Commands that display information without requiring file analysis.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from argparse import Namespace
|
|
10
|
+
|
|
11
|
+
from ..language_detector import detect_language_from_file, detector
|
|
12
|
+
from ..output_manager import output_data, output_error, output_info, output_list
|
|
13
|
+
from ..query_loader import query_loader
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class InfoCommand(ABC):
|
|
17
|
+
"""Base class for information commands that don't require file analysis."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, args: Namespace):
|
|
20
|
+
self.args = args
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def execute(self) -> int:
|
|
24
|
+
"""Execute the information command."""
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ListQueriesCommand(InfoCommand):
|
|
29
|
+
"""Command to list available queries."""
|
|
30
|
+
|
|
31
|
+
def execute(self) -> int:
|
|
32
|
+
if self.args.language:
|
|
33
|
+
language = self.args.language
|
|
34
|
+
elif hasattr(self.args, "file_path") and self.args.file_path:
|
|
35
|
+
language = detect_language_from_file(self.args.file_path)
|
|
36
|
+
else:
|
|
37
|
+
output_list("Supported languages:")
|
|
38
|
+
for lang in query_loader.list_supported_languages():
|
|
39
|
+
output_list(f" {lang}")
|
|
40
|
+
queries = query_loader.list_queries_for_language(lang)
|
|
41
|
+
for query_key in queries:
|
|
42
|
+
description = (
|
|
43
|
+
query_loader.get_query_description(lang, query_key)
|
|
44
|
+
or "No description"
|
|
45
|
+
)
|
|
46
|
+
output_list(f" {query_key:<20} - {description}")
|
|
47
|
+
return 0
|
|
48
|
+
|
|
49
|
+
output_list(f"Available query keys ({language}):")
|
|
50
|
+
for query_key in query_loader.list_queries_for_language(language):
|
|
51
|
+
description = (
|
|
52
|
+
query_loader.get_query_description(language, query_key)
|
|
53
|
+
or "No description"
|
|
54
|
+
)
|
|
55
|
+
output_list(f" {query_key:<20} - {description}")
|
|
56
|
+
return 0
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class DescribeQueryCommand(InfoCommand):
|
|
60
|
+
"""Command to describe a specific query."""
|
|
61
|
+
|
|
62
|
+
def execute(self) -> int:
|
|
63
|
+
if self.args.language:
|
|
64
|
+
language = self.args.language
|
|
65
|
+
elif hasattr(self.args, "file_path") and self.args.file_path:
|
|
66
|
+
language = detect_language_from_file(self.args.file_path)
|
|
67
|
+
else:
|
|
68
|
+
output_error(
|
|
69
|
+
"ERROR: Query description display requires --language or target file specification"
|
|
70
|
+
)
|
|
71
|
+
return 1
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
query_description = query_loader.get_query_description(
|
|
75
|
+
language, self.args.describe_query
|
|
76
|
+
)
|
|
77
|
+
query_content = query_loader.get_query(language, self.args.describe_query)
|
|
78
|
+
|
|
79
|
+
if query_description is None or query_content is None:
|
|
80
|
+
output_error(
|
|
81
|
+
f"Query '{self.args.describe_query}' not found for language '{language}'"
|
|
82
|
+
)
|
|
83
|
+
return 1
|
|
84
|
+
|
|
85
|
+
output_info(
|
|
86
|
+
f"Query key '{self.args.describe_query}' ({language}): {query_description}"
|
|
87
|
+
)
|
|
88
|
+
output_data(f"Query content:\n{query_content}")
|
|
89
|
+
except ValueError as e:
|
|
90
|
+
output_error(f"{e}")
|
|
91
|
+
return 1
|
|
92
|
+
return 0
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class ShowLanguagesCommand(InfoCommand):
|
|
96
|
+
"""Command to show supported languages."""
|
|
97
|
+
|
|
98
|
+
def execute(self) -> int:
|
|
99
|
+
output_list("Supported languages:")
|
|
100
|
+
for language in detector.get_supported_languages():
|
|
101
|
+
info = detector.get_language_info(language)
|
|
102
|
+
extensions = ", ".join(info["extensions"][:5])
|
|
103
|
+
if len(info["extensions"]) > 5:
|
|
104
|
+
extensions += f", ... ({len(info['extensions']) - 5} more)"
|
|
105
|
+
output_list(f" {language:<12} - Extensions: {extensions}")
|
|
106
|
+
return 0
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class ShowExtensionsCommand(InfoCommand):
|
|
110
|
+
"""Command to show supported extensions."""
|
|
111
|
+
|
|
112
|
+
def execute(self) -> int:
|
|
113
|
+
output_list("Supported file extensions:")
|
|
114
|
+
supported_extensions = detector.get_supported_extensions()
|
|
115
|
+
# Use more efficient chunking with itertools.islice
|
|
116
|
+
from itertools import islice
|
|
117
|
+
|
|
118
|
+
chunk_size = 8
|
|
119
|
+
for i in range(0, len(supported_extensions), chunk_size):
|
|
120
|
+
chunk = list(islice(supported_extensions, i, i + chunk_size))
|
|
121
|
+
line = " " + " ".join(f"{ext:<6}" for ext in chunk)
|
|
122
|
+
output_list(line)
|
|
123
|
+
output_info(f"\nTotal {len(supported_extensions)} extensions supported")
|
|
124
|
+
return 0
|