tree-sitter-analyzer 1.7.5__py3-none-any.whl → 1.8.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tree-sitter-analyzer might be problematic. Click here for more details.
- tree_sitter_analyzer/__init__.py +1 -1
- tree_sitter_analyzer/api.py +26 -32
- 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/exceptions.py +334 -0
- tree_sitter_analyzer/file_handler.py +16 -1
- 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/interfaces/mcp_server.py +3 -1
- tree_sitter_analyzer/language_detector.py +91 -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/analyze_scale_tool.py +68 -3
- tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +32 -7
- tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +10 -0
- tree_sitter_analyzer/mcp/tools/list_files_tool.py +9 -0
- tree_sitter_analyzer/mcp/tools/query_tool.py +100 -52
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +98 -14
- tree_sitter_analyzer/mcp/tools/search_content_tool.py +9 -0
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +37 -13
- 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 +343 -46
- 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.5.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/METADATA +136 -14
- {tree_sitter_analyzer-1.7.5.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/RECORD +47 -38
- {tree_sitter_analyzer-1.7.5.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/entry_points.txt +2 -0
- {tree_sitter_analyzer-1.7.5.dist-info → tree_sitter_analyzer-1.8.2.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
HTML Language Plugin
|
|
4
|
+
|
|
5
|
+
True HTML parser using tree-sitter-html for comprehensive HTML analysis.
|
|
6
|
+
Provides HTML-specific analysis capabilities including element extraction,
|
|
7
|
+
attribute parsing, and document structure analysis.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
12
|
+
|
|
13
|
+
from ..models import AnalysisResult, MarkupElement
|
|
14
|
+
from ..plugins.base import ElementExtractor, LanguagePlugin
|
|
15
|
+
from ..utils import log_debug, log_error, log_info
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
import tree_sitter
|
|
19
|
+
|
|
20
|
+
from ..core.analysis_engine import AnalysisRequest
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class HtmlElementExtractor(ElementExtractor):
|
|
26
|
+
"""HTML-specific element extractor using tree-sitter-html"""
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self.element_categories = {
|
|
30
|
+
# HTML要素の分類システム
|
|
31
|
+
"structure": ["html", "body", "div", "span", "section", "article", "aside", "nav", "main", "header", "footer"],
|
|
32
|
+
"heading": ["h1", "h2", "h3", "h4", "h5", "h6"],
|
|
33
|
+
"text": ["p", "a", "strong", "em", "b", "i", "u", "small", "mark", "del", "ins", "sub", "sup"],
|
|
34
|
+
"list": ["ul", "ol", "li", "dl", "dt", "dd"],
|
|
35
|
+
"media": ["img", "video", "audio", "source", "track", "canvas", "svg", "picture"],
|
|
36
|
+
"form": ["form", "input", "textarea", "button", "select", "option", "optgroup", "label", "fieldset", "legend"],
|
|
37
|
+
"table": ["table", "thead", "tbody", "tfoot", "tr", "td", "th", "caption", "colgroup", "col"],
|
|
38
|
+
"metadata": ["head", "title", "meta", "link", "style", "script", "noscript", "base"]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
def extract_functions(self, tree: "tree_sitter.Tree", source_code: str) -> list:
|
|
42
|
+
"""HTML doesn't have functions, return empty list"""
|
|
43
|
+
return []
|
|
44
|
+
|
|
45
|
+
def extract_classes(self, tree: "tree_sitter.Tree", source_code: str) -> list:
|
|
46
|
+
"""HTML doesn't have classes in the traditional sense, return empty list"""
|
|
47
|
+
return []
|
|
48
|
+
|
|
49
|
+
def extract_variables(self, tree: "tree_sitter.Tree", source_code: str) -> list:
|
|
50
|
+
"""HTML doesn't have variables, return empty list"""
|
|
51
|
+
return []
|
|
52
|
+
|
|
53
|
+
def extract_imports(self, tree: "tree_sitter.Tree", source_code: str) -> list:
|
|
54
|
+
"""HTML doesn't have imports, return empty list"""
|
|
55
|
+
return []
|
|
56
|
+
|
|
57
|
+
def extract_html_elements(self, tree: "tree_sitter.Tree", source_code: str) -> list[MarkupElement]:
|
|
58
|
+
"""Extract HTML elements using tree-sitter-html parser"""
|
|
59
|
+
elements = []
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
if hasattr(tree, "root_node"):
|
|
63
|
+
self._traverse_for_html_elements(tree.root_node, elements, source_code, None)
|
|
64
|
+
except Exception as e:
|
|
65
|
+
log_error(f"Error in HTML element extraction: {e}")
|
|
66
|
+
|
|
67
|
+
return elements
|
|
68
|
+
|
|
69
|
+
def _traverse_for_html_elements(
|
|
70
|
+
self,
|
|
71
|
+
node: "tree_sitter.Node",
|
|
72
|
+
elements: list[MarkupElement],
|
|
73
|
+
source_code: str,
|
|
74
|
+
parent: MarkupElement | None
|
|
75
|
+
) -> None:
|
|
76
|
+
"""Traverse tree to find HTML elements using tree-sitter-html grammar"""
|
|
77
|
+
if hasattr(node, "type") and self._is_html_element_node(node.type):
|
|
78
|
+
try:
|
|
79
|
+
element = self._create_markup_element(node, source_code, parent)
|
|
80
|
+
if element:
|
|
81
|
+
elements.append(element)
|
|
82
|
+
|
|
83
|
+
# Process children with this element as parent
|
|
84
|
+
if hasattr(node, "children"):
|
|
85
|
+
for child in node.children:
|
|
86
|
+
self._traverse_for_html_elements(child, elements, source_code, element)
|
|
87
|
+
return
|
|
88
|
+
except Exception as e:
|
|
89
|
+
log_debug(f"Failed to extract HTML element: {e}")
|
|
90
|
+
|
|
91
|
+
# Continue traversing children if this node is not an HTML element
|
|
92
|
+
if hasattr(node, "children"):
|
|
93
|
+
for child in node.children:
|
|
94
|
+
self._traverse_for_html_elements(child, elements, source_code, parent)
|
|
95
|
+
|
|
96
|
+
def _is_html_element_node(self, node_type: str) -> bool:
|
|
97
|
+
"""Check if a node type represents an HTML element in tree-sitter-html grammar"""
|
|
98
|
+
html_element_types = [
|
|
99
|
+
"element",
|
|
100
|
+
"start_tag",
|
|
101
|
+
"self_closing_tag",
|
|
102
|
+
"script_element",
|
|
103
|
+
"style_element",
|
|
104
|
+
"void_element"
|
|
105
|
+
]
|
|
106
|
+
return node_type in html_element_types
|
|
107
|
+
|
|
108
|
+
def _create_markup_element(
|
|
109
|
+
self,
|
|
110
|
+
node: "tree_sitter.Node",
|
|
111
|
+
source_code: str,
|
|
112
|
+
parent: MarkupElement | None
|
|
113
|
+
) -> MarkupElement | None:
|
|
114
|
+
"""Create MarkupElement from tree-sitter node using tree-sitter-html grammar"""
|
|
115
|
+
try:
|
|
116
|
+
# Extract tag name using tree-sitter-html structure
|
|
117
|
+
tag_name = self._extract_tag_name(node, source_code)
|
|
118
|
+
if not tag_name:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
# Extract attributes using tree-sitter-html structure
|
|
122
|
+
attributes = self._extract_attributes(node, source_code)
|
|
123
|
+
|
|
124
|
+
# Determine element class based on tag name
|
|
125
|
+
element_class = self._classify_element(tag_name)
|
|
126
|
+
|
|
127
|
+
# Extract text content
|
|
128
|
+
raw_text = self._extract_node_text(node, source_code)
|
|
129
|
+
|
|
130
|
+
# Create MarkupElement
|
|
131
|
+
element = MarkupElement(
|
|
132
|
+
name=tag_name,
|
|
133
|
+
start_line=node.start_point[0] + 1 if hasattr(node, "start_point") else 0,
|
|
134
|
+
end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
|
|
135
|
+
raw_text=raw_text,
|
|
136
|
+
language="html",
|
|
137
|
+
tag_name=tag_name,
|
|
138
|
+
attributes=attributes,
|
|
139
|
+
parent=parent,
|
|
140
|
+
children=[],
|
|
141
|
+
element_class=element_class
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Add to parent's children if parent exists
|
|
145
|
+
if parent:
|
|
146
|
+
parent.children.append(element)
|
|
147
|
+
|
|
148
|
+
return element
|
|
149
|
+
|
|
150
|
+
except Exception as e:
|
|
151
|
+
log_debug(f"Failed to create MarkupElement: {e}")
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
def _extract_tag_name(self, node: "tree_sitter.Node", source_code: str) -> str:
|
|
155
|
+
"""Extract tag name from HTML element node using tree-sitter-html grammar"""
|
|
156
|
+
try:
|
|
157
|
+
# For tree-sitter-html, tag names are in specific child nodes
|
|
158
|
+
if hasattr(node, "children"):
|
|
159
|
+
for child in node.children:
|
|
160
|
+
if hasattr(child, "type"):
|
|
161
|
+
# Handle different node types in tree-sitter-html
|
|
162
|
+
if child.type == "tag_name":
|
|
163
|
+
return self._extract_node_text(child, source_code).strip()
|
|
164
|
+
elif child.type in ["start_tag", "self_closing_tag"]:
|
|
165
|
+
# Look for tag_name within start_tag or self_closing_tag
|
|
166
|
+
for grandchild in child.children:
|
|
167
|
+
if hasattr(grandchild, "type") and grandchild.type == "tag_name":
|
|
168
|
+
return self._extract_node_text(grandchild, source_code).strip()
|
|
169
|
+
|
|
170
|
+
# Fallback: try to extract from node text
|
|
171
|
+
node_text = self._extract_node_text(node, source_code)
|
|
172
|
+
if node_text.startswith("<"):
|
|
173
|
+
# Extract tag name from <tagname ...> pattern
|
|
174
|
+
tag_part = node_text.split(">")[0].split()[0]
|
|
175
|
+
return tag_part.lstrip("<").rstrip(">")
|
|
176
|
+
|
|
177
|
+
return "unknown"
|
|
178
|
+
except Exception:
|
|
179
|
+
return "unknown"
|
|
180
|
+
|
|
181
|
+
def _extract_attributes(self, node: "tree_sitter.Node", source_code: str) -> dict[str, str]:
|
|
182
|
+
"""Extract attributes from HTML element node using tree-sitter-html grammar"""
|
|
183
|
+
attributes = {}
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
if hasattr(node, "children"):
|
|
187
|
+
for child in node.children:
|
|
188
|
+
if hasattr(child, "type"):
|
|
189
|
+
# Handle attribute nodes in tree-sitter-html
|
|
190
|
+
if child.type == "attribute":
|
|
191
|
+
attr_name, attr_value = self._parse_attribute(child, source_code)
|
|
192
|
+
if attr_name:
|
|
193
|
+
attributes[attr_name] = attr_value
|
|
194
|
+
elif child.type in ["start_tag", "self_closing_tag"]:
|
|
195
|
+
# Look for attributes within start_tag or self_closing_tag
|
|
196
|
+
for grandchild in child.children:
|
|
197
|
+
if hasattr(grandchild, "type") and grandchild.type == "attribute":
|
|
198
|
+
attr_name, attr_value = self._parse_attribute(grandchild, source_code)
|
|
199
|
+
if attr_name:
|
|
200
|
+
attributes[attr_name] = attr_value
|
|
201
|
+
except Exception as e:
|
|
202
|
+
log_debug(f"Failed to extract attributes: {e}")
|
|
203
|
+
|
|
204
|
+
return attributes
|
|
205
|
+
|
|
206
|
+
def _parse_attribute(self, attr_node: "tree_sitter.Node", source_code: str) -> tuple[str, str]:
|
|
207
|
+
"""Parse individual attribute node using tree-sitter-html grammar"""
|
|
208
|
+
try:
|
|
209
|
+
# In tree-sitter-html, attributes have specific structure
|
|
210
|
+
attr_name = ""
|
|
211
|
+
attr_value = ""
|
|
212
|
+
|
|
213
|
+
if hasattr(attr_node, "children"):
|
|
214
|
+
for child in attr_node.children:
|
|
215
|
+
if hasattr(child, "type"):
|
|
216
|
+
if child.type == "attribute_name":
|
|
217
|
+
attr_name = self._extract_node_text(child, source_code).strip()
|
|
218
|
+
elif child.type == "quoted_attribute_value":
|
|
219
|
+
attr_value = self._extract_node_text(child, source_code).strip().strip('"').strip("'")
|
|
220
|
+
elif child.type == "attribute_value":
|
|
221
|
+
attr_value = self._extract_node_text(child, source_code).strip()
|
|
222
|
+
|
|
223
|
+
# Fallback to simple parsing
|
|
224
|
+
if not attr_name:
|
|
225
|
+
attr_text = self._extract_node_text(attr_node, source_code)
|
|
226
|
+
if "=" in attr_text:
|
|
227
|
+
name, value = attr_text.split("=", 1)
|
|
228
|
+
attr_name = name.strip()
|
|
229
|
+
attr_value = value.strip().strip('"').strip("'")
|
|
230
|
+
else:
|
|
231
|
+
# Boolean attribute
|
|
232
|
+
attr_name = attr_text.strip()
|
|
233
|
+
attr_value = ""
|
|
234
|
+
|
|
235
|
+
return attr_name, attr_value
|
|
236
|
+
except Exception:
|
|
237
|
+
return "", ""
|
|
238
|
+
|
|
239
|
+
def _classify_element(self, tag_name: str) -> str:
|
|
240
|
+
"""Classify HTML element based on tag name"""
|
|
241
|
+
tag_name_lower = tag_name.lower()
|
|
242
|
+
|
|
243
|
+
for category, tags in self.element_categories.items():
|
|
244
|
+
if tag_name_lower in tags:
|
|
245
|
+
return category
|
|
246
|
+
|
|
247
|
+
return "unknown"
|
|
248
|
+
|
|
249
|
+
def _extract_node_text(self, node: "tree_sitter.Node", source_code: str) -> str:
|
|
250
|
+
"""Extract text content from a tree-sitter node"""
|
|
251
|
+
try:
|
|
252
|
+
if hasattr(node, "start_byte") and hasattr(node, "end_byte"):
|
|
253
|
+
source_bytes = source_code.encode("utf-8")
|
|
254
|
+
node_bytes = source_bytes[node.start_byte : node.end_byte]
|
|
255
|
+
return node_bytes.decode("utf-8", errors="replace")
|
|
256
|
+
return ""
|
|
257
|
+
except Exception as e:
|
|
258
|
+
log_debug(f"Failed to extract node text: {e}")
|
|
259
|
+
return ""
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class HtmlPlugin(LanguagePlugin):
|
|
263
|
+
"""HTML language plugin using tree-sitter-html for true HTML parsing"""
|
|
264
|
+
|
|
265
|
+
def get_language_name(self) -> str:
|
|
266
|
+
return "html"
|
|
267
|
+
|
|
268
|
+
def get_file_extensions(self) -> list[str]:
|
|
269
|
+
return [".html", ".htm", ".xhtml"]
|
|
270
|
+
|
|
271
|
+
def create_extractor(self) -> ElementExtractor:
|
|
272
|
+
return HtmlElementExtractor()
|
|
273
|
+
|
|
274
|
+
def get_supported_element_types(self) -> list[str]:
|
|
275
|
+
return ["html_element"]
|
|
276
|
+
|
|
277
|
+
def get_queries(self) -> dict[str, str]:
|
|
278
|
+
"""Return HTML-specific tree-sitter queries"""
|
|
279
|
+
from ..queries.html import HTML_QUERIES
|
|
280
|
+
return HTML_QUERIES
|
|
281
|
+
|
|
282
|
+
def execute_query_strategy(self, query_key: str | None, language: str) -> str | None:
|
|
283
|
+
"""Execute query strategy for HTML"""
|
|
284
|
+
if language != "html":
|
|
285
|
+
return None
|
|
286
|
+
|
|
287
|
+
queries = self.get_queries()
|
|
288
|
+
return queries.get(query_key) if query_key else None
|
|
289
|
+
|
|
290
|
+
def get_element_categories(self) -> dict[str, list[str]]:
|
|
291
|
+
"""Return HTML element categories for query execution"""
|
|
292
|
+
return {
|
|
293
|
+
"structure": ["element"],
|
|
294
|
+
"heading": ["element"],
|
|
295
|
+
"text": ["element"],
|
|
296
|
+
"list": ["element"],
|
|
297
|
+
"media": ["element"],
|
|
298
|
+
"form": ["element"],
|
|
299
|
+
"table": ["element"],
|
|
300
|
+
"metadata": ["element"]
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async def analyze_file(
|
|
304
|
+
self, file_path: str, request: "AnalysisRequest"
|
|
305
|
+
) -> "AnalysisResult":
|
|
306
|
+
"""Analyze HTML file using tree-sitter-html parser"""
|
|
307
|
+
from ..core.analysis_engine import UnifiedAnalysisEngine
|
|
308
|
+
from ..encoding_utils import read_file_safe
|
|
309
|
+
|
|
310
|
+
try:
|
|
311
|
+
# Read file content
|
|
312
|
+
content, encoding = read_file_safe(file_path)
|
|
313
|
+
|
|
314
|
+
# Create analysis engine
|
|
315
|
+
engine = UnifiedAnalysisEngine()
|
|
316
|
+
|
|
317
|
+
# Use tree-sitter-html for parsing
|
|
318
|
+
try:
|
|
319
|
+
import tree_sitter_html as ts_html
|
|
320
|
+
import tree_sitter
|
|
321
|
+
|
|
322
|
+
# Get HTML language
|
|
323
|
+
HTML_LANGUAGE = tree_sitter.Language(ts_html.language())
|
|
324
|
+
|
|
325
|
+
# Create parser
|
|
326
|
+
parser = tree_sitter.Parser()
|
|
327
|
+
parser.language = HTML_LANGUAGE
|
|
328
|
+
|
|
329
|
+
# Parse the HTML content
|
|
330
|
+
tree = parser.parse(content.encode('utf-8'))
|
|
331
|
+
|
|
332
|
+
# Extract elements using the extractor
|
|
333
|
+
extractor = self.create_extractor()
|
|
334
|
+
elements = extractor.extract_html_elements(tree, content)
|
|
335
|
+
|
|
336
|
+
log_info(f"Extracted {len(elements)} HTML elements from {file_path}")
|
|
337
|
+
|
|
338
|
+
return AnalysisResult(
|
|
339
|
+
file_path=file_path,
|
|
340
|
+
language="html",
|
|
341
|
+
line_count=len(content.splitlines()),
|
|
342
|
+
elements=elements,
|
|
343
|
+
node_count=len(elements),
|
|
344
|
+
query_results={},
|
|
345
|
+
source_code=content,
|
|
346
|
+
success=True,
|
|
347
|
+
error_message=None
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
except ImportError:
|
|
351
|
+
log_error("tree-sitter-html not available, falling back to basic parsing")
|
|
352
|
+
# Fallback to basic parsing
|
|
353
|
+
lines = content.splitlines()
|
|
354
|
+
line_count = len(lines)
|
|
355
|
+
|
|
356
|
+
# Create basic MarkupElement for the HTML document
|
|
357
|
+
html_element = MarkupElement(
|
|
358
|
+
name="html",
|
|
359
|
+
start_line=1,
|
|
360
|
+
end_line=line_count,
|
|
361
|
+
raw_text=content[:200] + "..." if len(content) > 200 else content,
|
|
362
|
+
language="html",
|
|
363
|
+
tag_name="html",
|
|
364
|
+
attributes={},
|
|
365
|
+
parent=None,
|
|
366
|
+
children=[],
|
|
367
|
+
element_class="structure"
|
|
368
|
+
)
|
|
369
|
+
elements = [html_element]
|
|
370
|
+
|
|
371
|
+
return AnalysisResult(
|
|
372
|
+
file_path=file_path,
|
|
373
|
+
language="html",
|
|
374
|
+
line_count=line_count,
|
|
375
|
+
elements=elements,
|
|
376
|
+
node_count=len(elements),
|
|
377
|
+
query_results={},
|
|
378
|
+
source_code=content,
|
|
379
|
+
success=True,
|
|
380
|
+
error_message=None
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
except Exception as e:
|
|
384
|
+
log_error(f"Failed to analyze HTML file {file_path}: {e}")
|
|
385
|
+
return AnalysisResult(
|
|
386
|
+
file_path=file_path,
|
|
387
|
+
language="html",
|
|
388
|
+
line_count=0,
|
|
389
|
+
elements=[],
|
|
390
|
+
node_count=0,
|
|
391
|
+
query_results={},
|
|
392
|
+
source_code="",
|
|
393
|
+
success=False,
|
|
394
|
+
error_message=str(e)
|
|
395
|
+
)
|
|
@@ -1191,6 +1191,122 @@ class JavaPlugin(LanguagePlugin):
|
|
|
1191
1191
|
"supported_queries": self.get_supported_queries(),
|
|
1192
1192
|
}
|
|
1193
1193
|
|
|
1194
|
+
def execute_query_strategy(self, tree: "tree_sitter.Tree", source_code: str, query_key: str) -> list[dict]:
|
|
1195
|
+
"""
|
|
1196
|
+
Execute query strategy for Java language
|
|
1197
|
+
|
|
1198
|
+
Args:
|
|
1199
|
+
tree: Tree-sitter tree object
|
|
1200
|
+
source_code: Source code string
|
|
1201
|
+
query_key: Query key to execute
|
|
1202
|
+
|
|
1203
|
+
Returns:
|
|
1204
|
+
List of query results
|
|
1205
|
+
"""
|
|
1206
|
+
# Use the extractor to get elements based on query_key
|
|
1207
|
+
extractor = self.get_extractor()
|
|
1208
|
+
|
|
1209
|
+
# Map query keys to extraction methods
|
|
1210
|
+
if query_key in ["method", "methods", "function", "functions"]:
|
|
1211
|
+
elements = extractor.extract_functions(tree, source_code)
|
|
1212
|
+
elif query_key in ["class", "classes"]:
|
|
1213
|
+
elements = extractor.extract_classes(tree, source_code)
|
|
1214
|
+
elif query_key in ["field", "fields", "variable", "variables"]:
|
|
1215
|
+
elements = extractor.extract_variables(tree, source_code)
|
|
1216
|
+
elif query_key in ["import", "imports"]:
|
|
1217
|
+
elements = extractor.extract_imports(tree, source_code)
|
|
1218
|
+
elif query_key in ["package", "packages"]:
|
|
1219
|
+
elements = extractor.extract_packages(tree, source_code)
|
|
1220
|
+
elif query_key in ["annotation", "annotations"]:
|
|
1221
|
+
elements = extractor.extract_annotations(tree, source_code)
|
|
1222
|
+
else:
|
|
1223
|
+
# For unknown query keys, return empty list
|
|
1224
|
+
return []
|
|
1225
|
+
|
|
1226
|
+
# Convert elements to query result format
|
|
1227
|
+
results = []
|
|
1228
|
+
for element in elements:
|
|
1229
|
+
result = {
|
|
1230
|
+
"capture_name": query_key,
|
|
1231
|
+
"node_type": self._get_node_type_for_element(element),
|
|
1232
|
+
"start_line": element.start_line,
|
|
1233
|
+
"end_line": element.end_line,
|
|
1234
|
+
"text": element.raw_text,
|
|
1235
|
+
"name": element.name,
|
|
1236
|
+
}
|
|
1237
|
+
results.append(result)
|
|
1238
|
+
|
|
1239
|
+
return results
|
|
1240
|
+
|
|
1241
|
+
def _get_node_type_for_element(self, element) -> str:
|
|
1242
|
+
"""Get appropriate node type for element"""
|
|
1243
|
+
from ..models import Function, Class, Variable, Import, Package
|
|
1244
|
+
|
|
1245
|
+
if isinstance(element, Function):
|
|
1246
|
+
return "method_declaration" if not element.is_constructor else "constructor_declaration"
|
|
1247
|
+
elif isinstance(element, Class):
|
|
1248
|
+
if element.class_type == "interface":
|
|
1249
|
+
return "interface_declaration"
|
|
1250
|
+
elif element.class_type == "enum":
|
|
1251
|
+
return "enum_declaration"
|
|
1252
|
+
else:
|
|
1253
|
+
return "class_declaration"
|
|
1254
|
+
elif isinstance(element, Variable):
|
|
1255
|
+
return "field_declaration"
|
|
1256
|
+
elif isinstance(element, Import):
|
|
1257
|
+
return "import_declaration"
|
|
1258
|
+
elif isinstance(element, Package):
|
|
1259
|
+
return "package_declaration"
|
|
1260
|
+
else:
|
|
1261
|
+
return "unknown"
|
|
1262
|
+
|
|
1263
|
+
def get_element_categories(self) -> dict[str, list[str]]:
|
|
1264
|
+
"""
|
|
1265
|
+
Get element categories mapping query keys to node types
|
|
1266
|
+
|
|
1267
|
+
Returns:
|
|
1268
|
+
Dictionary mapping query keys to lists of node types
|
|
1269
|
+
"""
|
|
1270
|
+
return {
|
|
1271
|
+
# Method-related queries
|
|
1272
|
+
"method": ["method_declaration"],
|
|
1273
|
+
"methods": ["method_declaration"],
|
|
1274
|
+
"constructor": ["constructor_declaration"],
|
|
1275
|
+
"constructors": ["constructor_declaration"],
|
|
1276
|
+
|
|
1277
|
+
# Class-related queries
|
|
1278
|
+
"class": ["class_declaration"],
|
|
1279
|
+
"classes": ["class_declaration"],
|
|
1280
|
+
"interface": ["interface_declaration"],
|
|
1281
|
+
"interfaces": ["interface_declaration"],
|
|
1282
|
+
"enum": ["enum_declaration"],
|
|
1283
|
+
"enums": ["enum_declaration"],
|
|
1284
|
+
|
|
1285
|
+
# Field-related queries
|
|
1286
|
+
"field": ["field_declaration"],
|
|
1287
|
+
"fields": ["field_declaration"],
|
|
1288
|
+
|
|
1289
|
+
# Import-related queries
|
|
1290
|
+
"import": ["import_declaration"],
|
|
1291
|
+
"imports": ["import_declaration"],
|
|
1292
|
+
|
|
1293
|
+
# Package-related queries
|
|
1294
|
+
"package": ["package_declaration"],
|
|
1295
|
+
"packages": ["package_declaration"],
|
|
1296
|
+
|
|
1297
|
+
# Annotation-related queries
|
|
1298
|
+
"annotation": ["annotation", "marker_annotation"],
|
|
1299
|
+
"annotations": ["annotation", "marker_annotation"],
|
|
1300
|
+
|
|
1301
|
+
# Generic queries
|
|
1302
|
+
"all_elements": [
|
|
1303
|
+
"method_declaration", "constructor_declaration",
|
|
1304
|
+
"class_declaration", "interface_declaration", "enum_declaration",
|
|
1305
|
+
"field_declaration", "import_declaration", "package_declaration",
|
|
1306
|
+
"annotation", "marker_annotation"
|
|
1307
|
+
],
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1194
1310
|
async def analyze_file(
|
|
1195
1311
|
self, file_path: str, request: "AnalysisRequest"
|
|
1196
1312
|
) -> "AnalysisResult":
|
|
@@ -1440,6 +1440,119 @@ class JavaScriptPlugin(LanguagePlugin):
|
|
|
1440
1440
|
],
|
|
1441
1441
|
}
|
|
1442
1442
|
|
|
1443
|
+
def execute_query_strategy(self, tree: "tree_sitter.Tree", source_code: str, query_key: str) -> list[dict]:
|
|
1444
|
+
"""
|
|
1445
|
+
Execute query strategy for JavaScript language
|
|
1446
|
+
|
|
1447
|
+
Args:
|
|
1448
|
+
tree: Tree-sitter tree object
|
|
1449
|
+
source_code: Source code string
|
|
1450
|
+
query_key: Query key to execute
|
|
1451
|
+
|
|
1452
|
+
Returns:
|
|
1453
|
+
List of query results
|
|
1454
|
+
"""
|
|
1455
|
+
# Use the extractor to get elements based on query_key
|
|
1456
|
+
extractor = self.get_extractor()
|
|
1457
|
+
|
|
1458
|
+
# Map query keys to extraction methods
|
|
1459
|
+
if query_key in ["function", "functions", "async_function", "async_functions", "arrow_function", "arrow_functions", "method", "methods", "constructor", "constructors"]:
|
|
1460
|
+
elements = extractor.extract_functions(tree, source_code)
|
|
1461
|
+
elif query_key in ["class", "classes", "react_component", "react_components"]:
|
|
1462
|
+
elements = extractor.extract_classes(tree, source_code)
|
|
1463
|
+
elif query_key in ["variable", "variables"]:
|
|
1464
|
+
elements = extractor.extract_variables(tree, source_code)
|
|
1465
|
+
elif query_key in ["import", "imports"]:
|
|
1466
|
+
elements = extractor.extract_imports(tree, source_code)
|
|
1467
|
+
else:
|
|
1468
|
+
# For unknown query keys, return empty list
|
|
1469
|
+
return []
|
|
1470
|
+
|
|
1471
|
+
# Convert elements to query result format
|
|
1472
|
+
results = []
|
|
1473
|
+
for element in elements:
|
|
1474
|
+
result = {
|
|
1475
|
+
"capture_name": query_key,
|
|
1476
|
+
"node_type": self._get_node_type_for_element(element),
|
|
1477
|
+
"start_line": element.start_line,
|
|
1478
|
+
"end_line": element.end_line,
|
|
1479
|
+
"text": element.raw_text,
|
|
1480
|
+
"name": element.name,
|
|
1481
|
+
}
|
|
1482
|
+
results.append(result)
|
|
1483
|
+
|
|
1484
|
+
return results
|
|
1485
|
+
|
|
1486
|
+
def _get_node_type_for_element(self, element) -> str:
|
|
1487
|
+
"""Get appropriate node type for element"""
|
|
1488
|
+
from ..models import Function, Class, Variable, Import
|
|
1489
|
+
|
|
1490
|
+
if isinstance(element, Function):
|
|
1491
|
+
if hasattr(element, 'is_arrow') and element.is_arrow:
|
|
1492
|
+
return "arrow_function"
|
|
1493
|
+
elif hasattr(element, 'is_method') and element.is_method:
|
|
1494
|
+
return "method_definition"
|
|
1495
|
+
else:
|
|
1496
|
+
return "function_declaration"
|
|
1497
|
+
elif isinstance(element, Class):
|
|
1498
|
+
return "class_declaration"
|
|
1499
|
+
elif isinstance(element, Variable):
|
|
1500
|
+
return "variable_declaration"
|
|
1501
|
+
elif isinstance(element, Import):
|
|
1502
|
+
return "import_statement"
|
|
1503
|
+
else:
|
|
1504
|
+
return "unknown"
|
|
1505
|
+
|
|
1506
|
+
def get_element_categories(self) -> dict[str, list[str]]:
|
|
1507
|
+
"""
|
|
1508
|
+
Get element categories mapping query keys to node types
|
|
1509
|
+
|
|
1510
|
+
Returns:
|
|
1511
|
+
Dictionary mapping query keys to lists of node types
|
|
1512
|
+
"""
|
|
1513
|
+
return {
|
|
1514
|
+
# Function-related queries
|
|
1515
|
+
"function": ["function_declaration", "function_expression"],
|
|
1516
|
+
"functions": ["function_declaration", "function_expression"],
|
|
1517
|
+
"async_function": ["function_declaration", "function_expression"],
|
|
1518
|
+
"async_functions": ["function_declaration", "function_expression"],
|
|
1519
|
+
"arrow_function": ["arrow_function"],
|
|
1520
|
+
"arrow_functions": ["arrow_function"],
|
|
1521
|
+
"method": ["method_definition"],
|
|
1522
|
+
"methods": ["method_definition"],
|
|
1523
|
+
"constructor": ["method_definition"],
|
|
1524
|
+
"constructors": ["method_definition"],
|
|
1525
|
+
|
|
1526
|
+
# Class-related queries
|
|
1527
|
+
"class": ["class_declaration", "class_expression"],
|
|
1528
|
+
"classes": ["class_declaration", "class_expression"],
|
|
1529
|
+
|
|
1530
|
+
# Variable-related queries
|
|
1531
|
+
"variable": ["variable_declaration", "lexical_declaration"],
|
|
1532
|
+
"variables": ["variable_declaration", "lexical_declaration"],
|
|
1533
|
+
|
|
1534
|
+
# Import/Export-related queries
|
|
1535
|
+
"import": ["import_statement"],
|
|
1536
|
+
"imports": ["import_statement"],
|
|
1537
|
+
"export": ["export_statement"],
|
|
1538
|
+
"exports": ["export_statement"],
|
|
1539
|
+
|
|
1540
|
+
# React-specific queries
|
|
1541
|
+
"react_component": ["class_declaration", "function_declaration"],
|
|
1542
|
+
"react_components": ["class_declaration", "function_declaration"],
|
|
1543
|
+
"react_hook": ["function_declaration"],
|
|
1544
|
+
"react_hooks": ["function_declaration"],
|
|
1545
|
+
"jsx_element": ["jsx_element", "jsx_self_closing_element"],
|
|
1546
|
+
"jsx_elements": ["jsx_element", "jsx_self_closing_element"],
|
|
1547
|
+
|
|
1548
|
+
# Generic queries
|
|
1549
|
+
"all_elements": [
|
|
1550
|
+
"function_declaration", "function_expression", "arrow_function", "method_definition",
|
|
1551
|
+
"class_declaration", "class_expression", "variable_declaration", "lexical_declaration",
|
|
1552
|
+
"import_statement", "export_statement", "jsx_element", "jsx_self_closing_element"
|
|
1553
|
+
],
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1443
1556
|
async def analyze_file(
|
|
1444
1557
|
self, file_path: str, request: AnalysisRequest
|
|
1445
1558
|
) -> AnalysisResult:
|