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
@@ -25,7 +25,7 @@ from ..encoding_utils import extract_text_slice, safe_encode
25
25
  from ..models import AnalysisResult, Class, CodeElement, Function, Import, Variable
26
26
  from ..plugins.base import ElementExtractor, LanguagePlugin
27
27
  from ..utils import log_debug, log_error, log_warning
28
- from ..utils.tree_sitter_compat import TreeSitterQueryCompat, get_node_text_safe
28
+ from ..utils.tree_sitter_compat import TreeSitterQueryCompat
29
29
 
30
30
 
31
31
  class PythonElementExtractor(ElementExtractor):
@@ -72,7 +72,7 @@ class PythonElementExtractor(ElementExtractor):
72
72
  if tree is None or tree.root_node is None:
73
73
  log_debug("Tree or root_node is None, returning empty functions list")
74
74
  return functions
75
-
75
+
76
76
  try:
77
77
  self._traverse_and_extract_iterative(
78
78
  tree.root_node, extractors, functions, "function"
@@ -103,7 +103,7 @@ class PythonElementExtractor(ElementExtractor):
103
103
  if tree is None or tree.root_node is None:
104
104
  log_debug("Tree or root_node is None, returning empty classes list")
105
105
  return classes
106
-
106
+
107
107
  self._traverse_and_extract_iterative(
108
108
  tree.root_node, extractors, classes, "class"
109
109
  )
@@ -136,9 +136,11 @@ class PythonElementExtractor(ElementExtractor):
136
136
  if capture_name == "class.body":
137
137
  class_bodies.append(node)
138
138
  except Exception as e:
139
- log_debug(f"Could not extract Python class attributes using query: {e}")
139
+ log_debug(
140
+ f"Could not extract Python class attributes using query: {e}"
141
+ )
140
142
  class_bodies = []
141
-
143
+
142
144
  # For each class body, extract attribute assignments
143
145
  for class_body in class_bodies:
144
146
  variables.extend(
@@ -267,7 +269,7 @@ class PythonElementExtractor(ElementExtractor):
267
269
  except TypeError:
268
270
  # If children is not iterable, skip
269
271
  children = []
270
-
272
+
271
273
  for child in children:
272
274
  node_stack.append((child, depth + 1))
273
275
 
@@ -301,10 +303,10 @@ class PythonElementExtractor(ElementExtractor):
301
303
  end_point = node.end_point
302
304
 
303
305
  # Validate points are within bounds
304
- if (start_point[0] < 0 or start_point[0] >= len(self.content_lines)):
306
+ if start_point[0] < 0 or start_point[0] >= len(self.content_lines):
305
307
  return ""
306
-
307
- if (end_point[0] < 0 or end_point[0] >= len(self.content_lines)):
308
+
309
+ if end_point[0] < 0 or end_point[0] >= len(self.content_lines):
308
310
  return ""
309
311
 
310
312
  if start_point[0] == end_point[0]:
@@ -504,8 +506,8 @@ class PythonElementExtractor(ElementExtractor):
504
506
  # Join preserving formatting and add leading newline for multi-line
505
507
  docstring = "\n".join(docstring_lines)
506
508
  # Add leading newline for multi-line docstrings to match expected format
507
- if not docstring.startswith('\n'):
508
- docstring = '\n' + docstring
509
+ if not docstring.startswith("\n"):
510
+ docstring = "\n" + docstring
509
511
  self._docstring_cache[target_line] = docstring
510
512
  return docstring
511
513
 
@@ -519,7 +521,7 @@ class PythonElementExtractor(ElementExtractor):
519
521
  def _calculate_complexity_optimized(self, node: "tree_sitter.Node") -> int:
520
522
  """Calculate cyclomatic complexity efficiently"""
521
523
  import re
522
-
524
+
523
525
  node_id = id(node)
524
526
  if node_id in self._complexity_cache:
525
527
  return self._complexity_cache[node_id]
@@ -541,7 +543,7 @@ class PythonElementExtractor(ElementExtractor):
541
543
  ]
542
544
  for keyword in keywords:
543
545
  # More flexible keyword matching
544
- pattern = rf'\b{keyword}\b'
546
+ pattern = rf"\b{keyword}\b"
545
547
  matches = re.findall(pattern, node_text)
546
548
  complexity += len(matches)
547
549
  except Exception as e:
@@ -580,7 +582,11 @@ class PythonElementExtractor(ElementExtractor):
580
582
  if child.children: # Check if children exists and is not None
581
583
  for grandchild in child.children:
582
584
  if grandchild.type == "identifier":
583
- superclass_name = grandchild.text.decode("utf8") if grandchild.text else None
585
+ superclass_name = (
586
+ grandchild.text.decode("utf8")
587
+ if grandchild.text
588
+ else None
589
+ )
584
590
  if superclass_name:
585
591
  superclasses.append(superclass_name)
586
592
 
@@ -740,14 +746,14 @@ class PythonElementExtractor(ElementExtractor):
740
746
  captures = TreeSitterQueryCompat.safe_execute_query(
741
747
  language, query_string, tree.root_node, fallback_result=[]
742
748
  )
743
-
749
+
744
750
  # Group captures by name
745
751
  captures_dict = {}
746
752
  for node, capture_name in captures:
747
753
  if capture_name not in captures_dict:
748
754
  captures_dict[capture_name] = []
749
755
  captures_dict[capture_name].append(node)
750
-
756
+
751
757
  # Process different types of imports
752
758
  for key, nodes in captures_dict.items():
753
759
  if key.endswith("statement"):
@@ -760,8 +766,12 @@ class PythonElementExtractor(ElementExtractor):
760
766
  imports.append(imp)
761
767
  except Exception as query_error:
762
768
  # Fallback to manual extraction for tree-sitter compatibility
763
- log_debug(f"Query execution failed, using manual extraction: {query_error}")
764
- imports.extend(self._extract_imports_manual(tree.root_node, source_code))
769
+ log_debug(
770
+ f"Query execution failed, using manual extraction: {query_error}"
771
+ )
772
+ imports.extend(
773
+ self._extract_imports_manual(tree.root_node, source_code)
774
+ )
765
775
  break
766
776
 
767
777
  except Exception as e:
@@ -771,51 +781,67 @@ class PythonElementExtractor(ElementExtractor):
771
781
 
772
782
  return imports
773
783
 
774
- def _extract_imports_manual(self, root_node: "tree_sitter.Node", source_code: str) -> list[Import]:
784
+ def _extract_imports_manual(
785
+ self, root_node: "tree_sitter.Node", source_code: str
786
+ ) -> list[Import]:
775
787
  """Manual import extraction for tree-sitter 0.25.x compatibility"""
776
788
  imports = []
777
-
789
+
778
790
  def walk_tree(node):
779
791
  if node.type in ["import_statement", "import_from_statement"]:
780
792
  try:
781
793
  start_line = node.start_point[0] + 1
782
794
  end_line = node.end_point[0] + 1
783
- raw_text = source_code[node.start_byte:node.end_byte] if hasattr(node, 'start_byte') else ""
784
-
795
+ raw_text = (
796
+ source_code[node.start_byte : node.end_byte]
797
+ if hasattr(node, "start_byte")
798
+ else ""
799
+ )
800
+
785
801
  # Extract module name from the import statement
786
802
  module_name = ""
787
803
  imported_names = []
788
-
804
+
789
805
  if node.type == "import_statement":
790
806
  # Simple import: import os, sys
791
807
  for child in node.children:
792
808
  if child.type == "dotted_name":
793
- module_name = source_code[child.start_byte:child.end_byte] if hasattr(child, 'start_byte') else ""
809
+ module_name = (
810
+ source_code[child.start_byte : child.end_byte]
811
+ if hasattr(child, "start_byte")
812
+ else ""
813
+ )
794
814
  imported_names.append(module_name)
795
815
  elif node.type == "import_from_statement":
796
816
  # From import: from os import path
797
817
  for child in node.children:
798
818
  if child.type == "dotted_name" and not module_name:
799
- module_name = source_code[child.start_byte:child.end_byte] if hasattr(child, 'start_byte') else ""
800
-
819
+ module_name = (
820
+ source_code[child.start_byte : child.end_byte]
821
+ if hasattr(child, "start_byte")
822
+ else ""
823
+ )
824
+
801
825
  if module_name or imported_names:
802
826
  import_obj = Import(
803
- name=module_name or imported_names[0] if imported_names else "unknown",
827
+ name=module_name or imported_names[0]
828
+ if imported_names
829
+ else "unknown",
804
830
  start_line=start_line,
805
831
  end_line=end_line,
806
832
  raw_text=raw_text,
807
833
  module_name=module_name,
808
834
  imported_names=imported_names,
809
- element_type="import"
835
+ element_type="import",
810
836
  )
811
837
  imports.append(import_obj)
812
838
  except Exception as e:
813
839
  log_warning(f"Failed to extract import manually: {e}")
814
-
840
+
815
841
  # Recursively process children
816
842
  for child in node.children:
817
843
  walk_tree(child)
818
-
844
+
819
845
  walk_tree(root_node)
820
846
  return imports
821
847
 
@@ -1140,7 +1166,7 @@ class PythonPlugin(LanguagePlugin):
1140
1166
  super().__init__()
1141
1167
  self._language_cache: tree_sitter.Language | None = None
1142
1168
  self._extractor: PythonElementExtractor | None = None
1143
-
1169
+
1144
1170
  # Legacy compatibility attributes for tests
1145
1171
  self.language = "python"
1146
1172
  self.extractor = self.get_extractor()
@@ -1167,22 +1193,30 @@ class PythonPlugin(LanguagePlugin):
1167
1193
  """Get the language name for Python (legacy compatibility)"""
1168
1194
  return "python"
1169
1195
 
1170
- def extract_functions(self, tree: "tree_sitter.Tree", source_code: str) -> list[Function]:
1196
+ def extract_functions(
1197
+ self, tree: "tree_sitter.Tree", source_code: str
1198
+ ) -> list[Function]:
1171
1199
  """Extract functions from the tree (legacy compatibility)"""
1172
1200
  extractor = self.get_extractor()
1173
1201
  return extractor.extract_functions(tree, source_code)
1174
1202
 
1175
- def extract_classes(self, tree: "tree_sitter.Tree", source_code: str) -> list[Class]:
1203
+ def extract_classes(
1204
+ self, tree: "tree_sitter.Tree", source_code: str
1205
+ ) -> list[Class]:
1176
1206
  """Extract classes from the tree (legacy compatibility)"""
1177
1207
  extractor = self.get_extractor()
1178
1208
  return extractor.extract_classes(tree, source_code)
1179
1209
 
1180
- def extract_variables(self, tree: "tree_sitter.Tree", source_code: str) -> list[Variable]:
1210
+ def extract_variables(
1211
+ self, tree: "tree_sitter.Tree", source_code: str
1212
+ ) -> list[Variable]:
1181
1213
  """Extract variables from the tree (legacy compatibility)"""
1182
1214
  extractor = self.get_extractor()
1183
1215
  return extractor.extract_variables(tree, source_code)
1184
1216
 
1185
- def extract_imports(self, tree: "tree_sitter.Tree", source_code: str) -> list[Import]:
1217
+ def extract_imports(
1218
+ self, tree: "tree_sitter.Tree", source_code: str
1219
+ ) -> list[Import]:
1186
1220
  """Extract imports from the tree (legacy compatibility)"""
1187
1221
  extractor = self.get_extractor()
1188
1222
  return extractor.extract_imports(tree, source_code)
@@ -1259,21 +1293,23 @@ class PythonPlugin(LanguagePlugin):
1259
1293
  ],
