tree-sitter-analyzer 0.3.0__py3-none-any.whl → 0.4.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.

Files changed (51) hide show
  1. tree_sitter_analyzer/__init__.py +4 -3
  2. tree_sitter_analyzer/api.py +4 -2
  3. tree_sitter_analyzer/cli/__init__.py +3 -3
  4. tree_sitter_analyzer/cli/commands/advanced_command.py +1 -1
  5. tree_sitter_analyzer/cli/commands/base_command.py +1 -1
  6. tree_sitter_analyzer/cli/commands/partial_read_command.py +2 -2
  7. tree_sitter_analyzer/cli/commands/summary_command.py +2 -2
  8. tree_sitter_analyzer/cli/commands/table_command.py +11 -8
  9. tree_sitter_analyzer/cli_main.py +2 -1
  10. tree_sitter_analyzer/core/analysis_engine.py +33 -69
  11. tree_sitter_analyzer/core/engine.py +6 -4
  12. tree_sitter_analyzer/core/parser.py +1 -1
  13. tree_sitter_analyzer/core/query.py +16 -8
  14. tree_sitter_analyzer/encoding_utils.py +0 -2
  15. tree_sitter_analyzer/exceptions.py +23 -23
  16. tree_sitter_analyzer/file_handler.py +4 -11
  17. tree_sitter_analyzer/formatters/java_formatter.py +8 -4
  18. tree_sitter_analyzer/formatters/python_formatter.py +8 -4
  19. tree_sitter_analyzer/interfaces/cli.py +1 -1
  20. tree_sitter_analyzer/interfaces/cli_adapter.py +30 -9
  21. tree_sitter_analyzer/interfaces/mcp_adapter.py +43 -17
  22. tree_sitter_analyzer/interfaces/mcp_server.py +9 -9
  23. tree_sitter_analyzer/java_analyzer.py +20 -51
  24. tree_sitter_analyzer/language_loader.py +2 -2
  25. tree_sitter_analyzer/languages/java_plugin.py +86 -41
  26. tree_sitter_analyzer/{plugins → languages}/javascript_plugin.py +3 -3
  27. tree_sitter_analyzer/languages/python_plugin.py +16 -6
  28. tree_sitter_analyzer/mcp/resources/code_file_resource.py +0 -3
  29. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +0 -5
  30. tree_sitter_analyzer/mcp/server.py +4 -4
  31. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +63 -30
  32. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +9 -4
  33. tree_sitter_analyzer/mcp/tools/table_format_tool.py +2 -2
  34. tree_sitter_analyzer/mcp/utils/__init__.py +10 -8
  35. tree_sitter_analyzer/models.py +1 -1
  36. tree_sitter_analyzer/output_manager.py +4 -10
  37. tree_sitter_analyzer/plugins/__init__.py +9 -62
  38. tree_sitter_analyzer/plugins/base.py +20 -1
  39. tree_sitter_analyzer/plugins/manager.py +29 -12
  40. tree_sitter_analyzer/query_loader.py +4 -1
  41. tree_sitter_analyzer/table_formatter.py +4 -1
  42. tree_sitter_analyzer/utils.py +6 -6
  43. {tree_sitter_analyzer-0.3.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/METADATA +3 -3
  44. tree_sitter_analyzer-0.4.0.dist-info/RECORD +73 -0
  45. {tree_sitter_analyzer-0.3.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/entry_points.txt +2 -1
  46. tree_sitter_analyzer/plugins/java_plugin.py +0 -608
  47. tree_sitter_analyzer/plugins/plugin_loader.py +0 -85
  48. tree_sitter_analyzer/plugins/python_plugin.py +0 -606
  49. tree_sitter_analyzer/plugins/registry.py +0 -374
  50. tree_sitter_analyzer-0.3.0.dist-info/RECORD +0 -77
  51. {tree_sitter_analyzer-0.3.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/WHEEL +0 -0
@@ -42,7 +42,7 @@ class AnalysisError(TreeSitterAnalyzerError):
42
42
  message: str,
43
43
  file_path: str | Path | None = None,
44
44
  language: str | None = None,
45
- **kwargs,
45
+ **kwargs: Any,
46
46
  ) -> None:
47
47
  context = kwargs.get("context", {})
48
48
  if file_path:
@@ -60,7 +60,7 @@ class ParseError(TreeSitterAnalyzerError):
60
60
  message: str,
61
61
  language: str | None = None,
62
62
  source_info: dict[str, Any] | None = None,
63
- **kwargs,
63
+ **kwargs: Any,
64
64
  ) -> None:
65
65
  context = kwargs.get("context", {})
66
66
  if language:
@@ -74,7 +74,7 @@ class LanguageNotSupportedError(TreeSitterAnalyzerError):
74
74
  """Raised when a language is not supported."""
75
75
 
76
76
  def __init__(
77
- self, language: str, supported_languages: list | None = None, **kwargs
77
+ self, language: str, supported_languages: list[str] | None = None, **kwargs: Any
78
78
  ) -> None:
79
79
  message = f"Language '{language}' is not supported"
80
80
  context = kwargs.get("context", {})
@@ -93,7 +93,7 @@ class PluginError(TreeSitterAnalyzerError):
93
93
  message: str,
94
94
  plugin_name: str | None = None,
95
95
  operation: str | None = None,
96
- **kwargs,
96
+ **kwargs: Any,
97
97
  ) -> None:
