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.
Files changed (149) hide show
  1. tree_sitter_analyzer/__init__.py +132 -0
  2. tree_sitter_analyzer/__main__.py +11 -0
  3. tree_sitter_analyzer/api.py +853 -0
  4. tree_sitter_analyzer/cli/__init__.py +39 -0
  5. tree_sitter_analyzer/cli/__main__.py +12 -0
  6. tree_sitter_analyzer/cli/argument_validator.py +89 -0
  7. tree_sitter_analyzer/cli/commands/__init__.py +26 -0
  8. tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
  9. tree_sitter_analyzer/cli/commands/base_command.py +181 -0
  10. tree_sitter_analyzer/cli/commands/default_command.py +18 -0
  11. tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
  12. tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
  13. tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
  14. tree_sitter_analyzer/cli/commands/query_command.py +109 -0
  15. tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
  16. tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
  17. tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
  18. tree_sitter_analyzer/cli/commands/table_command.py +414 -0
  19. tree_sitter_analyzer/cli/info_commands.py +124 -0
  20. tree_sitter_analyzer/cli_main.py +472 -0
  21. tree_sitter_analyzer/constants.py +85 -0
  22. tree_sitter_analyzer/core/__init__.py +15 -0
  23. tree_sitter_analyzer/core/analysis_engine.py +580 -0
  24. tree_sitter_analyzer/core/cache_service.py +333 -0
  25. tree_sitter_analyzer/core/engine.py +585 -0
  26. tree_sitter_analyzer/core/parser.py +293 -0
  27. tree_sitter_analyzer/core/query.py +605 -0
  28. tree_sitter_analyzer/core/query_filter.py +200 -0
  29. tree_sitter_analyzer/core/query_service.py +340 -0
  30. tree_sitter_analyzer/encoding_utils.py +530 -0
  31. tree_sitter_analyzer/exceptions.py +747 -0
  32. tree_sitter_analyzer/file_handler.py +246 -0
  33. tree_sitter_analyzer/formatters/__init__.py +1 -0
  34. tree_sitter_analyzer/formatters/base_formatter.py +201 -0
  35. tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
  36. tree_sitter_analyzer/formatters/formatter_config.py +197 -0
  37. tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
  38. tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
  39. tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
  40. tree_sitter_analyzer/formatters/go_formatter.py +368 -0
  41. tree_sitter_analyzer/formatters/html_formatter.py +498 -0
  42. tree_sitter_analyzer/formatters/java_formatter.py +423 -0
  43. tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
  44. tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
  45. tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
  46. tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
  47. tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
  48. tree_sitter_analyzer/formatters/php_formatter.py +301 -0
  49. tree_sitter_analyzer/formatters/python_formatter.py +830 -0
  50. tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
  51. tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
  52. tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
  53. tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
  54. tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
  55. tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
  56. tree_sitter_analyzer/interfaces/__init__.py +9 -0
  57. tree_sitter_analyzer/interfaces/cli.py +535 -0
  58. tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
  59. tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
  60. tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
  61. tree_sitter_analyzer/language_detector.py +553 -0
  62. tree_sitter_analyzer/language_loader.py +271 -0
  63. tree_sitter_analyzer/languages/__init__.py +10 -0
  64. tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
  65. tree_sitter_analyzer/languages/css_plugin.py +449 -0
  66. tree_sitter_analyzer/languages/go_plugin.py +836 -0
  67. tree_sitter_analyzer/languages/html_plugin.py +496 -0
  68. tree_sitter_analyzer/languages/java_plugin.py +1299 -0
  69. tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
  70. tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
  71. tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
  72. tree_sitter_analyzer/languages/php_plugin.py +862 -0
  73. tree_sitter_analyzer/languages/python_plugin.py +1636 -0
  74. tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
  75. tree_sitter_analyzer/languages/rust_plugin.py +673 -0
  76. tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
  77. tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
  78. tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
  79. tree_sitter_analyzer/legacy_table_formatter.py +860 -0
  80. tree_sitter_analyzer/mcp/__init__.py +34 -0
  81. tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
  82. tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
  83. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
  84. tree_sitter_analyzer/mcp/server.py +869 -0
  85. tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
  86. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
  87. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
  88. tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
  89. tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
  90. tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
  91. tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
  92. tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
  93. tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
  94. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
  95. tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
  96. tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
  97. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
  98. tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
  99. tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
  100. tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
  101. tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
  102. tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
  103. tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
  104. tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
  105. tree_sitter_analyzer/models.py +840 -0
  106. tree_sitter_analyzer/mypy_current_errors.txt +2 -0
  107. tree_sitter_analyzer/output_manager.py +255 -0
  108. tree_sitter_analyzer/platform_compat/__init__.py +3 -0
  109. tree_sitter_analyzer/platform_compat/adapter.py +324 -0
  110. tree_sitter_analyzer/platform_compat/compare.py +224 -0
  111. tree_sitter_analyzer/platform_compat/detector.py +67 -0
  112. tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
  113. tree_sitter_analyzer/platform_compat/profiles.py +217 -0
  114. tree_sitter_analyzer/platform_compat/record.py +55 -0
  115. tree_sitter_analyzer/platform_compat/recorder.py +155 -0
  116. tree_sitter_analyzer/platform_compat/report.py +92 -0
  117. tree_sitter_analyzer/plugins/__init__.py +280 -0
  118. tree_sitter_analyzer/plugins/base.py +647 -0
  119. tree_sitter_analyzer/plugins/manager.py +384 -0
  120. tree_sitter_analyzer/project_detector.py +328 -0
  121. tree_sitter_analyzer/queries/__init__.py +27 -0
  122. tree_sitter_analyzer/queries/csharp.py +216 -0
  123. tree_sitter_analyzer/queries/css.py +615 -0
  124. tree_sitter_analyzer/queries/go.py +275 -0
  125. tree_sitter_analyzer/queries/html.py +543 -0
  126. tree_sitter_analyzer/queries/java.py +402 -0
  127. tree_sitter_analyzer/queries/javascript.py +724 -0
  128. tree_sitter_analyzer/queries/kotlin.py +192 -0
  129. tree_sitter_analyzer/queries/markdown.py +258 -0
  130. tree_sitter_analyzer/queries/php.py +95 -0
  131. tree_sitter_analyzer/queries/python.py +859 -0
  132. tree_sitter_analyzer/queries/ruby.py +92 -0
  133. tree_sitter_analyzer/queries/rust.py +223 -0
  134. tree_sitter_analyzer/queries/sql.py +555 -0
  135. tree_sitter_analyzer/queries/typescript.py +871 -0
  136. tree_sitter_analyzer/queries/yaml.py +236 -0
  137. tree_sitter_analyzer/query_loader.py +272 -0
  138. tree_sitter_analyzer/security/__init__.py +22 -0
  139. tree_sitter_analyzer/security/boundary_manager.py +277 -0
  140. tree_sitter_analyzer/security/regex_checker.py +297 -0
  141. tree_sitter_analyzer/security/validator.py +599 -0
  142. tree_sitter_analyzer/table_formatter.py +782 -0
  143. tree_sitter_analyzer/utils/__init__.py +53 -0
  144. tree_sitter_analyzer/utils/logging.py +433 -0
  145. tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
  146. tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
  147. tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
  148. tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
  149. 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