1260
1294
  }
1261
1295
 
1262
- def execute_query_strategy(self, tree: "tree_sitter.Tree", source_code: str, query_key: str) -> list[dict]:
1296
+ def execute_query_strategy(
1297
+ self, tree: "tree_sitter.Tree", source_code: str, query_key: str
1298
+ ) -> list[dict]:
1263
1299
  """
1264
1300
  Execute query strategy for Python language
1265
-
1301
+
1266
1302
  Args:
1267
1303
  tree: Tree-sitter tree object
1268
1304
  source_code: Source code string
1269
1305
  query_key: Query key to execute
1270
-
1306
+
1271
1307
  Returns:
1272
1308
  List of query results
1273
1309
  """
1274
1310
  # Use the extractor to get elements based on query_key
1275
1311
  extractor = self.get_extractor()
1276
-
1312
+
1277
1313
  # Map query keys to extraction methods
1278
1314
  if query_key in ["function", "functions", "method", "methods"]:
1279
1315
  elements = extractor.extract_functions(tree, source_code)
@@ -1286,7 +1322,7 @@ class PythonPlugin(LanguagePlugin):
1286
1322
  else:
1287
1323
  # For unknown query keys, return empty list
1288
1324
  return []
1289
-
1325
+
1290
1326
  # Convert elements to query result format
