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.
- tree_sitter_analyzer/__init__.py +132 -0
- tree_sitter_analyzer/__main__.py +11 -0
- tree_sitter_analyzer/api.py +853 -0
- tree_sitter_analyzer/cli/__init__.py +39 -0
- tree_sitter_analyzer/cli/__main__.py +12 -0
- tree_sitter_analyzer/cli/argument_validator.py +89 -0
- tree_sitter_analyzer/cli/commands/__init__.py +26 -0
- tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
- tree_sitter_analyzer/cli/commands/base_command.py +181 -0
- tree_sitter_analyzer/cli/commands/default_command.py +18 -0
- tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
- tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
- tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
- tree_sitter_analyzer/cli/commands/query_command.py +109 -0
- tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
- tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
- tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
- tree_sitter_analyzer/cli/commands/table_command.py +414 -0
- tree_sitter_analyzer/cli/info_commands.py +124 -0
- tree_sitter_analyzer/cli_main.py +472 -0
- tree_sitter_analyzer/constants.py +85 -0
- tree_sitter_analyzer/core/__init__.py +15 -0
- tree_sitter_analyzer/core/analysis_engine.py +580 -0
- tree_sitter_analyzer/core/cache_service.py +333 -0
- tree_sitter_analyzer/core/engine.py +585 -0
- tree_sitter_analyzer/core/parser.py +293 -0
- tree_sitter_analyzer/core/query.py +605 -0
- tree_sitter_analyzer/core/query_filter.py +200 -0
- tree_sitter_analyzer/core/query_service.py +340 -0
- tree_sitter_analyzer/encoding_utils.py +530 -0
- tree_sitter_analyzer/exceptions.py +747 -0
- tree_sitter_analyzer/file_handler.py +246 -0
- tree_sitter_analyzer/formatters/__init__.py +1 -0
- tree_sitter_analyzer/formatters/base_formatter.py +201 -0
- tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
- tree_sitter_analyzer/formatters/formatter_config.py +197 -0
- tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
- tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
- tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
- tree_sitter_analyzer/formatters/go_formatter.py +368 -0
- tree_sitter_analyzer/formatters/html_formatter.py +498 -0
- tree_sitter_analyzer/formatters/java_formatter.py +423 -0
- tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
- tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
- tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
- tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
- tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
- tree_sitter_analyzer/formatters/php_formatter.py +301 -0
- tree_sitter_analyzer/formatters/python_formatter.py +830 -0
- tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
- tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
- tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
- tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
- tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
- tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
- tree_sitter_analyzer/interfaces/__init__.py +9 -0
- tree_sitter_analyzer/interfaces/cli.py +535 -0
- tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
- tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
- tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
- tree_sitter_analyzer/language_detector.py +553 -0
- tree_sitter_analyzer/language_loader.py +271 -0
- tree_sitter_analyzer/languages/__init__.py +10 -0
- tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
- tree_sitter_analyzer/languages/css_plugin.py +449 -0
- tree_sitter_analyzer/languages/go_plugin.py +836 -0
- tree_sitter_analyzer/languages/html_plugin.py +496 -0
- tree_sitter_analyzer/languages/java_plugin.py +1299 -0
- tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
- tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
- tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
- tree_sitter_analyzer/languages/php_plugin.py +862 -0
- tree_sitter_analyzer/languages/python_plugin.py +1636 -0
- tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
- tree_sitter_analyzer/languages/rust_plugin.py +673 -0
- tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
- tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
- tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
- tree_sitter_analyzer/legacy_table_formatter.py +860 -0
- tree_sitter_analyzer/mcp/__init__.py +34 -0
- tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
- tree_sitter_analyzer/mcp/server.py +869 -0
- tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
- tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
- tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
- tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
- tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
- tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
- tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
- tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
- tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
- tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
- tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
- tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
- tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
- tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
- tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
- tree_sitter_analyzer/models.py +840 -0
- tree_sitter_analyzer/mypy_current_errors.txt +2 -0
- tree_sitter_analyzer/output_manager.py +255 -0
- tree_sitter_analyzer/platform_compat/__init__.py +3 -0
- tree_sitter_analyzer/platform_compat/adapter.py +324 -0
- tree_sitter_analyzer/platform_compat/compare.py +224 -0
- tree_sitter_analyzer/platform_compat/detector.py +67 -0
- tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
- tree_sitter_analyzer/platform_compat/profiles.py +217 -0
- tree_sitter_analyzer/platform_compat/record.py +55 -0
- tree_sitter_analyzer/platform_compat/recorder.py +155 -0
- tree_sitter_analyzer/platform_compat/report.py +92 -0
- tree_sitter_analyzer/plugins/__init__.py +280 -0
- tree_sitter_analyzer/plugins/base.py +647 -0
- tree_sitter_analyzer/plugins/manager.py +384 -0
- tree_sitter_analyzer/project_detector.py +328 -0
- tree_sitter_analyzer/queries/__init__.py +27 -0
- tree_sitter_analyzer/queries/csharp.py +216 -0
- tree_sitter_analyzer/queries/css.py +615 -0
- tree_sitter_analyzer/queries/go.py +275 -0
- tree_sitter_analyzer/queries/html.py +543 -0
- tree_sitter_analyzer/queries/java.py +402 -0
- tree_sitter_analyzer/queries/javascript.py +724 -0
- tree_sitter_analyzer/queries/kotlin.py +192 -0
- tree_sitter_analyzer/queries/markdown.py +258 -0
- tree_sitter_analyzer/queries/php.py +95 -0
- tree_sitter_analyzer/queries/python.py +859 -0
- tree_sitter_analyzer/queries/ruby.py +92 -0
- tree_sitter_analyzer/queries/rust.py +223 -0
- tree_sitter_analyzer/queries/sql.py +555 -0
- tree_sitter_analyzer/queries/typescript.py +871 -0
- tree_sitter_analyzer/queries/yaml.py +236 -0
- tree_sitter_analyzer/query_loader.py +272 -0
- tree_sitter_analyzer/security/__init__.py +22 -0
- tree_sitter_analyzer/security/boundary_manager.py +277 -0
- tree_sitter_analyzer/security/regex_checker.py +297 -0
- tree_sitter_analyzer/security/validator.py +599 -0
- tree_sitter_analyzer/table_formatter.py +782 -0
- tree_sitter_analyzer/utils/__init__.py +53 -0
- tree_sitter_analyzer/utils/logging.py +433 -0
- tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/entry_points.txt +25 -0
|
@@ -0,0 +1,836 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Go Language Plugin
|
|
4
|
+
|
|
5
|
+
Provides Go-specific parsing and element extraction functionality.
|
|
6
|
+
Supports packages, functions, methods, structs, interfaces, type aliases,
|
|
7
|
+
const/var declarations, goroutines, and channels.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import re
|
|
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 ..encoding_utils import extract_text_slice, safe_encode
|
|
20
|
+
from ..models import Class, Function, Import, Package, Variable
|
|
21
|
+
from ..plugins.base import ElementExtractor, LanguagePlugin
|
|
22
|
+
from ..utils import log_debug, log_error
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class GoElementExtractor(ElementExtractor):
|
|
26
|
+
"""Go-specific element extractor"""
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None:
|
|
29
|
+
"""Initialize the Go element extractor."""
|
|
30
|
+
self.current_package: str = ""
|
|
31
|
+
self.current_file: str = ""
|
|
32
|
+
self.source_code: str = ""
|
|
33
|
+
self.content_lines: list[str] = []
|
|
34
|
+
self._node_text_cache: dict[int, str] = {}
|
|
35
|
+
# Go-specific metadata
|
|
36
|
+
self.goroutines: list[dict[str, Any]] = []
|
|
37
|
+
self.channels: list[dict[str, Any]] = []
|
|
38
|
+
self.defers: list[dict[str, Any]] = []
|
|
39
|
+
|
|
40
|
+
def extract_functions(
|
|
41
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
42
|
+
) -> list[Function]:
|
|
43
|
+
"""Extract Go function and method declarations"""
|
|
44
|
+
self.source_code = source_code
|
|
45
|
+
self.content_lines = source_code.split("\n")
|
|
46
|
+
self._reset_caches()
|
|
47
|
+
|
|
48
|
+
functions: list[Function] = []
|
|
49
|
+
|
|
50
|
+
extractors = {
|
|
51
|
+
"function_declaration": self._extract_function,
|
|
52
|
+
"method_declaration": self._extract_method,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
self._traverse_and_extract(tree.root_node, extractors, functions)
|
|
56
|
+
|
|
57
|
+
log_debug(f"Extracted {len(functions)} Go functions/methods")
|
|
58
|
+
return functions
|
|
59
|
+
|
|
60
|
+
def extract_classes(
|
|
61
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
62
|
+
) -> list[Class]:
|
|
63
|
+
"""Extract Go struct and interface definitions"""
|
|
64
|
+
self.source_code = source_code
|
|
65
|
+
self.content_lines = source_code.split("\n")
|
|
66
|
+
self._reset_caches()
|
|
67
|
+
|
|
68
|
+
classes: list[Class] = []
|
|
69
|
+
|
|
70
|
+
# Extract type declarations (struct, interface, type alias)
|
|
71
|
+
self._traverse_for_types(tree.root_node, classes)
|
|
72
|
+
|
|
73
|
+
log_debug(f"Extracted {len(classes)} Go structs/interfaces")
|
|
74
|
+
return classes
|
|
75
|
+
|
|
76
|
+
def extract_variables(
|
|
77
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
78
|
+
) -> list[Variable]:
|
|
79
|
+
"""Extract Go const and var declarations"""
|
|
80
|
+
self.source_code = source_code
|
|
81
|
+
self.content_lines = source_code.split("\n")
|
|
82
|
+
self._reset_caches()
|
|
83
|
+
|
|
84
|
+
variables: list[Variable] = []
|
|
85
|
+
|
|
86
|
+
extractors = {
|
|
87
|
+
"const_declaration": self._extract_const_declaration,
|
|
88
|
+
"var_declaration": self._extract_var_declaration,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
self._traverse_and_extract(tree.root_node, extractors, variables)
|
|
92
|
+
|
|
93
|
+
log_debug(f"Extracted {len(variables)} Go const/var declarations")
|
|
94
|
+
return variables
|
|
95
|
+
|
|
96
|
+
def extract_imports(
|
|
97
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
98
|
+
) -> list[Import]:
|
|
99
|
+
"""Extract Go import declarations"""
|
|
100
|
+
self.source_code = source_code
|
|
101
|
+
self.content_lines = source_code.split("\n")
|
|
102
|
+
self._reset_caches()
|
|
103
|
+
|
|
104
|
+
imports: list[Import] = []
|
|
105
|
+
|
|
106
|
+
extractors = {
|
|
107
|
+
"import_declaration": self._extract_import_declaration,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
self._traverse_and_extract(tree.root_node, extractors, imports)
|
|
111
|
+
|
|
112
|
+
log_debug(f"Extracted {len(imports)} Go imports")
|
|
113
|
+
return imports
|
|
114
|
+
|
|
115
|
+
def extract_packages(
|
|
116
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
117
|
+
) -> list[Package]:
|
|
118
|
+
"""Extract Go package declaration"""
|
|
119
|
+
self.source_code = source_code
|
|
120
|
+
self.content_lines = source_code.split("\n")
|
|
121
|
+
self._reset_caches()
|
|
122
|
+
|
|
123
|
+
packages: list[Package] = []
|
|
124
|
+
|
|
125
|
+
for child in tree.root_node.children:
|
|
126
|
+
if child.type == "package_clause":
|
|
127
|
+
pkg = self._extract_package(child)
|
|
128
|
+
if pkg:
|
|
129
|
+
packages.append(pkg)
|
|
130
|
+
self.current_package = pkg.name
|
|
131
|
+
break
|
|
132
|
+
|
|
133
|
+
log_debug(f"Extracted {len(packages)} Go packages")
|
|
134
|
+
return packages
|
|
135
|
+
|
|
136
|
+
def _reset_caches(self) -> None:
|
|
137
|
+
"""Reset performance caches"""
|
|
138
|
+
self._node_text_cache.clear()
|
|
139
|
+
self.goroutines.clear()
|
|
140
|
+
self.channels.clear()
|
|
141
|
+
self.defers.clear()
|
|
142
|
+
|
|
143
|
+
def _traverse_and_extract(
|
|
144
|
+
self,
|
|
145
|
+
node: "tree_sitter.Node",
|
|
146
|
+
extractors: dict[str, Any],
|
|
147
|
+
results: list[Any],
|
|
148
|
+
) -> None:
|
|
149
|
+
"""Recursive traversal to find and extract elements"""
|
|
150
|
+
if node.type in extractors:
|
|
151
|
+
element = extractors[node.type](node)
|
|
152
|
+
if element:
|
|
153
|
+
if isinstance(element, list):
|
|
154
|
+
results.extend(element)
|
|
155
|
+
else:
|
|
156
|
+
results.append(element)
|
|
157
|
+
|
|
158
|
+
# Also detect goroutines, channels, defers
|
|
159
|
+
if node.type == "go_statement":
|
|
160
|
+
self._extract_goroutine(node)
|
|
161
|
+
elif node.type == "send_statement":
|
|
162
|
+
self._extract_channel_operation(node, "send")
|
|
163
|
+
elif node.type == "defer_statement":
|
|
164
|
+
self._extract_defer(node)
|
|
165
|
+
|
|
166
|
+
for child in node.children:
|
|
167
|
+
self._traverse_and_extract(child, extractors, results)
|
|
168
|
+
|
|
169
|
+
def _traverse_for_types(
|
|
170
|
+
self, node: "tree_sitter.Node", results: list[Class]
|
|
171
|
+
) -> None:
|
|
172
|
+
"""Traverse to find type declarations"""
|
|
173
|
+
if node.type == "type_declaration":
|
|
174
|
+
classes = self._extract_type_declaration(node)
|
|
175
|
+
if classes:
|
|
176
|
+
results.extend(classes)
|
|
177
|
+
|
|
178
|
+
for child in node.children:
|
|
179
|
+
self._traverse_for_types(child, results)
|
|
180
|
+
|
|
181
|
+
def _extract_package(self, node: "tree_sitter.Node") -> Package | None:
|
|
182
|
+
"""Extract package declaration"""
|
|
183
|
+
try:
|
|
184
|
+
for child in node.children:
|
|
185
|
+
if child.type == "package_identifier":
|
|
186
|
+
name = self._get_node_text(child)
|
|
187
|
+
return Package(
|
|
188
|
+
name=name,
|
|
189
|
+
start_line=node.start_point[0] + 1,
|
|
190
|
+
end_line=node.end_point[0] + 1,
|
|
191
|
+
raw_text=self._get_node_text(node),
|
|
192
|
+
language="go",
|
|
193
|
+
)
|
|
194
|
+
return None
|
|
195
|
+
except Exception as e:
|
|
196
|
+
log_error(f"Error extracting Go package: {e}")
|
|
197
|
+
return None
|
|
198
|
+
|
|
199
|
+
def _extract_import_declaration(
|
|
200
|
+
self, node: "tree_sitter.Node"
|
|
201
|
+
) -> list[Import] | None:
|
|
202
|
+
"""Extract import declaration (may contain multiple imports)"""
|
|
203
|
+
imports: list[Import] = []
|
|
204
|
+
try:
|
|
205
|
+
# Find import_spec_list or single import_spec
|
|
206
|
+
for child in node.children:
|
|
207
|
+
if child.type == "import_spec_list":
|
|
208
|
+
for spec in child.children:
|
|
209
|
+
if spec.type == "import_spec":
|
|
210
|
+
imp = self._extract_import_spec(spec)
|
|
211
|
+
if imp:
|
|
212
|
+
imports.append(imp)
|
|
213
|
+
elif child.type == "import_spec":
|
|
214
|
+
imp = self._extract_import_spec(child)
|
|
215
|
+
if imp:
|
|
216
|
+
imports.append(imp)
|
|
217
|
+
|
|
218
|
+
return imports if imports else None
|
|
219
|
+
except Exception as e:
|
|
220
|
+
log_error(f"Error extracting Go import: {e}")
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
def _extract_import_spec(self, node: "tree_sitter.Node") -> Import | None:
|
|
224
|
+
"""Extract single import spec"""
|
|
225
|
+
try:
|
|
226
|
+
raw_text = self._get_node_text(node)
|
|
227
|
+
start_line = node.start_point[0] + 1
|
|
228
|
+
end_line = node.end_point[0] + 1
|
|
229
|
+
|
|
230
|
+
# Extract path and optional alias
|
|
231
|
+
alias = None
|
|
232
|
+
path = None
|
|
233
|
+
|
|
234
|
+
for child in node.children:
|
|
235
|
+
if child.type == "package_identifier":
|
|
236
|
+
alias = self._get_node_text(child)
|
|
237
|
+
elif child.type == "blank_identifier":
|
|
238
|
+
alias = "_"
|
|
239
|
+
elif child.type == "dot":
|
|
240
|
+
alias = "."
|
|
241
|
+
elif child.type == "interpreted_string_literal":
|
|
242
|
+
path = self._get_node_text(child).strip('"')
|
|
243
|
+
|
|
244
|
+
if path:
|
|
245
|
+
# Extract package name from path
|
|
246
|
+
name = path.split("/")[-1] if "/" in path else path
|
|
247
|
+
return Import(
|
|
248
|
+
name=name,
|
|
249
|
+
start_line=start_line,
|
|
250
|
+
end_line=end_line,
|
|
251
|
+
raw_text=raw_text,
|
|
252
|
+
language="go",
|
|
253
|
+
module_name=path,
|
|
254
|
+
import_statement=raw_text,
|
|
255
|
+
alias=alias,
|
|
256
|
+
)
|
|
257
|
+
return None
|
|
258
|
+
except Exception as e:
|
|
259
|
+
log_error(f"Error extracting Go import spec: {e}")
|
|
260
|
+
return None
|
|
261
|
+
|
|
262
|
+
def _extract_function(self, node: "tree_sitter.Node") -> Function | None:
|
|
263
|
+
"""Extract function declaration"""
|
|
264
|
+
try:
|
|
265
|
+
name_node = node.child_by_field_name("name")
|
|
266
|
+
if not name_node:
|
|
267
|
+
return None
|
|
268
|
+
|
|
269
|
+
name = self._get_node_text(name_node)
|
|
270
|
+
if not name:
|
|
271
|
+
return None
|
|
272
|
+
|
|
273
|
+
start_line = node.start_point[0] + 1
|
|
274
|
+
end_line = node.end_point[0] + 1
|
|
275
|
+
|
|
276
|
+
# Parameters
|
|
277
|
+
parameters = self._extract_parameters(node)
|
|
278
|
+
|
|
279
|
+
# Return type
|
|
280
|
+
return_type = self._extract_return_type(node)
|
|
281
|
+
|
|
282
|
+
# Visibility (exported if starts with uppercase)
|
|
283
|
+
visibility = "public" if name[0].isupper() else "private"
|
|
284
|
+
|
|
285
|
+
# Docstring
|
|
286
|
+
docstring = self._extract_docstring(node)
|
|
287
|
+
|
|
288
|
+
raw_text = self._get_node_text(node)
|
|
289
|
+
|
|
290
|
+
return Function(
|
|
291
|
+
name=name,
|
|
292
|
+
start_line=start_line,
|
|
293
|
+
end_line=end_line,
|
|
294
|
+
raw_text=raw_text,
|
|
295
|
+
language="go",
|
|
296
|
+
parameters=parameters,
|
|
297
|
+
return_type=return_type,
|
|
298
|
+
visibility=visibility,
|
|
299
|
+
docstring=docstring,
|
|
300
|
+
is_public=visibility == "public",
|
|
301
|
+
)
|
|
302
|
+
except Exception as e:
|
|
303
|
+
log_error(f"Error extracting Go function: {e}")
|
|
304
|
+
return None
|
|
305
|
+
|
|
306
|
+
def _extract_method(self, node: "tree_sitter.Node") -> Function | None:
|
|
307
|
+
"""Extract method declaration (function with receiver)"""
|
|
308
|
+
try:
|
|
309
|
+
name_node = node.child_by_field_name("name")
|
|
310
|
+
if not name_node:
|
|
311
|
+
return None
|
|
312
|
+
|
|
313
|
+
name = self._get_node_text(name_node)
|
|
314
|
+
if not name:
|
|
315
|
+
return None
|
|
316
|
+
|
|
317
|
+
start_line = node.start_point[0] + 1
|
|
318
|
+
end_line = node.end_point[0] + 1
|
|
319
|
+
|
|
320
|
+
# Receiver
|
|
321
|
+
receiver = None
|
|
322
|
+
receiver_type = None
|
|
323
|
+
receiver_node = node.child_by_field_name("receiver")
|
|
324
|
+
if receiver_node:
|
|
325
|
+
receiver_text = self._get_node_text(receiver_node)
|
|
326
|
+
# Parse receiver: (r *ReceiverType) or (r ReceiverType)
|
|
327
|
+
match = re.search(r"\(\s*(\w+)\s+(\*?\w+)\s*\)", receiver_text)
|
|
328
|
+
if match:
|
|
329
|
+
receiver = match.group(1)
|
|
330
|
+
receiver_type = match.group(2)
|
|
331
|
+
|
|
332
|
+
# Parameters
|
|
333
|
+
parameters = self._extract_parameters(node)
|
|
334
|
+
|
|
335
|
+
# Return type
|
|
336
|
+
return_type = self._extract_return_type(node)
|
|
337
|
+
|
|
338
|
+
# Visibility
|
|
339
|
+
visibility = "public" if name[0].isupper() else "private"
|
|
340
|
+
|
|
341
|
+
# Docstring
|
|
342
|
+
docstring = self._extract_docstring(node)
|
|
343
|
+
|
|
344
|
+
raw_text = self._get_node_text(node)
|
|
345
|
+
|
|
346
|
+
func = Function(
|
|
347
|
+
name=name,
|
|
348
|
+
start_line=start_line,
|
|
349
|
+
end_line=end_line,
|
|
350
|
+
raw_text=raw_text,
|
|
351
|
+
language="go",
|
|
352
|
+
parameters=parameters,
|
|
353
|
+
return_type=return_type,
|
|
354
|
+
visibility=visibility,
|
|
355
|
+
docstring=docstring,
|
|
356
|
+
is_public=visibility == "public",
|
|
357
|
+
)
|
|
358
|
+
# Attach Go-specific method attributes
|
|
359
|
+
func.receiver = receiver
|
|
360
|
+
func.receiver_type = receiver_type
|
|
361
|
+
func.is_method = True
|
|
362
|
+
|
|
363
|
+
return func
|
|
364
|
+
except Exception as e:
|
|
365
|
+
log_error(f"Error extracting Go method: {e}")
|
|
366
|
+
return None
|
|
367
|
+
|
|
368
|
+
def _extract_parameters(self, node: "tree_sitter.Node") -> list[str]:
|
|
369
|
+
"""Extract function/method parameters"""
|
|
370
|
+
parameters = []
|
|
371
|
+
params_node = node.child_by_field_name("parameters")
|
|
372
|
+
if params_node:
|
|
373
|
+
for child in params_node.children:
|
|
374
|
+
if child.type == "parameter_declaration":
|
|
375
|
+
param_text = self._get_node_text(child)
|
|
376
|
+
parameters.append(param_text)
|
|
377
|
+
return parameters
|
|
378
|
+
|
|
379
|
+
def _extract_return_type(self, node: "tree_sitter.Node") -> str:
|
|
380
|
+
"""Extract function/method return type"""
|
|
381
|
+
result_node = node.child_by_field_name("result")
|
|
382
|
+
if result_node:
|
|
383
|
+
return self._get_node_text(result_node)
|
|
384
|
+
return ""
|
|
385
|
+
|
|
386
|
+
def _extract_type_declaration(self, node: "tree_sitter.Node") -> list[Class]:
|
|
387
|
+
"""Extract type declaration (struct, interface, type alias)"""
|
|
388
|
+
classes: list[Class] = []
|
|
389
|
+
try:
|
|
390
|
+
for child in node.children:
|
|
391
|
+
if child.type == "type_spec":
|
|
392
|
+
cls = self._extract_type_spec(child)
|
|
393
|
+
if cls:
|
|
394
|
+
classes.append(cls)
|
|
395
|
+
except Exception as e:
|
|
396
|
+
log_error(f"Error extracting Go type declaration: {e}")
|
|
397
|
+
return classes
|
|
398
|
+
|
|
399
|
+
def _extract_type_spec(self, node: "tree_sitter.Node") -> Class | None:
|
|
400
|
+
"""Extract single type spec"""
|
|
401
|
+
try:
|
|
402
|
+
name_node = node.child_by_field_name("name")
|
|
403
|
+
type_node = node.child_by_field_name("type")
|
|
404
|
+
|
|
405
|
+
if not name_node:
|
|
406
|
+
return None
|
|
407
|
+
|
|
408
|
+
name = self._get_node_text(name_node)
|
|
409
|
+
if not name:
|
|
410
|
+
return None
|
|
411
|
+
|
|
412
|
+
start_line = node.start_point[0] + 1
|
|
413
|
+
end_line = node.end_point[0] + 1
|
|
414
|
+
|
|
415
|
+
# Determine type kind
|
|
416
|
+
class_type = "type"
|
|
417
|
+
if type_node:
|
|
418
|
+
if type_node.type == "struct_type":
|
|
419
|
+
class_type = "struct"
|
|
420
|
+
elif type_node.type == "interface_type":
|
|
421
|
+
class_type = "interface"
|
|
422
|
+
else:
|
|
423
|
+
class_type = "type_alias"
|
|
424
|
+
|
|
425
|
+
# Visibility
|
|
426
|
+
visibility = "public" if name[0].isupper() else "private"
|
|
427
|
+
|
|
428
|
+
# Docstring
|
|
429
|
+
docstring = self._extract_docstring(node)
|
|
430
|
+
|
|
431
|
+
raw_text = self._get_node_text(node)
|
|
432
|
+
|
|
433
|
+
# For struct, extract embedded types (interfaces)
|
|
434
|
+
interfaces: list[str] = []
|
|
435
|
+
if type_node and type_node.type == "struct_type":
|
|
436
|
+
interfaces = self._extract_embedded_types(type_node)
|
|
437
|
+
|
|
438
|
+
return Class(
|
|
439
|
+
name=name,
|
|
440
|
+
start_line=start_line,
|
|
441
|
+
end_line=end_line,
|
|
442
|
+
raw_text=raw_text,
|
|
443
|
+
language="go",
|
|
444
|
+
class_type=class_type,
|
|
445
|
+
visibility=visibility,
|
|
446
|
+
docstring=docstring,
|
|
447
|
+
interfaces=interfaces,
|
|
448
|
+
)
|
|
449
|
+
except Exception as e:
|
|
450
|
+
log_error(f"Error extracting Go type spec: {e}")
|
|
451
|
+
return None
|
|
452
|
+
|
|
453
|
+
def _extract_embedded_types(self, struct_node: "tree_sitter.Node") -> list[str]:
|
|
454
|
+
"""Extract embedded types from struct"""
|
|
455
|
+
embedded: list[str] = []
|
|
456
|
+
for child in struct_node.children:
|
|
457
|
+
if child.type == "field_declaration_list":
|
|
458
|
+
for field in child.children:
|
|
459
|
+
if field.type == "field_declaration":
|
|
460
|
+
# Check if it's an embedded field (no name, just type)
|
|
461
|
+
has_name = False
|
|
462
|
+
type_text = None
|
|
463
|
+
for fc in field.children:
|
|
464
|
+
if fc.type == "field_identifier":
|
|
465
|
+
has_name = True
|
|
466
|
+
elif fc.type in ["type_identifier", "qualified_type"]:
|
|
467
|
+
type_text = self._get_node_text(fc)
|
|
468
|
+
if not has_name and type_text:
|
|
469
|
+
embedded.append(type_text)
|
|
470
|
+
return embedded
|
|
471
|
+
|
|
472
|
+
def _extract_const_declaration(
|
|
473
|
+
self, node: "tree_sitter.Node"
|
|
474
|
+
) -> list[Variable] | None:
|
|
475
|
+
"""Extract const declaration"""
|
|
476
|
+
return self._extract_var_or_const(node, is_const=True)
|
|
477
|
+
|
|
478
|
+
def _extract_var_declaration(
|
|
479
|
+
self, node: "tree_sitter.Node"
|
|
480
|
+
) -> list[Variable] | None:
|
|
481
|
+
"""Extract var declaration"""
|
|
482
|
+
return self._extract_var_or_const(node, is_const=False)
|
|
483
|
+
|
|
484
|
+
def _extract_var_or_const(
|
|
485
|
+
self, node: "tree_sitter.Node", is_const: bool
|
|
486
|
+
) -> list[Variable] | None:
|
|
487
|
+
"""Extract var or const declaration"""
|
|
488
|
+
variables: list[Variable] = []
|
|
489
|
+
try:
|
|
490
|
+
for child in node.children:
|
|
491
|
+
if child.type in ["const_spec", "var_spec"]:
|
|
492
|
+
vars_from_spec = self._extract_var_spec(child, is_const)
|
|
493
|
+
if vars_from_spec:
|
|
494
|
+
variables.extend(vars_from_spec)
|
|
495
|
+
except Exception as e:
|
|
496
|
+
log_error(f"Error extracting Go {'const' if is_const else 'var'}: {e}")
|
|
497
|
+
return variables if variables else None
|
|
498
|
+
|
|
499
|
+
def _extract_var_spec(
|
|
500
|
+
self, node: "tree_sitter.Node", is_const: bool
|
|
501
|
+
) -> list[Variable]:
|
|
502
|
+
"""Extract single var/const spec"""
|
|
503
|
+
variables: list[Variable] = []
|
|
504
|
+
try:
|
|
505
|
+
start_line = node.start_point[0] + 1
|
|
506
|
+
end_line = node.end_point[0] + 1
|
|
507
|
+
raw_text = self._get_node_text(node)
|
|
508
|
+
|
|
509
|
+
# Extract names and type
|
|
510
|
+
names: list[str] = []
|
|
511
|
+
var_type = ""
|
|
512
|
+
|
|
513
|
+
for child in node.children:
|
|
514
|
+
if child.type == "identifier":
|
|
515
|
+
names.append(self._get_node_text(child))
|
|
516
|
+
elif child.type in [
|
|
517
|
+
"type_identifier",
|
|
518
|
+
"pointer_type",
|
|
519
|
+
"array_type",
|
|
520
|
+
"slice_type",
|
|
521
|
+
"map_type",
|
|
522
|
+
"channel_type",
|
|
523
|
+
"qualified_type",
|
|
524
|
+
]:
|
|
525
|
+
var_type = self._get_node_text(child)
|
|
526
|
+
|
|
527
|
+
for name in names:
|
|
528
|
+
visibility = "public" if name[0].isupper() else "private"
|
|
529
|
+
variables.append(
|
|
530
|
+
Variable(
|
|
531
|
+
name=name,
|
|
532
|
+
start_line=start_line,
|
|
533
|
+
end_line=end_line,
|
|
534
|
+
raw_text=raw_text,
|
|
535
|
+
language="go",
|
|
536
|
+
variable_type=var_type,
|
|
537
|
+
visibility=visibility,
|
|
538
|
+
is_constant=is_const,
|
|
539
|
+
)
|
|
540
|
+
)
|
|
541
|
+
except Exception as e:
|
|
542
|
+
log_error(f"Error extracting Go var spec: {e}")
|
|
543
|
+
return variables
|
|
544
|
+
|
|
545
|
+
def _extract_goroutine(self, node: "tree_sitter.Node") -> None:
|
|
546
|
+
"""Extract goroutine invocation"""
|
|
547
|
+
try:
|
|
548
|
+
self.goroutines.append(
|
|
549
|
+
{
|
|
550
|
+
"line": node.start_point[0] + 1,
|
|
551
|
+
"text": self._get_node_text(node),
|
|
552
|
+
}
|
|
553
|
+
)
|
|
554
|
+
except Exception as e:
|
|
555
|
+
log_error(f"Error extracting goroutine: {e}")
|
|
556
|
+
|
|
557
|
+
def _extract_channel_operation(
|
|
558
|
+
self, node: "tree_sitter.Node", op_type: str
|
|
559
|
+
) -> None:
|
|
560
|
+
"""Extract channel operation"""
|
|
561
|
+
try:
|
|
562
|
+
self.channels.append(
|
|
563
|
+
{
|
|
564
|
+
"type": op_type,
|
|
565
|
+
"line": node.start_point[0] + 1,
|
|
566
|
+
"text": self._get_node_text(node),
|
|
567
|
+
}
|
|
568
|
+
)
|
|
569
|
+
except Exception as e:
|
|
570
|
+
log_error(f"Error extracting channel operation: {e}")
|
|
571
|
+
|
|
572
|
+
def _extract_defer(self, node: "tree_sitter.Node") -> None:
|
|
573
|
+
"""Extract defer statement"""
|
|
574
|
+
try:
|
|
575
|
+
self.defers.append(
|
|
576
|
+
{
|
|
577
|
+
"line": node.start_point[0] + 1,
|
|
578
|
+
"text": self._get_node_text(node),
|
|
579
|
+
}
|
|
580
|
+
)
|
|
581
|
+
except Exception as e:
|
|
582
|
+
log_error(f"Error extracting defer: {e}")
|
|
583
|
+
|
|
584
|
+
def _extract_docstring(self, node: "tree_sitter.Node") -> str | None:
|
|
585
|
+
"""Extract doc comments preceding the node"""
|
|
586
|
+
# In Go, doc comments are // comments immediately before the declaration
|
|
587
|
+
start_line = node.start_point[0]
|
|
588
|
+
if start_line == 0:
|
|
589
|
+
return None
|
|
590
|
+
|
|
591
|
+
docs: list[str] = []
|
|
592
|
+
line_idx = start_line - 1
|
|
593
|
+
|
|
594
|
+
# Ensure line_idx is within valid range
|
|
595
|
+
if line_idx >= len(self.content_lines):
|
|
596
|
+
line_idx = len(self.content_lines) - 1
|
|
597
|
+
|
|
598
|
+
while line_idx >= 0:
|
|
599
|
+
line = self.content_lines[line_idx].strip()
|
|
600
|
+
if line.startswith("//"):
|
|
601
|
+
docs.insert(0, line[2:].strip())
|
|
602
|
+
line_idx -= 1
|
|
603
|
+
elif line == "":
|
|
604
|
+
line_idx -= 1
|
|
605
|
+
else:
|
|
606
|
+
break
|
|
607
|
+
|
|
608
|
+
return "\n".join(docs) if docs else None
|
|
609
|
+
|
|
610
|
+
def _get_node_text(self, node: "tree_sitter.Node") -> str:
|
|
611
|
+
"""Get node text with caching"""
|
|
612
|
+
node_id = id(node)
|
|
613
|
+
if node_id in self._node_text_cache:
|
|
614
|
+
return self._node_text_cache[node_id]
|
|
615
|
+
|
|
616
|
+
try:
|
|
617
|
+
start_byte = node.start_byte
|
|
618
|
+
end_byte = node.end_byte
|
|
619
|
+
encoding = "utf-8"
|
|
620
|
+
content_bytes = safe_encode("\n".join(self.content_lines), encoding)
|
|
621
|
+
text = extract_text_slice(content_bytes, start_byte, end_byte, encoding)
|
|
622
|
+
self._node_text_cache[node_id] = text
|
|
623
|
+
return text
|
|
624
|
+
except Exception:
|
|
625
|
+
return ""
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
class GoPlugin(LanguagePlugin):
|
|
629
|
+
"""Go language plugin implementation"""
|
|
630
|
+
|
|
631
|
+
def __init__(self) -> None:
|
|
632
|
+
"""Initialize the Go language plugin."""
|
|
633
|
+
super().__init__()
|
|
634
|
+
self.extractor = GoElementExtractor()
|
|
635
|
+
self.language = "go"
|
|
636
|
+
self.supported_extensions = self.get_file_extensions()
|
|
637
|
+
self._cached_language: Any | None = None
|
|
638
|
+
|
|
639
|
+
def get_language_name(self) -> str:
|
|
640
|
+
"""Get the language name."""
|
|
641
|
+
return "go"
|
|
642
|
+
|
|
643
|
+
def get_file_extensions(self) -> list[str]:
|
|
644
|
+
"""Get supported file extensions."""
|
|
645
|
+
return [".go"]
|
|
646
|
+
|
|
647
|
+
def create_extractor(self) -> ElementExtractor:
|
|
648
|
+
"""Create a new element extractor instance."""
|
|
649
|
+
return GoElementExtractor()
|
|
650
|
+
|
|
651
|
+
def get_supported_element_types(self) -> list[str]:
|
|
652
|
+
"""Get supported element types for Go."""
|
|
653
|
+
return [
|
|
654
|
+
"package",
|
|
655
|
+
"import",
|
|
656
|
+
"function",
|
|
657
|
+
"method",
|
|
658
|
+
"struct",
|
|
659
|
+
"interface",
|
|
660
|
+
"type_alias",
|
|
661
|
+
"const",
|
|
662
|
+
"var",
|
|
663
|
+
"goroutine",
|
|
664
|
+
"channel",
|
|
665
|
+
]
|
|
666
|
+
|
|
667
|
+
def get_queries(self) -> dict[str, str]:
|
|
668
|
+
"""Get Go-specific tree-sitter queries."""
|
|
669
|
+
from ..queries.go import GO_QUERIES
|
|
670
|
+
|
|
671
|
+
return GO_QUERIES
|
|
672
|
+
|
|
673
|
+
async def analyze_file(
|
|
674
|
+
self, file_path: str, request: "AnalysisRequest"
|
|
675
|
+
) -> "AnalysisResult":
|
|
676
|
+
"""Analyze Go code and return structured results."""
|
|
677
|
+
from ..models import AnalysisResult
|
|
678
|
+
|
|
679
|
+
try:
|
|
680
|
+
from ..encoding_utils import read_file_safe
|
|
681
|
+
|
|
682
|
+
file_content, detected_encoding = read_file_safe(file_path)
|
|
683
|
+
|
|
684
|
+
# Get tree-sitter language and parse
|
|
685
|
+
language = self.get_tree_sitter_language()
|
|
686
|
+
if language is None:
|
|
687
|
+
return AnalysisResult(
|
|
688
|
+
file_path=file_path,
|
|
689
|
+
language="go",
|
|
690
|
+
line_count=len(file_content.split("\n")),
|
|
691
|
+
elements=[],
|
|
692
|
+
source_code=file_content,
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
import tree_sitter
|
|
696
|
+
|
|
697
|
+
parser = tree_sitter.Parser()
|
|
698
|
+
|
|
699
|
+
# Set language (handle different tree-sitter versions)
|
|
700
|
+
if hasattr(parser, "set_language"):
|
|
701
|
+
parser.set_language(language)
|
|
702
|
+
elif hasattr(parser, "language"):
|
|
703
|
+
parser.language = language
|
|
704
|
+
else:
|
|
705
|
+
parser = tree_sitter.Parser(language)
|
|
706
|
+
|
|
707
|
+
tree = parser.parse(file_content.encode("utf-8"))
|
|
708
|
+
|
|
709
|
+
# Extract elements
|
|
710
|
+
elements_dict = self.extract_elements(tree, file_content)
|
|
711
|
+
|
|
712
|
+
all_elements = []
|
|
713
|
+
all_elements.extend(elements_dict.get("packages", []))
|
|
714
|
+
all_elements.extend(elements_dict.get("imports", []))
|
|
715
|
+
all_elements.extend(elements_dict.get("functions", []))
|
|
716
|
+
all_elements.extend(elements_dict.get("classes", []))
|
|
717
|
+
all_elements.extend(elements_dict.get("variables", []))
|
|
718
|
+
|
|
719
|
+
# Count nodes
|
|
720
|
+
node_count = (
|
|
721
|
+
self._count_tree_nodes(tree.root_node) if tree and tree.root_node else 0
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
result = AnalysisResult(
|
|
725
|
+
file_path=file_path,
|
|
726
|
+
language="go",
|
|
727
|
+
line_count=len(file_content.split("\n")),
|
|
728
|
+
elements=all_elements,
|
|
729
|
+
node_count=node_count,
|
|
730
|
+
source_code=file_content,
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
# Attach Go-specific metadata
|
|
734
|
+
result.goroutines = self.extractor.goroutines
|
|
735
|
+
result.channels = self.extractor.channels
|
|
736
|
+
result.defers = self.extractor.defers
|
|
737
|
+
|
|
738
|
+
return result
|
|
739
|
+
|
|
740
|
+
except Exception as e:
|
|
741
|
+
log_error(f"Error analyzing Go file {file_path}: {e}")
|
|
742
|
+
return AnalysisResult(
|
|
743
|
+
file_path=file_path,
|
|
744
|
+
language="go",
|
|
745
|
+
line_count=0,
|
|
746
|
+
elements=[],
|
|
747
|
+
source_code="",
|
|
748
|
+
error_message=str(e),
|
|
749
|
+
success=False,
|
|
750
|
+
)
|
|
751
|
+
|
|
752
|
+
def _count_tree_nodes(self, node: Any) -> int:
|
|
753
|
+
"""Recursively count nodes."""
|
|
754
|
+
if node is None:
|
|
755
|
+
return 0
|
|
756
|
+
count = 1
|
|
757
|
+
if hasattr(node, "children"):
|
|
758
|
+
for child in node.children:
|
|
759
|
+
count += self._count_tree_nodes(child)
|
|
760
|
+
return count
|
|
761
|
+
|
|
762
|
+
def get_tree_sitter_language(self) -> Any | None:
|
|
763
|
+
"""Get the tree-sitter language for Go."""
|
|
764
|
+
if self._cached_language is not None:
|
|
765
|
+
return self._cached_language
|
|
766
|
+
|
|
767
|
+
try:
|
|
768
|
+
import tree_sitter
|
|
769
|
+
import tree_sitter_go
|
|
770
|
+
|
|
771
|
+
caps_or_lang = tree_sitter_go.language()
|
|
772
|
+
|
|
773
|
+
if hasattr(caps_or_lang, "__class__") and "Language" in str(
|
|
774
|
+
type(caps_or_lang)
|
|
775
|
+
):
|
|
776
|
+
self._cached_language = caps_or_lang
|
|
777
|
+
else:
|
|
778
|
+
try:
|
|
779
|
+
self._cached_language = tree_sitter.Language(caps_or_lang)
|
|
780
|
+
except Exception as e:
|
|
781
|
+
log_error(f"Failed to create Language object: {e}")
|
|
782
|
+
return None
|
|
783
|
+
|
|
784
|
+
return self._cached_language
|
|
785
|
+
except ImportError as e:
|
|
786
|
+
log_error(f"tree-sitter-go not available: {e}")
|
|
787
|
+
return None
|
|
788
|
+
except Exception as e:
|
|
789
|
+
log_error(f"Failed to load tree-sitter language for Go: {e}")
|
|
790
|
+
return None
|
|
791
|
+
|
|
792
|
+
def extract_elements(self, tree: Any | None, source_code: str) -> dict[str, Any]:
|
|
793
|
+
"""Extract all elements from Go source code."""
|
|
794
|
+
if tree is None:
|
|
795
|
+
return {
|
|
796
|
+
"packages": [],
|
|
797
|
+
"imports": [],
|
|
798
|
+
"functions": [],
|
|
799
|
+
"classes": [],
|
|
800
|
+
"variables": [],
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
try:
|
|
804
|
+
extractor = self.create_extractor()
|
|
805
|
+
|
|
806
|
+
result = {
|
|
807
|
+
"packages": extractor.extract_packages(tree, source_code),
|
|
808
|
+
"imports": extractor.extract_imports(tree, source_code),
|
|
809
|
+
"functions": extractor.extract_functions(tree, source_code),
|
|
810
|
+
"classes": extractor.extract_classes(tree, source_code),
|
|
811
|
+
"variables": extractor.extract_variables(tree, source_code),
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
# Capture Go-specific metadata
|
|
815
|
+
if isinstance(extractor, GoElementExtractor):
|
|
816
|
+
self.extractor.goroutines = extractor.goroutines
|
|
817
|
+
self.extractor.channels = extractor.channels
|
|
818
|
+
self.extractor.defers = extractor.defers
|
|
819
|
+
|
|
820
|
+
return result
|
|
821
|
+
|
|
822
|
+
except Exception as e:
|
|
823
|
+
log_error(f"Error extracting Go elements: {e}")
|
|
824
|
+
return {
|
|
825
|
+
"packages": [],
|
|
826
|
+
"imports": [],
|
|
827
|
+
"functions": [],
|
|
828
|
+
"classes": [],
|
|
829
|
+
"variables": [],
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
def supports_file(self, file_path: str) -> bool:
|
|
833
|
+
"""Check if this plugin supports the given file."""
|
|
834
|
+
return any(
|
|
835
|
+
file_path.lower().endswith(ext) for ext in self.get_file_extensions()
|
|
836
|
+
)
|