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,498 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ HTML Formatter
4
+
5
+ Specialized formatter for HTML/CSS code elements including MarkupElement and StyleElement.
6
+ Provides HTML-specific formatting with element classification and hierarchy display.
7
+ """
8
+
9
+ import json
10
+ from typing import Any
11
+
12
+ from ..models import CodeElement, MarkupElement, StyleElement
13
+ from .base_formatter import BaseFormatter
14
+ from .formatter_registry import IFormatter
15
+
16
+
17
+ class HtmlFormatter(BaseFormatter, IFormatter):
18
+ """HTML-specific formatter for MarkupElement and StyleElement"""
19
+
20
+ def __init__(self) -> None:
21
+ """Initialize HTML formatter"""
22
+ pass
23
+
24
+ @staticmethod
25
+ def get_format_name() -> str:
26
+ return "html"
27
+
28
+ def format(self, elements: list[CodeElement]) -> str:
29
+ """Format HTML elements with hierarchy and classification"""
30
+ if not elements:
31
+ return "No HTML elements found."
32
+
33
+ lines = []
34
+ lines.append("# HTML Structure Analysis")
35
+ lines.append("")
36
+
37
+ # Handle both CodeElement objects and dictionaries
38
+ markup_elements = []
39
+ style_elements = []
40
+ other_elements: list[dict[str, Any]] = []
41
+
42
+ for e in elements:
43
+ if isinstance(e, MarkupElement):
44
+ markup_elements.append(e)
45
+ elif isinstance(e, StyleElement):
46
+ style_elements.append(e)
47
+ elif isinstance(e, dict):
48
+ # Convert dictionary to appropriate element type based on content
49
+ element_type = e.get("type", e.get("element_type", "unknown"))
50
+ if "tag_name" in e or element_type in ["tag", "element", "markup"]:
51
+ markup_elements.append(self._dict_to_markup_element(e))
52
+ elif "selector" in e or element_type in ["rule", "style"]:
53
+ style_elements.append(self._dict_to_style_element(e))
54
+ else:
55
+ other_elements.append(e)
56
+ else:
57
+ other_elements.append(self._element_to_dict(e))
58
+
59
+ # Format markup elements
60
+ if markup_elements:
61
+ lines.extend(self._format_markup_elements(markup_elements))
62
+
63
+ # Format style elements
64
+ if style_elements:
65
+ lines.extend(self._format_style_elements(style_elements))
66
+
67
+ # Format other elements
68
+ if other_elements:
69
+ lines.extend(self._format_other_elements(other_elements))
70
+
71
+ return "\n".join(lines)
72
+
73
+ def format_summary(self, analysis_result: dict[str, Any]) -> str:
74
+ """Format summary output for HTML elements"""
75
+ elements = analysis_result.get("elements", [])
76
+ if not elements:
77
+ return "No HTML elements found."
78
+
79
+ markup_count = sum(1 for e in elements if isinstance(e, MarkupElement))
80
+ style_count = sum(1 for e in elements if isinstance(e, StyleElement))
81
+ other_count = len(elements) - markup_count - style_count
82
+
83
+ lines = []
84
+ lines.append("# HTML Analysis Summary")
85
+ lines.append("")
86
+ lines.append(f"**Total Elements:** {len(elements)}")
87
+ lines.append(f"- Markup Elements: {markup_count}")
88
+ lines.append(f"- Style Elements: {style_count}")
89
+ lines.append(f"- Other Elements: {other_count}")
90
+
91
+ return "\n".join(lines)
92
+
93
+ def format_structure(self, analysis_result: dict[str, Any]) -> str:
94
+ """Format structure analysis output"""
95
+ elements = analysis_result.get("elements", [])
96
+ return self.format(elements)
97
+
98
+ def format_advanced(
99
+ self, analysis_result: dict[str, Any], output_format: str = "json"
100
+ ) -> str:
101
+ """Format advanced analysis output"""
102
+ elements = analysis_result.get("elements", [])
103
+
104
+ if output_format == "json":
105
+ formatter = HtmlJsonFormatter()
106
+ return formatter.format(elements)
107
+ else:
108
+ return self.format(elements)
109
+
110
+ def format_table(
111
+ self, analysis_result: dict[str, Any], table_type: str = "full"
112
+ ) -> str:
113
+ """Format table output"""
114
+ elements = analysis_result.get("elements", [])
115
+
116
+ if table_type == "compact":
117
+ formatter: IFormatter = HtmlCompactFormatter()
118
+ return formatter.format(elements)
119
+ elif table_type == "json":
120
+ formatter = HtmlJsonFormatter()
121
+ return formatter.format(elements)
122
+ else:
123
+ # Default to full format (including "html" and "full")
124
+ return self.format(elements)
125
+
126
+ def _format_markup_elements(self, elements: list[MarkupElement]) -> list[str]:
127
+ """Format MarkupElement list with hierarchy"""
128
+ lines = []
129
+ lines.append("## HTML Elements")
130
+ lines.append("")
131
+
132
+ # Group by element class
133
+ element_groups: dict[str, list[MarkupElement]] = {}
134
+ for element in elements:
135
+ element_class = element.element_class or "unknown"
136
+ if element_class not in element_groups:
137
+ element_groups[element_class] = []
138
+ element_groups[element_class].append(element)
139
+
140
+ # Format each group
141
+ for element_class, group_elements in element_groups.items():
142
+ lines.append(
143
+ f"### {element_class.title()} Elements ({len(group_elements)})"
144
+ )
145
+ lines.append("")
146
+ lines.append("| Tag | Name | Lines | Attributes | Children |")
147
+ lines.append("|-----|------|-------|------------|----------|")
148
+
149
+ for element in group_elements:
150
+ tag_name = element.tag_name or "unknown"
151
+ name = element.name or tag_name
152
+ lines_str = f"{element.start_line}-{element.end_line}"
153
+
154
+ # Format attributes
155
+ attrs = []
156
+ attributes = element.attributes or {}
157
+ for key, value in attributes.items():
158
+ if value:
159
+ attrs.append(f'{key}="{value}"')
160
+ else:
161
+ attrs.append(key)
162
+ attrs_str = ", ".join(attrs) if attrs else "-"
163
+ if len(attrs_str) > 30:
164
+ attrs_str = attrs_str[:27] + "..."
165
+
166
+ # Count children
167
+ children_count = len(element.children)
168
+
169
+ lines.append(
170
+ f"| `{tag_name}` | {name} | {lines_str} | {attrs_str} | {children_count} |"
171
+ )
172
+
173
+ lines.append("")
174
+
175
+ # Show hierarchy for root elements
176
+ root_elements = [e for e in elements if e.parent is None]
177
+ if root_elements and len(root_elements) < len(elements):
178
+ lines.append("### Element Hierarchy")
179
+ lines.append("")
180
+ for root in root_elements:
181
+ lines.extend(self._format_element_tree(root, 0))
182
+ lines.append("")
183
+
184
+ return lines
185
+
186
+ def _format_element_tree(self, element: MarkupElement, depth: int) -> list[str]:
187
+ """Format element tree hierarchy"""
188
+ lines = []
189
+ indent = " " * depth
190
+ tag_name = element.tag_name or "unknown"
191
+
192
+ # Format element info
193
+ attrs_info = ""
194
+ attributes = element.attributes or {}
195
+ if attributes:
196
+ key_attrs = []
197
+ for key, value in attributes.items():
198
+ if key in ["id", "class", "name"]:
199
+ key_attrs.append(f'{key}="{value}"' if value else key)
200
+ if key_attrs:
201
+ attrs_info = f" ({', '.join(key_attrs)})"
202
+
203
+ lines.append(
204
+ f"{indent}- `{tag_name}`{attrs_info} [{element.start_line}-{element.end_line}]"
205
+ )
206
+
207
+ # Format children
208
+ for child in element.children:
209
+ lines.extend(self._format_element_tree(child, depth + 1))
210
+
211
+ return lines
212
+
213
+ def _format_style_elements(self, elements: list[StyleElement]) -> list[str]:
214
+ """Format StyleElement list"""
215
+ lines = []
216
+ lines.append("## CSS Rules")
217
+ lines.append("")
218
+
219
+ # Group by element class
220
+ element_groups: dict[str, list[StyleElement]] = {}
221
+ for element in elements:
222
+ element_class = element.element_class or "unknown"
223
+ if element_class not in element_groups:
224
+ element_groups[element_class] = []
225
+ element_groups[element_class].append(element)
226
+
227
+ # Format each group
228
+ for element_class, group_elements in element_groups.items():
229
+ lines.append(f"### {element_class.title()} Rules ({len(group_elements)})")
230
+ lines.append("")
231
+ lines.append("| Selector | Properties | Lines |")
232
+ lines.append("|----------|------------|-------|")
233
+
234
+ for element in group_elements:
235
+ selector = element.selector or element.name
236
+ lines_str = f"{element.start_line}-{element.end_line}"
237
+
238
+ # Format properties
239
+ props = []
240
+ properties = element.properties or {}
241
+ for key, value in properties.items():
242
+ props.append(f"{key}: {value}")
243
+ props_str = "; ".join(props) if props else "-"
244
+ if len(props_str) > 40:
245
+ props_str = props_str[:37] + "..."
246
+
247
+ lines.append(f"| `{selector}` | {props_str} | {lines_str} |")
248
+
249
+ lines.append("")
250
+
251
+ return lines
252
+
253
+ def _format_other_elements(self, elements: list) -> list[str]:
254
+ """Format other code elements"""
255
+ lines = []
256
+ lines.append("## Other Elements")
257
+ lines.append("")
258
+ lines.append("| Type | Name | Lines | Language |")
259
+ lines.append("|------|------|-------|----------|")
260
+
261
+ for element in elements:
262
+ if isinstance(element, dict):
263
+ element_type = element.get(
264
+ "element_type", element.get("type", "unknown")
265
+ )
266
+ name = element.get("name", "unknown")
267
+ start_line = element.get("start_line", 0)
268
+ end_line = element.get("end_line", 0)
269
+ language = element.get("language", "unknown")
270
+ else:
271
+ element_type = getattr(element, "element_type", "unknown")
272
+ name = getattr(element, "name", "unknown")
273
+ start_line = getattr(element, "start_line", 0)
274
+ end_line = getattr(element, "end_line", 0)
275
+ language = getattr(element, "language", "unknown")
276
+
277
+ lines_str = f"{start_line}-{end_line}"
278
+ lines.append(f"| {element_type} | {name} | {lines_str} | {language} |")
279
+
280
+ lines.append("")
281
+ return lines
282
+
283
+ def _dict_to_markup_element(self, data: dict) -> Any:
284
+ """Convert dictionary to MarkupElement-like object"""
285
+
286
+ # Create a mock MarkupElement-like object
287
+ class MockMarkupElement:
288
+ def __init__(self, data: dict[str, Any]) -> None:
289
+ self.name = data.get("name", "unknown")
290
+ self.tag_name = data.get("tag_name", data.get("name", "unknown"))
291
+ self.element_class = data.get("element_class", "unknown")
292
+ self.start_line = data.get("start_line", 0)
293
+ self.end_line = data.get("end_line", 0)
294
+ self.attributes = data.get("attributes", {})
295
+ self.children: list[MockMarkupElement] = []
296
+ self.parent = None
297
+ self.language = data.get("language", "html")
298
+
299
+ return MockMarkupElement(data)
300
+
301
+ def _dict_to_style_element(self, data: dict) -> Any:
302
+ """Convert dictionary to StyleElement-like object"""
303
+
304
+ # Create a mock StyleElement-like object
305
+ class MockStyleElement:
306
+ def __init__(self, data: dict[str, Any]) -> None:
307
+ self.name = data.get("name", "unknown")
308
+ self.selector = data.get("selector", data.get("name", "unknown"))
309
+ self.element_class = data.get("element_class", "unknown")
310
+ self.start_line = data.get("start_line", 0)
311
+ self.end_line = data.get("end_line", 0)
312
+ self.properties = data.get("properties", {})
313
+ self.language = data.get("language", "css")
314
+
315
+ return MockStyleElement(data)
316
+
317
+ def _element_to_dict(self, element: CodeElement) -> dict[str, Any]:
318
+ """Convert generic CodeElement to dictionary"""
319
+ return {
320
+ "name": element.name,
321
+ "type": getattr(element, "element_type", "unknown"),
322
+ "start_line": element.start_line,
323
+ "end_line": element.end_line,
324
+ "language": element.language,
325
+ }
326
+
327
+
328
+ class HtmlJsonFormatter(IFormatter):
329
+ """JSON formatter specifically for HTML elements"""
330
+
331
+ @staticmethod
332
+ def get_format_name() -> str:
333
+ return "html_json"
334
+
335
+ def format(self, elements: list[CodeElement]) -> str:
336
+ """Format HTML elements as JSON with hierarchy"""
337
+ result: dict[str, Any] = {
338
+ "html_analysis": {
339
+ "total_elements": len(elements),
340
+ "markup_elements": [],
341
+ "style_elements": [],
342
+ "other_elements": [],
343
+ }
344
+ }
345
+
346
+ for element in elements:
347
+ if isinstance(element, MarkupElement):
348
+ result["html_analysis"]["markup_elements"].append(
349
+ self._markup_to_dict(element)
350
+ )
351
+ elif isinstance(element, StyleElement):
352
+ result["html_analysis"]["style_elements"].append(
353
+ self._style_to_dict(element)
354
+ )
355
+ elif isinstance(element, dict):
356
+ # Handle dictionary format
357
+ element_type = element.get(
358
+ "element_type", element.get("type", "unknown")
359
+ )
360
+ if "tag_name" in element or element_type in [
361
+ "tag",
362
+ "element",
363
+ "markup",
364
+ ]:
365
+ result["html_analysis"]["markup_elements"].append(element)
366
+ elif "selector" in element or element_type in ["rule", "style"]:
367
+ result["html_analysis"]["style_elements"].append(element)
368
+ else:
369
+ result["html_analysis"]["other_elements"].append(element)
370
+ else:
371
+ result["html_analysis"]["other_elements"].append(
372
+ self._element_to_dict(element)
373
+ )
374
+
375
+ return json.dumps(result, indent=2, ensure_ascii=False)
376
+
377
+ def _markup_to_dict(self, element: MarkupElement) -> dict[str, Any]:
378
+ """Convert MarkupElement to dictionary"""
379
+ return {
380
+ "name": element.name,
381
+ "tag_name": element.tag_name,
382
+ "element_class": element.element_class,
383
+ "start_line": element.start_line,
384
+ "end_line": element.end_line,
385
+ "attributes": element.attributes,
386
+ "children_count": len(element.children),
387
+ "children": [self._markup_to_dict(child) for child in element.children],
388
+ "language": element.language,
389
+ }
390
+
391
+ def _style_to_dict(self, element: StyleElement) -> dict[str, Any]:
392
+ """Convert StyleElement to dictionary"""
393
+ return {
394
+ "name": element.name,
395
+ "selector": element.selector,
396
+ "element_class": element.element_class,
397
+ "start_line": element.start_line,
398
+ "end_line": element.end_line,
399
+ "properties": element.properties,
400
+ "language": element.language,
401
+ }
402
+
403
+ def _element_to_dict(self, element: CodeElement) -> dict[str, Any]:
404
+ """Convert generic CodeElement to dictionary"""
405
+ return {
406
+ "name": element.name,
407
+ "type": getattr(element, "element_type", "unknown"),
408
+ "start_line": element.start_line,
409
+ "end_line": element.end_line,
410
+ "language": element.language,
411
+ }
412
+
413
+
414
+ class HtmlCompactFormatter(IFormatter):
415
+ """Compact formatter for HTML elements"""
416
+
417
+ @staticmethod
418
+ def get_format_name() -> str:
419
+ return "html_compact"
420
+
421
+ def format(self, elements: list[CodeElement]) -> str:
422
+ """Format HTML elements in compact format"""
423
+ if not elements:
424
+ return "No HTML elements found."
425
+
426
+ lines = []
427
+ lines.append("HTML ELEMENTS")
428
+ lines.append("-" * 20)
429
+
430
+ markup_count = sum(1 for e in elements if isinstance(e, MarkupElement))
431
+ style_count = sum(1 for e in elements if isinstance(e, StyleElement))
432
+ other_count = len(elements) - markup_count - style_count
433
+
434
+ lines.append(f"Total: {len(elements)} elements")
435
+ lines.append(f" Markup: {markup_count}")
436
+ lines.append(f" Style: {style_count}")
437
+ lines.append(f" Other: {other_count}")
438
+ lines.append("")
439
+
440
+ for element in elements:
441
+ if isinstance(element, MarkupElement):
442
+ symbol = "🏷️"
443
+ info = f"<{element.tag_name}>"
444
+ if element.attributes.get("id"):
445
+ info += f" #{element.attributes['id']}"
446
+ if element.attributes.get("class"):
447
+ info += f" .{element.attributes['class']}"
448
+ name = element.name
449
+ start_line = element.start_line
450
+ end_line = element.end_line
451
+ elif isinstance(element, StyleElement):
452
+ symbol = "🎨"
453
+ info = element.selector
454
+ name = element.name
455
+ start_line = element.start_line
456
+ end_line = element.end_line
457
+ elif isinstance(element, dict):
458
+ # Handle dictionary format
459
+ element_type = element.get(
460
+ "element_type", element.get("type", "unknown")
461
+ )
462
+ name = element.get("name", "unknown")
463
+ start_line = element.get("start_line", 0)
464
+ end_line = element.get("end_line", 0)
465
+
466
+ if "tag_name" in element or element_type in [
467
+ "tag",
468
+ "element",
469
+ "markup",
470
+ ]:
471
+ symbol = "🏷️"
472
+ tag_name = element.get("tag_name", name)
473
+ info = f"<{tag_name}>"
474
+ attributes = element.get("attributes", {})
475
+ if attributes.get("id"):
476
+ info += f" #{attributes['id']}"
477
+ if attributes.get("class"):
478
+ info += f" .{attributes['class']}"
479
+ elif "selector" in element or element_type in ["rule", "style"]:
480
+ symbol = "🎨"
481
+ info = str(element.get("selector", name))
482
+ else:
483
+ symbol = "📄"
484
+ info = str(element_type)
485
+ else:
486
+ symbol = "📄"
487
+ info = getattr(element, "element_type", "unknown")
488
+ name = getattr(element, "name", "unknown")
489
+ start_line = getattr(element, "start_line", 0)
490
+ end_line = getattr(element, "end_line", 0)
491
+
492
+ lines.append(f"{symbol} {name} {info} [{start_line}-{end_line}]")
493
+
494
+ return "\n".join(lines)
495
+
496
+
497
+ # HTML formatters are registered via formatter_registry.py
498
+ # to avoid duplicate registration warnings