tree-sitter-analyzer 1.9.1__py3-none-any.whl → 1.9.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tree-sitter-analyzer might be problematic. Click here for more details.
- tree_sitter_analyzer/__init__.py +1 -1
- tree_sitter_analyzer/api.py +10 -6
- tree_sitter_analyzer/cli/argument_validator.py +1 -1
- tree_sitter_analyzer/cli/commands/advanced_command.py +3 -6
- tree_sitter_analyzer/cli/commands/query_command.py +3 -1
- tree_sitter_analyzer/cli/commands/table_command.py +3 -3
- tree_sitter_analyzer/constants.py +5 -3
- tree_sitter_analyzer/core/analysis_engine.py +1 -1
- tree_sitter_analyzer/core/cache_service.py +1 -1
- tree_sitter_analyzer/core/engine.py +1 -1
- tree_sitter_analyzer/core/query.py +0 -2
- tree_sitter_analyzer/exceptions.py +1 -1
- tree_sitter_analyzer/file_handler.py +6 -6
- tree_sitter_analyzer/formatters/base_formatter.py +1 -1
- tree_sitter_analyzer/formatters/html_formatter.py +24 -14
- tree_sitter_analyzer/formatters/javascript_formatter.py +28 -21
- tree_sitter_analyzer/formatters/language_formatter_factory.py +7 -4
- tree_sitter_analyzer/formatters/markdown_formatter.py +4 -4
- tree_sitter_analyzer/formatters/python_formatter.py +4 -4
- tree_sitter_analyzer/formatters/typescript_formatter.py +1 -1
- tree_sitter_analyzer/interfaces/mcp_adapter.py +4 -2
- tree_sitter_analyzer/interfaces/mcp_server.py +10 -10
- tree_sitter_analyzer/language_detector.py +30 -5
- tree_sitter_analyzer/language_loader.py +46 -26
- tree_sitter_analyzer/languages/css_plugin.py +6 -6
- tree_sitter_analyzer/languages/html_plugin.py +12 -8
- tree_sitter_analyzer/languages/java_plugin.py +307 -520
- tree_sitter_analyzer/languages/javascript_plugin.py +22 -78
- tree_sitter_analyzer/languages/markdown_plugin.py +277 -297
- tree_sitter_analyzer/languages/python_plugin.py +47 -85
- tree_sitter_analyzer/languages/typescript_plugin.py +48 -123
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +14 -8
- tree_sitter_analyzer/mcp/server.py +38 -23
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +10 -7
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +51 -7
- tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +15 -2
- tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +8 -6
- tree_sitter_analyzer/mcp/tools/list_files_tool.py +6 -6
- tree_sitter_analyzer/mcp/tools/search_content_tool.py +48 -19
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +13 -8
- tree_sitter_analyzer/mcp/utils/file_output_manager.py +8 -3
- tree_sitter_analyzer/mcp/utils/gitignore_detector.py +24 -12
- tree_sitter_analyzer/mcp/utils/path_resolver.py +2 -2
- tree_sitter_analyzer/models.py +16 -0
- tree_sitter_analyzer/mypy_current_errors.txt +2 -0
- tree_sitter_analyzer/plugins/base.py +66 -0
- tree_sitter_analyzer/queries/java.py +1 -1
- tree_sitter_analyzer/queries/javascript.py +3 -8
- tree_sitter_analyzer/queries/markdown.py +1 -1
- tree_sitter_analyzer/queries/python.py +2 -2
- tree_sitter_analyzer/security/boundary_manager.py +2 -5
- tree_sitter_analyzer/security/regex_checker.py +2 -2
- tree_sitter_analyzer/security/validator.py +5 -1
- tree_sitter_analyzer/table_formatter.py +4 -4
- tree_sitter_analyzer/utils/__init__.py +27 -116
- tree_sitter_analyzer/{utils.py → utils/logging.py} +2 -2
- tree_sitter_analyzer/utils/tree_sitter_compat.py +2 -2
- {tree_sitter_analyzer-1.9.1.dist-info → tree_sitter_analyzer-1.9.3.dist-info}/METADATA +70 -30
- tree_sitter_analyzer-1.9.3.dist-info/RECORD +110 -0
- tree_sitter_analyzer-1.9.1.dist-info/RECORD +0 -109
- {tree_sitter_analyzer-1.9.1.dist-info → tree_sitter_analyzer-1.9.3.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-1.9.1.dist-info → tree_sitter_analyzer-1.9.3.dist-info}/entry_points.txt +0 -0
tree_sitter_analyzer/__init__.py
CHANGED
tree_sitter_analyzer/api.py
CHANGED
|
@@ -85,7 +85,8 @@ def analyze_file(
|
|
|
85
85
|
|
|
86
86
|
# Add elements if requested and available
|
|
87
87
|
if include_elements and hasattr(analysis_result, "elements"):
|
|
88
|
-
|
|
88
|
+
elements_list: list[dict[str, Any]] = []
|
|
89
|
+
result["elements"] = elements_list
|
|
89
90
|
for elem in analysis_result.elements:
|
|
90
91
|
elem_dict = {
|
|
91
92
|
"name": elem.name,
|
|
@@ -145,7 +146,7 @@ def analyze_file(
|
|
|
145
146
|
else:
|
|
146
147
|
elem_dict["class_name"] = None
|
|
147
148
|
|
|
148
|
-
|
|
149
|
+
elements_list.append(elem_dict)
|
|
149
150
|
|
|
150
151
|
# Add query results if requested and available
|
|
151
152
|
if include_queries and hasattr(analysis_result, "query_results"):
|
|
@@ -219,7 +220,8 @@ def analyze_code(
|
|
|
219
220
|
|
|
220
221
|
# Add elements if requested and available
|
|
221
222
|
if include_elements and hasattr(analysis_result, "elements"):
|
|
222
|
-
|
|
223
|
+
elements_list: list[dict[str, Any]] = []
|
|
224
|
+
result["elements"] = elements_list
|
|
223
225
|
for elem in analysis_result.elements:
|
|
224
226
|
elem_dict = {
|
|
225
227
|
"name": elem.name,
|
|
@@ -279,7 +281,7 @@ def analyze_code(
|
|
|
279
281
|
else:
|
|
280
282
|
elem_dict["class_name"] = None
|
|
281
283
|
|
|
282
|
-
|
|
284
|
+
elements_list.append(elem_dict)
|
|
283
285
|
|
|
284
286
|
# Add query results if requested and available
|
|
285
287
|
if include_queries and hasattr(analysis_result, "query_results"):
|
|
@@ -454,8 +456,10 @@ def validate_file(file_path: str | Path) -> dict[str, Any]:
|
|
|
454
456
|
|
|
455
457
|
# Check if file is readable
|
|
456
458
|
try:
|
|
457
|
-
|
|
458
|
-
|
|
459
|
+
from .encoding_utils import read_file_safe
|
|
460
|
+
|
|
461
|
+
# Test file readability by reading it
|
|
462
|
+
read_file_safe(file_path)
|
|
459
463
|
result["readable"] = True
|
|
460
464
|
result["size"] = file_path.stat().st_size
|
|
461
465
|
except Exception as e:
|
|
@@ -49,8 +49,9 @@ class AdvancedCommand(BaseCommand):
|
|
|
49
49
|
Dictionary containing file metrics
|
|
50
50
|
"""
|
|
51
51
|
try:
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
from ...encoding_utils import read_file_safe
|
|
53
|
+
|
|
54
|
+
content, _ = read_file_safe(file_path)
|
|
54
55
|
|
|
55
56
|
lines = content.split("\n")
|
|
56
57
|
total_lines = len(lines)
|
|
@@ -111,10 +112,6 @@ class AdvancedCommand(BaseCommand):
|
|
|
111
112
|
if "-->" not in stripped:
|
|
112
113
|
in_multiline_comment = True
|
|
113
114
|
continue
|
|
114
|
-
elif in_multiline_comment and "-->" in stripped:
|
|
115
|
-
comment_lines += 1
|
|
116
|
-
in_multiline_comment = False
|
|
117
|
-
continue
|
|
118
115
|
|
|
119
116
|
# If not a comment, it's code
|
|
120
117
|
code_lines += 1
|
|
@@ -5,6 +5,8 @@ Query Command
|
|
|
5
5
|
Handles query execution functionality.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
8
10
|
from ...core.query_service import QueryService
|
|
9
11
|
from ...output_manager import output_data, output_error, output_info, output_json
|
|
10
12
|
from .base_command import BaseCommand
|
|
@@ -13,7 +15,7 @@ from .base_command import BaseCommand
|
|
|
13
15
|
class QueryCommand(BaseCommand):
|
|
14
16
|
"""Command for executing queries."""
|
|
15
17
|
|
|
16
|
-
def __init__(self, args):
|
|
18
|
+
def __init__(self, args: Any) -> None:
|
|
17
19
|
"""Initialize the query command with QueryService."""
|
|
18
20
|
super().__init__(args)
|
|
19
21
|
self.query_service = QueryService()
|
|
@@ -25,7 +25,7 @@ from .base_command import BaseCommand
|
|
|
25
25
|
class TableCommand(BaseCommand):
|
|
26
26
|
"""Command for generating table format output."""
|
|
27
27
|
|
|
28
|
-
def __init__(self, args):
|
|
28
|
+
def __init__(self, args: Any) -> None:
|
|
29
29
|
"""Initialize the table command."""
|
|
30
30
|
super().__init__(args)
|
|
31
31
|
|
|
@@ -56,10 +56,10 @@ class TableCommand(BaseCommand):
|
|
|
56
56
|
|
|
57
57
|
# Create table formatter
|
|
58
58
|
include_javadoc = getattr(self.args, "include_javadoc", False)
|
|
59
|
-
|
|
59
|
+
table_formatter: Any = create_table_formatter(
|
|
60
60
|
self.args.table, language, include_javadoc
|
|
61
61
|
)
|
|
62
|
-
table_output =
|
|
62
|
+
table_output = table_formatter.format_structure(structure_result)
|
|
63
63
|
|
|
64
64
|
# Output table
|
|
65
65
|
self._output_table(table_output)
|
|
@@ -5,6 +5,8 @@ Constants for tree-sitter-analyzer
|
|
|
5
5
|
This module defines constants used throughout the project to ensure consistency.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from typing import Any, cast
|
|
9
|
+
|
|
8
10
|
# Element types for unified element management system
|
|
9
11
|
ELEMENT_TYPE_CLASS = "class"
|
|
10
12
|
ELEMENT_TYPE_FUNCTION = "function"
|
|
@@ -34,7 +36,7 @@ LEGACY_CLASS_MAPPING = {
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
|
|
37
|
-
def get_element_type(element) -> str:
|
|
39
|
+
def get_element_type(element: Any) -> str:
|
|
38
40
|
"""
|
|
39
41
|
Get the element type from an element object.
|
|
40
42
|
|
|
@@ -45,7 +47,7 @@ def get_element_type(element) -> str:
|
|
|
45
47
|
Standardized element type string
|
|
46
48
|
"""
|
|
47
49
|
if hasattr(element, "element_type"):
|
|
48
|
-
return element.element_type
|
|
50
|
+
return cast(str, element.element_type)
|
|
49
51
|
|
|
50
52
|
if hasattr(element, "__class__") and hasattr(element.__class__, "__name__"):
|
|
51
53
|
class_name = element.__class__.__name__
|
|
@@ -54,7 +56,7 @@ def get_element_type(element) -> str:
|
|
|
54
56
|
return "unknown"
|
|
55
57
|
|
|
56
58
|
|
|
57
|
-
def is_element_of_type(element, element_type: str) -> bool:
|
|
59
|
+
def is_element_of_type(element: Any, element_type: str) -> bool:
|
|
58
60
|
"""
|
|
59
61
|
Check if an element is of a specific type.
|
|
60
62
|
|
|
@@ -218,7 +218,7 @@ class UnifiedAnalysisEngine:
|
|
|
218
218
|
instance = super().__new__(cls)
|
|
219
219
|
cls._instances[instance_key] = instance
|
|
220
220
|
# Mark as not initialized for this instance
|
|
221
|
-
instance._initialized
|
|
221
|
+
instance._initialized = False
|
|
222
222
|
|
|
223
223
|
return cls._instances[instance_key]
|
|
224
224
|
|
|
@@ -18,7 +18,7 @@ from dataclasses import dataclass
|
|
|
18
18
|
from datetime import datetime, timedelta
|
|
19
19
|
from typing import Any
|
|
20
20
|
|
|
21
|
-
from cachetools import LRUCache, TTLCache
|
|
21
|
+
from cachetools import LRUCache, TTLCache # type: ignore[import-untyped]
|
|
22
22
|
|
|
23
23
|
from ..utils import log_debug, log_info
|
|
24
24
|
|
|
@@ -535,7 +535,7 @@ class AnalysisEngine:
|
|
|
535
535
|
logger.error(f"Error getting extensions for {language}: {e}")
|
|
536
536
|
return []
|
|
537
537
|
|
|
538
|
-
def get_registry_info(self) -> dict:
|
|
538
|
+
def get_registry_info(self) -> dict[str, Any]:
|
|
539
539
|
"""
|
|
540
540
|
Get registry information (compatibility method)
|
|
541
541
|
|
|
@@ -64,7 +64,6 @@ class QueryExecutor:
|
|
|
64
64
|
# Validate inputs
|
|
65
65
|
if tree is None:
|
|
66
66
|
return self._create_error_result("Tree is None", query_name=query_name)
|
|
67
|
-
|
|
68
67
|
if language is None:
|
|
69
68
|
return self._create_error_result( # type: ignore[unreachable]
|
|
70
69
|
"Language is None", query_name=query_name
|
|
@@ -166,7 +165,6 @@ class QueryExecutor:
|
|
|
166
165
|
# Validate inputs
|
|
167
166
|
if tree is None:
|
|
168
167
|
return self._create_error_result("Tree is None")
|
|
169
|
-
|
|
170
168
|
if language is None:
|
|
171
169
|
return self._create_error_result("Language is None") # type: ignore[unreachable]
|
|
172
170
|
|
|
@@ -616,7 +616,7 @@ def create_mcp_error_response(
|
|
|
616
616
|
|
|
617
617
|
def _sanitize_error_context(context: dict[str, Any]) -> dict[str, Any]:
|
|
618
618
|
"""Sanitize sensitive information from error context."""
|
|
619
|
-
sanitized = {}
|
|
619
|
+
sanitized: dict[str, Any] = {}
|
|
620
620
|
sensitive_keys = {
|
|
621
621
|
"password",
|
|
622
622
|
"token",
|
|
@@ -14,19 +14,19 @@ from .utils import setup_logger
|
|
|
14
14
|
logger = setup_logger(__name__)
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def log_error(message: str, *args, **kwargs) -> None:
|
|
17
|
+
def log_error(message: str, *args: object, **kwargs: object) -> None:
|
|
18
18
|
"""Log error message"""
|
|
19
|
-
logger.error(message, *args, **kwargs)
|
|
19
|
+
logger.error(message, *args, **kwargs) # type: ignore[arg-type]
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def log_info(message: str, *args, **kwargs) -> None:
|
|
22
|
+
def log_info(message: str, *args: object, **kwargs: object) -> None:
|
|
23
23
|
"""Log info message"""
|
|
24
|
-
logger.info(message, *args, **kwargs)
|
|
24
|
+
logger.info(message, *args, **kwargs) # type: ignore[arg-type]
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
def log_warning(message: str, *args, **kwargs) -> None:
|
|
27
|
+
def log_warning(message: str, *args: object, **kwargs: object) -> None:
|
|
28
28
|
"""Log warning message"""
|
|
29
|
-
logger.warning(message, *args, **kwargs)
|
|
29
|
+
logger.warning(message, *args, **kwargs) # type: ignore[arg-type]
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def detect_language_from_extension(file_path: str) -> str:
|
|
@@ -17,7 +17,7 @@ from .formatter_registry import IFormatter
|
|
|
17
17
|
class HtmlFormatter(BaseFormatter, IFormatter):
|
|
18
18
|
"""HTML-specific formatter for MarkupElement and StyleElement"""
|
|
19
19
|
|
|
20
|
-
def __init__(self):
|
|
20
|
+
def __init__(self) -> None:
|
|
21
21
|
"""Initialize HTML formatter"""
|
|
22
22
|
pass
|
|
23
23
|
|
|
@@ -37,7 +37,7 @@ class HtmlFormatter(BaseFormatter, IFormatter):
|
|
|
37
37
|
# Handle both CodeElement objects and dictionaries
|
|
38
38
|
markup_elements = []
|
|
39
39
|
style_elements = []
|
|
40
|
-
other_elements = []
|
|
40
|
+
other_elements: list[dict[str, Any]] = []
|
|
41
41
|
|
|
42
42
|
for e in elements:
|
|
43
43
|
if isinstance(e, MarkupElement):
|
|
@@ -54,7 +54,7 @@ class HtmlFormatter(BaseFormatter, IFormatter):
|
|
|
54
54
|
else:
|
|
55
55
|
other_elements.append(e)
|
|
56
56
|
else:
|
|
57
|
-
other_elements.append(e)
|
|
57
|
+
other_elements.append(self._element_to_dict(e))
|
|
58
58
|
|
|
59
59
|
# Format markup elements
|
|
60
60
|
if markup_elements:
|
|
@@ -114,7 +114,7 @@ class HtmlFormatter(BaseFormatter, IFormatter):
|
|
|
114
114
|
elements = analysis_result.get("elements", [])
|
|
115
115
|
|
|
116
116
|
if table_type == "compact":
|
|
117
|
-
formatter = HtmlCompactFormatter()
|
|
117
|
+
formatter: IFormatter = HtmlCompactFormatter()
|
|
118
118
|
return formatter.format(elements)
|
|
119
119
|
elif table_type == "json":
|
|
120
120
|
formatter = HtmlJsonFormatter()
|
|
@@ -130,7 +130,7 @@ class HtmlFormatter(BaseFormatter, IFormatter):
|
|
|
130
130
|
lines.append("")
|
|
131
131
|
|
|
132
132
|
# Group by element class
|
|
133
|
-
element_groups = {}
|
|
133
|
+
element_groups: dict[str, list[MarkupElement]] = {}
|
|
134
134
|
for element in elements:
|
|
135
135
|
element_class = element.element_class or "unknown"
|
|
136
136
|
if element_class not in element_groups:
|
|
@@ -217,7 +217,7 @@ class HtmlFormatter(BaseFormatter, IFormatter):
|
|
|
217
217
|
lines.append("")
|
|
218
218
|
|
|
219
219
|
# Group by element class
|
|
220
|
-
element_groups = {}
|
|
220
|
+
element_groups: dict[str, list[StyleElement]] = {}
|
|
221
221
|
for element in elements:
|
|
222
222
|
element_class = element.element_class or "unknown"
|
|
223
223
|
if element_class not in element_groups:
|
|
@@ -280,30 +280,30 @@ class HtmlFormatter(BaseFormatter, IFormatter):
|
|
|
280
280
|
lines.append("")
|
|
281
281
|
return lines
|
|
282
282
|
|
|
283
|
-
def _dict_to_markup_element(self, data: dict):
|
|
283
|
+
def _dict_to_markup_element(self, data: dict) -> Any:
|
|
284
284
|
"""Convert dictionary to MarkupElement-like object"""
|
|
285
285
|
|
|
286
286
|
# Create a mock MarkupElement-like object
|
|
287
287
|
class MockMarkupElement:
|
|
288
|
-
def __init__(self, data):
|
|
288
|
+
def __init__(self, data: dict[str, Any]) -> None:
|
|
289
289
|
self.name = data.get("name", "unknown")
|
|
290
290
|
self.tag_name = data.get("tag_name", data.get("name", "unknown"))
|
|
291
291
|
self.element_class = data.get("element_class", "unknown")
|
|
292
292
|
self.start_line = data.get("start_line", 0)
|
|
293
293
|
self.end_line = data.get("end_line", 0)
|
|
294
294
|
self.attributes = data.get("attributes", {})
|
|
295
|
-
self.children = []
|
|
295
|
+
self.children: list[MockMarkupElement] = []
|
|
296
296
|
self.parent = None
|
|
297
297
|
self.language = data.get("language", "html")
|
|
298
298
|
|
|
299
299
|
return MockMarkupElement(data)
|
|
300
300
|
|
|
301
|
-
def _dict_to_style_element(self, data: dict):
|
|
301
|
+
def _dict_to_style_element(self, data: dict) -> Any:
|
|
302
302
|
"""Convert dictionary to StyleElement-like object"""
|
|
303
303
|
|
|
304
304
|
# Create a mock StyleElement-like object
|
|
305
305
|
class MockStyleElement:
|
|
306
|
-
def __init__(self, data):
|
|
306
|
+
def __init__(self, data: dict[str, Any]) -> None:
|
|
307
307
|
self.name = data.get("name", "unknown")
|
|
308
308
|
self.selector = data.get("selector", data.get("name", "unknown"))
|
|
309
309
|
self.element_class = data.get("element_class", "unknown")
|
|
@@ -314,6 +314,16 @@ class HtmlFormatter(BaseFormatter, IFormatter):
|
|
|
314
314
|
|
|
315
315
|
return MockStyleElement(data)
|
|
316
316
|
|
|
317
|
+
def _element_to_dict(self, element: CodeElement) -> dict[str, Any]:
|
|
318
|
+
"""Convert generic CodeElement to dictionary"""
|
|
319
|
+
return {
|
|
320
|
+
"name": element.name,
|
|
321
|
+
"type": getattr(element, "element_type", "unknown"),
|
|
322
|
+
"start_line": element.start_line,
|
|
323
|
+
"end_line": element.end_line,
|
|
324
|
+
"language": element.language,
|
|
325
|
+
}
|
|
326
|
+
|
|
317
327
|
|
|
318
328
|
class HtmlJsonFormatter(IFormatter):
|
|
319
329
|
"""JSON formatter specifically for HTML elements"""
|
|
@@ -324,7 +334,7 @@ class HtmlJsonFormatter(IFormatter):
|
|
|
324
334
|
|
|
325
335
|
def format(self, elements: list[CodeElement]) -> str:
|
|
326
336
|
"""Format HTML elements as JSON with hierarchy"""
|
|
327
|
-
result = {
|
|
337
|
+
result: dict[str, Any] = {
|
|
328
338
|
"html_analysis": {
|
|
329
339
|
"total_elements": len(elements),
|
|
330
340
|
"markup_elements": [],
|
|
@@ -468,10 +478,10 @@ class HtmlCompactFormatter(IFormatter):
|
|
|
468
478
|
info += f" .{attributes['class']}"
|
|
469
479
|
elif "selector" in element or element_type in ["rule", "style"]:
|
|
470
480
|
symbol = "🎨"
|
|
471
|
-
info = element.get("selector", name)
|
|
481
|
+
info = str(element.get("selector", name))
|
|
472
482
|
else:
|
|
473
483
|
symbol = "📄"
|
|
474
|
-
info = element_type
|
|
484
|
+
info = str(element_type)
|
|
475
485
|
else:
|
|
476
486
|
symbol = "📄"
|
|
477
487
|
info = getattr(element, "element_type", "unknown")
|
|
@@ -15,15 +15,15 @@ from .base_formatter import BaseTableFormatter
|
|
|
15
15
|
class JavaScriptTableFormatter(BaseTableFormatter):
|
|
16
16
|
"""Table formatter specialized for JavaScript"""
|
|
17
17
|
|
|
18
|
-
def format(self, data: dict[str, Any], format_type: str =
|
|
18
|
+
def format(self, data: dict[str, Any] | None, format_type: str = "full") -> str:
|
|
19
19
|
"""Format data using the configured format type"""
|
|
20
|
-
# Handle None data
|
|
20
|
+
# Handle None data gracefully
|
|
21
21
|
if data is None:
|
|
22
|
-
|
|
22
|
+
data = {}
|
|
23
23
|
|
|
24
24
|
# Ensure data is a dictionary
|
|
25
25
|
if not isinstance(data, dict):
|
|
26
|
-
|
|
26
|
+
raise TypeError(f"Expected dict, got {type(data)}")
|
|
27
27
|
|
|
28
28
|
if format_type:
|
|
29
29
|
# Check for supported format types
|
|
@@ -47,11 +47,8 @@ class JavaScriptTableFormatter(BaseTableFormatter):
|
|
|
47
47
|
|
|
48
48
|
def _format_full_table(self, data: dict[str, Any]) -> str:
|
|
49
49
|
"""Full table format for JavaScript"""
|
|
50
|
-
if data is None:
|
|
51
|
-
return "# No data available\n"
|
|
52
|
-
|
|
53
50
|
if not isinstance(data, dict):
|
|
54
|
-
|
|
51
|
+
raise TypeError(f"Expected dict, got {type(data)}")
|
|
55
52
|
|
|
56
53
|
lines = []
|
|
57
54
|
|
|
@@ -243,16 +240,26 @@ class JavaScriptTableFormatter(BaseTableFormatter):
|
|
|
243
240
|
lines.append("| Export | Type | Name | Default |")
|
|
244
241
|
lines.append("|--------|------|------|---------|")
|
|
245
242
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
243
|
+
# Handle malformed exports data
|
|
244
|
+
if isinstance(exports, list):
|
|
245
|
+
for export in exports:
|
|
246
|
+
try:
|
|
247
|
+
export_type = self._get_export_type(export)
|
|
248
|
+
if isinstance(export, dict):
|
|
249
|
+
name = str(export.get("name", ""))
|
|
250
|
+
is_default = "✓" if export.get("is_default", False) else "-"
|
|
251
|
+
else:
|
|
252
|
+
name = str(export)
|
|
253
|
+
is_default = "-"
|
|
254
|
+
export_type = "unknown"
|
|
255
|
+
|
|
256
|
+
lines.append(f"| {export_type} | {name} | {is_default} |")
|
|
257
|
+
except (TypeError, AttributeError):
|
|
258
|
+
# Handle malformed export data gracefully
|
|
259
|
+
lines.append(f"| unknown | {str(export)} | - |")
|
|
260
|
+
else:
|
|
261
|
+
# Handle case where exports is not a list (malformed data)
|
|
262
|
+
lines.append(f"| unknown | {str(exports)} | - |")
|
|
256
263
|
lines.append("")
|
|
257
264
|
|
|
258
265
|
# Trim trailing blank lines
|
|
@@ -526,7 +533,7 @@ class JavaScriptTableFormatter(BaseTableFormatter):
|
|
|
526
533
|
else:
|
|
527
534
|
return "unknown"
|
|
528
535
|
|
|
529
|
-
def _get_export_type(self, export:
|
|
536
|
+
def _get_export_type(self, export: Any) -> str:
|
|
530
537
|
"""Get export type"""
|
|
531
538
|
if not isinstance(export, dict):
|
|
532
539
|
return "unknown"
|
|
@@ -551,10 +558,10 @@ class JavaScriptTableFormatter(BaseTableFormatter):
|
|
|
551
558
|
def _get_class_info(self, cls: dict[str, Any]) -> str:
|
|
552
559
|
"""Get class information as formatted string"""
|
|
553
560
|
if cls is None:
|
|
554
|
-
|
|
561
|
+
raise TypeError("Cannot format None data")
|
|
555
562
|
|
|
556
563
|
if not isinstance(cls, dict):
|
|
557
|
-
|
|
564
|
+
raise TypeError(f"Expected dict, got {type(cls)}")
|
|
558
565
|
|
|
559
566
|
name = str(cls.get("name", "Unknown"))
|
|
560
567
|
methods = cls.get("methods", [])
|
|
@@ -32,8 +32,7 @@ class LanguageFormatterFactory:
|
|
|
32
32
|
formatter_class = cls._formatters.get(language.lower())
|
|
33
33
|
|
|
34
34
|
if formatter_class is None:
|
|
35
|
-
|
|
36
|
-
return None
|
|
35
|
+
raise ValueError(f"Unsupported language: {language}")
|
|
37
36
|
|
|
38
37
|
return formatter_class()
|
|
39
38
|
|
|
@@ -74,7 +73,7 @@ class LanguageFormatterFactory:
|
|
|
74
73
|
return language.lower() in cls._formatters
|
|
75
74
|
|
|
76
75
|
|
|
77
|
-
def create_language_formatter(language: str) -> BaseFormatter:
|
|
76
|
+
def create_language_formatter(language: str) -> BaseFormatter | None:
|
|
78
77
|
"""
|
|
79
78
|
Create language formatter (function for compatibility)
|
|
80
79
|
|
|
@@ -84,4 +83,8 @@ def create_language_formatter(language: str) -> BaseFormatter:
|
|
|
84
83
|
Returns:
|
|
85
84
|
Language formatter or None if not supported
|
|
86
85
|
"""
|
|
87
|
-
|
|
86
|
+
try:
|
|
87
|
+
return LanguageFormatterFactory.create_formatter(language)
|
|
88
|
+
except ValueError:
|
|
89
|
+
# Return None for unsupported languages instead of raising exception
|
|
90
|
+
return None
|
|
@@ -14,8 +14,7 @@ from .base_formatter import BaseFormatter
|
|
|
14
14
|
class MarkdownFormatter(BaseFormatter):
|
|
15
15
|
"""Formatter specialized for Markdown documents"""
|
|
16
16
|
|
|
17
|
-
def __init__(self):
|
|
18
|
-
super().__init__()
|
|
17
|
+
def __init__(self) -> None:
|
|
19
18
|
self.language = "markdown"
|
|
20
19
|
|
|
21
20
|
def format_summary(self, analysis_result: dict[str, Any]) -> str:
|
|
@@ -624,8 +623,9 @@ class MarkdownFormatter(BaseFormatter):
|
|
|
624
623
|
return counts
|
|
625
624
|
|
|
626
625
|
try:
|
|
627
|
-
|
|
628
|
-
|
|
626
|
+
from ..encoding_utils import read_file_safe
|
|
627
|
+
|
|
628
|
+
content, _ = read_file_safe(file_path)
|
|
629
629
|
except Exception:
|
|
630
630
|
return counts
|
|
631
631
|
|
|
@@ -30,10 +30,10 @@ class PythonTableFormatter(BaseTableFormatter):
|
|
|
30
30
|
def _format_full_table(self, data: dict[str, Any]) -> str:
|
|
31
31
|
"""Full table format for Python"""
|
|
32
32
|
if data is None:
|
|
33
|
-
|
|
33
|
+
raise TypeError("Cannot format None data")
|
|
34
34
|
|
|
35
35
|
if not isinstance(data, dict):
|
|
36
|
-
|
|
36
|
+
raise TypeError(f"Expected dict, got {type(data)}")
|
|
37
37
|
|
|
38
38
|
lines = []
|
|
39
39
|
|
|
@@ -300,7 +300,7 @@ class PythonTableFormatter(BaseTableFormatter):
|
|
|
300
300
|
def _create_compact_signature(self, method: dict[str, Any]) -> str:
|
|
301
301
|
"""Create compact method signature for Python"""
|
|
302
302
|
if method is None or not isinstance(method, dict):
|
|
303
|
-
|
|
303
|
+
raise TypeError(f"Expected dict, got {type(method)}")
|
|
304
304
|
|
|
305
305
|
params = method.get("parameters", [])
|
|
306
306
|
param_types = []
|
|
@@ -387,7 +387,7 @@ class PythonTableFormatter(BaseTableFormatter):
|
|
|
387
387
|
|
|
388
388
|
# Single line docstring
|
|
389
389
|
if stripped.count(quote_type) >= 2:
|
|
390
|
-
return stripped.replace(quote_type, "").strip()
|
|
390
|
+
return str(stripped.replace(quote_type, "").strip())
|
|
391
391
|
|
|
392
392
|
# Multi-line docstring
|
|
393
393
|
docstring_lines = [stripped.replace(quote_type, "")]
|
|
@@ -462,7 +462,7 @@ class TypeScriptTableFormatter(BaseTableFormatter):
|
|
|
462
462
|
elif element_type == "import":
|
|
463
463
|
return "Import"
|
|
464
464
|
else:
|
|
465
|
-
return element_type.title()
|
|
465
|
+
return str(element_type.title())
|
|
466
466
|
|
|
467
467
|
def _format_element_details(self, element: dict[str, Any]) -> str:
|
|
468
468
|
"""Format TypeScript-specific element details"""
|
|
@@ -32,8 +32,10 @@ def handle_mcp_resource_request(uri: str) -> dict[str, Any]:
|
|
|
32
32
|
def read_file_safe(file_path: str) -> str:
|
|
33
33
|
"""Read file safely for MCP resource requests."""
|
|
34
34
|
try:
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
from ..encoding_utils import read_file_safe
|
|
36
|
+
|
|
37
|
+
content, _ = read_file_safe(file_path)
|
|
38
|
+
return content
|
|
37
39
|
except Exception as e:
|
|
38
40
|
raise FileNotFoundError(f"Could not read file {file_path}: {e}") from e
|
|
39
41
|
|
|
@@ -25,23 +25,23 @@ except ImportError:
|
|
|
25
25
|
MCP_AVAILABLE = False
|
|
26
26
|
|
|
27
27
|
# Fallback types for development without MCP
|
|
28
|
-
class Server:
|
|
28
|
+
class Server: # type: ignore
|
|
29
29
|
pass
|
|
30
30
|
|
|
31
|
-
class InitializationOptions:
|
|
31
|
+
class InitializationOptions: # type: ignore
|
|
32
32
|
def __init__(self, **kwargs: Any) -> None:
|
|
33
33
|
pass
|
|
34
34
|
|
|
35
|
-
class Tool:
|
|
35
|
+
class Tool: # type: ignore
|
|
36
36
|
pass
|
|
37
37
|
|
|
38
|
-
class Resource:
|
|
38
|
+
class Resource: # type: ignore
|
|
39
39
|
pass
|
|
40
40
|
|
|
41
|
-
class TextContent:
|
|
41
|
+
class TextContent: # type: ignore
|
|
42
42
|
pass
|
|
43
43
|
|
|
44
|
-
def stdio_server() -> None:
|
|
44
|
+
def stdio_server() -> None: # type: ignore[misc]
|
|
45
45
|
pass
|
|
46
46
|
|
|
47
47
|
|
|
@@ -337,13 +337,13 @@ class TreeSitterAnalyzerMCPServer:
|
|
|
337
337
|
"""List available resources."""
|
|
338
338
|
return [
|
|
339
339
|
Resource(
|
|
340
|
-
uri="code://file/{file_path}", # type: ignore
|
|
340
|
+
uri="code://file/{file_path}", # type: ignore[arg-type]
|
|
341
341
|
name="Code File Analysis",
|
|
342
342
|
description="Access to code file content and analysis",
|
|
343
343
|
mimeType="application/json",
|
|
344
344
|
),
|
|
345
345
|
Resource(
|
|
346
|
-
uri="code://stats/{stats_type}", # type: ignore
|
|
346
|
+
uri="code://stats/{stats_type}", # type: ignore[arg-type]
|
|
347
347
|
name="Project Statistics",
|
|
348
348
|
description="Access to project statistics and analysis data",
|
|
349
349
|
mimeType="application/json",
|
|
@@ -389,7 +389,7 @@ class TreeSitterAnalyzerMCPServer:
|
|
|
389
389
|
|
|
390
390
|
self.server = server
|
|
391
391
|
log_info("MCP server created successfully")
|
|
392
|
-
return server # type: ignore
|
|
392
|
+
return server # type: ignore[no-any-return]
|
|
393
393
|
|
|
394
394
|
async def run(self) -> None:
|
|
395
395
|
"""Run the MCP server."""
|
|
@@ -399,7 +399,7 @@ class TreeSitterAnalyzerMCPServer:
|
|
|
399
399
|
options = InitializationOptions(
|
|
400
400
|
server_name=self.name,
|
|
401
401
|
server_version=self.version,
|
|
402
|
-
capabilities={"tools": {}, "resources": {}}, # type: ignore
|
|
402
|
+
capabilities={"tools": {}, "resources": {}}, # type: ignore[arg-type]
|
|
403
403
|
)
|
|
404
404
|
|
|
405
405
|
log_info(f"Starting MCP server: {self.name} v{self.version}")
|