tree-sitter-analyzer 1.9.17.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.
Files changed (149) hide show
  1. tree_sitter_analyzer/__init__.py +132 -0
  2. tree_sitter_analyzer/__main__.py +11 -0
  3. tree_sitter_analyzer/api.py +853 -0
  4. tree_sitter_analyzer/cli/__init__.py +39 -0
  5. tree_sitter_analyzer/cli/__main__.py +12 -0
  6. tree_sitter_analyzer/cli/argument_validator.py +89 -0
  7. tree_sitter_analyzer/cli/commands/__init__.py +26 -0
  8. tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
  9. tree_sitter_analyzer/cli/commands/base_command.py +181 -0
  10. tree_sitter_analyzer/cli/commands/default_command.py +18 -0
  11. tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
  12. tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
  13. tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
  14. tree_sitter_analyzer/cli/commands/query_command.py +109 -0
  15. tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
  16. tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
  17. tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
  18. tree_sitter_analyzer/cli/commands/table_command.py +414 -0
  19. tree_sitter_analyzer/cli/info_commands.py +124 -0
  20. tree_sitter_analyzer/cli_main.py +472 -0
  21. tree_sitter_analyzer/constants.py +85 -0
  22. tree_sitter_analyzer/core/__init__.py +15 -0
  23. tree_sitter_analyzer/core/analysis_engine.py +580 -0
  24. tree_sitter_analyzer/core/cache_service.py +333 -0
  25. tree_sitter_analyzer/core/engine.py +585 -0
  26. tree_sitter_analyzer/core/parser.py +293 -0
  27. tree_sitter_analyzer/core/query.py +605 -0
  28. tree_sitter_analyzer/core/query_filter.py +200 -0
  29. tree_sitter_analyzer/core/query_service.py +340 -0
  30. tree_sitter_analyzer/encoding_utils.py +530 -0
  31. tree_sitter_analyzer/exceptions.py +747 -0
  32. tree_sitter_analyzer/file_handler.py +246 -0
  33. tree_sitter_analyzer/formatters/__init__.py +1 -0
  34. tree_sitter_analyzer/formatters/base_formatter.py +201 -0
  35. tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
  36. tree_sitter_analyzer/formatters/formatter_config.py +197 -0
  37. tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
  38. tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
  39. tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
  40. tree_sitter_analyzer/formatters/go_formatter.py +368 -0
  41. tree_sitter_analyzer/formatters/html_formatter.py +498 -0
  42. tree_sitter_analyzer/formatters/java_formatter.py +423 -0
  43. tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
  44. tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
  45. tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
  46. tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
  47. tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
  48. tree_sitter_analyzer/formatters/php_formatter.py +301 -0
  49. tree_sitter_analyzer/formatters/python_formatter.py +830 -0
  50. tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
  51. tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
  52. tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
  53. tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
  54. tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
  55. tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
  56. tree_sitter_analyzer/interfaces/__init__.py +9 -0
  57. tree_sitter_analyzer/interfaces/cli.py +535 -0
  58. tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
  59. tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
  60. tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
  61. tree_sitter_analyzer/language_detector.py +553 -0
  62. tree_sitter_analyzer/language_loader.py +271 -0
  63. tree_sitter_analyzer/languages/__init__.py +10 -0
  64. tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
  65. tree_sitter_analyzer/languages/css_plugin.py +449 -0
  66. tree_sitter_analyzer/languages/go_plugin.py +836 -0
  67. tree_sitter_analyzer/languages/html_plugin.py +496 -0
  68. tree_sitter_analyzer/languages/java_plugin.py +1299 -0
  69. tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
  70. tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
  71. tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
  72. tree_sitter_analyzer/languages/php_plugin.py +862 -0
  73. tree_sitter_analyzer/languages/python_plugin.py +1636 -0
  74. tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
  75. tree_sitter_analyzer/languages/rust_plugin.py +673 -0
  76. tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
  77. tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
  78. tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
  79. tree_sitter_analyzer/legacy_table_formatter.py +860 -0
  80. tree_sitter_analyzer/mcp/__init__.py +34 -0
  81. tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
  82. tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
  83. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
  84. tree_sitter_analyzer/mcp/server.py +869 -0
  85. tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
  86. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
  87. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
  88. tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
  89. tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
  90. tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
  91. tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
  92. tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
  93. tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
  94. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
  95. tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
  96. tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
  97. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
  98. tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
  99. tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
  100. tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
  101. tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
  102. tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
  103. tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
  104. tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
  105. tree_sitter_analyzer/models.py +840 -0
  106. tree_sitter_analyzer/mypy_current_errors.txt +2 -0
  107. tree_sitter_analyzer/output_manager.py +255 -0
  108. tree_sitter_analyzer/platform_compat/__init__.py +3 -0
  109. tree_sitter_analyzer/platform_compat/adapter.py +324 -0
  110. tree_sitter_analyzer/platform_compat/compare.py +224 -0
  111. tree_sitter_analyzer/platform_compat/detector.py +67 -0
  112. tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
  113. tree_sitter_analyzer/platform_compat/profiles.py +217 -0
  114. tree_sitter_analyzer/platform_compat/record.py +55 -0
  115. tree_sitter_analyzer/platform_compat/recorder.py +155 -0
  116. tree_sitter_analyzer/platform_compat/report.py +92 -0
  117. tree_sitter_analyzer/plugins/__init__.py +280 -0
  118. tree_sitter_analyzer/plugins/base.py +647 -0
  119. tree_sitter_analyzer/plugins/manager.py +384 -0
  120. tree_sitter_analyzer/project_detector.py +328 -0
  121. tree_sitter_analyzer/queries/__init__.py +27 -0
  122. tree_sitter_analyzer/queries/csharp.py +216 -0
  123. tree_sitter_analyzer/queries/css.py +615 -0
  124. tree_sitter_analyzer/queries/go.py +275 -0
  125. tree_sitter_analyzer/queries/html.py +543 -0
  126. tree_sitter_analyzer/queries/java.py +402 -0
  127. tree_sitter_analyzer/queries/javascript.py +724 -0
  128. tree_sitter_analyzer/queries/kotlin.py +192 -0
  129. tree_sitter_analyzer/queries/markdown.py +258 -0
  130. tree_sitter_analyzer/queries/php.py +95 -0
  131. tree_sitter_analyzer/queries/python.py +859 -0
  132. tree_sitter_analyzer/queries/ruby.py +92 -0
  133. tree_sitter_analyzer/queries/rust.py +223 -0
  134. tree_sitter_analyzer/queries/sql.py +555 -0
  135. tree_sitter_analyzer/queries/typescript.py +871 -0
  136. tree_sitter_analyzer/queries/yaml.py +236 -0
  137. tree_sitter_analyzer/query_loader.py +272 -0
  138. tree_sitter_analyzer/security/__init__.py +22 -0
  139. tree_sitter_analyzer/security/boundary_manager.py +277 -0
  140. tree_sitter_analyzer/security/regex_checker.py +297 -0
  141. tree_sitter_analyzer/security/validator.py +599 -0
  142. tree_sitter_analyzer/table_formatter.py +782 -0
  143. tree_sitter_analyzer/utils/__init__.py +53 -0
  144. tree_sitter_analyzer/utils/logging.py +433 -0
  145. tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
  146. tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
  147. tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
  148. tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
  149. tree_sitter_analyzer-1.9.17.1.dist-info/entry_points.txt +25 -0
