tree-sitter-analyzer 1.8.4__py3-none-any.whl → 1.9.0__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 (64) hide show
  1. tree_sitter_analyzer/__init__.py +1 -1
  2. tree_sitter_analyzer/api.py +4 -4
  3. tree_sitter_analyzer/cli/argument_validator.py +29 -17
  4. tree_sitter_analyzer/cli/commands/advanced_command.py +7 -5
  5. tree_sitter_analyzer/cli/commands/structure_command.py +7 -5
  6. tree_sitter_analyzer/cli/commands/summary_command.py +10 -6
  7. tree_sitter_analyzer/cli/commands/table_command.py +8 -7
  8. tree_sitter_analyzer/cli/info_commands.py +1 -1
  9. tree_sitter_analyzer/cli_main.py +3 -2
  10. tree_sitter_analyzer/core/analysis_engine.py +5 -5
  11. tree_sitter_analyzer/core/cache_service.py +3 -1
  12. tree_sitter_analyzer/core/query.py +17 -5
  13. tree_sitter_analyzer/core/query_service.py +1 -1
  14. tree_sitter_analyzer/encoding_utils.py +3 -3
  15. tree_sitter_analyzer/exceptions.py +61 -50
  16. tree_sitter_analyzer/file_handler.py +3 -0
  17. tree_sitter_analyzer/formatters/base_formatter.py +10 -5
  18. tree_sitter_analyzer/formatters/formatter_registry.py +83 -68
  19. tree_sitter_analyzer/formatters/html_formatter.py +90 -54
  20. tree_sitter_analyzer/formatters/javascript_formatter.py +21 -16
  21. tree_sitter_analyzer/formatters/language_formatter_factory.py +7 -6
  22. tree_sitter_analyzer/formatters/markdown_formatter.py +247 -124
  23. tree_sitter_analyzer/formatters/python_formatter.py +61 -38
  24. tree_sitter_analyzer/formatters/typescript_formatter.py +113 -45
  25. tree_sitter_analyzer/interfaces/mcp_server.py +2 -2
  26. tree_sitter_analyzer/language_detector.py +6 -6
  27. tree_sitter_analyzer/language_loader.py +3 -1
  28. tree_sitter_analyzer/languages/css_plugin.py +120 -61
  29. tree_sitter_analyzer/languages/html_plugin.py +159 -62
  30. tree_sitter_analyzer/languages/java_plugin.py +42 -34
  31. tree_sitter_analyzer/languages/javascript_plugin.py +59 -30
  32. tree_sitter_analyzer/languages/markdown_plugin.py +402 -368
  33. tree_sitter_analyzer/languages/python_plugin.py +111 -64
  34. tree_sitter_analyzer/languages/typescript_plugin.py +241 -132
  35. tree_sitter_analyzer/mcp/server.py +22 -18
  36. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +13 -8
  37. tree_sitter_analyzer/mcp/tools/base_tool.py +2 -2
  38. tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +232 -26
  39. tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +31 -23
  40. tree_sitter_analyzer/mcp/tools/list_files_tool.py +21 -19
  41. tree_sitter_analyzer/mcp/tools/query_tool.py +17 -18
  42. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +30 -31
  43. tree_sitter_analyzer/mcp/tools/search_content_tool.py +131 -77
  44. tree_sitter_analyzer/mcp/tools/table_format_tool.py +29 -16
  45. tree_sitter_analyzer/mcp/utils/file_output_factory.py +64 -51
  46. tree_sitter_analyzer/mcp/utils/file_output_manager.py +34 -24
  47. tree_sitter_analyzer/mcp/utils/gitignore_detector.py +8 -4
  48. tree_sitter_analyzer/models.py +7 -5
  49. tree_sitter_analyzer/plugins/base.py +9 -7
  50. tree_sitter_analyzer/plugins/manager.py +1 -0
  51. tree_sitter_analyzer/queries/css.py +2 -21
  52. tree_sitter_analyzer/queries/html.py +2 -15
  53. tree_sitter_analyzer/queries/markdown.py +30 -41
  54. tree_sitter_analyzer/queries/python.py +20 -5
  55. tree_sitter_analyzer/query_loader.py +5 -5
  56. tree_sitter_analyzer/security/validator.py +114 -86
  57. tree_sitter_analyzer/utils/__init__.py +58 -28
  58. tree_sitter_analyzer/utils/tree_sitter_compat.py +72 -65
  59. tree_sitter_analyzer/utils.py +26 -15
  60. {tree_sitter_analyzer-1.8.4.dist-info → tree_sitter_analyzer-1.9.0.dist-info}/METADATA +1 -1
  61. tree_sitter_analyzer-1.9.0.dist-info/RECORD +109 -0
  62. tree_sitter_analyzer-1.8.4.dist-info/RECORD +0 -109
  63. {tree_sitter_analyzer-1.8.4.dist-info → tree_sitter_analyzer-1.9.0.dist-info}/WHEEL +0 -0
  64. {tree_sitter_analyzer-1.8.4.dist-info → tree_sitter_analyzer-1.9.0.dist-info}/entry_points.txt +0 -0
