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.
Files changed (101) hide show
  1. codegraphcontext/__init__.py +2 -0
  2. codegraphcontext/__main__.py +5 -0
  3. codegraphcontext/_cgc_rust.cp310-win_amd64.pyd +0 -0
  4. codegraphcontext/cli/__init__.py +1 -0
  5. codegraphcontext/cli/cli_helpers.py +774 -0
  6. codegraphcontext/cli/config_manager.py +1051 -0
  7. codegraphcontext/cli/main.py +2386 -0
  8. codegraphcontext/cli/registry_commands.py +404 -0
  9. codegraphcontext/cli/setup_macos.py +93 -0
  10. codegraphcontext/cli/setup_wizard.py +991 -0
  11. codegraphcontext/cli/visualizer.py +50 -0
  12. codegraphcontext/core/__init__.py +165 -0
  13. codegraphcontext/core/bundle_registry.py +181 -0
  14. codegraphcontext/core/cgc_bundle.py +858 -0
  15. codegraphcontext/core/cgcignore.py +118 -0
  16. codegraphcontext/core/database.py +273 -0
  17. codegraphcontext/core/database_falkordb.py +480 -0
  18. codegraphcontext/core/database_falkordb_remote.py +199 -0
  19. codegraphcontext/core/database_kuzu.py +636 -0
  20. codegraphcontext/core/falkor_worker.py +132 -0
  21. codegraphcontext/core/jobs.py +132 -0
  22. codegraphcontext/core/watcher.py +260 -0
  23. codegraphcontext/prompts.py +125 -0
  24. codegraphcontext/server.py +471 -0
  25. codegraphcontext/tool_definitions.py +220 -0
  26. codegraphcontext/tools/__init__.py +0 -0
  27. codegraphcontext/tools/advanced_language_query_tool.py +103 -0
  28. codegraphcontext/tools/code_finder.py +1160 -0
  29. codegraphcontext/tools/graph_builder.py +337 -0
  30. codegraphcontext/tools/handlers/analysis_handlers.py +114 -0
  31. codegraphcontext/tools/handlers/indexing_handlers.py +116 -0
  32. codegraphcontext/tools/handlers/management_handlers.py +339 -0
  33. codegraphcontext/tools/handlers/query_handlers.py +83 -0
  34. codegraphcontext/tools/handlers/watcher_handlers.py +83 -0
  35. codegraphcontext/tools/indexing/__init__.py +1 -0
  36. codegraphcontext/tools/indexing/constants.py +25 -0
  37. codegraphcontext/tools/indexing/discovery.py +64 -0
  38. codegraphcontext/tools/indexing/engine.py +109 -0
  39. codegraphcontext/tools/indexing/persistence/__init__.py +3 -0
  40. codegraphcontext/tools/indexing/persistence/parquet_loader.py +643 -0
  41. codegraphcontext/tools/indexing/persistence/writer.py +1114 -0
  42. codegraphcontext/tools/indexing/pipeline.py +171 -0
  43. codegraphcontext/tools/indexing/pre_scan.py +105 -0
  44. codegraphcontext/tools/indexing/resolution/__init__.py +8 -0
  45. codegraphcontext/tools/indexing/resolution/calls.py +253 -0
  46. codegraphcontext/tools/indexing/resolution/inheritance.py +91 -0
  47. codegraphcontext/tools/indexing/sanitize.py +41 -0
  48. codegraphcontext/tools/indexing/schema.py +79 -0
  49. codegraphcontext/tools/indexing/schema_contract.py +44 -0
  50. codegraphcontext/tools/indexing/scip_pipeline.py +140 -0
  51. codegraphcontext/tools/languages/c.py +563 -0
  52. codegraphcontext/tools/languages/cpp.py +616 -0
  53. codegraphcontext/tools/languages/csharp.py +551 -0
  54. codegraphcontext/tools/languages/dart.py +378 -0
  55. codegraphcontext/tools/languages/elixir.py +460 -0
  56. codegraphcontext/tools/languages/go.py +499 -0
  57. codegraphcontext/tools/languages/haskell.py +426 -0
  58. codegraphcontext/tools/languages/java.py +500 -0
  59. codegraphcontext/tools/languages/javascript.py +589 -0
  60. codegraphcontext/tools/languages/kotlin.py +640 -0
  61. codegraphcontext/tools/languages/perl.py +260 -0
  62. codegraphcontext/tools/languages/php.py +519 -0
  63. codegraphcontext/tools/languages/python.py +576 -0
  64. codegraphcontext/tools/languages/ruby.py +537 -0
  65. codegraphcontext/tools/languages/rust.py +295 -0
  66. codegraphcontext/tools/languages/scala.py +519 -0
  67. codegraphcontext/tools/languages/swift.py +491 -0
  68. codegraphcontext/tools/languages/typescript.py +575 -0
  69. codegraphcontext/tools/languages/typescriptjsx.py +151 -0
  70. codegraphcontext/tools/package_resolver.py +472 -0
  71. codegraphcontext/tools/query_tool_languages/c_toolkit.py +5 -0
  72. codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +87 -0
  73. codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +5 -0
  74. codegraphcontext/tools/query_tool_languages/dart_toolkit.py +5 -0
  75. codegraphcontext/tools/query_tool_languages/go_toolkit.py +5 -0
  76. codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +5 -0
  77. codegraphcontext/tools/query_tool_languages/java_toolkit.py +5 -0
  78. codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +5 -0
  79. codegraphcontext/tools/query_tool_languages/perl_toolkit.py +5 -0
  80. codegraphcontext/tools/query_tool_languages/python_toolkit.py +5 -0
  81. codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +5 -0
  82. codegraphcontext/tools/query_tool_languages/rust_toolkit.py +5 -0
  83. codegraphcontext/tools/query_tool_languages/scala_toolkit.py +5 -0
  84. codegraphcontext/tools/query_tool_languages/swift_toolkit.py +5 -0
  85. codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +5 -0
  86. codegraphcontext/tools/scip_indexer.py +467 -0
  87. codegraphcontext/tools/scip_pb2.py +2455 -0
  88. codegraphcontext/tools/system.py +133 -0
  89. codegraphcontext/tools/tree_sitter_parser.py +103 -0
  90. codegraphcontext/utils/debug_log.py +90 -0
  91. codegraphcontext/utils/path_ignore.py +55 -0
  92. codegraphcontext/utils/repo_path.py +27 -0
  93. codegraphcontext/utils/tree_sitter_manager.py +285 -0
  94. codegraphcontext/utils/visualize_graph.py +0 -0
  95. codegraphcontext/viz/server.py +282 -0
  96. codegraphcontext_rust-0.5.5.dist-info/METADATA +537 -0
  97. codegraphcontext_rust-0.5.5.dist-info/RECORD +101 -0
  98. codegraphcontext_rust-0.5.5.dist-info/WHEEL +4 -0
  99. codegraphcontext_rust-0.5.5.dist-info/entry_points.txt +3 -0
  100. codegraphcontext_rust-0.5.5.dist-info/licenses/LICENSE +21 -0
  101. 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