tree-sitter-analyzer 1.8.3__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 +83 -25
- {tree_sitter_analyzer-1.8.3.dist-info → tree_sitter_analyzer-1.9.0.dist-info}/METADATA +19 -5
- tree_sitter_analyzer-1.9.0.dist-info/RECORD +109 -0
- tree_sitter_analyzer-1.8.3.dist-info/RECORD +0 -109
- {tree_sitter_analyzer-1.8.3.dist-info → tree_sitter_analyzer-1.9.0.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-1.8.3.dist-info → tree_sitter_analyzer-1.9.0.dist-info}/entry_points.txt +0 -0
|
@@ -9,7 +9,6 @@ across MCP tools.
|
|
|
9
9
|
|
|
10
10
|
import threading
|
|
11
11
|
from pathlib import Path
|
|
12
|
-
from typing import Dict, Optional
|
|
13
12
|
|
|
14
13
|
from ...utils import setup_logger
|
|
15
14
|
from .file_output_manager import FileOutputManager
|
|
@@ -24,95 +23,105 @@ class FileOutputManagerFactory:
|
|
|
24
23
|
pattern. Each project root gets its own singleton instance, ensuring consistency
|
|
25
24
|
across MCP tools while preventing duplicate initialization.
|
|
26
25
|
"""
|
|
27
|
-
|
|
26
|
+
|
|
28
27
|
# Class-level lock for thread safety
|
|
29
28
|
_lock = threading.RLock()
|
|
30
|
-
|
|
29
|
+
|
|
31
30
|
# Dictionary to store instances by project root
|
|
32
|
-
_instances:
|
|
33
|
-
|
|
31
|
+
_instances: dict[str, FileOutputManager] = {}
|
|
32
|
+
|
|
34
33
|
@classmethod
|
|
35
|
-
def get_instance(cls, project_root:
|
|
34
|
+
def get_instance(cls, project_root: str | None = None) -> FileOutputManager:
|
|
36
35
|
"""
|
|
37
36
|
Get or create a FileOutputManager instance for the specified project root.
|
|
38
|
-
|
|
37
|
+
|
|
39
38
|
This method implements the Managed Singleton pattern - one instance per
|
|
40
39
|
project root, ensuring consistency across all MCP tools.
|
|
41
|
-
|
|
40
|
+
|
|
42
41
|
Args:
|
|
43
42
|
project_root: Project root directory. If None, uses current working directory.
|
|
44
|
-
|
|
43
|
+
|
|
45
44
|
Returns:
|
|
46
45
|
FileOutputManager instance for the specified project root
|
|
47
46
|
"""
|
|
48
47
|
# Normalize project root path
|
|
49
48
|
normalized_root = cls._normalize_project_root(project_root)
|
|
50
|
-
|
|
49
|
+
|
|
51
50
|
# Double-checked locking pattern for thread safety
|
|
52
51
|
if normalized_root not in cls._instances:
|
|
53
52
|
with cls._lock:
|
|
54
53
|
if normalized_root not in cls._instances:
|
|
55
|
-
logger.info(
|
|
54
|
+
logger.info(
|
|
55
|
+
f"Creating new FileOutputManager instance for project root: {normalized_root}"
|
|
56
|
+
)
|
|
56
57
|
cls._instances[normalized_root] = FileOutputManager(normalized_root)
|
|
57
58
|
else:
|
|
58
|
-
logger.debug(
|
|
59
|
+
logger.debug(
|
|
60
|
+
f"Using existing FileOutputManager instance for project root: {normalized_root}"
|
|
61
|
+
)
|
|
59
62
|
else:
|
|
60
|
-
logger.debug(
|
|
61
|
-
|
|
63
|
+
logger.debug(
|
|
64
|
+
f"Using existing FileOutputManager instance for project root: {normalized_root}"
|
|
65
|
+
)
|
|
66
|
+
|
|
62
67
|
return cls._instances[normalized_root]
|
|
63
|
-
|
|
68
|
+
|
|
64
69
|
@classmethod
|
|
65
|
-
def _normalize_project_root(cls, project_root:
|
|
70
|
+
def _normalize_project_root(cls, project_root: str | None) -> str:
|
|
66
71
|
"""
|
|
67
72
|
Normalize project root path for consistent key generation.
|
|
68
|
-
|
|
73
|
+
|
|
69
74
|
Args:
|
|
70
75
|
project_root: Raw project root path
|
|
71
|
-
|
|
76
|
+
|
|
72
77
|
Returns:
|
|
73
78
|
Normalized absolute path string
|
|
74
79
|
"""
|
|
75
80
|
if project_root is None:
|
|
76
81
|
return str(Path.cwd().resolve())
|
|
77
|
-
|
|
82
|
+
|
|
78
83
|
try:
|
|
79
84
|
return str(Path(project_root).resolve())
|
|
80
85
|
except Exception as e:
|
|
81
86
|
logger.warning(f"Failed to resolve project root path '{project_root}': {e}")
|
|
82
87
|
return str(Path.cwd().resolve())
|
|
83
|
-
|
|
88
|
+
|
|
84
89
|
@classmethod
|
|
85
|
-
def clear_instance(cls, project_root:
|
|
90
|
+
def clear_instance(cls, project_root: str | None = None) -> bool:
|
|
86
91
|
"""
|
|
87
92
|
Clear a specific FileOutputManager instance from the factory.
|
|
88
|
-
|
|
93
|
+
|
|
89
94
|
This method is primarily for testing purposes or when you need to
|
|
90
95
|
force recreation of an instance.
|
|
91
|
-
|
|
96
|
+
|
|
92
97
|
Args:
|
|
93
98
|
project_root: Project root directory. If None, uses current working directory.
|
|
94
|
-
|
|
99
|
+
|
|
95
100
|
Returns:
|
|
96
101
|
True if instance was cleared, False if it didn't exist
|
|
97
102
|
"""
|
|
98
103
|
normalized_root = cls._normalize_project_root(project_root)
|
|
99
|
-
|
|
104
|
+
|
|
100
105
|
with cls._lock:
|
|
101
106
|
if normalized_root in cls._instances:
|
|
102
|
-
logger.info(
|
|
107
|
+
logger.info(
|
|
108
|
+
f"Clearing FileOutputManager instance for project root: {normalized_root}"
|
|
109
|
+
)
|
|
103
110
|
del cls._instances[normalized_root]
|
|
104
111
|
return True
|
|
105
112
|
else:
|
|
106
|
-
logger.debug(
|
|
113
|
+
logger.debug(
|
|
114
|
+
f"No FileOutputManager instance found for project root: {normalized_root}"
|
|
115
|
+
)
|
|
107
116
|
return False
|
|
108
|
-
|
|
117
|
+
|
|
109
118
|
@classmethod
|
|
110
119
|
def clear_all_instances(cls) -> int:
|
|
111
120
|
"""
|
|
112
121
|
Clear all FileOutputManager instances from the factory.
|
|
113
|
-
|
|
122
|
+
|
|
114
123
|
This method is primarily for testing purposes or cleanup.
|
|
115
|
-
|
|
124
|
+
|
|
116
125
|
Returns:
|
|
117
126
|
Number of instances that were cleared
|
|
118
127
|
"""
|
|
@@ -124,81 +133,85 @@ class FileOutputManagerFactory:
|
|
|
124
133
|
else:
|
|
125
134
|
logger.debug("No FileOutputManager instances to clear")
|
|
126
135
|
return count
|
|
127
|
-
|
|
136
|
+
|
|
128
137
|
@classmethod
|
|
129
138
|
def get_instance_count(cls) -> int:
|
|
130
139
|
"""
|
|
131
140
|
Get the current number of managed instances.
|
|
132
|
-
|
|
141
|
+
|
|
133
142
|
Returns:
|
|
134
143
|
Number of currently managed FileOutputManager instances
|
|
135
144
|
"""
|
|
136
145
|
with cls._lock:
|
|
137
146
|
return len(cls._instances)
|
|
138
|
-
|
|
147
|
+
|
|
139
148
|
@classmethod
|
|
140
149
|
def get_managed_project_roots(cls) -> list[str]:
|
|
141
150
|
"""
|
|
142
151
|
Get list of all currently managed project roots.
|
|
143
|
-
|
|
152
|
+
|
|
144
153
|
Returns:
|
|
145
154
|
List of project root paths that have managed instances
|
|
146
155
|
"""
|
|
147
156
|
with cls._lock:
|
|
148
157
|
return list(cls._instances.keys())
|
|
149
|
-
|
|
158
|
+
|
|
150
159
|
@classmethod
|
|
151
|
-
def update_project_root(cls, old_root:
|
|
160
|
+
def update_project_root(cls, old_root: str | None, new_root: str) -> bool:
|
|
152
161
|
"""
|
|
153
162
|
Update the project root for an existing instance.
|
|
154
|
-
|
|
163
|
+
|
|
155
164
|
This method moves an existing instance from one project root key to another,
|
|
156
165
|
and updates the instance's internal project root.
|
|
157
|
-
|
|
166
|
+
|
|
158
167
|
Args:
|
|
159
168
|
old_root: Current project root (None for current working directory)
|
|
160
169
|
new_root: New project root
|
|
161
|
-
|
|
170
|
+
|
|
162
171
|
Returns:
|
|
163
172
|
True if update was successful, False if old instance didn't exist
|
|
164
173
|
"""
|
|
165
174
|
old_normalized = cls._normalize_project_root(old_root)
|
|
166
175
|
new_normalized = cls._normalize_project_root(new_root)
|
|
167
|
-
|
|
176
|
+
|
|
168
177
|
if old_normalized == new_normalized:
|
|
169
178
|
logger.debug(f"Project root update not needed: {old_normalized}")
|
|
170
179
|
return True
|
|
171
|
-
|
|
180
|
+
|
|
172
181
|
with cls._lock:
|
|
173
182
|
if old_normalized in cls._instances:
|
|
174
183
|
instance = cls._instances[old_normalized]
|
|
175
|
-
|
|
184
|
+
|
|
176
185
|
# Update the instance's internal project root
|
|
177
186
|
instance.set_project_root(new_root)
|
|
178
|
-
|
|
187
|
+
|
|
179
188
|
# Move to new key
|
|
180
189
|
cls._instances[new_normalized] = instance
|
|
181
190
|
del cls._instances[old_normalized]
|
|
182
|
-
|
|
183
|
-
logger.info(
|
|
191
|
+
|
|
192
|
+
logger.info(
|
|
193
|
+
f"Updated FileOutputManager project root: {old_normalized} -> {new_normalized}"
|
|
194
|
+
)
|
|
184
195
|
return True
|
|
185
196
|
else:
|
|
186
|
-
logger.warning(
|
|
197
|
+
logger.warning(
|
|
198
|
+
f"No FileOutputManager instance found for old project root: {old_normalized}"
|
|
199
|
+
)
|
|
187
200
|
return False
|
|
188
201
|
|
|
189
202
|
|
|
190
203
|
# Convenience function for backward compatibility and ease of use
|
|
191
|
-
def get_file_output_manager(project_root:
|
|
204
|
+
def get_file_output_manager(project_root: str | None = None) -> FileOutputManager:
|
|
192
205
|
"""
|
|
193
206
|
Convenience function to get a FileOutputManager instance.
|
|
194
|
-
|
|
207
|
+
|
|
195
208
|
This function provides a simple interface to the factory while maintaining
|
|
196
209
|
the singleton behavior per project root.
|
|
197
|
-
|
|
210
|
+
|
|
198
211
|
Args:
|
|
199
212
|
project_root: Project root directory. If None, uses current working directory.
|
|
200
|
-
|
|
213
|
+
|
|
201
214
|
Returns:
|
|
202
215
|
FileOutputManager instance for the specified project root
|
|
203
216
|
"""
|
|
204
|
-
return FileOutputManagerFactory.get_instance(project_root)
|
|
217
|
+
return FileOutputManagerFactory.get_instance(project_root)
|
|
@@ -12,7 +12,6 @@ instance management across MCP tools.
|
|
|
12
12
|
import json
|
|
13
13
|
import os
|
|
14
14
|
from pathlib import Path
|
|
15
|
-
from typing import Any, Optional
|
|
16
15
|
|
|
17
16
|
from ...utils import setup_logger
|
|
18
17
|
|
|
@@ -24,12 +23,12 @@ class FileOutputManager:
|
|
|
24
23
|
"""
|
|
25
24
|
Manages file output for analysis results with automatic extension detection
|
|
26
25
|
and security validation.
|
|
27
|
-
|
|
26
|
+
|
|
28
27
|
Enhanced with factory method support for consistent instance management
|
|
29
28
|
across MCP tools while maintaining full backward compatibility.
|
|
30
29
|
"""
|
|
31
30
|
|
|
32
|
-
def __init__(self, project_root:
|
|
31
|
+
def __init__(self, project_root: str | None = None):
|
|
33
32
|
"""
|
|
34
33
|
Initialize the file output manager.
|
|
35
34
|
|
|
@@ -39,22 +38,24 @@ class FileOutputManager:
|
|
|
39
38
|
self.project_root = project_root
|
|
40
39
|
self._output_path = None
|
|
41
40
|
self._initialize_output_path()
|
|
42
|
-
|
|
41
|
+
|
|
43
42
|
@classmethod
|
|
44
|
-
def get_managed_instance(
|
|
43
|
+
def get_managed_instance(
|
|
44
|
+
cls, project_root: str | None = None
|
|
45
|
+
) -> "FileOutputManager":
|
|
45
46
|
"""
|
|
46
47
|
Get a managed FileOutputManager instance using the factory pattern.
|
|
47
|
-
|
|
48
|
+
|
|
48
49
|
This method provides access to the Managed Singleton Factory Pattern,
|
|
49
50
|
ensuring one instance per project root for optimal resource usage
|
|
50
51
|
and consistency across MCP tools.
|
|
51
|
-
|
|
52
|
+
|
|
52
53
|
Args:
|
|
53
54
|
project_root: Project root directory. If None, uses current working directory.
|
|
54
|
-
|
|
55
|
+
|
|
55
56
|
Returns:
|
|
56
57
|
FileOutputManager instance managed by the factory
|
|
57
|
-
|
|
58
|
+
|
|
58
59
|
Note:
|
|
59
60
|
This method requires the factory module to be available. If the factory
|
|
60
61
|
is not available, it falls back to creating a new instance directly.
|
|
@@ -62,23 +63,26 @@ class FileOutputManager:
|
|
|
62
63
|
try:
|
|
63
64
|
# Import here to avoid circular imports
|
|
64
65
|
from .file_output_factory import FileOutputManagerFactory
|
|
66
|
+
|
|
65
67
|
return FileOutputManagerFactory.get_instance(project_root)
|
|
66
68
|
except ImportError as e:
|
|
67
|
-
logger.warning(
|
|
69
|
+
logger.warning(
|
|
70
|
+
f"Factory not available, creating new instance directly: {e}"
|
|
71
|
+
)
|
|
68
72
|
return cls(project_root)
|
|
69
|
-
|
|
73
|
+
|
|
70
74
|
@classmethod
|
|
71
|
-
def create_instance(cls, project_root:
|
|
75
|
+
def create_instance(cls, project_root: str | None = None) -> "FileOutputManager":
|
|
72
76
|
"""
|
|
73
77
|
Create a new FileOutputManager instance directly (bypass factory).
|
|
74
|
-
|
|
78
|
+
|
|
75
79
|
This method creates a new instance without using the factory pattern.
|
|
76
80
|
Use this when you specifically need a separate instance that won't
|
|
77
81
|
be managed by the factory.
|
|
78
|
-
|
|
82
|
+
|
|
79
83
|
Args:
|
|
80
84
|
project_root: Project root directory. If None, uses current working directory.
|
|
81
|
-
|
|
85
|
+
|
|
82
86
|
Returns:
|
|
83
87
|
New FileOutputManager instance
|
|
84
88
|
"""
|
|
@@ -159,14 +163,18 @@ class FileOutputManager:
|
|
|
159
163
|
if first_line_commas > 0:
|
|
160
164
|
# Check if at least 2 more lines have similar comma counts
|
|
161
165
|
similar_comma_lines = sum(
|
|
162
|
-
1
|
|
166
|
+
1
|
|
167
|
+
for line in lines[1:4]
|
|
168
|
+
if abs(line.count(",") - first_line_commas) <= 1
|
|
163
169
|
)
|
|
164
170
|
if similar_comma_lines >= 1:
|
|
165
171
|
return "csv"
|
|
166
172
|
|
|
167
173
|
# Check for Markdown (simple heuristic)
|
|
168
174
|
markdown_indicators = ["#", "##", "###", "|", "```", "*", "-", "+"]
|
|
169
|
-
if any(
|
|
175
|
+
if any(
|
|
176
|
+
content_stripped.startswith(indicator) for indicator in markdown_indicators
|
|
177
|
+
):
|
|
170
178
|
return "markdown"
|
|
171
179
|
|
|
172
180
|
# Check for table format (pipe-separated)
|
|
@@ -193,7 +201,7 @@ class FileOutputManager:
|
|
|
193
201
|
"json": ".json",
|
|
194
202
|
"csv": ".csv",
|
|
195
203
|
"markdown": ".md",
|
|
196
|
-
"text": ".txt"
|
|
204
|
+
"text": ".txt",
|
|
197
205
|
}
|
|
198
206
|
return extension_map.get(content_type, ".txt")
|
|
199
207
|
|
|
@@ -210,13 +218,15 @@ class FileOutputManager:
|
|
|
210
218
|
"""
|
|
211
219
|
content_type = self.detect_content_type(content)
|
|
212
220
|
extension = self.get_file_extension(content_type)
|
|
213
|
-
|
|
221
|
+
|
|
214
222
|
# Remove existing extension if present
|
|
215
223
|
base_name_clean = Path(base_name).stem
|
|
216
|
-
|
|
224
|
+
|
|
217
225
|
return f"{base_name_clean}{extension}"
|
|
218
226
|
|
|
219
|
-
def save_to_file(
|
|
227
|
+
def save_to_file(
|
|
228
|
+
self, content: str, filename: str | None = None, base_name: str | None = None
|
|
229
|
+
) -> str:
|
|
220
230
|
"""
|
|
221
231
|
Save content to file with automatic extension detection.
|
|
222
232
|
|
|
@@ -252,7 +262,7 @@ class FileOutputManager:
|
|
|
252
262
|
try:
|
|
253
263
|
with open(output_file, "w", encoding="utf-8") as f:
|
|
254
264
|
f.write(content)
|
|
255
|
-
|
|
265
|
+
|
|
256
266
|
logger.info(f"Content saved to file: {output_file}")
|
|
257
267
|
return str(output_file)
|
|
258
268
|
|
|
@@ -272,7 +282,7 @@ class FileOutputManager:
|
|
|
272
282
|
"""
|
|
273
283
|
try:
|
|
274
284
|
path_obj = Path(path).resolve()
|
|
275
|
-
|
|
285
|
+
|
|
276
286
|
# Check if parent directory exists or can be created
|
|
277
287
|
parent_dir = path_obj.parent
|
|
278
288
|
if not parent_dir.exists():
|
|
@@ -304,4 +314,4 @@ class FileOutputManager:
|
|
|
304
314
|
self.project_root = project_root
|
|
305
315
|
# Only reinitialize if we don't have an explicit output path from environment
|
|
306
316
|
if not os.environ.get("TREE_SITTER_OUTPUT_PATH"):
|
|
307
|
-
self._initialize_output_path()
|
|
317
|
+
self._initialize_output_path()
|
|
@@ -207,9 +207,11 @@ class GitignoreDetector:
|
|
|
207
207
|
try:
|
|
208
208
|
# Check if paths exist before resolving
|
|
209
209
|
if not search_dir.exists() or not pattern_dir.exists():
|
|
210
|
-
logger.debug(
|
|
210
|
+
logger.debug(
|
|
211
|
+
f"Path does not exist: {search_dir} or {pattern_dir}, assuming affected"
|
|
212
|
+
)
|
|
211
213
|
return True
|
|
212
|
-
|
|
214
|
+
|
|
213
215
|
# If search_dir is the same as pattern_dir or is a subdirectory of pattern_dir
|
|
214
216
|
search_resolved = search_dir.resolve()
|
|
215
217
|
pattern_resolved = pattern_dir.resolve()
|
|
@@ -220,7 +222,9 @@ class GitignoreDetector:
|
|
|
220
222
|
).startswith(str(pattern_resolved) + os.sep)
|
|
221
223
|
except (OSError, ValueError, RuntimeError):
|
|
222
224
|
# If path resolution fails, assume it could be affected
|
|
223
|
-
logger.debug(
|
|
225
|
+
logger.debug(
|
|
226
|
+
f"Path resolution failed for {search_dir} or {pattern_dir}, assuming affected"
|
|
227
|
+
)
|
|
224
228
|
return True
|
|
225
229
|
|
|
226
230
|
def _directory_has_searchable_files(self, directory: Path) -> bool:
|
|
@@ -280,7 +284,7 @@ class GitignoreDetector:
|
|
|
280
284
|
# Check if project path exists
|
|
281
285
|
if not project_path.exists():
|
|
282
286
|
raise FileNotFoundError(f"Project root does not exist: {project_root}")
|
|
283
|
-
|
|
287
|
+
|
|
284
288
|
gitignore_files = self._find_gitignore_files(project_path)
|
|
285
289
|
info["detected_gitignore_files"] = [str(f) for f in gitignore_files]
|
|
286
290
|
|
tree_sitter_analyzer/models.py
CHANGED
|
@@ -161,14 +161,14 @@ class MarkupElement(CodeElement):
|
|
|
161
161
|
HTML要素を表現するデータモデル。
|
|
162
162
|
CodeElementを継承し、マークアップ固有の属性を追加する。
|
|
163
163
|
"""
|
|
164
|
-
|
|
164
|
+
|
|
165
165
|
tag_name: str = ""
|
|
166
166
|
attributes: dict[str, str] = field(default_factory=dict)
|
|
167
167
|
parent: "MarkupElement | None" = None
|
|
168
168
|
children: list["MarkupElement"] = field(default_factory=list)
|
|
169
169
|
element_class: str = "" # 分類システムのカテゴリ (例: 'structure', 'media', 'form')
|
|
170
170
|
element_type: str = "html_element"
|
|
171
|
-
|
|
171
|
+
|
|
172
172
|
def to_summary_item(self) -> dict[str, Any]:
|
|
173
173
|
"""Return dictionary for summary item"""
|
|
174
174
|
return {
|
|
@@ -186,12 +186,14 @@ class StyleElement(CodeElement):
|
|
|
186
186
|
CSSルールを表現するデータモデル。
|
|
187
187
|
CodeElementを継承する。
|
|
188
188
|
"""
|
|
189
|
-
|
|
189
|
+
|
|
190
190
|
selector: str = ""
|
|
191
191
|
properties: dict[str, str] = field(default_factory=dict)
|
|
192
|
-
element_class: str =
|
|
192
|
+
element_class: str = (
|
|
193
|
+
"" # 分類システムのカテゴリ (例: 'layout', 'typography', 'color')
|
|
194
|
+
)
|
|
193
195
|
element_type: str = "css_rule"
|
|
194
|
-
|
|
196
|
+
|
|
195
197
|
def to_summary_item(self) -> dict[str, Any]:
|
|
196
198
|
"""Return dictionary for summary item"""
|
|
197
199
|
return {
|
|
@@ -181,7 +181,7 @@ class LanguagePlugin(ABC):
|
|
|
181
181
|
def get_supported_element_types(self) -> list[str]:
|
|
182
182
|
"""
|
|
183
183
|
Return list of supported CodeElement types.
|
|
184
|
-
|
|
184
|
+
|
|
185
185
|
Returns:
|
|
186
186
|
List of element types (e.g., ["function", "class", "variable"])
|
|
187
187
|
"""
|
|
@@ -190,20 +190,22 @@ class LanguagePlugin(ABC):
|
|
|
190
190
|
def get_queries(self) -> dict[str, str]:
|
|
191
191
|
"""
|
|
192
192
|
Return language-specific tree-sitter queries.
|
|
193
|
-
|
|
193
|
+
|
|
194
194
|
Returns:
|
|
195
195
|
Dictionary mapping query names to query strings
|
|
196
196
|
"""
|
|
197
197
|
return {}
|
|
198
198
|
|
|
199
|
-
def execute_query_strategy(
|
|
199
|
+
def execute_query_strategy(
|
|
200
|
+
self, query_key: str | None, language: str
|
|
201
|
+
) -> str | None:
|
|
200
202
|
"""
|
|
201
203
|
Execute query strategy for this language plugin.
|
|
202
|
-
|
|
204
|
+
|
|
203
205
|
Args:
|
|
204
206
|
query_key: Query key to execute
|
|
205
207
|
language: Programming language
|
|
206
|
-
|
|
208
|
+
|
|
207
209
|
Returns:
|
|
208
210
|
Query string or None if not supported
|
|
209
211
|
"""
|
|
@@ -213,7 +215,7 @@ class LanguagePlugin(ABC):
|
|
|
213
215
|
def get_formatter_map(self) -> dict[str, str]:
|
|
214
216
|
"""
|
|
215
217
|
Return mapping of format types to formatter class names.
|
|
216
|
-
|
|
218
|
+
|
|
217
219
|
Returns:
|
|
218
220
|
Dictionary mapping format names to formatter classes
|
|
219
221
|
"""
|
|
@@ -222,7 +224,7 @@ class LanguagePlugin(ABC):
|
|
|
222
224
|
def get_element_categories(self) -> dict[str, list[str]]:
|
|
223
225
|
"""
|
|
224
226
|
Return element categories for HTML/CSS languages.
|
|
225
|
-
|
|
227
|
+
|
|
226
228
|
Returns:
|
|
227
229
|
Dictionary mapping category names to element lists
|
|
228
230
|
"""
|
|
@@ -66,6 +66,7 @@ class PluginManager:
|
|
|
66
66
|
final_plugins = list(unique_plugins.values())
|
|
67
67
|
# Only log if not in CLI mode (check if we're in quiet mode)
|
|
68
68
|
import os
|
|
69
|
+
|
|
69
70
|
log_level = os.environ.get("LOG_LEVEL", "WARNING")
|
|
70
71
|
if log_level != "ERROR":
|
|
71
72
|
log_info(f"Successfully loaded {len(final_plugins)} plugins")
|
|
@@ -32,7 +32,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
32
32
|
(declaration
|
|
33
33
|
value: (_) @property_value)
|
|
34
34
|
""",
|
|
35
|
-
|
|
36
35
|
# --- Selectors ---
|
|
37
36
|
"selector": """
|
|
38
37
|
(selectors
|
|
@@ -74,7 +73,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
74
73
|
"adjacent_sibling_selector": """
|
|
75
74
|
(adjacent_sibling_selector) @adjacent_sibling_selector
|
|
76
75
|
""",
|
|
77
|
-
|
|
78
76
|
# --- At-Rules ---
|
|
79
77
|
"at_rule": """
|
|
80
78
|
(at_rule) @at_rule
|
|
@@ -103,7 +101,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
103
101
|
"font_face_statement": """
|
|
104
102
|
(font_face_statement) @font_face_statement
|
|
105
103
|
""",
|
|
106
|
-
|
|
107
104
|
# --- Media Queries ---
|
|
108
105
|
"media_query": """
|
|
109
106
|
(media_query) @media_query
|
|
@@ -114,7 +111,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
114
111
|
"media_type": """
|
|
115
112
|
(media_type) @media_type
|
|
116
113
|
""",
|
|
117
|
-
|
|
118
114
|
# --- Values ---
|
|
119
115
|
"string_value": """
|
|
120
116
|
(string_value) @string_value
|
|
@@ -181,7 +177,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
181
177
|
(#match? @func_name "^hsla$")
|
|
182
178
|
arguments: (arguments) @hsla_args) @hsla
|
|
183
179
|
""",
|
|
184
|
-
|
|
185
180
|
# --- Units ---
|
|
186
181
|
"dimension": """
|
|
187
182
|
(dimension) @dimension
|
|
@@ -193,7 +188,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
193
188
|
(dimension
|
|
194
189
|
unit: (unit) @unit)
|
|
195
190
|
""",
|
|
196
|
-
|
|
197
191
|
# --- Layout Properties ---
|
|
198
192
|
"display": """
|
|
199
193
|
(declaration
|
|
@@ -237,7 +231,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
237
231
|
(#match? @prop_name "^z-index$")
|
|
238
232
|
value: (_) @z_index_value) @z_index
|
|
239
233
|
""",
|
|
240
|
-
|
|
241
234
|
# --- Box Model Properties ---
|
|
242
235
|
"width": """
|
|
243
236
|
(declaration
|
|
@@ -275,7 +268,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
275
268
|
(#match? @prop_name "^box-sizing$")
|
|
276
269
|
value: (_) @box_sizing_value) @box_sizing
|
|
277
270
|
""",
|
|
278
|
-
|
|
279
271
|
# --- Typography Properties ---
|
|
280
272
|
"font": """
|
|
281
273
|
(declaration
|
|
@@ -313,7 +305,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
313
305
|
(#match? @prop_name "^word-spacing$")
|
|
314
306
|
value: (_) @word_spacing_value) @word_spacing
|
|
315
307
|
""",
|
|
316
|
-
|
|
317
308
|
# --- Background Properties ---
|
|
318
309
|
"background": """
|
|
319
310
|
(declaration
|
|
@@ -321,7 +312,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
321
312
|
(#match? @prop_name "^background")
|
|
322
313
|
value: (_) @background_value) @background
|
|
323
314
|
""",
|
|
324
|
-
|
|
325
315
|
# --- Flexbox Properties ---
|
|
326
316
|
"flex": """
|
|
327
317
|
(declaration
|
|
@@ -347,7 +337,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
347
337
|
(#match? @prop_name "^align-content$")
|
|
348
338
|
value: (_) @align_content_value) @align_content
|
|
349
339
|
""",
|
|
350
|
-
|
|
351
340
|
# --- Grid Properties ---
|
|
352
341
|
"grid": """
|
|
353
342
|
(declaration
|
|
@@ -355,7 +344,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
355
344
|
(#match? @prop_name "^grid")
|
|
356
345
|
value: (_) @grid_value) @grid
|
|
357
346
|
""",
|
|
358
|
-
|
|
359
347
|
# --- Animation Properties ---
|
|
360
348
|
"animation": """
|
|
361
349
|
(declaration
|
|
@@ -375,12 +363,10 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
375
363
|
(#match? @prop_name "^transform")
|
|
376
364
|
value: (_) @transform_value) @transform
|
|
377
365
|
""",
|
|
378
|
-
|
|
379
366
|
# --- Comments ---
|
|
380
367
|
"comment": """
|
|
381
368
|
(comment) @comment
|
|
382
369
|
""",
|
|
383
|
-
|
|
384
370
|
# --- Custom Properties (CSS Variables) ---
|
|
385
371
|
"custom_property": """
|
|
386
372
|
(declaration
|
|
@@ -388,7 +374,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
388
374
|
(#match? @prop_name "^--")
|
|
389
375
|
value: (_) @custom_value) @custom_property
|
|
390
376
|
""",
|
|
391
|
-
|
|
392
377
|
# --- Important Declarations ---
|
|
393
378
|
"important": """
|
|
394
379
|
(declaration
|
|
@@ -396,7 +381,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
396
381
|
"!" @important_mark
|
|
397
382
|
"important" @important_keyword) @important
|
|
398
383
|
""",
|
|
399
|
-
|
|
400
384
|
# --- Keyframe Rules ---
|
|
401
385
|
"keyframe_block": """
|
|
402
386
|
(keyframe_block) @keyframe_block
|
|
@@ -410,7 +394,6 @@ CSS_QUERIES: dict[str, str] = {
|
|
|
410
394
|
"to": """
|
|
411
395
|
(to) @to
|
|
412
396
|
""",
|
|
413
|
-
|
|
414
397
|
# --- Name-only Extraction ---
|
|
415
398
|
"class_name": """
|
|
416
399
|
(class_selector
|
|
@@ -585,9 +568,7 @@ def get_css_query(name: str) -> str:
|
|
|
585
568
|
"""
|
|
586
569
|
if name not in CSS_QUERIES:
|
|
587
570
|
available = list(CSS_QUERIES.keys())
|
|
588
|
-
raise ValueError(
|
|
589
|
-
f"CSS query '{name}' does not exist. Available: {available}"
|
|
590
|
-
)
|
|
571
|
+
raise ValueError(f"CSS query '{name}' does not exist. Available: {available}")
|
|
591
572
|
|
|
592
573
|
return CSS_QUERIES[name]
|
|
593
574
|
|
|
@@ -631,4 +612,4 @@ def get_available_css_queries() -> list[str]:
|
|
|
631
612
|
Returns:
|
|
632
613
|
List of query names
|
|
633
614
|
"""
|
|
634
|
-
return list(CSS_QUERIES.keys())
|
|
615
|
+
return list(CSS_QUERIES.keys())
|