1291
1327
  results = []
1292
1328
  for element in elements:
@@ -1299,13 +1335,13 @@ class PythonPlugin(LanguagePlugin):
1299
1335
  "name": element.name,
1300
1336
  }
1301
1337
  results.append(result)
1302
-
1338
+
1303
1339
  return results
1304
-
1340
+
1305
1341
  def _get_node_type_for_element(self, element) -> str:
1306
1342
  """Get appropriate node type for element"""
1307
- from ..models import Function, Class, Variable, Import
1308
-
1343
+ from ..models import Class, Function, Import, Variable
1344
+
1309
1345
  if isinstance(element, Function):
1310
1346
  return "function_definition"
1311
1347
  elif isinstance(element, Class):
@@ -1320,7 +1356,7 @@ class PythonPlugin(LanguagePlugin):
1320
1356
  def get_element_categories(self) -> dict[str, list[str]]:
1321
1357
  """
1322
1358
  Get element categories mapping query keys to node types
1323
-
1359
+
1324
1360
  Returns:
1325
1361
  Dictionary mapping query keys to lists of node types
1326
1362
  """
@@ -1334,45 +1370,45 @@ class PythonPlugin(LanguagePlugin):
1334
1370
  "methods": ["function_definition"],
1335
1371
  "lambda": ["lambda"],