@@ -10,13 +10,17 @@ import json
10
10
  from typing import Any
11
11
 
12
12
  from ..models import CodeElement, MarkupElement, StyleElement
13
- from .formatter_registry import IFormatter
14
13
  from .base_formatter import BaseFormatter
14
+ from .formatter_registry import IFormatter
15
15
 
16
16
 
17
17
  class HtmlFormatter(BaseFormatter, IFormatter):
18
18
  """HTML-specific formatter for MarkupElement and StyleElement"""
19
19
 
20
+ def __init__(self):
21
+ """Initialize HTML formatter"""
22
+ pass
23
+
20
24
  @staticmethod
21
25
  def get_format_name() -> str:
22
26
  return "html"
@@ -34,7 +38,7 @@ class HtmlFormatter(BaseFormatter, IFormatter):
34
38
  markup_elements = []
35
39
  style_elements = []
36
40
  other_elements = []
37
-
41
+
38
42
  for e in elements:
39
43
  if isinstance(e, MarkupElement):
40
44
  markup_elements.append(e)
@@ -42,10 +46,10 @@ class HtmlFormatter(BaseFormatter, IFormatter):
42
46
  style_elements.append(e)
43
47
  elif isinstance(e, dict):
44
48
  # Convert dictionary to appropriate element type based on content
45
- element_type = e.get('type', e.get('element_type', 'unknown'))
46
- if 'tag_name' in e or element_type in ['tag', 'element', 'markup']:
49
+ element_type = e.get("type", e.get("element_type", "unknown"))
50
+ if "tag_name" in e or element_type in ["tag", "element", "markup"]:
47
51
  markup_elements.append(self._dict_to_markup_element(e))
48
- elif 'selector' in e or element_type in ['rule', 'style']:
52
+ elif "selector" in e or element_type in ["rule", "style"]:
49
53
  style_elements.append(self._dict_to_style_element(e))
50
54
  else:
51
55
  other_elements.append(e)
@@ -71,11 +75,11 @@ class HtmlFormatter(BaseFormatter, IFormatter):
71
75
  elements = analysis_result.get("elements", [])
72
76
  if not elements:
73
77
  return "No HTML elements found."
74
-
78
+
75
79
  markup_count = sum(1 for e in elements if isinstance(e, MarkupElement))
76
80
  style_count = sum(1 for e in elements if isinstance(e, StyleElement))
77
81
  other_count = len(elements) - markup_count - style_count
78
-
82
+
79
83
  lines = []
80
84
  lines.append("# HTML Analysis Summary")
81
85
  lines.append("")
@@ -83,7 +87,7 @@ class HtmlFormatter(BaseFormatter, IFormatter):
83
87
  lines.append(f"- Markup Elements: {markup_count}")
84
88
  lines.append(f"- Style Elements: {style_count}")
85
89
  lines.append(f"- Other Elements: {other_count}")
86
-
90
+
87
91
  return "\n".join(lines)
88
92
 
89
93
  def format_structure(self, analysis_result: dict[str, Any]) -> str:
@@ -91,20 +95,24 @@ class HtmlFormatter(BaseFormatter, IFormatter):
91
95
  elements = analysis_result.get("elements", [])
92
96
  return self.format(elements)
93
97
 
94
- def format_advanced(self, analysis_result: dict[str, Any], output_format: str = "json") -> str:
98
+ def format_advanced(
99
+ self, analysis_result: dict[str, Any], output_format: str = "json"
100
+ ) -> str:
95
101
  """Format advanced analysis output"""
