tree-sitter-analyzer 0.9.1__py3-none-any.whl → 0.9.2__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 +182 -178
- tree_sitter_analyzer/cli/commands/structure_command.py +138 -138
- tree_sitter_analyzer/cli/commands/summary_command.py +101 -101
- tree_sitter_analyzer/core/__init__.py +15 -15
- tree_sitter_analyzer/core/analysis_engine.py +74 -78
- 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/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/formatters/java_formatter.py +18 -18
- tree_sitter_analyzer/formatters/python_formatter.py +19 -19
- tree_sitter_analyzer/interfaces/__init__.py +9 -9
- tree_sitter_analyzer/interfaces/cli.py +528 -528
- tree_sitter_analyzer/interfaces/cli_adapter.py +344 -343
- tree_sitter_analyzer/interfaces/mcp_adapter.py +206 -206
- tree_sitter_analyzer/language_detector.py +53 -53
- tree_sitter_analyzer/languages/__init__.py +10 -10
- tree_sitter_analyzer/languages/java_plugin.py +1 -1
- tree_sitter_analyzer/languages/javascript_plugin.py +446 -446
- tree_sitter_analyzer/languages/python_plugin.py +755 -755
- tree_sitter_analyzer/mcp/__init__.py +34 -45
- 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 +623 -568
- tree_sitter_analyzer/mcp/tools/__init__.py +30 -30
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +681 -673
- 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 +310 -308
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +386 -379
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +563 -559
- tree_sitter_analyzer/mcp/utils/__init__.py +107 -107
- tree_sitter_analyzer/models.py +10 -10
- 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/validator.py +246 -241
- tree_sitter_analyzer/utils.py +294 -277
- {tree_sitter_analyzer-0.9.1.dist-info → tree_sitter_analyzer-0.9.2.dist-info}/METADATA +1 -1
- tree_sitter_analyzer-0.9.2.dist-info/RECORD +77 -0
- {tree_sitter_analyzer-0.9.1.dist-info → tree_sitter_analyzer-0.9.2.dist-info}/entry_points.txt +1 -0
- tree_sitter_analyzer-0.9.1.dist-info/RECORD +0 -77
- {tree_sitter_analyzer-0.9.1.dist-info → tree_sitter_analyzer-0.9.2.dist-info}/WHEEL +0 -0
|
@@ -1,210 +1,210 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
File Handler Module
|
|
4
|
-
|
|
5
|
-
This module provides file reading functionality with encoding detection and fallback.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import os
|
|
9
|
-
|
|
10
|
-
from .encoding_utils import read_file_safe
|
|
11
|
-
from .utils import log_error, log_info, log_warning
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def detect_language_from_extension(file_path: str) -> str:
|
|
15
|
-
"""
|
|
16
|
-
Detect programming language from file extension
|
|
17
|
-
|
|
18
|
-
Args:
|
|
19
|
-
file_path: File path to analyze
|
|
20
|
-
|
|
21
|
-
Returns:
|
|
22
|
-
Language name or 'unknown' if not recognized
|
|
23
|
-
"""
|
|
24
|
-
extension = os.path.splitext(file_path)[1].lower()
|
|
25
|
-
|
|
26
|
-
extension_map = {
|
|
27
|
-
".java": "java",
|
|
28
|
-
".py": "python",
|
|
29
|
-
".js": "javascript",
|
|
30
|
-
".ts": "typescript",
|
|
31
|
-
".cpp": "cpp",
|
|
32
|
-
".c": "c",
|
|
33
|
-
".h": "c",
|
|
34
|
-
".hpp": "cpp",
|
|
35
|
-
".cs": "csharp",
|
|
36
|
-
".go": "go",
|
|
37
|
-
".rs": "rust",
|
|
38
|
-
".rb": "ruby",
|
|
39
|
-
".php": "php",
|
|
40
|
-
".kt": "kotlin",
|
|
41
|
-
".scala": "scala",
|
|
42
|
-
".swift": "swift",
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return extension_map.get(extension, "unknown")
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def read_file_with_fallback(file_path: str) -> bytes | None:
|
|
49
|
-
"""
|
|
50
|
-
Read file with encoding fallback using unified encoding utilities
|
|
51
|
-
|
|
52
|
-
Args:
|
|
53
|
-
file_path: Path to the file to read
|
|
54
|
-
|
|
55
|
-
Returns:
|
|
56
|
-
File content as bytes, or None if file doesn't exist
|
|
57
|
-
"""
|
|
58
|
-
#
|
|
59
|
-
if not os.path.exists(file_path):
|
|
60
|
-
log_error(f"File does not exist: {file_path}")
|
|
61
|
-
return None
|
|
62
|
-
|
|
63
|
-
try:
|
|
64
|
-
content, detected_encoding = read_file_safe(file_path)
|
|
65
|
-
log_info(
|
|
66
|
-
f"Successfully read file {file_path} with encoding: {detected_encoding}"
|
|
67
|
-
)
|
|
68
|
-
return content.encode("utf-8")
|
|
69
|
-
|
|
70
|
-
except Exception as e:
|
|
71
|
-
log_error(f"Failed to read file {file_path}: {e}")
|
|
72
|
-
return None
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def read_file_partial(
|
|
76
|
-
file_path: str,
|
|
77
|
-
start_line: int,
|
|
78
|
-
end_line: int | None = None,
|
|
79
|
-
start_column: int | None = None,
|
|
80
|
-
end_column: int | None = None,
|
|
81
|
-
) -> str | None:
|
|
82
|
-
"""
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Args:
|
|
86
|
-
file_path:
|
|
87
|
-
start_line:
|
|
88
|
-
end_line:
|
|
89
|
-
start_column:
|
|
90
|
-
end_column:
|
|
91
|
-
|
|
92
|
-
Returns:
|
|
93
|
-
|
|
94
|
-
"""
|
|
95
|
-
#
|
|
96
|
-
if not os.path.exists(file_path):
|
|
97
|
-
log_error(f"File does not exist: {file_path}")
|
|
98
|
-
return None
|
|
99
|
-
|
|
100
|
-
#
|
|
101
|
-
if start_line < 1:
|
|
102
|
-
log_error(f"Invalid start_line: {start_line}. Line numbers start from 1.")
|
|
103
|
-
return None
|
|
104
|
-
|
|
105
|
-
if end_line is not None and end_line < start_line:
|
|
106
|
-
log_error(f"Invalid range: end_line ({end_line}) < start_line ({start_line})")
|
|
107
|
-
return None
|
|
108
|
-
|
|
109
|
-
try:
|
|
110
|
-
#
|
|
111
|
-
content, detected_encoding = read_file_safe(file_path)
|
|
112
|
-
|
|
113
|
-
#
|
|
114
|
-
lines = content.splitlines(keepends=True)
|
|
115
|
-
total_lines = len(lines)
|
|
116
|
-
|
|
117
|
-
#
|
|
118
|
-
start_idx = start_line - 1 # 0
|
|
119
|
-
end_idx = min(
|
|
120
|
-
end_line - 1 if end_line is not None else total_lines - 1, total_lines - 1
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
#
|
|
124
|
-
if start_idx >= total_lines:
|
|
125
|
-
log_warning(
|
|
126
|
-
f"start_line ({start_line}) exceeds file length ({total_lines})"
|
|
127
|
-
)
|
|
128
|
-
return ""
|
|
129
|
-
|
|
130
|
-
#
|
|
131
|
-
selected_lines = lines[start_idx : end_idx + 1]
|
|
132
|
-
|
|
133
|
-
#
|
|
134
|
-
if start_column is not None or end_column is not None:
|
|
135
|
-
processed_lines = []
|
|
136
|
-
for i, line in enumerate(selected_lines):
|
|
137
|
-
#
|
|
138
|
-
line_content = line.rstrip("\r\n")
|
|
139
|
-
|
|
140
|
-
if i == 0 and start_column is not None:
|
|
141
|
-
#
|
|
142
|
-
line_content = (
|
|
143
|
-
line_content[start_column:]
|
|
144
|
-
if start_column < len(line_content)
|
|
145
|
-
else ""
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
if i == len(selected_lines) - 1 and end_column is not None:
|
|
149
|
-
#
|
|
150
|
-
if i == 0 and start_column is not None:
|
|
151
|
-
#
|
|
152
|
-
col_end = (
|
|
153
|
-
end_column - start_column
|
|
154
|
-
if end_column >= start_column
|
|
155
|
-
else 0
|
|
156
|
-
)
|
|
157
|
-
line_content = line_content[:col_end] if col_end > 0 else ""
|
|
158
|
-
else:
|
|
159
|
-
line_content = (
|
|
160
|
-
line_content[:end_column]
|
|
161
|
-
if end_column < len(line_content)
|
|
162
|
-
else line_content
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
#
|
|
166
|
-
if i < len(selected_lines) - 1:
|
|
167
|
-
#
|
|
168
|
-
original_line = lines[start_idx + i]
|
|
169
|
-
if original_line.endswith("\r\n"):
|
|
170
|
-
line_content += "\r\n"
|
|
171
|
-
elif original_line.endswith("\n"):
|
|
172
|
-
line_content += "\n"
|
|
173
|
-
elif original_line.endswith("\r"):
|
|
174
|
-
line_content += "\r"
|
|
175
|
-
|
|
176
|
-
processed_lines.append(line_content)
|
|
177
|
-
|
|
178
|
-
result = "".join(processed_lines)
|
|
179
|
-
else:
|
|
180
|
-
#
|
|
181
|
-
result = "".join(selected_lines)
|
|
182
|
-
|
|
183
|
-
log_info(
|
|
184
|
-
f"Successfully read partial file {file_path}: "
|
|
185
|
-
f"lines {start_line}-{end_line or total_lines}"
|
|
186
|
-
f"{f', columns {start_column}-{end_column}' if start_column is not None or end_column is not None else ''}"
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
return result
|
|
190
|
-
|
|
191
|
-
except Exception as e:
|
|
192
|
-
log_error(f"Failed to read partial file {file_path}: {e}")
|
|
193
|
-
return None
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
def read_file_lines_range(
|
|
197
|
-
file_path: str, start_line: int, end_line: int | None = None
|
|
198
|
-
) -> str | None:
|
|
199
|
-
"""
|
|
200
|
-
指定した行番号範囲でファイルの一部を読み込み(列指定なし)
|
|
201
|
-
|
|
202
|
-
Args:
|
|
203
|
-
file_path: 読み込むファイルのパス
|
|
204
|
-
start_line: 開始行番号(1ベース)
|
|
205
|
-
end_line: 終了行番号(Noneの場合はファイル末尾まで、1ベース)
|
|
206
|
-
|
|
207
|
-
Returns:
|
|
208
|
-
指定範囲のファイル内容(文字列)、エラーの場合はNone
|
|
209
|
-
"""
|
|
210
|
-
return read_file_partial(file_path, start_line, end_line)
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
File Handler Module
|
|
4
|
+
|
|
5
|
+
This module provides file reading functionality with encoding detection and fallback.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
from .encoding_utils import read_file_safe
|
|
11
|
+
from .utils import log_error, log_info, log_warning
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def detect_language_from_extension(file_path: str) -> str:
|
|
15
|
+
"""
|
|
16
|
+
Detect programming language from file extension
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
file_path: File path to analyze
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Language name or 'unknown' if not recognized
|
|
23
|
+
"""
|
|
24
|
+
extension = os.path.splitext(file_path)[1].lower()
|
|
25
|
+
|
|
26
|
+
extension_map = {
|
|
27
|
+
".java": "java",
|
|
28
|
+
".py": "python",
|
|
29
|
+
".js": "javascript",
|
|
30
|
+
".ts": "typescript",
|
|
31
|
+
".cpp": "cpp",
|
|
32
|
+
".c": "c",
|
|
33
|
+
".h": "c",
|
|
34
|
+
".hpp": "cpp",
|
|
35
|
+
".cs": "csharp",
|
|
36
|
+
".go": "go",
|
|
37
|
+
".rs": "rust",
|
|
38
|
+
".rb": "ruby",
|
|
39
|
+
".php": "php",
|
|
40
|
+
".kt": "kotlin",
|
|
41
|
+
".scala": "scala",
|
|
42
|
+
".swift": "swift",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return extension_map.get(extension, "unknown")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def read_file_with_fallback(file_path: str) -> bytes | None:
|
|
49
|
+
"""
|
|
50
|
+
Read file with encoding fallback using unified encoding utilities
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
file_path: Path to the file to read
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
File content as bytes, or None if file doesn't exist
|
|
57
|
+
"""
|
|
58
|
+
# Check file existence first
|
|
59
|
+
if not os.path.exists(file_path):
|
|
60
|
+
log_error(f"File does not exist: {file_path}")
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
content, detected_encoding = read_file_safe(file_path)
|
|
65
|
+
log_info(
|
|
66
|
+
f"Successfully read file {file_path} with encoding: {detected_encoding}"
|
|
67
|
+
)
|
|
68
|
+
return content.encode("utf-8")
|
|
69
|
+
|
|
70
|
+
except Exception as e:
|
|
71
|
+
log_error(f"Failed to read file {file_path}: {e}")
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def read_file_partial(
|
|
76
|
+
file_path: str,
|
|
77
|
+
start_line: int,
|
|
78
|
+
end_line: int | None = None,
|
|
79
|
+
start_column: int | None = None,
|
|
80
|
+
end_column: int | None = None,
|
|
81
|
+
) -> str | None:
|
|
82
|
+
"""
|
|
83
|
+
Read partial file content by line/column range
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
file_path: Path to file
|
|
87
|
+
start_line: Start line (1-based)
|
|
88
|
+
end_line: End line (1-based, None means EOF)
|
|
89
|
+
start_column: Start column (0-based, optional)
|
|
90
|
+
end_column: End column (0-based, optional)
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Selected content string, or None on error
|
|
94
|
+
"""
|
|
95
|
+
# Check file existence
|
|
96
|
+
if not os.path.exists(file_path):
|
|
97
|
+
log_error(f"File does not exist: {file_path}")
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
# Parameter validation
|
|
101
|
+
if start_line < 1:
|
|
102
|
+
log_error(f"Invalid start_line: {start_line}. Line numbers start from 1.")
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
if end_line is not None and end_line < start_line:
|
|
106
|
+
log_error(f"Invalid range: end_line ({end_line}) < start_line ({start_line})")
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
# Read whole file safely
|
|
111
|
+
content, detected_encoding = read_file_safe(file_path)
|
|
112
|
+
|
|
113
|
+
# Split to lines
|
|
114
|
+
lines = content.splitlines(keepends=True)
|
|
115
|
+
total_lines = len(lines)
|
|
116
|
+
|
|
117
|
+
# Adjust line indexes
|
|
118
|
+
start_idx = start_line - 1 # convert to 0-based
|
|
119
|
+
end_idx = min(
|
|
120
|
+
end_line - 1 if end_line is not None else total_lines - 1, total_lines - 1
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Range check
|
|
124
|
+
if start_idx >= total_lines:
|
|
125
|
+
log_warning(
|
|
126
|
+
f"start_line ({start_line}) exceeds file length ({total_lines})"
|
|
127
|
+
)
|
|
128
|
+
return ""
|
|
129
|
+
|
|
130
|
+
# Select lines
|
|
131
|
+
selected_lines = lines[start_idx : end_idx + 1]
|
|
132
|
+
|
|
133
|
+
# Handle column range
|
|
134
|
+
if start_column is not None or end_column is not None:
|
|
135
|
+
processed_lines = []
|
|
136
|
+
for i, line in enumerate(selected_lines):
|
|
137
|
+
# Strip newline for processing
|
|
138
|
+
line_content = line.rstrip("\r\n")
|
|
139
|
+
|
|
140
|
+
if i == 0 and start_column is not None:
|
|
141
|
+
# First line: apply start_column
|
|
142
|
+
line_content = (
|
|
143
|
+
line_content[start_column:]
|
|
144
|
+
if start_column < len(line_content)
|
|
145
|
+
else ""
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
if i == len(selected_lines) - 1 and end_column is not None:
|
|
149
|
+
# Last line: apply end_column
|
|
150
|
+
if i == 0 and start_column is not None:
|
|
151
|
+
# Single line: apply both start and end columns
|
|
152
|
+
col_end = (
|
|
153
|
+
end_column - start_column
|
|
154
|
+
if end_column >= start_column
|
|
155
|
+
else 0
|
|
156
|
+
)
|
|
157
|
+
line_content = line_content[:col_end] if col_end > 0 else ""
|
|
158
|
+
else:
|
|
159
|
+
line_content = (
|
|
160
|
+
line_content[:end_column]
|
|
161
|
+
if end_column < len(line_content)
|
|
162
|
+
else line_content
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Preserve original newline (except last line)
|
|
166
|
+
if i < len(selected_lines) - 1:
|
|
167
|
+
# Detect original newline char of the line
|
|
168
|
+
original_line = lines[start_idx + i]
|
|
169
|
+
if original_line.endswith("\r\n"):
|
|
170
|
+
line_content += "\r\n"
|
|
171
|
+
elif original_line.endswith("\n"):
|
|
172
|
+
line_content += "\n"
|
|
173
|
+
elif original_line.endswith("\r"):
|
|
174
|
+
line_content += "\r"
|
|
175
|
+
|
|
176
|
+
processed_lines.append(line_content)
|
|
177
|
+
|
|
178
|
+
result = "".join(processed_lines)
|
|
179
|
+
else:
|
|
180
|
+
# No column range: join lines directly
|
|
181
|
+
result = "".join(selected_lines)
|
|
182
|
+
|
|
183
|
+
log_info(
|
|
184
|
+
f"Successfully read partial file {file_path}: "
|
|
185
|
+
f"lines {start_line}-{end_line or total_lines}"
|
|
186
|
+
f"{f', columns {start_column}-{end_column}' if start_column is not None or end_column is not None else ''}"
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
return result
|
|
190
|
+
|
|
191
|
+
except Exception as e:
|
|
192
|
+
log_error(f"Failed to read partial file {file_path}: {e}")
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def read_file_lines_range(
|
|
197
|
+
file_path: str, start_line: int, end_line: int | None = None
|
|
198
|
+
) -> str | None:
|
|
199
|
+
"""
|
|
200
|
+
指定した行番号範囲でファイルの一部を読み込み(列指定なし)
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
file_path: 読み込むファイルのパス
|
|
204
|
+
start_line: 開始行番号(1ベース)
|
|
205
|
+
end_line: 終了行番号(Noneの場合はファイル末尾まで、1ベース)
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
指定範囲のファイル内容(文字列)、エラーの場合はNone
|
|
209
|
+
"""
|
|
210
|
+
return read_file_partial(file_path, start_line, end_line)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
# Language-specific formatters
|
|
1
|
+
# Language-specific formatters
|