tree-sitter-analyzer 1.9.17.1__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.
Files changed (149) hide show
  1. tree_sitter_analyzer/__init__.py +132 -0
  2. tree_sitter_analyzer/__main__.py +11 -0
  3. tree_sitter_analyzer/api.py +853 -0
  4. tree_sitter_analyzer/cli/__init__.py +39 -0
  5. tree_sitter_analyzer/cli/__main__.py +12 -0
  6. tree_sitter_analyzer/cli/argument_validator.py +89 -0
  7. tree_sitter_analyzer/cli/commands/__init__.py +26 -0
  8. tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
  9. tree_sitter_analyzer/cli/commands/base_command.py +181 -0
  10. tree_sitter_analyzer/cli/commands/default_command.py +18 -0
  11. tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
  12. tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
  13. tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
  14. tree_sitter_analyzer/cli/commands/query_command.py +109 -0
  15. tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
  16. tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
  17. tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
  18. tree_sitter_analyzer/cli/commands/table_command.py +414 -0
  19. tree_sitter_analyzer/cli/info_commands.py +124 -0
  20. tree_sitter_analyzer/cli_main.py +472 -0
  21. tree_sitter_analyzer/constants.py +85 -0
  22. tree_sitter_analyzer/core/__init__.py +15 -0
  23. tree_sitter_analyzer/core/analysis_engine.py +580 -0
  24. tree_sitter_analyzer/core/cache_service.py +333 -0
  25. tree_sitter_analyzer/core/engine.py +585 -0
  26. tree_sitter_analyzer/core/parser.py +293 -0
  27. tree_sitter_analyzer/core/query.py +605 -0
  28. tree_sitter_analyzer/core/query_filter.py +200 -0
  29. tree_sitter_analyzer/core/query_service.py +340 -0
  30. tree_sitter_analyzer/encoding_utils.py +530 -0
  31. tree_sitter_analyzer/exceptions.py +747 -0
  32. tree_sitter_analyzer/file_handler.py +246 -0
  33. tree_sitter_analyzer/formatters/__init__.py +1 -0
  34. tree_sitter_analyzer/formatters/base_formatter.py +201 -0
  35. tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
  36. tree_sitter_analyzer/formatters/formatter_config.py +197 -0
  37. tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
  38. tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
  39. tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
  40. tree_sitter_analyzer/formatters/go_formatter.py +368 -0
  41. tree_sitter_analyzer/formatters/html_formatter.py +498 -0
  42. tree_sitter_analyzer/formatters/java_formatter.py +423 -0
  43. tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
  44. tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
  45. tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
  46. tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
  47. tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
  48. tree_sitter_analyzer/formatters/php_formatter.py +301 -0
  49. tree_sitter_analyzer/formatters/python_formatter.py +830 -0
  50. tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
  51. tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
  52. tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
  53. tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
  54. tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
  55. tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
  56. tree_sitter_analyzer/interfaces/__init__.py +9 -0
  57. tree_sitter_analyzer/interfaces/cli.py +535 -0
  58. tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
  59. tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
  60. tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
  61. tree_sitter_analyzer/language_detector.py +553 -0
  62. tree_sitter_analyzer/language_loader.py +271 -0
  63. tree_sitter_analyzer/languages/__init__.py +10 -0
  64. tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
  65. tree_sitter_analyzer/languages/css_plugin.py +449 -0
  66. tree_sitter_analyzer/languages/go_plugin.py +836 -0
  67. tree_sitter_analyzer/languages/html_plugin.py +496 -0
  68. tree_sitter_analyzer/languages/java_plugin.py +1299 -0
  69. tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
  70. tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
  71. tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
  72. tree_sitter_analyzer/languages/php_plugin.py +862 -0
  73. tree_sitter_analyzer/languages/python_plugin.py +1636 -0
  74. tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
  75. tree_sitter_analyzer/languages/rust_plugin.py +673 -0
  76. tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
  77. tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
  78. tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
  79. tree_sitter_analyzer/legacy_table_formatter.py +860 -0
  80. tree_sitter_analyzer/mcp/__init__.py +34 -0
  81. tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
  82. tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
  83. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
  84. tree_sitter_analyzer/mcp/server.py +869 -0
  85. tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
  86. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
  87. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
  88. tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
  89. tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
  90. tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
  91. tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
  92. tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
  93. tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
  94. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
  95. tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
  96. tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
  97. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
  98. tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
  99. tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
  100. tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
  101. tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
  102. tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
  103. tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
  104. tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
  105. tree_sitter_analyzer/models.py +840 -0
  106. tree_sitter_analyzer/mypy_current_errors.txt +2 -0
  107. tree_sitter_analyzer/output_manager.py +255 -0
  108. tree_sitter_analyzer/platform_compat/__init__.py +3 -0
  109. tree_sitter_analyzer/platform_compat/adapter.py +324 -0
  110. tree_sitter_analyzer/platform_compat/compare.py +224 -0
  111. tree_sitter_analyzer/platform_compat/detector.py +67 -0
  112. tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
  113. tree_sitter_analyzer/platform_compat/profiles.py +217 -0
  114. tree_sitter_analyzer/platform_compat/record.py +55 -0
  115. tree_sitter_analyzer/platform_compat/recorder.py +155 -0
  116. tree_sitter_analyzer/platform_compat/report.py +92 -0
  117. tree_sitter_analyzer/plugins/__init__.py +280 -0
  118. tree_sitter_analyzer/plugins/base.py +647 -0
  119. tree_sitter_analyzer/plugins/manager.py +384 -0
  120. tree_sitter_analyzer/project_detector.py +328 -0
  121. tree_sitter_analyzer/queries/__init__.py +27 -0
  122. tree_sitter_analyzer/queries/csharp.py +216 -0
  123. tree_sitter_analyzer/queries/css.py +615 -0
  124. tree_sitter_analyzer/queries/go.py +275 -0
  125. tree_sitter_analyzer/queries/html.py +543 -0
  126. tree_sitter_analyzer/queries/java.py +402 -0
  127. tree_sitter_analyzer/queries/javascript.py +724 -0
  128. tree_sitter_analyzer/queries/kotlin.py +192 -0
  129. tree_sitter_analyzer/queries/markdown.py +258 -0
  130. tree_sitter_analyzer/queries/php.py +95 -0
  131. tree_sitter_analyzer/queries/python.py +859 -0
  132. tree_sitter_analyzer/queries/ruby.py +92 -0
  133. tree_sitter_analyzer/queries/rust.py +223 -0
  134. tree_sitter_analyzer/queries/sql.py +555 -0
  135. tree_sitter_analyzer/queries/typescript.py +871 -0
  136. tree_sitter_analyzer/queries/yaml.py +236 -0
  137. tree_sitter_analyzer/query_loader.py +272 -0
  138. tree_sitter_analyzer/security/__init__.py +22 -0
  139. tree_sitter_analyzer/security/boundary_manager.py +277 -0
  140. tree_sitter_analyzer/security/regex_checker.py +297 -0
  141. tree_sitter_analyzer/security/validator.py +599 -0
  142. tree_sitter_analyzer/table_formatter.py +782 -0
  143. tree_sitter_analyzer/utils/__init__.py +53 -0
  144. tree_sitter_analyzer/utils/logging.py +433 -0
  145. tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
  146. tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
  147. tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
  148. tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
  149. tree_sitter_analyzer-1.9.17.1.dist-info/entry_points.txt +25 -0