96
102
  elements = analysis_result.get("elements", [])
97
-
103
+
98
104
  if output_format == "json":
99
105
  formatter = HtmlJsonFormatter()
100
106
  return formatter.format(elements)
101
107
  else:
102
108
  return self.format(elements)
103
109
 
104
- def format_table(self, analysis_result: dict[str, Any], table_type: str = "full") -> str:
110
+ def format_table(
111
+ self, analysis_result: dict[str, Any], table_type: str = "full"
112
+ ) -> str:
105
113
  """Format table output"""
106
114
  elements = analysis_result.get("elements", [])
107
-
115
+
108
116
  if table_type == "compact":
109
117
  formatter = HtmlCompactFormatter()
110
118
  return formatter.format(elements)
@@ -131,7 +139,9 @@ class HtmlFormatter(BaseFormatter, IFormatter):
131
139
 
132
140
  # Format each group
133
141
  for element_class, group_elements in element_groups.items():
134
- lines.append(f"### {element_class.title()} Elements ({len(group_elements)})")
142
+ lines.append(
143
+ f"### {element_class.title()} Elements ({len(group_elements)})"
144
+ )
135
145
  lines.append("")
136
146
  lines.append("| Tag | Name | Lines | Attributes | Children |")
137
147
  lines.append("|-----|------|-------|------------|----------|")
@@ -140,13 +150,13 @@ class HtmlFormatter(BaseFormatter, IFormatter):
140
150
  tag_name = element.tag_name or "unknown"
141
151
  name = element.name or tag_name
142
152
  lines_str = f"{element.start_line}-{element.end_line}"
143
-
153
+
144
154
  # Format attributes
145
155
  attrs = []
146
156
  attributes = element.attributes or {}
147
157
  for key, value in attributes.items():
148
158
  if value:
149
- attrs.append(f"{key}=\"{value}\"")
159
+ attrs.append(f'{key}="{value}"')
150
160
  else:
151
161
  attrs.append(key)
152
162
  attrs_str = ", ".join(attrs) if attrs else "-"
@@ -156,7 +166,9 @@ class HtmlFormatter(BaseFormatter, IFormatter):
156
166
  # Count children
157
167
  children_count = len(element.children)
158
168
 
159
- lines.append(f"| `{tag_name}` | {name} | {lines_str} | {attrs_str} | {children_count} |")
169
+ lines.append(
170
+ f"| `{tag_name}` | {name} | {lines_str} | {attrs_str} | {children_count} |"
171
+ )
160
172
 
161
173
  lines.append("")
162
174
 
@@ -176,7 +188,7 @@ class HtmlFormatter(BaseFormatter, IFormatter):
176
188
  lines = []
177
189
  indent = " " * depth
178
190
  tag_name = element.tag_name or "unknown"
179
-
191
+
180
192
  # Format element info
181
193
  attrs_info = ""
182
194
  attributes = element.attributes or {}
@@ -184,11 +196,13 @@ class HtmlFormatter(BaseFormatter, IFormatter):
184
196
  key_attrs = []
185
197
  for key, value in attributes.items():
186
198
  if key in ["id", "class", "name"]:
187
- key_attrs.append(f"{key}=\"{value}\"" if value else key)
199
+ key_attrs.append(f'{key}="{value}"' if value else key)
188
200
  if key_attrs:
189
201
  attrs_info = f" ({', '.join(key_attrs)})"
190
202
 
191
- lines.append(f"{indent}- `{tag_name}`{attrs_info} [{element.start_line}-{element.end_line}]")
203
+ lines.append(
204
+ f"{indent}- `{tag_name}`{attrs_info} [{element.start_line}-{element.end_line}]"
205
+ )
192
206
 
193
207
  # Format children
194
208
  for child in element.children:
@@ -220,7 +234,7 @@ class HtmlFormatter(BaseFormatter, IFormatter):
220
234
  for element in group_elements:
221
235
  selector = element.selector or element.name
222
236
  lines_str = f"{element.start_line}-{element.end_line}"
223
-
237
+
224
238
  # Format properties
225
239
  props = []
226
240
  properties = element.properties or {}