98
98
  context = kwargs.get("context", {})
99
99
  if plugin_name:
@@ -112,7 +112,7 @@ class QueryError(TreeSitterAnalyzerError):
112
112
  query_name: str | None = None,
113
113
  query_string: str | None = None,
114
114
  language: str | None = None,
115
- **kwargs,
115
+ **kwargs: Any,
116
116
  ) -> None:
117
117
  context = kwargs.get("context", {})
118
118
  if query_name:
@@ -132,7 +132,7 @@ class FileHandlingError(TreeSitterAnalyzerError):
132
132
  message: str,
133
133
  file_path: str | Path | None = None,
134
134
  operation: str | None = None,
135
- **kwargs,
135
+ **kwargs: Any,
136
136
  ) -> None:
137
137
  context = kwargs.get("context", {})
138
138
  if file_path:
@@ -150,7 +150,7 @@ class ConfigurationError(TreeSitterAnalyzerError):
150
150
  message: str,
151
151
  config_key: str | None = None,
152
152
  config_value: Any | None = None,
153
- **kwargs,
153
+ **kwargs: Any,
154
154
  ) -> None:
155
155
  context = kwargs.get("context", {})
156
156
  if config_key:
@@ -168,7 +168,7 @@ class ValidationError(TreeSitterAnalyzerError):
168
168
  message: str,
169
169
  validation_type: str | None = None,
170
170
  invalid_value: Any | None = None,
171
- **kwargs,
171
+ **kwargs: Any,
172
172
  ) -> None:
173
173
  context = kwargs.get("context", {})
174
174
  if validation_type:
@@ -186,7 +186,7 @@ class MCPError(TreeSitterAnalyzerError):
186
186
  message: str,
187
187
  tool_name: str | None = None,
188
188
  resource_uri: str | None = None,
189
- **kwargs,
189
+ **kwargs: Any,
190
190
  ) -> None:
191
191
  context = kwargs.get("context", {})
192
192
  if tool_name:
@@ -200,7 +200,7 @@ class MCPError(TreeSitterAnalyzerError):
200
200
  def handle_exception(
201
201
  exception: Exception,
202
202
  context: dict[str, Any] | None = None,
203
- reraise_as: type | None = None,
203
+ reraise_as: type[Exception] | None = None,
204
204
  ) -> None:
205
205
  """
206
206
  Handle exceptions with optional context and re-raising.
@@ -231,13 +231,13 @@ def handle_exception(
231
231
 
232
232
 
233
233
  def safe_execute(
234
- func,
235
- *args,
236
- default_return=None,
237
- exception_types: tuple = (Exception,),
234
+ func: Any,
235
+ *args: Any,
236
+ default_return: Any = None,
237
+ exception_types: tuple[type[Exception], ...] = (Exception,),
238
238
  log_errors: bool = True,
239
- **kwargs,
240
- ):
239
+ **kwargs: Any,
240
+ ) -> Any:
241
241
  """
242
242
  Safely execute a function with exception handling.
243
243
 
@@ -277,7 +277,7 @@ def create_error_response(
277
277
  """
278
278
  import traceback
279
279
 
280
- response = {
280
+ response: dict[str, Any] = {
281
281
  "success": False,
282
282
  "error": {"type": exception.__class__.__name__, "message": str(exception)},
283
283
  }
@@ -299,11 +299,11 @@ def create_error_response(
299
299
 
300
300
  # Decorator for exception handling
301
301
  def handle_exceptions(
302
- default_return=None,
303
- exception_types: tuple = (Exception,),
304
- reraise_as: type | None = None,
302
+ default_return: Any = None,
303
+ exception_types: tuple[type[Exception], ...] = (Exception,),
304
+ reraise_as: type[Exception] | None = None,
305
305
  log_errors: bool = True,
306
- ):
306
+ ) -> Any:
307
307
  """
308
308
  Decorator for automatic exception handling.
309
309
 
@@ -314,8 +314,8 @@ def handle_exceptions(
314
314
  log_errors: Whether to log errors
315
315
  """
316
316
 
317
- def decorator(func):
318
- def wrapper(*args, **kwargs):
317
+ def decorator(func: Any) -> Any:
318
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
319
319
  try:
320
320
  return func(*args, **kwargs)
321
321
  except exception_types as e:
@@ -62,14 +62,10 @@ def read_file_with_fallback(file_path: str) -> bytes | None:
62
62
 
63
63
  try:
64
64
  content, detected_encoding = read_file_safe(file_path)
65
- if content is not None: # 空文字列も有効なコンテンツ
66
- log_info(
67
- f"Successfully read file {file_path} with encoding: {detected_encoding}"
68
- )
69
- return content.encode("utf-8")
70
- else:
71
- log_warning(f"File {file_path} is empty or could not be read")
72
- return b""
65
+ log_info(
66
+ f"Successfully read file {file_path} with encoding: {detected_encoding}"
67
+ )
68
+ return content.encode("utf-8")
73
69
 
74
70
  except Exception as e:
75
71
  log_error(f"Failed to read file {file_path}: {e}")
@@ -113,9 +109,6 @@ def read_file_partial(
113
109
  try:
114
110
  # ファイル全体を安全に読み込み
115
111
  content, detected_encoding = read_file_safe(file_path)
116
- if content is None:
117
- log_error(f"Failed to read file: {file_path}")
118
- return None
119
112
 
120
113
  # 行に分割
121
114
  lines = content.splitlines(keepends=True)
@@ -266,22 +266,26 @@ class JavaTableFormatter(BaseTableFormatter):
266
266
 
267
267
  # Map<String,Object> -> M<S,O>
268
268
  if "Map<" in type_name:
269
- return (
269
+ result = (
270
270
  type_name.replace("Map<", "M<")
271
271
  .replace("String", "S")
272
272
  .replace("Object", "O")
273
273
  )
274
+ return str(result)
274
275
 
275
276
  # List<String> -> L<S>
276
277
  if "List<" in type_name:
277
- return type_name.replace("List<", "L<").replace("String", "S")
278
+ result = type_name.replace("List<", "L<").replace("String", "S")
279
+ return str(result)
278
280
 
279
281
  # String[] -> S[]
280
282
  if "[]" in type_name:
281
283
  base_type = type_name.replace("[]", "")
282
284
  if base_type:
283
- return type_mapping.get(base_type, base_type[0].upper()) + "[]"
285
+ result = type_mapping.get(base_type, base_type[0].upper()) + "[]"
286
+ return str(result)
284
287
  else:
285
288
  return "O[]"
286
289
 
287
- return type_mapping.get(type_name, type_name)
290
+ result = type_mapping.get(type_name, type_name)
291
+ return str(result)
@@ -236,20 +236,24 @@ class PythonTableFormatter(BaseTableFormatter):
236
236
 
237
237
  # List[str] -> L[s]
238
238
  if "List[" in type_name:
239
- return (
239
+ result = (
240
240
  type_name.replace("List[", "L[").replace("str", "s").replace("int", "i")
241
241
  )
242
+ return str(result)
242
243
 
243
244
  # Dict[str, int] -> D[s,i]
244
245
  if "Dict[" in type_name:
245
- return (
246
+ result = (
246
247
  type_name.replace("Dict[", "D[").replace("str", "s").replace("int", "i")
247
248
  )
249
+ return str(result)
248
250
 
249
251
  # Optional[str] -> O[s]
250
252
  if "Optional[" in type_name:
251
- return type_name.replace("Optional[", "O[").replace("str", "s")
253
+ result = type_name.replace("Optional[", "O[").replace("str", "s")
254
+ return str(result)
252
255
 
253
- return type_mapping.get(
256
+ result = type_mapping.get(
254
257
  type_name, type_name[:3] if len(type_name) > 3 else type_name
255
258
  )
259
+ return str(result)
@@ -407,7 +407,7 @@ def format_analysis_output(result: dict[str, Any], output_format: str) -> None:
407
407
  elements = result.get("elements", [])
408
408
  if elements:
409
409
  print(f"\nCode Elements: {len(elements)} found")
410
- element_types = {}
410
+ element_types: dict[str, int] = {}
411
411
  for element in elements:
412
412
  elem_type = element.get("type", "unknown")
413
413
  element_types[elem_type] = element_types.get(elem_type, 0) + 1
@@ -153,13 +153,34 @@ class CLIAdapter:
153
153
  "file_path": result.file_path,
154
154
  "language": result.language,
155
155
  "package": result.package,
156
- "imports": [imp.to_dict() for imp in result.imports],
157
- "classes": [cls.to_dict() for cls in result.classes],
158
- "methods": [method.to_dict() for method in result.methods],
159
- "fields": [field.to_dict() for field in result.fields],
160
- "annotations": [ann.to_dict() for ann in result.annotations],
156
+ "imports": [
157
+ {"name": imp.name, "type": str(type(imp).__name__)}
158
+ for imp in result.imports
159
+ ],
160
+ "classes": [
161
+ {"name": cls.name, "type": str(type(cls).__name__)}
162
+ for cls in result.classes
163
+ ],
164
+ "methods": [
165
+ {"name": method.name, "type": str(type(method).__name__)}
166
+ for method in result.methods
167
+ ],
168
+ "fields": [
169
+ {"name": field.name, "type": str(type(field).__name__)}
170
+ for field in result.fields
171
+ ],
172
+ "annotations": [
173
+ {
174
+ "name": getattr(ann, "name", str(ann)),
175
+ "type": str(type(ann).__name__),
176
+ }
177
+ for ann in getattr(result, "annotations", [])
178
+ ],
161
179
  "analysis_time": result.analysis_time,
162
- "elements": [elem.to_dict() for elem in result.elements],
180
+ "elements": [
181
+ {"name": elem.name, "type": str(type(elem).__name__)}
182
+ for elem in result.elements
183
+ ],
163
184
  "success": result.success,
164
185
  }
165
186
 
@@ -315,8 +336,8 @@ class CLIAdapter:
315
336
  # Legacy AdvancedAnalyzerAdapter removed - use UnifiedAnalysisEngine directly
316
337
 
317
338
 
318
- def get_analysis_engine():
339
+ def get_analysis_engine() -> "UnifiedAnalysisEngine":
319
340
  """Get analysis engine instance for testing compatibility."""
320
- from ..core.analysis_engine import AnalysisEngine
341
+ from ..core.analysis_engine import UnifiedAnalysisEngine
321
342
 
322
- return AnalysisEngine()
343
+ return UnifiedAnalysisEngine()
@@ -5,19 +5,22 @@ MCP Adapter for Tree-Sitter Analyzer
5
5
  This module provides an adapter interface for integrating with the MCP protocol.
6
6
  """
7
7
 
8
- from typing import Any
8
+ from typing import TYPE_CHECKING, Any
9
9
 
10
10
  from ..models import AnalysisResult
11
11
 
12
+ if TYPE_CHECKING:
13
+ from ..core.analysis_engine import UnifiedAnalysisEngine
12
14
 
13
- def get_analysis_engine():
15
+
16
+ def get_analysis_engine() -> "UnifiedAnalysisEngine":
14
17
  """Get analysis engine instance for testing compatibility."""
15
- from ..core.analysis_engine import AnalysisEngine
18
+ from ..core.analysis_engine import UnifiedAnalysisEngine
16
19
 
17
- return AnalysisEngine()
20
+ return UnifiedAnalysisEngine()
18
21
 
19
22
 
20
- def handle_mcp_resource_request(uri):
23
+ def handle_mcp_resource_request(uri: str) -> dict[str, Any]:
21
24
  """Handle MCP resource request for testing compatibility."""
22
25
  return {
23
26
  "contents": [
@@ -38,13 +41,15 @@ def read_file_safe(file_path: str) -> str:
38
41
  class MCPAdapter:
39
42
  """MCP Adapter for testing compatibility."""
40
43
 
41
- def __init__(self):
44
+ def __init__(self) -> None:
42
45
  """Initialize MCP Adapter."""
43
46
  from ..core.analysis_engine import UnifiedAnalysisEngine
44
47
 
45
48
  self.engine = UnifiedAnalysisEngine()
46
49
 
47
- async def analyze_file_async(self, file_path: str, **kwargs) -> "AnalysisResult":
50
+ async def analyze_file_async(
51
+ self, file_path: str, **kwargs: Any
52
+ ) -> "AnalysisResult":
48
53
  """Analyze file asynchronously."""
49
54
  from ..core.analysis_engine import AnalysisRequest
50
55
 
@@ -58,7 +63,7 @@ class MCPAdapter:
58
63
  return await self.engine.analyze(request)
59
64
 
60
65
  async def get_file_structure_async(
61
- self, file_path: str, **kwargs
66
+ self, file_path: str, **kwargs: Any
62
67
  ) -> dict[str, Any]:
63
68
  """Get file structure asynchronously."""
64
69
  result = await self.analyze_file_async(file_path, **kwargs)
@@ -66,11 +71,29 @@ class MCPAdapter:
66
71
  "file_path": result.file_path,
67
72
  "language": result.language,
68
73
  "structure": {
69
- "classes": [cls.to_dict() for cls in result.classes],
70
- "methods": [method.to_dict() for method in result.methods],
71
- "fields": [field.to_dict() for field in result.fields],
72
- "imports": [imp.to_dict() for imp in result.imports],
73
- "annotations": [ann.to_dict() for ann in result.annotations],
74
+ "classes": [
75
+ {"name": cls.name, "type": str(type(cls).__name__)}
76
+ for cls in result.classes
77
+ ],
78
+ "methods": [
79
+ {"name": method.name, "type": str(type(method).__name__)}
80
+ for method in result.methods
81
+ ],
82
+ "fields": [
83
+ {"name": field.name, "type": str(type(field).__name__)}
84
+ for field in result.fields
85
+ ],
86
+ "imports": [
87
+ {"name": imp.name, "type": str(type(imp).__name__)}
88
+ for imp in result.imports
89
+ ],
90
+ "annotations": [
91
+ {
92
+ "name": getattr(ann, "name", str(ann)),
93
+ "type": str(type(ann).__name__),
94
+ }
95
+ for ann in getattr(result, "annotations", [])
96
+ ],
74
97
  },
75
98
  "metadata": {
76
99
  "analysis_time": result.analysis_time,
@@ -86,7 +109,7 @@ class MCPAdapter:
86
109
  }
87
110
 
88
111
  async def analyze_batch_async(
89
- self, file_paths: list[str], **kwargs
112
+ self, file_paths: list[str], **kwargs: Any
90
113
  ) -> list["AnalysisResult"]:
91
114
  """Analyze multiple files asynchronously."""
92
115
  results = []
@@ -157,17 +180,20 @@ class MCPAdapter:
157
180
  """Async cleanup."""
158
181
  pass
159
182
 
160
- def analyze_with_mcp_request(self, arguments):
183
+ async def analyze_with_mcp_request(
184
+ self, arguments: dict[str, Any]
185
+ ) -> dict[str, Any]:
161
186
  """Analyze with MCP request."""
162
187
  if "file_path" not in arguments:
163
188
  raise KeyError("file_path is required in MCP request")
164
- return self.analyze_file_async(arguments["file_path"])
189
+ result = await self.analyze_file_async(arguments["file_path"])
190
+ return {"result": str(result), "success": True}
165
191
 
166
192
 
167
193
  class MCPServerAdapter:
168
194
  """MCP Server Adapter for testing compatibility."""
169
195
 
170
- def __init__(self):
196
+ def __init__(self) -> None:
171
197
  """Initialize MCP Server Adapter."""
172
198
  self.mcp_adapter = MCPAdapter()
173
199
 
@@ -53,9 +53,9 @@ class TreeSitterAnalyzerMCPServer:
53
53
 
54
54
  def create_server(self) -> Server:
55
55
  """Create and configure the MCP server."""
56
- server = Server(self.name)
56
+ server: Any = Server(self.name)
57
57
 
58
- @server.list_tools()
58
+ @server.list_tools() # type: ignore
59
59
  async def handle_list_tools() -> list[Tool]:
60
60
  """List available tools."""
61
61
  return [
@@ -224,7 +224,7 @@ class TreeSitterAnalyzerMCPServer:
224
224
  ),
225
225
  ]
226
226
 
227
- @server.call_tool()
227
+ @server.call_tool() # type: ignore
228
228
  async def handle_call_tool(
229
229
  name: str, arguments: dict[str, Any]
230
230
  ) -> list[TextContent]:
@@ -309,25 +309,25 @@ class TreeSitterAnalyzerMCPServer:
309
309
  )
310
310
  ]
311
311
 
312
- @server.list_resources()
312
+ @server.list_resources() # type: ignore
313
313
  async def handle_list_resources() -> list[Resource]:
314
314
  """List available resources."""
315
315
  return [
316
316
  Resource(
317
- uri="code://file/{file_path}",
317
+ uri="code://file/{file_path}", # type: ignore
318
318
  name="Code File Analysis",
319
319
  description="Access to code file content and analysis",
320
320
  mimeType="application/json",
321
321
  ),
322
322
  Resource(
323
- uri="code://stats/{stats_type}",
323
+ uri="code://stats/{stats_type}", # type: ignore
324
324
  name="Project Statistics",
325
325
  description="Access to project statistics and analysis data",
326
326
  mimeType="application/json",
327
327
  ),
328
328
  ]
329
329
 
330
- @server.read_resource()
330
+ @server.read_resource() # type: ignore
331
331
  async def handle_read_resource(uri: str) -> str:
332
332
  """Read resource content."""
333
333
  try:
@@ -366,7 +366,7 @@ class TreeSitterAnalyzerMCPServer:
366
366
 
367
367
  self.server = server
368
368
  log_info("MCP server created successfully")
369
- return server
369
+ return server # type: ignore
370
370
 
371
371
  async def run(self) -> None:
372
372
  """Run the MCP server."""
@@ -376,7 +376,7 @@ class TreeSitterAnalyzerMCPServer:
376
376
  options = InitializationOptions(
377
377
  server_name=self.name,
378
378
  server_version=self.version,
379
- capabilities={"tools": {}, "resources": {}},
379
+ capabilities={"tools": {}, "resources": {}}, # type: ignore
380
380
  )
381
381
 
382
382
  log_info(f"Starting MCP server: {self.name} v{self.version}")
@@ -93,10 +93,7 @@ class CodeAnalyzer:
93
93
  self.source_code_bytes = source_bytes
94
94
  self.tree = self.parser.parse(self.source_code_bytes)
95
95
 
96
- if self.tree is None:
97
- output_error(f"ERROR: '{file_path}' のAST構築に失敗しました。")
98
- return False
99
-
96
+ # Tree parsing should always succeed with valid input
100
97
  log_info(f"INFO: '{file_path}' の解析が完了し、ASTを構築しました。")
101
98
  return True
102
99
 
@@ -141,54 +138,26 @@ class CodeAnalyzer:
141
138
 
142
139
  # Tree-sitter 0.24以降の辞書形式に対応
143
140
  try:
144
- if isinstance(captures, dict):
145
- # 新しい辞書形式: {capture_name: [nodes...]}
146
- for capture_name, nodes in captures.items():
147
- if isinstance(nodes, list):
148
- for node in nodes:
149
- try:
150
- start_line = node.start_point[0] + 1
151
- end_line = node.end_point[0] + 1
152
- node_text = self.source_code_bytes[
153
- node.start_byte : node.end_byte
154
- ].decode("utf-8", errors="ignore")
155
-
156
- results.append(
157
- {
158
- "capture_name": capture_name,
159
- "content": node_text,
160
- "start_line": start_line,
161
- "end_line": end_line,
162
- "node_type": node.type,
163
- }
164
- )
165
- except Exception as e:
166
- output_warning(
167
- f"WARNING: ノード処理中にエラーが発生しました: {e}"
168
- )
169
- continue
170
- else:
171
- # 古い形式への対応(フォールバック)
172
- if hasattr(captures, "__iter__"):
173
- for capture in captures:
141
+ # 辞書形式: {capture_name: [nodes...]}
142
+ for capture_name, nodes in captures.items():
143
+ if isinstance(nodes, list):
144
+ for node in nodes:
174
145
  try:
175
- if isinstance(capture, tuple) and len(capture) == 2:
176
- node, capture_name = capture
177
- start_line = node.start_point[0] + 1
178
- end_line = node.end_point[0] + 1
179
- node_text = self.source_code_bytes[
180
- node.start_byte : node.end_byte
181
- ].decode("utf-8", errors="ignore")
182
-
183
- results.append(
184
- {
185
- "capture_name": capture_name,
186
- "content": node_text,
187
- "start_line": start_line,
188
- "end_line": end_line,
189
- "node_type": node.type,
190
- }
191
- )
146
+ start_line = node.start_point[0] + 1
147
+ end_line = node.end_point[0] + 1
148
+ node_text = self.source_code_bytes[
149
+ node.start_byte : node.end_byte
150
+ ].decode("utf-8", errors="ignore")
151
+
152
+ results.append(
153
+ {
154
+ "capture_name": capture_name,
155
+ "content": node_text,
156
+ "start_line": start_line,
157
+ "end_line": end_line,
158
+ "node_type": node.type,
159
+ }
160
+ )
192
161
  except Exception as e:
193
162
  output_warning(
194
163
  f"WARNING: ノード処理中にエラーが発生しました: {e}"
@@ -189,8 +189,8 @@ class LanguageLoader:
189
189
  self._availability_cache.clear()
190
190
  self._parser_cache.clear()
191
191
  self._unavailable_languages.clear()
192
- # LRUキャッシュもクリア
193
- self.is_language_available.cache_clear()
192
+ # 可用性キャッシュもクリア
193
+ self._availability_cache.clear()
194
194
 
195
195
 
196
196
  # グローバルインスタンス(最適化:シングルトンパターン)