@@ -0,0 +1,449 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ CSS Language Plugin
4
+
5
+ True CSS parser using tree-sitter-css for comprehensive CSS analysis.
6
+ Provides CSS-specific analysis capabilities including rule extraction,
7
+ selector parsing, and property analysis.
8
+ """
9
+
10
+ import logging
11
+ from typing import TYPE_CHECKING
12
+
13
+ from ..models import AnalysisResult, StyleElement
14
+ from ..plugins.base import ElementExtractor, LanguagePlugin
15
+ from ..utils import log_debug, log_error, log_info
16
+
17
+ if TYPE_CHECKING:
18
+ import tree_sitter
19
+
20
+ from ..core.analysis_engine import AnalysisRequest
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class CssElementExtractor(ElementExtractor):
26
+ """CSS-specific element extractor using tree-sitter-css"""
27
+
28
+ def __init__(self) -> None:
29
+ self.property_categories = {
30
+ # CSS プロパティの分類システム
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
+ ],
71
+ "grid": ["grid", "grid-template", "grid-area", "grid-column", "grid-row"],
72
+ "animation": ["animation", "transition", "transform", "keyframes"],
73
+ "responsive": [
74
+ "media",
75
+ "min-width",
76
+ "max-width",
77
+ "min-height",
78
+ "max-height",
79
+ ],
80
+ "other": [],
81
+ }
82
+
83
+ def extract_functions(self, tree: "tree_sitter.Tree", source_code: str) -> list:
84
+ """CSS doesn't have functions in the traditional sense, return empty list"""
85
+ return []
86
+
87
+ def extract_classes(self, tree: "tree_sitter.Tree", source_code: str) -> list:
88
+ """CSS doesn't have classes in the traditional sense, return empty list"""
89
+ return []
90
+
91
+ def extract_variables(self, tree: "tree_sitter.Tree", source_code: str) -> list:
92
+ """CSS doesn't have variables (except custom properties), return empty list"""
93
+ return []
94
+
95
+ def extract_imports(self, tree: "tree_sitter.Tree", source_code: str) -> list:
96
+ """CSS doesn't have imports in the traditional sense, return empty list"""
97
+ return []
98
+
99
+ def extract_css_rules(
100
+ self, tree: "tree_sitter.Tree", source_code: str
101
+ ) -> list[StyleElement]:
102
+ """Extract CSS rules using tree-sitter-css parser"""
103
+ elements: list[StyleElement] = []
104
+
105
+ try:
106
+ if hasattr(tree, "root_node"):
107
+ self._traverse_for_css_rules(tree.root_node, elements, source_code)
108
+ except Exception as e:
109
+ log_error(f"Error in CSS rule extraction: {e}")
110
+
111
+ return elements
112
+
113
+ def _traverse_for_css_rules(
114
+ self, node: "tree_sitter.Node", elements: list[StyleElement], source_code: str
115
+ ) -> None:
116
+ """Traverse tree to find CSS rules using tree-sitter-css grammar"""
117
+ if hasattr(node, "type") and self._is_css_rule_node(node.type):
118
+ try:
119
+ element = self._create_style_element(node, source_code)
120
+ if element:
121
+ elements.append(element)
122
+ except Exception as e:
123
+ log_debug(f"Failed to extract CSS rule: {e}")
124
+
125
+ # Continue traversing children
126
+ if hasattr(node, "children"):
127
+ for child in node.children:
128
+ self._traverse_for_css_rules(child, elements, source_code)
129
+
130
+ def _is_css_rule_node(self, node_type: str) -> bool:
131
+ """Check if a node type represents a CSS rule in tree-sitter-css grammar"""
132
+ css_rule_types = [
133
+ "rule_set",
134
+ "at_rule",
135
+ "media_statement",
136
+ "import_statement",
137
+ "keyframes_statement",
138
+ "supports_statement",
139
+ "font_face_statement",
140
+ "page_statement",
141
+ "charset_statement",
142
+ "namespace_statement",
143
+ ]
144
+ return node_type in css_rule_types
145
+
146
+ def _create_style_element(
147
+ self, node: "tree_sitter.Node", source_code: str
148
+ ) -> StyleElement | None:
149
+ """Create StyleElement from tree-sitter node using tree-sitter-css grammar"""
150
+ try:
151
+ # Extract selector and properties based on node type
152
+ if node.type == "rule_set":
153
+ selector = self._extract_selector(node, source_code)
154
+ properties = self._extract_properties(node, source_code)
155
+ element_class = self._classify_rule(properties)
156
+ name = selector or "unknown_rule"
157
+ elif node.type in [
158
+ "at_rule",
159
+ "media_statement",
160
+ "import_statement",
161
+ "keyframes_statement",
162
+ ]:
163
+ selector = self._extract_at_rule_name(node, source_code)
164
+ properties = {}
165
+ element_class = "at_rule"
166
+ name = selector or "unknown_at_rule"
167
+ else:
168
+ selector = self._extract_node_text(node, source_code)[:50]
169
+ properties = {}
170
+ element_class = "other"
171
+ name = selector or "unknown"
172
+
173
+ # Extract raw text
174
+ raw_text = self._extract_node_text(node, source_code)
175
+
176
+ # Create StyleElement
177
+ element = StyleElement(
178
+ name=name,
179
+ start_line=(
180
+ node.start_point[0] + 1 if hasattr(node, "start_point") else 0
181
+ ),
182
+ end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
183
+ raw_text=raw_text,
184
+ language="css",
185
+ selector=selector,
186
+ properties=properties,
187
+ element_class=element_class,
188
+ )
189
+
190
+ return element
191
+
192
+ except Exception as e:
193
+ log_debug(f"Failed to create StyleElement: {e}")
194
+ return None
195
+
196
+ def _extract_selector(self, node: "tree_sitter.Node", source_code: str) -> str:
197
+ """Extract selector from CSS rule_set node using tree-sitter-css grammar"""
198
+ try:
199
+ if hasattr(node, "children"):
200
+ for child in node.children:
201
+ if hasattr(child, "type") and child.type == "selectors":
202
+ return self._extract_node_text(child, source_code).strip()
203
+
204
+ # Fallback: extract from beginning of node text
205
+ node_text = self._extract_node_text(node, source_code)
206
+ if "{" in node_text:
207
+ return node_text.split("{")[0].strip()
208
+
209
+ return "unknown"
210
+ except Exception:
211
+ return "unknown"
212
+
213
+ def _extract_properties(
214
+ self, node: "tree_sitter.Node", source_code: str
215
+ ) -> dict[str, str]:
216
+ """Extract properties from CSS rule_set node using tree-sitter-css grammar"""
217
+ properties = {}
218
+
219
+ try:
220
+ if hasattr(node, "children"):
221
+ for child in node.children:
222
+ if hasattr(child, "type") and child.type == "block":
223
+ # Look for declarations within the block
224
+ for grandchild in child.children:
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
+ )
232
+ if prop_name:
233
+ properties[prop_name] = prop_value
234
+ except Exception as e:
235
+ log_debug(f"Failed to extract properties: {e}")
236
+
237
+ return properties
238
+
239
+ def _parse_declaration(
240
+ self, decl_node: "tree_sitter.Node", source_code: str
241
+ ) -> tuple[str, str]:
242
+ """Parse individual CSS declaration using tree-sitter-css grammar"""
243
+ try:
244
+ prop_name = ""
245
+ prop_value = ""
246
+
247
+ if hasattr(decl_node, "children"):
248
+ for child in decl_node.children:
249
+ if hasattr(child, "type"):
250
+ if child.type == "property_name":
251
+ prop_name = self._extract_node_text(
252
+ child, source_code
253
+ ).strip()
254
+ elif child.type in ["value", "values"]:
255
+ prop_value = self._extract_node_text(
256
+ child, source_code
257
+ ).strip()
258
+
259
+ # Fallback to simple parsing
260
+ if not prop_name:
261
+ decl_text = self._extract_node_text(decl_node, source_code)
262
+ if ":" in decl_text:
263
+ parts = decl_text.split(":", 1)
264
+ prop_name = parts[0].strip()
265
+ prop_value = parts[1].strip().rstrip(";")
266
+
267
+ return prop_name, prop_value
268
+ except Exception:
269
+ return "", ""
270
+
271
+ def _extract_at_rule_name(self, node: "tree_sitter.Node", source_code: str) -> str:
272
+ """Extract at-rule name from CSS at-rule node"""
273
+ try:
274
+ node_text = self._extract_node_text(node, source_code)
275
+ if node_text.startswith("@"):
276
+ # Extract @rule-name part
277
+ parts = node_text.split()
278
+ if parts:
279
+ return parts[0]
280
+ return node_text[:50] # Truncate for readability
281
+ except Exception:
282
+ return "unknown"
283
+
284
+ def _classify_rule(self, properties: dict[str, str]) -> str:
285
+ """Classify CSS rule based on properties"""
286
+ if not properties:
287
+ return "other"
288
+
289
+ # Count properties in each category
290
+ category_scores = dict.fromkeys(self.property_categories, 0)
291
+
292
+ for prop_name in properties.keys():
293
+ prop_name_lower = prop_name.lower()
294
+ for category, props in self.property_categories.items():
295
+ if any(prop in prop_name_lower for prop in props):
296
+ category_scores[category] += 1
297
+
298
+ # Return category with highest score
299
+ best_category = max(category_scores, key=lambda k: category_scores[k])
300
+ return best_category if category_scores[best_category] > 0 else "other"
301
+
302
+ def _extract_node_text(self, node: "tree_sitter.Node", source_code: str) -> str:
303
+ """Extract text content from a tree-sitter node"""
304
+ try:
305
+ if hasattr(node, "start_byte") and hasattr(node, "end_byte"):
306
+ source_bytes = source_code.encode("utf-8")
307
+ node_bytes = source_bytes[node.start_byte : node.end_byte]
308
+ return node_bytes.decode("utf-8", errors="replace")
309
+ return ""
310
+ except Exception as e:
311
+ log_debug(f"Failed to extract node text: {e}")
312
+ return ""
313
+
314
+
315
+ class CssPlugin(LanguagePlugin):
316
+ """CSS language plugin using tree-sitter-css for true CSS parsing"""
317
+
318
+ def get_language_name(self) -> str:
319
+ return "css"
320
+
321
+ def get_file_extensions(self) -> list[str]:
322
+ return [".css", ".scss", ".sass", ".less"]
323
+
324
+ def create_extractor(self) -> ElementExtractor:
325
+ return CssElementExtractor()
326
+
327
+ def get_supported_element_types(self) -> list[str]:
328
+ return ["css_rule"]
329
+
330
+ def get_queries(self) -> dict[str, str]:
331
+ """Return CSS-specific tree-sitter queries"""
332
+ from ..queries.css import CSS_QUERIES
333
+
334
+ return CSS_QUERIES
335
+
336
+ def execute_query_strategy(
337
+ self, query_key: str | None, language: str
338
+ ) -> str | None:
339
+ """Execute query strategy for CSS"""
340
+ if language != "css":
341
+ return None
342
+
343
+ queries = self.get_queries()
344
+ return queries.get(query_key) if query_key else None
345
+
346
+ def get_element_categories(self) -> dict[str, list[str]]:
347
+ """Return CSS element categories for query execution"""
348
+ return {
349
+ "layout": ["rule_set"],
350
+ "box_model": ["rule_set"],
351
+ "typography": ["rule_set"],
352
+ "background": ["rule_set"],
353
+ "flexbox": ["rule_set"],
354
+ "grid": ["rule_set"],
355
+ "animation": ["rule_set"],
356
+ "responsive": ["media_statement"],
357
+ "at_rules": ["at_rule"],
358
+ "other": ["rule_set"],
359
+ }
360
+
361
+ async def analyze_file(
362
+ self, file_path: str, request: "AnalysisRequest"
363
+ ) -> "AnalysisResult":
364
+ """Analyze CSS file using tree-sitter-css parser"""
365
+ from ..encoding_utils import read_file_safe
366
+
367
+ try:
368
+ # Read file content
369
+ content, encoding = read_file_safe(file_path)
370
+
371
+ # Use tree-sitter-css for parsing
372
+ try:
373
+ import tree_sitter
374
+ import tree_sitter_css as ts_css
375
+
376
+ # Get CSS language
377
+ CSS_LANGUAGE = tree_sitter.Language(ts_css.language())
378
+
379
+ # Create parser
380
+ parser = tree_sitter.Parser()
381
+ parser.language = CSS_LANGUAGE
382
+
383
+ # Parse the CSS content
384
+ tree = parser.parse(content.encode("utf-8"))
385
+
386
+ # Extract elements using the extractor
387
+ extractor = self.create_extractor()
388
+ elements = extractor.extract_css_rules(tree, content)
389
+
390
+ log_info(f"Extracted {len(elements)} CSS rules from {file_path}")
391
+
392
+ return AnalysisResult(
393
+ file_path=file_path,
394
+ language="css",
395
+ line_count=len(content.splitlines()),
396
+ elements=elements,
397
+ node_count=len(elements),
398
+ query_results={},
399
+ source_code=content,
400
+ success=True,
401
+ error_message=None,
402
+ )
403
+
404
+ except ImportError:
405
+ log_error(
406
+ "tree-sitter-css not available, falling back to basic parsing"
407
+ )
408
+ # Fallback to basic parsing
409
+ lines = content.splitlines()
410
+ line_count = len(lines)
411
+
412
+ # Create basic StyleElement for the CSS document
413
+ css_element = StyleElement(
414
+ name="css",
415
+ start_line=1,
416
+ end_line=line_count,
417
+ raw_text=content[:200] + "..." if len(content) > 200 else content,
418
+ language="css",
419
+ selector="*",
420
+ properties={},
421
+ element_class="other",
422
+ )
423
+ elements = [css_element]
424
+
425
+ return AnalysisResult(
426
+ file_path=file_path,
427
+ language="css",
428
+ line_count=line_count,
429
+ elements=elements,
430
+ node_count=len(elements),
431
+ query_results={},
432
+ source_code=content,
433
+ success=True,
434
+ error_message=None,
435
+ )
436
+
437
+ except Exception as e:
438
+ log_error(f"Failed to analyze CSS file {file_path}: {e}")
439
+ return AnalysisResult(
440
+ file_path=file_path,
441
+ language="css",
442
+ line_count=0,
443
+ elements=[],
444
+ node_count=0,
445
+ query_results={},
446
+ source_code="",
447
+ success=False,
448
+ error_message=str(e),
449
+ )