tree-sitter-analyzer 1.1.3__py3-none-any.whl → 1.2.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.
- tree_sitter_analyzer/cli/commands/advanced_command.py +145 -6
- tree_sitter_analyzer/cli/commands/structure_command.py +23 -5
- tree_sitter_analyzer/cli/commands/summary_command.py +19 -4
- tree_sitter_analyzer/cli/commands/table_command.py +14 -6
- tree_sitter_analyzer/constants.py +68 -0
- tree_sitter_analyzer/core/analysis_engine.py +0 -5
- tree_sitter_analyzer/core/engine.py +0 -12
- tree_sitter_analyzer/interfaces/cli_adapter.py +27 -12
- tree_sitter_analyzer/interfaces/mcp_adapter.py +31 -15
- tree_sitter_analyzer/mcp/server.py +187 -35
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +42 -19
- tree_sitter_analyzer/mcp/tools/base_tool.py +90 -5
- tree_sitter_analyzer/mcp/tools/query_tool.py +73 -6
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +3 -6
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +37 -11
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +102 -22
- tree_sitter_analyzer/models.py +138 -43
- tree_sitter_analyzer/security/boundary_manager.py +29 -9
- tree_sitter_analyzer/security/validator.py +16 -3
- {tree_sitter_analyzer-1.1.3.dist-info → tree_sitter_analyzer-1.2.2.dist-info}/METADATA +298 -127
- {tree_sitter_analyzer-1.1.3.dist-info → tree_sitter_analyzer-1.2.2.dist-info}/RECORD +23 -22
- {tree_sitter_analyzer-1.1.3.dist-info → tree_sitter_analyzer-1.2.2.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-1.1.3.dist-info → tree_sitter_analyzer-1.2.2.dist-info}/entry_points.txt +0 -0
|
@@ -7,6 +7,13 @@ Handles advanced analysis functionality.
|
|
|
7
7
|
|
|
8
8
|
from typing import TYPE_CHECKING
|
|
9
9
|
|
|
10
|
+
from ...constants import (
|
|
11
|
+
ELEMENT_TYPE_CLASS,
|
|
12
|
+
ELEMENT_TYPE_FUNCTION,
|
|
13
|
+
ELEMENT_TYPE_IMPORT,
|
|
14
|
+
ELEMENT_TYPE_VARIABLE,
|
|
15
|
+
get_element_type,
|
|
16
|
+
)
|
|
10
17
|
from ...output_manager import output_data, output_json, output_section
|
|
11
18
|
from .base_command import BaseCommand
|
|
12
19
|
|
|
@@ -29,6 +36,111 @@ class AdvancedCommand(BaseCommand):
|
|
|
29
36
|
|
|
30
37
|
return 0
|
|
31
38
|
|
|
39
|
+
def _calculate_file_metrics(self, file_path: str, language: str) -> dict[str, int]:
|
|
40
|
+
"""
|
|
41
|
+
Calculate accurate file metrics including line counts.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
file_path: Path to the file to analyze
|
|
45
|
+
language: Programming language
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Dictionary containing file metrics
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
with open(file_path, encoding="utf-8") as f:
|
|
52
|
+
content = f.read()
|
|
53
|
+
|
|
54
|
+
lines = content.split("\n")
|
|
55
|
+
total_lines = len(lines)
|
|
56
|
+
|
|
57
|
+
# Remove empty line at the end if file ends with newline
|
|
58
|
+
if lines and not lines[-1]:
|
|
59
|
+
total_lines -= 1
|
|
60
|
+
|
|
61
|
+
# Count different types of lines
|
|
62
|
+
code_lines = 0
|
|
63
|
+
comment_lines = 0
|
|
64
|
+
blank_lines = 0
|
|
65
|
+
in_multiline_comment = False
|
|
66
|
+
|
|
67
|
+
for line in lines:
|
|
68
|
+
stripped = line.strip()
|
|
69
|
+
|
|
70
|
+
# Check for blank lines first
|
|
71
|
+
if not stripped:
|
|
72
|
+
blank_lines += 1
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
# Check if we're in a multi-line comment
|
|
76
|
+
if in_multiline_comment:
|
|
77
|
+
comment_lines += 1
|
|
78
|
+
# Check if this line ends the multi-line comment
|
|
79
|
+
if "*/" in stripped:
|
|
80
|
+
in_multiline_comment = False
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
# Check for multi-line comment start
|
|
84
|
+
if stripped.startswith("/**") or stripped.startswith("/*"):
|
|
85
|
+
comment_lines += 1
|
|
86
|
+
# Check if this line also ends the comment
|
|
87
|
+
if "*/" not in stripped:
|
|
88
|
+
in_multiline_comment = True
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
# Check for single-line comments
|
|
92
|
+
if stripped.startswith("//"):
|
|
93
|
+
comment_lines += 1
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
# Check for JavaDoc continuation lines (lines starting with * but not */)
|
|
97
|
+
if stripped.startswith("*") and not stripped.startswith("*/"):
|
|
98
|
+
comment_lines += 1
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
# Check for other comment types based on language
|
|
102
|
+
if language == "python" and stripped.startswith("#"):
|
|
103
|
+
comment_lines += 1
|
|
104
|
+
continue
|
|
105
|
+
elif language == "sql" and stripped.startswith("--"):
|
|
106
|
+
comment_lines += 1
|
|
107
|
+
continue
|
|
108
|
+
elif language in ["html", "xml"] and stripped.startswith("<!--"):
|
|
109
|
+
comment_lines += 1
|
|
110
|
+
if "-->" not in stripped:
|
|
111
|
+
in_multiline_comment = True
|
|
112
|
+
continue
|
|
113
|
+
elif in_multiline_comment and "-->" in stripped:
|
|
114
|
+
comment_lines += 1
|
|
115
|
+
in_multiline_comment = False
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
# If not a comment, it's code
|
|
119
|
+
code_lines += 1
|
|
120
|
+
|
|
121
|
+
# Ensure the sum equals total_lines (handle any rounding errors)
|
|
122
|
+
calculated_total = code_lines + comment_lines + blank_lines
|
|
123
|
+
if calculated_total != total_lines:
|
|
124
|
+
# Adjust blank_lines to match total (since it's most likely to be off by 1)
|
|
125
|
+
blank_lines = total_lines - code_lines - comment_lines
|
|
126
|
+
# Ensure blank_lines is not negative
|
|
127
|
+
blank_lines = max(0, blank_lines)
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
"total_lines": total_lines,
|
|
131
|
+
"code_lines": code_lines,
|
|
132
|
+
"comment_lines": comment_lines,
|
|
133
|
+
"blank_lines": blank_lines,
|
|
134
|
+
}
|
|
135
|
+
except Exception:
|
|
136
|
+
# Fallback to basic counting if file read fails
|
|
137
|
+
return {
|
|
138
|
+
"total_lines": 0,
|
|
139
|
+
"code_lines": 0,
|
|
140
|
+
"comment_lines": 0,
|
|
141
|
+
"blank_lines": 0,
|
|
142
|
+
}
|
|
143
|
+
|
|
32
144
|
def _output_statistics(self, analysis_result: "AnalysisResult") -> None:
|
|
33
145
|
"""Output statistics only."""
|
|
34
146
|
stats = {
|
|
@@ -57,7 +169,7 @@ class AdvancedCommand(BaseCommand):
|
|
|
57
169
|
"elements": [
|
|
58
170
|
{
|
|
59
171
|
"name": getattr(element, "name", str(element)),
|
|
60
|
-
"type":
|
|
172
|
+
"type": get_element_type(element),
|
|
61
173
|
"start_line": getattr(element, "start_line", 0),
|
|
62
174
|
"end_line": getattr(element, "end_line", 0),
|
|
63
175
|
}
|
|
@@ -77,12 +189,39 @@ class AdvancedCommand(BaseCommand):
|
|
|
77
189
|
output_data(f"Lines: {analysis_result.line_count}")
|
|
78
190
|
|
|
79
191
|
element_counts: dict[str, int] = {}
|
|
192
|
+
complexity_scores: list[int] = []
|
|
193
|
+
|
|
80
194
|
for element in analysis_result.elements:
|
|
81
|
-
element_type =
|
|
195
|
+
element_type = get_element_type(element)
|
|
82
196
|
element_counts[element_type] = element_counts.get(element_type, 0) + 1
|
|
83
197
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
198
|
+
# Collect complexity scores for methods
|
|
199
|
+
if element_type == ELEMENT_TYPE_FUNCTION:
|
|
200
|
+
complexity = getattr(element, "complexity_score", 1)
|
|
201
|
+
complexity_scores.append(complexity)
|
|
202
|
+
|
|
203
|
+
# Calculate accurate file metrics
|
|
204
|
+
file_metrics = self._calculate_file_metrics(
|
|
205
|
+
analysis_result.file_path, analysis_result.language
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Calculate complexity statistics
|
|
209
|
+
total_complexity = sum(complexity_scores) if complexity_scores else 0
|
|
210
|
+
avg_complexity = (
|
|
211
|
+
total_complexity / len(complexity_scores) if complexity_scores else 0
|
|
212
|
+
)
|
|
213
|
+
max_complexity = max(complexity_scores) if complexity_scores else 0
|
|
214
|
+
|
|
215
|
+
output_data(f"Classes: {element_counts.get(ELEMENT_TYPE_CLASS, 0)}")
|
|
216
|
+
output_data(f"Methods: {element_counts.get(ELEMENT_TYPE_FUNCTION, 0)}")
|
|
217
|
+
output_data(f"Fields: {element_counts.get(ELEMENT_TYPE_VARIABLE, 0)}")
|
|
218
|
+
output_data(f"Imports: {element_counts.get(ELEMENT_TYPE_IMPORT, 0)}")
|
|
88
219
|
output_data("Annotations: 0")
|
|
220
|
+
|
|
221
|
+
# Add detailed metrics using accurate calculations
|
|
222
|
+
output_data(f"Code Lines: {file_metrics['code_lines']}")
|
|
223
|
+
output_data(f"Comment Lines: {file_metrics['comment_lines']}")
|
|
224
|
+
output_data(f"Blank Lines: {file_metrics['blank_lines']}")
|
|
225
|
+
output_data(f"Total Complexity: {total_complexity}")
|
|
226
|
+
output_data(f"Average Complexity: {avg_complexity:.2f}")
|
|
227
|
+
output_data(f"Max Complexity: {max_complexity}")
|
|
@@ -7,6 +7,14 @@ Handles structure analysis functionality with appropriate Japanese output.
|
|
|
7
7
|
|
|
8
8
|
from typing import TYPE_CHECKING
|
|
9
9
|
|
|
10
|
+
from ...constants import (
|
|
11
|
+
ELEMENT_TYPE_CLASS,
|
|
12
|
+
ELEMENT_TYPE_FUNCTION,
|
|
13
|
+
ELEMENT_TYPE_IMPORT,
|
|
14
|
+
ELEMENT_TYPE_PACKAGE,
|
|
15
|
+
ELEMENT_TYPE_VARIABLE,
|
|
16
|
+
is_element_of_type,
|
|
17
|
+
)
|
|
10
18
|
from ...output_manager import output_data, output_json, output_section
|
|
11
19
|
from .base_command import BaseCommand
|
|
12
20
|
|
|
@@ -43,19 +51,29 @@ class StructureCommand(BaseCommand):
|
|
|
43
51
|
|
|
44
52
|
# Extract elements by type
|
|
45
53
|
classes = [
|
|
46
|
-
e
|
|
54
|
+
e
|
|
55
|
+
for e in analysis_result.elements
|
|
56
|
+
if is_element_of_type(e, ELEMENT_TYPE_CLASS)
|
|
47
57
|
]
|
|
48
58
|
methods = [
|
|
49
|
-
e
|
|
59
|
+
e
|
|
60
|
+
for e in analysis_result.elements
|
|
61
|
+
if is_element_of_type(e, ELEMENT_TYPE_FUNCTION)
|
|
50
62
|
]
|
|
51
63
|
fields = [
|
|
52
|
-
e
|
|
64
|
+
e
|
|
65
|
+
for e in analysis_result.elements
|
|
66
|
+
if is_element_of_type(e, ELEMENT_TYPE_VARIABLE)
|
|
53
67
|
]
|
|
54
68
|
imports = [
|
|
55
|
-
e
|
|
69
|
+
e
|
|
70
|
+
for e in analysis_result.elements
|
|
71
|
+
if is_element_of_type(e, ELEMENT_TYPE_IMPORT)
|
|
56
72
|
]
|
|
57
73
|
packages = [
|
|
58
|
-
e
|
|
74
|
+
e
|
|
75
|
+
for e in analysis_result.elements
|
|
76
|
+
if is_element_of_type(e, ELEMENT_TYPE_PACKAGE)
|
|
59
77
|
]
|
|
60
78
|
|
|
61
79
|
return {
|
|
@@ -7,6 +7,13 @@ Handles summary functionality with specified element types.
|
|
|
7
7
|
|
|
8
8
|
from typing import TYPE_CHECKING, Any
|
|
9
9
|
|
|
10
|
+
from ...constants import (
|
|
11
|
+
ELEMENT_TYPE_CLASS,
|
|
12
|
+
ELEMENT_TYPE_FUNCTION,
|
|
13
|
+
ELEMENT_TYPE_IMPORT,
|
|
14
|
+
ELEMENT_TYPE_VARIABLE,
|
|
15
|
+
is_element_of_type,
|
|
16
|
+
)
|
|
10
17
|
from ...output_manager import output_data, output_json, output_section
|
|
11
18
|
from .base_command import BaseCommand
|
|
12
19
|
|
|
@@ -38,16 +45,24 @@ class SummaryCommand(BaseCommand):
|
|
|
38
45
|
|
|
39
46
|
# Extract elements by type
|
|
40
47
|
classes = [
|
|
41
|
-
e
|
|
48
|
+
e
|
|
49
|
+
for e in analysis_result.elements
|
|
50
|
+
if is_element_of_type(e, ELEMENT_TYPE_CLASS)
|
|
42
51
|
]
|
|
43
52
|
methods = [
|
|
44
|
-
e
|
|
53
|
+
e
|
|
54
|
+
for e in analysis_result.elements
|
|
55
|
+
if is_element_of_type(e, ELEMENT_TYPE_FUNCTION)
|
|
45
56
|
]
|
|
46
57
|
fields = [
|
|
47
|
-
e
|
|
58
|
+
e
|
|
59
|
+
for e in analysis_result.elements
|
|
60
|
+
if is_element_of_type(e, ELEMENT_TYPE_VARIABLE)
|
|
48
61
|
]
|
|
49
62
|
imports = [
|
|
50
|
-
e
|
|
63
|
+
e
|
|
64
|
+
for e in analysis_result.elements
|
|
65
|
+
if is_element_of_type(e, ELEMENT_TYPE_IMPORT)
|
|
51
66
|
]
|
|
52
67
|
|
|
53
68
|
summary_data: dict[str, Any] = {
|
|
@@ -8,6 +8,14 @@ Handles table format output generation.
|
|
|
8
8
|
import sys
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
|
+
from ...constants import (
|
|
12
|
+
ELEMENT_TYPE_CLASS,
|
|
13
|
+
ELEMENT_TYPE_FUNCTION,
|
|
14
|
+
ELEMENT_TYPE_IMPORT,
|
|
15
|
+
ELEMENT_TYPE_PACKAGE,
|
|
16
|
+
ELEMENT_TYPE_VARIABLE,
|
|
17
|
+
get_element_type,
|
|
18
|
+
)
|
|
11
19
|
from ...output_manager import output_error
|
|
12
20
|
from ...table_formatter import create_table_formatter
|
|
13
21
|
from .base_command import BaseCommand
|
|
@@ -58,18 +66,18 @@ class TableCommand(BaseCommand):
|
|
|
58
66
|
# Process each element
|
|
59
67
|
for i, element in enumerate(analysis_result.elements):
|
|
60
68
|
try:
|
|
61
|
-
element_type =
|
|
69
|
+
element_type = get_element_type(element)
|
|
62
70
|
element_name = getattr(element, "name", None)
|
|
63
71
|
|
|
64
|
-
if element_type ==
|
|
72
|
+
if element_type == ELEMENT_TYPE_PACKAGE:
|
|
65
73
|
package_name = str(element_name)
|
|
66
|
-
elif element_type ==
|
|
74
|
+
elif element_type == ELEMENT_TYPE_CLASS:
|
|
67
75
|
classes.append(self._convert_class_element(element, i))
|
|
68
|
-
elif element_type ==
|
|
76
|
+
elif element_type == ELEMENT_TYPE_FUNCTION:
|
|
69
77
|
methods.append(self._convert_function_element(element, language))
|
|
70
|
-
elif element_type ==
|
|
78
|
+
elif element_type == ELEMENT_TYPE_VARIABLE:
|
|
71
79
|
fields.append(self._convert_variable_element(element, language))
|
|
72
|
-
elif element_type ==
|
|
80
|
+
elif element_type == ELEMENT_TYPE_IMPORT:
|
|
73
81
|
imports.append(self._convert_import_element(element))
|
|
74
82
|
|
|
75
83
|
except Exception as element_error:
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Constants for tree-sitter-analyzer
|
|
4
|
+
|
|
5
|
+
This module defines constants used throughout the project to ensure consistency.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Element types for unified element management system
|
|
9
|
+
ELEMENT_TYPE_CLASS = "class"
|
|
10
|
+
ELEMENT_TYPE_FUNCTION = "function"
|
|
11
|
+
ELEMENT_TYPE_VARIABLE = "variable"
|
|
12
|
+
ELEMENT_TYPE_IMPORT = "import"
|
|
13
|
+
ELEMENT_TYPE_PACKAGE = "package"
|
|
14
|
+
ELEMENT_TYPE_ANNOTATION = "annotation"
|
|
15
|
+
|
|
16
|
+
# Element type mapping for backward compatibility
|
|
17
|
+
ELEMENT_TYPE_MAPPING = {
|
|
18
|
+
"Class": ELEMENT_TYPE_CLASS,
|
|
19
|
+
"Function": ELEMENT_TYPE_FUNCTION,
|
|
20
|
+
"Variable": ELEMENT_TYPE_VARIABLE,
|
|
21
|
+
"Import": ELEMENT_TYPE_IMPORT,
|
|
22
|
+
"Package": ELEMENT_TYPE_PACKAGE,
|
|
23
|
+
"Annotation": ELEMENT_TYPE_ANNOTATION,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Legacy class name to element type mapping
|
|
27
|
+
LEGACY_CLASS_MAPPING = {
|
|
28
|
+
"Class": ELEMENT_TYPE_CLASS,
|
|
29
|
+
"Function": ELEMENT_TYPE_FUNCTION,
|
|
30
|
+
"Variable": ELEMENT_TYPE_VARIABLE,
|
|
31
|
+
"Import": ELEMENT_TYPE_IMPORT,
|
|
32
|
+
"Package": ELEMENT_TYPE_PACKAGE,
|
|
33
|
+
"Annotation": ELEMENT_TYPE_ANNOTATION,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_element_type(element) -> str:
|
|
38
|
+
"""
|
|
39
|
+
Get the element type from an element object.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
element: Element object with element_type attribute or __class__.__name__
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Standardized element type string
|
|
46
|
+
"""
|
|
47
|
+
if hasattr(element, "element_type"):
|
|
48
|
+
return element.element_type
|
|
49
|
+
|
|
50
|
+
if hasattr(element, "__class__") and hasattr(element.__class__, "__name__"):
|
|
51
|
+
class_name = element.__class__.__name__
|
|
52
|
+
return LEGACY_CLASS_MAPPING.get(class_name, "unknown")
|
|
53
|
+
|
|
54
|
+
return "unknown"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def is_element_of_type(element, element_type: str) -> bool:
|
|
58
|
+
"""
|
|
59
|
+
Check if an element is of a specific type.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
element: Element object to check
|
|
63
|
+
element_type: Expected element type
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
True if element is of the specified type
|
|
67
|
+
"""
|
|
68
|
+
return get_element_type(element) == element_type
|
|
@@ -560,11 +560,6 @@ class MockLanguagePlugin:
|
|
|
560
560
|
source_code="// Mock source code", # 新しいアーキテクチャ用
|
|
561
561
|
language=self.language, # 言語を設定
|
|
562
562
|
package=None,
|
|
563
|
-
imports=[],
|
|
564
|
-
classes=[],
|
|
565
|
-
methods=[],
|
|
566
|
-
fields=[],
|
|
567
|
-
annotations=[],
|
|
568
563
|
analysis_time=0.1,
|
|
569
564
|
success=True,
|
|
570
565
|
error_message=None,
|
|
@@ -200,12 +200,6 @@ class AnalysisEngine:
|
|
|
200
200
|
success=True,
|
|
201
201
|
error_message=None,
|
|
202
202
|
analysis_time=0.0,
|
|
203
|
-
# Initialize empty collections for now
|
|
204
|
-
classes=[],
|
|
205
|
-
methods=[],
|
|
206
|
-
fields=[],
|
|
207
|
-
imports=[],
|
|
208
|
-
annotations=[],
|
|
209
203
|
package=None,
|
|
210
204
|
)
|
|
211
205
|
|
|
@@ -458,12 +452,6 @@ class AnalysisEngine:
|
|
|
458
452
|
source_code="",
|
|
459
453
|
error_message=error,
|
|
460
454
|
analysis_time=0.0,
|
|
461
|
-
# Initialize empty collections
|
|
462
|
-
classes=[],
|
|
463
|
-
methods=[],
|
|
464
|
-
fields=[],
|
|
465
|
-
imports=[],
|
|
466
|
-
annotations=[],
|
|
467
455
|
package=None,
|
|
468
456
|
)
|
|
469
457
|
|
|
@@ -150,32 +150,51 @@ class CLIAdapter:
|
|
|
150
150
|
result = self.analyze_file(file_path, **kwargs)
|
|
151
151
|
|
|
152
152
|
# 構造情報を辞書形式に変換
|
|
153
|
+
# Use unified elements system
|
|
154
|
+
from ..constants import (
|
|
155
|
+
ELEMENT_TYPE_ANNOTATION,
|
|
156
|
+
ELEMENT_TYPE_CLASS,
|
|
157
|
+
ELEMENT_TYPE_FUNCTION,
|
|
158
|
+
ELEMENT_TYPE_IMPORT,
|
|
159
|
+
ELEMENT_TYPE_VARIABLE,
|
|
160
|
+
is_element_of_type,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
elements = result.elements or []
|
|
164
|
+
|
|
165
|
+
# Extract elements by type from unified list
|
|
166
|
+
imports = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_IMPORT)]
|
|
167
|
+
classes = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_CLASS)]
|
|
168
|
+
methods = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_FUNCTION)]
|
|
169
|
+
fields = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_VARIABLE)]
|
|
170
|
+
annotations = [
|
|
171
|
+
e for e in elements if is_element_of_type(e, ELEMENT_TYPE_ANNOTATION)
|
|
172
|
+
]
|
|
173
|
+
|
|
153
174
|
return {
|
|
154
175
|
"file_path": result.file_path,
|
|
155
176
|
"language": result.language,
|
|
156
177
|
"package": result.package,
|
|
157
178
|
"imports": [
|
|
158
|
-
{"name": imp.name, "type": str(type(imp).__name__)}
|
|
159
|
-
for imp in result.imports
|
|
179
|
+
{"name": imp.name, "type": str(type(imp).__name__)} for imp in imports
|
|
160
180
|
],
|
|
161
181
|
"classes": [
|
|
162
|
-
{"name": cls.name, "type": str(type(cls).__name__)}
|
|
163
|
-
for cls in result.classes
|
|
182
|
+
{"name": cls.name, "type": str(type(cls).__name__)} for cls in classes
|
|
164
183
|
],
|
|
165
184
|
"methods": [
|
|
166
185
|
{"name": method.name, "type": str(type(method).__name__)}
|
|
167
|
-
for method in
|
|
186
|
+
for method in methods
|
|
168
187
|
],
|
|
169
188
|
"fields": [
|
|
170
189
|
{"name": field.name, "type": str(type(field).__name__)}
|
|
171
|
-
for field in
|
|
190
|
+
for field in fields
|
|
172
191
|
],
|
|
173
192
|
"annotations": [
|
|
174
193
|
{
|
|
175
194
|
"name": getattr(ann, "name", str(ann)),
|
|
176
195
|
"type": str(type(ann).__name__),
|
|
177
196
|
}
|
|
178
|
-
for ann in
|
|
197
|
+
for ann in annotations
|
|
179
198
|
],
|
|
180
199
|
"analysis_time": result.analysis_time,
|
|
181
200
|
"elements": [
|
|
@@ -215,11 +234,7 @@ class CLIAdapter:
|
|
|
215
234
|
error_result = AnalysisResult(
|
|
216
235
|
file_path=file_path,
|
|
217
236
|
package=None,
|
|
218
|
-
|
|
219
|
-
classes=[],
|
|
220
|
-
methods=[],
|
|
221
|
-
fields=[],
|
|
222
|
-
annotations=[],
|
|
237
|
+
elements=[],
|
|
223
238
|
analysis_time=0.0,
|
|
224
239
|
success=False,
|
|
225
240
|
error_message=str(e),
|
|
@@ -67,32 +67,53 @@ class MCPAdapter:
|
|
|
67
67
|
) -> dict[str, Any]:
|
|
68
68
|
"""Get file structure asynchronously."""
|
|
69
69
|
result = await self.analyze_file_async(file_path, **kwargs)
|
|
70
|
+
# Use unified elements system
|
|
71
|
+
from ..constants import (
|
|
72
|
+
ELEMENT_TYPE_ANNOTATION,
|
|
73
|
+
ELEMENT_TYPE_CLASS,
|
|
74
|
+
ELEMENT_TYPE_FUNCTION,
|
|
75
|
+
ELEMENT_TYPE_IMPORT,
|
|
76
|
+
ELEMENT_TYPE_VARIABLE,
|
|
77
|
+
is_element_of_type,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
elements = result.elements or []
|
|
81
|
+
|
|
82
|
+
# Extract elements by type from unified list
|
|
83
|
+
imports = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_IMPORT)]
|
|
84
|
+
classes = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_CLASS)]
|
|
85
|
+
methods = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_FUNCTION)]
|
|
86
|
+
fields = [e for e in elements if is_element_of_type(e, ELEMENT_TYPE_VARIABLE)]
|
|
87
|
+
annotations = [
|
|
88
|
+
e for e in elements if is_element_of_type(e, ELEMENT_TYPE_ANNOTATION)
|
|
89
|
+
]
|
|
90
|
+
|
|
70
91
|
return {
|
|
71
92
|
"file_path": result.file_path,
|
|
72
93
|
"language": result.language,
|
|
73
94
|
"structure": {
|
|
74
95
|
"classes": [
|
|
75
96
|
{"name": cls.name, "type": str(type(cls).__name__)}
|
|
76
|
-
for cls in
|
|
97
|
+
for cls in classes
|
|
77
98
|
],
|
|
78
99
|
"methods": [
|
|
79
100
|
{"name": method.name, "type": str(type(method).__name__)}
|
|
80
|
-
for method in
|
|
101
|
+
for method in methods
|
|
81
102
|
],
|
|
82
103
|
"fields": [
|
|
83
104
|
{"name": field.name, "type": str(type(field).__name__)}
|
|
84
|
-
for field in
|
|
105
|
+
for field in fields
|
|
85
106
|
],
|
|
86
107
|
"imports": [
|
|
87
108
|
{"name": imp.name, "type": str(type(imp).__name__)}
|
|
88
|
-
for imp in
|
|
109
|
+
for imp in imports
|
|
89
110
|
],
|
|
90
111
|
"annotations": [
|
|
91
112
|
{
|
|
92
113
|
"name": getattr(ann, "name", str(ann)),
|
|
93
114
|
"type": str(type(ann).__name__),
|
|
94
115
|
}
|
|
95
|
-
for ann in
|
|
116
|
+
for ann in annotations
|
|
96
117
|
],
|
|
97
118
|
},
|
|
98
119
|
"metadata": {
|
|
@@ -100,11 +121,11 @@ class MCPAdapter:
|
|
|
100
121
|
"success": result.success,
|
|
101
122
|
"error_message": result.error_message,
|
|
102
123
|
"package": result.package,
|
|
103
|
-
"class_count": len(
|
|
104
|
-
"method_count": len(
|
|
105
|
-
"field_count": len(
|
|
106
|
-
"import_count": len(
|
|
107
|
-
"annotation_count": len(
|
|
124
|
+
"class_count": len(classes),
|
|
125
|
+
"method_count": len(methods),
|
|
126
|
+
"field_count": len(fields),
|
|
127
|
+
"import_count": len(imports),
|
|
128
|
+
"annotation_count": len(annotations),
|
|
108
129
|
},
|
|
109
130
|
}
|
|
110
131
|
|
|
@@ -130,11 +151,6 @@ class MCPAdapter:
|
|
|
130
151
|
query_results={},
|
|
131
152
|
source_code="",
|
|
132
153
|
package=None,
|
|
133
|
-
imports=[],
|
|
134
|
-
classes=[],
|
|
135
|
-
methods=[],
|
|
136
|
-
fields=[],
|
|
137
|
-
annotations=[],
|
|
138
154
|
analysis_time=0.0,
|
|
139
155
|
success=False,
|
|
140
156
|
error_message=str(e),
|