tree-sitter-analyzer 0.2.0__py3-none-any.whl → 0.3.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 (78) hide show
  1. tree_sitter_analyzer/__init__.py +133 -121
  2. tree_sitter_analyzer/__main__.py +11 -12
  3. tree_sitter_analyzer/api.py +531 -539
  4. tree_sitter_analyzer/cli/__init__.py +39 -39
  5. tree_sitter_analyzer/cli/__main__.py +12 -13
  6. tree_sitter_analyzer/cli/commands/__init__.py +26 -27
  7. tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
  8. tree_sitter_analyzer/cli/commands/base_command.py +160 -155
  9. tree_sitter_analyzer/cli/commands/default_command.py +18 -19
  10. tree_sitter_analyzer/cli/commands/partial_read_command.py +141 -133
  11. tree_sitter_analyzer/cli/commands/query_command.py +81 -82
  12. tree_sitter_analyzer/cli/commands/structure_command.py +138 -121
  13. tree_sitter_analyzer/cli/commands/summary_command.py +101 -93
  14. tree_sitter_analyzer/cli/commands/table_command.py +232 -233
  15. tree_sitter_analyzer/cli/info_commands.py +120 -121
  16. tree_sitter_analyzer/cli_main.py +277 -276
  17. tree_sitter_analyzer/core/__init__.py +15 -20
  18. tree_sitter_analyzer/core/analysis_engine.py +591 -574
  19. tree_sitter_analyzer/core/cache_service.py +320 -330
  20. tree_sitter_analyzer/core/engine.py +557 -560
  21. tree_sitter_analyzer/core/parser.py +293 -288
  22. tree_sitter_analyzer/core/query.py +494 -502
  23. tree_sitter_analyzer/encoding_utils.py +458 -460
  24. tree_sitter_analyzer/exceptions.py +337 -340
  25. tree_sitter_analyzer/file_handler.py +217 -222
  26. tree_sitter_analyzer/formatters/__init__.py +1 -1
  27. tree_sitter_analyzer/formatters/base_formatter.py +167 -168
  28. tree_sitter_analyzer/formatters/formatter_factory.py +78 -74
  29. tree_sitter_analyzer/formatters/java_formatter.py +287 -270
  30. tree_sitter_analyzer/formatters/python_formatter.py +255 -235
  31. tree_sitter_analyzer/interfaces/__init__.py +9 -10
  32. tree_sitter_analyzer/interfaces/cli.py +528 -557
  33. tree_sitter_analyzer/interfaces/cli_adapter.py +322 -319
  34. tree_sitter_analyzer/interfaces/mcp_adapter.py +180 -170
  35. tree_sitter_analyzer/interfaces/mcp_server.py +405 -416
  36. tree_sitter_analyzer/java_analyzer.py +218 -219
  37. tree_sitter_analyzer/language_detector.py +398 -400
  38. tree_sitter_analyzer/language_loader.py +224 -228
  39. tree_sitter_analyzer/languages/__init__.py +10 -11
  40. tree_sitter_analyzer/languages/java_plugin.py +1129 -1113
  41. tree_sitter_analyzer/languages/python_plugin.py +737 -712
  42. tree_sitter_analyzer/mcp/__init__.py +31 -32
  43. tree_sitter_analyzer/mcp/resources/__init__.py +44 -47
  44. tree_sitter_analyzer/mcp/resources/code_file_resource.py +212 -213
  45. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +560 -550
  46. tree_sitter_analyzer/mcp/server.py +333 -345
  47. tree_sitter_analyzer/mcp/tools/__init__.py +30 -31
  48. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +621 -557
  49. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +242 -245
  50. tree_sitter_analyzer/mcp/tools/base_tool.py +54 -55
  51. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +300 -302
  52. tree_sitter_analyzer/mcp/tools/table_format_tool.py +362 -359
  53. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +543 -476
  54. tree_sitter_analyzer/mcp/utils/__init__.py +105 -106
  55. tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
  56. tree_sitter_analyzer/models.py +470 -481
  57. tree_sitter_analyzer/output_manager.py +261 -264
  58. tree_sitter_analyzer/plugins/__init__.py +333 -334
  59. tree_sitter_analyzer/plugins/base.py +477 -446
  60. tree_sitter_analyzer/plugins/java_plugin.py +608 -625
  61. tree_sitter_analyzer/plugins/javascript_plugin.py +446 -439
  62. tree_sitter_analyzer/plugins/manager.py +362 -355
  63. tree_sitter_analyzer/plugins/plugin_loader.py +85 -83
  64. tree_sitter_analyzer/plugins/python_plugin.py +606 -598
  65. tree_sitter_analyzer/plugins/registry.py +374 -366
  66. tree_sitter_analyzer/queries/__init__.py +26 -27
  67. tree_sitter_analyzer/queries/java.py +391 -394
  68. tree_sitter_analyzer/queries/javascript.py +148 -149
  69. tree_sitter_analyzer/queries/python.py +285 -286
  70. tree_sitter_analyzer/queries/typescript.py +229 -230
  71. tree_sitter_analyzer/query_loader.py +254 -260
  72. tree_sitter_analyzer/table_formatter.py +468 -448
  73. tree_sitter_analyzer/utils.py +277 -277
  74. {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/METADATA +21 -6
  75. tree_sitter_analyzer-0.3.0.dist-info/RECORD +77 -0
  76. tree_sitter_analyzer-0.2.0.dist-info/RECORD +0 -77
  77. {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/WHEEL +0 -0
  78. {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/entry_points.txt +0 -0
@@ -1,334 +1,333 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Plugin System for Multi-Language Code Analysis
5
-
6
- This package provides a plugin-based architecture for extending
7
- the tree-sitter analyzer with language-specific parsers and extractors.
8
- """
9
-
10
- from abc import ABC, abstractmethod
11
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type
12
-
13
- if TYPE_CHECKING:
14
- import tree_sitter
15
-
16
- from ..models import Class as ModelClass
17
- from ..models import (
18
- CodeElement,
19
- )
20
- from ..models import Function as ModelFunction
21
- from ..models import Import as ModelImport
22
- from ..models import Variable as ModelVariable
23
- from ..utils import log_debug, log_error, log_warning
24
-
25
- __all__ = ["LanguagePlugin", "ElementExtractor", "PluginRegistry", "plugin_registry"]
26
-
27
-
28
- class ElementExtractor(ABC):
29
- """Abstract base class for language-specific element extractors"""
30
-
31
- @abstractmethod
32
- def extract_functions(
33
- self, tree: "tree_sitter.Tree", source_code: str
34
- ) -> List[ModelFunction]:
35
- """Extract function definitions from the syntax tree"""
36
- log_warning("extract_functions not implemented in subclass")
37
- return []
38
-
39
- @abstractmethod
40
- def extract_classes(
41
- self, tree: "tree_sitter.Tree", source_code: str
42
- ) -> List[ModelClass]:
43
- """Extract class definitions from the syntax tree"""
44
- log_warning("extract_classes not implemented in subclass")
45
- return []
46
-
47
- @abstractmethod
48
- def extract_variables(
49
- self, tree: "tree_sitter.Tree", source_code: str
50
- ) -> List[ModelVariable]:
51
- """Extract variable declarations from the syntax tree"""
52
- log_warning("extract_variables not implemented in subclass")
53
- return []
54
-
55
- @abstractmethod
56
- def extract_imports(
57
- self, tree: "tree_sitter.Tree", source_code: str
58
- ) -> List[ModelImport]:
59
- """Extract import statements from the syntax tree"""
60
- log_warning("extract_imports not implemented in subclass")
61
- return []
62
-
63
-
64
- class LanguagePlugin(ABC):
65
- """Abstract base class for language-specific plugins"""
66
-
67
- @abstractmethod
68
- def get_language_name(self) -> str:
69
- """Return the name of the programming language this plugin supports"""
70
- return "unknown"
71
-
72
- @abstractmethod
73
- def get_file_extensions(self) -> List[str]:
74
- """Return list of file extensions this plugin supports"""
75
- return []
76
-
77
- @abstractmethod
78
- def create_extractor(self) -> ElementExtractor:
79
- """Create and return an element extractor for this language"""
80
- return DefaultExtractor()
81
-
82
- def is_applicable(self, file_path: str) -> bool:
83
- """Check if this plugin is applicable for the given file"""
84
- extensions = self.get_file_extensions()
85
- return any(file_path.lower().endswith(ext.lower()) for ext in extensions)
86
-
87
-
88
- class DefaultExtractor(ElementExtractor):
89
- """Default implementation of ElementExtractor with basic functionality"""
90
-
91
- def extract_functions(
92
- self, tree: "tree_sitter.Tree", source_code: str
93
- ) -> List[ModelFunction]:
94
- """Basic function extraction implementation"""
95
- functions: List[ModelFunction] = []
96
- try:
97
- if hasattr(tree, "root_node"):
98
- # Generic function extraction logic
99
- self._traverse_for_functions(
100
- tree.root_node, functions, source_code.splitlines()
101
- )
102
- except Exception as e:
103
- log_error(f"Error in function extraction: {e}")
104
- return functions
105
-
106
- def extract_classes(
107
- self, tree: "tree_sitter.Tree", source_code: str
108
- ) -> List[ModelClass]:
109
- """Basic class extraction implementation"""
110
- classes: List[ModelClass] = []
111
- try:
112
- if hasattr(tree, "root_node"):
113
- # Generic class extraction logic
114
- self._traverse_for_classes(
115
- tree.root_node, classes, source_code.splitlines()
116
- )
117
- except Exception as e:
118
- log_error(f"Error in class extraction: {e}")
119
- return classes
120
-
121
- def extract_variables(
122
- self, tree: "tree_sitter.Tree", source_code: str
123
- ) -> List[ModelVariable]:
124
- """Basic variable extraction implementation"""
125
- variables: List[ModelVariable] = []
126
- try:
127
- if hasattr(tree, "root_node"):
128
- # Generic variable extraction logic
129
- self._traverse_for_variables(
130
- tree.root_node, variables, source_code.splitlines()
131
- )
132
- except Exception as e:
133
- log_error(f"Error in variable extraction: {e}")
134
- return variables
135
-
136
- def extract_imports(
137
- self, tree: "tree_sitter.Tree", source_code: str
138
- ) -> List[ModelImport]:
139
- """Basic import extraction implementation"""
140
- imports: List[ModelImport] = []
141
- try:
142
- if hasattr(tree, "root_node"):
143
- # Generic import extraction logic
144
- self._traverse_for_imports(
145
- tree.root_node, imports, source_code.splitlines()
146
- )
147
- except Exception as e:
148
- log_error(f"Error in import extraction: {e}")
149
- return imports
150
-
151
- def _traverse_for_functions(
152
- self, node: "tree_sitter.Node", functions: List[ModelFunction], lines: List[str]
153
- ) -> None:
154
- """Traverse tree to find function-like nodes"""
155
- if hasattr(node, "type") and "function" in node.type.lower():
156
- try:
157
- func = ModelFunction(
158
- name=self._extract_node_name(node) or "unknown",
159
- start_line=(
160
- node.start_point[0] + 1 if hasattr(node, "start_point") else 0
161
- ),
162
- end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
163
- raw_text="",
164
- language="unknown",
165
- )
166
- functions.append(func)
167
- except Exception as e:
168
- log_debug(f"Failed to extract function: {e}")
169
-
170
- if hasattr(node, "children"):
171
- for child in node.children:
172
- self._traverse_for_functions(child, functions, lines)
173
-
174
- def _traverse_for_classes(
175
- self, node: "tree_sitter.Node", classes: List[ModelClass], lines: List[str]
176
- ) -> None:
177
- """Traverse tree to find class-like nodes"""
178
- if hasattr(node, "type") and "class" in node.type.lower():
179
- try:
180
- cls = ModelClass(
181
- name=self._extract_node_name(node) or "unknown",
182
- start_line=(
183
- node.start_point[0] + 1 if hasattr(node, "start_point") else 0
184
- ),
185
- end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
186
- raw_text="",
187
- language="unknown",
188
- )
189
- classes.append(cls)
190
- except Exception as e:
191
- log_debug(f"Failed to extract class: {e}")
192
-
193
- if hasattr(node, "children"):
194
- for child in node.children:
195
- self._traverse_for_classes(child, classes, lines)
196
-
197
- def _traverse_for_variables(
198
- self, node: "tree_sitter.Node", variables: List[ModelVariable], lines: List[str]
199
- ) -> None:
200
- """Traverse tree to find variable declarations"""
201
- if hasattr(node, "type") and (
202
- "variable" in node.type.lower() or "declaration" in node.type.lower()
203
- ):
204
- try:
205
- var = ModelVariable(
206
- name=self._extract_node_name(node) or "unknown",
207
- start_line=(
208
- node.start_point[0] + 1 if hasattr(node, "start_point") else 0
209
- ),
210
- end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
211
- raw_text="",
212
- language="unknown",
213
- )
214
- variables.append(var)
215
- except Exception as e:
216
- log_debug(f"Failed to extract variable: {e}")
217
-
218
- if hasattr(node, "children"):
219
- for child in node.children:
220
- self._traverse_for_variables(child, variables, lines)
221
-
222
- def _traverse_for_imports(
223
- self, node: "tree_sitter.Node", imports: List[ModelImport], lines: List[str]
224
- ) -> None:
225
- """Traverse tree to find import statements"""
226
- if hasattr(node, "type") and "import" in node.type.lower():
227
- try:
228
- imp = ModelImport(
229
- name=self._extract_node_name(node) or "unknown",
230
- start_line=(
231
- node.start_point[0] + 1 if hasattr(node, "start_point") else 0
232
- ),
233
- end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
234
- raw_text="",
235
- language="unknown",
236
- )
237
- imports.append(imp)
238
- except Exception as e:
239
- log_debug(f"Failed to extract import: {e}")
240
-
241
- if hasattr(node, "children"):
242
- for child in node.children:
243
- self._traverse_for_imports(child, imports, lines)
244
-
245
- def _extract_node_name(self, node: "tree_sitter.Node") -> Optional[str]:
246
- """Extract name from a tree-sitter node"""
247
- try:
248
- # Look for identifier children
249
- if hasattr(node, "children"):
250
- for child in node.children:
251
- if hasattr(child, "type") and child.type == "identifier":
252
- # This would need actual text extraction in real implementation
253
- return f"element_{child.start_point[0]}_{child.start_point[1]}"
254
- return None
255
- except Exception:
256
- return None
257
-
258
-
259
- class PluginRegistry:
260
- """Registry for managing language plugins"""
261
-
262
- def __init__(self) -> None:
263
- self._plugins: Dict[str, LanguagePlugin] = {}
264
- self._extension_map: Dict[str, LanguagePlugin] = {}
265
- self._default_plugin = DefaultLanguagePlugin()
266
-
267
- def register_plugin(self, plugin: LanguagePlugin) -> None:
268
- """Register a language plugin"""
269
- try:
270
- language = plugin.get_language_name()
271
- self._plugins[language] = plugin
272
-
273
- # Register file extensions
274
- for ext in plugin.get_file_extensions():
275
- self._extension_map[ext] = plugin
276
-
277
- log_debug(f"Registered plugin for language: {language}")
278
- except Exception as e:
279
- log_error(f"Failed to register plugin: {e}")
280
-
281
- def get_plugin(self, language: str) -> Optional[LanguagePlugin]:
282
- """Get plugin for specified language"""
283
- return self._plugins.get(language)
284
-
285
- def get_plugin_by_extension(self, extension: str) -> Optional[LanguagePlugin]:
286
- """Get plugin for specified file extension"""
287
- return self._extension_map.get(extension)
288
-
289
- def get_plugin_for_file(self, file_path: str) -> LanguagePlugin:
290
- """Get appropriate plugin for a file"""
291
- for plugin in self._plugins.values():
292
- if plugin.is_applicable(file_path):
293
- return plugin
294
- return self._default_plugin
295
-
296
- def list_supported_languages(self) -> List[str]:
297
- """List all supported languages"""
298
- return list(self._plugins.keys())
299
-
300
- def list_supported_extensions(self) -> List[str]:
301
- """List all supported file extensions"""
302
- return list(self._extension_map.keys())
303
-
304
-
305
- class DefaultLanguagePlugin(LanguagePlugin):
306
- """Default plugin that provides basic functionality for any language"""
307
-
308
- def get_language_name(self) -> str:
309
- return "generic"
310
-
311
- def get_file_extensions(self) -> List[str]:
312
- return [".txt", ".md"] # Fallback extensions
313
-
314
- def create_extractor(self) -> ElementExtractor:
315
- return DefaultExtractor()
316
-
317
-
318
- # Global plugin registry instance
319
- plugin_registry = PluginRegistry()
320
-
321
-
322
- # Auto-load all plugins when the package is imported
323
- try:
324
- from .plugin_loader import (
325
- get_supported_extensions,
326
- get_supported_languages,
327
- load_all_plugins,
328
- )
329
-
330
- # Plugins are automatically loaded when plugin_loader is imported
331
- except ImportError as e:
332
- from ..utils import log_warning
333
-
334
- log_warning(f"Could not load plugin loader: {e}")
1
+ #!/usr/bin/env python3
2
+ """
3
+ Plugin System for Multi-Language Code Analysis
4
+
5
+ This package provides a plugin-based architecture for extending
6
+ the tree-sitter analyzer with language-specific parsers and extractors.
7
+ """
8
+
9
+ from abc import ABC, abstractmethod
10
+ from typing import TYPE_CHECKING
11
+
12
+ if TYPE_CHECKING:
13
+ import tree_sitter
14
+
15
+ # from ..models import (
16
+ # CodeElement,
17
+ # ) # Not used currently
18
+ from ..models import Class as ModelClass
19
+ from ..models import Function as ModelFunction
20
+ from ..models import Import as ModelImport
21
+ from ..models import Variable as ModelVariable
22
+ from ..utils import log_debug, log_error, log_warning
23
+
24
+ __all__ = ["LanguagePlugin", "ElementExtractor", "PluginRegistry", "plugin_registry"]
25
+
26
+
27
+ class ElementExtractor(ABC):
28
+ """Abstract base class for language-specific element extractors"""
29
+
30
+ @abstractmethod
31
+ def extract_functions(
32
+ self, tree: "tree_sitter.Tree", source_code: str
33
+ ) -> list[ModelFunction]:
34
+ """Extract function definitions from the syntax tree"""
35
+ log_warning("extract_functions not implemented in subclass")
36
+ return []
37
+
38
+ @abstractmethod
39
+ def extract_classes(
40
+ self, tree: "tree_sitter.Tree", source_code: str
41
+ ) -> list[ModelClass]:
42
+ """Extract class definitions from the syntax tree"""
43
+ log_warning("extract_classes not implemented in subclass")
44
+ return []
45
+
46
+ @abstractmethod
47
+ def extract_variables(
48
+ self, tree: "tree_sitter.Tree", source_code: str
49
+ ) -> list[ModelVariable]:
50
+ """Extract variable declarations from the syntax tree"""
51
+ log_warning("extract_variables not implemented in subclass")
52
+ return []
53
+
54
+ @abstractmethod
55
+ def extract_imports(
56
+ self, tree: "tree_sitter.Tree", source_code: str
57
+ ) -> list[ModelImport]:
58
+ """Extract import statements from the syntax tree"""
59
+ log_warning("extract_imports not implemented in subclass")
60
+ return []
61
+
62
+
63
+ class LanguagePlugin(ABC):
64
+ """Abstract base class for language-specific plugins"""
65
+
66
+ @abstractmethod
67
+ def get_language_name(self) -> str:
68
+ """Return the name of the programming language this plugin supports"""
69
+ return "unknown"
70
+
71
+ @abstractmethod
72
+ def get_file_extensions(self) -> list[str]:
73
+ """Return list of file extensions this plugin supports"""
74
+ return []
75
+
76
+ @abstractmethod
77
+ def create_extractor(self) -> ElementExtractor:
78
+ """Create and return an element extractor for this language"""
79
+ return DefaultExtractor()
80
+
81
+ def is_applicable(self, file_path: str) -> bool:
82
+ """Check if this plugin is applicable for the given file"""
83
+ extensions = self.get_file_extensions()
84
+ return any(file_path.lower().endswith(ext.lower()) for ext in extensions)
85
+
86
+
87
+ class DefaultExtractor(ElementExtractor):
88
+ """Default implementation of ElementExtractor with basic functionality"""
89
+
90
+ def extract_functions(
91
+ self, tree: "tree_sitter.Tree", source_code: str
92
+ ) -> list[ModelFunction]:
93
+ """Basic function extraction implementation"""
94
+ functions: list[ModelFunction] = []
95
+ try:
96
+ if hasattr(tree, "root_node"):
97
+ # Generic function extraction logic
98
+ self._traverse_for_functions(
99
+ tree.root_node, functions, source_code.splitlines()
100
+ )
101
+ except Exception as e:
102
+ log_error(f"Error in function extraction: {e}")
103
+ return functions
104
+
105
+ def extract_classes(
106
+ self, tree: "tree_sitter.Tree", source_code: str
107
+ ) -> list[ModelClass]:
108
+ """Basic class extraction implementation"""
109
+ classes: list[ModelClass] = []
110
+ try:
111
+ if hasattr(tree, "root_node"):
112
+ # Generic class extraction logic
113
+ self._traverse_for_classes(
114
+ tree.root_node, classes, source_code.splitlines()
115
+ )
116
+ except Exception as e:
117
+ log_error(f"Error in class extraction: {e}")
118
+ return classes
119
+
120
+ def extract_variables(
121
+ self, tree: "tree_sitter.Tree", source_code: str
122
+ ) -> list[ModelVariable]:
123
+ """Basic variable extraction implementation"""
124
+ variables: list[ModelVariable] = []
125
+ try:
126
+ if hasattr(tree, "root_node"):
127
+ # Generic variable extraction logic
128
+ self._traverse_for_variables(
129
+ tree.root_node, variables, source_code.splitlines()
130
+ )
131
+ except Exception as e:
132
+ log_error(f"Error in variable extraction: {e}")
133
+ return variables
134
+
135
+ def extract_imports(
136
+ self, tree: "tree_sitter.Tree", source_code: str
137
+ ) -> list[ModelImport]:
138
+ """Basic import extraction implementation"""
139
+ imports: list[ModelImport] = []
140
+ try:
141
+ if hasattr(tree, "root_node"):
142
+ # Generic import extraction logic
143
+ self._traverse_for_imports(
144
+ tree.root_node, imports, source_code.splitlines()
145
+ )
146
+ except Exception as e:
147
+ log_error(f"Error in import extraction: {e}")
148
+ return imports
149
+
150
+ def _traverse_for_functions(
151
+ self, node: "tree_sitter.Node", functions: list[ModelFunction], lines: list[str]
152
+ ) -> None:
153
+ """Traverse tree to find function-like nodes"""
154
+ if hasattr(node, "type") and "function" in node.type.lower():
155
+ try:
156
+ func = ModelFunction(
157
+ name=self._extract_node_name(node) or "unknown",
158
+ start_line=(
159
+ node.start_point[0] + 1 if hasattr(node, "start_point") else 0
160
+ ),
161
+ end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
162
+ raw_text="",
163
+ language="unknown",
164
+ )
165
+ functions.append(func)
166
+ except Exception as e:
167
+ log_debug(f"Failed to extract function: {e}")
168
+
169
+ if hasattr(node, "children"):
170
+ for child in node.children:
171
+ self._traverse_for_functions(child, functions, lines)
172
+
173
+ def _traverse_for_classes(
174
+ self, node: "tree_sitter.Node", classes: list[ModelClass], lines: list[str]
175
+ ) -> None:
176
+ """Traverse tree to find class-like nodes"""
177
+ if hasattr(node, "type") and "class" in node.type.lower():
178
+ try:
179
+ cls = ModelClass(
180
+ name=self._extract_node_name(node) or "unknown",
181
+ start_line=(
182
+ node.start_point[0] + 1 if hasattr(node, "start_point") else 0
183
+ ),
184
+ end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
185
+ raw_text="",
186
+ language="unknown",
187
+ )
188
+ classes.append(cls)
189
+ except Exception as e:
190
+ log_debug(f"Failed to extract class: {e}")
191
+
192
+ if hasattr(node, "children"):
193
+ for child in node.children:
194
+ self._traverse_for_classes(child, classes, lines)
195
+
196
+ def _traverse_for_variables(
197
+ self, node: "tree_sitter.Node", variables: list[ModelVariable], lines: list[str]
198
+ ) -> None:
199
+ """Traverse tree to find variable declarations"""
200
+ if hasattr(node, "type") and (
201
+ "variable" in node.type.lower() or "declaration" in node.type.lower()
202
+ ):
203
+ try:
204
+ var = ModelVariable(
205
+ name=self._extract_node_name(node) or "unknown",
206
+ start_line=(
207
+ node.start_point[0] + 1 if hasattr(node, "start_point") else 0
208
+ ),
209
+ end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
210
+ raw_text="",
211
+ language="unknown",
212
+ )
213
+ variables.append(var)
214
+ except Exception as e:
215
+ log_debug(f"Failed to extract variable: {e}")
216
+
217
+ if hasattr(node, "children"):
218
+ for child in node.children:
219
+ self._traverse_for_variables(child, variables, lines)
220
+
221
+ def _traverse_for_imports(
222
+ self, node: "tree_sitter.Node", imports: list[ModelImport], lines: list[str]
223
+ ) -> None:
224
+ """Traverse tree to find import statements"""
225
+ if hasattr(node, "type") and "import" in node.type.lower():
226
+ try:
227
+ imp = ModelImport(
228
+ name=self._extract_node_name(node) or "unknown",
229
+ start_line=(
230
+ node.start_point[0] + 1 if hasattr(node, "start_point") else 0
231
+ ),
232
+ end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
233
+ raw_text="",
234
+ language="unknown",
235
+ )
236
+ imports.append(imp)
237
+ except Exception as e:
238
+ log_debug(f"Failed to extract import: {e}")
239
+
240
+ if hasattr(node, "children"):
241
+ for child in node.children:
242
+ self._traverse_for_imports(child, imports, lines)
243
+
244
+ def _extract_node_name(self, node: "tree_sitter.Node") -> str | None:
245
+ """Extract name from a tree-sitter node"""
246
+ try:
247
+ # Look for identifier children
248
+ if hasattr(node, "children"):
249
+ for child in node.children:
250
+ if hasattr(child, "type") and child.type == "identifier":
251
+ # This would need actual text extraction in real implementation
252
+ return f"element_{child.start_point[0]}_{child.start_point[1]}"
253
+ return None
254
+ except Exception:
255
+ return None
256
+
257
+
258
+ class PluginRegistry:
259
+ """Registry for managing language plugins"""
260
+
261
+ def __init__(self) -> None:
262
+ self._plugins: dict[str, LanguagePlugin] = {}
263
+ self._extension_map: dict[str, LanguagePlugin] = {}
264
+ self._default_plugin = DefaultLanguagePlugin()
265
+
266
+ def register_plugin(self, plugin: LanguagePlugin) -> None:
267
+ """Register a language plugin"""
268
+ try:
269
+ language = plugin.get_language_name()
270
+ self._plugins[language] = plugin
271
+
272
+ # Register file extensions
273
+ for ext in plugin.get_file_extensions():
274
+ self._extension_map[ext] = plugin
275
+
276
+ log_debug(f"Registered plugin for language: {language}")
277
+ except Exception as e:
278
+ log_error(f"Failed to register plugin: {e}")
279
+
280
+ def get_plugin(self, language: str) -> LanguagePlugin | None:
281
+ """Get plugin for specified language"""
282
+ return self._plugins.get(language)
283
+
284
+ def get_plugin_by_extension(self, extension: str) -> LanguagePlugin | None:
285
+ """Get plugin for specified file extension"""
286
+ return self._extension_map.get(extension)
287
+
288
+ def get_plugin_for_file(self, file_path: str) -> LanguagePlugin:
289
+ """Get appropriate plugin for a file"""
290
+ for plugin in self._plugins.values():
291
+ if plugin.is_applicable(file_path):
292
+ return plugin
293
+ return self._default_plugin
294
+
295
+ def list_supported_languages(self) -> list[str]:
296
+ """List all supported languages"""
297
+ return list(self._plugins.keys())
298
+
299
+ def list_supported_extensions(self) -> list[str]:
300
+ """List all supported file extensions"""
301
+ return list(self._extension_map.keys())
302
+
303
+
304
+ class DefaultLanguagePlugin(LanguagePlugin):
305
+ """Default plugin that provides basic functionality for any language"""
306
+
307
+ def get_language_name(self) -> str:
308
+ return "generic"
309
+
310
+ def get_file_extensions(self) -> list[str]:
311
+ return [".txt", ".md"] # Fallback extensions
312
+
313
+ def create_extractor(self) -> ElementExtractor:
314
+ return DefaultExtractor()
315
+
316
+
317
+ # Global plugin registry instance
318
+ plugin_registry = PluginRegistry()
319
+
320
+
321
+ # Auto-load all plugins when the package is imported
322
+ try:
323
+ # from .plugin_loader import (
324
+ # get_supported_extensions, # Not used currently
325
+ # get_supported_languages, # Not used currently
326
+ # load_all_plugins, # Not used currently
327
+ # )
328
+ pass
329
+ # Plugins are automatically loaded when plugin_loader is imported
330
+ except ImportError as e:
331
+ from ..utils import log_warning
332
+
333
+ log_warning(f"Could not load plugin loader: {e}")