tree-sitter-analyzer 0.1.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 +121 -0
  2. tree_sitter_analyzer/__main__.py +12 -0
  3. tree_sitter_analyzer/api.py +539 -0
  4. tree_sitter_analyzer/cli/__init__.py +39 -0
  5. tree_sitter_analyzer/cli/__main__.py +13 -0
  6. tree_sitter_analyzer/cli/commands/__init__.py +27 -0
  7. tree_sitter_analyzer/cli/commands/advanced_command.py +88 -0
  8. tree_sitter_analyzer/cli/commands/base_command.py +155 -0
  9. tree_sitter_analyzer/cli/commands/default_command.py +19 -0
  10. tree_sitter_analyzer/cli/commands/partial_read_command.py +133 -0
  11. tree_sitter_analyzer/cli/commands/query_command.py +82 -0
  12. tree_sitter_analyzer/cli/commands/structure_command.py +121 -0
  13. tree_sitter_analyzer/cli/commands/summary_command.py +93 -0
  14. tree_sitter_analyzer/cli/commands/table_command.py +233 -0
  15. tree_sitter_analyzer/cli/info_commands.py +121 -0
  16. tree_sitter_analyzer/cli_main.py +276 -0
  17. tree_sitter_analyzer/core/__init__.py +20 -0
  18. tree_sitter_analyzer/core/analysis_engine.py +574 -0
  19. tree_sitter_analyzer/core/cache_service.py +330 -0
  20. tree_sitter_analyzer/core/engine.py +560 -0
  21. tree_sitter_analyzer/core/parser.py +288 -0
  22. tree_sitter_analyzer/core/query.py +502 -0
  23. tree_sitter_analyzer/encoding_utils.py +460 -0
  24. tree_sitter_analyzer/exceptions.py +340 -0
  25. tree_sitter_analyzer/file_handler.py +222 -0
  26. tree_sitter_analyzer/formatters/__init__.py +1 -0
  27. tree_sitter_analyzer/formatters/base_formatter.py +168 -0
  28. tree_sitter_analyzer/formatters/formatter_factory.py +74 -0
  29. tree_sitter_analyzer/formatters/java_formatter.py +270 -0
  30. tree_sitter_analyzer/formatters/python_formatter.py +235 -0
  31. tree_sitter_analyzer/interfaces/__init__.py +10 -0
  32. tree_sitter_analyzer/interfaces/cli.py +557 -0
  33. tree_sitter_analyzer/interfaces/cli_adapter.py +319 -0
  34. tree_sitter_analyzer/interfaces/mcp_adapter.py +170 -0
  35. tree_sitter_analyzer/interfaces/mcp_server.py +416 -0
  36. tree_sitter_analyzer/java_analyzer.py +219 -0
  37. tree_sitter_analyzer/language_detector.py +400 -0
  38. tree_sitter_analyzer/language_loader.py +228 -0
  39. tree_sitter_analyzer/languages/__init__.py +11 -0
  40. tree_sitter_analyzer/languages/java_plugin.py +1113 -0
  41. tree_sitter_analyzer/languages/python_plugin.py +712 -0
  42. tree_sitter_analyzer/mcp/__init__.py +32 -0
  43. tree_sitter_analyzer/mcp/resources/__init__.py +47 -0
  44. tree_sitter_analyzer/mcp/resources/code_file_resource.py +213 -0
  45. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +550 -0
  46. tree_sitter_analyzer/mcp/server.py +319 -0
  47. tree_sitter_analyzer/mcp/tools/__init__.py +36 -0
  48. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +558 -0
  49. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +245 -0
  50. tree_sitter_analyzer/mcp/tools/base_tool.py +55 -0
  51. tree_sitter_analyzer/mcp/tools/get_positions_tool.py +448 -0
  52. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +302 -0
  53. tree_sitter_analyzer/mcp/tools/table_format_tool.py +359 -0
  54. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +476 -0
  55. tree_sitter_analyzer/mcp/utils/__init__.py +106 -0
  56. tree_sitter_analyzer/mcp/utils/error_handler.py +549 -0
  57. tree_sitter_analyzer/models.py +481 -0
  58. tree_sitter_analyzer/output_manager.py +264 -0
  59. tree_sitter_analyzer/plugins/__init__.py +334 -0
  60. tree_sitter_analyzer/plugins/base.py +446 -0
  61. tree_sitter_analyzer/plugins/java_plugin.py +625 -0
  62. tree_sitter_analyzer/plugins/javascript_plugin.py +439 -0
  63. tree_sitter_analyzer/plugins/manager.py +355 -0
  64. tree_sitter_analyzer/plugins/plugin_loader.py +83 -0
  65. tree_sitter_analyzer/plugins/python_plugin.py +598 -0
  66. tree_sitter_analyzer/plugins/registry.py +366 -0
  67. tree_sitter_analyzer/queries/__init__.py +27 -0
  68. tree_sitter_analyzer/queries/java.py +394 -0
  69. tree_sitter_analyzer/queries/javascript.py +149 -0
  70. tree_sitter_analyzer/queries/python.py +286 -0
  71. tree_sitter_analyzer/queries/typescript.py +230 -0
  72. tree_sitter_analyzer/query_loader.py +260 -0
  73. tree_sitter_analyzer/table_formatter.py +448 -0
  74. tree_sitter_analyzer/utils.py +201 -0
  75. tree_sitter_analyzer-0.1.0.dist-info/METADATA +581 -0
  76. tree_sitter_analyzer-0.1.0.dist-info/RECORD +78 -0
  77. tree_sitter_analyzer-0.1.0.dist-info/WHEEL +4 -0
  78. tree_sitter_analyzer-0.1.0.dist-info/entry_points.txt +8 -0
@@ -0,0 +1,502 @@
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