codegraphcontext-rust 0.5.5__cp310-cp310-win_amd64.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.
- codegraphcontext/__init__.py +2 -0
- codegraphcontext/__main__.py +5 -0
- codegraphcontext/_cgc_rust.cp310-win_amd64.pyd +0 -0
- codegraphcontext/cli/__init__.py +1 -0
- codegraphcontext/cli/cli_helpers.py +774 -0
- codegraphcontext/cli/config_manager.py +1051 -0
- codegraphcontext/cli/main.py +2386 -0
- codegraphcontext/cli/registry_commands.py +404 -0
- codegraphcontext/cli/setup_macos.py +93 -0
- codegraphcontext/cli/setup_wizard.py +991 -0
- codegraphcontext/cli/visualizer.py +50 -0
- codegraphcontext/core/__init__.py +165 -0
- codegraphcontext/core/bundle_registry.py +181 -0
- codegraphcontext/core/cgc_bundle.py +858 -0
- codegraphcontext/core/cgcignore.py +118 -0
- codegraphcontext/core/database.py +273 -0
- codegraphcontext/core/database_falkordb.py +480 -0
- codegraphcontext/core/database_falkordb_remote.py +199 -0
- codegraphcontext/core/database_kuzu.py +636 -0
- codegraphcontext/core/falkor_worker.py +132 -0
- codegraphcontext/core/jobs.py +132 -0
- codegraphcontext/core/watcher.py +260 -0
- codegraphcontext/prompts.py +125 -0
- codegraphcontext/server.py +471 -0
- codegraphcontext/tool_definitions.py +220 -0
- codegraphcontext/tools/__init__.py +0 -0
- codegraphcontext/tools/advanced_language_query_tool.py +103 -0
- codegraphcontext/tools/code_finder.py +1160 -0
- codegraphcontext/tools/graph_builder.py +337 -0
- codegraphcontext/tools/handlers/analysis_handlers.py +114 -0
- codegraphcontext/tools/handlers/indexing_handlers.py +116 -0
- codegraphcontext/tools/handlers/management_handlers.py +339 -0
- codegraphcontext/tools/handlers/query_handlers.py +83 -0
- codegraphcontext/tools/handlers/watcher_handlers.py +83 -0
- codegraphcontext/tools/indexing/__init__.py +1 -0
- codegraphcontext/tools/indexing/constants.py +25 -0
- codegraphcontext/tools/indexing/discovery.py +64 -0
- codegraphcontext/tools/indexing/engine.py +109 -0
- codegraphcontext/tools/indexing/persistence/__init__.py +3 -0
- codegraphcontext/tools/indexing/persistence/parquet_loader.py +643 -0
- codegraphcontext/tools/indexing/persistence/writer.py +1114 -0
- codegraphcontext/tools/indexing/pipeline.py +171 -0
- codegraphcontext/tools/indexing/pre_scan.py +105 -0
- codegraphcontext/tools/indexing/resolution/__init__.py +8 -0
- codegraphcontext/tools/indexing/resolution/calls.py +253 -0
- codegraphcontext/tools/indexing/resolution/inheritance.py +91 -0
- codegraphcontext/tools/indexing/sanitize.py +41 -0
- codegraphcontext/tools/indexing/schema.py +79 -0
- codegraphcontext/tools/indexing/schema_contract.py +44 -0
- codegraphcontext/tools/indexing/scip_pipeline.py +140 -0
- codegraphcontext/tools/languages/c.py +563 -0
- codegraphcontext/tools/languages/cpp.py +616 -0
- codegraphcontext/tools/languages/csharp.py +551 -0
- codegraphcontext/tools/languages/dart.py +378 -0
- codegraphcontext/tools/languages/elixir.py +460 -0
- codegraphcontext/tools/languages/go.py +499 -0
- codegraphcontext/tools/languages/haskell.py +426 -0
- codegraphcontext/tools/languages/java.py +500 -0
- codegraphcontext/tools/languages/javascript.py +589 -0
- codegraphcontext/tools/languages/kotlin.py +640 -0
- codegraphcontext/tools/languages/perl.py +260 -0
- codegraphcontext/tools/languages/php.py +519 -0
- codegraphcontext/tools/languages/python.py +576 -0
- codegraphcontext/tools/languages/ruby.py +537 -0
- codegraphcontext/tools/languages/rust.py +295 -0
- codegraphcontext/tools/languages/scala.py +519 -0
- codegraphcontext/tools/languages/swift.py +491 -0
- codegraphcontext/tools/languages/typescript.py +575 -0
- codegraphcontext/tools/languages/typescriptjsx.py +151 -0
- codegraphcontext/tools/package_resolver.py +472 -0
- codegraphcontext/tools/query_tool_languages/c_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +87 -0
- codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/dart_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/go_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/java_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/perl_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/python_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/rust_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/scala_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/swift_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +5 -0
- codegraphcontext/tools/scip_indexer.py +467 -0
- codegraphcontext/tools/scip_pb2.py +2455 -0
- codegraphcontext/tools/system.py +133 -0
- codegraphcontext/tools/tree_sitter_parser.py +103 -0
- codegraphcontext/utils/debug_log.py +90 -0
- codegraphcontext/utils/path_ignore.py +55 -0
- codegraphcontext/utils/repo_path.py +27 -0
- codegraphcontext/utils/tree_sitter_manager.py +285 -0
- codegraphcontext/utils/visualize_graph.py +0 -0
- codegraphcontext/viz/server.py +282 -0
- codegraphcontext_rust-0.5.5.dist-info/METADATA +537 -0
- codegraphcontext_rust-0.5.5.dist-info/RECORD +101 -0
- codegraphcontext_rust-0.5.5.dist-info/WHEEL +4 -0
- codegraphcontext_rust-0.5.5.dist-info/entry_points.txt +3 -0
- codegraphcontext_rust-0.5.5.dist-info/licenses/LICENSE +21 -0
- codegraphcontext_rust-0.5.5.dist-info/sboms/cgc-python.cyclonedx.json +2333 -0
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Dict, Optional, Tuple
|
|
3
|
+
import re
|
|
4
|
+
from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logger, warning_logger
|
|
5
|
+
from codegraphcontext.utils.tree_sitter_manager import execute_query
|
|
6
|
+
|
|
7
|
+
CSHARP_QUERIES = {
|
|
8
|
+
"functions": """
|
|
9
|
+
(method_declaration
|
|
10
|
+
name: (identifier) @name
|
|
11
|
+
parameters: (parameter_list) @params
|
|
12
|
+
) @function_node
|
|
13
|
+
|
|
14
|
+
(constructor_declaration
|
|
15
|
+
name: (identifier) @name
|
|
16
|
+
parameters: (parameter_list) @params
|
|
17
|
+
) @function_node
|
|
18
|
+
|
|
19
|
+
(local_function_statement
|
|
20
|
+
name: (identifier) @name
|
|
21
|
+
parameters: (parameter_list) @params
|
|
22
|
+
) @function_node
|
|
23
|
+
""",
|
|
24
|
+
"classes": """
|
|
25
|
+
(class_declaration
|
|
26
|
+
name: (identifier) @name
|
|
27
|
+
(base_list)? @bases
|
|
28
|
+
) @class
|
|
29
|
+
""",
|
|
30
|
+
"interfaces": """
|
|
31
|
+
(interface_declaration
|
|
32
|
+
name: (identifier) @name
|
|
33
|
+
(base_list)? @bases
|
|
34
|
+
) @interface
|
|
35
|
+
""",
|
|
36
|
+
"structs": """
|
|
37
|
+
(struct_declaration
|
|
38
|
+
name: (identifier) @name
|
|
39
|
+
(base_list)? @bases
|
|
40
|
+
) @struct
|
|
41
|
+
""",
|
|
42
|
+
"enums": """
|
|
43
|
+
(enum_declaration
|
|
44
|
+
name: (identifier) @name
|
|
45
|
+
) @enum
|
|
46
|
+
""",
|
|
47
|
+
"records": """
|
|
48
|
+
(record_declaration
|
|
49
|
+
name: (identifier) @name
|
|
50
|
+
(base_list)? @bases
|
|
51
|
+
) @record
|
|
52
|
+
""",
|
|
53
|
+
"properties": """
|
|
54
|
+
(property_declaration
|
|
55
|
+
name: (identifier) @name
|
|
56
|
+
) @property
|
|
57
|
+
""",
|
|
58
|
+
"imports": """
|
|
59
|
+
(using_directive) @import
|
|
60
|
+
""",
|
|
61
|
+
"calls": """
|
|
62
|
+
(invocation_expression
|
|
63
|
+
function: [
|
|
64
|
+
(identifier) @name
|
|
65
|
+
(member_access_expression
|
|
66
|
+
name: (identifier) @name
|
|
67
|
+
)
|
|
68
|
+
]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
(object_creation_expression
|
|
72
|
+
type: [
|
|
73
|
+
(identifier) @name
|
|
74
|
+
(qualified_name) @name
|
|
75
|
+
]
|
|
76
|
+
)
|
|
77
|
+
""",
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
class CSharpTreeSitterParser:
|
|
81
|
+
def __init__(self, generic_parser_wrapper: Any):
|
|
82
|
+
self.generic_parser_wrapper = generic_parser_wrapper
|
|
83
|
+
self.language_name = "c_sharp"
|
|
84
|
+
self.language = generic_parser_wrapper.language
|
|
85
|
+
self.parser = generic_parser_wrapper.parser
|
|
86
|
+
|
|
87
|
+
def parse(self, path: Path, is_dependency: bool = False, index_source: bool = False) -> Dict[str, Any]:
|
|
88
|
+
try:
|
|
89
|
+
self.index_source = index_source
|
|
90
|
+
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
91
|
+
source_code = f.read()
|
|
92
|
+
|
|
93
|
+
if not source_code.strip():
|
|
94
|
+
warning_logger(f"Empty or whitespace-only file: {path}")
|
|
95
|
+
return {
|
|
96
|
+
"path": str(path),
|
|
97
|
+
"functions": [],
|
|
98
|
+
"classes": [],
|
|
99
|
+
"interfaces": [],
|
|
100
|
+
"structs": [],
|
|
101
|
+
"enums": [],
|
|
102
|
+
"records": [],
|
|
103
|
+
"properties": [],
|
|
104
|
+
"variables": [],
|
|
105
|
+
"imports": [],
|
|
106
|
+
"function_calls": [],
|
|
107
|
+
"is_dependency": is_dependency,
|
|
108
|
+
"lang": self.language_name,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
tree = self.parser.parse(bytes(source_code, "utf8"))
|
|
112
|
+
|
|
113
|
+
parsed_functions = []
|
|
114
|
+
parsed_classes = []
|
|
115
|
+
parsed_interfaces = []
|
|
116
|
+
parsed_structs = []
|
|
117
|
+
parsed_enums = []
|
|
118
|
+
parsed_records = []
|
|
119
|
+
parsed_properties = []
|
|
120
|
+
parsed_imports = []
|
|
121
|
+
parsed_calls = []
|
|
122
|
+
|
|
123
|
+
for capture_name, query_str in CSHARP_QUERIES.items():
|
|
124
|
+
captures = execute_query(self.language, query_str, tree.root_node)
|
|
125
|
+
|
|
126
|
+
if capture_name == "functions":
|
|
127
|
+
parsed_functions = self._parse_functions(captures, source_code, path, tree.root_node)
|
|
128
|
+
elif capture_name == "classes":
|
|
129
|
+
parsed_classes = self._parse_type_declarations(captures, source_code, path, "Class")
|
|
130
|
+
elif capture_name == "interfaces":
|
|
131
|
+
parsed_interfaces = self._parse_type_declarations(captures, source_code, path, "Interface")
|
|
132
|
+
elif capture_name == "structs":
|
|
133
|
+
parsed_structs = self._parse_type_declarations(captures, source_code, path, "Struct")
|
|
134
|
+
elif capture_name == "enums":
|
|
135
|
+
parsed_enums = self._parse_type_declarations(captures, source_code, path, "Enum")
|
|
136
|
+
elif capture_name == "records":
|
|
137
|
+
parsed_records = self._parse_type_declarations(captures, source_code, path, "Record")
|
|
138
|
+
elif capture_name == "properties":
|
|
139
|
+
parsed_properties = self._parse_properties(captures, source_code, path, tree.root_node)
|
|
140
|
+
elif capture_name == "imports":
|
|
141
|
+
parsed_imports = self._parse_imports(captures, source_code)
|
|
142
|
+
elif capture_name == "calls":
|
|
143
|
+
parsed_calls = self._parse_calls(captures, source_code)
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
"path": str(path),
|
|
147
|
+
"functions": parsed_functions,
|
|
148
|
+
"classes": parsed_classes,
|
|
149
|
+
"interfaces": parsed_interfaces,
|
|
150
|
+
"structs": parsed_structs,
|
|
151
|
+
"enums": parsed_enums,
|
|
152
|
+
"records": parsed_records,
|
|
153
|
+
"properties": parsed_properties,
|
|
154
|
+
"variables": [],
|
|
155
|
+
"imports": parsed_imports,
|
|
156
|
+
"function_calls": parsed_calls,
|
|
157
|
+
"is_dependency": is_dependency,
|
|
158
|
+
"lang": self.language_name,
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
except Exception as e:
|
|
162
|
+
error_logger(f"Error parsing C# file {path}: {e}")
|
|
163
|
+
return {
|
|
164
|
+
"path": str(path),
|
|
165
|
+
"functions": [],
|
|
166
|
+
"classes": [],
|
|
167
|
+
"interfaces": [],
|
|
168
|
+
"structs": [],
|
|
169
|
+
"enums": [],
|
|
170
|
+
"records": [],
|
|
171
|
+
"properties": [],
|
|
172
|
+
"variables": [],
|
|
173
|
+
"imports": [],
|
|
174
|
+
"function_calls": [],
|
|
175
|
+
"is_dependency": is_dependency,
|
|
176
|
+
"lang": self.language_name,
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
def _parse_functions(self, captures: list, source_code: str, path: Path, root_node) -> list[Dict[str, Any]]:
|
|
180
|
+
functions = []
|
|
181
|
+
source_lines = source_code.splitlines()
|
|
182
|
+
|
|
183
|
+
for node, capture_name in captures:
|
|
184
|
+
if capture_name == "function_node":
|
|
185
|
+
try:
|
|
186
|
+
start_line = node.start_point[0] + 1
|
|
187
|
+
end_line = node.end_point[0] + 1
|
|
188
|
+
|
|
189
|
+
name_captures = [
|
|
190
|
+
(n, cn) for n, cn in captures
|
|
191
|
+
if cn == "name" and n.parent.id == node.id
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
if name_captures:
|
|
195
|
+
name_node = name_captures[0][0]
|
|
196
|
+
func_name = self._get_node_text(name_node)
|
|
197
|
+
|
|
198
|
+
params_captures = [
|
|
199
|
+
(n, cn) for n, cn in captures
|
|
200
|
+
if cn == "params" and n.parent.id == node.id
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
parameters = []
|
|
204
|
+
if params_captures:
|
|
205
|
+
params_node = params_captures[0][0]
|
|
206
|
+
parameters = self._extract_parameters(params_node)
|
|
207
|
+
|
|
208
|
+
# Extract attributes applied to this function
|
|
209
|
+
attributes = []
|
|
210
|
+
if node.parent and node.parent.type == "attribute_list":
|
|
211
|
+
attr_text = self._get_node_text(node.parent)
|
|
212
|
+
attributes.append(attr_text)
|
|
213
|
+
|
|
214
|
+
# Find containing class/struct/interface
|
|
215
|
+
class_context = self._find_containing_type(node, source_code)
|
|
216
|
+
|
|
217
|
+
source_text = self._get_node_text(node)
|
|
218
|
+
|
|
219
|
+
func_data = {
|
|
220
|
+
"name": func_name,
|
|
221
|
+
"args": parameters,
|
|
222
|
+
"attributes": attributes,
|
|
223
|
+
"line_number": start_line,
|
|
224
|
+
"end_line": end_line,
|
|
225
|
+
"path": str(path),
|
|
226
|
+
"lang": self.language_name,
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
# Add class context if found
|
|
230
|
+
if class_context:
|
|
231
|
+
func_data["class_context"] = class_context
|
|
232
|
+
|
|
233
|
+
if self.index_source:
|
|
234
|
+
func_data["source"] = source_text
|
|
235
|
+
|
|
236
|
+
functions.append(func_data)
|
|
237
|
+
|
|
238
|
+
except Exception as e:
|
|
239
|
+
error_logger(f"Error parsing function in {path}: {e}")
|
|
240
|
+
continue
|
|
241
|
+
|
|
242
|
+
return functions
|
|
243
|
+
|
|
244
|
+
def _parse_type_declarations(self, captures: list, source_code: str, path: Path, type_label: str) -> list[Dict[str, Any]]:
|
|
245
|
+
"""Parse class, interface, struct, enum, or record declarations with inheritance info."""
|
|
246
|
+
types = []
|
|
247
|
+
|
|
248
|
+
# Map capture names based on type
|
|
249
|
+
capture_map = {
|
|
250
|
+
"Class": "class",
|
|
251
|
+
"Interface": "interface",
|
|
252
|
+
"Struct": "struct",
|
|
253
|
+
"Enum": "enum",
|
|
254
|
+
"Record": "record"
|
|
255
|
+
}
|
|
256
|
+
expected_capture = capture_map.get(type_label, "class")
|
|
257
|
+
|
|
258
|
+
for node, capture_name in captures:
|
|
259
|
+
if capture_name == expected_capture:
|
|
260
|
+
try:
|
|
261
|
+
start_line = node.start_point[0] + 1
|
|
262
|
+
end_line = node.end_point[0] + 1
|
|
263
|
+
|
|
264
|
+
name_captures = [
|
|
265
|
+
(n, cn) for n, cn in captures
|
|
266
|
+
if cn == "name" and n.parent.id == node.id
|
|
267
|
+
]
|
|
268
|
+
|
|
269
|
+
if name_captures:
|
|
270
|
+
name_node = name_captures[0][0]
|
|
271
|
+
type_name = self._get_node_text(name_node)
|
|
272
|
+
|
|
273
|
+
# Extract base classes/interfaces
|
|
274
|
+
bases = []
|
|
275
|
+
bases_captures = [
|
|
276
|
+
(n, cn) for n, cn in captures
|
|
277
|
+
if cn == "bases" and n.parent.id == node.id
|
|
278
|
+
]
|
|
279
|
+
|
|
280
|
+
if bases_captures:
|
|
281
|
+
bases_node = bases_captures[0][0]
|
|
282
|
+
bases_text = self._get_node_text(bases_node)
|
|
283
|
+
# Parse base list: ": BaseClass, IInterface1, IInterface2"
|
|
284
|
+
bases_text = bases_text.strip().lstrip(':').strip()
|
|
285
|
+
if bases_text:
|
|
286
|
+
bases = [b.strip() for b in bases_text.split(',')]
|
|
287
|
+
|
|
288
|
+
source_text = self._get_node_text(node)
|
|
289
|
+
|
|
290
|
+
type_data = {
|
|
291
|
+
"name": type_name,
|
|
292
|
+
"line_number": start_line,
|
|
293
|
+
"end_line": end_line,
|
|
294
|
+
"path": str(path),
|
|
295
|
+
"lang": self.language_name,
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
# Add bases if found
|
|
299
|
+
if bases:
|
|
300
|
+
type_data["bases"] = bases
|
|
301
|
+
|
|
302
|
+
if self.index_source:
|
|
303
|
+
type_data["source"] = source_text
|
|
304
|
+
|
|
305
|
+
types.append(type_data)
|
|
306
|
+
|
|
307
|
+
except Exception as e:
|
|
308
|
+
error_logger(f"Error parsing {type_label} in {path}: {e}")
|
|
309
|
+
continue
|
|
310
|
+
|
|
311
|
+
return types
|
|
312
|
+
|
|
313
|
+
def _parse_imports(self, captures: list, source_code: str) -> list[dict]:
|
|
314
|
+
imports = []
|
|
315
|
+
|
|
316
|
+
for node, capture_name in captures:
|
|
317
|
+
if capture_name == "import":
|
|
318
|
+
try:
|
|
319
|
+
import_text = self._get_node_text(node)
|
|
320
|
+
# Match: using System.Collections.Generic; or using static System.Math;
|
|
321
|
+
import_match = re.search(r'using\s+(?:static\s+)?([^;]+)', import_text)
|
|
322
|
+
if import_match:
|
|
323
|
+
import_path = import_match.group(1).strip()
|
|
324
|
+
|
|
325
|
+
# Check for alias: using MyAlias = System.Collections.Generic.List<int>;
|
|
326
|
+
alias = None
|
|
327
|
+
if '=' in import_path:
|
|
328
|
+
parts = import_path.split('=')
|
|
329
|
+
alias = parts[0].strip()
|
|
330
|
+
import_path = parts[1].strip()
|
|
331
|
+
|
|
332
|
+
import_data = {
|
|
333
|
+
"name": import_path,
|
|
334
|
+
"full_import_name": import_path,
|
|
335
|
+
"line_number": node.start_point[0] + 1,
|
|
336
|
+
"alias": alias,
|
|
337
|
+
"context": (None, None),
|
|
338
|
+
"lang": self.language_name,
|
|
339
|
+
"is_dependency": False,
|
|
340
|
+
}
|
|
341
|
+
imports.append(import_data)
|
|
342
|
+
except Exception as e:
|
|
343
|
+
error_logger(f"Error parsing import: {e}")
|
|
344
|
+
continue
|
|
345
|
+
|
|
346
|
+
return imports
|
|
347
|
+
|
|
348
|
+
def _get_parent_context(self, node: Any, types: Tuple[str, ...] = ('class_declaration', 'struct_declaration', 'function_declaration', 'method_declaration')):
|
|
349
|
+
"""Find parent context for C# constructs."""
|
|
350
|
+
curr = node.parent
|
|
351
|
+
while curr:
|
|
352
|
+
if curr.type in types:
|
|
353
|
+
if curr.type in ('method_declaration', 'function_declaration'):
|
|
354
|
+
name_node = curr.child_by_field_name('name')
|
|
355
|
+
return self._get_node_text(name_node) if name_node else None, curr.type, curr.start_point[0] + 1
|
|
356
|
+
else:
|
|
357
|
+
# Classes, structs, etc.
|
|
358
|
+
name_node = curr.child_by_field_name('name')
|
|
359
|
+
return self._get_node_text(name_node) if name_node else None, curr.type, curr.start_point[0] + 1
|
|
360
|
+
curr = curr.parent
|
|
361
|
+
return None, None, None
|
|
362
|
+
|
|
363
|
+
def _get_node_text(self, node: Any) -> str:
|
|
364
|
+
if not node: return ""
|
|
365
|
+
return node.text.decode("utf-8")
|
|
366
|
+
|
|
367
|
+
def _parse_calls(self, captures: list, source_code: str) -> list[dict]:
|
|
368
|
+
calls = []
|
|
369
|
+
seen_calls = set()
|
|
370
|
+
|
|
371
|
+
for node, capture_name in captures:
|
|
372
|
+
if capture_name == "name":
|
|
373
|
+
try:
|
|
374
|
+
call_name = self._get_node_text(node)
|
|
375
|
+
line_number = node.start_point[0] + 1
|
|
376
|
+
|
|
377
|
+
# Avoid duplicates
|
|
378
|
+
call_key = f"{call_name}_{line_number}"
|
|
379
|
+
if call_key in seen_calls:
|
|
380
|
+
continue
|
|
381
|
+
seen_calls.add(call_key)
|
|
382
|
+
|
|
383
|
+
# Get context
|
|
384
|
+
context_name, context_type, context_line = self._get_parent_context(node)
|
|
385
|
+
class_context = context_name if context_type and 'class' in context_type else None
|
|
386
|
+
|
|
387
|
+
call_data = {
|
|
388
|
+
"name": call_name,
|
|
389
|
+
"full_name": call_name,
|
|
390
|
+
"line_number": line_number,
|
|
391
|
+
"args": [],
|
|
392
|
+
"inferred_obj_type": None,
|
|
393
|
+
"context": (context_name, context_type, context_line),
|
|
394
|
+
"class_context": class_context,
|
|
395
|
+
"lang": self.language_name,
|
|
396
|
+
"is_dependency": False,
|
|
397
|
+
}
|
|
398
|
+
calls.append(call_data)
|
|
399
|
+
except Exception as e:
|
|
400
|
+
error_logger(f"Error parsing call: {e}")
|
|
401
|
+
continue
|
|
402
|
+
|
|
403
|
+
return calls
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def _extract_parameters(self, params_node) -> list[str]:
|
|
407
|
+
params = []
|
|
408
|
+
if not params_node:
|
|
409
|
+
return params
|
|
410
|
+
|
|
411
|
+
# Iterate over parameter nodes in the parameter list
|
|
412
|
+
for child in params_node.children:
|
|
413
|
+
if child.type == "parameter":
|
|
414
|
+
# find the identifier
|
|
415
|
+
name_node = child.child_by_field_name("name")
|
|
416
|
+
if name_node:
|
|
417
|
+
params.append(self._get_node_text(name_node))
|
|
418
|
+
else:
|
|
419
|
+
# Fallback: scan children for identifier if field name not present in this grammar version
|
|
420
|
+
for sub in child.children:
|
|
421
|
+
if sub.type == "identifier":
|
|
422
|
+
params.append(self._get_node_text(sub))
|
|
423
|
+
break
|
|
424
|
+
return params
|
|
425
|
+
|
|
426
|
+
def _find_containing_type(self, node, source_code):
|
|
427
|
+
"""Find the containing class, struct, interface, or record for a given node."""
|
|
428
|
+
current = node.parent
|
|
429
|
+
while current:
|
|
430
|
+
if current.type in ['class_declaration', 'struct_declaration', 'interface_declaration', 'record_declaration']:
|
|
431
|
+
# Find the name of this type
|
|
432
|
+
for child in current.children:
|
|
433
|
+
if child.type == 'identifier':
|
|
434
|
+
return self._get_node_text(child)
|
|
435
|
+
current = current.parent
|
|
436
|
+
return None
|
|
437
|
+
|
|
438
|
+
def _parse_properties(self, captures: list, source_code: str, path: Path, root_node) -> list[Dict[str, Any]]:
|
|
439
|
+
"""Parse C# properties."""
|
|
440
|
+
properties = []
|
|
441
|
+
|
|
442
|
+
for node, capture_name in captures:
|
|
443
|
+
if capture_name == "property":
|
|
444
|
+
try:
|
|
445
|
+
start_line = node.start_point[0] + 1
|
|
446
|
+
end_line = node.end_point[0] + 1
|
|
447
|
+
|
|
448
|
+
name_captures = [
|
|
449
|
+
(n, cn) for n, cn in captures
|
|
450
|
+
if cn == "name" and n.parent == node
|
|
451
|
+
]
|
|
452
|
+
|
|
453
|
+
if name_captures:
|
|
454
|
+
name_node = name_captures[0][0]
|
|
455
|
+
prop_name = self._get_node_text(name_node)
|
|
456
|
+
|
|
457
|
+
# Get property type from node children
|
|
458
|
+
prop_type = None
|
|
459
|
+
for child in node.children:
|
|
460
|
+
if child.type in ['predefined_type', 'identifier', 'generic_name', 'nullable_type', 'array_type']:
|
|
461
|
+
prop_type = self._get_node_text(child)
|
|
462
|
+
break
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
# Find containing class/struct
|
|
466
|
+
class_context = self._find_containing_type(node, source_code)
|
|
467
|
+
|
|
468
|
+
source_text = self._get_node_text(node)
|
|
469
|
+
|
|
470
|
+
prop_data = {
|
|
471
|
+
"name": prop_name,
|
|
472
|
+
"type": prop_type,
|
|
473
|
+
"line_number": start_line,
|
|
474
|
+
"end_line": end_line,
|
|
475
|
+
"path": str(path),
|
|
476
|
+
"lang": self.language_name,
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if class_context:
|
|
480
|
+
prop_data["class_context"] = class_context
|
|
481
|
+
|
|
482
|
+
if self.index_source:
|
|
483
|
+
prop_data["source"] = source_text
|
|
484
|
+
|
|
485
|
+
properties.append(prop_data)
|
|
486
|
+
|
|
487
|
+
except Exception as e:
|
|
488
|
+
error_logger(f"Error parsing property in {path}: {e}")
|
|
489
|
+
continue
|
|
490
|
+
|
|
491
|
+
return properties
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def pre_scan_csharp(files: list[Path], parser_wrapper) -> dict:
|
|
496
|
+
"""Pre-scan C# files to build a name-to-files mapping."""
|
|
497
|
+
name_to_files = {}
|
|
498
|
+
|
|
499
|
+
for path in files:
|
|
500
|
+
try:
|
|
501
|
+
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
502
|
+
content = f.read()
|
|
503
|
+
|
|
504
|
+
# Match class declarations
|
|
505
|
+
class_matches = re.finditer(
|
|
506
|
+
r'\b(?:public\s+|private\s+|protected\s+|internal\s+)?(?:static\s+)?(?:abstract\s+)?(?:sealed\s+)?(?:partial\s+)?class\s+(\w+)',
|
|
507
|
+
content
|
|
508
|
+
)
|
|
509
|
+
for match in class_matches:
|
|
510
|
+
class_name = match.group(1)
|
|
511
|
+
if class_name not in name_to_files:
|
|
512
|
+
name_to_files[class_name] = []
|
|
513
|
+
name_to_files[class_name].append(str(path))
|
|
514
|
+
|
|
515
|
+
# Match interface declarations
|
|
516
|
+
interface_matches = re.finditer(
|
|
517
|
+
r'\b(?:public\s+|private\s+|protected\s+|internal\s+)?(?:partial\s+)?interface\s+(\w+)',
|
|
518
|
+
content
|
|
519
|
+
)
|
|
520
|
+
for match in interface_matches:
|
|
521
|
+
interface_name = match.group(1)
|
|
522
|
+
if interface_name not in name_to_files:
|
|
523
|
+
name_to_files[interface_name] = []
|
|
524
|
+
name_to_files[interface_name].append(str(path))
|
|
525
|
+
|
|
526
|
+
# Match struct declarations
|
|
527
|
+
struct_matches = re.finditer(
|
|
528
|
+
r'\b(?:public\s+|private\s+|protected\s+|internal\s+)?(?:readonly\s+)?(?:partial\s+)?struct\s+(\w+)',
|
|
529
|
+
content
|
|
530
|
+
)
|
|
531
|
+
for match in struct_matches:
|
|
532
|
+
struct_name = match.group(1)
|
|
533
|
+
if struct_name not in name_to_files:
|
|
534
|
+
name_to_files[struct_name] = []
|
|
535
|
+
name_to_files[struct_name].append(str(path))
|
|
536
|
+
|
|
537
|
+
# Match record declarations
|
|
538
|
+
record_matches = re.finditer(
|
|
539
|
+
r'\b(?:public\s+|private\s+|protected\s+|internal\s+)?(?:sealed\s+)?record\s+(?:class\s+)?(\w+)',
|
|
540
|
+
content
|
|
541
|
+
)
|
|
542
|
+
for match in record_matches:
|
|
543
|
+
record_name = match.group(1)
|
|
544
|
+
if record_name not in name_to_files:
|
|
545
|
+
name_to_files[record_name] = []
|
|
546
|
+
name_to_files[record_name].append(str(path))
|
|
547
|
+
|
|
548
|
+
except Exception as e:
|
|
549
|
+
error_logger(f"Error pre-scanning C# file {path}: {e}")
|
|
550
|
+
|
|
551
|
+
return name_to_files
|