tree-sitter-analyzer 1.9.17.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tree_sitter_analyzer/__init__.py +132 -0
- tree_sitter_analyzer/__main__.py +11 -0
- tree_sitter_analyzer/api.py +853 -0
- tree_sitter_analyzer/cli/__init__.py +39 -0
- tree_sitter_analyzer/cli/__main__.py +12 -0
- tree_sitter_analyzer/cli/argument_validator.py +89 -0
- tree_sitter_analyzer/cli/commands/__init__.py +26 -0
- tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
- tree_sitter_analyzer/cli/commands/base_command.py +181 -0
- tree_sitter_analyzer/cli/commands/default_command.py +18 -0
- tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
- tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
- tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
- tree_sitter_analyzer/cli/commands/query_command.py +109 -0
- tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
- tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
- tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
- tree_sitter_analyzer/cli/commands/table_command.py +414 -0
- tree_sitter_analyzer/cli/info_commands.py +124 -0
- tree_sitter_analyzer/cli_main.py +472 -0
- tree_sitter_analyzer/constants.py +85 -0
- tree_sitter_analyzer/core/__init__.py +15 -0
- tree_sitter_analyzer/core/analysis_engine.py +580 -0
- tree_sitter_analyzer/core/cache_service.py +333 -0
- tree_sitter_analyzer/core/engine.py +585 -0
- tree_sitter_analyzer/core/parser.py +293 -0
- tree_sitter_analyzer/core/query.py +605 -0
- tree_sitter_analyzer/core/query_filter.py +200 -0
- tree_sitter_analyzer/core/query_service.py +340 -0
- tree_sitter_analyzer/encoding_utils.py +530 -0
- tree_sitter_analyzer/exceptions.py +747 -0
- tree_sitter_analyzer/file_handler.py +246 -0
- tree_sitter_analyzer/formatters/__init__.py +1 -0
- tree_sitter_analyzer/formatters/base_formatter.py +201 -0
- tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
- tree_sitter_analyzer/formatters/formatter_config.py +197 -0
- tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
- tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
- tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
- tree_sitter_analyzer/formatters/go_formatter.py +368 -0
- tree_sitter_analyzer/formatters/html_formatter.py +498 -0
- tree_sitter_analyzer/formatters/java_formatter.py +423 -0
- tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
- tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
- tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
- tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
- tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
- tree_sitter_analyzer/formatters/php_formatter.py +301 -0
- tree_sitter_analyzer/formatters/python_formatter.py +830 -0
- tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
- tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
- tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
- tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
- tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
- tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
- tree_sitter_analyzer/interfaces/__init__.py +9 -0
- tree_sitter_analyzer/interfaces/cli.py +535 -0
- tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
- tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
- tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
- tree_sitter_analyzer/language_detector.py +553 -0
- tree_sitter_analyzer/language_loader.py +271 -0
- tree_sitter_analyzer/languages/__init__.py +10 -0
- tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
- tree_sitter_analyzer/languages/css_plugin.py +449 -0
- tree_sitter_analyzer/languages/go_plugin.py +836 -0
- tree_sitter_analyzer/languages/html_plugin.py +496 -0
- tree_sitter_analyzer/languages/java_plugin.py +1299 -0
- tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
- tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
- tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
- tree_sitter_analyzer/languages/php_plugin.py +862 -0
- tree_sitter_analyzer/languages/python_plugin.py +1636 -0
- tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
- tree_sitter_analyzer/languages/rust_plugin.py +673 -0
- tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
- tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
- tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
- tree_sitter_analyzer/legacy_table_formatter.py +860 -0
- tree_sitter_analyzer/mcp/__init__.py +34 -0
- tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
- tree_sitter_analyzer/mcp/server.py +869 -0
- tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
- tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
- tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
- tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
- tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
- tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
- tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
- tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
- tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
- tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
- tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
- tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
- tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
- tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
- tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
- tree_sitter_analyzer/models.py +840 -0
- tree_sitter_analyzer/mypy_current_errors.txt +2 -0
- tree_sitter_analyzer/output_manager.py +255 -0
- tree_sitter_analyzer/platform_compat/__init__.py +3 -0
- tree_sitter_analyzer/platform_compat/adapter.py +324 -0
- tree_sitter_analyzer/platform_compat/compare.py +224 -0
- tree_sitter_analyzer/platform_compat/detector.py +67 -0
- tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
- tree_sitter_analyzer/platform_compat/profiles.py +217 -0
- tree_sitter_analyzer/platform_compat/record.py +55 -0
- tree_sitter_analyzer/platform_compat/recorder.py +155 -0
- tree_sitter_analyzer/platform_compat/report.py +92 -0
- tree_sitter_analyzer/plugins/__init__.py +280 -0
- tree_sitter_analyzer/plugins/base.py +647 -0
- tree_sitter_analyzer/plugins/manager.py +384 -0
- tree_sitter_analyzer/project_detector.py +328 -0
- tree_sitter_analyzer/queries/__init__.py +27 -0
- tree_sitter_analyzer/queries/csharp.py +216 -0
- tree_sitter_analyzer/queries/css.py +615 -0
- tree_sitter_analyzer/queries/go.py +275 -0
- tree_sitter_analyzer/queries/html.py +543 -0
- tree_sitter_analyzer/queries/java.py +402 -0
- tree_sitter_analyzer/queries/javascript.py +724 -0
- tree_sitter_analyzer/queries/kotlin.py +192 -0
- tree_sitter_analyzer/queries/markdown.py +258 -0
- tree_sitter_analyzer/queries/php.py +95 -0
- tree_sitter_analyzer/queries/python.py +859 -0
- tree_sitter_analyzer/queries/ruby.py +92 -0
- tree_sitter_analyzer/queries/rust.py +223 -0
- tree_sitter_analyzer/queries/sql.py +555 -0
- tree_sitter_analyzer/queries/typescript.py +871 -0
- tree_sitter_analyzer/queries/yaml.py +236 -0
- tree_sitter_analyzer/query_loader.py +272 -0
- tree_sitter_analyzer/security/__init__.py +22 -0
- tree_sitter_analyzer/security/boundary_manager.py +277 -0
- tree_sitter_analyzer/security/regex_checker.py +297 -0
- tree_sitter_analyzer/security/validator.py +599 -0
- tree_sitter_analyzer/table_formatter.py +782 -0
- tree_sitter_analyzer/utils/__init__.py +53 -0
- tree_sitter_analyzer/utils/logging.py +433 -0
- tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/entry_points.txt +25 -0
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Query module for tree_sitter_analyzer.core.
|
|
4
|
+
|
|
5
|
+
This module provides the QueryExecutor class which handles Tree-sitter
|
|
6
|
+
query execution in the new architecture.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import time
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from tree_sitter import Language, Node, Tree
|
|
14
|
+
|
|
15
|
+
from ..query_loader import get_query_loader
|
|
16
|
+
from ..utils.tree_sitter_compat import TreeSitterQueryCompat, get_node_text_safe
|
|
17
|
+
|
|
18
|
+
# Configure logging
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class QueryExecutor:
|
|
23
|
+
"""
|
|
24
|
+
Tree-sitter query executor for the new architecture.
|
|
25
|
+
|
|
26
|
+
This class provides a unified interface for executing Tree-sitter queries
|
|
27
|
+
with proper error handling and result processing.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self) -> None:
|
|
31
|
+
"""Initialize the QueryExecutor."""
|
|
32
|
+
self._query_loader = get_query_loader()
|
|
33
|
+
self._execution_stats: dict[str, Any] = {
|
|
34
|
+
"total_queries": 0,
|
|
35
|
+
"successful_queries": 0,
|
|
36
|
+
"failed_queries": 0,
|
|
37
|
+
"total_execution_time": 0.0,
|
|
38
|
+
}
|
|
39
|
+
logger.info("QueryExecutor initialized successfully")
|
|
40
|
+
|
|
41
|
+
def execute_query(
|
|
42
|
+
self,
|
|
43
|
+
tree: Tree | None,
|
|
44
|
+
language: Language,
|
|
45
|
+
query_name: str,
|
|
46
|
+
source_code: str,
|
|
47
|
+
) -> dict[str, Any]:
|
|
48
|
+
"""
|
|
49
|
+
Execute a predefined query by name.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
tree: Tree-sitter tree to query
|
|
53
|
+
language: Tree-sitter language object
|
|
54
|
+
query_name: Name of the predefined query
|
|
55
|
+
source_code: Source code for context
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Dictionary containing query results and metadata
|
|
59
|
+
"""
|
|
60
|
+
start_time = time.time()
|
|
61
|
+
self._execution_stats["total_queries"] += 1
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
# Validate inputs
|
|
65
|
+
if tree is None:
|
|
66
|
+
return self._create_error_result("Tree is None", query_name=query_name)
|
|
67
|
+
if language is None:
|
|
68
|
+
return self._create_error_result(
|
|
69
|
+
"Language is None", query_name=query_name
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Get the query string with robust language name handling
|
|
73
|
+
language_name = None
|
|
74
|
+
if language:
|
|
75
|
+
# Try multiple ways to get language name
|
|
76
|
+
language_name = getattr(language, "name", None)
|
|
77
|
+
if not language_name:
|
|
78
|
+
language_name = getattr(language, "_name", None)
|
|
79
|
+
if not language_name:
|
|
80
|
+
language_name = (
|
|
81
|
+
str(language).split(".")[-1]
|
|
82
|
+
if hasattr(language, "__class__")
|
|
83
|
+
else None
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Ensure we have a valid language name
|
|
87
|
+
if (
|
|
88
|
+
not language_name
|
|
89
|
+
or language_name.strip() == ""
|
|
90
|
+
or language_name == "None"
|
|
91
|
+
):
|
|
92
|
+
language_name = "unknown"
|
|
93
|
+
else:
|
|
94
|
+
language_name = language_name.strip().lower()
|
|
95
|
+
|
|
96
|
+
query_string = self._query_loader.get_query(language_name, query_name)
|
|
97
|
+
if query_string is None:
|
|
98
|
+
return self._create_error_result(
|
|
99
|
+
f"Query '{query_name}' not found", query_name=query_name
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Create and execute the query using modern API
|
|
103
|
+
try:
|
|
104
|
+
captures = TreeSitterQueryCompat.safe_execute_query(
|
|
105
|
+
language, query_string, tree.root_node, fallback_result=[]
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Process captures
|
|
109
|
+
try:
|
|
110
|
+
processed_captures = self._process_captures(captures, source_code)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.error(f"Error processing captures for {query_name}: {e}")
|
|
113
|
+
return self._create_error_result(
|
|
114
|
+
f"Capture processing failed: {str(e)}", query_name=query_name
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
self._execution_stats["successful_queries"] += 1
|
|
118
|
+
execution_time = time.time() - start_time
|
|
119
|
+
self._execution_stats["total_execution_time"] += execution_time
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
"captures": processed_captures,
|
|
123
|
+
"query_name": query_name,
|
|
124
|
+
"query_string": query_string,
|
|
125
|
+
"execution_time": execution_time,
|
|
126
|
+
"success": True,
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
except Exception as e:
|
|
130
|
+
logger.error(f"Error executing query '{query_name}': {e}")
|
|
131
|
+
return self._create_error_result(
|
|
132
|
+
f"Query execution failed: {str(e)}", query_name=query_name
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
except Exception as e:
|
|
136
|
+
logger.error(f"Unexpected error in execute_query: {e}")
|
|
137
|
+
self._execution_stats["failed_queries"] += 1
|
|
138
|
+
return self._create_error_result(
|
|
139
|
+
f"Unexpected error: {str(e)}", query_name=query_name
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def execute_query_with_language_name(
|
|
143
|
+
self,
|
|
144
|
+
tree: Tree | None,
|
|
145
|
+
language: Language,
|
|
146
|
+
query_name: str,
|
|
147
|
+
source_code: str,
|
|
148
|
+
language_name: str,
|
|
149
|
+
) -> dict[str, Any]:
|
|
150
|
+
"""
|
|
151
|
+
Execute a predefined query by name with explicit language name.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
tree: Tree-sitter tree to query
|
|
155
|
+
language: Tree-sitter language object
|
|
156
|
+
query_name: Name of the predefined query
|
|
157
|
+
source_code: Source code for context
|
|
158
|
+
language_name: Name of the programming language
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Dictionary containing query results and metadata
|
|
162
|
+
"""
|
|
163
|
+
start_time = time.time()
|
|
164
|
+
self._execution_stats["total_queries"] += 1
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
# Validate inputs
|
|
168
|
+
if tree is None:
|
|
169
|
+
return self._create_error_result("Tree is None", query_name=query_name)
|
|
170
|
+
if language is None:
|
|
171
|
+
return self._create_error_result(
|
|
172
|
+
"Language is None", query_name=query_name
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Use the provided language name
|
|
176
|
+
language_name = (
|
|
177
|
+
language_name.strip().lower() if language_name else "unknown"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
query_string = self._query_loader.get_query(language_name, query_name)
|
|
181
|
+
if query_string is None:
|
|
182
|
+
return self._create_error_result(
|
|
183
|
+
f"Query '{query_name}' not found", query_name=query_name
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# Create and execute the query using modern API
|
|
187
|
+
try:
|
|
188
|
+
captures = TreeSitterQueryCompat.safe_execute_query(
|
|
189
|
+
language, query_string, tree.root_node, fallback_result=[]
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Process captures
|
|
193
|
+
try:
|
|
194
|
+
processed_captures = self._process_captures(captures, source_code)
|
|
195
|
+
except Exception as e:
|
|
196
|
+
logger.error(f"Error processing captures for {query_name}: {e}")
|
|
197
|
+
return self._create_error_result(
|
|
198
|
+
f"Capture processing failed: {str(e)}", query_name=query_name
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
self._execution_stats["successful_queries"] += 1
|
|
202
|
+
execution_time = time.time() - start_time
|
|
203
|
+
self._execution_stats["total_execution_time"] += execution_time
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
"captures": processed_captures,
|
|
207
|
+
"query_name": query_name,
|
|
208
|
+
"query_string": query_string,
|
|
209
|
+
"execution_time": execution_time,
|
|
210
|
+
"success": True,
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
except Exception as e:
|
|
214
|
+
logger.error(f"Error executing query '{query_name}': {e}")
|
|
215
|
+
return self._create_error_result(
|
|
216
|
+
f"Query execution failed: {str(e)}", query_name=query_name
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
logger.error(f"Unexpected error in execute_query: {e}")
|
|
221
|
+
self._execution_stats["failed_queries"] += 1
|
|
222
|
+
return self._create_error_result(
|
|
223
|
+
f"Unexpected error: {str(e)}", query_name=query_name
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def execute_query_string(
|
|
227
|
+
self,
|
|
228
|
+
tree: Tree | None,
|
|
229
|
+
language: Language,
|
|
230
|
+
query_string: str,
|
|
231
|
+
source_code: str,
|
|
232
|
+
) -> dict[str, Any]:
|
|
233
|
+
"""
|
|
234
|
+
Execute a query string directly.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
tree: Tree-sitter tree to query
|
|
238
|
+
language: Tree-sitter language object
|
|
239
|
+
query_string: Query string to execute
|
|
240
|
+
source_code: Source code for context
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
Dictionary containing query results and metadata
|
|
244
|
+
"""
|
|
245
|
+
start_time = time.time()
|
|
246
|
+
self._execution_stats["total_queries"] += 1
|
|
247
|
+
|
|
248
|
+
try:
|
|
249
|
+
# Validate inputs
|
|
250
|
+
if tree is None:
|
|
251
|
+
return self._create_error_result("Tree is None")
|
|
252
|
+
if language is None:
|
|
253
|
+
return self._create_error_result("Language is None")
|
|
254
|
+
|
|
255
|
+
# Create and execute the query using modern API
|
|
256
|
+
try:
|
|
257
|
+
captures = TreeSitterQueryCompat.safe_execute_query(
|
|
258
|
+
language, query_string, tree.root_node, fallback_result=[]
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# Process captures
|
|
262
|
+
try:
|
|
263
|
+
processed_captures = self._process_captures(captures, source_code)
|
|
264
|
+
except Exception as e:
|
|
265
|
+
logger.error(f"Error processing captures: {e}")
|
|
266
|
+
return self._create_error_result(
|
|
267
|
+
f"Capture processing failed: {str(e)}"
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
self._execution_stats["successful_queries"] += 1
|
|
271
|
+
execution_time = time.time() - start_time
|
|
272
|
+
self._execution_stats["total_execution_time"] += execution_time
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
"captures": processed_captures,
|
|
276
|
+
"query_string": query_string,
|
|
277
|
+
"execution_time": execution_time,
|
|
278
|
+
"success": True,
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
except Exception as e:
|
|
282
|
+
logger.error(f"Error executing query string: {e}")
|
|
283
|
+
return self._create_error_result(
|
|
284
|
+
f"Query execution failed: {str(e)}", query_string=query_string
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
except Exception as e:
|
|
288
|
+
logger.error(f"Unexpected error in execute_query_string: {e}")
|
|
289
|
+
self._execution_stats["failed_queries"] += 1
|
|
290
|
+
return self._create_error_result(f"Unexpected error: {str(e)}")
|
|
291
|
+
|
|
292
|
+
def execute_multiple_queries(
|
|
293
|
+
self, tree: Tree, language: Language, query_names: list[str], source_code: str
|
|
294
|
+
) -> dict[str, dict[str, Any]]:
|
|
295
|
+
"""
|
|
296
|
+
Execute multiple queries and return combined results.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
tree: Tree-sitter tree to query
|
|
300
|
+
language: Tree-sitter language object
|
|
301
|
+
query_names: List of query names to execute
|
|
302
|
+
source_code: Source code for context
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Dictionary mapping query names to their results
|
|
306
|
+
"""
|
|
307
|
+
results = {}
|
|
308
|
+
|
|
309
|
+
for query_name in query_names:
|
|
310
|
+
result = self.execute_query(tree, language, query_name, source_code)
|
|
311
|
+
results[query_name] = result
|
|
312
|
+
|
|
313
|
+
return results
|
|
314
|
+
|
|
315
|
+
def _process_captures(
|
|
316
|
+
self, captures: Any, source_code: str
|
|
317
|
+
) -> list[dict[str, Any]]:
|
|
318
|
+
"""
|
|
319
|
+
Process query captures into standardized format.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
captures: Raw captures from Tree-sitter query
|
|
323
|
+
source_code: Source code for context
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
List of processed capture dictionaries
|
|
327
|
+
"""
|
|
328
|
+
processed = []
|
|
329
|
+
|
|
330
|
+
try:
|
|
331
|
+
for capture in captures:
|
|
332
|
+
try:
|
|
333
|
+
# Handle tuple format from modern API
|
|
334
|
+
if isinstance(capture, tuple) and len(capture) == 2:
|
|
335
|
+
node, name = capture
|
|
336
|
+
# Handle dictionary format (legacy API compatibility)
|
|
337
|
+
elif (
|
|
338
|
+
isinstance(capture, dict)
|
|
339
|
+
and "node" in capture
|
|
340
|
+
and "name" in capture
|
|
341
|
+
):
|
|
342
|
+
node = capture["node"]
|
|
343
|
+
name = capture["name"]
|
|
344
|
+
else:
|
|
345
|
+
logger.warning(f"Unexpected capture format: {type(capture)}")
|
|
346
|
+
continue
|
|
347
|
+
|
|
348
|
+
if node is None:
|
|
349
|
+
continue
|
|
350
|
+
|
|
351
|
+
result_dict = self._create_result_dict(node, name, source_code)
|
|
352
|
+
processed.append(result_dict)
|
|
353
|
+
|
|
354
|
+
except Exception as e:
|
|
355
|
+
logger.error(f"Error processing capture: {e}")
|
|
356
|
+
continue
|
|
357
|
+
|
|
358
|
+
except Exception as e:
|
|
359
|
+
logger.error(f"Error in _process_captures: {e}")
|
|
360
|
+
|
|
361
|
+
return processed
|
|
362
|
+
|
|
363
|
+
def _create_result_dict(
|
|
364
|
+
self, node: Node, capture_name: str, source_code: str
|
|
365
|
+
) -> dict[str, Any]:
|
|
366
|
+
"""
|
|
367
|
+
Create a result dictionary from a Tree-sitter node.
|
|
368
|
+
|
|
369
|
+
Args:
|
|
370
|
+
node: Tree-sitter node
|
|
371
|
+
capture_name: Name of the capture
|
|
372
|
+
source_code: Source code for context
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
Dictionary containing node information
|
|
376
|
+
"""
|
|
377
|
+
try:
|
|
378
|
+
# Extract node text using safe utility
|
|
379
|
+
node_text = get_node_text_safe(node, source_code)
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
"capture_name": capture_name,
|
|
383
|
+
"node_type": getattr(node, "type", "unknown"),
|
|
384
|
+
"start_point": getattr(node, "start_point", (0, 0)),
|
|
385
|
+
"end_point": getattr(node, "end_point", (0, 0)),
|
|
386
|
+
"start_byte": getattr(node, "start_byte", 0),
|
|
387
|
+
"end_byte": getattr(node, "end_byte", 0),
|
|
388
|
+
"text": node_text,
|
|
389
|
+
"line_number": getattr(node, "start_point", (0, 0))[0] + 1,
|
|
390
|
+
"column_number": getattr(node, "start_point", (0, 0))[1],
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
except Exception as e:
|
|
394
|
+
logger.error(f"Error creating result dict: {e}")
|
|
395
|
+
return {"capture_name": capture_name, "node_type": "error", "error": str(e)}
|
|
396
|
+
|
|
397
|
+
def _create_error_result(
|
|
398
|
+
self, error_message: str, query_name: str | None = None, **kwargs: Any
|
|
399
|
+
) -> dict[str, Any]:
|
|
400
|
+
"""
|
|
401
|
+
Create an error result dictionary.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
error_message: Error message
|
|
405
|
+
query_name: Optional query name
|
|
406
|
+
**kwargs: Additional fields to include in the error result
|
|
407
|
+
|
|
408
|
+
Returns:
|
|
409
|
+
Error result dictionary
|
|
410
|
+
"""
|
|
411
|
+
result = {"captures": [], "error": error_message, "success": False}
|
|
412
|
+
|
|
413
|
+
if query_name:
|
|
414
|
+
result["query_name"] = query_name
|
|
415
|
+
|
|
416
|
+
result.update(kwargs)
|
|
417
|
+
return result
|
|
418
|
+
|
|
419
|
+
def get_available_queries(self, language: str) -> list[str]:
|
|
420
|
+
"""
|
|
421
|
+
Get available queries for a language.
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
language: Programming language name
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
List of available query names
|
|
428
|
+
"""
|
|
429
|
+
try:
|
|
430
|
+
queries = self._query_loader.get_all_queries_for_language(language)
|
|
431
|
+
if isinstance(queries, dict):
|
|
432
|
+
return list(queries.keys())
|
|
433
|
+
# Handle other iterable types
|
|
434
|
+
try: # type: ignore[unreachable]
|
|
435
|
+
return list(queries) if queries else []
|
|
436
|
+
except (TypeError, ValueError):
|
|
437
|
+
return []
|
|
438
|
+
except Exception as e:
|
|
439
|
+
logger.error(f"Error getting available queries for {language}: {e}")
|
|
440
|
+
return []
|
|
441
|
+
|
|
442
|
+
def get_query_description(self, language: str, query_name: str) -> str | None:
|
|
443
|
+
"""
|
|
444
|
+
Get description for a specific query.
|
|
445
|
+
|
|
446
|
+
Args:
|
|
447
|
+
language: Programming language name
|
|
448
|
+
query_name: Name of the query
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
Query description or None if not found
|
|
452
|
+
"""
|
|
453
|
+
try:
|
|
454
|
+
return self._query_loader.get_query_description(language, query_name)
|
|
455
|
+
except Exception as e:
|
|
456
|
+
logger.error(f"Error getting query description: {e}")
|
|
457
|
+
return None
|
|
458
|
+
|
|
459
|
+
def validate_query(self, language: str, query_string: str) -> bool:
|
|
460
|
+
"""
|
|
461
|
+
Validate a query string for a specific language.
|
|
462
|
+
|
|
463
|
+
Args:
|
|
464
|
+
language: Programming language name
|
|
465
|
+
query_string: Query string to validate
|
|
466
|
+
|
|
467
|
+
Returns:
|
|
468
|
+
True if query is valid, False otherwise
|
|
469
|
+
"""
|
|
470
|
+
try:
|
|
471
|
+
# This would require loading the language and attempting to create the query
|
|
472
|
+
# For now, we'll do basic validation
|
|
473
|
+
from ..language_loader import get_loader
|
|
474
|
+
|
|
475
|
+
loader = get_loader()
|
|
476
|
+
|
|
477
|
+
lang_obj = loader.load_language(language)
|
|
478
|
+
if lang_obj is None:
|
|
479
|
+
return False
|
|
480
|
+
|
|
481
|
+
# Try to create the query
|
|
482
|
+
lang_obj.query(query_string)
|
|
483
|
+
return True
|
|
484
|
+
|
|
485
|
+
except Exception as e:
|
|
486
|
+
logger.error(f"Query validation failed: {e}")
|
|
487
|
+
return False
|
|
488
|
+
|
|
489
|
+
def get_query_statistics(self) -> dict[str, Any]:
|
|
490
|
+
"""
|
|
491
|
+
Get query execution statistics.
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
Dictionary containing execution statistics
|
|
495
|
+
"""
|
|
496
|
+
stats = self._execution_stats.copy()
|
|
497
|
+
|
|
498
|
+
if stats["total_queries"] > 0:
|
|
499
|
+
stats["success_rate"] = stats["successful_queries"] / stats["total_queries"]
|
|
500
|
+
stats["average_execution_time"] = (
|
|
501
|
+
stats["total_execution_time"] / stats["total_queries"]
|
|
502
|
+
)
|
|
503
|
+
else:
|
|
504
|
+
stats["success_rate"] = 0.0
|
|
505
|
+
stats["average_execution_time"] = 0.0
|
|
506
|
+
|
|
507
|
+
return stats
|
|
508
|
+
|
|
509
|
+
def reset_statistics(self) -> None:
|
|
510
|
+
"""Reset query execution statistics."""
|
|
511
|
+
self._execution_stats = {
|
|
512
|
+
"total_queries": 0,
|
|
513
|
+
"successful_queries": 0,
|
|
514
|
+
"failed_queries": 0,
|
|
515
|
+
"total_execution_time": 0.0,
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
# Module-level convenience functions for backward compatibility
|
|
520
|
+
def get_available_queries(language: str | None = None) -> list[str]:
|
|
521
|
+
"""
|
|
522
|
+
Get available queries for a language (module-level function).
|
|
523
|
+
|
|
524
|
+
Args:
|
|
525
|
+
language: Programming language name (optional)
|
|
526
|
+
|
|
527
|
+
Returns:
|
|
528
|
+
List of available query names
|
|
529
|
+
"""
|
|
530
|
+
try:
|
|
531
|
+
loader = get_query_loader()
|
|
532
|
+
if language:
|
|
533
|
+
return loader.list_queries_for_language(language)
|
|
534
|
+
|
|
535
|
+
# If no language, return a list of all query names across supported languages
|
|
536
|
+
all_queries = set()
|
|
537
|
+
for lang in loader.list_supported_languages():
|
|
538
|
+
all_queries.update(loader.list_queries_for_language(lang))
|
|
539
|
+
return sorted(all_queries)
|
|
540
|
+
|
|
541
|
+
except Exception as e:
|
|
542
|
+
logger.error(f"Error getting available queries: {e}")
|
|
543
|
+
return []
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
def get_query_description(language: str, query_name: str) -> str | None:
|
|
547
|
+
"""
|
|
548
|
+
Get description for a specific query (module-level function).
|
|
549
|
+
|
|
550
|
+
Args:
|
|
551
|
+
language: Programming language name
|
|
552
|
+
query_name: Name of the query
|
|
553
|
+
|
|
554
|
+
Returns:
|
|
555
|
+
Query description or None if not found
|
|
556
|
+
"""
|
|
557
|
+
try:
|
|
558
|
+
from ..query_loader import get_query_loader
|
|
559
|
+
|
|
560
|
+
loader = get_query_loader()
|
|
561
|
+
return loader.get_query_description(language, query_name)
|
|
562
|
+
except Exception as e:
|
|
563
|
+
logger.error(f"Error getting query description: {e}")
|
|
564
|
+
return None
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
# Module-level attributes for backward compatibility
|
|
568
|
+
try:
|
|
569
|
+
query_loader = get_query_loader()
|
|
570
|
+
except Exception:
|
|
571
|
+
query_loader = None # type: ignore
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def get_all_queries_for_language(language: str) -> list[str]:
|
|
575
|
+
"""
|
|
576
|
+
Get all available queries for a specific language.
|
|
577
|
+
|
|
578
|
+
Args:
|
|
579
|
+
language: Programming language name
|
|
580
|
+
|
|
581
|
+
Returns:
|
|
582
|
+
List of available query names for the language
|
|
583
|
+
|
|
584
|
+
.. deprecated:: 0.2.1
|
|
585
|
+
This function is deprecated and will be removed in a future version.
|
|
586
|
+
Use the unified analysis engine instead.
|
|
587
|
+
"""
|
|
588
|
+
import warnings
|
|
589
|
+
|
|
590
|
+
warnings.warn(
|
|
591
|
+
"get_all_queries_for_language is deprecated and will be removed "
|
|
592
|
+
"in a future version. Use the unified analysis engine instead.",
|
|
593
|
+
DeprecationWarning,
|
|
594
|
+
stacklevel=2,
|
|
595
|
+
)
|
|
596
|
+
return []
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
# Update module-level attributes for backward compatibility
|
|
600
|
+
try:
|
|
601
|
+
from ..language_loader import get_loader
|
|
602
|
+
|
|
603
|
+
loader = get_loader()
|
|
604
|
+
except Exception:
|
|
605
|
+
loader = None # type: ignore
|