tree-sitter-analyzer 1.0.0__py3-none-any.whl → 1.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tree-sitter-analyzer might be problematic. Click here for more details.

Files changed (29) hide show
  1. tree_sitter_analyzer/__init__.py +132 -132
  2. tree_sitter_analyzer/api.py +542 -542
  3. tree_sitter_analyzer/cli/commands/base_command.py +181 -181
  4. tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -139
  5. tree_sitter_analyzer/cli/info_commands.py +124 -124
  6. tree_sitter_analyzer/cli_main.py +327 -327
  7. tree_sitter_analyzer/core/analysis_engine.py +584 -584
  8. tree_sitter_analyzer/core/query_service.py +162 -162
  9. tree_sitter_analyzer/file_handler.py +212 -212
  10. tree_sitter_analyzer/formatters/base_formatter.py +169 -169
  11. tree_sitter_analyzer/interfaces/cli.py +535 -535
  12. tree_sitter_analyzer/mcp/__init__.py +1 -1
  13. tree_sitter_analyzer/mcp/resources/__init__.py +0 -1
  14. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +16 -5
  15. tree_sitter_analyzer/mcp/server.py +655 -655
  16. tree_sitter_analyzer/mcp/tools/__init__.py +28 -30
  17. tree_sitter_analyzer/mcp/utils/__init__.py +1 -2
  18. tree_sitter_analyzer/mcp/utils/error_handler.py +569 -569
  19. tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -414
  20. tree_sitter_analyzer/output_manager.py +257 -257
  21. tree_sitter_analyzer/project_detector.py +330 -330
  22. tree_sitter_analyzer/security/boundary_manager.py +260 -260
  23. tree_sitter_analyzer/security/validator.py +257 -257
  24. tree_sitter_analyzer/table_formatter.py +710 -710
  25. tree_sitter_analyzer/utils.py +335 -335
  26. {tree_sitter_analyzer-1.0.0.dist-info → tree_sitter_analyzer-1.1.1.dist-info}/METADATA +12 -12
  27. {tree_sitter_analyzer-1.0.0.dist-info → tree_sitter_analyzer-1.1.1.dist-info}/RECORD +29 -29
  28. {tree_sitter_analyzer-1.0.0.dist-info → tree_sitter_analyzer-1.1.1.dist-info}/WHEEL +0 -0
  29. {tree_sitter_analyzer-1.0.0.dist-info → tree_sitter_analyzer-1.1.1.dist-info}/entry_points.txt +0 -0
