tree-sitter-analyzer 0.1.0__py3-none-any.whl → 0.1.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.

@@ -1,448 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Get Code Positions MCP Tool
5
-
6
- This tool provides precise position information for code elements
7
- like classes, methods, fields, and imports through the MCP protocol.
8
- """
9
-
10
- import json
11
- import logging
12
- from pathlib import Path
13
- from typing import Any, Dict, List, Optional
14
-
15
- from ...core.analysis_engine import get_analysis_engine, AnalysisRequest
16
- from ...language_detector import detect_language_from_file
17
- from ...utils import log_performance, setup_logger
18
-
19
- # Set up logging
20
- logger = setup_logger(__name__)
21
-
22
-
23
- class GetPositionsTool:
24
- """
25
- MCP Tool for getting precise position information of code elements.
26
-
27
- This tool integrates with existing analyzer components to provide
28
- detailed location data for various code elements.
29
- """
30
-
31
- def __init__(self) -> None:
32
- """Initialize the get positions tool."""
33
- self.analysis_engine = get_analysis_engine()
34
- logger.info("GetPositionsTool initialized")
35
-
36
- def get_tool_schema(self) -> Dict[str, Any]:
37
- """
38
- Get the MCP tool schema for get_code_positions.
39
-
40
- Returns:
41
- Dictionary containing the tool schema
42
- """
43
- return {
44
- "type": "object",
45
- "properties": {
46
- "file_path": {
47
- "type": "string",
48
- "description": "Path to the code file to analyze",
49
- },
50
- "language": {
51
- "type": "string",
52
- "description": "Programming language (optional, auto-detected if not specified)",
53
- },
54
- "element_types": {
55
- "type": "array",
56
- "items": {
57
- "type": "string",
58
- "enum": [
59
- "classes",
60
- "methods",
61
- "fields",
62
- "imports",
63
- "annotations",
64
- ],
65
- },
66
- "description": "Types of code elements to get positions for",
67
- "default": ["classes", "methods"],
68
- },
69
- "include_details": {
70
- "type": "boolean",
71
- "description": "Include detailed information like signatures and visibility",
72
- "default": False,
73
- },
74
- "format": {
75
- "type": "string",
76
- "description": "Output format for the position data",
77
- "enum": ["json", "text"],
78
- "default": "json",
79
- },
80
- },
81
- "required": ["file_path"],
82
- "additionalProperties": False,
83
- }
84
-
85
- async def execute(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
86
- """
87
- Execute the get_code_positions tool.
88
-
89
- Args:
90
- arguments: Tool arguments containing file_path and analysis options
91
-
92
- Returns:
93
- Dictionary containing position information for requested elements
94
-
95
- Raises:
96
- ValueError: If required arguments are missing or invalid
97
- FileNotFoundError: If the specified file doesn't exist
98
- """
99
- # Validate required arguments
100
- if "file_path" not in arguments:
101
- raise ValueError("file_path is required")
102
-
103
- file_path = arguments["file_path"]
104
- language = arguments.get("language")
105
- element_types = arguments.get("element_types", ["classes", "methods"])
106
- include_details = arguments.get("include_details", False)
107
- output_format = arguments.get("format", "json")
108
-
109
- # Validate file exists
110
- if not Path(file_path).exists():
111
- raise FileNotFoundError(f"File not found: {file_path}")
112
-
113
- # Detect language if not specified
114
- if not language:
115
- language = detect_language_from_file(file_path)
116
- if language == "unknown":
117
- raise ValueError(f"Could not detect language for file: {file_path}")
118
-
119
- logger.info(f"Getting positions for {file_path} (language: {language})")
120
-
121
- try:
122
- # Use AdvancedAnalyzer for comprehensive analysis
123
- # Use performance monitoring with proper context manager
124
- from ...mcp.utils import get_performance_monitor
125
-
126
- with get_performance_monitor().measure_operation("get_code_positions"):
127
- # Use unified analysis engine instead of deprecated advanced_analyzer
128
- request = AnalysisRequest(
129
- file_path=file_path,
130
- language=language,
131
- include_complexity=True,
132
- include_details=True
133
- )
134
- analysis_result = await self.analysis_engine.analyze(request)
135
-
136
- if analysis_result is None:
137
- raise RuntimeError(f"Failed to analyze file: {file_path}")
138
-
139
- # Extract position information for requested element types
140
- positions = {}
141
-
142
- for element_type in element_types:
143
- positions[element_type] = self._get_element_positions(
144
- analysis_result, element_type, include_details
145
- )
146
-
147
- # Build result structure
148
- result = {
149
- "file_path": file_path,
150
- "language": language,
151
- "element_types": element_types,
152
- "include_details": include_details,
153
- "format": output_format,
154
- "total_elements": sum(
155
- len(pos_list) for pos_list in positions.values()
156
- ),
157
- }
158
-
159
- # Format output based on requested format
160
- if output_format == "text":
161
- # Text format: human-readable representation
162
- result["content"] = self._format_positions_as_text(
163
- positions, include_details
164
- )
165
- else:
166
- # JSON format: structured position data
167
- result["positions"] = positions
168
-
169
- logger.info(
170
- f"Successfully extracted positions for {result['total_elements']} elements from {file_path}"
171
- )
172
- return result
173
-
174
- except Exception as e:
175
- logger.error(f"Error getting positions from {file_path}: {e}")
176
- raise
177
-
178
- def _get_element_positions(
179
- self, analysis_result: Any, element_type: str, include_details: bool
180
- ) -> List[Dict[str, Any]]:
181
- """
182
- Extract position information for a specific element type.
183
-
184
- Args:
185
- analysis_result: Result from AdvancedAnalyzer
186
- element_type: Type of elements to extract
187
- include_details: Whether to include detailed information
188
-
189
- Returns:
190
- List of position dictionaries for the element type
191
- """
192
- positions = []
193
-
194
- if element_type == "classes":
195
- classes = [e for e in analysis_result.elements if e.__class__.__name__ == 'Class']
196
- for cls in classes:
197
- position = {
198
- "name": cls.name,
199
- "start_line": cls.start_line,
200
- "end_line": cls.end_line,
201
- "start_column": 0, # Tree-sitter provides line-based positions
202
- "end_column": 0,
203
- }
204
-
205
- if include_details:
206
- position.update(
207
- {
208
- "type": cls.class_type,
209
- "visibility": cls.visibility,
210
- "extends": cls.extends_class,
211
- "implements": cls.implements_interfaces,
212
- "annotations": [ann.name for ann in cls.annotations],
213
- }
214
- )
215
-
216
- positions.append(position)
217
-
218
- elif element_type == "methods":
219
- methods = [e for e in analysis_result.elements if e.__class__.__name__ == 'Function']
220
- for method in methods:
221
- position = {
222
- "name": method.name,
223
- "start_line": method.start_line,
224
- "end_line": method.end_line,
225
- "start_column": 0,
226
- "end_column": 0,
227
- }
228
-
229
- if include_details:
230
- position.update(
231
- {
232
- "signature": f"{method.name}({', '.join(method.parameters)})",
233
- "return_type": method.return_type,
234
- "visibility": method.visibility,
235
- "is_static": method.is_static,
236
- "is_constructor": method.is_constructor,
237
- "complexity": method.complexity_score,
238
- "annotations": [ann.name for ann in method.annotations],
239
- }
240
- )
241
-
242
- positions.append(position)
243
-
244
- elif element_type == "fields":
245
- fields = [e for e in analysis_result.elements if e.__class__.__name__ == 'Variable']
246
- for field in fields:
247
- position = {
248
- "name": field.name,
249
- "start_line": field.start_line,
250
- "end_line": field.end_line,
251
- "start_column": 0,
252
- "end_column": 0,
253
- }
254
-
255
- if include_details:
256
- position.update(
257
- {
258
- "type": field.field_type,
259
- "visibility": field.visibility,
260
- "is_static": field.is_static,
261
- "is_final": field.is_final,
262
- "annotations": [ann.name for ann in field.annotations],
263
- }
264
- )
265
-
266
- positions.append(position)
267
-
268
- elif element_type == "imports":
269
- imports = [e for e in analysis_result.elements if e.__class__.__name__ == 'Import']
270
- for imp in imports:
271
- position = {
272
- "name": imp.imported_name,
273
- "start_line": imp.line_number,
274
- "end_line": imp.line_number,
275
- "start_column": 0,
276
- "end_column": 0,
277
- }
278
-
279
- if include_details:
280
- position.update(
281
- {
282
- "statement": imp.import_statement,
283
- "is_static": imp.is_static,
284
- "is_wildcard": imp.is_wildcard,
285
- }
286
- )
287
-
288
- positions.append(position)
289
-
290
- elif element_type == "annotations":
291
- for ann in analysis_result.annotations:
292
- position = {
293
- "name": ann.name,
294
- "start_line": ann.line_number,
295
- "end_line": ann.line_number,
296
- "start_column": 0,
297
- "end_column": 0,
298
- }
299
-
300
- if include_details:
301
- position.update(
302
- {
303
- "parameters": ann.parameters,
304
- }
305
- )
306
-
307
- positions.append(position)
308
-
309
- return positions
310
-
311
- def _format_positions_as_text(
312
- self, positions: Dict[str, List[Dict[str, Any]]], include_details: bool
313
- ) -> str:
314
- """
315
- Format position data as human-readable text.
316
-
317
- Args:
318
- positions: Position data for all element types
319
- include_details: Whether to include detailed information
320
-
321
- Returns:
322
- Formatted text representation
323
- """
324
- lines = []
325
-
326
- for element_type, elements in positions.items():
327
- if not elements:
328
- continue
329
-
330
- lines.append(f"\n{element_type.upper()}:")
331
- lines.append("=" * (len(element_type) + 1))
332
-
333
- for element in elements:
334
- name = element["name"]
335
- start_line = element["start_line"]
336
- end_line = element["end_line"]
337
-
338
- if start_line == end_line:
339
- location = f"line {start_line}"
340
- else:
341
- location = f"lines {start_line}-{end_line}"
342
-
343
- lines.append(f" {name} ({location})")
344
-
345
- if include_details:
346
- for key, value in element.items():
347
- if key not in [
348
- "name",
349
- "start_line",
350
- "end_line",
351
- "start_column",
352
- "end_column",
353
- ]:
354
- if value: # Only show non-empty values
355
- lines.append(f" {key}: {value}")
356
-
357
- return "\n".join(lines)
358
-
359
- def validate_arguments(self, arguments: Dict[str, Any]) -> bool:
360
- """
361
- Validate tool arguments against the schema.
362
-
363
- Args:
364
- arguments: Arguments to validate
365
-
366
- Returns:
367
- True if arguments are valid
368
-
369
- Raises:
370
- ValueError: If arguments are invalid
371
- """
372
- schema = self.get_tool_schema()
373
- required_fields = schema.get("required", [])
374
-
375
- # Check required fields
376
- for field in required_fields:
377
- if field not in arguments:
378
- raise ValueError(f"Required field '{field}' is missing")
379
-
380
- # Validate file_path
381
- if "file_path" in arguments:
382
- file_path = arguments["file_path"]
383
- if not isinstance(file_path, str):
384
- raise ValueError("file_path must be a string")
385
- if not file_path.strip():
386
- raise ValueError("file_path cannot be empty")
387
-
388
- # Validate element_types
389
- if "element_types" in arguments:
390
- element_types = arguments["element_types"]
391
- if not isinstance(element_types, list):
392
- raise ValueError("element_types must be a list")
393
- if not element_types:
394
- raise ValueError("element_types cannot be empty")
395
-
396
- valid_types = ["classes", "methods", "fields", "imports", "annotations"]
397
- for element_type in element_types:
398
- if element_type not in valid_types:
399
- raise ValueError(
400
- f"Invalid element_type: {element_type}. Must be one of {valid_types}"
401
- )
402
-
403
- # Validate optional fields
404
- if "language" in arguments:
405
- language = arguments["language"]
406
- if not isinstance(language, str):
407
- raise ValueError("language must be a string")
408
-
409
- if "include_details" in arguments:
410
- include_details = arguments["include_details"]
411
- if not isinstance(include_details, bool):
412
- raise ValueError("include_details must be a boolean")
413
-
414
- if "format" in arguments:
415
- format_value = arguments["format"]
416
- if not isinstance(format_value, str):
417
- raise ValueError("format must be a string")
418
- if format_value not in ["json", "text"]:
419
- raise ValueError("format must be 'json' or 'text'")
420
-
421
- return True
422
-
423
- def get_tool_definition(self) -> Any:
424
- """
425
- Get the MCP tool definition for get_code_positions.
426
-
427
- Returns:
428
- Tool definition object compatible with MCP server
429
- """
430
- try:
431
- from mcp.types import Tool
432
-
433
- return Tool(
434
- name="get_code_positions",
435
- description="Get precise position information for code elements like classes, methods, fields, and imports",
436
- inputSchema=self.get_tool_schema(),
437
- )
438
- except ImportError:
439
- # Fallback for when MCP is not available
440
- return {
441
- "name": "get_code_positions",
442
- "description": "Get precise position information for code elements like classes, methods, fields, and imports",
443
- "inputSchema": self.get_tool_schema(),
444
- }
445
-
446
-
447
- # Tool instance for easy access
448
- get_positions_tool = GetPositionsTool()
@@ -1,8 +0,0 @@
1
- [console_scripts]
2
- code-analyzer = tree_sitter_analyzer.interfaces.cli:main
3
- java-analyzer = tree_sitter_analyzer.interfaces.cli:main
4
- tree-sitter-analyzer = tree_sitter_analyzer.interfaces.cli:main
5
-
6
- [tree_sitter_analyzer.plugins]
7
- java = tree_sitter_analyzer.languages.java_plugin:JavaPlugin
8
- python = tree_sitter_analyzer.plugins.python_plugin:PythonPlugin