tree-sitter-analyzer 1.8.4__py3-none-any.whl → 1.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tree-sitter-analyzer might be problematic. Click here for more details.
- tree_sitter_analyzer/__init__.py +1 -1
- tree_sitter_analyzer/api.py +4 -4
- tree_sitter_analyzer/cli/argument_validator.py +29 -17
- tree_sitter_analyzer/cli/commands/advanced_command.py +7 -5
- tree_sitter_analyzer/cli/commands/structure_command.py +7 -5
- tree_sitter_analyzer/cli/commands/summary_command.py +10 -6
- tree_sitter_analyzer/cli/commands/table_command.py +8 -7
- tree_sitter_analyzer/cli/info_commands.py +1 -1
- tree_sitter_analyzer/cli_main.py +3 -2
- tree_sitter_analyzer/core/analysis_engine.py +5 -5
- tree_sitter_analyzer/core/cache_service.py +3 -1
- tree_sitter_analyzer/core/query.py +17 -5
- tree_sitter_analyzer/core/query_service.py +1 -1
- tree_sitter_analyzer/encoding_utils.py +3 -3
- tree_sitter_analyzer/exceptions.py +61 -50
- tree_sitter_analyzer/file_handler.py +3 -0
- tree_sitter_analyzer/formatters/base_formatter.py +10 -5
- tree_sitter_analyzer/formatters/formatter_registry.py +83 -68
- tree_sitter_analyzer/formatters/html_formatter.py +90 -54
- tree_sitter_analyzer/formatters/javascript_formatter.py +21 -16
- tree_sitter_analyzer/formatters/language_formatter_factory.py +7 -6
- tree_sitter_analyzer/formatters/markdown_formatter.py +247 -124
- tree_sitter_analyzer/formatters/python_formatter.py +61 -38
- tree_sitter_analyzer/formatters/typescript_formatter.py +113 -45
- tree_sitter_analyzer/interfaces/mcp_server.py +2 -2
- tree_sitter_analyzer/language_detector.py +6 -6
- tree_sitter_analyzer/language_loader.py +3 -1
- tree_sitter_analyzer/languages/css_plugin.py +120 -61
- tree_sitter_analyzer/languages/html_plugin.py +159 -62
- tree_sitter_analyzer/languages/java_plugin.py +42 -34
- tree_sitter_analyzer/languages/javascript_plugin.py +59 -30
- tree_sitter_analyzer/languages/markdown_plugin.py +402 -368
- tree_sitter_analyzer/languages/python_plugin.py +111 -64
- tree_sitter_analyzer/languages/typescript_plugin.py +241 -132
- tree_sitter_analyzer/mcp/server.py +22 -18
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +13 -8
- tree_sitter_analyzer/mcp/tools/base_tool.py +2 -2
- tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +232 -26
- tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +31 -23
- tree_sitter_analyzer/mcp/tools/list_files_tool.py +21 -19
- tree_sitter_analyzer/mcp/tools/query_tool.py +17 -18
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +30 -31
- tree_sitter_analyzer/mcp/tools/search_content_tool.py +131 -77
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +29 -16
- tree_sitter_analyzer/mcp/utils/file_output_factory.py +64 -51
- tree_sitter_analyzer/mcp/utils/file_output_manager.py +34 -24
- tree_sitter_analyzer/mcp/utils/gitignore_detector.py +8 -4
- tree_sitter_analyzer/models.py +7 -5
- tree_sitter_analyzer/plugins/base.py +9 -7
- tree_sitter_analyzer/plugins/manager.py +1 -0
- tree_sitter_analyzer/queries/css.py +2 -21
- tree_sitter_analyzer/queries/html.py +2 -15
- tree_sitter_analyzer/queries/markdown.py +30 -41
- tree_sitter_analyzer/queries/python.py +20 -5
- tree_sitter_analyzer/query_loader.py +5 -5
- tree_sitter_analyzer/security/validator.py +114 -86
- tree_sitter_analyzer/utils/__init__.py +58 -28
- tree_sitter_analyzer/utils/tree_sitter_compat.py +72 -65
- tree_sitter_analyzer/utils.py +26 -15
- {tree_sitter_analyzer-1.8.4.dist-info → tree_sitter_analyzer-1.9.0.dist-info}/METADATA +1 -1
- tree_sitter_analyzer-1.9.0.dist-info/RECORD +109 -0
- tree_sitter_analyzer-1.8.4.dist-info/RECORD +0 -109
- {tree_sitter_analyzer-1.8.4.dist-info → tree_sitter_analyzer-1.9.0.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-1.8.4.dist-info → tree_sitter_analyzer-1.9.0.dist-info}/entry_points.txt +0 -0
|
@@ -8,7 +8,7 @@ selector parsing, and property analysis.
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import logging
|
|
11
|
-
from typing import TYPE_CHECKING
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
12
12
|
|
|
13
13
|
from ..models import AnalysisResult, StyleElement
|
|
14
14
|
from ..plugins.base import ElementExtractor, LanguagePlugin
|
|
@@ -28,15 +28,56 @@ class CssElementExtractor(ElementExtractor):
|
|
|
28
28
|
def __init__(self):
|
|
29
29
|
self.property_categories = {
|
|
30
30
|
# CSS プロパティの分類システム
|
|
31
|
-
"layout": [
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
"layout": [
|
|
32
|
+
"display",
|
|
33
|
+
"position",
|
|
34
|
+
"float",
|
|
35
|
+
"clear",
|
|
36
|
+
"overflow",
|
|
37
|
+
"visibility",
|
|
38
|
+
"z-index",
|
|
39
|
+
],
|
|
40
|
+
"box_model": [
|
|
41
|
+
"width",
|
|
42
|
+
"height",
|
|
43
|
+
"margin",
|
|
44
|
+
"padding",
|
|
45
|
+
"border",
|
|
46
|
+
"box-sizing",
|
|
47
|
+
],
|
|
48
|
+
"typography": [
|
|
49
|
+
"font",
|
|
50
|
+
"color",
|
|
51
|
+
"text",
|
|
52
|
+
"line-height",
|
|
53
|
+
"letter-spacing",
|
|
54
|
+
"word-spacing",
|
|
55
|
+
],
|
|
56
|
+
"background": [
|
|
57
|
+
"background",
|
|
58
|
+
"background-color",
|
|
59
|
+
"background-image",
|
|
60
|
+
"background-position",
|
|
61
|
+
"background-size",
|
|
62
|
+
],
|
|
63
|
+
"flexbox": [
|
|
64
|
+
"flex",
|
|
65
|
+
"justify-content",
|
|
66
|
+
"align-items",
|
|
67
|
+
"align-content",
|
|
68
|
+
"flex-direction",
|
|
69
|
+
"flex-wrap",
|
|
70
|
+
],
|
|
36
71
|
"grid": ["grid", "grid-template", "grid-area", "grid-column", "grid-row"],
|
|
37
72
|
"animation": ["animation", "transition", "transform", "keyframes"],
|
|
38
|
-
"responsive": [
|
|
39
|
-
|
|
73
|
+
"responsive": [
|
|
74
|
+
"media",
|
|
75
|
+
"min-width",
|
|
76
|
+
"max-width",
|
|
77
|
+
"min-height",
|
|
78
|
+
"max-height",
|
|
79
|
+
],
|
|
80
|
+
"other": [],
|
|
40
81
|
}
|
|
41
82
|
|
|
42
83
|
def extract_functions(self, tree: "tree_sitter.Tree", source_code: str) -> list:
|
|
@@ -55,10 +96,12 @@ class CssElementExtractor(ElementExtractor):
|
|
|
55
96
|
"""CSS doesn't have imports in the traditional sense, return empty list"""
|
|
56
97
|
return []
|
|
57
98
|
|
|
58
|
-
def extract_css_rules(
|
|
99
|
+
def extract_css_rules(
|
|
100
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
101
|
+
) -> list[StyleElement]:
|
|
59
102
|
"""Extract CSS rules using tree-sitter-css parser"""
|
|
60
103
|
elements = []
|
|
61
|
-
|
|
104
|
+
|
|
62
105
|
try:
|
|
63
106
|
if hasattr(tree, "root_node"):
|
|
64
107
|
self._traverse_for_css_rules(tree.root_node, elements, source_code)
|
|
@@ -68,10 +111,7 @@ class CssElementExtractor(ElementExtractor):
|
|
|
68
111
|
return elements
|
|
69
112
|
|
|
70
113
|
def _traverse_for_css_rules(
|
|
71
|
-
self,
|
|
72
|
-
node: "tree_sitter.Node",
|
|
73
|
-
elements: list[StyleElement],
|
|
74
|
-
source_code: str
|
|
114
|
+
self, node: "tree_sitter.Node", elements: list[StyleElement], source_code: str
|
|
75
115
|
) -> None:
|
|
76
116
|
"""Traverse tree to find CSS rules using tree-sitter-css grammar"""
|
|
77
117
|
if hasattr(node, "type") and self._is_css_rule_node(node.type):
|
|
@@ -99,14 +139,12 @@ class CssElementExtractor(ElementExtractor):
|
|
|
99
139
|
"font_face_statement",
|
|
100
140
|
"page_statement",
|
|
101
141
|
"charset_statement",
|
|
102
|
-
"namespace_statement"
|
|
142
|
+
"namespace_statement",
|
|
103
143
|
]
|
|
104
144
|
return node_type in css_rule_types
|
|
105
145
|
|
|
106
146
|
def _create_style_element(
|
|
107
|
-
self,
|
|
108
|
-
node: "tree_sitter.Node",
|
|
109
|
-
source_code: str
|
|
147
|
+
self, node: "tree_sitter.Node", source_code: str
|
|
110
148
|
) -> StyleElement | None:
|
|
111
149
|
"""Create StyleElement from tree-sitter node using tree-sitter-css grammar"""
|
|
112
150
|
try:
|
|
@@ -116,7 +154,12 @@ class CssElementExtractor(ElementExtractor):
|
|
|
116
154
|
properties = self._extract_properties(node, source_code)
|
|
117
155
|
element_class = self._classify_rule(properties)
|
|
118
156
|
name = selector or "unknown_rule"
|
|
119
|
-
elif node.type in [
|
|
157
|
+
elif node.type in [
|
|
158
|
+
"at_rule",
|
|
159
|
+
"media_statement",
|
|
160
|
+
"import_statement",
|
|
161
|
+
"keyframes_statement",
|
|
162
|
+
]:
|
|
120
163
|
selector = self._extract_at_rule_name(node, source_code)
|
|
121
164
|
properties = {}
|
|
122
165
|
element_class = "at_rule"
|
|
@@ -133,13 +176,15 @@ class CssElementExtractor(ElementExtractor):
|
|
|
133
176
|
# Create StyleElement
|
|
134
177
|
element = StyleElement(
|
|
135
178
|
name=name,
|
|
136
|
-
start_line=node.start_point[0] + 1
|
|
179
|
+
start_line=node.start_point[0] + 1
|
|
180
|
+
if hasattr(node, "start_point")
|
|
181
|
+
else 0,
|
|
137
182
|
end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
|
|
138
183
|
raw_text=raw_text,
|
|
139
184
|
language="css",
|
|
140
185
|
selector=selector,
|
|
141
186
|
properties=properties,
|
|
142
|
-
element_class=element_class
|
|
187
|
+
element_class=element_class,
|
|
143
188
|
)
|
|
144
189
|
|
|
145
190
|
return element
|
|
@@ -155,28 +200,35 @@ class CssElementExtractor(ElementExtractor):
|
|
|
155
200
|
for child in node.children:
|
|
156
201
|
if hasattr(child, "type") and child.type == "selectors":
|
|
157
202
|
return self._extract_node_text(child, source_code).strip()
|
|
158
|
-
|
|
203
|
+
|
|
159
204
|
# Fallback: extract from beginning of node text
|
|
160
205
|
node_text = self._extract_node_text(node, source_code)
|
|
161
206
|
if "{" in node_text:
|
|
162
207
|
return node_text.split("{")[0].strip()
|
|
163
|
-
|
|
208
|
+
|
|
164
209
|
return "unknown"
|
|
165
210
|
except Exception:
|
|
166
211
|
return "unknown"
|
|
167
212
|
|
|
168
|
-
def _extract_properties(
|
|
213
|
+
def _extract_properties(
|
|
214
|
+
self, node: "tree_sitter.Node", source_code: str
|
|
215
|
+
) -> dict[str, str]:
|
|
169
216
|
"""Extract properties from CSS rule_set node using tree-sitter-css grammar"""
|
|
170
217
|
properties = {}
|
|
171
|
-
|
|
218
|
+
|
|
172
219
|
try:
|
|
173
220
|
if hasattr(node, "children"):
|
|
174
221
|
for child in node.children:
|
|
175
222
|
if hasattr(child, "type") and child.type == "block":
|
|
176
223
|
# Look for declarations within the block
|
|
177
224
|
for grandchild in child.children:
|
|
178
|
-
if
|
|
179
|
-
|
|
225
|
+
if (
|
|
226
|
+
hasattr(grandchild, "type")
|
|
227
|
+
and grandchild.type == "declaration"
|
|
228
|
+
):
|
|
229
|
+
prop_name, prop_value = self._parse_declaration(
|
|
230
|
+
grandchild, source_code
|
|
231
|
+
)
|
|
180
232
|
if prop_name:
|
|
181
233
|
properties[prop_name] = prop_value
|
|
182
234
|
except Exception as e:
|
|
@@ -184,20 +236,26 @@ class CssElementExtractor(ElementExtractor):
|
|
|
184
236
|
|
|
185
237
|
return properties
|
|
186
238
|
|
|
187
|
-
def _parse_declaration(
|
|
239
|
+
def _parse_declaration(
|
|
240
|
+
self, decl_node: "tree_sitter.Node", source_code: str
|
|
241
|
+
) -> tuple[str, str]:
|
|
188
242
|
"""Parse individual CSS declaration using tree-sitter-css grammar"""
|
|
189
243
|
try:
|
|
190
244
|
prop_name = ""
|
|
191
245
|
prop_value = ""
|
|
192
|
-
|
|
246
|
+
|
|
193
247
|
if hasattr(decl_node, "children"):
|
|
194
248
|
for child in decl_node.children:
|
|
195
249
|
if hasattr(child, "type"):
|
|
196
250
|
if child.type == "property_name":
|
|
197
|
-
prop_name = self._extract_node_text(
|
|
251
|
+
prop_name = self._extract_node_text(
|
|
252
|
+
child, source_code
|
|
253
|
+
).strip()
|
|
198
254
|
elif child.type in ["value", "values"]:
|
|
199
|
-
prop_value = self._extract_node_text(
|
|
200
|
-
|
|
255
|
+
prop_value = self._extract_node_text(
|
|
256
|
+
child, source_code
|
|
257
|
+
).strip()
|
|
258
|
+
|
|
201
259
|
# Fallback to simple parsing
|
|
202
260
|
if not prop_name:
|
|
203
261
|
decl_text = self._extract_node_text(decl_node, source_code)
|
|
@@ -205,7 +263,7 @@ class CssElementExtractor(ElementExtractor):
|
|
|
205
263
|
parts = decl_text.split(":", 1)
|
|
206
264
|
prop_name = parts[0].strip()
|
|
207
265
|
prop_value = parts[1].strip().rstrip(";")
|
|
208
|
-
|
|
266
|
+
|
|
209
267
|
return prop_name, prop_value
|
|
210
268
|
except Exception:
|
|
211
269
|
return "", ""
|
|
@@ -227,16 +285,16 @@ class CssElementExtractor(ElementExtractor):
|
|
|
227
285
|
"""Classify CSS rule based on properties"""
|
|
228
286
|
if not properties:
|
|
229
287
|
return "other"
|
|
230
|
-
|
|
288
|
+
|
|
231
289
|
# Count properties in each category
|
|
232
|
-
category_scores =
|
|
233
|
-
|
|
290
|
+
category_scores = dict.fromkeys(self.property_categories, 0)
|
|
291
|
+
|
|
234
292
|
for prop_name in properties.keys():
|
|
235
293
|
prop_name_lower = prop_name.lower()
|
|
236
294
|
for category, props in self.property_categories.items():
|
|
237
295
|
if any(prop in prop_name_lower for prop in props):
|
|
238
296
|
category_scores[category] += 1
|
|
239
|
-
|
|
297
|
+
|
|
240
298
|
# Return category with highest score
|
|
241
299
|
best_category = max(category_scores, key=category_scores.get)
|
|
242
300
|
return best_category if category_scores[best_category] > 0 else "other"
|
|
@@ -272,13 +330,16 @@ class CssPlugin(LanguagePlugin):
|
|
|
272
330
|
def get_queries(self) -> dict[str, str]:
|
|
273
331
|
"""Return CSS-specific tree-sitter queries"""
|
|
274
332
|
from ..queries.css import CSS_QUERIES
|
|
333
|
+
|
|
275
334
|
return CSS_QUERIES
|
|
276
335
|
|
|
277
|
-
def execute_query_strategy(
|
|
336
|
+
def execute_query_strategy(
|
|
337
|
+
self, query_key: str | None, language: str
|
|
338
|
+
) -> str | None:
|
|
278
339
|
"""Execute query strategy for CSS"""
|
|
279
340
|
if language != "css":
|
|
280
341
|
return None
|
|
281
|
-
|
|
342
|
+
|
|
282
343
|
queries = self.get_queries()
|
|
283
344
|
return queries.get(query_key) if query_key else None
|
|
284
345
|
|
|
@@ -294,44 +355,40 @@ class CssPlugin(LanguagePlugin):
|
|
|
294
355
|
"animation": ["rule_set"],
|
|
295
356
|
"responsive": ["media_statement"],
|
|
296
357
|
"at_rules": ["at_rule"],
|
|
297
|
-
"other": ["rule_set"]
|
|
358
|
+
"other": ["rule_set"],
|
|
298
359
|
}
|
|
299
360
|
|
|
300
361
|
async def analyze_file(
|
|
301
362
|
self, file_path: str, request: "AnalysisRequest"
|
|
302
363
|
) -> "AnalysisResult":
|
|
303
364
|
"""Analyze CSS file using tree-sitter-css parser"""
|
|
304
|
-
from ..core.analysis_engine import UnifiedAnalysisEngine
|
|
305
365
|
from ..encoding_utils import read_file_safe
|
|
306
366
|
|
|
307
367
|
try:
|
|
308
368
|
# Read file content
|
|
309
369
|
content, encoding = read_file_safe(file_path)
|
|
310
|
-
|
|
311
|
-
# Create analysis engine
|
|
312
|
-
engine = UnifiedAnalysisEngine()
|
|
313
|
-
|
|
370
|
+
|
|
314
371
|
# Use tree-sitter-css for parsing
|
|
315
372
|
try:
|
|
316
|
-
import tree_sitter_css as ts_css
|
|
317
373
|
import tree_sitter
|
|
318
|
-
|
|
374
|
+
import tree_sitter_css as ts_css
|
|
375
|
+
|
|
319
376
|
# Get CSS language
|
|
320
377
|
CSS_LANGUAGE = tree_sitter.Language(ts_css.language())
|
|
321
|
-
|
|
378
|
+
|
|
322
379
|
# Create parser
|
|
323
380
|
parser = tree_sitter.Parser()
|
|
324
381
|
parser.language = CSS_LANGUAGE
|
|
325
|
-
|
|
382
|
+
|
|
326
383
|
# Parse the CSS content
|
|
327
|
-
tree = parser.parse(content.encode(
|
|
328
|
-
|
|
384
|
+
tree = parser.parse(content.encode("utf-8"))
|
|
385
|
+
|
|
329
386
|
# Extract elements using the extractor
|
|
330
387
|
extractor = self.create_extractor()
|
|
331
388
|
elements = extractor.extract_css_rules(tree, content)
|
|
332
|
-
|
|
389
|
+
|
|
333
390
|
log_info(f"Extracted {len(elements)} CSS rules from {file_path}")
|
|
334
|
-
|
|
391
|
+
|
|
335
392
|
return AnalysisResult(
|
|
336
393
|
file_path=file_path,
|
|
337
394
|
language="css",
|
|
@@ -341,15 +398,17 @@ class CssPlugin(LanguagePlugin):
|
|
|
341
398
|
query_results={},
|
|
342
399
|
source_code=content,
|
|
343
400
|
success=True,
|
|
344
|
-
error_message=None
|
|
401
|
+
error_message=None,
|
|
345
402
|
)
|
|
346
|
-
|
|
403
|
+
|
|
347
404
|
except ImportError:
|
|
348
|
-
log_error(
|
|
405
|
+
log_error(
|
|
406
|
+
"tree-sitter-css not available, falling back to basic parsing"
|
|
407
|
+
)
|
|
349
408
|
# Fallback to basic parsing
|
|
350
409
|
lines = content.splitlines()
|
|
351
410
|
line_count = len(lines)
|
|
352
|
-
|
|
411
|
+
|
|
353
412
|
# Create basic StyleElement for the CSS document
|
|
354
413
|
css_element = StyleElement(
|
|
355
414
|
name="css",
|
|
@@ -359,7 +418,7 @@ class CssPlugin(LanguagePlugin):
|
|
|
359
418
|
language="css",
|
|
360
419
|
selector="*",
|
|
361
420
|
properties={},
|
|
362
|
-
element_class="other"
|
|
421
|
+
element_class="other",
|
|
363
422
|
)
|
|
364
423
|
elements = [css_element]
|
|
365
424
|
|
|
@@ -372,7 +431,7 @@ class CssPlugin(LanguagePlugin):
|
|
|
372
431
|
query_results={},
|
|
373
432
|
source_code=content,
|
|
374
433
|
success=True,
|
|
375
|
-
error_message=None
|
|
434
|
+
error_message=None,
|
|
376
435
|
)
|
|
377
436
|
|
|
378
437
|
except Exception as e:
|
|
@@ -386,5 +445,5 @@ class CssPlugin(LanguagePlugin):
|
|
|
386
445
|
query_results={},
|
|
387
446
|
source_code="",
|
|
388
447
|
success=False,
|
|
389
|
-
error_message=str(e)
|
|
390
|
-
)
|
|
448
|
+
error_message=str(e),
|
|
449
|
+
)
|