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
tree_sitter_analyzer/__init__.py
CHANGED
tree_sitter_analyzer/api.py
CHANGED
|
@@ -65,7 +65,7 @@ def analyze_file(
|
|
|
65
65
|
|
|
66
66
|
# Perform the analysis
|
|
67
67
|
analysis_result = engine.analyze_file(file_path, language)
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
# Convert AnalysisResult to expected API format (same as analyze_code)
|
|
70
70
|
result = {
|
|
71
71
|
"success": analysis_result.success,
|
|
@@ -375,15 +375,15 @@ def detect_language(file_path: str | Path) -> str:
|
|
|
375
375
|
# Handle invalid input
|
|
376
376
|
if not file_path:
|
|
377
377
|
return "unknown"
|
|
378
|
-
|
|
378
|
+
|
|
379
379
|
engine = get_engine()
|
|
380
380
|
# Use language_detector instead of language_registry
|
|
381
381
|
result = engine.language_detector.detect_from_extension(str(file_path))
|
|
382
|
-
|
|
382
|
+
|
|
383
383
|
# Ensure result is valid
|
|
384
384
|
if not result or result.strip() == "":
|
|
385
385
|
return "unknown"
|
|
386
|
-
|
|
386
|
+
|
|
387
387
|
return result
|
|
388
388
|
except Exception as e:
|
|
389
389
|
log_error(f"Failed to detect language for {file_path}: {e}")
|
|
@@ -5,7 +5,7 @@ CLI Argument Validator
|
|
|
5
5
|
Validates CLI argument combinations and provides clear error messages.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from typing import Any
|
|
8
|
+
from typing import Any
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class CLIArgumentValidator:
|
|
@@ -15,48 +15,60 @@ class CLIArgumentValidator:
|
|
|
15
15
|
"""Initialize the validator."""
|
|
16
16
|
pass
|
|
17
17
|
|
|
18
|
-
def validate_arguments(self, args: Any) ->
|
|
18
|
+
def validate_arguments(self, args: Any) -> str | None:
|
|
19
19
|
"""
|
|
20
20
|
Validate CLI argument combinations.
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
Args:
|
|
23
23
|
args: Parsed command line arguments
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
Returns:
|
|
26
26
|
None if valid, error message string if invalid
|
|
27
27
|
"""
|
|
28
28
|
# Check for --table and --query-key combination
|
|
29
|
-
table_specified =
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
table_specified = (
|
|
30
|
+
hasattr(args, "table") and args.table is not None and args.table != ""
|
|
31
|
+
)
|
|
32
|
+
query_key_specified = (
|
|
33
|
+
hasattr(args, "query_key")
|
|
34
|
+
and args.query_key is not None
|
|
35
|
+
and args.query_key != ""
|
|
36
|
+
)
|
|
37
|
+
|
|
32
38
|
if table_specified and query_key_specified:
|
|
33
39
|
return "--table and --query-key cannot be used together. Use --query-key with --filter instead."
|
|
34
|
-
|
|
40
|
+
|
|
35
41
|
# All validations passed
|
|
36
42
|
return None
|
|
37
43
|
|
|
38
|
-
def validate_table_query_exclusivity(self, args: Any) ->
|
|
44
|
+
def validate_table_query_exclusivity(self, args: Any) -> str | None:
|
|
39
45
|
"""
|
|
40
46
|
Validate that --table and --query-key are mutually exclusive.
|
|
41
|
-
|
|
47
|
+
|
|
42
48
|
Args:
|
|
43
49
|
args: Parsed command line arguments
|
|
44
|
-
|
|
50
|
+
|
|
45
51
|
Returns:
|
|
46
52
|
None if valid, error message string if invalid
|
|
47
53
|
"""
|
|
48
|
-
table_specified =
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
table_specified = (
|
|
55
|
+
hasattr(args, "table") and args.table is not None and args.table != ""
|
|
56
|
+
)
|
|
57
|
+
query_key_specified = (
|
|
58
|
+
hasattr(args, "query_key")
|
|
59
|
+
and args.query_key is not None
|
|
60
|
+
and args.query_key != ""
|
|
61
|
+
)
|
|
62
|
+
|
|
51
63
|
if table_specified and query_key_specified:
|
|
52
64
|
return "--table and --query-key cannot be used together. Use --query-key with --filter instead."
|
|
53
|
-
|
|
65
|
+
|
|
54
66
|
return None
|
|
55
67
|
|
|
56
68
|
def get_usage_examples(self) -> str:
|
|
57
69
|
"""
|
|
58
70
|
Get usage examples for correct argument combinations.
|
|
59
|
-
|
|
71
|
+
|
|
60
72
|
Returns:
|
|
61
73
|
String containing usage examples
|
|
62
74
|
"""
|
|
@@ -74,4 +86,4 @@ uv run python -m tree_sitter_analyzer examples/BigService.java --query-key metho
|
|
|
74
86
|
|
|
75
87
|
# Invalid combination (will cause error):
|
|
76
88
|
uv run python -m tree_sitter_analyzer examples/BigService.java --table full --query-key methods
|
|
77
|
-
"""
|
|
89
|
+
"""
|
|
@@ -14,8 +14,8 @@ from ...constants import (
|
|
|
14
14
|
ELEMENT_TYPE_VARIABLE,
|
|
15
15
|
get_element_type,
|
|
16
16
|
)
|
|
17
|
-
from ...output_manager import output_data, output_json, output_section
|
|
18
17
|
from ...formatters.language_formatter_factory import create_language_formatter
|
|
18
|
+
from ...output_manager import output_data, output_json, output_section
|
|
19
19
|
from .base_command import BaseCommand
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
@@ -163,8 +163,10 @@ class AdvancedCommand(BaseCommand):
|
|
|
163
163
|
formatter = create_language_formatter(analysis_result.language)
|
|
164
164
|
if formatter:
|
|
165
165
|
# Use language-specific formatter
|
|
166
|
-
output_format = getattr(self.args,
|
|
167
|
-
formatted_output = formatter.format_advanced(
|
|
166
|
+
output_format = getattr(self.args, "output_format", "json")
|
|
167
|
+
formatted_output = formatter.format_advanced(
|
|
168
|
+
self._convert_to_formatter_format(analysis_result), output_format
|
|
169
|
+
)
|
|
168
170
|
print(formatted_output)
|
|
169
171
|
return
|
|
170
172
|
|
|
@@ -220,7 +222,7 @@ class AdvancedCommand(BaseCommand):
|
|
|
220
222
|
"line_range": {
|
|
221
223
|
"start": getattr(element, "start_line", 0),
|
|
222
224
|
"end": getattr(element, "end_line", 0),
|
|
223
|
-
}
|
|
225
|
+
},
|
|
224
226
|
}
|
|
225
227
|
for element in analysis_result.elements
|
|
226
228
|
],
|
|
@@ -231,7 +233,7 @@ class AdvancedCommand(BaseCommand):
|
|
|
231
233
|
"language": analysis_result.language,
|
|
232
234
|
"file_path": analysis_result.file_path,
|
|
233
235
|
"analyzer_version": "2.0.0",
|
|
234
|
-
}
|
|
236
|
+
},
|
|
235
237
|
}
|
|
236
238
|
|
|
237
239
|
def _output_text_analysis(self, analysis_result: "AnalysisResult") -> None:
|
|
@@ -15,8 +15,8 @@ from ...constants import (
|
|
|
15
15
|
ELEMENT_TYPE_VARIABLE,
|
|
16
16
|
is_element_of_type,
|
|
17
17
|
)
|
|
18
|
-
from ...output_manager import output_data, output_json, output_section
|
|
19
18
|
from ...formatters.language_formatter_factory import create_language_formatter
|
|
19
|
+
from ...output_manager import output_data, output_json, output_section
|
|
20
20
|
from .base_command import BaseCommand
|
|
21
21
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
@@ -40,7 +40,9 @@ class StructureCommand(BaseCommand):
|
|
|
40
40
|
formatter = create_language_formatter(analysis_result.language)
|
|
41
41
|
if formatter:
|
|
42
42
|
# Use language-specific formatter
|
|
43
|
-
formatted_output = formatter.format_structure(
|
|
43
|
+
formatted_output = formatter.format_structure(
|
|
44
|
+
self._convert_to_formatter_format(analysis_result)
|
|
45
|
+
)
|
|
44
46
|
print(formatted_output)
|
|
45
47
|
return
|
|
46
48
|
|
|
@@ -58,7 +60,7 @@ class StructureCommand(BaseCommand):
|
|
|
58
60
|
def _convert_to_formatter_format(self, analysis_result: "AnalysisResult") -> dict:
|
|
59
61
|
"""Convert AnalysisResult to format expected by formatters."""
|
|
60
62
|
from ...constants import get_element_type
|
|
61
|
-
|
|
63
|
+
|
|
62
64
|
return {
|
|
63
65
|
"file_path": analysis_result.file_path,
|
|
64
66
|
"language": analysis_result.language,
|
|
@@ -82,7 +84,7 @@ class StructureCommand(BaseCommand):
|
|
|
82
84
|
"line_range": {
|
|
83
85
|
"start": getattr(element, "start_line", 0),
|
|
84
86
|
"end": getattr(element, "end_line", 0),
|
|
85
|
-
}
|
|
87
|
+
},
|
|
86
88
|
}
|
|
87
89
|
for element in analysis_result.elements
|
|
88
90
|
],
|
|
@@ -91,7 +93,7 @@ class StructureCommand(BaseCommand):
|
|
|
91
93
|
"language": analysis_result.language,
|
|
92
94
|
"file_path": analysis_result.file_path,
|
|
93
95
|
"analyzer_version": "2.0.0",
|
|
94
|
-
}
|
|
96
|
+
},
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
def _convert_to_legacy_format(self, analysis_result: "AnalysisResult") -> dict:
|
|
@@ -14,8 +14,8 @@ from ...constants import (
|
|
|
14
14
|
ELEMENT_TYPE_VARIABLE,
|
|
15
15
|
is_element_of_type,
|
|
16
16
|
)
|
|
17
|
-
from ...output_manager import output_data, output_json, output_section
|
|
18
17
|
from ...formatters.language_formatter_factory import create_language_formatter
|
|
18
|
+
from ...output_manager import output_data, output_json, output_section
|
|
19
19
|
from .base_command import BaseCommand
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
@@ -39,7 +39,9 @@ class SummaryCommand(BaseCommand):
|
|
|
39
39
|
formatter = create_language_formatter(analysis_result.language)
|
|
40
40
|
if formatter:
|
|
41
41
|
# Use language-specific formatter
|
|
42
|
-
formatted_output = formatter.format_summary(
|
|
42
|
+
formatted_output = formatter.format_summary(
|
|
43
|
+
self._convert_to_formatter_format(analysis_result)
|
|
44
|
+
)
|
|
43
45
|
print(formatted_output)
|
|
44
46
|
return
|
|
45
47
|
|
|
@@ -106,10 +108,12 @@ class SummaryCommand(BaseCommand):
|
|
|
106
108
|
else:
|
|
107
109
|
self._output_text_format(summary_data, requested_types)
|
|
108
110
|
|
|
109
|
-
def _convert_to_formatter_format(
|
|
111
|
+
def _convert_to_formatter_format(
|
|
112
|
+
self, analysis_result: "AnalysisResult"
|
|
113
|
+
) -> dict[str, Any]:
|
|
110
114
|
"""Convert AnalysisResult to format expected by formatters."""
|
|
111
115
|
from ...constants import get_element_type
|
|
112
|
-
|
|
116
|
+
|
|
113
117
|
return {
|
|
114
118
|
"file_path": analysis_result.file_path,
|
|
115
119
|
"language": analysis_result.language,
|
|
@@ -133,7 +137,7 @@ class SummaryCommand(BaseCommand):
|
|
|
133
137
|
"line_range": {
|
|
134
138
|
"start": getattr(element, "start_line", 0),
|
|
135
139
|
"end": getattr(element, "end_line", 0),
|
|
136
|
-
}
|
|
140
|
+
},
|
|
137
141
|
}
|
|
138
142
|
for element in analysis_result.elements
|
|
139
143
|
],
|
|
@@ -142,7 +146,7 @@ class SummaryCommand(BaseCommand):
|
|
|
142
146
|
"language": analysis_result.language,
|
|
143
147
|
"file_path": analysis_result.file_path,
|
|
144
148
|
"analyzer_version": "2.0.0",
|
|
145
|
-
}
|
|
149
|
+
},
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
def _output_text_format(self, summary_data: dict, requested_types: list) -> None:
|
|
@@ -16,9 +16,9 @@ from ...constants import (
|
|
|
16
16
|
ELEMENT_TYPE_VARIABLE,
|
|
17
17
|
get_element_type,
|
|
18
18
|
)
|
|
19
|
-
from ...output_manager import output_error, output_info
|
|
20
|
-
from ...table_formatter import create_table_formatter
|
|
21
19
|
from ...formatters.language_formatter_factory import create_language_formatter
|
|
20
|
+
from ...output_manager import output_error
|
|
21
|
+
from ...table_formatter import create_table_formatter
|
|
22
22
|
from .base_command import BaseCommand
|
|
23
23
|
|
|
24
24
|
|
|
@@ -41,8 +41,10 @@ class TableCommand(BaseCommand):
|
|
|
41
41
|
formatter = create_language_formatter(analysis_result.language)
|
|
42
42
|
if formatter:
|
|
43
43
|
# Use language-specific formatter
|
|
44
|
-
table_type = getattr(self.args,
|
|
45
|
-
formatted_output = formatter.format_table(
|
|
44
|
+
table_type = getattr(self.args, "table", "full")
|
|
45
|
+
formatted_output = formatter.format_table(
|
|
46
|
+
self._convert_to_formatter_format(analysis_result), table_type
|
|
47
|
+
)
|
|
46
48
|
self._output_table(formatted_output)
|
|
47
49
|
return 0
|
|
48
50
|
|
|
@@ -68,7 +70,6 @@ class TableCommand(BaseCommand):
|
|
|
68
70
|
output_error(f"An error occurred during table format analysis: {e}")
|
|
69
71
|
return 1
|
|
70
72
|
|
|
71
|
-
|
|
72
73
|
def _convert_to_formatter_format(self, analysis_result: Any) -> dict[str, Any]:
|
|
73
74
|
"""Convert AnalysisResult to format expected by formatters."""
|
|
74
75
|
return {
|
|
@@ -94,7 +95,7 @@ class TableCommand(BaseCommand):
|
|
|
94
95
|
"line_range": {
|
|
95
96
|
"start": getattr(element, "start_line", 0),
|
|
96
97
|
"end": getattr(element, "end_line", 0),
|
|
97
|
-
}
|
|
98
|
+
},
|
|
98
99
|
}
|
|
99
100
|
for element in analysis_result.elements
|
|
100
101
|
],
|
|
@@ -103,7 +104,7 @@ class TableCommand(BaseCommand):
|
|
|
103
104
|
"language": analysis_result.language,
|
|
104
105
|
"file_path": analysis_result.file_path,
|
|
105
106
|
"analyzer_version": "2.0.0",
|
|
106
|
-
}
|
|
107
|
+
},
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
def _convert_to_structure_format(
|
|
@@ -101,7 +101,7 @@ class ShowLanguagesCommand(InfoCommand):
|
|
|
101
101
|
info = detector.get_language_info(language)
|
|
102
102
|
extensions = ", ".join(info["extensions"][:5])
|
|
103
103
|
if len(info["extensions"]) > 5:
|
|
104
|
-
extensions += f", ... ({len(info['extensions'])-5} more)"
|
|
104
|
+
extensions += f", ... ({len(info['extensions']) - 5} more)"
|
|
105
105
|
output_list(f" {language:<12} - Extensions: {extensions}")
|
|
106
106
|
return 0
|
|
107
107
|
|
tree_sitter_analyzer/cli_main.py
CHANGED
|
@@ -7,6 +7,8 @@ import os
|
|
|
7
7
|
import sys
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
|
+
from .cli.argument_validator import CLIArgumentValidator
|
|
11
|
+
|
|
10
12
|
# Import command classes
|
|
11
13
|
from .cli.commands import (
|
|
12
14
|
AdvancedCommand,
|
|
@@ -25,7 +27,6 @@ from .cli.info_commands import (
|
|
|
25
27
|
)
|
|
26
28
|
from .output_manager import output_error, output_info, output_list
|
|
27
29
|
from .query_loader import query_loader
|
|
28
|
-
from .cli.argument_validator import CLIArgumentValidator
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
class CLICommandFactory:
|
|
@@ -34,7 +35,7 @@ class CLICommandFactory:
|
|
|
34
35
|
@staticmethod
|
|
35
36
|
def create_command(args: argparse.Namespace) -> Any:
|
|
36
37
|
"""Create appropriate command based on arguments."""
|
|
37
|
-
|
|
38
|
+
|
|
38
39
|
# Validate argument combinations first
|
|
39
40
|
validator = CLIArgumentValidator()
|
|
40
41
|
validation_error = validator.validate_arguments(args)
|
|
@@ -207,7 +207,7 @@ class UnifiedAnalysisEngine:
|
|
|
207
207
|
_instances: dict[str, "UnifiedAnalysisEngine"] = {}
|
|
208
208
|
_lock: threading.Lock = threading.Lock()
|
|
209
209
|
|
|
210
|
-
def __new__(cls, project_root: str = None) -> "UnifiedAnalysisEngine":
|
|
210
|
+
def __new__(cls, project_root: str | None = None) -> "UnifiedAnalysisEngine":
|
|
211
211
|
"""Singleton instance sharing (project_root aware)"""
|
|
212
212
|
# Create a key based on project_root for different instances
|
|
213
213
|
instance_key = project_root or "default"
|
|
@@ -218,13 +218,13 @@ class UnifiedAnalysisEngine:
|
|
|
218
218
|
instance = super().__new__(cls)
|
|
219
219
|
cls._instances[instance_key] = instance
|
|
220
220
|
# Mark as not initialized for this instance
|
|
221
|
-
instance._initialized = False
|
|
221
|
+
instance._initialized: bool = False
|
|
222
222
|
|
|
223
223
|
return cls._instances[instance_key]
|
|
224
224
|
|
|
225
|
-
def __init__(self, project_root: str = None) -> None:
|
|
225
|
+
def __init__(self, project_root: str | None = None) -> None:
|
|
226
226
|
"""Initialize (executed only once per instance)"""
|
|
227
|
-
if hasattr(self, "_initialized") and self
|
|
227
|
+
if hasattr(self, "_initialized") and getattr(self, "_initialized", False):
|
|
228
228
|
return
|
|
229
229
|
|
|
230
230
|
self._cache_service = CacheService()
|
|
@@ -566,7 +566,7 @@ class MockLanguagePlugin:
|
|
|
566
566
|
)
|
|
567
567
|
|
|
568
568
|
|
|
569
|
-
def get_analysis_engine(project_root: str = None) -> UnifiedAnalysisEngine:
|
|
569
|
+
def get_analysis_engine(project_root: str | None = None) -> UnifiedAnalysisEngine:
|
|
570
570
|
"""
|
|
571
571
|
Get unified analysis engine instance
|
|
572
572
|
|
|
@@ -20,7 +20,7 @@ from typing import Any
|
|
|
20
20
|
|
|
21
21
|
from cachetools import LRUCache, TTLCache
|
|
22
22
|
|
|
23
|
-
from ..utils import log_debug,
|
|
23
|
+
from ..utils import log_debug, log_info
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
@dataclass(frozen=True)
|
|
@@ -227,6 +227,7 @@ class CacheService:
|
|
|
227
227
|
|
|
228
228
|
# Only log if not in quiet mode (check log level)
|
|
229
229
|
import logging
|
|
230
|
+
|
|
230
231
|
if logging.getLogger("tree_sitter_analyzer").level <= logging.INFO:
|
|
231
232
|
log_info("All caches cleared")
|
|
232
233
|
|
|
@@ -320,6 +321,7 @@ class CacheService:
|
|
|
320
321
|
try:
|
|
321
322
|
# Only clear if not in shutdown mode
|
|
322
323
|
import sys
|
|
324
|
+
|
|
323
325
|
if sys.meta_path is not None: # Check if Python is not shutting down
|
|
324
326
|
# Clear caches without logging to avoid shutdown issues
|
|
325
327
|
with self._lock:
|
|
@@ -78,14 +78,22 @@ class QueryExecutor:
|
|
|
78
78
|
if not language_name:
|
|
79
79
|
language_name = getattr(language, "_name", None)
|
|
80
80
|
if not language_name:
|
|
81
|
-
language_name =
|
|
82
|
-
|
|
81
|
+
language_name = (
|
|
82
|
+
str(language).split(".")[-1]
|
|
83
|
+
if hasattr(language, "__class__")
|
|
84
|
+
else None
|
|
85
|
+
)
|
|
86
|
+
|
|
83
87
|
# Ensure we have a valid language name
|
|
84
|
-
if
|
|
88
|
+
if (
|
|
89
|
+
not language_name
|
|
90
|
+
or language_name.strip() == ""
|
|
91
|
+
or language_name == "None"
|
|
92
|
+
):
|
|
85
93
|
language_name = "unknown"
|
|
86
94
|
else:
|
|
87
95
|
language_name = language_name.strip().lower()
|
|
88
|
-
|
|
96
|
+
|
|
89
97
|
query_string = self._query_loader.get_query(language_name, query_name)
|
|
90
98
|
if query_string is None:
|
|
91
99
|
return self._create_error_result(
|
|
@@ -244,7 +252,11 @@ class QueryExecutor:
|
|
|
244
252
|
if isinstance(capture, tuple) and len(capture) == 2:
|
|
245
253
|
node, name = capture
|
|
246
254
|
# Handle dictionary format (legacy API compatibility)
|
|
247
|
-
elif
|
|
255
|
+
elif (
|
|
256
|
+
isinstance(capture, dict)
|
|
257
|
+
and "node" in capture
|
|
258
|
+
and "name" in capture
|
|
259
|
+
):
|
|
248
260
|
node = capture["node"]
|
|
249
261
|
name = capture["name"]
|
|
250
262
|
else:
|
|
@@ -298,7 +298,7 @@ class QueryService:
|
|
|
298
298
|
node_type = getattr(node, "type", "")
|
|
299
299
|
if not isinstance(node_type, str):
|
|
300
300
|
node_type = str(node_type)
|
|
301
|
-
|
|
301
|
+
|
|
302
302
|
# Generic node type matching (support both singular and plural forms)
|
|
303
303
|
if query_key in ("function", "functions") and "function" in node_type:
|
|
304
304
|
captures.append((node, query_key))
|
|
@@ -72,9 +72,9 @@ class EncodingCache:
|
|
|
72
72
|
max_size: Maximum number of cached entries
|
|
73
73
|
ttl_seconds: Time-to-live for cache entries in seconds
|
|
74
74
|
"""
|
|
75
|
-
self._cache: dict[
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
self._cache: dict[
|
|
76
|
+
str, tuple[str, float]
|
|
77
|
+
] = {} # file_path -> (encoding, timestamp)
|
|
78
78
|
self._lock = threading.RLock()
|
|
79
79
|
self._max_size = max_size
|
|
80
80
|
self._ttl_seconds = ttl_seconds
|