tree-sitter-analyzer 1.7.5__py3-none-any.whl → 1.8.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tree-sitter-analyzer might be problematic. Click here for more details.

Files changed (47) hide show
  1. tree_sitter_analyzer/__init__.py +1 -1
  2. tree_sitter_analyzer/api.py +26 -32
  3. tree_sitter_analyzer/cli/argument_validator.py +77 -0
  4. tree_sitter_analyzer/cli/commands/table_command.py +7 -2
  5. tree_sitter_analyzer/cli_main.py +17 -3
  6. tree_sitter_analyzer/core/cache_service.py +15 -5
  7. tree_sitter_analyzer/core/query.py +33 -22
  8. tree_sitter_analyzer/core/query_service.py +179 -154
  9. tree_sitter_analyzer/exceptions.py +334 -0
  10. tree_sitter_analyzer/file_handler.py +16 -1
  11. tree_sitter_analyzer/formatters/formatter_registry.py +355 -0
  12. tree_sitter_analyzer/formatters/html_formatter.py +462 -0
  13. tree_sitter_analyzer/formatters/language_formatter_factory.py +3 -0
  14. tree_sitter_analyzer/formatters/markdown_formatter.py +1 -1
  15. tree_sitter_analyzer/interfaces/mcp_server.py +3 -1
  16. tree_sitter_analyzer/language_detector.py +91 -7
  17. tree_sitter_analyzer/languages/css_plugin.py +390 -0
  18. tree_sitter_analyzer/languages/html_plugin.py +395 -0
  19. tree_sitter_analyzer/languages/java_plugin.py +116 -0
  20. tree_sitter_analyzer/languages/javascript_plugin.py +113 -0
  21. tree_sitter_analyzer/languages/markdown_plugin.py +266 -46
  22. tree_sitter_analyzer/languages/python_plugin.py +176 -33
  23. tree_sitter_analyzer/languages/typescript_plugin.py +130 -1
  24. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +68 -3
  25. tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +32 -7
  26. tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +10 -0
  27. tree_sitter_analyzer/mcp/tools/list_files_tool.py +9 -0
  28. tree_sitter_analyzer/mcp/tools/query_tool.py +100 -52
  29. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +98 -14
  30. tree_sitter_analyzer/mcp/tools/search_content_tool.py +9 -0
  31. tree_sitter_analyzer/mcp/tools/table_format_tool.py +37 -13
  32. tree_sitter_analyzer/models.py +53 -0
  33. tree_sitter_analyzer/output_manager.py +1 -1
  34. tree_sitter_analyzer/plugins/base.py +50 -0
  35. tree_sitter_analyzer/plugins/manager.py +5 -1
  36. tree_sitter_analyzer/queries/css.py +634 -0
  37. tree_sitter_analyzer/queries/html.py +556 -0
  38. tree_sitter_analyzer/queries/markdown.py +54 -164
  39. tree_sitter_analyzer/query_loader.py +16 -3
  40. tree_sitter_analyzer/security/validator.py +343 -46
  41. tree_sitter_analyzer/utils/__init__.py +113 -0
  42. tree_sitter_analyzer/utils/tree_sitter_compat.py +282 -0
  43. tree_sitter_analyzer/utils.py +62 -24
  44. {tree_sitter_analyzer-1.7.5.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/METADATA +136 -14
  45. {tree_sitter_analyzer-1.7.5.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/RECORD +47 -38
  46. {tree_sitter_analyzer-1.7.5.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/entry_points.txt +2 -0
  47. {tree_sitter_analyzer-1.7.5.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/WHEEL +0 -0
@@ -0,0 +1,355 @@
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
+ from typing import Any
12
+
13
+ from ..models import CodeElement
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class IFormatter(ABC):
19
+ """
20
+ Interface for code element formatters.
21
+
22
+ All formatters must implement this interface to be compatible
23
+ with the FormatterRegistry system.
24
+ """
25
+
26
+ @staticmethod
27
+ @abstractmethod
28
+ def get_format_name() -> str:
29
+ """
30
+ Return the format name this formatter supports.
31
+
32
+ Returns:
33
+ Format name (e.g., "json", "csv", "markdown")
34
+ """
35
+ pass
36
+
37
+ @abstractmethod
38
+ def format(self, elements: list[CodeElement]) -> str:
39
+ """
40
+ Format a list of CodeElements into a string representation.
41
+
42
+ Args:
43
+ elements: List of CodeElement objects to format
44
+
45
+ Returns:
46
+ Formatted string representation
47
+ """
48
+ pass
49
+
50
+
51
+ class FormatterRegistry:
52
+ """
53
+ Registry for managing and providing formatter instances.
54
+
55
+ Implements the Registry pattern to allow dynamic registration
56
+ and retrieval of formatters by format name.
57
+ """
58
+
59
+ _formatters: dict[str, type[IFormatter]] = {}
60
+
61
+ @classmethod
62
+ def register_formatter(cls, formatter_class: type[IFormatter]) -> None:
63
+ """
64
+ Register a formatter class in the registry.
65
+
66
+ Args:
67
+ formatter_class: Formatter class implementing IFormatter
68
+
69
+ Raises:
70
+ ValueError: If formatter_class doesn't implement IFormatter
71
+ """
72
+ if not issubclass(formatter_class, IFormatter):
73
+ raise ValueError(f"Formatter class must implement IFormatter interface")
74
+
75
+ format_name = formatter_class.get_format_name()
76
+ if not format_name:
77
+ raise ValueError("Formatter must provide a non-empty format name")
78
+
79
+ if format_name in cls._formatters:
80
+ logger.warning(f"Overriding existing formatter for format: {format_name}")
81
+
82
+ cls._formatters[format_name] = formatter_class
83
+ logger.debug(f"Registered formatter for format: {format_name}")
84
+
85
+ @classmethod
86
+ def get_formatter(cls, format_name: str) -> IFormatter:
87
+ """
88
+ Get a formatter instance for the specified format.
89
+
90
+ Args:
91
+ format_name: Name of the format to get formatter for
92
+
93
+ Returns:
94
+ Formatter instance
95
+
96
+ Raises:
97
+ ValueError: If format is not supported
98
+ """
99
+ if format_name not in cls._formatters:
100
+ available_formats = list(cls._formatters.keys())
101
+ raise ValueError(
102
+ f"Unsupported format: {format_name}. "
103
+ f"Available formats: {available_formats}"
104
+ )
105
+
106
+ formatter_class = cls._formatters[format_name]
107
+ return formatter_class()
108
+
109
+ @classmethod
110
+ def get_available_formats(cls) -> list[str]:
111
+ """
112
+ Get list of all available format names.
113
+
114
+ Returns:
115
+ List of available format names
116
+ """
117
+ return list(cls._formatters.keys())
118
+
119
+ @classmethod
120
+ def is_format_supported(cls, format_name: str) -> bool:
121
+ """
122
+ Check if a format is supported.
123
+
124
+ Args:
125
+ format_name: Format name to check
126
+
127
+ Returns:
128
+ True if format is supported
129
+ """
130
+ return format_name in cls._formatters
131
+
132
+ @classmethod
133
+ def unregister_formatter(cls, format_name: str) -> bool:
134
+ """
135
+ Unregister a formatter for the specified format.
136
+
137
+ Args:
138
+ format_name: Format name to unregister
139
+
140
+ Returns:
141
+ True if formatter was unregistered, False if not found
142
+ """
143
+ if format_name in cls._formatters:
144
+ del cls._formatters[format_name]
145
+ logger.debug(f"Unregistered formatter for format: {format_name}")
146
+ return True
147
+ return False
148
+
149
+ @classmethod
150
+ def clear_registry(cls) -> None:
151
+ """
152
+ Clear all registered formatters.
153
+
154
+ This method is primarily for testing purposes.
155
+ """
156
+ cls._formatters.clear()
157
+ logger.debug("Cleared all registered formatters")
158
+
159
+
160
+ # Built-in formatter implementations
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
+ "Type", "Name", "Start Line", "End Line", "Language",
222
+ "Visibility", "Parameters", "Return Type", "Modifiers"
223
+ ])
224
+
225
+ # Write data rows
226
+ for element in elements:
227
+ writer.writerow([
228
+ getattr(element, "element_type", "unknown"),
229
+ element.name,
230
+ element.start_line,
231
+ element.end_line,
232
+ element.language,
233
+ getattr(element, "visibility", ""),
234
+ str(getattr(element, "parameters", [])),
235
+ getattr(element, "return_type", ""),
236
+ str(getattr(element, "modifiers", []))
237
+ ])
238
+
239
+ csv_content = output.getvalue()
240
+ output.close()
241
+ return csv_content.rstrip("\n")
242
+
243
+
244
+ class FullFormatter(IFormatter):
245
+ """Full table formatter for CodeElement lists"""
246
+
247
+ @staticmethod
248
+ def get_format_name() -> str:
249
+ return "full"
250
+
251
+ def format(self, elements: list[CodeElement]) -> str:
252
+ """Format elements as full table"""
253
+ if not elements:
254
+ return "No elements found."
255
+
256
+ lines = []
257
+ lines.append("=" * 80)
258
+ lines.append("CODE STRUCTURE ANALYSIS")
259
+ lines.append("=" * 80)
260
+ lines.append("")
261
+
262
+ # Group elements by type
263
+ element_groups: dict[str, list[CodeElement]] = {}
264
+ for element in elements:
265
+ element_type = getattr(element, "element_type", "unknown")
266
+ if element_type not in element_groups:
267
+ element_groups[element_type] = []
268
+ element_groups[element_type].append(element)
269
+
270
+ # Format each group
271
+ for element_type, group_elements in element_groups.items():
272
+ lines.append(f"{element_type.upper()}S ({len(group_elements)})")
273
+ lines.append("-" * 40)
274
+
275
+ for element in group_elements:
276
+ lines.append(f" {element.name}")
277
+ lines.append(f" Lines: {element.start_line}-{element.end_line}")
278
+ lines.append(f" Language: {element.language}")
279
+
280
+ if hasattr(element, "visibility"):
281
+ lines.append(f" Visibility: {getattr(element, 'visibility', 'unknown')}")
282
+ if hasattr(element, "parameters"):
283
+ params = getattr(element, "parameters", [])
284
+ if params:
285
+ lines.append(f" Parameters: {', '.join(str(p) for p in params)}")
286
+ if hasattr(element, "return_type"):
287
+ ret_type = getattr(element, "return_type", None)
288
+ if ret_type:
289
+ lines.append(f" Return Type: {ret_type}")
290
+
291
+ lines.append("")
292
+
293
+ lines.append("")
294
+
295
+ return "\n".join(lines)
296
+
297
+
298
+ class CompactFormatter(IFormatter):
299
+ """Compact formatter for CodeElement lists"""
300
+
301
+ @staticmethod
302
+ def get_format_name() -> str:
303
+ return "compact"
304
+
305
+ def format(self, elements: list[CodeElement]) -> str:
306
+ """Format elements in compact format"""
307
+ if not elements:
308
+ return "No elements found."
309
+
310
+ lines = []
311
+ lines.append("CODE ELEMENTS")
312
+ lines.append("-" * 20)
313
+
314
+ for element in elements:
315
+ element_type = getattr(element, "element_type", "unknown")
316
+ visibility = getattr(element, "visibility", "")
317
+ vis_symbol = self._get_visibility_symbol(visibility)
318
+
319
+ line = f"{vis_symbol} {element.name} ({element_type}) [{element.start_line}-{element.end_line}]"
320
+ lines.append(line)
321
+
322
+ return "\n".join(lines)
323
+
324
+ def _get_visibility_symbol(self, visibility: str) -> str:
325
+ """Get symbol for visibility"""
326
+ mapping = {
327
+ "public": "+",
328
+ "private": "-",
329
+ "protected": "#",
330
+ "package": "~"
331
+ }
332
+ return mapping.get(visibility, "?")
333
+
334
+
335
+ # Register built-in formatters
336
+ def register_builtin_formatters() -> None:
337
+ """Register all built-in formatters"""
338
+ FormatterRegistry.register_formatter(JsonFormatter)
339
+ FormatterRegistry.register_formatter(CsvFormatter)
340
+ FormatterRegistry.register_formatter(FullFormatter)
341
+ FormatterRegistry.register_formatter(CompactFormatter)
342
+
343
+ # Register HTML formatters if available
344
+ try:
345
+ from .html_formatter import HtmlFormatter, HtmlJsonFormatter, HtmlCompactFormatter
346
+ FormatterRegistry.register_formatter(HtmlFormatter)
347
+ FormatterRegistry.register_formatter(HtmlJsonFormatter)
348
+ FormatterRegistry.register_formatter(HtmlCompactFormatter)
349
+ except ImportError:
350
+ # HTML formatters not available, skip registration
351
+ pass
352
+
353
+
354
+ # Auto-register built-in formatters when module is imported
355
+ register_builtin_formatters()