1336
1372
  "lambdas": ["lambda"],
1337
-
1338
1373
  # Class-related queries
1339
1374
  "class": ["class_definition"],
1340
1375
  "classes": ["class_definition"],
1341
-
1342
1376
  # Import-related queries
1343
1377
  "import": ["import_statement", "import_from_statement"],
1344
1378
  "imports": ["import_statement", "import_from_statement"],
1345
1379
  "from_import": ["import_from_statement"],
1346
1380
  "from_imports": ["import_from_statement"],
1347
-
1348
1381
  # Variable-related queries
1349
1382
  "variable": ["assignment"],
1350
1383
  "variables": ["assignment"],
1351
-
1352
1384
  # Decorator-related queries
1353
1385
  "decorator": ["decorator"],
1354
1386
  "decorators": ["decorator"],
1355
-
1356
1387
  # Exception-related queries
1357
1388
  "exception": ["raise_statement", "except_clause"],
1358
1389
  "exceptions": ["raise_statement", "except_clause"],
1359
-
1360
1390
  # Comprehension-related queries
1361
- "comprehension": ["list_comprehension", "set_comprehension", "dictionary_comprehension", "generator_expression"],
1362
- "comprehensions": ["list_comprehension", "set_comprehension", "dictionary_comprehension", "generator_expression"],
1363
-
1391
+ "comprehension": [
1392
+ "list_comprehension",
1393
+ "set_comprehension",
1394
+ "dictionary_comprehension",
1395
+ "generator_expression",
1396
+ ],
1397
+ "comprehensions": [
1398
+ "list_comprehension",
1399
+ "set_comprehension",
1400
+ "dictionary_comprehension",
1401
+ "generator_expression",
1402
+ ],
1364
1403
  # Context manager queries
1365
1404
  "context_manager": ["with_statement"],
1366
1405
  "context_managers": ["with_statement"],
1367
-
1368
1406
  # Type hint queries
1369
1407
  "type_hint": ["type"],
1370
1408
  "type_hints": ["type"],
1371
-
1372
1409
  # Docstring queries
1373
1410
  "docstring": ["string"],
1374
1411
  "docstrings": ["string"],
1375
-
1376
1412
  # Framework-specific queries
1377
1413
  "django_model": ["class_definition"],
1378
1414
  "django_models": ["class_definition"],
@@ -1380,13 +1416,24 @@ class PythonPlugin(LanguagePlugin):
1380
1416
  "flask_routes": ["decorator"],
1381
1417
  "fastapi_endpoint": ["function_definition"],
1382
1418
  "fastapi_endpoints": ["function_definition"],
1383
-
1384
1419
  # Generic queries
1385
1420
  "all_elements": [
1386
- "function_definition", "class_definition", "import_statement", "import_from_statement",
1387
- "assignment", "decorator", "raise_statement", "except_clause",
1388
- "list_comprehension", "set_comprehension", "dictionary_comprehension", "generator_expression",
1389
- "with_statement", "type", "string", "lambda"
1421
+ "function_definition",
1422
+ "class_definition",
1423
+ "import_statement",
1424
+ "import_from_statement",
1425
+ "assignment",
1426
+ "decorator",
1427
+ "raise_statement",
1428
+ "except_clause",
1429
+ "list_comprehension",
1430
+ "set_comprehension",
1431
+ "dictionary_comprehension",
1432
+ "generator_expression",
1433
+ "with_statement",
1434
+ "type",
1435
+ "string",
1436
+ "lambda",
1390
1437
  ],
1391
1438
  }
1392
1439
 
@@ -1486,7 +1533,7 @@ class PythonPlugin(LanguagePlugin):
1486
1533
  """Extract elements from source code using tree-sitter AST"""
1487
1534
  extractor = self.get_extractor()
1488
1535
  elements = []
1489
-
1536
+
1490
1537
  try:
1491
1538
  elements.extend(extractor.extract_functions(tree, source_code))
1492
1539
  elements.extend(extractor.extract_classes(tree, source_code))
@@ -1494,5 +1541,5 @@ class PythonPlugin(LanguagePlugin):
1494
1541
  elements.extend(extractor.extract_imports(tree, source_code))
1495
1542
  except Exception as e:
1496
1543
  log_error(f"Failed to extract elements: {e}")
1497
-
1544
+
1498
1545
  return elements