tree-sitter-analyzer 0.2.0__py3-none-any.whl → 0.3.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 (78) hide show
  1. tree_sitter_analyzer/__init__.py +133 -121
  2. tree_sitter_analyzer/__main__.py +11 -12
  3. tree_sitter_analyzer/api.py +531 -539
  4. tree_sitter_analyzer/cli/__init__.py +39 -39
  5. tree_sitter_analyzer/cli/__main__.py +12 -13
  6. tree_sitter_analyzer/cli/commands/__init__.py +26 -27
  7. tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
  8. tree_sitter_analyzer/cli/commands/base_command.py +160 -155
  9. tree_sitter_analyzer/cli/commands/default_command.py +18 -19
  10. tree_sitter_analyzer/cli/commands/partial_read_command.py +141 -133
  11. tree_sitter_analyzer/cli/commands/query_command.py +81 -82
  12. tree_sitter_analyzer/cli/commands/structure_command.py +138 -121
  13. tree_sitter_analyzer/cli/commands/summary_command.py +101 -93
  14. tree_sitter_analyzer/cli/commands/table_command.py +232 -233
  15. tree_sitter_analyzer/cli/info_commands.py +120 -121
  16. tree_sitter_analyzer/cli_main.py +277 -276
  17. tree_sitter_analyzer/core/__init__.py +15 -20
  18. tree_sitter_analyzer/core/analysis_engine.py +591 -574
  19. tree_sitter_analyzer/core/cache_service.py +320 -330
  20. tree_sitter_analyzer/core/engine.py +557 -560
  21. tree_sitter_analyzer/core/parser.py +293 -288
  22. tree_sitter_analyzer/core/query.py +494 -502
  23. tree_sitter_analyzer/encoding_utils.py +458 -460
  24. tree_sitter_analyzer/exceptions.py +337 -340
  25. tree_sitter_analyzer/file_handler.py +217 -222
  26. tree_sitter_analyzer/formatters/__init__.py +1 -1
  27. tree_sitter_analyzer/formatters/base_formatter.py +167 -168
  28. tree_sitter_analyzer/formatters/formatter_factory.py +78 -74
  29. tree_sitter_analyzer/formatters/java_formatter.py +287 -270
  30. tree_sitter_analyzer/formatters/python_formatter.py +255 -235
  31. tree_sitter_analyzer/interfaces/__init__.py +9 -10
  32. tree_sitter_analyzer/interfaces/cli.py +528 -557
  33. tree_sitter_analyzer/interfaces/cli_adapter.py +322 -319
  34. tree_sitter_analyzer/interfaces/mcp_adapter.py +180 -170
  35. tree_sitter_analyzer/interfaces/mcp_server.py +405 -416
  36. tree_sitter_analyzer/java_analyzer.py +218 -219
  37. tree_sitter_analyzer/language_detector.py +398 -400
  38. tree_sitter_analyzer/language_loader.py +224 -228
  39. tree_sitter_analyzer/languages/__init__.py +10 -11
  40. tree_sitter_analyzer/languages/java_plugin.py +1129 -1113
  41. tree_sitter_analyzer/languages/python_plugin.py +737 -712
  42. tree_sitter_analyzer/mcp/__init__.py +31 -32
  43. tree_sitter_analyzer/mcp/resources/__init__.py +44 -47
  44. tree_sitter_analyzer/mcp/resources/code_file_resource.py +212 -213
  45. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +560 -550
  46. tree_sitter_analyzer/mcp/server.py +333 -345
  47. tree_sitter_analyzer/mcp/tools/__init__.py +30 -31
  48. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +621 -557
  49. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +242 -245
  50. tree_sitter_analyzer/mcp/tools/base_tool.py +54 -55
  51. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +300 -302
  52. tree_sitter_analyzer/mcp/tools/table_format_tool.py +362 -359
  53. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +543 -476
  54. tree_sitter_analyzer/mcp/utils/__init__.py +105 -106
  55. tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
  56. tree_sitter_analyzer/models.py +470 -481
  57. tree_sitter_analyzer/output_manager.py +261 -264
  58. tree_sitter_analyzer/plugins/__init__.py +333 -334
  59. tree_sitter_analyzer/plugins/base.py +477 -446
  60. tree_sitter_analyzer/plugins/java_plugin.py +608 -625
  61. tree_sitter_analyzer/plugins/javascript_plugin.py +446 -439
  62. tree_sitter_analyzer/plugins/manager.py +362 -355
  63. tree_sitter_analyzer/plugins/plugin_loader.py +85 -83
  64. tree_sitter_analyzer/plugins/python_plugin.py +606 -598
  65. tree_sitter_analyzer/plugins/registry.py +374 -366
  66. tree_sitter_analyzer/queries/__init__.py +26 -27
  67. tree_sitter_analyzer/queries/java.py +391 -394
  68. tree_sitter_analyzer/queries/javascript.py +148 -149
  69. tree_sitter_analyzer/queries/python.py +285 -286
  70. tree_sitter_analyzer/queries/typescript.py +229 -230
  71. tree_sitter_analyzer/query_loader.py +254 -260
  72. tree_sitter_analyzer/table_formatter.py +468 -448
  73. tree_sitter_analyzer/utils.py +277 -277
  74. {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/METADATA +21 -6
  75. tree_sitter_analyzer-0.3.0.dist-info/RECORD +77 -0
  76. tree_sitter_analyzer-0.2.0.dist-info/RECORD +0 -77
  77. {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/WHEEL +0 -0
  78. {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/entry_points.txt +0 -0
@@ -1,502 +1,494 @@
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 Dict, List, Any, Optional, Union, Tuple
12
- from tree_sitter import Language, Tree, Node
13
-
14
- from ..query_loader import get_query_loader
15
-
16
- # Configure logging
17
- logger = logging.getLogger(__name__)
18
-
19
-
20
- class QueryExecutor:
21
- """
22
- Tree-sitter query executor for the new architecture.
23
-
24
- This class provides a unified interface for executing Tree-sitter queries
25
- with proper error handling and result processing.
26
- """
27
-
28
- def __init__(self) -> None:
29
- """Initialize the QueryExecutor."""
30
- self._query_loader = get_query_loader()
31
- self._execution_stats: Dict[str, Any] = {
32
- 'total_queries': 0,
33
- 'successful_queries': 0,
34
- 'failed_queries': 0,
35
- 'total_execution_time': 0.0
36
- }
37
- logger.info("QueryExecutor initialized successfully")
38
-
39
- def execute_query(
40
- self,
41
- tree: Optional[Tree],
42
- language: Language,
43
- query_name: str,
44
- source_code: str
45
- ) -> Dict[str, Any]:
46
- """
47
- Execute a predefined query by name.
48
-
49
- Args:
50
- tree: Tree-sitter tree to query
51
- language: Tree-sitter language object
52
- query_name: Name of the predefined query
53
- source_code: Source code for context
54
-
55
- Returns:
56
- Dictionary containing query results and metadata
57
- """
58
- start_time = time.time()
59
- self._execution_stats['total_queries'] += 1
60
-
61
- try:
62
- # Validate inputs
63
- if tree is None:
64
- return self._create_error_result(
65
- "Tree is None",
66
- query_name=query_name
67
- )
68
-
69
- if language is None:
70
- return self._create_error_result(
71
- "Language is None",
72
- query_name=query_name
73
- )
74
-
75
- # Get the query string
76
- language_name = language.name if hasattr(language, 'name') else 'unknown'
77
- query_string = self._query_loader.get_query(language_name, query_name)
78
- if query_string is None:
79
- return self._create_error_result(
80
- f"Query '{query_name}' not found",
81
- query_name=query_name
82
- )
83
-
84
- # Create and execute the query
85
- try:
86
- query = language.query(query_string)
87
- captures = query.captures(tree.root_node)
88
-
89
- # Process captures
90
- try:
91
- processed_captures = self._process_captures(captures, source_code)
92
- except Exception as e:
93
- logger.error(f"Error processing captures for {query_name}: {e}")
94
- return self._create_error_result(
95
- f"Capture processing failed: {str(e)}",
96
- query_name=query_name
97
- )
98
-
99
- self._execution_stats['successful_queries'] += 1
100
- execution_time = time.time() - start_time
101
- self._execution_stats['total_execution_time'] += execution_time
102
-
103
- return {
104
- 'captures': processed_captures,
105
- 'query_name': query_name,
106
- 'query_string': query_string,
107
- 'execution_time': execution_time,
108
- 'success': True
109
- }
110
-
111
- except Exception as e:
112
- logger.error(f"Error executing query '{query_name}': {e}")
113
- return self._create_error_result(
114
- f"Query execution failed: {str(e)}",
115
- query_name=query_name
116
- )
117
-
118
- except Exception as e:
119
- logger.error(f"Unexpected error in execute_query: {e}")
120
- self._execution_stats['failed_queries'] += 1
121
- return self._create_error_result(
122
- f"Unexpected error: {str(e)}",
123
- query_name=query_name
124
- )
125
-
126
- def execute_query_string(
127
- self,
128
- tree: Optional[Tree],
129
- language: Language,
130
- query_string: str,
131
- source_code: str
132
- ) -> Dict[str, Any]:
133
- """
134
- Execute a query string directly.
135
-
136
- Args:
137
- tree: Tree-sitter tree to query
138
- language: Tree-sitter language object
139
- query_string: Query string to execute
140
- source_code: Source code for context
141
-
142
- Returns:
143
- Dictionary containing query results and metadata
144
- """
145
- start_time = time.time()
146
- self._execution_stats['total_queries'] += 1
147
-
148
- try:
149
- # Validate inputs
150
- if tree is None:
151
- return self._create_error_result("Tree is None")
152
-
153
- if language is None:
154
- return self._create_error_result("Language is None")
155
-
156
- # Create and execute the query
157
- try:
158
- query = language.query(query_string)
159
- captures = query.captures(tree.root_node)
160
-
161
- # Process captures
162
- try:
163
- processed_captures = self._process_captures(captures, source_code)
164
- except Exception as e:
165
- logger.error(f"Error processing captures: {e}")
166
- return self._create_error_result(f"Capture processing failed: {str(e)}")
167
-
168
- self._execution_stats['successful_queries'] += 1
169
- execution_time = time.time() - start_time
170
- self._execution_stats['total_execution_time'] += execution_time
171
-
172
- return {
173
- 'captures': processed_captures,
174
- 'query_string': query_string,
175
- 'execution_time': execution_time,
176
- 'success': True
177
- }
178
-
179
- except Exception as e:
180
- logger.error(f"Error executing query string: {e}")
181
- return self._create_error_result(f"Query execution failed: {str(e)}", query_string=query_string)
182
-
183
- except Exception as e:
184
- logger.error(f"Unexpected error in execute_query_string: {e}")
185
- self._execution_stats['failed_queries'] += 1
186
- return self._create_error_result(f"Unexpected error: {str(e)}")
187
-
188
- def execute_multiple_queries(
189
- self,
190
- tree: Tree,
191
- language: Language,
192
- query_names: List[str],
193
- source_code: str
194
- ) -> Dict[str, Dict[str, Any]]:
195
- """
196
- Execute multiple queries and return combined results.
197
-
198
- Args:
199
- tree: Tree-sitter tree to query
200
- language: Tree-sitter language object
201
- query_names: List of query names to execute
202
- source_code: Source code for context
203
-
204
- Returns:
205
- Dictionary mapping query names to their results
206
- """
207
- results = {}
208
-
209
- for query_name in query_names:
210
- result = self.execute_query(tree, language, query_name, source_code)
211
- results[query_name] = result
212
-
213
- return results
214
-
215
- def _process_captures(
216
- self,
217
- captures: List[Union[Dict[str, Any], Tuple[Node, str]]],
218
- source_code: str
219
- ) -> List[Dict[str, Any]]:
220
- """
221
- Process query captures into standardized format.
222
-
223
- Args:
224
- captures: Raw captures from Tree-sitter query
225
- source_code: Source code for context
226
-
227
- Returns:
228
- List of processed capture dictionaries
229
- """
230
- processed = []
231
-
232
- try:
233
- for capture in captures:
234
- try:
235
- # Handle both dictionary and tuple formats
236
- if isinstance(capture, dict):
237
- # New Tree-sitter API format
238
- node = capture.get('node')
239
- name = capture.get('name', 'unknown')
240
- elif isinstance(capture, tuple) and len(capture) == 2:
241
- # Old Tree-sitter API format
242
- node, name = capture
243
- else:
244
- logger.warning(f"Unexpected capture format: {type(capture)}")
245
- continue
246
-
247
- if node is None:
248
- continue
249
-
250
- result_dict = self._create_result_dict(node, name, source_code)
251
- processed.append(result_dict)
252
-
253
- except Exception as e:
254
- logger.error(f"Error processing capture: {e}")
255
- continue
256
-
257
- except Exception as e:
258
- logger.error(f"Error in _process_captures: {e}")
259
-
260
- return processed
261
-
262
- def _create_result_dict(
263
- self,
264
- node: Node,
265
- capture_name: str,
266
- source_code: str
267
- ) -> Dict[str, Any]:
268
- """
269
- Create a result dictionary from a Tree-sitter node.
270
-
271
- Args:
272
- node: Tree-sitter node
273
- capture_name: Name of the capture
274
- source_code: Source code for context
275
-
276
- Returns:
277
- Dictionary containing node information
278
- """
279
- try:
280
- # Extract node text
281
- node_text = ""
282
- if hasattr(node, 'text') and node.text:
283
- try:
284
- node_text = node.text.decode('utf-8', errors='replace')
285
- except Exception:
286
- node_text = str(node.text)
287
-
288
- return {
289
- 'capture_name': capture_name,
290
- 'node_type': getattr(node, 'type', 'unknown'),
291
- 'start_point': getattr(node, 'start_point', (0, 0)),
292
- 'end_point': getattr(node, 'end_point', (0, 0)),
293
- 'start_byte': getattr(node, 'start_byte', 0),
294
- 'end_byte': getattr(node, 'end_byte', 0),
295
- 'text': node_text,
296
- 'line_number': getattr(node, 'start_point', (0, 0))[0] + 1,
297
- 'column_number': getattr(node, 'start_point', (0, 0))[1]
298
- }
299
-
300
- except Exception as e:
301
- logger.error(f"Error creating result dict: {e}")
302
- return {
303
- 'capture_name': capture_name,
304
- 'node_type': 'error',
305
- 'error': str(e)
306
- }
307
-
308
- def _create_error_result(
309
- self,
310
- error_message: str,
311
- query_name: Optional[str] = None,
312
- **kwargs
313
- ) -> Dict[str, Any]:
314
- """
315
- Create an error result dictionary.
316
-
317
- Args:
318
- error_message: Error message
319
- query_name: Optional query name
320
- **kwargs: Additional fields to include in the error result
321
-
322
- Returns:
323
- Error result dictionary
324
- """
325
- result = {
326
- 'captures': [],
327
- 'error': error_message,
328
- 'success': False
329
- }
330
-
331
- if query_name:
332
- result['query_name'] = query_name
333
-
334
- result.update(kwargs)
335
- return result
336
-
337
- def get_available_queries(self, language: str) -> List[str]:
338
- """
339
- Get available queries for a language.
340
-
341
- Args:
342
- language: Programming language name
343
-
344
- Returns:
345
- List of available query names
346
- """
347
- try:
348
- return self._query_loader.get_all_queries_for_language(language)
349
- except Exception as e:
350
- logger.error(f"Error getting available queries for {language}: {e}")
351
- return []
352
-
353
- def get_query_description(self, language: str, query_name: str) -> Optional[str]:
354
- """
355
- Get description for a specific query.
356
-
357
- Args:
358
- language: Programming language name
359
- query_name: Name of the query
360
-
361
- Returns:
362
- Query description or None if not found
363
- """
364
- try:
365
- return self._query_loader.get_query_description(language, query_name)
366
- except Exception as e:
367
- logger.error(f"Error getting query description: {e}")
368
- return None
369
-
370
- def validate_query(self, language: str, query_string: str) -> bool:
371
- """
372
- Validate a query string for a specific language.
373
-
374
- Args:
375
- language: Programming language name
376
- query_string: Query string to validate
377
-
378
- Returns:
379
- True if query is valid, False otherwise
380
- """
381
- try:
382
- # This would require loading the language and attempting to create the query
383
- # For now, we'll do basic validation
384
- from ..language_loader import get_loader
385
- loader = get_loader()
386
-
387
- lang_obj = loader.load_language(language)
388
- if lang_obj is None:
389
- return False
390
-
391
- # Try to create the query
392
- lang_obj.query(query_string)
393
- return True
394
-
395
- except Exception as e:
396
- logger.error(f"Query validation failed: {e}")
397
- return False
398
-
399
- def get_query_statistics(self) -> Dict[str, Any]:
400
- """
401
- Get query execution statistics.
402
-
403
- Returns:
404
- Dictionary containing execution statistics
405
- """
406
- stats = self._execution_stats.copy()
407
-
408
- if stats['total_queries'] > 0:
409
- stats['success_rate'] = stats['successful_queries'] / stats['total_queries']
410
- stats['average_execution_time'] = (
411
- stats['total_execution_time'] / stats['total_queries']
412
- )
413
- else:
414
- stats['success_rate'] = 0.0
415
- stats['average_execution_time'] = 0.0
416
-
417
- return stats
418
-
419
- def reset_statistics(self) -> None:
420
- """Reset query execution statistics."""
421
- self._execution_stats = {
422
- 'total_queries': 0,
423
- 'successful_queries': 0,
424
- 'failed_queries': 0,
425
- 'total_execution_time': 0.0
426
- }
427
-
428
- # Module-level convenience functions for backward compatibility
429
- def get_available_queries(language: str = None) -> List[str]:
430
- """
431
- Get available queries for a language (module-level function).
432
-
433
- Args:
434
- language: Programming language name (optional)
435
-
436
- Returns:
437
- List of available query names
438
- """
439
- try:
440
- loader = get_query_loader()
441
- if language:
442
- return loader.list_queries_for_language(language)
443
-
444
- # If no language, return a list of all query names across supported languages
445
- all_queries = set()
446
- for lang in loader.list_supported_languages():
447
- all_queries.update(loader.list_queries_for_language(lang))
448
- return sorted(list(all_queries))
449
-
450
- except Exception as e:
451
- logger.error(f"Error getting available queries: {e}")
452
- return []
453
-
454
-
455
- def get_query_description(language: str, query_name: str) -> Optional[str]:
456
- """
457
- Get description for a specific query (module-level function).
458
-
459
- Args:
460
- language: Programming language name
461
- query_name: Name of the query
462
-
463
- Returns:
464
- Query description or None if not found
465
- """
466
- try:
467
- from ..query_loader import get_query_loader
468
- loader = get_query_loader()
469
- return loader.get_query_description(language, query_name)
470
- except Exception as e:
471
- logger.error(f"Error getting query description: {e}")
472
-
473
-
474
- # Module-level attributes for backward compatibility
475
- try:
476
- from ..query_loader import get_query_loader
477
- query_loader = get_query_loader()
478
- except Exception:
479
- query_loader = None
480
-
481
-
482
- def get_all_queries_for_language(language: str) -> List[str]:
483
- """
484
- Get all available queries for a specific language.
485
-
486
- Args:
487
- language: Programming language name
488
-
489
- Returns:
490
- List of available query names for the language
491
- """
492
- # This function is deprecated and will be removed.
493
- # For now, return an empty list to avoid breaking changes.
494
- return []
495
-
496
-
497
- # Update module-level attributes for backward compatibility
498
- try:
499
- from ..language_loader import get_loader
500
- loader = get_loader()
501
- except Exception:
502
- loader = None
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
+
17
+ # Configure logging
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class QueryExecutor:
22
+ """
23
+ Tree-sitter query executor for the new architecture.
24
+
25
+ This class provides a unified interface for executing Tree-sitter queries
26
+ with proper error handling and result processing.
27
+ """
28
+
29
+ def __init__(self) -> None:
30
+ """Initialize the QueryExecutor."""
31
+ self._query_loader = get_query_loader()
32
+ self._execution_stats: dict[str, Any] = {
33
+ "total_queries": 0,
34
+ "successful_queries": 0,
35
+ "failed_queries": 0,
36
+ "total_execution_time": 0.0,
37
+ }
38
+ logger.info("QueryExecutor initialized successfully")
39
+
40
+ def execute_query(
41
+ self,
42
+ tree: Tree | None,
43
+ language: Language,
44
+ query_name: str,
45
+ source_code: str,
46
+ ) -> dict[str, Any]:
47
+ """
48
+ Execute a predefined query by name.
49
+
50
+ Args:
51
+ tree: Tree-sitter tree to query
52
+ language: Tree-sitter language object
53
+ query_name: Name of the predefined query
54
+ source_code: Source code for context
55
+
56
+ Returns:
57
+ Dictionary containing query results and metadata
58
+ """
59
+ start_time = time.time()
60
+ self._execution_stats["total_queries"] += 1
61
+
62
+ try:
63
+ # Validate inputs
64
+ if tree is None:
65
+ return self._create_error_result("Tree is None", query_name=query_name)
66
+
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
73
+ language_name = language.name if hasattr(language, "name") else "unknown"
74
+ query_string = self._query_loader.get_query(language_name, query_name)
75
+ if query_string is None:
76
+ return self._create_error_result(
77
+ f"Query '{query_name}' not found", query_name=query_name
78
+ )
79
+
80
+ # Create and execute the query
81
+ try:
82
+ query = language.query(query_string)
83
+ captures = query.captures(tree.root_node)
84
+
85
+ # Process captures
86
+ try:
87
+ processed_captures = self._process_captures(captures, source_code)
88
+ except Exception as e:
89
+ logger.error(f"Error processing captures for {query_name}: {e}")
90
+ return self._create_error_result(
91
+ f"Capture processing failed: {str(e)}", query_name=query_name
92
+ )
93
+
94
+ self._execution_stats["successful_queries"] += 1
95
+ execution_time = time.time() - start_time
96
+ self._execution_stats["total_execution_time"] += execution_time
97
+
98
+ return {
99
+ "captures": processed_captures,
100
+ "query_name": query_name,
101
+ "query_string": query_string,
102
+ "execution_time": execution_time,
103
+ "success": True,
104
+ }
105
+
106
+ except Exception as e:
107
+ logger.error(f"Error executing query '{query_name}': {e}")
108
+ return self._create_error_result(
109
+ f"Query execution failed: {str(e)}", query_name=query_name
110
+ )
111
+
112
+ except Exception as e:
113
+ logger.error(f"Unexpected error in execute_query: {e}")
114
+ self._execution_stats["failed_queries"] += 1
115
+ return self._create_error_result(
116
+ f"Unexpected error: {str(e)}", query_name=query_name
117
+ )
118
+
119
+ def execute_query_string(
120
+ self,
121
+ tree: Tree | None,
122
+ language: Language,
123
+ query_string: str,
124
+ source_code: str,
125
+ ) -> dict[str, Any]:
126
+ """
127
+ Execute a query string directly.
128
+
129
+ Args:
130
+ tree: Tree-sitter tree to query
131
+ language: Tree-sitter language object
132
+ query_string: Query string to execute
133
+ source_code: Source code for context
134
+
135
+ Returns:
136
+ Dictionary containing query results and metadata
137
+ """
138
+ start_time = time.time()
139
+ self._execution_stats["total_queries"] += 1
140
+
141
+ try:
142
+ # Validate inputs
143
+ if tree is None:
144
+ return self._create_error_result("Tree is None")
145
+
146
+ if language is None:
147
+ return self._create_error_result("Language is None")
148
+
149
+ # Create and execute the query
150
+ try:
151
+ query = language.query(query_string)
152
+ captures = query.captures(tree.root_node)
153
+
154
+ # Process captures
155
+ try:
156
+ processed_captures = self._process_captures(captures, source_code)
157
+ except Exception as e:
158
+ logger.error(f"Error processing captures: {e}")
159
+ return self._create_error_result(
160
+ f"Capture processing failed: {str(e)}"
161
+ )
162
+
163
+ self._execution_stats["successful_queries"] += 1
164
+ execution_time = time.time() - start_time
165
+ self._execution_stats["total_execution_time"] += execution_time
166
+
167
+ return {
168
+ "captures": processed_captures,
169
+ "query_string": query_string,
170
+ "execution_time": execution_time,
171
+ "success": True,
172
+ }
173
+
174
+ except Exception as e:
175
+ logger.error(f"Error executing query string: {e}")
176
+ return self._create_error_result(
177
+ f"Query execution failed: {str(e)}", query_string=query_string
178
+ )
179
+
180
+ except Exception as e:
181
+ logger.error(f"Unexpected error in execute_query_string: {e}")
182
+ self._execution_stats["failed_queries"] += 1
183
+ return self._create_error_result(f"Unexpected error: {str(e)}")
184
+
185
+ def execute_multiple_queries(
186
+ self, tree: Tree, language: Language, query_names: list[str], source_code: str
187
+ ) -> dict[str, dict[str, Any]]:
188
+ """
189
+ Execute multiple queries and return combined results.
190
+
191
+ Args:
192
+ tree: Tree-sitter tree to query
193
+ language: Tree-sitter language object
194
+ query_names: List of query names to execute
195
+ source_code: Source code for context
196
+
197
+ Returns:
198
+ Dictionary mapping query names to their results
199
+ """
200
+ results = {}
201
+
202
+ for query_name in query_names:
203
+ result = self.execute_query(tree, language, query_name, source_code)
204
+ results[query_name] = result
205
+
206
+ return results
207
+
208
+ def _process_captures(
209
+ self, captures: list[dict[str, Any] | tuple[Node, str]], source_code: str
210
+ ) -> list[dict[str, Any]]:
211
+ """
212
+ Process query captures into standardized format.
213
+
214
+ Args:
215
+ captures: Raw captures from Tree-sitter query
216
+ source_code: Source code for context
217
+
218
+ Returns:
219
+ List of processed capture dictionaries
220
+ """
221
+ processed = []
222
+
223
+ try:
224
+ for capture in captures:
225
+ try:
226
+ # Handle both dictionary and tuple formats
227
+ if isinstance(capture, dict):
228
+ # New Tree-sitter API format
229
+ node = capture.get("node")
230
+ name = capture.get("name", "unknown")
231
+ elif isinstance(capture, tuple) and len(capture) == 2:
232
+ # Old Tree-sitter API format
233
+ node, name = capture
234
+ else:
235
+ logger.warning(f"Unexpected capture format: {type(capture)}")
236
+ continue
237
+
238
+ if node is None:
239
+ continue
240
+
241
+ result_dict = self._create_result_dict(node, name, source_code)
242
+ processed.append(result_dict)
243
+
244
+ except Exception as e:
245
+ logger.error(f"Error processing capture: {e}")
246
+ continue
247
+
248
+ except Exception as e:
249
+ logger.error(f"Error in _process_captures: {e}")
250
+
251
+ return processed
252
+
253
+ def _create_result_dict(
254
+ self, node: Node, capture_name: str, source_code: str
255
+ ) -> dict[str, Any]:
256
+ """
257
+ Create a result dictionary from a Tree-sitter node.
258
+
259
+ Args:
260
+ node: Tree-sitter node
261
+ capture_name: Name of the capture
262
+ source_code: Source code for context
263
+
264
+ Returns:
265
+ Dictionary containing node information
266
+ """
267
+ try:
268
+ # Extract node text
269
+ node_text = ""
270
+ if hasattr(node, "text") and node.text:
271
+ try:
272
+ node_text = node.text.decode("utf-8", errors="replace")
273
+ except Exception:
274
+ node_text = str(node.text)
275
+
276
+ return {
277
+ "capture_name": capture_name,
278
+ "node_type": getattr(node, "type", "unknown"),
279
+ "start_point": getattr(node, "start_point", (0, 0)),
280
+ "end_point": getattr(node, "end_point", (0, 0)),
281
+ "start_byte": getattr(node, "start_byte", 0),
282
+ "end_byte": getattr(node, "end_byte", 0),
283
+ "text": node_text,
284
+ "line_number": getattr(node, "start_point", (0, 0))[0] + 1,
285
+ "column_number": getattr(node, "start_point", (0, 0))[1],
286
+ }
287
+
288
+ except Exception as e:
289
+ logger.error(f"Error creating result dict: {e}")
290
+ return {"capture_name": capture_name, "node_type": "error", "error": str(e)}
291
+
292
+ def _create_error_result(
293
+ self, error_message: str, query_name: str | None = None, **kwargs
294
+ ) -> dict[str, Any]:
295
+ """
296
+ Create an error result dictionary.
297
+
298
+ Args:
299
+ error_message: Error message
300
+ query_name: Optional query name
301
+ **kwargs: Additional fields to include in the error result
302
+
303
+ Returns:
304
+ Error result dictionary
305
+ """
306
+ result = {"captures": [], "error": error_message, "success": False}
307
+
308
+ if query_name:
309
+ result["query_name"] = query_name
310
+
311
+ result.update(kwargs)
312
+ return result
313
+
314
+ def get_available_queries(self, language: str) -> list[str]:
315
+ """
316
+ Get available queries for a language.
317
+
318
+ Args:
319
+ language: Programming language name
320
+
321
+ Returns:
322
+ List of available query names
323
+ """
324
+ try:
325
+ return self._query_loader.get_all_queries_for_language(language)
326
+ except Exception as e:
327
+ logger.error(f"Error getting available queries for {language}: {e}")
328
+ return []
329
+
330
+ def get_query_description(self, language: str, query_name: str) -> str | None:
331
+ """
332
+ Get description for a specific query.
333
+
334
+ Args:
335
+ language: Programming language name
336
+ query_name: Name of the query
337
+
338
+ Returns:
339
+ Query description or None if not found
340
+ """
341
+ try:
342
+ return self._query_loader.get_query_description(language, query_name)
343
+ except Exception as e:
344
+ logger.error(f"Error getting query description: {e}")
345
+ return None
346
+
347
+ def validate_query(self, language: str, query_string: str) -> bool:
348
+ """
349
+ Validate a query string for a specific language.
350
+
351
+ Args:
352
+ language: Programming language name
353
+ query_string: Query string to validate
354
+
355
+ Returns:
356
+ True if query is valid, False otherwise
357
+ """
358
+ try:
359
+ # This would require loading the language and attempting to create the query
360
+ # For now, we'll do basic validation
361
+ from ..language_loader import get_loader
362
+
363
+ loader = get_loader()
364
+
365
+ lang_obj = loader.load_language(language)
366
+ if lang_obj is None:
367
+ return False
368
+
369
+ # Try to create the query
370
+ lang_obj.query(query_string)
371
+ return True
372
+
373
+ except Exception as e:
374
+ logger.error(f"Query validation failed: {e}")
375
+ return False
376
+
377
+ def get_query_statistics(self) -> dict[str, Any]:
378
+ """
379
+ Get query execution statistics.
380
+
381
+ Returns:
382
+ Dictionary containing execution statistics
383
+ """
384
+ stats = self._execution_stats.copy()
385
+
386
+ if stats["total_queries"] > 0:
387
+ stats["success_rate"] = stats["successful_queries"] / stats["total_queries"]
388
+ stats["average_execution_time"] = (
389
+ stats["total_execution_time"] / stats["total_queries"]
390
+ )
391
+ else:
392
+ stats["success_rate"] = 0.0
393
+ stats["average_execution_time"] = 0.0
394
+
395
+ return stats
396
+
397
+ def reset_statistics(self) -> None:
398
+ """Reset query execution statistics."""
399
+ self._execution_stats = {
400
+ "total_queries": 0,
401
+ "successful_queries": 0,
402
+ "failed_queries": 0,
403
+ "total_execution_time": 0.0,
404
+ }
405
+
406
+
407
+ # Module-level convenience functions for backward compatibility
408
+ def get_available_queries(language: str = None) -> list[str]:
409
+ """
410
+ Get available queries for a language (module-level function).
411
+
412
+ Args:
413
+ language: Programming language name (optional)
414
+
415
+ Returns:
416
+ List of available query names
417
+ """
418
+ try:
419
+ loader = get_query_loader()
420
+ if language:
421
+ return loader.list_queries_for_language(language)
422
+
423
+ # If no language, return a list of all query names across supported languages
424
+ all_queries = set()
425
+ for lang in loader.list_supported_languages():
426
+ all_queries.update(loader.list_queries_for_language(lang))
427
+ return sorted(all_queries)
428
+
429
+ except Exception as e:
430
+ logger.error(f"Error getting available queries: {e}")
431
+ return []
432
+
433
+
434
+ def get_query_description(language: str, query_name: str) -> str | None:
435
+ """
436
+ Get description for a specific query (module-level function).
437
+
438
+ Args:
439
+ language: Programming language name
440
+ query_name: Name of the query
441
+
442
+ Returns:
443
+ Query description or None if not found
444
+ """
445
+ try:
446
+ from ..query_loader import get_query_loader
447
+
448
+ loader = get_query_loader()
449
+ return loader.get_query_description(language, query_name)
450
+ except Exception as e:
451
+ logger.error(f"Error getting query description: {e}")
452
+
453
+
454
+ # Module-level attributes for backward compatibility
455
+ try:
456
+ from ..query_loader import get_query_loader
457
+
458
+ query_loader = get_query_loader()
459
+ except Exception:
460
+ query_loader = None
461
+
462
+
463
+ def get_all_queries_for_language(language: str) -> list[str]:
464
+ """
465
+ Get all available queries for a specific language.
466
+
467
+ Args:
468
+ language: Programming language name
469
+
470
+ Returns:
471
+ List of available query names for the language
472
+
473
+ .. deprecated:: 0.2.1
474
+ This function is deprecated and will be removed in a future version.
475
+ Use the unified analysis engine instead.
476
+ """
477
+ import warnings
478
+
479
+ warnings.warn(
480
+ "get_all_queries_for_language is deprecated and will be removed in a future version. "
481
+ "Use the unified analysis engine instead.",
482
+ DeprecationWarning,
483
+ stacklevel=2,
484
+ )
485
+ return []
486
+
487
+
488
+ # Update module-level attributes for backward compatibility
489
+ try:
490
+ from ..language_loader import get_loader
491
+
492
+ loader = get_loader()
493
+ except Exception:
494
+ loader = None