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,377 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Formatter Registry
4
+
5
+ Dynamic formatter registration and management system.
6
+ Provides extensible formatter architecture following the Registry pattern.
7
+ """
8
+
9
+ import logging
10
+ from abc import ABC, abstractmethod
11
+
12
+ from ..models import CodeElement
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class IFormatter(ABC):
18
+ """
19
+ Interface for code element formatters.
20
+
21
+ All formatters must implement this interface to be compatible
22
+ with the FormatterRegistry system.
23
+ """
24
+
25
+ @staticmethod
26
+ @abstractmethod
27
+ def get_format_name() -> str:
28
+ """
29
+ Return the format name this formatter supports.
30
+
31
+ Returns:
32
+ Format name (e.g., "json", "csv", "markdown")
33
+ """
34
+ pass
35
+
36
+ @abstractmethod
37
+ def format(self, elements: list[CodeElement]) -> str:
38
+ """
39
+ Format a list of CodeElements into a string representation.
40
+
41
+ Args:
42
+ elements: List of CodeElement objects to format
43
+
44
+ Returns:
45
+ Formatted string representation
46
+ """
47
+ pass
48
+
49
+
50
+ class FormatterRegistry:
51
+ """
52
+ Registry for managing and providing formatter instances.
53
+
54
+ Implements the Registry pattern to allow dynamic registration
55
+ and retrieval of formatters by format name.
56
+ """
57
+
58
+ _formatters: dict[str, type[IFormatter]] = {}
59
+
60
+ @classmethod
61
+ def register_formatter(cls, formatter_class: type[IFormatter]) -> None:
62
+ """
63
+ Register a formatter class in the registry.
64
+
65
+ Args:
66
+ formatter_class: Formatter class implementing IFormatter
67
+
68
+ Raises:
69
+ ValueError: If formatter_class doesn't implement IFormatter
70
+ """
71
+ if not issubclass(formatter_class, IFormatter):
72
+ raise ValueError("Formatter class must implement IFormatter interface")
73
+
74
+ format_name = formatter_class.get_format_name()
75
+ if not format_name:
76
+ raise ValueError("Formatter must provide a non-empty format name")
77
+
78
+ if format_name in cls._formatters:
79
+ logger.warning(f"Overriding existing formatter for format: {format_name}")
80
+
81
+ cls._formatters[format_name] = formatter_class
82
+ logger.debug(f"Registered formatter for format: {format_name}")
83
+
84
+ @classmethod
85
+ def get_formatter(cls, format_name: str) -> IFormatter:
86
+ """
87
+ Get a formatter instance for the specified format.
88
+
89
+ Args:
90
+ format_name: Name of the format to get formatter for
91
+
92
+ Returns:
93
+ Formatter instance
94
+
95
+ Raises:
96
+ ValueError: If format is not supported
97
+ """
98
+ if format_name not in cls._formatters:
99
+ available_formats = list(cls._formatters.keys())
100
+ raise ValueError(
101
+ f"Unsupported format: {format_name}. "
102
+ f"Available formats: {available_formats}"
103
+ )
104
+
105
+ formatter_class = cls._formatters[format_name]
106
+ return formatter_class()
107
+
108
+ @classmethod
109
+ def get_available_formats(cls) -> list[str]:
110
+ """
111
+ Get list of all available format names.
112
+
113
+ Returns:
114
+ List of available format names
115
+ """
116
+ return list(cls._formatters.keys())
117
+
118
+ @classmethod
119
+ def is_format_supported(cls, format_name: str) -> bool:
120
+ """
121
+ Check if a format is supported.
122
+
123
+ Args:
124
+ format_name: Format name to check
125
+
126
+ Returns:
127
+ True if format is supported
128
+ """
129
+ return format_name in cls._formatters
130
+
131
+ @classmethod
132
+ def unregister_formatter(cls, format_name: str) -> bool:
133
+ """
134
+ Unregister a formatter for the specified format.
135
+
136
+ Args:
137
+ format_name: Format name to unregister
138
+
139
+ Returns:
140
+ True if formatter was unregistered, False if not found
141
+ """
142
+ if format_name in cls._formatters:
143
+ del cls._formatters[format_name]
144
+ logger.debug(f"Unregistered formatter for format: {format_name}")
145
+ return True
146
+ return False
147
+
148
+ @classmethod
149
+ def clear_registry(cls) -> None:
150
+ """
151
+ Clear all registered formatters.
152
+
153
+ This method is primarily for testing purposes.
154
+ """
155
+ cls._formatters.clear()
156
+ logger.debug("Cleared all registered formatters")
157
+
158
+
159
+ # Built-in formatter implementations
160
+
161
+
162
+ class JsonFormatter(IFormatter):
163
+ """JSON formatter for CodeElement lists"""
164
+
165
+ @staticmethod
166
+ def get_format_name() -> str:
167
+ return "json"
168
+
169
+ def format(self, elements: list[CodeElement]) -> str:
170
+ """Format elements as JSON"""
171
+ import json
172
+
173
+ result = []
174
+ for element in elements:
175
+ element_dict = {
176
+ "name": element.name,
177
+ "type": getattr(element, "element_type", "unknown"),
178
+ "start_line": element.start_line,
179
+ "end_line": element.end_line,
180
+ "language": element.language,
181
+ }
182
+
183
+ # Add type-specific attributes
184
+ if hasattr(element, "parameters"):
185
+ element_dict["parameters"] = getattr(element, "parameters", [])
186
+ if hasattr(element, "return_type"):
187
+ element_dict["return_type"] = getattr(element, "return_type", None)
188
+ if hasattr(element, "visibility"):
189
+ element_dict["visibility"] = getattr(element, "visibility", "unknown")
190
+ if hasattr(element, "modifiers"):
191
+ element_dict["modifiers"] = getattr(element, "modifiers", [])
192
+ if hasattr(element, "tag_name"):
193
+ element_dict["tag_name"] = getattr(element, "tag_name", "")
194
+ if hasattr(element, "selector"):
195
+ element_dict["selector"] = getattr(element, "selector", "")
196
+ if hasattr(element, "element_class"):
197
+ element_dict["element_class"] = getattr(element, "element_class", "")
198
+
199
+ result.append(element_dict)
200
+
201
+ return json.dumps(result, indent=2, ensure_ascii=False)
202
+
203
+
204
+ class CsvFormatter(IFormatter):
205
+ """CSV formatter for CodeElement lists"""
206
+
207
+ @staticmethod
208
+ def get_format_name() -> str:
209
+ return "csv"
210
+
211
+ def format(self, elements: list[CodeElement]) -> str:
212
+ """Format elements as CSV"""
213
+ import csv
214
+ import io
215
+
216
+ output = io.StringIO()
217
+ writer = csv.writer(output, lineterminator="\n")
218
+
219
+ # Write header
220
+ writer.writerow(
221
+ [
222
+ "Type",
223
+ "Name",
224
+ "Start Line",
225
+ "End Line",
226
+ "Language",
227
+ "Visibility",
228
+ "Parameters",
229
+ "Return Type",
230
+ "Modifiers",
231
+ ]
232
+ )
233
+
234
+ # Write data rows
235
+ for element in elements:
236
+ writer.writerow(
237
+ [
238
+ getattr(element, "element_type", "unknown"),
239
+ element.name,
240
+ element.start_line,
241
+ element.end_line,
242
+ element.language,
243
+ getattr(element, "visibility", ""),
244
+ str(getattr(element, "parameters", [])),
245
+ getattr(element, "return_type", ""),
246
+ str(getattr(element, "modifiers", [])),
247
+ ]
248
+ )
249
+
250
+ csv_content = output.getvalue()
251
+ output.close()
252
+ return csv_content.rstrip("\n")
253
+
254
+
255
+ class FullFormatter(IFormatter):
256
+ """Full table formatter for CodeElement lists"""
257
+
258
+ @staticmethod
259
+ def get_format_name() -> str:
260
+ return "full"
261
+
262
+ def format(self, elements: list[CodeElement]) -> str:
263
+ """Format elements as full table"""
264
+ if not elements:
265
+ return "No elements found."
266
+
267
+ lines = []
268
+ lines.append("=" * 80)
269
+ lines.append("CODE STRUCTURE ANALYSIS")
270
+ lines.append("=" * 80)
271
+ lines.append("")
272
+
273
+ # Group elements by type
274
+ element_groups: dict[str, list[CodeElement]] = {}
275
+ for element in elements:
276
+ element_type = getattr(element, "element_type", "unknown")
277
+ if element_type not in element_groups:
278
+ element_groups[element_type] = []
279
+ element_groups[element_type].append(element)
280
+
281
+ # Format each group
282
+ for element_type, group_elements in element_groups.items():
283
+ lines.append(f"{element_type.upper()}S ({len(group_elements)})")
284
+ lines.append("-" * 40)
285
+
286
+ for element in group_elements:
287
+ lines.append(f" {element.name}")
288
+ lines.append(f" Lines: {element.start_line}-{element.end_line}")
289
+ lines.append(f" Language: {element.language}")
290
+
291
+ if hasattr(element, "visibility"):
292
+ lines.append(
293
+ f" Visibility: {getattr(element, 'visibility', 'unknown')}"
294
+ )
295
+ if hasattr(element, "parameters"):
296
+ params = getattr(element, "parameters", [])
297
+ if params:
298
+ lines.append(
299
+ f" Parameters: {', '.join(str(p) for p in params)}"
300
+ )
301
+ if hasattr(element, "return_type"):
302
+ ret_type = getattr(element, "return_type", None)
303
+ if ret_type:
304
+ lines.append(f" Return Type: {ret_type}")
305
+
306
+ lines.append("")
307
+
308
+ lines.append("")
309
+
310
+ return "\n".join(lines)
311
+
312
+
313
+ class CompactFormatter(IFormatter):
314
+ """Compact formatter for CodeElement lists"""
315
+
316
+ @staticmethod
317
+ def get_format_name() -> str:
318
+ return "compact"
319
+
320
+ def format(self, elements: list[CodeElement]) -> str:
321
+ """Format elements in compact format"""
322
+ if not elements:
323
+ return "No elements found."
324
+
325
+ lines = []
326
+ lines.append("CODE ELEMENTS")
327
+ lines.append("-" * 20)
328
+
329
+ for element in elements:
330
+ element_type = getattr(element, "element_type", "unknown")
331
+ visibility = getattr(element, "visibility", "")
332
+ vis_symbol = self._get_visibility_symbol(visibility)
333
+
334
+ line = f"{vis_symbol} {element.name} ({element_type}) [{element.start_line}-{element.end_line}]"
335
+ lines.append(line)
336
+
337
+ return "\n".join(lines)
338
+
339
+ def _get_visibility_symbol(self, visibility: str) -> str:
340
+ """Get symbol for visibility"""
341
+ mapping = {"public": "+", "private": "-", "protected": "#", "package": "~"}
342
+ return mapping.get(visibility, "?")
343
+
344
+
345
+ # Register built-in formatters
346
+ def register_builtin_formatters() -> None:
347
+ """Register all built-in formatters"""
348
+ FormatterRegistry.register_formatter(JsonFormatter)
349
+
350
+ # Register legacy formatters for backward compatibility (v1.6.1.4 format restoration)
351
+ try:
352
+ from .legacy_formatter_adapters import (
353
+ LegacyCompactFormatter,
354
+ LegacyCsvFormatter,
355
+ LegacyFullFormatter,
356
+ )
357
+
358
+ # Replace broken v1.9.4 formatters with legacy-compatible ones
359
+ FormatterRegistry.register_formatter(LegacyCsvFormatter)
360
+ FormatterRegistry.register_formatter(LegacyFullFormatter)
361
+ FormatterRegistry.register_formatter(LegacyCompactFormatter)
362
+
363
+ logger.info("Registered legacy formatters for v1.6.1.4 compatibility")
364
+ except ImportError as e:
365
+ logger.warning(f"Failed to register legacy formatters: {e}")
366
+ # Fallback to broken v1.9.4 formatters (should not happen in normal operation)
367
+ FormatterRegistry.register_formatter(CsvFormatter)
368
+ FormatterRegistry.register_formatter(FullFormatter)
369
+ FormatterRegistry.register_formatter(CompactFormatter)
370
+
371
+ # NOTE: HTML formatters are intentionally excluded from analyze_code_structure
372
+ # as they are not part of the v1.6.1.4 specification and cause format regression.
373
+ # HTML formatters can still be registered separately for other tools if needed.
374
+
375
+
376
+ # Auto-register built-in formatters when module is imported
377
+ register_builtin_formatters()
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Formatter selector service - chooses appropriate formatter based on configuration.
4
+ """
5
+
6
+ from typing import Any
7
+
8
+ from ..table_formatter import create_table_formatter
9
+ from .formatter_config import get_formatter_strategy
10
+ from .language_formatter_factory import create_language_formatter
11
+
12
+
13
+ class FormatterSelector:
14
+ """
15
+ Service for selecting appropriate formatter based on language and format type.
16
+
17
+ This service decouples command layer from formatter layer by using explicit
18
+ configuration instead of implicit "if formatter exists" logic.
19
+ """
20
+
21
+ @staticmethod
22
+ def get_formatter(language: str, format_type: str, **kwargs: Any) -> Any:
23
+ """
24
+ Get appropriate formatter for language and format type.
25
+
26
+ Args:
27
+ language: Programming language name
28
+ format_type: Output format type (table, compact, full, csv, json)
29
+ **kwargs: Additional arguments for formatter (e.g., include_javadoc)
30
+
31
+ Returns:
32
+ Formatter instance (either legacy TableFormatter or new language-specific formatter)
33
+
34
+ Example:
35
+ >>> formatter = FormatterSelector.get_formatter("java", "compact")
36
+ >>> output = formatter.format_structure(data)
37
+ """
38
+ strategy = get_formatter_strategy(language, format_type)
39
+
40
+ if strategy == "new":
41
+ return FormatterSelector._create_new_formatter(
42
+ language, format_type, **kwargs
43
+ )
44
+ else:
45
+ return FormatterSelector._create_legacy_formatter(
46
+ language, format_type, **kwargs
47
+ )
48
+
49
+ @staticmethod
50
+ def _create_new_formatter(language: str, format_type: str, **kwargs: Any) -> Any:
51
+ """Create formatter from new system (LanguageFormatterFactory)."""
52
+ formatter = create_language_formatter(language)
53
+ if formatter is None:
54
+ # Fallback to legacy if new formatter doesn't exist
55
+ return FormatterSelector._create_legacy_formatter(
56
+ language, format_type, **kwargs
57
+ )
58
+
59
+ # Set format type on formatter if it supports it
60
+ if hasattr(formatter, "format_type"):
61
+ formatter.format_type = format_type
62
+
63
+ return formatter
64
+
65
+ @staticmethod
66
+ def _create_legacy_formatter(language: str, format_type: str, **kwargs: Any) -> Any:
67
+ """Create formatter from legacy system (TableFormatter)."""
68
+ include_javadoc = kwargs.get("include_javadoc", False)
69
+ return create_table_formatter(format_type, language, include_javadoc)
70
+
71
+ @staticmethod
72
+ def is_legacy_formatter(language: str, format_type: str) -> bool:
73
+ """
74
+ Check if language uses legacy formatter for given format type.
75
+
76
+ Args:
77
+ language: Programming language name
78
+ format_type: Output format type
79
+
80
+ Returns:
81
+ True if legacy formatter should be used, False otherwise
82
+ """
83
+ strategy = get_formatter_strategy(language, format_type)
84
+ return strategy == "legacy"
85
+
86
+ @staticmethod
87
+ def get_supported_languages() -> list[str]:
88
+ """
89
+ Get list of all supported languages.
90
+
91
+ Returns:
92
+ List of supported language names
93
+ """
94
+ from .formatter_config import LANGUAGE_FORMATTER_CONFIG
95
+
96
+ return list(LANGUAGE_FORMATTER_CONFIG.keys())