@@ -246,7 +260,9 @@ class HtmlFormatter(BaseFormatter, IFormatter):
246
260
 
247
261
  for element in elements:
248
262
  if isinstance(element, dict):
249
- element_type = element.get("element_type", element.get("type", "unknown"))
263
+ element_type = element.get(
264
+ "element_type", element.get("type", "unknown")
265
+ )
250
266
  name = element.get("name", "unknown")
251
267
  start_line = element.get("start_line", 0)
252
268
  end_line = element.get("end_line", 0)
@@ -257,7 +273,7 @@ class HtmlFormatter(BaseFormatter, IFormatter):
257
273
  start_line = getattr(element, "start_line", 0)
258
274
  end_line = getattr(element, "end_line", 0)
259
275
  language = getattr(element, "language", "unknown")
260
-
276
+
261
277
  lines_str = f"{start_line}-{end_line}"
262
278
  lines.append(f"| {element_type} | {name} | {lines_str} | {language} |")
263
279
 
@@ -266,34 +282,36 @@ class HtmlFormatter(BaseFormatter, IFormatter):
266
282
 
267
283
  def _dict_to_markup_element(self, data: dict):
268
284
  """Convert dictionary to MarkupElement-like object"""
285
+
269
286
  # Create a mock MarkupElement-like object
270
287
  class MockMarkupElement:
271
288
  def __init__(self, data):
272
- self.name = data.get('name', 'unknown')
273
- self.tag_name = data.get('tag_name', data.get('name', 'unknown'))
274
- self.element_class = data.get('element_class', 'unknown')
275
- self.start_line = data.get('start_line', 0)
276
- self.end_line = data.get('end_line', 0)
277
- self.attributes = data.get('attributes', {})
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", {})
278
295
  self.children = []
279
296
  self.parent = None
280
- self.language = data.get('language', 'html')
281
-
297
+ self.language = data.get("language", "html")
298
+
282
299
  return MockMarkupElement(data)
283
300
 
284
301
  def _dict_to_style_element(self, data: dict):
285
302
  """Convert dictionary to StyleElement-like object"""
303
+
286
304
  # Create a mock StyleElement-like object
287
305
  class MockStyleElement:
288
306
  def __init__(self, data):
289
- self.name = data.get('name', 'unknown')
290
- self.selector = data.get('selector', 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.properties = data.get('properties', {})
295
- self.language = data.get('language', 'css')
296
-
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
+
297
315
  return MockStyleElement(data)
298
316
 
299
317
 
@@ -311,26 +329,38 @@ class HtmlJsonFormatter(IFormatter):
311
329
  "total_elements": len(elements),
312
330
  "markup_elements": [],
313
331
  "style_elements": [],
314
- "other_elements": []
332
+ "other_elements": [],
315
333
  }
316
334
  }
317
335
 
318
336
  for element in elements:
319
337
  if isinstance(element, MarkupElement):
320
- result["html_analysis"]["markup_elements"].append(self._markup_to_dict(element))
338
+ result["html_analysis"]["markup_elements"].append(
339
+ self._markup_to_dict(element)
340
+ )
321
341
  elif isinstance(element, StyleElement):
322
- result["html_analysis"]["style_elements"].append(self._style_to_dict(element))
342
+ result["html_analysis"]["style_elements"].append(
343
+ self._style_to_dict(element)
344
+ )
323
345
  elif isinstance(element, dict):
324
346
  # Handle dictionary format
325
- element_type = element.get("element_type", element.get("type", "unknown"))
326
- if "tag_name" in element or element_type in ['tag', 'element', 'markup']:
347
+ element_type = element.get(
348
+ "element_type", element.get("type", "unknown")
349
+ )
350
+ if "tag_name" in element or element_type in [
351
+ "tag",
352
+ "element",
353
+ "markup",
354
+ ]:
327
355
  result["html_analysis"]["markup_elements"].append(element)
328
- elif "selector" in element or element_type in ['rule', 'style']:
356
+ elif "selector" in element or element_type in ["rule", "style"]:
329
357
  result["html_analysis"]["style_elements"].append(element)
330
358
  else:
331
359
  result["html_analysis"]["other_elements"].append(element)
332
360
  else:
333
- result["html_analysis"]["other_elements"].append(self._element_to_dict(element))
361
+ result["html_analysis"]["other_elements"].append(
362
+ self._element_to_dict(element)
363
+ )
334
364
 
335
365
  return json.dumps(result, indent=2, ensure_ascii=False)
336
366
 
@@ -345,7 +375,7 @@ class HtmlJsonFormatter(IFormatter):
345
375
  "attributes": element.attributes,
346
376
  "children_count": len(element.children),
347
377
  "children": [self._markup_to_dict(child) for child in element.children],
348
- "language": element.language
378
+ "language": element.language,
349
379
  }
350
380
 
351
381
  def _style_to_dict(self, element: StyleElement) -> dict[str, Any]:
@@ -357,7 +387,7 @@ class HtmlJsonFormatter(IFormatter):
357
387
  "start_line": element.start_line,
358
388
  "end_line": element.end_line,
359
389
  "properties": element.properties,
360
- "language": element.language
390
+ "language": element.language,
361
391
  }
362
392
 
363
393
  def _element_to_dict(self, element: CodeElement) -> dict[str, Any]:
@@ -367,7 +397,7 @@ class HtmlJsonFormatter(IFormatter):
367
397
  "type": getattr(element, "element_type", "unknown"),
368
398
  "start_line": element.start_line,
369
399
  "end_line": element.end_line,
370
- "language": element.language
400
+ "language": element.language,
371
401
  }
372
402
 
373
403
 
@@ -416,12 +446,18 @@ class HtmlCompactFormatter(IFormatter):
416
446
  end_line = element.end_line
417
447
  elif isinstance(element, dict):
418
448
  # Handle dictionary format
419
- element_type = element.get("element_type", element.get("type", "unknown"))
449
+ element_type = element.get(
450
+ "element_type", element.get("type", "unknown")
451
+ )
420
452
  name = element.get("name", "unknown")
421
453
  start_line = element.get("start_line", 0)
422
454
  end_line = element.get("end_line", 0)
423
-
424
- if "tag_name" in element or element_type in ['tag', 'element', 'markup']:
455
+
456
+ if "tag_name" in element or element_type in [
457
+ "tag",
458
+ "element",
459
+ "markup",
460
+ ]:
425
461
  symbol = "🏷️"
426
462
  tag_name = element.get("tag_name", name)
427
463
  info = f"<{tag_name}>"
@@ -430,7 +466,7 @@ class HtmlCompactFormatter(IFormatter):
430
466
  info += f" #{attributes['id']}"
431
467
  if attributes.get("class"):
432
468
  info += f" .{attributes['class']}"
433
- elif "selector" in element or element_type in ['rule', 'style']:
469
+ elif "selector" in element or element_type in ["rule", "style"]:
434
470
  symbol = "🎨"
435
471
  info = element.get("selector", name)
436
472
  else:
@@ -452,11 +488,11 @@ class HtmlCompactFormatter(IFormatter):
452
488
  def register_html_formatters() -> None:
453
489
  """Register HTML-specific formatters"""
454
490
  from .formatter_registry import FormatterRegistry
455
-
491
+
456
492
  FormatterRegistry.register_formatter(HtmlFormatter)
457
493
  FormatterRegistry.register_formatter(HtmlJsonFormatter)
458
494
  FormatterRegistry.register_formatter(HtmlCompactFormatter)
459
495
 
460
496
 
461
497
  # Auto-register when module is imported
462
- register_html_formatters()
498
+ register_html_formatters()
@@ -20,21 +20,23 @@ class JavaScriptTableFormatter(BaseTableFormatter):
20
20
  # Handle None data
21
21
  if data is None:
22
22
  return "# No data available\n"
23
-
23
+
24
24
  # Ensure data is a dictionary
25
25
  if not isinstance(data, dict):
26
26
  return f"# Invalid data type: {type(data)}\n"
27
-
27
+
28
28
  if format_type:
29
29
  # Check for supported format types
30
- supported_formats = ['full', 'compact', 'csv', 'json']
30
+ supported_formats = ["full", "compact", "csv", "json"]
31
31
  if format_type not in supported_formats:
32
- raise ValueError(f"Unsupported format type: {format_type}. Supported formats: {supported_formats}")
33
-
32
+ raise ValueError(
33
+ f"Unsupported format type: {format_type}. Supported formats: {supported_formats}"
34
+ )
35
+
34
36
  # Handle json format separately