@@ -0,0 +1,647 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Plugin Base Classes
4
+
5
+ Defines the base interfaces for language plugins and element extractors.
6
+ All language plugins must inherit from these base classes.
7
+ """
8
+
9
+ import logging
10
+ from abc import ABC, abstractmethod
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ if TYPE_CHECKING:
14
+ import tree_sitter
15
+
16
+ from ..core.analysis_engine import AnalysisRequest
17
+ from ..models import AnalysisResult
18
+
19
+ from ..models import Class as ModelClass
20
+ from ..models import CodeElement
21
+ from ..models import Function as ModelFunction
22
+ from ..models import Import as ModelImport
23
+ from ..models import Variable as ModelVariable
24
+ from ..utils import log_debug, log_error
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class ElementExtractor(ABC):
30
+ """
31
+ Abstract base class for language-specific element extractors.
32
+
33
+ Element extractors are responsible for parsing ASTs and extracting
34
+ meaningful code elements like functions, classes, variables, etc.
35
+ """
36
+
37
+ def __init__(self) -> None:
38
+ """Initialize the element extractor."""
39
+ self.current_file: str = "" # Current file being processed
40
+
41
+ @abstractmethod
42
+ def extract_functions(
43
+ self, tree: "tree_sitter.Tree", source_code: str
44
+ ) -> list[ModelFunction]:
45
+ """
46
+ Extract function definitions from the syntax tree.
47
+
48
+ Args:
49
+ tree: Tree-sitter AST
50
+ source_code: Original source code
51
+
52
+ Returns:
53
+ List of extracted function objects
54
+ """
55
+ pass
56
+
57
+ @abstractmethod
58
+ def extract_classes(
59
+ self, tree: "tree_sitter.Tree", source_code: str
60
+ ) -> list[ModelClass]:
61
+ """
62
+ Extract class definitions from the syntax tree.
63
+
64
+ Args:
65
+ tree: Tree-sitter AST
66
+ source_code: Original source code
67
+
68
+ Returns:
69
+ List of extracted class objects
70
+ """
71
+ pass
72
+
73
+ @abstractmethod
74
+ def extract_variables(
75
+ self, tree: "tree_sitter.Tree", source_code: str
76
+ ) -> list[ModelVariable]:
77
+ """
78
+ Extract variable declarations from the syntax tree.
79
+
80
+ Args:
81
+ tree: Tree-sitter AST
82
+ source_code: Original source code
83
+
84
+ Returns:
85
+ List of extracted variable objects
86
+ """
87
+ pass
88
+
89
+ @abstractmethod
90
+ def extract_imports(
91
+ self, tree: "tree_sitter.Tree", source_code: str
92
+ ) -> list[ModelImport]:
93
+ """
94
+ Extract import statements from the syntax tree.
95
+
96
+ Args:
97
+ tree: Tree-sitter AST
98
+ source_code: Original source code
99
+
100
+ Returns:
101
+ List of extracted import objects
102
+ """
103
+ pass
104
+
105
+ def extract_packages(self, tree: "tree_sitter.Tree", source_code: str) -> list[Any]:
106
+ """
107
+ Extract package declarations from the syntax tree.
108
+
109
+ Args:
110
+ tree: Tree-sitter AST
111
+ source_code: Original source code
112
+
113
+ Returns:
114
+ List of extracted package objects
115
+ """
116
+ # Default implementation returns empty list
117
+ return []
118
+
119
+ def extract_annotations(
120
+ self, tree: "tree_sitter.Tree", source_code: str
121
+ ) -> list[Any]:
122
+ """
123
+ Extract annotations from the syntax tree.
124
+
125
+ Args:
126
+ tree: Tree-sitter AST
127
+ source_code: Original source code
128
+
129
+ Returns:
130
+ List of extracted annotation objects
131
+ """
132
+ # Default implementation returns empty list
133
+ return []
134
+
135
+ def extract_all_elements(
136
+ self, tree: "tree_sitter.Tree", source_code: str
137
+ ) -> list[CodeElement]:
138
+ """
139
+ Extract all code elements from the syntax tree.
140
+
141
+ Args:
142
+ tree: Tree-sitter AST
143
+ source_code: Original source code
144
+
145
+ Returns:
146
+ List of all extracted code elements
147
+ """
148
+ elements: list[CodeElement] = []
149
+
150
+ try:
151
+ elements.extend(self.extract_functions(tree, source_code))
152
+ elements.extend(self.extract_classes(tree, source_code))
153
+ elements.extend(self.extract_variables(tree, source_code))
154
+ elements.extend(self.extract_imports(tree, source_code))
155
+ except Exception as e:
156
+ log_error(f"Failed to extract all elements: {e}")
157
+
158
+ return elements
159
+
160
+ def extract_html_elements(
161
+ self, tree: "tree_sitter.Tree", source_code: str
162
+ ) -> list[Any]:
163
+ """
164
+ Extract HTML elements from the syntax tree.
165
+
166
+ Args:
167
+ tree: Tree-sitter AST
168
+ source_code: Original source code
169
+
170
+ Returns:
171
+ List of extracted HTML elements
172
+ """
173
+ # Default implementation returns empty list
174
+ return []
175
+
176
+ def extract_css_rules(
177
+ self, tree: "tree_sitter.Tree", source_code: str
178
+ ) -> list[Any]:
179
+ """
180
+ Extract CSS rules from the syntax tree.
181
+
182
+ Args:
183
+ tree: Tree-sitter AST
184
+ source_code: Original source code
185
+
186
+ Returns:
187
+ List of extracted CSS rules
188
+ """
189
+ # Default implementation returns empty list
190
+ return []
191
+
192
+
193
+ class LanguagePlugin(ABC):
194
+ """
195
+ Abstract base class for language-specific plugins.
196
+
197
+ Language plugins provide language-specific functionality including
198
+ element extraction, file extension mapping, and language identification.
199
+ """
200
+
201
+ @abstractmethod
202
+ def get_language_name(self) -> str:
203
+ """
204
+ Return the name of the programming language this plugin supports.
205
+
206
+ Returns:
207
+ Language name (e.g., "java", "python", "javascript")
208
+ """
209
+ pass
210
+
211
+ @abstractmethod
212
+ def get_file_extensions(self) -> list[str]:
213
+ """
214
+ Return list of file extensions this plugin supports.
215
+
216
+ Returns:
217
+ List of file extensions (e.g., [".java", ".class"])
218
+ """
219
+ pass
220
+
221
+ @abstractmethod
222
+ def create_extractor(self) -> ElementExtractor:
223
+ """
224
+ Create and return an element extractor for this language.
225
+
226
+ Returns:
227
+ ElementExtractor instance for this language
228
+ """
229
+ pass
230
+
231
+ @abstractmethod
232
+ async def analyze_file(
233
+ self, file_path: str, request: "AnalysisRequest"
234
+ ) -> "AnalysisResult":
235
+ """
236
+ Analyze a file and return analysis results.
237
+
238
+ Args:
239
+ file_path: Path to the file to analyze
240
+ request: Analysis request with configuration
241
+
242
+ Returns:
243
+ AnalysisResult containing extracted information
244
+ """
245
+ pass
246
+
247
+ def get_supported_element_types(self) -> list[str]:
248
+ """
249
+ Return list of supported CodeElement types.
250
+
251
+ Returns:
252
+ List of element types (e.g., ["function", "class", "variable"])
253
+ """
254
+ return ["function", "class", "variable", "import"]
255
+
256
+ def get_queries(self) -> dict[str, str]:
257
+ """
258
+ Return language-specific tree-sitter queries.
259
+
260
+ Returns:
261
+ Dictionary mapping query names to query strings
262
+ """
263
+ return {}
264
+
265
+ def execute_query_strategy(
266
+ self, query_key: str | None, language: str
267
+ ) -> str | None:
268
+ """
269
+ Execute query strategy for this language plugin.
270
+
271
+ Args:
272
+ query_key: Query key to execute
273
+ language: Programming language
274
+
275
+ Returns:
276
+ Query string or None if not supported
277
+ """
278
+ queries = self.get_queries()
279
+ return queries.get(query_key) if query_key else None
280
+
281
+ def get_formatter_map(self) -> dict[str, str]:
282
+ """
283
+ Return mapping of format types to formatter class names.
284
+
285
+ Returns:
286
+ Dictionary mapping format names to formatter classes
287
+ """
288
+ return {}
289
+
290
+ def get_element_categories(self) -> dict[str, list[str]]:
291
+ """
292
+ Return element categories for HTML/CSS languages.
293
+
294
+ Returns:
295
+ Dictionary mapping category names to element lists
296
+ """
297
+ return {}
298
+
299
+ def is_applicable(self, file_path: str) -> bool:
300
+ """
301
+ Check if this plugin is applicable for the given file.
302
+
303
+ Args:
304
+ file_path: Path to the file to check
305
+
306
+ Returns:
307
+ True if this plugin can handle the file
308
+ """
309
+ extensions = self.get_file_extensions()
310
+ return any(file_path.lower().endswith(ext.lower()) for ext in extensions)
311
+
312
+ def get_plugin_info(self) -> dict[str, Any]:
313
+ """
314
+ Get information about this plugin.
315
+
316
+ Returns:
317
+ Dictionary containing plugin information
318
+ """
319
+ return {
320
+ "language": self.get_language_name(),
321
+ "extensions": self.get_file_extensions(),
322
+ "class_name": self.__class__.__name__,
323
+ "module": self.__class__.__module__,
324
+ }
325
+
326
+
327
+ class DefaultExtractor(ElementExtractor):
328
+ """
329
+ Default implementation of ElementExtractor with basic functionality.
330
+
331
+ This extractor provides generic extraction logic that works across
332
+ multiple languages by looking for common node types.
333
+ """
334
+
335
+ def extract_functions(
336
+ self, tree: "tree_sitter.Tree", source_code: str
337
+ ) -> list[ModelFunction]:
338
+ """Basic function extraction implementation."""
339
+ functions: list[ModelFunction] = []
340
+
341
+ try:
342
+ if hasattr(tree, "root_node"):
343
+ lines = source_code.splitlines()
344
+ self._traverse_for_functions(
345
+ tree.root_node, functions, lines, source_code
346
+ )
347
+ except Exception as e:
348
+ log_error(f"Error in function extraction: {e}")
349
+
350
+ return functions
351
+
352
+ def extract_classes(
353
+ self, tree: "tree_sitter.Tree", source_code: str
354
+ ) -> list[ModelClass]:
355
+ """Basic class extraction implementation."""
356
+ classes: list[ModelClass] = []
357
+
358
+ try:
359
+ if hasattr(tree, "root_node"):
360
+ lines = source_code.splitlines()
361
+ self._traverse_for_classes(tree.root_node, classes, lines, source_code)
362
+ except Exception as e:
363
+ log_error(f"Error in class extraction: {e}")
364
+
365
+ return classes
366
+
367
+ def extract_variables(
368
+ self, tree: "tree_sitter.Tree", source_code: str
369
+ ) -> list[ModelVariable]:
370
+ """Basic variable extraction implementation."""
371
+ variables: list[ModelVariable] = []
372
+
373
+ try:
374
+ if hasattr(tree, "root_node"):
375
+ lines = source_code.splitlines()
376
+ self._traverse_for_variables(
377
+ tree.root_node, variables, lines, source_code
378
+ )
379
+ except Exception as e:
380
+ log_error(f"Error in variable extraction: {e}")
381
+
382
+ return variables
383
+
384
+ def extract_imports(
385
+ self, tree: "tree_sitter.Tree", source_code: str
386
+ ) -> list[ModelImport]:
387
+ """Basic import extraction implementation."""
388
+ imports: list[ModelImport] = []
389
+
390
+ try:
391
+ if hasattr(tree, "root_node"):
392
+ lines = source_code.splitlines()
393
+ self._traverse_for_imports(tree.root_node, imports, lines, source_code)
394
+ except Exception as e:
395
+ log_error(f"Error in import extraction: {e}")
396
+
397
+ return imports
398
+
399
+ def _traverse_for_functions(
400
+ self,
401
+ node: "tree_sitter.Node",
402
+ functions: list[ModelFunction],
403
+ lines: list[str],
404
+ source_code: str,
405
+ ) -> None:
406
+ """Traverse tree to find function-like nodes."""
407
+ if hasattr(node, "type") and self._is_function_node(node.type):
408
+ try:
409
+ name = self._extract_node_name(node, source_code) or "unknown"
410
+ raw_text = self._extract_node_text(node, source_code)
411
+
412
+ func = ModelFunction(
413
+ name=name,
414
+ start_line=(
415
+ node.start_point[0] + 1 if hasattr(node, "start_point") else 0
416
+ ),
417
+ end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
418
+ raw_text=raw_text,
419
+ language=self._get_language_hint(),
420
+ )
421
+ functions.append(func)
422
+ except Exception as e:
423
+ log_debug(f"Failed to extract function: {e}")
424
+
425
+ if hasattr(node, "children"):
426
+ for child in node.children:
427
+ self._traverse_for_functions(child, functions, lines, source_code)
428
+
429
+ def _traverse_for_classes(
430
+ self,
431
+ node: "tree_sitter.Node",
432
+ classes: list[ModelClass],
433
+ lines: list[str],
434
+ source_code: str,
435
+ ) -> None:
436
+ """Traverse tree to find class-like nodes."""
437
+ if hasattr(node, "type") and self._is_class_node(node.type):
438
+ try:
439
+ name = self._extract_node_name(node, source_code) or "unknown"
440
+ raw_text = self._extract_node_text(node, source_code)
441
+
442
+ cls = ModelClass(
443
+ name=name,
444
+ start_line=(
445
+ node.start_point[0] + 1 if hasattr(node, "start_point") else 0
446
+ ),
447
+ end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
448
+ raw_text=raw_text,
449
+ language=self._get_language_hint(),
450
+ )
451
+ classes.append(cls)
452
+ except Exception as e:
453
+ log_debug(f"Failed to extract class: {e}")
454
+
455
+ if hasattr(node, "children"):
456
+ for child in node.children:
457
+ self._traverse_for_classes(child, classes, lines, source_code)
458
+
459
+ def _traverse_for_variables(
460
+ self,
461
+ node: "tree_sitter.Node",
462
+ variables: list[ModelVariable],
463
+ lines: list[str],
464
+ source_code: str,
465
+ ) -> None:
466
+ """Traverse tree to find variable declarations."""
467
+ if hasattr(node, "type") and self._is_variable_node(node.type):
468
+ try:
469
+ name = self._extract_node_name(node, source_code) or "unknown"
470
+ raw_text = self._extract_node_text(node, source_code)
471
+
472
+ var = ModelVariable(
473
+ name=name,
474
+ start_line=(
475
+ node.start_point[0] + 1 if hasattr(node, "start_point") else 0
476
+ ),
477
+ end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
478
+ raw_text=raw_text,
479
+ language=self._get_language_hint(),
480
+ )
481
+ variables.append(var)
482
+ except Exception as e:
483
+ log_debug(f"Failed to extract variable: {e}")
484
+
485
+ if hasattr(node, "children"):
486
+ for child in node.children:
487
+ self._traverse_for_variables(child, variables, lines, source_code)
488
+
489
+ def _traverse_for_imports(
490
+ self,
491
+ node: "tree_sitter.Node",
492
+ imports: list[ModelImport],
493
+ lines: list[str],
494
+ source_code: str,
495
+ ) -> None:
496
+ """Traverse tree to find import statements."""
497
+ if hasattr(node, "type") and self._is_import_node(node.type):
498
+ try:
499
+ name = self._extract_node_name(node, source_code) or "unknown"
500
+ raw_text = self._extract_node_text(node, source_code)
501
+
502
+ imp = ModelImport(
503
+ name=name,
504
+ start_line=(
505
+ node.start_point[0] + 1 if hasattr(node, "start_point") else 0
506
+ ),
507
+ end_line=node.end_point[0] + 1 if hasattr(node, "end_point") else 0,
508
+ raw_text=raw_text,
509
+ language=self._get_language_hint(),
510
+ )
511
+ imports.append(imp)
512
+ except Exception as e:
513
+ log_debug(f"Failed to extract import: {e}")
514
+
515
+ if hasattr(node, "children"):
516
+ for child in node.children:
517
+ self._traverse_for_imports(child, imports, lines, source_code)
518
+
519
+ def _is_function_node(self, node_type: str) -> bool:
520
+ """Check if a node type represents a function."""
521
+ function_types = [
522
+ "function_definition",
523
+ "function_declaration",
524
+ "method_definition",
525
+ "function",
526
+ "method",
527
+ "procedure",
528
+ "subroutine",
529
+ ]
530
+ return any(ftype in node_type.lower() for ftype in function_types)
531
+
532
+ def _is_class_node(self, node_type: str) -> bool:
533
+ """Check if a node type represents a class."""
534
+ class_types = [
535
+ "class_definition",
536
+ "class_declaration",
537
+ "interface_definition",
538
+ "class",
539
+ "interface",
540
+ "struct",
541
+ "enum",
542
+ ]
543
+ return any(ctype in node_type.lower() for ctype in class_types)
544
+
545
+ def _is_variable_node(self, node_type: str) -> bool:
546
+ """Check if a node type represents a variable."""
547
+ variable_types = [
548
+ "variable_declaration",
549
+ "variable_definition",
550
+ "field_declaration",
551
+ "assignment",
552
+ "declaration",
553
+ "variable",
554
+ "field",
555
+ ]
556
+ return any(vtype in node_type.lower() for vtype in variable_types)
557
+
558
+ def _is_import_node(self, node_type: str) -> bool:
559
+ """Check if a node type represents an import."""
560
+ import_types = [
561
+ "import_statement",
562
+ "import_declaration",
563
+ "include_statement",
564
+ "import",
565
+ "include",
566
+ "require",
567
+ "use",
568
+ ]
569
+ return any(itype in node_type.lower() for itype in import_types)
570
+
571
+ def _extract_node_name(
572
+ self, node: "tree_sitter.Node", source_code: str
573
+ ) -> str | None:
574
+ """Extract name from a tree-sitter node."""
575
+ try:
576
+ # Look for identifier children
577
+ if hasattr(node, "children"):
578
+ for child in node.children:
579
+ if hasattr(child, "type") and child.type == "identifier":
580
+ return self._extract_node_text(child, source_code)
581
+
582
+ # Fallback: use position-based name
583
+ return f"element_{node.start_point[0]}_{node.start_point[1]}"
584
+ except Exception:
585
+ return None
586
+
587
+ def _extract_node_text(self, node: "tree_sitter.Node", source_code: str) -> str:
588
+ """Extract text content from a tree-sitter node."""
589
+ try:
590
+ if hasattr(node, "start_byte") and hasattr(node, "end_byte"):
591
+ source_bytes = source_code.encode("utf-8")
592
+ node_bytes = source_bytes[node.start_byte : node.end_byte]
593
+ return node_bytes.decode("utf-8", errors="replace")
594
+ return ""
595
+ except Exception as e:
596
+ log_debug(f"Failed to extract node text: {e}")
597
+ return ""
598
+
599
+ def _get_language_hint(self) -> str:
600
+ """Get a hint about the language being processed."""
601
+ return "unknown"
602
+
603
+
604
+ class DefaultLanguagePlugin(LanguagePlugin):
605
+ """Default plugin that provides basic functionality for any language."""
606
+
607
+ def get_language_name(self) -> str:
608
+ return "generic"
609
+
610
+ def get_file_extensions(self) -> list[str]:
611
+ return [".txt", ".md"] # Fallback extensions
612
+
613
+ def create_extractor(self) -> ElementExtractor:
614
+ return DefaultExtractor()
615
+
616
+ async def analyze_file(
617
+ self, file_path: str, request: "AnalysisRequest"
618
+ ) -> "AnalysisResult":
619
+ """
620
+ Analyze a file using the default extractor.
621
+
622
+ Args:
623
+ file_path: Path to the file to analyze
624
+ request: Analysis request with configuration
625
+
626
+ Returns:
627
+ AnalysisResult containing extracted information
628
+ """
629
+ from ..core.analysis_engine import UnifiedAnalysisEngine
630
+ from ..models import AnalysisResult
631
+
632
+ try:
633
+ engine = UnifiedAnalysisEngine()
634
+ return await engine.analyze_file(file_path)
635
+ except Exception as e:
636
+ log_error(f"Failed to analyze file {file_path}: {e}")
637
+ return AnalysisResult(
638
+ file_path=file_path,
639
+ language=self.get_language_name(),
640
+ line_count=0,
641
+ elements=[],
642
+ node_count=0,
643
+ query_results={},
644
+ source_code="",
645
+ success=False,
646
+ error_message=str(e),
647
+ )