tree-sitter-analyzer 1.7.7__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.
- tree_sitter_analyzer/__init__.py +1 -1
- tree_sitter_analyzer/api.py +23 -30
- tree_sitter_analyzer/cli/argument_validator.py +77 -0
- tree_sitter_analyzer/cli/commands/table_command.py +7 -2
- tree_sitter_analyzer/cli_main.py +17 -3
- tree_sitter_analyzer/core/cache_service.py +15 -5
- tree_sitter_analyzer/core/query.py +33 -22
- tree_sitter_analyzer/core/query_service.py +179 -154
- tree_sitter_analyzer/formatters/formatter_registry.py +355 -0
- tree_sitter_analyzer/formatters/html_formatter.py +462 -0
- tree_sitter_analyzer/formatters/language_formatter_factory.py +3 -0
- tree_sitter_analyzer/formatters/markdown_formatter.py +1 -1
- tree_sitter_analyzer/language_detector.py +80 -7
- tree_sitter_analyzer/languages/css_plugin.py +390 -0
- tree_sitter_analyzer/languages/html_plugin.py +395 -0
- tree_sitter_analyzer/languages/java_plugin.py +116 -0
- tree_sitter_analyzer/languages/javascript_plugin.py +113 -0
- tree_sitter_analyzer/languages/markdown_plugin.py +266 -46
- tree_sitter_analyzer/languages/python_plugin.py +176 -33
- tree_sitter_analyzer/languages/typescript_plugin.py +130 -1
- tree_sitter_analyzer/mcp/tools/query_tool.py +99 -58
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +24 -10
- tree_sitter_analyzer/models.py +53 -0
- tree_sitter_analyzer/output_manager.py +1 -1
- tree_sitter_analyzer/plugins/base.py +50 -0
- tree_sitter_analyzer/plugins/manager.py +5 -1
- tree_sitter_analyzer/queries/css.py +634 -0
- tree_sitter_analyzer/queries/html.py +556 -0
- tree_sitter_analyzer/queries/markdown.py +54 -164
- tree_sitter_analyzer/query_loader.py +16 -3
- tree_sitter_analyzer/security/validator.py +182 -44
- tree_sitter_analyzer/utils/__init__.py +113 -0
- tree_sitter_analyzer/utils/tree_sitter_compat.py +282 -0
- tree_sitter_analyzer/utils.py +62 -24
- {tree_sitter_analyzer-1.7.7.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/METADATA +120 -14
- {tree_sitter_analyzer-1.7.7.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/RECORD +38 -29
- {tree_sitter_analyzer-1.7.7.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/entry_points.txt +2 -0
- {tree_sitter_analyzer-1.7.7.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/WHEEL +0 -0
|
@@ -24,6 +24,7 @@ from ..encoding_utils import extract_text_slice, safe_encode
|
|
|
24
24
|
from ..models import AnalysisResult, CodeElement
|
|
25
25
|
from ..plugins.base import ElementExtractor, LanguagePlugin
|
|
26
26
|
from ..utils import log_debug, log_error, log_warning
|
|
27
|
+
from ..utils.tree_sitter_compat import TreeSitterQueryCompat, get_node_text_safe
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
class MarkdownElement(CodeElement):
|
|
@@ -1447,22 +1448,9 @@ class MarkdownPlugin(LanguagePlugin):
|
|
|
1447
1448
|
import tree_sitter
|
|
1448
1449
|
import tree_sitter_markdown as tsmarkdown
|
|
1449
1450
|
|
|
1450
|
-
#
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
language_capsule = tsmarkdown.language()
|
|
1454
|
-
self._language_cache = tree_sitter.Language(language_capsule)
|
|
1455
|
-
except (AttributeError, TypeError):
|
|
1456
|
-
# For older API or different format
|
|
1457
|
-
try:
|
|
1458
|
-
# Get Language object directly
|
|
1459
|
-
self._language_cache = tsmarkdown.language()
|
|
1460
|
-
except Exception:
|
|
1461
|
-
# Last resort: get directly from module
|
|
1462
|
-
if hasattr(tsmarkdown, 'LANGUAGE'):
|
|
1463
|
-
self._language_cache = tree_sitter.Language(tsmarkdown.LANGUAGE)
|
|
1464
|
-
else:
|
|
1465
|
-
raise ImportError("Cannot access markdown language")
|
|
1451
|
+
# Use modern tree-sitter-markdown API
|
|
1452
|
+
language_capsule = tsmarkdown.language()
|
|
1453
|
+
self._language_cache = tree_sitter.Language(language_capsule)
|
|
1466
1454
|
except ImportError:
|
|
1467
1455
|
log_error("tree-sitter-markdown not available")
|
|
1468
1456
|
return None
|
|
@@ -1637,35 +1625,11 @@ class MarkdownPlugin(LanguagePlugin):
|
|
|
1637
1625
|
except KeyError:
|
|
1638
1626
|
return {"error": f"Unknown query: {query_name}"}
|
|
1639
1627
|
|
|
1640
|
-
# Use
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
matches = []
|
|
1646
|
-
captures = []
|
|
1647
|
-
|
|
1648
|
-
# Walk through the tree and find matches manually
|
|
1649
|
-
def walk_tree(node):
|
|
1650
|
-
# This is a simplified approach - in practice, you'd want to use
|
|
1651
|
-
# the proper query execution method when it becomes available
|
|
1652
|
-
if query_name == "headers" and node.type in ["atx_heading", "setext_heading"]:
|
|
1653
|
-
matches.append(node)
|
|
1654
|
-
elif query_name == "code_blocks" and node.type in ["fenced_code_block", "indented_code_block"]:
|
|
1655
|
-
matches.append(node)
|
|
1656
|
-
elif query_name == "links" and node.type in ["link", "autolink", "reference_link"]:
|
|
1657
|
-
matches.append(node)
|
|
1658
|
-
|
|
1659
|
-
for child in node.children:
|
|
1660
|
-
walk_tree(child)
|
|
1661
|
-
|
|
1662
|
-
walk_tree(tree.root_node)
|
|
1663
|
-
|
|
1664
|
-
# Convert matches to capture format
|
|
1665
|
-
for match in matches:
|
|
1666
|
-
captures.append((match, query_name))
|
|
1667
|
-
|
|
1668
|
-
return {"captures": captures, "query": query_string, "matches": len(matches)}
|
|
1628
|
+
# Use tree-sitter API with modern handling
|
|
1629
|
+
captures = TreeSitterQueryCompat.safe_execute_query(
|
|
1630
|
+
language, query_string, tree.root_node, fallback_result=[]
|
|
1631
|
+
)
|
|
1632
|
+
return {"captures": captures, "query": query_string, "matches": len(captures)}
|
|
1669
1633
|
|
|
1670
1634
|
except Exception as e:
|
|
1671
1635
|
log_error(f"Query execution failed: {e}")
|
|
@@ -1692,4 +1656,260 @@ class MarkdownPlugin(LanguagePlugin):
|
|
|
1692
1656
|
except Exception as e:
|
|
1693
1657
|
log_error(f"Failed to extract elements: {e}")
|
|
1694
1658
|
|
|
1695
|
-
return elements
|
|
1659
|
+
return elements
|
|
1660
|
+
|
|
1661
|
+
def execute_query_strategy(self, tree: "tree_sitter.Tree", source_code: str, query_key: str) -> list[CodeElement]:
|
|
1662
|
+
"""Execute Markdown-specific query strategy based on query_key"""
|
|
1663
|
+
if not tree or not source_code:
|
|
1664
|
+
return []
|
|
1665
|
+
|
|
1666
|
+
# Initialize extractor with source code
|
|
1667
|
+
self._extractor.source_code = source_code
|
|
1668
|
+
self._extractor.content_lines = source_code.split("\n")
|
|
1669
|
+
self._extractor._reset_caches()
|
|
1670
|
+
|
|
1671
|
+
# Map query_key to appropriate extraction method
|
|
1672
|
+
query_mapping = {
|
|
1673
|
+
# Header-related queries (mapped to functions)
|
|
1674
|
+
"function": lambda: self._extractor.extract_headers(tree, source_code),
|
|
1675
|
+
"headers": lambda: self._extractor.extract_headers(tree, source_code),
|
|
1676
|
+
"heading": lambda: self._extractor.extract_headers(tree, source_code),
|
|
1677
|
+
|
|
1678
|
+
# Code block-related queries (mapped to classes)
|
|
1679
|
+
"class": lambda: self._extractor.extract_code_blocks(tree, source_code),
|
|
1680
|
+
"code_blocks": lambda: self._extractor.extract_code_blocks(tree, source_code),
|
|
1681
|
+
"code_block": lambda: self._extractor.extract_code_blocks(tree, source_code),
|
|
1682
|
+
|
|
1683
|
+
# Link and image queries (mapped to variables)
|
|
1684
|
+
"variable": lambda: self._extractor.extract_links(tree, source_code) + self._extractor.extract_images(tree, source_code),
|
|
1685
|
+
"links": lambda: self._extractor.extract_links(tree, source_code),
|
|
1686
|
+
"link": lambda: self._extractor.extract_links(tree, source_code),
|
|
1687
|
+
"images": lambda: self._extractor.extract_images(tree, source_code),
|
|
1688
|
+
"image": lambda: self._extractor.extract_images(tree, source_code),
|
|
1689
|
+
|
|
1690
|
+
# Reference queries (mapped to imports)
|
|
1691
|
+
"import": lambda: self._extractor.extract_references(tree, source_code),
|
|
1692
|
+
"references": lambda: self._extractor.extract_references(tree, source_code),
|
|
1693
|
+
"reference": lambda: self._extractor.extract_references(tree, source_code),
|
|
1694
|
+
|
|
1695
|
+
# List and table queries
|
|
1696
|
+
"lists": lambda: self._extractor.extract_lists(tree, source_code),
|
|
1697
|
+
"list": lambda: self._extractor.extract_lists(tree, source_code),
|
|
1698
|
+
"task_lists": lambda: [l for l in self._extractor.extract_lists(tree, source_code) if getattr(l, 'element_type', '') == 'task_list'],
|
|
1699
|
+
"tables": lambda: self._extractor.extract_tables(tree, source_code),
|
|
1700
|
+
"table": lambda: self._extractor.extract_tables(tree, source_code),
|
|
1701
|
+
|
|
1702
|
+
# Content structure queries
|
|
1703
|
+
"blockquotes": lambda: self._extractor.extract_blockquotes(tree, source_code),
|
|
1704
|
+
"blockquote": lambda: self._extractor.extract_blockquotes(tree, source_code),
|
|
1705
|
+
"horizontal_rules": lambda: self._extractor.extract_horizontal_rules(tree, source_code),
|
|
1706
|
+
"horizontal_rule": lambda: self._extractor.extract_horizontal_rules(tree, source_code),
|
|
1707
|
+
|
|
1708
|
+
# HTML and formatting queries
|
|
1709
|
+
"html_blocks": lambda: self._extractor.extract_html_elements(tree, source_code),
|
|
1710
|
+
"html_block": lambda: self._extractor.extract_html_elements(tree, source_code),
|
|
1711
|
+
"html": lambda: self._extractor.extract_html_elements(tree, source_code),
|
|
1712
|
+
"emphasis": lambda: self._extractor.extract_text_formatting(tree, source_code),
|
|
1713
|
+
"formatting": lambda: self._extractor.extract_text_formatting(tree, source_code),
|
|
1714
|
+
"text_formatting": lambda: self._extractor.extract_text_formatting(tree, source_code),
|
|
1715
|
+
"inline_code": lambda: [f for f in self._extractor.extract_text_formatting(tree, source_code) if getattr(f, 'element_type', '') == 'inline_code'],
|
|
1716
|
+
"strikethrough": lambda: [f for f in self._extractor.extract_text_formatting(tree, source_code) if getattr(f, 'element_type', '') == 'strikethrough'],
|
|
1717
|
+
|
|
1718
|
+
# Footnote queries
|
|
1719
|
+
"footnotes": lambda: self._extractor.extract_footnotes(tree, source_code),
|
|
1720
|
+
"footnote": lambda: self._extractor.extract_footnotes(tree, source_code),
|
|
1721
|
+
|
|
1722
|
+
# Comprehensive queries
|
|
1723
|
+
"all_elements": lambda: self.extract_elements(tree, source_code),
|
|
1724
|
+
"text_content": lambda: self._extractor.extract_headers(tree, source_code) + self._extractor.extract_text_formatting(tree, source_code),
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
# Execute the appropriate extraction method
|
|
1728
|
+
if query_key in query_mapping:
|
|
1729
|
+
try:
|
|
1730
|
+
return query_mapping[query_key]()
|
|
1731
|
+
except Exception as e:
|
|
1732
|
+
log_error(f"Error executing Markdown query '{query_key}': {e}")
|
|
1733
|
+
return []
|
|
1734
|
+
else:
|
|
1735
|
+
log_warning(f"Unsupported Markdown query key: {query_key}")
|
|
1736
|
+
return []
|
|
1737
|
+
|
|
1738
|
+
def get_element_categories(self) -> dict[str, list[str]]:
|
|
1739
|
+
"""Get Markdown element categories mapping query_key to node_types"""
|
|
1740
|
+
return {
|
|
1741
|
+
# Header categories (function-like)
|
|
1742
|
+
"function": [
|
|
1743
|
+
"atx_heading",
|
|
1744
|
+
"setext_heading"
|
|
1745
|
+
],
|
|
1746
|
+
"headers": [
|
|
1747
|
+
"atx_heading",
|
|
1748
|
+
"setext_heading"
|
|
1749
|
+
],
|
|
1750
|
+
"heading": [
|
|
1751
|
+
"atx_heading",
|
|
1752
|
+
"setext_heading"
|
|
1753
|
+
],
|
|
1754
|
+
|
|
1755
|
+
# Code block categories (class-like)
|
|
1756
|
+
"class": [
|
|
1757
|
+
"fenced_code_block",
|
|
1758
|
+
"indented_code_block"
|
|
1759
|
+
],
|
|
1760
|
+
"code_blocks": [
|
|
1761
|
+
"fenced_code_block",
|
|
1762
|
+
"indented_code_block"
|
|
1763
|
+
],
|
|
1764
|
+
"code_block": [
|
|
1765
|
+
"fenced_code_block",
|
|
1766
|
+
"indented_code_block"
|
|
1767
|
+
],
|
|
1768
|
+
|
|
1769
|
+
# Link and image categories (variable-like)
|
|
1770
|
+
"variable": [
|
|
1771
|
+
"inline", # Contains links and images
|
|
1772
|
+
"link",
|
|
1773
|
+
"autolink",
|
|
1774
|
+
"reference_link",
|
|
1775
|
+
"image"
|
|
1776
|
+
],
|
|
1777
|
+
"links": [
|
|
1778
|
+
"inline", # Contains inline links
|
|
1779
|
+
"link",
|
|
1780
|
+
"autolink",
|
|
1781
|
+
"reference_link"
|
|
1782
|
+
],
|
|
1783
|
+
"link": [
|
|
1784
|
+
"inline",
|
|
1785
|
+
"link",
|
|
1786
|
+
"autolink",
|
|
1787
|
+
"reference_link"
|
|
1788
|
+
],
|
|
1789
|
+
"images": [
|
|
1790
|
+
"inline", # Contains inline images
|
|
1791
|
+
"image"
|
|
1792
|
+
],
|
|
1793
|
+
"image": [
|
|
1794
|
+
"inline",
|
|
1795
|
+
"image"
|
|
1796
|
+
],
|
|
1797
|
+
|
|
1798
|
+
# Reference categories (import-like)
|
|
1799
|
+
"import": [
|
|
1800
|
+
"link_reference_definition"
|
|
1801
|
+
],
|
|
1802
|
+
"references": [
|
|
1803
|
+
"link_reference_definition"
|
|
1804
|
+
],
|
|
1805
|
+
"reference": [
|
|
1806
|
+
"link_reference_definition"
|
|
1807
|
+
],
|
|
1808
|
+
|
|
1809
|
+
# List categories
|
|
1810
|
+
"lists": [
|
|
1811
|
+
"list",
|
|
1812
|
+
"list_item"
|
|
1813
|
+
],
|
|
1814
|
+
"list": [
|
|
1815
|
+
"list",
|
|
1816
|
+
"list_item"
|
|
1817
|
+
],
|
|
1818
|
+
"task_lists": [
|
|
1819
|
+
"list",
|
|
1820
|
+
"list_item"
|
|
1821
|
+
],
|
|
1822
|
+
|
|
1823
|
+
# Table categories
|
|
1824
|
+
"tables": [
|
|
1825
|
+
"pipe_table",
|
|
1826
|
+
"table"
|
|
1827
|
+
],
|
|
1828
|
+
"table": [
|
|
1829
|
+
"pipe_table",
|
|
1830
|
+
"table"
|
|
1831
|
+
],
|
|
1832
|
+
|
|
1833
|
+
# Content structure categories
|
|
1834
|
+
"blockquotes": [
|
|
1835
|
+
"block_quote"
|
|
1836
|
+
],
|
|
1837
|
+
"blockquote": [
|
|
1838
|
+
"block_quote"
|
|
1839
|
+
],
|
|
1840
|
+
"horizontal_rules": [
|
|
1841
|
+
"thematic_break"
|
|
1842
|
+
],
|
|
1843
|
+
"horizontal_rule": [
|
|
1844
|
+
"thematic_break"
|
|
1845
|
+
],
|
|
1846
|
+
|
|
1847
|
+
# HTML categories
|
|
1848
|
+
"html_blocks": [
|
|
1849
|
+
"html_block",
|
|
1850
|
+
"inline" # Contains inline HTML
|
|
1851
|
+
],
|
|
1852
|
+
"html_block": [
|
|
1853
|
+
"html_block",
|
|
1854
|
+
"inline"
|
|
1855
|
+
],
|
|
1856
|
+
"html": [
|
|
1857
|
+
"html_block",
|
|
1858
|
+
"inline"
|
|
1859
|
+
],
|
|
1860
|
+
|
|
1861
|
+
# Text formatting categories
|
|
1862
|
+
"emphasis": [
|
|
1863
|
+
"inline" # Contains emphasis elements
|
|
1864
|
+
],
|
|
1865
|
+
"formatting": [
|
|
1866
|
+
"inline"
|
|
1867
|
+
],
|
|
1868
|
+
"text_formatting": [
|
|
1869
|
+
"inline"
|
|
1870
|
+
],
|
|
1871
|
+
"inline_code": [
|
|
1872
|
+
"inline"
|
|
1873
|
+
],
|
|
1874
|
+
"strikethrough": [
|
|
1875
|
+
"inline"
|
|
1876
|
+
],
|
|
1877
|
+
|
|
1878
|
+
# Footnote categories
|
|
1879
|
+
"footnotes": [
|
|
1880
|
+
"inline", # Contains footnote references
|
|
1881
|
+
"paragraph" # Contains footnote definitions
|
|
1882
|
+
],
|
|
1883
|
+
"footnote": [
|
|
1884
|
+
"inline",
|
|
1885
|
+
"paragraph"
|
|
1886
|
+
],
|
|
1887
|
+
|
|
1888
|
+
# Comprehensive categories
|
|
1889
|
+
"all_elements": [
|
|
1890
|
+
"atx_heading",
|
|
1891
|
+
"setext_heading",
|
|
1892
|
+
"fenced_code_block",
|
|
1893
|
+
"indented_code_block",
|
|
1894
|
+
"inline",
|
|
1895
|
+
"link",
|
|
1896
|
+
"autolink",
|
|
1897
|
+
"reference_link",
|
|
1898
|
+
"image",
|
|
1899
|
+
"link_reference_definition",
|
|
1900
|
+
"list",
|
|
1901
|
+
"list_item",
|
|
1902
|
+
"pipe_table",
|
|
1903
|
+
"table",
|
|
1904
|
+
"block_quote",
|
|
1905
|
+
"thematic_break",
|
|
1906
|
+
"html_block",
|
|
1907
|
+
"paragraph"
|
|
1908
|
+
],
|
|
1909
|
+
"text_content": [
|
|
1910
|
+
"atx_heading",
|
|
1911
|
+
"setext_heading",
|
|
1912
|
+
"inline",
|
|
1913
|
+
"paragraph"
|
|
1914
|
+
]
|
|
1915
|
+
}
|
|
@@ -25,6 +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
29
|
|
|
29
30
|
|
|
30
31
|
class PythonElementExtractor(ElementExtractor):
|
|
@@ -118,7 +119,7 @@ class PythonElementExtractor(ElementExtractor):
|
|
|
118
119
|
|
|
119
120
|
# Only extract class-level attributes, not function-level variables
|
|
120
121
|
try:
|
|
121
|
-
# Find class declarations
|
|
122
|
+
# Find class declarations using compatible API
|
|
122
123
|
class_query = """
|
|
123
124
|
(class_definition
|
|
124
125
|
body: (block) @class.body) @class.definition
|
|
@@ -126,18 +127,23 @@ class PythonElementExtractor(ElementExtractor):
|
|
|
126
127
|
|
|
127
128
|
language = tree.language if hasattr(tree, "language") else None
|
|
128
129
|
if language:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
130
|
+
try:
|
|
131
|
+
captures = TreeSitterQueryCompat.safe_execute_query(
|
|
132
|
+
language, class_query, tree.root_node, fallback_result=[]
|
|
133
|
+
)
|
|
134
|
+
class_bodies = []
|
|
135
|
+
for node, capture_name in captures:
|
|
136
|
+
if capture_name == "class.body":
|
|
137
|
+
class_bodies.append(node)
|
|
138
|
+
except Exception as e:
|
|
139
|
+
log_debug(f"Could not extract Python class attributes using query: {e}")
|
|
140
|
+
class_bodies = []
|
|
141
|
+
|
|
142
|
+
# For each class body, extract attribute assignments
|
|
143
|
+
for class_body in class_bodies:
|
|
144
|
+
variables.extend(
|
|
145
|
+
self._extract_class_attributes(class_body, source_code)
|
|
146
|
+
)
|
|
141
147
|
|
|
142
148
|
except Exception as e:
|
|
143
149
|
log_warning(f"Could not extract Python class attributes: {e}")
|
|
@@ -731,24 +737,30 @@ class PythonElementExtractor(ElementExtractor):
|
|
|
731
737
|
if language:
|
|
732
738
|
for query_string in import_queries:
|
|
733
739
|
try:
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
740
|
+
captures = TreeSitterQueryCompat.safe_execute_query(
|
|
741
|
+
language, query_string, tree.root_node, fallback_result=[]
|
|
742
|
+
)
|
|
743
|
+
|
|
744
|
+
# Group captures by name
|
|
745
|
+
captures_dict = {}
|
|
746
|
+
for node, capture_name in captures:
|
|
747
|
+
if capture_name not in captures_dict:
|
|
748
|
+
captures_dict[capture_name] = []
|
|
749
|
+
captures_dict[capture_name].append(node)
|
|
750
|
+
|
|
751
|
+
# Process different types of imports
|
|
752
|
+
for key, nodes in captures_dict.items():
|
|
753
|
+
if key.endswith("statement"):
|
|
754
|
+
import_type = key.split(".")[0]
|
|
755
|
+
for node in nodes:
|
|
756
|
+
imp = self._extract_import_info(
|
|
757
|
+
node, source_code, import_type
|
|
758
|
+
)
|
|
759
|
+
if imp:
|
|
760
|
+
imports.append(imp)
|
|
749
761
|
except Exception as query_error:
|
|
750
|
-
# Fallback to manual extraction for tree-sitter
|
|
751
|
-
|
|
762
|
+
# Fallback to manual extraction for tree-sitter compatibility
|
|
763
|
+
log_debug(f"Query execution failed, using manual extraction: {query_error}")
|
|
752
764
|
imports.extend(self._extract_imports_manual(tree.root_node, source_code))
|
|
753
765
|
break
|
|
754
766
|
|
|
@@ -1247,6 +1259,137 @@ class PythonPlugin(LanguagePlugin):
|
|
|
1247
1259
|
],
|
|
1248
1260
|
}
|
|
1249
1261
|
|
|
1262
|
+
def execute_query_strategy(self, tree: "tree_sitter.Tree", source_code: str, query_key: str) -> list[dict]:
|
|
1263
|
+
"""
|
|
1264
|
+
Execute query strategy for Python language
|
|
1265
|
+
|
|
1266
|
+
Args:
|
|
1267
|
+
tree: Tree-sitter tree object
|
|
1268
|
+
source_code: Source code string
|
|
1269
|
+
query_key: Query key to execute
|
|
1270
|
+
|
|
1271
|
+
Returns:
|
|
1272
|
+
List of query results
|
|
1273
|
+
"""
|
|
1274
|
+
# Use the extractor to get elements based on query_key
|
|
1275
|
+
extractor = self.get_extractor()
|
|
1276
|
+
|
|
1277
|
+
# Map query keys to extraction methods
|
|
1278
|
+
if query_key in ["function", "functions", "method", "methods"]:
|
|
1279
|
+
elements = extractor.extract_functions(tree, source_code)
|
|
1280
|
+
elif query_key in ["class", "classes"]:
|
|
1281
|
+
elements = extractor.extract_classes(tree, source_code)
|
|
1282
|
+
elif query_key in ["variable", "variables"]:
|
|
1283
|
+
elements = extractor.extract_variables(tree, source_code)
|
|
1284
|
+
elif query_key in ["import", "imports", "from_import", "from_imports"]:
|
|
1285
|
+
elements = extractor.extract_imports(tree, source_code)
|
|
1286
|
+
else:
|
|
1287
|
+
# For unknown query keys, return empty list
|
|
1288
|
+
return []
|
|
1289
|
+
|
|
1290
|
+
# Convert elements to query result format
|
|
1291
|
+
results = []
|
|
1292
|
+
for element in elements:
|
|
1293
|
+
result = {
|
|
1294
|
+
"capture_name": query_key,
|
|
1295
|
+
"node_type": self._get_node_type_for_element(element),
|
|
1296
|
+
"start_line": element.start_line,
|
|
1297
|
+
"end_line": element.end_line,
|
|
1298
|
+
"text": element.raw_text,
|
|
1299
|
+
"name": element.name,
|
|
1300
|
+
}
|
|
1301
|
+
results.append(result)
|
|
1302
|
+
|
|
1303
|
+
return results
|
|
1304
|
+
|
|
1305
|
+
def _get_node_type_for_element(self, element) -> str:
|
|
1306
|
+
"""Get appropriate node type for element"""
|
|
1307
|
+
from ..models import Function, Class, Variable, Import
|
|
1308
|
+
|
|
1309
|
+
if isinstance(element, Function):
|
|
1310
|
+
return "function_definition"
|
|
1311
|
+
elif isinstance(element, Class):
|
|
1312
|
+
return "class_definition"
|
|
1313
|
+
elif isinstance(element, Variable):
|
|
1314
|
+
return "assignment"
|
|
1315
|
+
elif isinstance(element, Import):
|
|
1316
|
+
return "import_statement"
|
|
1317
|
+
else:
|
|
1318
|
+
return "unknown"
|
|
1319
|
+
|
|
1320
|
+
def get_element_categories(self) -> dict[str, list[str]]:
|
|
1321
|
+
"""
|
|
1322
|
+
Get element categories mapping query keys to node types
|
|
1323
|
+
|
|
1324
|
+
Returns:
|
|
1325
|
+
Dictionary mapping query keys to lists of node types
|
|
1326
|
+
"""
|
|
1327
|
+
return {
|
|
1328
|
+
# Function-related queries
|
|
1329
|
+
"function": ["function_definition"],
|
|
1330
|
+
"functions": ["function_definition"],
|
|
1331
|
+
"async_function": ["function_definition"],
|
|
1332
|
+
"async_functions": ["function_definition"],
|
|
1333
|
+
"method": ["function_definition"],
|
|
1334
|
+
"methods": ["function_definition"],
|
|
1335
|
+
"lambda": ["lambda"],
|
|
1336
|
+
"lambdas": ["lambda"],
|
|
1337
|
+
|
|
1338
|
+
# Class-related queries
|
|
1339
|
+
"class": ["class_definition"],
|
|
1340
|
+
"classes": ["class_definition"],
|
|
1341
|
+
|
|
1342
|
+
# Import-related queries
|
|
1343
|
+
"import": ["import_statement", "import_from_statement"],
|
|
1344
|
+
"imports": ["import_statement", "import_from_statement"],
|
|
1345
|
+
"from_import": ["import_from_statement"],
|
|
1346
|
+
"from_imports": ["import_from_statement"],
|
|
1347
|
+
|
|
1348
|
+
# Variable-related queries
|
|
1349
|
+
"variable": ["assignment"],
|
|
1350
|
+
"variables": ["assignment"],
|
|
1351
|
+
|
|
1352
|
+
# Decorator-related queries
|
|
1353
|
+
"decorator": ["decorator"],
|
|
1354
|
+
"decorators": ["decorator"],
|
|
1355
|
+
|
|
1356
|
+
# Exception-related queries
|
|
1357
|
+
"exception": ["raise_statement", "except_clause"],
|
|
1358
|
+
"exceptions": ["raise_statement", "except_clause"],
|
|
1359
|
+
|
|
1360
|
+
# Comprehension-related queries
|
|
1361
|
+
"comprehension": ["list_comprehension", "set_comprehension", "dictionary_comprehension", "generator_expression"],
|
|
1362
|
+
"comprehensions": ["list_comprehension", "set_comprehension", "dictionary_comprehension", "generator_expression"],
|
|
1363
|
+
|
|
1364
|
+
# Context manager queries
|
|
1365
|
+
"context_manager": ["with_statement"],
|
|
1366
|
+
"context_managers": ["with_statement"],
|
|
1367
|
+
|
|
1368
|
+
# Type hint queries
|
|
1369
|
+
"type_hint": ["type"],
|
|
1370
|
+
"type_hints": ["type"],
|
|
1371
|
+
|
|
1372
|
+
# Docstring queries
|
|
1373
|
+
"docstring": ["string"],
|
|
1374
|
+
"docstrings": ["string"],
|
|
1375
|
+
|
|
1376
|
+
# Framework-specific queries
|
|
1377
|
+
"django_model": ["class_definition"],
|
|
1378
|
+
"django_models": ["class_definition"],
|
|
1379
|
+
"flask_route": ["decorator"],
|
|
1380
|
+
"flask_routes": ["decorator"],
|
|
1381
|
+
"fastapi_endpoint": ["function_definition"],
|
|
1382
|
+
"fastapi_endpoints": ["function_definition"],
|
|
1383
|
+
|
|
1384
|
+
# Generic queries
|
|
1385
|
+
"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"
|
|
1390
|
+
],
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1250
1393
|
async def analyze_file(
|
|
1251
1394
|
self, file_path: str, request: AnalysisRequest
|
|
1252
1395
|
) -> AnalysisResult:
|
|
@@ -1330,9 +1473,9 @@ class PythonPlugin(LanguagePlugin):
|
|
|
1330
1473
|
else:
|
|
1331
1474
|
return {"error": f"Unknown query: {query_name}"}
|
|
1332
1475
|
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1476
|
+
captures = TreeSitterQueryCompat.safe_execute_query(
|
|
1477
|
+
language, query_string, tree.root_node, fallback_result=[]
|
|
1478
|
+
)
|
|
1336
1479
|
return {"captures": captures, "query": query_string}
|
|
1337
1480
|
|
|
1338
1481
|
except Exception as e:
|