tree-sitter-analyzer 0.6.2__py3-none-any.whl → 0.8.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 +132 -132
- tree_sitter_analyzer/__main__.py +11 -11
- tree_sitter_analyzer/api.py +533 -533
- tree_sitter_analyzer/cli/__init__.py +39 -39
- tree_sitter_analyzer/cli/__main__.py +12 -12
- tree_sitter_analyzer/cli/commands/__init__.py +26 -26
- tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
- tree_sitter_analyzer/cli/commands/base_command.py +160 -160
- tree_sitter_analyzer/cli/commands/default_command.py +18 -18
- tree_sitter_analyzer/cli/commands/partial_read_command.py +141 -141
- tree_sitter_analyzer/cli/commands/query_command.py +81 -81
- tree_sitter_analyzer/cli/commands/structure_command.py +138 -138
- tree_sitter_analyzer/cli/commands/summary_command.py +101 -101
- tree_sitter_analyzer/cli/commands/table_command.py +235 -235
- tree_sitter_analyzer/cli/info_commands.py +121 -121
- tree_sitter_analyzer/cli_main.py +297 -297
- tree_sitter_analyzer/core/__init__.py +15 -15
- tree_sitter_analyzer/core/analysis_engine.py +555 -555
- tree_sitter_analyzer/core/cache_service.py +320 -320
- tree_sitter_analyzer/core/engine.py +566 -566
- tree_sitter_analyzer/core/parser.py +293 -293
- tree_sitter_analyzer/encoding_utils.py +459 -459
- tree_sitter_analyzer/exceptions.py +406 -337
- tree_sitter_analyzer/file_handler.py +210 -210
- tree_sitter_analyzer/formatters/__init__.py +1 -1
- tree_sitter_analyzer/formatters/base_formatter.py +167 -167
- tree_sitter_analyzer/formatters/formatter_factory.py +78 -78
- tree_sitter_analyzer/interfaces/__init__.py +9 -9
- tree_sitter_analyzer/interfaces/cli.py +528 -528
- tree_sitter_analyzer/interfaces/cli_adapter.py +343 -343
- tree_sitter_analyzer/interfaces/mcp_adapter.py +206 -206
- tree_sitter_analyzer/interfaces/mcp_server.py +425 -405
- tree_sitter_analyzer/languages/__init__.py +10 -10
- tree_sitter_analyzer/languages/javascript_plugin.py +446 -446
- tree_sitter_analyzer/languages/python_plugin.py +755 -755
- tree_sitter_analyzer/mcp/__init__.py +31 -31
- tree_sitter_analyzer/mcp/resources/__init__.py +44 -44
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +209 -209
- tree_sitter_analyzer/mcp/server.py +346 -333
- tree_sitter_analyzer/mcp/tools/__init__.py +30 -30
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +654 -654
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +247 -247
- tree_sitter_analyzer/mcp/tools/base_tool.py +54 -54
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +300 -300
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +362 -362
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +543 -543
- tree_sitter_analyzer/mcp/utils/__init__.py +107 -107
- tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
- tree_sitter_analyzer/output_manager.py +253 -253
- tree_sitter_analyzer/plugins/__init__.py +280 -280
- tree_sitter_analyzer/plugins/base.py +529 -529
- tree_sitter_analyzer/plugins/manager.py +379 -379
- tree_sitter_analyzer/queries/__init__.py +26 -26
- tree_sitter_analyzer/queries/java.py +391 -391
- tree_sitter_analyzer/queries/javascript.py +148 -148
- tree_sitter_analyzer/queries/python.py +285 -285
- tree_sitter_analyzer/queries/typescript.py +229 -229
- tree_sitter_analyzer/query_loader.py +257 -257
- tree_sitter_analyzer/security/__init__.py +22 -0
- tree_sitter_analyzer/security/boundary_manager.py +237 -0
- tree_sitter_analyzer/security/regex_checker.py +292 -0
- tree_sitter_analyzer/security/validator.py +224 -0
- tree_sitter_analyzer/table_formatter.py +652 -473
- tree_sitter_analyzer/utils.py +277 -277
- {tree_sitter_analyzer-0.6.2.dist-info → tree_sitter_analyzer-0.8.0.dist-info}/METADATA +4 -1
- tree_sitter_analyzer-0.8.0.dist-info/RECORD +76 -0
- tree_sitter_analyzer-0.6.2.dist-info/RECORD +0 -72
- {tree_sitter_analyzer-0.6.2.dist-info → tree_sitter_analyzer-0.8.0.dist-info}/WHEEL +0 -0
- {tree_sitter_analyzer-0.6.2.dist-info → tree_sitter_analyzer-0.8.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Default Command
|
|
4
|
-
|
|
5
|
-
Handles default analysis when no specific command is specified.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from ...output_manager import output_error
|
|
9
|
-
from .base_command import BaseCommand
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class DefaultCommand(BaseCommand):
|
|
13
|
-
"""Default command that shows error when no specific command is given."""
|
|
14
|
-
|
|
15
|
-
async def execute_async(self, language: str) -> int:
|
|
16
|
-
"""Execute default command - show error for missing options."""
|
|
17
|
-
output_error("ERROR: Please specify a query or --advanced option")
|
|
18
|
-
return 1
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Default Command
|
|
4
|
+
|
|
5
|
+
Handles default analysis when no specific command is specified.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from ...output_manager import output_error
|
|
9
|
+
from .base_command import BaseCommand
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DefaultCommand(BaseCommand):
|
|
13
|
+
"""Default command that shows error when no specific command is given."""
|
|
14
|
+
|
|
15
|
+
async def execute_async(self, language: str) -> int:
|
|
16
|
+
"""Execute default command - show error for missing options."""
|
|
17
|
+
output_error("ERROR: Please specify a query or --advanced option")
|
|
18
|
+
return 1
|
|
@@ -1,141 +1,141 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Partial Read Command
|
|
4
|
-
|
|
5
|
-
Handles partial file reading functionality, extracting specified line ranges.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from typing import TYPE_CHECKING, Any
|
|
9
|
-
|
|
10
|
-
from ...file_handler import read_file_partial
|
|
11
|
-
from ...output_manager import output_data, output_json, output_section
|
|
12
|
-
from .base_command import BaseCommand
|
|
13
|
-
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
pass
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class PartialReadCommand(BaseCommand):
|
|
19
|
-
"""Command for reading partial file content by line range."""
|
|
20
|
-
|
|
21
|
-
def __init__(self, args: Any) -> None:
|
|
22
|
-
"""Initialize with arguments but skip base class analysis engine setup."""
|
|
23
|
-
self.args = args
|
|
24
|
-
# Don't call super().__init__() to avoid unnecessary analysis engine setup
|
|
25
|
-
|
|
26
|
-
def validate_file(self) -> bool:
|
|
27
|
-
"""Validate input file exists and is accessible."""
|
|
28
|
-
if not hasattr(self.args, "file_path") or not self.args.file_path:
|
|
29
|
-
from ...output_manager import output_error
|
|
30
|
-
|
|
31
|
-
output_error("ERROR: File path not specified.")
|
|
32
|
-
return False
|
|
33
|
-
|
|
34
|
-
import os
|
|
35
|
-
|
|
36
|
-
if not os.path.exists(self.args.file_path):
|
|
37
|
-
from ...output_manager import output_error
|
|
38
|
-
|
|
39
|
-
output_error(f"ERROR: File not found: {self.args.file_path}")
|
|
40
|
-
return False
|
|
41
|
-
|
|
42
|
-
return True
|
|
43
|
-
|
|
44
|
-
def execute(self) -> int:
|
|
45
|
-
"""
|
|
46
|
-
Execute partial read command.
|
|
47
|
-
|
|
48
|
-
Returns:
|
|
49
|
-
int: Exit code (0 for success, 1 for failure)
|
|
50
|
-
"""
|
|
51
|
-
# Validate inputs
|
|
52
|
-
if not self.validate_file():
|
|
53
|
-
return 1
|
|
54
|
-
|
|
55
|
-
# Validate partial read arguments
|
|
56
|
-
if not self.args.start_line:
|
|
57
|
-
from ...output_manager import output_error
|
|
58
|
-
|
|
59
|
-
output_error("ERROR: --start-line is required")
|
|
60
|
-
return 1
|
|
61
|
-
|
|
62
|
-
if self.args.start_line < 1:
|
|
63
|
-
from ...output_manager import output_error
|
|
64
|
-
|
|
65
|
-
output_error("ERROR: --start-line must be 1 or greater")
|
|
66
|
-
return 1
|
|
67
|
-
|
|
68
|
-
if self.args.end_line and self.args.end_line < self.args.start_line:
|
|
69
|
-
from ...output_manager import output_error
|
|
70
|
-
|
|
71
|
-
output_error(
|
|
72
|
-
"ERROR: --end-line must be greater than or equal to --start-line"
|
|
73
|
-
)
|
|
74
|
-
return 1
|
|
75
|
-
|
|
76
|
-
# Read partial content
|
|
77
|
-
try:
|
|
78
|
-
partial_content = read_file_partial(
|
|
79
|
-
self.args.file_path,
|
|
80
|
-
start_line=self.args.start_line,
|
|
81
|
-
end_line=getattr(self.args, "end_line", None),
|
|
82
|
-
start_column=getattr(self.args, "start_column", None),
|
|
83
|
-
end_column=getattr(self.args, "end_column", None),
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
if partial_content is None:
|
|
87
|
-
from ...output_manager import output_error
|
|
88
|
-
|
|
89
|
-
output_error("ERROR: Failed to read file partially")
|
|
90
|
-
return 1
|
|
91
|
-
|
|
92
|
-
# Output the result
|
|
93
|
-
self._output_partial_content(partial_content)
|
|
94
|
-
return 0
|
|
95
|
-
|
|
96
|
-
except Exception as e:
|
|
97
|
-
from ...output_manager import output_error
|
|
98
|
-
|
|
99
|
-
output_error(f"ERROR: Failed to read file partially: {e}")
|
|
100
|
-
return 1
|
|
101
|
-
|
|
102
|
-
def _output_partial_content(self, content: str) -> None:
|
|
103
|
-
"""Output the partial content in the specified format."""
|
|
104
|
-
# Build result data
|
|
105
|
-
result_data = {
|
|
106
|
-
"file_path": self.args.file_path,
|
|
107
|
-
"range": {
|
|
108
|
-
"start_line": self.args.start_line,
|
|
109
|
-
"end_line": getattr(self.args, "end_line", None),
|
|
110
|
-
"start_column": getattr(self.args, "start_column", None),
|
|
111
|
-
"end_column": getattr(self.args, "end_column", None),
|
|
112
|
-
},
|
|
113
|
-
"content": content,
|
|
114
|
-
"content_length": len(content),
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
# Build range info for header
|
|
118
|
-
range_info = f"Line {self.args.start_line}"
|
|
119
|
-
if hasattr(self.args, "end_line") and self.args.end_line:
|
|
120
|
-
range_info += f"-{self.args.end_line}"
|
|
121
|
-
|
|
122
|
-
# Output format selection
|
|
123
|
-
output_format = getattr(self.args, "output_format", "text")
|
|
124
|
-
|
|
125
|
-
if output_format == "json":
|
|
126
|
-
# Pure JSON output
|
|
127
|
-
output_json(result_data)
|
|
128
|
-
else:
|
|
129
|
-
# Human-readable format with header
|
|
130
|
-
output_section("Partial Read Result")
|
|
131
|
-
output_data(f"File: {self.args.file_path}")
|
|
132
|
-
output_data(f"Range: {range_info}")
|
|
133
|
-
output_data(f"Characters read: {len(content)}")
|
|
134
|
-
output_data("") # Empty line for separation
|
|
135
|
-
|
|
136
|
-
# Output the actual content
|
|
137
|
-
print(content, end="") # Use print to avoid extra formatting
|
|
138
|
-
|
|
139
|
-
async def execute_async(self, language: str) -> int:
|
|
140
|
-
"""Not used for partial read command."""
|
|
141
|
-
return self.execute()
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Partial Read Command
|
|
4
|
+
|
|
5
|
+
Handles partial file reading functionality, extracting specified line ranges.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
9
|
+
|
|
10
|
+
from ...file_handler import read_file_partial
|
|
11
|
+
from ...output_manager import output_data, output_json, output_section
|
|
12
|
+
from .base_command import BaseCommand
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PartialReadCommand(BaseCommand):
|
|
19
|
+
"""Command for reading partial file content by line range."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, args: Any) -> None:
|
|
22
|
+
"""Initialize with arguments but skip base class analysis engine setup."""
|
|
23
|
+
self.args = args
|
|
24
|
+
# Don't call super().__init__() to avoid unnecessary analysis engine setup
|
|
25
|
+
|
|
26
|
+
def validate_file(self) -> bool:
|
|
27
|
+
"""Validate input file exists and is accessible."""
|
|
28
|
+
if not hasattr(self.args, "file_path") or not self.args.file_path:
|
|
29
|
+
from ...output_manager import output_error
|
|
30
|
+
|
|
31
|
+
output_error("ERROR: File path not specified.")
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
import os
|
|
35
|
+
|
|
36
|
+
if not os.path.exists(self.args.file_path):
|
|
37
|
+
from ...output_manager import output_error
|
|
38
|
+
|
|
39
|
+
output_error(f"ERROR: File not found: {self.args.file_path}")
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
def execute(self) -> int:
|
|
45
|
+
"""
|
|
46
|
+
Execute partial read command.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
int: Exit code (0 for success, 1 for failure)
|
|
50
|
+
"""
|
|
51
|
+
# Validate inputs
|
|
52
|
+
if not self.validate_file():
|
|
53
|
+
return 1
|
|
54
|
+
|
|
55
|
+
# Validate partial read arguments
|
|
56
|
+
if not self.args.start_line:
|
|
57
|
+
from ...output_manager import output_error
|
|
58
|
+
|
|
59
|
+
output_error("ERROR: --start-line is required")
|
|
60
|
+
return 1
|
|
61
|
+
|
|
62
|
+
if self.args.start_line < 1:
|
|
63
|
+
from ...output_manager import output_error
|
|
64
|
+
|
|
65
|
+
output_error("ERROR: --start-line must be 1 or greater")
|
|
66
|
+
return 1
|
|
67
|
+
|
|
68
|
+
if self.args.end_line and self.args.end_line < self.args.start_line:
|
|
69
|
+
from ...output_manager import output_error
|
|
70
|
+
|
|
71
|
+
output_error(
|
|
72
|
+
"ERROR: --end-line must be greater than or equal to --start-line"
|
|
73
|
+
)
|
|
74
|
+
return 1
|
|
75
|
+
|
|
76
|
+
# Read partial content
|
|
77
|
+
try:
|
|
78
|
+
partial_content = read_file_partial(
|
|
79
|
+
self.args.file_path,
|
|
80
|
+
start_line=self.args.start_line,
|
|
81
|
+
end_line=getattr(self.args, "end_line", None),
|
|
82
|
+
start_column=getattr(self.args, "start_column", None),
|
|
83
|
+
end_column=getattr(self.args, "end_column", None),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if partial_content is None:
|
|
87
|
+
from ...output_manager import output_error
|
|
88
|
+
|
|
89
|
+
output_error("ERROR: Failed to read file partially")
|
|
90
|
+
return 1
|
|
91
|
+
|
|
92
|
+
# Output the result
|
|
93
|
+
self._output_partial_content(partial_content)
|
|
94
|
+
return 0
|
|
95
|
+
|
|
96
|
+
except Exception as e:
|
|
97
|
+
from ...output_manager import output_error
|
|
98
|
+
|
|
99
|
+
output_error(f"ERROR: Failed to read file partially: {e}")
|
|
100
|
+
return 1
|
|
101
|
+
|
|
102
|
+
def _output_partial_content(self, content: str) -> None:
|
|
103
|
+
"""Output the partial content in the specified format."""
|
|
104
|
+
# Build result data
|
|
105
|
+
result_data = {
|
|
106
|
+
"file_path": self.args.file_path,
|
|
107
|
+
"range": {
|
|
108
|
+
"start_line": self.args.start_line,
|
|
109
|
+
"end_line": getattr(self.args, "end_line", None),
|
|
110
|
+
"start_column": getattr(self.args, "start_column", None),
|
|
111
|
+
"end_column": getattr(self.args, "end_column", None),
|
|
112
|
+
},
|
|
113
|
+
"content": content,
|
|
114
|
+
"content_length": len(content),
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Build range info for header
|
|
118
|
+
range_info = f"Line {self.args.start_line}"
|
|
119
|
+
if hasattr(self.args, "end_line") and self.args.end_line:
|
|
120
|
+
range_info += f"-{self.args.end_line}"
|
|
121
|
+
|
|
122
|
+
# Output format selection
|
|
123
|
+
output_format = getattr(self.args, "output_format", "text")
|
|
124
|
+
|
|
125
|
+
if output_format == "json":
|
|
126
|
+
# Pure JSON output
|
|
127
|
+
output_json(result_data)
|
|
128
|
+
else:
|
|
129
|
+
# Human-readable format with header
|
|
130
|
+
output_section("Partial Read Result")
|
|
131
|
+
output_data(f"File: {self.args.file_path}")
|
|
132
|
+
output_data(f"Range: {range_info}")
|
|
133
|
+
output_data(f"Characters read: {len(content)}")
|
|
134
|
+
output_data("") # Empty line for separation
|
|
135
|
+
|
|
136
|
+
# Output the actual content
|
|
137
|
+
print(content, end="") # Use print to avoid extra formatting
|
|
138
|
+
|
|
139
|
+
async def execute_async(self, language: str) -> int:
|
|
140
|
+
"""Not used for partial read command."""
|
|
141
|
+
return self.execute()
|
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Query Command
|
|
4
|
-
|
|
5
|
-
Handles query execution functionality.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from ...output_manager import output_data, output_error, output_info, output_json
|
|
9
|
-
from ...query_loader import query_loader
|
|
10
|
-
from .base_command import BaseCommand
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class QueryCommand(BaseCommand):
|
|
14
|
-
"""Command for executing queries."""
|
|
15
|
-
|
|
16
|
-
async def execute_async(self, language: str) -> int:
|
|
17
|
-
# Get the query to execute
|
|
18
|
-
query_to_execute = None
|
|
19
|
-
|
|
20
|
-
if hasattr(self.args, "query_key") and self.args.query_key:
|
|
21
|
-
try:
|
|
22
|
-
query_to_execute = query_loader.get_query(language, self.args.query_key)
|
|
23
|
-
if query_to_execute is None:
|
|
24
|
-
output_error(
|
|
25
|
-
f"ERROR: Query '{self.args.query_key}' not found for language '{language}'"
|
|
26
|
-
)
|
|
27
|
-
return 1
|
|
28
|
-
except ValueError as e:
|
|
29
|
-
output_error(f"ERROR: {e}")
|
|
30
|
-
return 1
|
|
31
|
-
elif hasattr(self.args, "query_string") and self.args.query_string:
|
|
32
|
-
query_to_execute = self.args.query_string
|
|
33
|
-
|
|
34
|
-
if not query_to_execute:
|
|
35
|
-
output_error("ERROR: No query specified.")
|
|
36
|
-
return 1
|
|
37
|
-
|
|
38
|
-
# Perform analysis
|
|
39
|
-
analysis_result = await self.analyze_file(language)
|
|
40
|
-
if not analysis_result:
|
|
41
|
-
return 1
|
|
42
|
-
|
|
43
|
-
# Process query results
|
|
44
|
-
results = []
|
|
45
|
-
if hasattr(analysis_result, "query_results") and analysis_result.query_results:
|
|
46
|
-
results = analysis_result.query_results.get("captures", [])
|
|
47
|
-
else:
|
|
48
|
-
# Create basic results from elements
|
|
49
|
-
if hasattr(analysis_result, "elements") and analysis_result.elements:
|
|
50
|
-
for element in analysis_result.elements:
|
|
51
|
-
results.append(
|
|
52
|
-
{
|
|
53
|
-
"capture_name": getattr(
|
|
54
|
-
element, "__class__", type(element)
|
|
55
|
-
).__name__.lower(),
|
|
56
|
-
"node_type": getattr(
|
|
57
|
-
element, "__class__", type(element)
|
|
58
|
-
).__name__,
|
|
59
|
-
"start_line": getattr(element, "start_line", 0),
|
|
60
|
-
"end_line": getattr(element, "end_line", 0),
|
|
61
|
-
"content": getattr(element, "name", str(element)),
|
|
62
|
-
}
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
# Output results
|
|
66
|
-
if results:
|
|
67
|
-
if self.args.output_format == "json":
|
|
68
|
-
output_json(results)
|
|
69
|
-
else:
|
|
70
|
-
for i, query_result in enumerate(results, 1):
|
|
71
|
-
output_data(
|
|
72
|
-
f"\n{i}. {query_result['capture_name']} ({query_result['node_type']})"
|
|
73
|
-
)
|
|
74
|
-
output_data(
|
|
75
|
-
f" Position: Line {query_result['start_line']}-{query_result['end_line']}"
|
|
76
|
-
)
|
|
77
|
-
output_data(f" Content:\n{query_result['content']}")
|
|
78
|
-
else:
|
|
79
|
-
output_info("\nINFO: No results found matching the query.")
|
|
80
|
-
|
|
81
|
-
return 0
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Query Command
|
|
4
|
+
|
|
5
|
+
Handles query execution functionality.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from ...output_manager import output_data, output_error, output_info, output_json
|
|
9
|
+
from ...query_loader import query_loader
|
|
10
|
+
from .base_command import BaseCommand
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class QueryCommand(BaseCommand):
|
|
14
|
+
"""Command for executing queries."""
|
|
15
|
+
|
|
16
|
+
async def execute_async(self, language: str) -> int:
|
|
17
|
+
# Get the query to execute
|
|
18
|
+
query_to_execute = None
|
|
19
|
+
|
|
20
|
+
if hasattr(self.args, "query_key") and self.args.query_key:
|
|
21
|
+
try:
|
|
22
|
+
query_to_execute = query_loader.get_query(language, self.args.query_key)
|
|
23
|
+
if query_to_execute is None:
|
|
24
|
+
output_error(
|
|
25
|
+
f"ERROR: Query '{self.args.query_key}' not found for language '{language}'"
|
|
26
|
+
)
|
|
27
|
+
return 1
|
|
28
|
+
except ValueError as e:
|
|
29
|
+
output_error(f"ERROR: {e}")
|
|
30
|
+
return 1
|
|
31
|
+
elif hasattr(self.args, "query_string") and self.args.query_string:
|
|
32
|
+
query_to_execute = self.args.query_string
|
|
33
|
+
|
|
34
|
+
if not query_to_execute:
|
|
35
|
+
output_error("ERROR: No query specified.")
|
|
36
|
+
return 1
|
|
37
|
+
|
|
38
|
+
# Perform analysis
|
|
39
|
+
analysis_result = await self.analyze_file(language)
|
|
40
|
+
if not analysis_result:
|
|
41
|
+
return 1
|
|
42
|
+
|
|
43
|
+
# Process query results
|
|
44
|
+
results = []
|
|
45
|
+
if hasattr(analysis_result, "query_results") and analysis_result.query_results:
|
|
46
|
+
results = analysis_result.query_results.get("captures", [])
|
|
47
|
+
else:
|
|
48
|
+
# Create basic results from elements
|
|
49
|
+
if hasattr(analysis_result, "elements") and analysis_result.elements:
|
|
50
|
+
for element in analysis_result.elements:
|
|
51
|
+
results.append(
|
|
52
|
+
{
|
|
53
|
+
"capture_name": getattr(
|
|
54
|
+
element, "__class__", type(element)
|
|
55
|
+
).__name__.lower(),
|
|
56
|
+
"node_type": getattr(
|
|
57
|
+
element, "__class__", type(element)
|
|
58
|
+
).__name__,
|
|
59
|
+
"start_line": getattr(element, "start_line", 0),
|
|
60
|
+
"end_line": getattr(element, "end_line", 0),
|
|
61
|
+
"content": getattr(element, "name", str(element)),
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Output results
|
|
66
|
+
if results:
|
|
67
|
+
if self.args.output_format == "json":
|
|
68
|
+
output_json(results)
|
|
69
|
+
else:
|
|
70
|
+
for i, query_result in enumerate(results, 1):
|
|
71
|
+
output_data(
|
|
72
|
+
f"\n{i}. {query_result['capture_name']} ({query_result['node_type']})"
|
|
73
|
+
)
|
|
74
|
+
output_data(
|
|
75
|
+
f" Position: Line {query_result['start_line']}-{query_result['end_line']}"
|
|
76
|
+
)
|
|
77
|
+
output_data(f" Content:\n{query_result['content']}")
|
|
78
|
+
else:
|
|
79
|
+
output_info("\nINFO: No results found matching the query.")
|
|
80
|
+
|
|
81
|
+
return 0
|