35
- if format_type == 'json':
37
+ if format_type == "json":
36
38
  return self._format_json(data)
37
-
39
+
38
40
  # Temporarily change format type for this call
39
41
  original_format = self.format_type
40
42
  self.format_type = format_type
@@ -47,10 +49,10 @@ class JavaScriptTableFormatter(BaseTableFormatter):
47
49
  """Full table format for JavaScript"""
48
50
  if data is None:
49
51
  return "# No data available\n"
50
-
52
+
51
53
  if not isinstance(data, dict):
52
54
  return f"# Invalid data type: {type(data)}\n"
53
-
55
+
54
56
  lines = []
55
57
 
56
58
  # Header - JavaScript (module/file based)
@@ -479,10 +481,12 @@ class JavaScriptTableFormatter(BaseTableFormatter):
479
481
  return "array"
480
482
  elif value_str.startswith("{") and value_str.endswith("}"):
481
483
  return "object"
482
- elif (value_str.startswith("function") or
483
- value_str.startswith("async function") or
484
- value_str.startswith("new Function") or
485
- "=>" in value_str):
484
+ elif (
485
+ value_str.startswith("function")
486
+ or value_str.startswith("async function")
487
+ or value_str.startswith("new Function")
488
+ or "=>" in value_str
489
+ ):
486
490
  return "function"
487
491
  elif value_str.startswith("class"):
488
492
  return "class"
@@ -548,19 +552,20 @@ class JavaScriptTableFormatter(BaseTableFormatter):
548
552
  """Get class information as formatted string"""
549
553
  if cls is None:
550
554
  return "Unknown (0 methods)"
551
-
555
+
552
556
  if not isinstance(cls, dict):
553
557
  return f"{str(cls)} (0 methods)"
554
-
558
+
555
559
  name = str(cls.get("name", "Unknown"))
556
560
  methods = cls.get("methods", [])
557
561
  method_count = len(methods) if isinstance(methods, list) else 0
558
-
562
+
559
563
  return f"{name} ({method_count} methods)"
560
564
 
561
565
  def _format_json(self, data: dict[str, Any]) -> str:
562
566
  """Format data as JSON"""
563
567
  import json
568
+
564
569
  try:
565
570
  return json.dumps(data, indent=2, ensure_ascii=False)
566
571
  except (TypeError, ValueError) as e:
@@ -3,20 +3,19 @@
3
3
  Factory for creating language-specific formatters for different output types.
4
4
  """
5
5
 
6
- from typing import Dict, Type, Any
7
6
  from .base_formatter import BaseFormatter
8
- from .markdown_formatter import MarkdownFormatter
9
7
  from .html_formatter import HtmlFormatter
8
+ from .markdown_formatter import MarkdownFormatter
10
9
 
11
10
 
12
11
  class LanguageFormatterFactory:
13
12
  """Factory for creating language-specific formatters"""
14
13
 
15
- _formatters: Dict[str, Type[BaseFormatter]] = {
14
+ _formatters: dict[str, type[BaseFormatter]] = {
16
15
  "markdown": MarkdownFormatter,
17
16
  "md": MarkdownFormatter, # Alias
18
17
  "html": HtmlFormatter,
19
- "css": HtmlFormatter, # CSS files also use HTML formatter
18
+ "css": HtmlFormatter, # CSS files also use HTML formatter
20
19
  }
21
20
 
22
21
  @classmethod
@@ -39,7 +38,9 @@ class LanguageFormatterFactory:
39
38
  return formatter_class()
40
39
 
41
40
  @classmethod
42
- def register_formatter(cls, language: str, formatter_class: Type[BaseFormatter]) -> None:
41
+ def register_formatter(
42
+ cls, language: str, formatter_class: type[BaseFormatter]
43
+ ) -> None:
43
44
  """
44
45
  Register new language formatter
45
46
 
@@ -83,4 +84,4 @@ def create_language_formatter(language: str) -> BaseFormatter:
83
84
  Returns:
84
85
  Language formatter or None if not supported
85
86
  """
86
- return LanguageFormatterFactory.create_formatter(language)
87
+ return LanguageFormatterFactory.create_formatter(language)