@@ -1,212 +1,212 @@
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
- from pathlib import Path
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 = Path(file_path).suffix.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
- file_obj = Path(file_path)
60
- if not file_obj.exists():
61
- log_error(f"File does not exist: {file_path}")
62
- return None
63
-
64
- try:
65
- content, detected_encoding = read_file_safe(file_path)
66
- log_info(
67
- f"Successfully read file {file_path} with encoding: {detected_encoding}"
68
- )
69
- return content.encode("utf-8")
70
-
71
- except Exception as e:
72
- log_error(f"Failed to read file {file_path}: {e}")
73
- return None
74
-
75
-
76
- def read_file_partial(
77
- file_path: str,
78
- start_line: int,
79
- end_line: int | None = None,
80
- start_column: int | None = None,
81
- end_column: int | None = None,
82
- ) -> str | None:
83
- """
84
- Read partial file content by line/column range
85
-
86
- Args:
87
- file_path: Path to file
88
- start_line: Start line (1-based)
89
- end_line: End line (1-based, None means EOF)
90
- start_column: Start column (0-based, optional)
91
- end_column: End column (0-based, optional)
92
-
93
- Returns:
94
- Selected content string, or None on error
95
- """
96
- # Check file existence
97
- file_obj = Path(file_path)
98
- if not file_obj.exists():
99
- log_error(f"File does not exist: {file_path}")
100
- return None
101
-
102
- # Parameter validation
103
- if start_line < 1:
104
- log_error(f"Invalid start_line: {start_line}. Line numbers start from 1.")
105
- return None
106
-
107
- if end_line is not None and end_line < start_line:
108
- log_error(f"Invalid range: end_line ({end_line}) < start_line ({start_line})")
109
- return None
110
-
111
- try:
112
- # Read whole file safely
113
- content, detected_encoding = read_file_safe(file_path)
114
-
115
- # Split to lines
116
- lines = content.splitlines(keepends=True)
117
- total_lines = len(lines)
118
-
119
- # Adjust line indexes
120
- start_idx = start_line - 1 # convert to 0-based
121
- end_idx = min(
122
- end_line - 1 if end_line is not None else total_lines - 1, total_lines - 1
123
- )
124
-
125
- # Range check
126
- if start_idx >= total_lines:
127
- log_warning(
128
- f"start_line ({start_line}) exceeds file length ({total_lines})"
129
- )
130
- return ""
131
-
132
- # Select lines
133
- selected_lines = lines[start_idx : end_idx + 1]
134
-
135
- # Handle column range
136
- if start_column is not None or end_column is not None:
137
- processed_lines = []
138
- for i, line in enumerate(selected_lines):
139
- # Strip newline for processing
140
- line_content = line.rstrip("\r\n")
141
-
142
- if i == 0 and start_column is not None:
143
- # First line: apply start_column
144
- line_content = (
145
- line_content[start_column:]
146
- if start_column < len(line_content)
147
- else ""
148
- )
149
-
150
- if i == len(selected_lines) - 1 and end_column is not None:
151
- # Last line: apply end_column
152
- if i == 0 and start_column is not None:
153
- # Single line: apply both start and end columns
154
- col_end = (
155
- end_column - start_column
156
- if end_column >= start_column
157
- else 0
158
- )
159
- line_content = line_content[:col_end] if col_end > 0 else ""
160
- else:
161
- line_content = (
162
- line_content[:end_column]
163
- if end_column < len(line_content)
164
- else line_content
165
- )
166
-
167
- # Preserve original newline (except last line)
168
- if i < len(selected_lines) - 1:
169
- # Detect original newline char of the line
170
- original_line = lines[start_idx + i]
171
- if original_line.endswith("\r\n"):
172
- line_content += "\r\n"
173
- elif original_line.endswith("\n"):
174
- line_content += "\n"
175
- elif original_line.endswith("\r"):
176
- line_content += "\r"
177
-
178
- processed_lines.append(line_content)
179
-
180
- result = "".join(processed_lines)
181
- else:
182
- # No column range: join lines directly
183
- result = "".join(selected_lines)
184
-
185
- log_info(
186
- f"Successfully read partial file {file_path}: "
187
- f"lines {start_line}-{end_line or total_lines}"
188
- f"{f', columns {start_column}-{end_column}' if start_column is not None or end_column is not None else ''}"
189
- )
190
-
191
- return result
192
-
193
- except Exception as e:
194
- log_error(f"Failed to read partial file {file_path}: {e}")
195
- return None
196
-
197
-
198
- def read_file_lines_range(
199
- file_path: str, start_line: int, end_line: int | None = None
200
- ) -> str | None:
201
- """
202
- 指定した行番号範囲でファイルの一部を読み込み(列指定なし)
203
-
204
- Args:
205
- file_path: 読み込むファイルのパス
206
- start_line: 開始行番号(1ベース)
207
- end_line: 終了行番号(Noneの場合はファイル末尾まで、1ベース)
208
-
209
- Returns:
210
- 指定範囲のファイル内容(文字列)、エラーの場合はNone
211
- """
212
- 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
+ from pathlib import Path
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 = Path(file_path).suffix.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
+ file_obj = Path(file_path)
60
+ if not file_obj.exists():
61
+ log_error(f"File does not exist: {file_path}")
62
+ return None
63
+
64
+ try:
65
+ content, detected_encoding = read_file_safe(file_path)
66
+ log_info(
67
+ f"Successfully read file {file_path} with encoding: {detected_encoding}"
68
+ )
69
+ return content.encode("utf-8")
70
+
71
+ except Exception as e:
72
+ log_error(f"Failed to read file {file_path}: {e}")
73
+ return None
74
+
75
+
76
+ def read_file_partial(
77
+ file_path: str,
78
+ start_line: int,
79
+ end_line: int | None = None,
80
+ start_column: int | None = None,
81
+ end_column: int | None = None,
82
+ ) -> str | None:
83
+ """
84
+ Read partial file content by line/column range
85
+
86
+ Args:
87
+ file_path: Path to file
88
+ start_line: Start line (1-based)
89
+ end_line: End line (1-based, None means EOF)
90
+ start_column: Start column (0-based, optional)
91
+ end_column: End column (0-based, optional)
92
+
93
+ Returns:
94
+ Selected content string, or None on error
95
+ """
96
+ # Check file existence
97
+ file_obj = Path(file_path)
98
+ if not file_obj.exists():
99
+ log_error(f"File does not exist: {file_path}")
100
+ return None
101
+
102
+ # Parameter validation
103
+ if start_line < 1:
104
+ log_error(f"Invalid start_line: {start_line}. Line numbers start from 1.")
105
+ return None
106
+
107
+ if end_line is not None and end_line < start_line:
108
+ log_error(f"Invalid range: end_line ({end_line}) < start_line ({start_line})")
109
+ return None
110
+
111
+ try:
112
+ # Read whole file safely
113
+ content, detected_encoding = read_file_safe(file_path)
114
+
115
+ # Split to lines
116
+ lines = content.splitlines(keepends=True)
117
+ total_lines = len(lines)
118
+
119
+ # Adjust line indexes
120
+ start_idx = start_line - 1 # convert to 0-based
121
+ end_idx = min(
122
+ end_line - 1 if end_line is not None else total_lines - 1, total_lines - 1
123
+ )
124
+
125
+ # Range check
126
+ if start_idx >= total_lines:
127
+ log_warning(
128
+ f"start_line ({start_line}) exceeds file length ({total_lines})"
129
+ )
130
+ return ""
131
+
132
+ # Select lines
133
+ selected_lines = lines[start_idx : end_idx + 1]
134
+
135
+ # Handle column range
136
+ if start_column is not None or end_column is not None:
137
+ processed_lines = []
138
+ for i, line in enumerate(selected_lines):
139
+ # Strip newline for processing
140
+ line_content = line.rstrip("\r\n")
141
+
142
+ if i == 0 and start_column is not None:
143
+ # First line: apply start_column
144
+ line_content = (
145
+ line_content[start_column:]
146
+ if start_column < len(line_content)
147
+ else ""
148
+ )
149
+
150
+ if i == len(selected_lines) - 1 and end_column is not None:
151
+ # Last line: apply end_column
152
+ if i == 0 and start_column is not None:
153
+ # Single line: apply both start and end columns
154
+ col_end = (
155
+ end_column - start_column
156
+ if end_column >= start_column
157
+ else 0
158
+ )
159
+ line_content = line_content[:col_end] if col_end > 0 else ""
160
+ else:
161
+ line_content = (
162
+ line_content[:end_column]
163
+ if end_column < len(line_content)
164
+ else line_content
165
+ )
166
+
167
+ # Preserve original newline (except last line)
168
+ if i < len(selected_lines) - 1:
169
+ # Detect original newline char of the line
170
+ original_line = lines[start_idx + i]
171
+ if original_line.endswith("\r\n"):
172
+ line_content += "\r\n"
173
+ elif original_line.endswith("\n"):
174
+ line_content += "\n"
175
+ elif original_line.endswith("\r"):
176
+ line_content += "\r"
177
+
178
+ processed_lines.append(line_content)
179
+
180
+ result = "".join(processed_lines)
181
+ else:
182
+ # No column range: join lines directly
183
+ result = "".join(selected_lines)
184
+
185
+ log_info(
186
+ f"Successfully read partial file {file_path}: "
187
+ f"lines {start_line}-{end_line or total_lines}"
188
+ f"{f', columns {start_column}-{end_column}' if start_column is not None or end_column is not None else ''}"
189
+ )
190
+
191
+ return result
192
+
193
+ except Exception as e:
194
+ log_error(f"Failed to read partial file {file_path}: {e}")
195
+ return None
196
+
197
+
198
+ def read_file_lines_range(
199
+ file_path: str, start_line: int, end_line: int | None = None
200
+ ) -> str | None:
201
+ """
202
+ 指定した行番号範囲でファイルの一部を読み込み(列指定なし)
203
+
204
+ Args:
205
+ file_path: 読み込むファイルのパス
206
+ start_line: 開始行番号(1ベース)
207
+ end_line: 終了行番号(Noneの場合はファイル末尾まで、1ベース)
208
+
209
+ Returns:
210
+ 指定範囲のファイル内容(文字列)、エラーの場合はNone
211
+ """
212
+ return read_file_partial(file_path, start_line, end_line)