tree-sitter-analyzer 1.7.2__py3-none-any.whl → 1.7.4__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/cli/commands/advanced_command.py +52 -0
- tree_sitter_analyzer/cli/commands/structure_command.py +50 -1
- tree_sitter_analyzer/cli/commands/summary_command.py +49 -0
- tree_sitter_analyzer/cli/commands/table_command.py +48 -0
- tree_sitter_analyzer/core/query_service.py +155 -5
- tree_sitter_analyzer/formatters/base_formatter.py +29 -2
- tree_sitter_analyzer/formatters/language_formatter_factory.py +83 -0
- tree_sitter_analyzer/formatters/markdown_formatter.py +557 -0
- tree_sitter_analyzer/language_detector.py +30 -0
- tree_sitter_analyzer/language_loader.py +1 -0
- tree_sitter_analyzer/languages/markdown_plugin.py +1673 -0
- tree_sitter_analyzer/languages/python_plugin.py +75 -16
- tree_sitter_analyzer/mcp/server.py +5 -74
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +8 -18
- tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +1 -1
- tree_sitter_analyzer/mcp/tools/list_files_tool.py +1 -1
- tree_sitter_analyzer/mcp/tools/query_tool.py +86 -3
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +91 -23
- tree_sitter_analyzer/mcp/tools/search_content_tool.py +1 -1
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +7 -17
- tree_sitter_analyzer/queries/javascript.py +20 -0
- tree_sitter_analyzer/queries/markdown.py +379 -0
- tree_sitter_analyzer/queries/typescript.py +22 -0
- tree_sitter_analyzer/query_loader.py +1 -0
- {tree_sitter_analyzer-1.7.2.dist-info → tree_sitter_analyzer-1.7.4.dist-info}/METADATA +45 -20
- {tree_sitter_analyzer-1.7.2.dist-info → tree_sitter_analyzer-1.7.4.dist-info}/RECORD +29 -25
- {tree_sitter_analyzer-1.7.2.dist-info → tree_sitter_analyzer-1.7.4.dist-info}/entry_points.txt +1 -0
- {tree_sitter_analyzer-1.7.2.dist-info → tree_sitter_analyzer-1.7.4.dist-info}/WHEEL +0 -0
tree_sitter_analyzer/__init__.py
CHANGED
|
@@ -15,6 +15,7 @@ from ...constants import (
|
|
|
15
15
|
get_element_type,
|
|
16
16
|
)
|
|
17
17
|
from ...output_manager import output_data, output_json, output_section
|
|
18
|
+
from ...formatters.language_formatter_factory import create_language_formatter
|
|
18
19
|
from .base_command import BaseCommand
|
|
19
20
|
|
|
20
21
|
if TYPE_CHECKING:
|
|
@@ -158,6 +159,16 @@ class AdvancedCommand(BaseCommand):
|
|
|
158
159
|
|
|
159
160
|
def _output_full_analysis(self, analysis_result: "AnalysisResult") -> None:
|
|
160
161
|
"""Output full analysis results."""
|
|
162
|
+
# Check if we have a language-specific formatter
|
|
163
|
+
formatter = create_language_formatter(analysis_result.language)
|
|
164
|
+
if formatter:
|
|
165
|
+
# Use language-specific formatter
|
|
166
|
+
output_format = getattr(self.args, 'output_format', 'json')
|
|
167
|
+
formatted_output = formatter.format_advanced(self._convert_to_formatter_format(analysis_result), output_format)
|
|
168
|
+
print(formatted_output)
|
|
169
|
+
return
|
|
170
|
+
|
|
171
|
+
# Fallback to original implementation for unsupported languages
|
|
161
172
|
output_section("Advanced Analysis Results")
|
|
162
173
|
if self.args.output_format == "json":
|
|
163
174
|
result_dict = {
|
|
@@ -182,6 +193,47 @@ class AdvancedCommand(BaseCommand):
|
|
|
182
193
|
else:
|
|
183
194
|
self._output_text_analysis(analysis_result)
|
|
184
195
|
|
|
196
|
+
def _convert_to_formatter_format(self, analysis_result: "AnalysisResult") -> dict:
|
|
197
|
+
"""Convert AnalysisResult to format expected by formatters."""
|
|
198
|
+
return {
|
|
199
|
+
"file_path": analysis_result.file_path,
|
|
200
|
+
"language": analysis_result.language,
|
|
201
|
+
"line_count": analysis_result.line_count,
|
|
202
|
+
"element_count": len(analysis_result.elements),
|
|
203
|
+
"node_count": getattr(analysis_result, "node_count", 0),
|
|
204
|
+
"elements": [
|
|
205
|
+
{
|
|
206
|
+
"name": getattr(element, "name", str(element)),
|
|
207
|
+
"type": get_element_type(element),
|
|
208
|
+
"start_line": getattr(element, "start_line", 0),
|
|
209
|
+
"end_line": getattr(element, "end_line", 0),
|
|
210
|
+
"text": getattr(element, "text", ""),
|
|
211
|
+
"level": getattr(element, "level", 1),
|
|
212
|
+
"url": getattr(element, "url", ""),
|
|
213
|
+
"alt": getattr(element, "alt", ""),
|
|
214
|
+
"language": getattr(element, "language", ""),
|
|
215
|
+
"line_count": getattr(element, "line_count", 0),
|
|
216
|
+
"list_type": getattr(element, "list_type", ""),
|
|
217
|
+
"item_count": getattr(element, "item_count", 0),
|
|
218
|
+
"column_count": getattr(element, "column_count", 0),
|
|
219
|
+
"row_count": getattr(element, "row_count", 0),
|
|
220
|
+
"line_range": {
|
|
221
|
+
"start": getattr(element, "start_line", 0),
|
|
222
|
+
"end": getattr(element, "end_line", 0),
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
for element in analysis_result.elements
|
|
226
|
+
],
|
|
227
|
+
"success": getattr(analysis_result, "success", True),
|
|
228
|
+
"analysis_time": getattr(analysis_result, "analysis_time", 0.0),
|
|
229
|
+
"analysis_metadata": {
|
|
230
|
+
"analysis_time": getattr(analysis_result, "analysis_time", 0.0),
|
|
231
|
+
"language": analysis_result.language,
|
|
232
|
+
"file_path": analysis_result.file_path,
|
|
233
|
+
"analyzer_version": "2.0.0",
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
185
237
|
def _output_text_analysis(self, analysis_result: "AnalysisResult") -> None:
|
|
186
238
|
"""Output analysis in text format."""
|
|
187
239
|
output_data(f"File: {analysis_result.file_path}")
|
|
@@ -16,6 +16,7 @@ from ...constants import (
|
|
|
16
16
|
is_element_of_type,
|
|
17
17
|
)
|
|
18
18
|
from ...output_manager import output_data, output_json, output_section
|
|
19
|
+
from ...formatters.language_formatter_factory import create_language_formatter
|
|
19
20
|
from .base_command import BaseCommand
|
|
20
21
|
|
|
21
22
|
if TYPE_CHECKING:
|
|
@@ -34,7 +35,16 @@ class StructureCommand(BaseCommand):
|
|
|
34
35
|
return 0
|
|
35
36
|
|
|
36
37
|
def _output_structure_analysis(self, analysis_result: "AnalysisResult") -> None:
|
|
37
|
-
"""Output structure analysis results with appropriate
|
|
38
|
+
"""Output structure analysis results with appropriate header."""
|
|
39
|
+
# Check if we have a language-specific formatter
|
|
40
|
+
formatter = create_language_formatter(analysis_result.language)
|
|
41
|
+
if formatter:
|
|
42
|
+
# Use language-specific formatter
|
|
43
|
+
formatted_output = formatter.format_structure(self._convert_to_formatter_format(analysis_result))
|
|
44
|
+
print(formatted_output)
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
# Fallback to original implementation for unsupported languages
|
|
38
48
|
output_section("Structure Analysis Results")
|
|
39
49
|
|
|
40
50
|
# Convert to legacy structure format expected by tests
|
|
@@ -45,6 +55,45 @@ class StructureCommand(BaseCommand):
|
|
|
45
55
|
else:
|
|
46
56
|
self._output_text_format(structure_dict)
|
|
47
57
|
|
|
58
|
+
def _convert_to_formatter_format(self, analysis_result: "AnalysisResult") -> dict:
|
|
59
|
+
"""Convert AnalysisResult to format expected by formatters."""
|
|
60
|
+
from ...constants import get_element_type
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
"file_path": analysis_result.file_path,
|
|
64
|
+
"language": analysis_result.language,
|
|
65
|
+
"line_count": analysis_result.line_count,
|
|
66
|
+
"elements": [
|
|
67
|
+
{
|
|
68
|
+
"name": getattr(element, "name", str(element)),
|
|
69
|
+
"type": get_element_type(element),
|
|
70
|
+
"start_line": getattr(element, "start_line", 0),
|
|
71
|
+
"end_line": getattr(element, "end_line", 0),
|
|
72
|
+
"text": getattr(element, "text", ""),
|
|
73
|
+
"level": getattr(element, "level", 1),
|
|
74
|
+
"url": getattr(element, "url", ""),
|
|
75
|
+
"alt": getattr(element, "alt", ""),
|
|
76
|
+
"language": getattr(element, "language", ""),
|
|
77
|
+
"line_count": getattr(element, "line_count", 0),
|
|
78
|
+
"list_type": getattr(element, "list_type", ""),
|
|
79
|
+
"item_count": getattr(element, "item_count", 0),
|
|
80
|
+
"column_count": getattr(element, "column_count", 0),
|
|
81
|
+
"row_count": getattr(element, "row_count", 0),
|
|
82
|
+
"line_range": {
|
|
83
|
+
"start": getattr(element, "start_line", 0),
|
|
84
|
+
"end": getattr(element, "end_line", 0),
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
for element in analysis_result.elements
|
|
88
|
+
],
|
|
89
|
+
"analysis_metadata": {
|
|
90
|
+
"analysis_time": getattr(analysis_result, "analysis_time", 0.0),
|
|
91
|
+
"language": analysis_result.language,
|
|
92
|
+
"file_path": analysis_result.file_path,
|
|
93
|
+
"analyzer_version": "2.0.0",
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
48
97
|
def _convert_to_legacy_format(self, analysis_result: "AnalysisResult") -> dict:
|
|
49
98
|
"""Convert AnalysisResult to legacy structure format expected by tests."""
|
|
50
99
|
import time
|
|
@@ -15,6 +15,7 @@ from ...constants import (
|
|
|
15
15
|
is_element_of_type,
|
|
16
16
|
)
|
|
17
17
|
from ...output_manager import output_data, output_json, output_section
|
|
18
|
+
from ...formatters.language_formatter_factory import create_language_formatter
|
|
18
19
|
from .base_command import BaseCommand
|
|
19
20
|
|
|
20
21
|
if TYPE_CHECKING:
|
|
@@ -34,6 +35,15 @@ class SummaryCommand(BaseCommand):
|
|
|
34
35
|
|
|
35
36
|
def _output_summary_analysis(self, analysis_result: "AnalysisResult") -> None:
|
|
36
37
|
"""Output summary analysis results."""
|
|
38
|
+
# Check if we have a language-specific formatter
|
|
39
|
+
formatter = create_language_formatter(analysis_result.language)
|
|
40
|
+
if formatter:
|
|
41
|
+
# Use language-specific formatter
|
|
42
|
+
formatted_output = formatter.format_summary(self._convert_to_formatter_format(analysis_result))
|
|
43
|
+
print(formatted_output)
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
# Fallback to original implementation for unsupported languages
|
|
37
47
|
output_section("Summary Results")
|
|
38
48
|
|
|
39
49
|
# Get summary types from args (default: classes,methods)
|
|
@@ -96,6 +106,45 @@ class SummaryCommand(BaseCommand):
|
|
|
96
106
|
else:
|
|
97
107
|
self._output_text_format(summary_data, requested_types)
|
|
98
108
|
|
|
109
|
+
def _convert_to_formatter_format(self, analysis_result: "AnalysisResult") -> dict[str, Any]:
|
|
110
|
+
"""Convert AnalysisResult to format expected by formatters."""
|
|
111
|
+
from ...constants import get_element_type
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
"file_path": analysis_result.file_path,
|
|
115
|
+
"language": analysis_result.language,
|
|
116
|
+
"line_count": analysis_result.line_count,
|
|
117
|
+
"elements": [
|
|
118
|
+
{
|
|
119
|
+
"name": getattr(element, "name", str(element)),
|
|
120
|
+
"type": get_element_type(element),
|
|
121
|
+
"start_line": getattr(element, "start_line", 0),
|
|
122
|
+
"end_line": getattr(element, "end_line", 0),
|
|
123
|
+
"text": getattr(element, "text", ""),
|
|
124
|
+
"level": getattr(element, "level", 1),
|
|
125
|
+
"url": getattr(element, "url", ""),
|
|
126
|
+
"alt": getattr(element, "alt", ""),
|
|
127
|
+
"language": getattr(element, "language", ""),
|
|
128
|
+
"line_count": getattr(element, "line_count", 0),
|
|
129
|
+
"list_type": getattr(element, "list_type", ""),
|
|
130
|
+
"item_count": getattr(element, "item_count", 0),
|
|
131
|
+
"column_count": getattr(element, "column_count", 0),
|
|
132
|
+
"row_count": getattr(element, "row_count", 0),
|
|
133
|
+
"line_range": {
|
|
134
|
+
"start": getattr(element, "start_line", 0),
|
|
135
|
+
"end": getattr(element, "end_line", 0),
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
for element in analysis_result.elements
|
|
139
|
+
],
|
|
140
|
+
"analysis_metadata": {
|
|
141
|
+
"analysis_time": getattr(analysis_result, "analysis_time", 0.0),
|
|
142
|
+
"language": analysis_result.language,
|
|
143
|
+
"file_path": analysis_result.file_path,
|
|
144
|
+
"analyzer_version": "2.0.0",
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
99
148
|
def _output_text_format(self, summary_data: dict, requested_types: list) -> None:
|
|
100
149
|
"""Output summary in human-readable text format."""
|
|
101
150
|
output_data(f"File: {summary_data['file_path']}")
|
|
@@ -18,6 +18,7 @@ from ...constants import (
|
|
|
18
18
|
)
|
|
19
19
|
from ...output_manager import output_error
|
|
20
20
|
from ...table_formatter import create_table_formatter
|
|
21
|
+
from ...formatters.language_formatter_factory import create_language_formatter
|
|
21
22
|
from .base_command import BaseCommand
|
|
22
23
|
|
|
23
24
|
|
|
@@ -32,6 +33,16 @@ class TableCommand(BaseCommand):
|
|
|
32
33
|
if not analysis_result:
|
|
33
34
|
return 1
|
|
34
35
|
|
|
36
|
+
# Check if we have a language-specific formatter
|
|
37
|
+
formatter = create_language_formatter(analysis_result.language)
|
|
38
|
+
if formatter:
|
|
39
|
+
# Use language-specific formatter
|
|
40
|
+
table_type = getattr(self.args, 'table', 'full')
|
|
41
|
+
formatted_output = formatter.format_table(self._convert_to_formatter_format(analysis_result), table_type)
|
|
42
|
+
self._output_table(formatted_output)
|
|
43
|
+
return 0
|
|
44
|
+
|
|
45
|
+
# Fallback to original implementation for unsupported languages
|
|
35
46
|
# Convert analysis result to structure format
|
|
36
47
|
structure_result = self._convert_to_structure_format(
|
|
37
48
|
analysis_result, language
|
|
@@ -53,6 +64,43 @@ class TableCommand(BaseCommand):
|
|
|
53
64
|
output_error(f"An error occurred during table format analysis: {e}")
|
|
54
65
|
return 1
|
|
55
66
|
|
|
67
|
+
def _convert_to_formatter_format(self, analysis_result: Any) -> dict[str, Any]:
|
|
68
|
+
"""Convert AnalysisResult to format expected by formatters."""
|
|
69
|
+
return {
|
|
70
|
+
"file_path": analysis_result.file_path,
|
|
71
|
+
"language": analysis_result.language,
|
|
72
|
+
"line_count": analysis_result.line_count,
|
|
73
|
+
"elements": [
|
|
74
|
+
{
|
|
75
|
+
"name": getattr(element, "name", str(element)),
|
|
76
|
+
"type": get_element_type(element),
|
|
77
|
+
"start_line": getattr(element, "start_line", 0),
|
|
78
|
+
"end_line": getattr(element, "end_line", 0),
|
|
79
|
+
"text": getattr(element, "text", ""),
|
|
80
|
+
"level": getattr(element, "level", 1),
|
|
81
|
+
"url": getattr(element, "url", ""),
|
|
82
|
+
"alt": getattr(element, "alt", ""),
|
|
83
|
+
"language": getattr(element, "language", ""),
|
|
84
|
+
"line_count": getattr(element, "line_count", 0),
|
|
85
|
+
"list_type": getattr(element, "list_type", ""),
|
|
86
|
+
"item_count": getattr(element, "item_count", 0),
|
|
87
|
+
"column_count": getattr(element, "column_count", 0),
|
|
88
|
+
"row_count": getattr(element, "row_count", 0),
|
|
89
|
+
"line_range": {
|
|
90
|
+
"start": getattr(element, "start_line", 0),
|
|
91
|
+
"end": getattr(element, "end_line", 0),
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
for element in analysis_result.elements
|
|
95
|
+
],
|
|
96
|
+
"analysis_metadata": {
|
|
97
|
+
"analysis_time": getattr(analysis_result, "analysis_time", 0.0),
|
|
98
|
+
"language": analysis_result.language,
|
|
99
|
+
"file_path": analysis_result.file_path,
|
|
100
|
+
"analyzer_version": "2.0.0",
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
56
104
|
def _convert_to_structure_format(
|
|
57
105
|
self, analysis_result: Any, language: str
|
|
58
106
|
) -> dict[str, Any]:
|
|
@@ -80,9 +80,24 @@ class QueryService:
|
|
|
80
80
|
f"Query '{query_key}' not found for language '{language}'"
|
|
81
81
|
)
|
|
82
82
|
|
|
83
|
-
# Execute tree-sitter query
|
|
84
|
-
|
|
85
|
-
captures =
|
|
83
|
+
# Execute tree-sitter query using new API with fallback
|
|
84
|
+
import tree_sitter
|
|
85
|
+
captures = []
|
|
86
|
+
|
|
87
|
+
# Try to create and execute the query
|
|
88
|
+
try:
|
|
89
|
+
ts_query = tree_sitter.Query(language_obj, query_string)
|
|
90
|
+
|
|
91
|
+
# Try to execute the query
|
|
92
|
+
captures = ts_query.captures(tree.root_node)
|
|
93
|
+
|
|
94
|
+
# If captures is empty or not in expected format, try manual fallback
|
|
95
|
+
if not captures or (isinstance(captures, list) and len(captures) == 0):
|
|
96
|
+
captures = self._manual_query_execution(tree.root_node, query_key, language)
|
|
97
|
+
|
|
98
|
+
except (AttributeError, Exception) as e:
|
|
99
|
+
# If query creation or execution fails, use manual fallback
|
|
100
|
+
captures = self._manual_query_execution(tree.root_node, query_key, language)
|
|
86
101
|
|
|
87
102
|
# Process capture results
|
|
88
103
|
results = []
|
|
@@ -91,12 +106,19 @@ class QueryService:
|
|
|
91
106
|
for capture_name, nodes in captures.items():
|
|
92
107
|
for node in nodes:
|
|
93
108
|
results.append(self._create_result_dict(node, capture_name))
|
|
94
|
-
|
|
95
|
-
#
|
|
109
|
+
elif isinstance(captures, list):
|
|
110
|
+
# Handle both old API (list of tuples) and manual execution (list of tuples)
|
|
96
111
|
for capture in captures:
|
|
97
112
|
if isinstance(capture, tuple) and len(capture) == 2:
|
|
98
113
|
node, name = capture
|
|
99
114
|
results.append(self._create_result_dict(node, name))
|
|
115
|
+
else:
|
|
116
|
+
# If captures is not in expected format, try manual fallback
|
|
117
|
+
manual_captures = self._manual_query_execution(tree.root_node, query_key, language)
|
|
118
|
+
for capture in manual_captures:
|
|
119
|
+
if isinstance(capture, tuple) and len(capture) == 2:
|
|
120
|
+
node, name = capture
|
|
121
|
+
results.append(self._create_result_dict(node, name))
|
|
100
122
|
|
|
101
123
|
# Apply filters
|
|
102
124
|
if filter_expression and results:
|
|
@@ -160,3 +182,131 @@ class QueryService:
|
|
|
160
182
|
return query_loader.get_query_description(language, query_key)
|
|
161
183
|
except Exception:
|
|
162
184
|
return None
|
|
185
|
+
|
|
186
|
+
def _manual_query_execution(self, root_node: Any, query_key: str | None, language: str) -> list[tuple[Any, str]]:
|
|
187
|
+
"""
|
|
188
|
+
Manual query execution fallback for tree-sitter 0.25.x compatibility
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
root_node: Root node of the parsed tree
|
|
192
|
+
query_key: Query key to execute (can be None for custom queries)
|
|
193
|
+
language: Programming language
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
List of (node, capture_name) tuples
|
|
197
|
+
"""
|
|
198
|
+
captures = []
|
|
199
|
+
|
|
200
|
+
def walk_tree(node):
|
|
201
|
+
"""Walk the tree and find matching nodes"""
|
|
202
|
+
# If query_key is None, this is a custom query - try to match common patterns
|
|
203
|
+
if query_key is None:
|
|
204
|
+
# For custom queries, try to match common node types
|
|
205
|
+
if language == "java":
|
|
206
|
+
if node.type == "method_declaration":
|
|
207
|
+
captures.append((node, "method"))
|
|
208
|
+
elif node.type == "class_declaration":
|
|
209
|
+
captures.append((node, "class"))
|
|
210
|
+
elif node.type == "field_declaration":
|
|
211
|
+
captures.append((node, "field"))
|
|
212
|
+
elif language == "python":
|
|
213
|
+
if node.type == "function_definition":
|
|
214
|
+
captures.append((node, "function"))
|
|
215
|
+
elif node.type == "class_definition":
|
|
216
|
+
captures.append((node, "class"))
|
|
217
|
+
elif node.type in ["import_statement", "import_from_statement"]:
|
|
218
|
+
captures.append((node, "import"))
|
|
219
|
+
elif language in ["javascript", "typescript"]:
|
|
220
|
+
if node.type in ["function_declaration", "method_definition"]:
|
|
221
|
+
captures.append((node, "function"))
|
|
222
|
+
elif node.type == "class_declaration":
|
|
223
|
+
captures.append((node, "class"))
|
|
224
|
+
|
|
225
|
+
# Markdown-specific queries
|
|
226
|
+
elif language == "markdown":
|
|
227
|
+
if query_key == "headers" and node.type in ["atx_heading", "setext_heading"]:
|
|
228
|
+
captures.append((node, "headers"))
|
|
229
|
+
elif query_key == "code_blocks" and node.type in ["fenced_code_block", "indented_code_block"]:
|
|
230
|
+
captures.append((node, "code_blocks"))
|
|
231
|
+
elif query_key == "links" and node.type == "inline":
|
|
232
|
+
# リンクは inline ノード内のパターンとして検出
|
|
233
|
+
node_text = node.text.decode('utf-8', errors='replace') if hasattr(node, 'text') and node.text else ""
|
|
234
|
+
if '[' in node_text and '](' in node_text:
|
|
235
|
+
captures.append((node, "links"))
|
|
236
|
+
elif query_key == "images" and node.type == "inline":
|
|
237
|
+
# 画像は inline ノード内のパターンとして検出
|
|
238
|
+
node_text = node.text.decode('utf-8', errors='replace') if hasattr(node, 'text') and node.text else ""
|
|
239
|
+
if ')
|
|
241
|
+
elif query_key == "lists" and node.type in ["list", "list_item"]:
|
|
242
|
+
captures.append((node, "lists"))
|
|
243
|
+
elif query_key == "emphasis" and node.type == "inline":
|
|
244
|
+
# 強調は inline ノード内の * や ** パターンとして検出
|
|
245
|
+
node_text = node.text.decode('utf-8', errors='replace') if hasattr(node, 'text') and node.text else ""
|
|
246
|
+
if '*' in node_text or '_' in node_text:
|
|
247
|
+
captures.append((node, "emphasis"))
|
|
248
|
+
elif query_key == "blockquotes" and node.type == "block_quote":
|
|
249
|
+
captures.append((node, "blockquotes"))
|
|
250
|
+
elif query_key == "tables" and node.type == "pipe_table":
|
|
251
|
+
captures.append((node, "tables"))
|
|
252
|
+
elif query_key == "horizontal_rules" and node.type == "thematic_break":
|
|
253
|
+
captures.append((node, "horizontal_rules"))
|
|
254
|
+
elif query_key == "html_blocks" and node.type == "html_block":
|
|
255
|
+
captures.append((node, "html_blocks"))
|
|
256
|
+
elif query_key == "inline_html" and node.type == "html_tag":
|
|
257
|
+
captures.append((node, "inline_html"))
|
|
258
|
+
elif query_key == "inline_code" and node.type == "code_span":
|
|
259
|
+
captures.append((node, "inline_code"))
|
|
260
|
+
elif query_key == "text_content" and node.type in ["paragraph", "inline"]:
|
|
261
|
+
captures.append((node, "text_content"))
|
|
262
|
+
elif query_key == "all_elements" and node.type in [
|
|
263
|
+
"atx_heading", "setext_heading", "fenced_code_block", "indented_code_block",
|
|
264
|
+
"inline", "list", "list_item", "block_quote", "pipe_table",
|
|
265
|
+
"paragraph", "section"
|
|
266
|
+
]:
|
|
267
|
+
captures.append((node, "all_elements"))
|
|
268
|
+
|
|
269
|
+
# Python-specific queries
|
|
270
|
+
elif language == "python":
|
|
271
|
+
if query_key in ["function", "functions"] and node.type == "function_definition":
|
|
272
|
+
captures.append((node, "function"))
|
|
273
|
+
elif query_key in ["class", "classes"] and node.type == "class_definition":
|
|
274
|
+
captures.append((node, "class"))
|
|
275
|
+
elif query_key in ["import", "imports"] and node.type in ["import_statement", "import_from_statement"]:
|
|
276
|
+
captures.append((node, "import"))
|
|
277
|
+
|
|
278
|
+
# JavaScript/TypeScript-specific queries
|
|
279
|
+
elif language in ["javascript", "typescript"]:
|
|
280
|
+
if query_key in ["function", "functions"] and node.type in ["function_declaration", "function_expression", "arrow_function", "method_definition"]:
|
|
281
|
+
captures.append((node, "function"))
|
|
282
|
+
elif query_key in ["class", "classes"] and node.type in ["class_declaration", "class_expression"]:
|
|
283
|
+
captures.append((node, "class"))
|
|
284
|
+
elif query_key in ["method", "methods"] and node.type == "method_definition":
|
|
285
|
+
captures.append((node, "method"))
|
|
286
|
+
elif query_key in ["interface", "interfaces"] and node.type == "interface_declaration" and language == "typescript":
|
|
287
|
+
captures.append((node, "interface"))
|
|
288
|
+
elif query_key in ["type", "types"] and node.type == "type_alias_declaration" and language == "typescript":
|
|
289
|
+
captures.append((node, "type"))
|
|
290
|
+
elif query_key in ["variable", "variables"] and node.type in ["variable_declaration", "lexical_declaration"]:
|
|
291
|
+
captures.append((node, "variable"))
|
|
292
|
+
elif query_key in ["import", "imports"] and node.type == "import_statement":
|
|
293
|
+
captures.append((node, "import"))
|
|
294
|
+
elif query_key in ["export", "exports"] and node.type == "export_statement":
|
|
295
|
+
captures.append((node, "export"))
|
|
296
|
+
|
|
297
|
+
# Java-specific queries
|
|
298
|
+
elif language == "java":
|
|
299
|
+
if query_key in ["method", "methods"] and node.type == "method_declaration":
|
|
300
|
+
# Always use "method" as capture name for consistency
|
|
301
|
+
captures.append((node, "method"))
|
|
302
|
+
elif query_key in ["class", "classes"] and node.type == "class_declaration":
|
|
303
|
+
captures.append((node, "class"))
|
|
304
|
+
elif query_key == "field" and node.type == "field_declaration":
|
|
305
|
+
captures.append((node, "field"))
|
|
306
|
+
|
|
307
|
+
# Recursively process children
|
|
308
|
+
for child in node.children:
|
|
309
|
+
walk_tree(child)
|
|
310
|
+
|
|
311
|
+
walk_tree(root_node)
|
|
312
|
+
return captures
|
|
@@ -1,12 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
Base formatter for language-specific
|
|
3
|
+
Base formatter for language-specific formatting.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import csv
|
|
7
7
|
import io
|
|
8
8
|
from abc import ABC, abstractmethod
|
|
9
|
-
from typing import Any
|
|
9
|
+
from typing import Any, Dict
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BaseFormatter(ABC):
|
|
13
|
+
"""Base class for language-specific formatters"""
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def format_summary(self, analysis_result: Dict[str, Any]) -> str:
|
|
20
|
+
"""Format summary output"""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def format_structure(self, analysis_result: Dict[str, Any]) -> str:
|
|
25
|
+
"""Format structure analysis output"""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def format_advanced(self, analysis_result: Dict[str, Any], output_format: str = "json") -> str:
|
|
30
|
+
"""Format advanced analysis output"""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def format_table(self, analysis_result: Dict[str, Any], table_type: str = "full") -> str:
|
|
35
|
+
"""Format table output"""
|
|
36
|
+
pass
|
|
10
37
|
|
|
11
38
|
|
|
12
39
|
class BaseTableFormatter(ABC):
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Factory for creating language-specific formatters for different output types.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Dict, Type, Any
|
|
7
|
+
from .base_formatter import BaseFormatter
|
|
8
|
+
from .markdown_formatter import MarkdownFormatter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LanguageFormatterFactory:
|
|
12
|
+
"""Factory for creating language-specific formatters"""
|
|
13
|
+
|
|
14
|
+
_formatters: Dict[str, Type[BaseFormatter]] = {
|
|
15
|
+
"markdown": MarkdownFormatter,
|
|
16
|
+
"md": MarkdownFormatter, # Alias
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def create_formatter(cls, language: str) -> BaseFormatter:
|
|
21
|
+
"""
|
|
22
|
+
Create formatter for specified language
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
language: Programming language name
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Language-specific formatter
|
|
29
|
+
"""
|
|
30
|
+
formatter_class = cls._formatters.get(language.lower())
|
|
31
|
+
|
|
32
|
+
if formatter_class is None:
|
|
33
|
+
# Return None for unsupported languages
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
return formatter_class()
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def register_formatter(cls, language: str, formatter_class: Type[BaseFormatter]) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Register new language formatter
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
language: Programming language name
|
|
45
|
+
formatter_class: Formatter class
|
|
46
|
+
"""
|
|
47
|
+
cls._formatters[language.lower()] = formatter_class
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def get_supported_languages(cls) -> list[str]:
|
|
51
|
+
"""
|
|
52
|
+
Get list of supported languages
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
List of supported languages
|
|
56
|
+
"""
|
|
57
|
+
return list(cls._formatters.keys())
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def supports_language(cls, language: str) -> bool:
|
|
61
|
+
"""
|
|
62
|
+
Check if language is supported
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
language: Programming language name
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
True if language is supported
|
|
69
|
+
"""
|
|
70
|
+
return language.lower() in cls._formatters
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def create_language_formatter(language: str) -> BaseFormatter:
|
|
74
|
+
"""
|
|
75
|
+
Create language formatter (function for compatibility)
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
language: Programming language name
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Language formatter or None if not supported
|
|
82
|
+
"""
|
|
83
|
+
return LanguageFormatterFactory.create_formatter(language)
|