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,500 @@
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
+ JAVA_QUERIES = {
8
+ "functions": """
9
+ (method_declaration
10
+ name: (identifier) @name
11
+ parameters: (formal_parameters) @params
12
+ ) @function_node
13
+
14
+ (constructor_declaration
15
+ name: (identifier) @name
16
+ parameters: (formal_parameters) @params
17
+ ) @function_node
18
+ """,
19
+ "classes": """
20
+ [
21
+ (class_declaration name: (identifier) @name)
22
+ (interface_declaration name: (identifier) @name)
23
+ (enum_declaration name: (identifier) @name)
24
+ (annotation_type_declaration name: (identifier) @name)
25
+ ] @class
26
+ """,
27
+ "imports": """
28
+ (import_declaration) @import
29
+ """,
30
+ # variables MUST be parsed before calls so we can build var_type_map
31
+ # and populate inferred_obj_type on method-call nodes for cross-file resolution.
32
+ "variables": """
33
+ (local_variable_declaration
34
+ type: (_) @type
35
+ declarator: (variable_declarator
36
+ name: (identifier) @name
37
+ )
38
+ ) @variable
39
+
40
+ (field_declaration
41
+ type: (_) @type
42
+ declarator: (variable_declarator
43
+ name: (identifier) @name
44
+ )
45
+ ) @variable
46
+ """,
47
+ "calls": """
48
+ (method_invocation
49
+ name: (identifier) @name
50
+ ) @call_node
51
+
52
+ (object_creation_expression
53
+ type: [
54
+ (type_identifier)
55
+ (scoped_type_identifier)
56
+ (generic_type)
57
+ ] @name
58
+ ) @call_node
59
+ """,
60
+ }
61
+
62
+ class JavaTreeSitterParser:
63
+ def __init__(self, generic_parser_wrapper: Any):
64
+ self.generic_parser_wrapper = generic_parser_wrapper
65
+ self.language_name = "java"
66
+ self.language = generic_parser_wrapper.language
67
+ self.parser = generic_parser_wrapper.parser
68
+
69
+ @staticmethod
70
+ def _strip_generic(type_str: str) -> str:
71
+ """Return the raw type name without generic parameters, e.g. 'List<String>' -> 'List'."""
72
+ bracket = type_str.find('<')
73
+ return type_str[:bracket].strip() if bracket != -1 else type_str.strip()
74
+
75
+ def parse(self, path: Path, is_dependency: bool = False, index_source: bool = False) -> Dict[str, Any]:
76
+ try:
77
+ self.index_source = index_source
78
+ with open(path, 'r', encoding='utf-8', errors='ignore') as f:
79
+ source_code = f.read()
80
+
81
+ if not source_code.strip():
82
+ warning_logger(f"Empty or whitespace-only file: {path}")
83
+ return {
84
+ "path": str(path),
85
+ "functions": [],
86
+ "classes": [],
87
+ "variables": [],
88
+ "imports": [],
89
+ "function_calls": [],
90
+ "is_dependency": is_dependency,
91
+ "lang": self.language_name,
92
+ }
93
+
94
+ tree = self.parser.parse(bytes(source_code, "utf8"))
95
+
96
+ parsed_functions = []
97
+ parsed_classes = []
98
+ parsed_variables = []
99
+ parsed_imports = []
100
+ parsed_calls = []
101
+ # var_type_map is built from the "variables" pass so that the subsequent
102
+ # "calls" pass can resolve the declared type of field/local-variable
103
+ # receivers (e.g. `service.doWork()` -> inferred_obj_type='WorkService').
104
+ var_type_map: Dict[str, str] = {}
105
+
106
+ for capture_name, query in JAVA_QUERIES.items():
107
+ results = execute_query(self.language, query, tree.root_node)
108
+
109
+ if capture_name == "functions":
110
+ parsed_functions = self._parse_functions(results, source_code, path)
111
+ elif capture_name == "classes":
112
+ parsed_classes = self._parse_classes(results, source_code, path)
113
+ elif capture_name == "imports":
114
+ parsed_imports = self._parse_imports(results, source_code)
115
+ elif capture_name == "variables":
116
+ parsed_variables = self._parse_variables(results, source_code, path)
117
+ # Build name->type map for cross-file call resolution
118
+ var_type_map = {
119
+ v["name"]: self._strip_generic(v["type"])
120
+ for v in parsed_variables
121
+ if v.get("type") and v.get("name")
122
+ }
123
+ elif capture_name == "calls":
124
+ parsed_calls = self._parse_calls(results, source_code, var_type_map)
125
+
126
+ return {
127
+ "path": str(path),
128
+ "functions": parsed_functions,
129
+ "classes": parsed_classes,
130
+ "variables": parsed_variables,
131
+ "imports": parsed_imports,
132
+ "function_calls": parsed_calls,
133
+ "is_dependency": is_dependency,
134
+ "lang": self.language_name,
135
+ }
136
+
137
+ except Exception as e:
138
+ error_logger(f"Error parsing Java file {path}: {e}")
139
+ return {
140
+ "path": str(path),
141
+ "functions": [],
142
+ "classes": [],
143
+ "variables": [],
144
+ "imports": [],
145
+ "function_calls": [],
146
+ "is_dependency": is_dependency,
147
+ "lang": self.language_name,
148
+ }
149
+
150
+ def _get_parent_context(self, node: Any) -> Tuple[Optional[str], Optional[str], Optional[int]]:
151
+ curr = node.parent
152
+ while curr:
153
+ if curr.type in ("method_declaration", "constructor_declaration"):
154
+ name_node = curr.child_by_field_name("name")
155
+ return (
156
+ self._get_node_text(name_node) if name_node else None,
157
+ curr.type,
158
+ curr.start_point[0] + 1,
159
+ )
160
+ if curr.type in ("class_declaration", "interface_declaration", "enum_declaration", "annotation_type_declaration"):
161
+ name_node = curr.child_by_field_name("name")
162
+ return (
163
+ self._get_node_text(name_node) if name_node else None,
164
+ curr.type,
165
+ curr.start_point[0] + 1,
166
+ )
167
+ curr = curr.parent
168
+ return None, None, None
169
+
170
+ def _get_node_text(self, node: Any) -> str:
171
+ if not node: return ""
172
+ return node.text.decode("utf-8")
173
+
174
+ def _parse_functions(self, captures: list, source_code: str, path: Path) -> list[Dict[str, Any]]:
175
+ functions = []
176
+ # Group by node identity or stable key to avoid duplicates
177
+ seen_nodes = set()
178
+
179
+ for node, capture_name in captures:
180
+ if capture_name == "function_node":
181
+ node_id = (node.start_byte, node.end_byte, node.type)
182
+ if node_id in seen_nodes:
183
+ continue
184
+ seen_nodes.add(node_id)
185
+
186
+ try:
187
+ start_line = node.start_point[0] + 1
188
+ end_line = node.end_point[0] + 1
189
+
190
+ name_node = node.child_by_field_name("name")
191
+ if name_node:
192
+ func_name = self._get_node_text(name_node)
193
+
194
+ params_node = node.child_by_field_name("parameters")
195
+ parameters = []
196
+ if params_node:
197
+ params_text = self._get_node_text(params_node)
198
+ parameters = self._extract_parameter_names(params_text)
199
+
200
+ source_text = self._get_node_text(node)
201
+
202
+ # Get class context
203
+ context_name, context_type, context_line = self._get_parent_context(node)
204
+
205
+ func_data = {
206
+ "name": func_name,
207
+ "parameters": parameters,
208
+ "line_number": start_line,
209
+ "end_line": end_line,
210
+ "path": str(path),
211
+ "lang": self.language_name,
212
+ "context": context_name,
213
+ "class_context": context_name if context_type and "class" in context_type else None
214
+ }
215
+
216
+ if self.index_source:
217
+ func_data["source"] = source_text
218
+
219
+ functions.append(func_data)
220
+
221
+ except Exception as e:
222
+ error_logger(f"Error parsing function in {path}: {e}")
223
+ continue
224
+
225
+ return functions
226
+
227
+ def _parse_classes(self, captures: list, source_code: str, path: Path) -> list[Dict[str, Any]]:
228
+ classes = []
229
+ seen_nodes = set()
230
+
231
+ for node, capture_name in captures:
232
+ if capture_name == "class":
233
+ node_id = (node.start_byte, node.end_byte, node.type)
234
+ if node_id in seen_nodes:
235
+ continue
236
+ seen_nodes.add(node_id)
237
+
238
+ try:
239
+ start_line = node.start_point[0] + 1
240
+ end_line = node.end_point[0] + 1
241
+
242
+ name_node = node.child_by_field_name("name")
243
+ if name_node:
244
+ class_name = self._get_node_text(name_node)
245
+ source_text = self._get_node_text(node)
246
+
247
+ bases = []
248
+ # Look for superclass (extends)
249
+ superclass_node = node.child_by_field_name('superclass')
250
+ if superclass_node:
251
+ # In Java, superclass field usually points to a type node
252
+ bases.append(self._get_node_text(superclass_node))
253
+
254
+ # Look for super_interfaces (implements)
255
+ interfaces_node = node.child_by_field_name('interfaces')
256
+ if not interfaces_node:
257
+ interfaces_node = next((c for c in node.children if c.type == 'super_interfaces'), None)
258
+
259
+ if interfaces_node:
260
+ type_list = interfaces_node.child_by_field_name('list')
261
+ if not type_list:
262
+ type_list = next((c for c in interfaces_node.children if c.type == 'type_list'), None)
263
+
264
+ if type_list:
265
+ for child in type_list.children:
266
+ if child.type in ('type_identifier', 'generic_type', 'scoped_type_identifier'):
267
+ bases.append(self._get_node_text(child))
268
+ else:
269
+ for child in interfaces_node.children:
270
+ if child.type in ('type_identifier', 'generic_type', 'scoped_type_identifier'):
271
+ bases.append(self._get_node_text(child))
272
+
273
+ class_data = {
274
+ "name": class_name,
275
+ "line_number": start_line,
276
+ "end_line": end_line,
277
+ "bases": bases,
278
+ "path": str(path),
279
+ "lang": self.language_name,
280
+ }
281
+
282
+ if self.index_source:
283
+ class_data["source"] = source_text
284
+
285
+ classes.append(class_data)
286
+
287
+ except Exception as e:
288
+ error_logger(f"Error parsing class in {path}: {e}")
289
+ continue
290
+
291
+ return classes
292
+
293
+ def _parse_variables(self, captures: list, source_code: str, path: Path) -> list[Dict[str, Any]]:
294
+ variables = []
295
+ seen_vars = set()
296
+
297
+ for node, capture_name in captures:
298
+ if capture_name == "variable":
299
+ # The capture is on the whole declaration, we look for name/type children or captures
300
+ # But our query captures @name and @type separately on subnodes.
301
+ # Actually, the query structure:
302
+ # (local_variable_declaration ... declarator: (variable_declarator name: (identifier) @name)) @variable
303
+ # This means we get 'variable', 'type', 'name' captures in sequence.
304
+ # We should iterate and group them.
305
+ pass
306
+
307
+ # Re-approach: Iterate captures and collect finding.
308
+ # Tree sitter returns a list of (node, capture_name).
309
+
310
+ # Simpler approach: Iterate 'name' captures that are inside a variable declaration context
311
+
312
+ current_var = {}
313
+
314
+ for node, capture_name in captures:
315
+ if capture_name == "name":
316
+ # Check parent to confirm it's a variable declarator
317
+ if node.parent.type == "variable_declarator":
318
+ var_name = self._get_node_text(node)
319
+ start_line = node.start_point[0] + 1
320
+
321
+ # Get type? Type is sibling of declarator usually, or child of declaration
322
+ # local_variable_declaration -> type, variable_declarator
323
+ declaration = node.parent.parent
324
+ type_node = declaration.child_by_field_name("type")
325
+ var_type = self._get_node_text(type_node) if type_node else "Unknown"
326
+
327
+ start_byte = node.start_byte
328
+ if start_byte in seen_vars:
329
+ continue
330
+ seen_vars.add(start_byte)
331
+
332
+ ctx_name, ctx_type, ctx_line = self._get_parent_context(node)
333
+
334
+ variables.append({
335
+ "name": var_name,
336
+ "type": var_type,
337
+ "line_number": start_line,
338
+ "path": str(path),
339
+ "lang": self.language_name,
340
+ "context": ctx_name,
341
+ "class_context": ctx_name if ctx_type and "class" in ctx_type else None
342
+ })
343
+
344
+ return variables
345
+
346
+ def _parse_imports(self, captures: list, source_code: str) -> list[dict]:
347
+ imports = []
348
+
349
+ for node, capture_name in captures:
350
+ if capture_name == "import":
351
+ try:
352
+ import_text = self._get_node_text(node)
353
+ import_match = re.search(r'import\s+(?:static\s+)?([^;]+)', import_text)
354
+ if import_match:
355
+ import_path = import_match.group(1).strip()
356
+
357
+ import_data = {
358
+ "name": import_path,
359
+ "full_import_name": import_path,
360
+ "line_number": node.start_point[0] + 1,
361
+ "alias": None,
362
+ "context": (None, None),
363
+ "lang": self.language_name,
364
+ "is_dependency": False,
365
+ }
366
+ imports.append(import_data)
367
+ except Exception as e:
368
+ error_logger(f"Error parsing import: {e}")
369
+ continue
370
+
371
+ return imports
372
+
373
+ def _parse_calls(self, captures: list, source_code: str, var_type_map: Optional[Dict[str, str]] = None) -> list[dict]:
374
+ calls = []
375
+ seen_calls = set()
376
+ if var_type_map is None:
377
+ var_type_map = {}
378
+
379
+ debug_log(f"Processing {len(captures)} captures for calls")
380
+
381
+ for node, capture_name in captures:
382
+ if capture_name == "name":
383
+ try:
384
+ call_name = self._get_node_text(node)
385
+ line_number = node.start_point[0] + 1
386
+
387
+ # Ensure we identify the full call node
388
+ call_node = node.parent
389
+ while call_node and call_node.type not in ("method_invocation", "object_creation_expression"):
390
+ call_node = call_node.parent
391
+
392
+ if not call_node:
393
+ # fallback if we matched a loose identifier
394
+ call_node = node
395
+
396
+ # Avoid duplicates
397
+ call_key = f"{call_name}_{line_number}"
398
+ if call_key in seen_calls:
399
+ continue
400
+ seen_calls.add(call_key)
401
+
402
+ # Extract arguments
403
+ args = []
404
+ if call_node:
405
+ args_node = next((c for c in call_node.children if c.type == 'argument_list'), None)
406
+ if args_node:
407
+ for arg in args_node.children:
408
+ if arg.type not in ('(', ')', ','):
409
+ args.append(self._get_node_text(arg))
410
+
411
+ # Extract meaningful full_name and infer the receiver's declared type.
412
+ # When a method is called on a field or local variable whose type was
413
+ # declared in this file (e.g. `private WorkService workService;`),
414
+ # we populate inferred_obj_type so that resolve_function_call can
415
+ # look it up in imports_map and create an accurate cross-file CALLS edge.
416
+ full_name = call_name
417
+ inferred_obj_type = None
418
+ if call_node.type == 'method_invocation':
419
+ obj_node = call_node.child_by_field_name('object')
420
+ if obj_node:
421
+ obj_text = self._get_node_text(obj_node)
422
+ full_name = f"{obj_text}.{call_name}"
423
+ # Only resolve simple identifiers (not chained calls like foo.bar().baz())
424
+ base_obj = obj_text.split(".")[0]
425
+ if "(" not in base_obj and base_obj in var_type_map:
426
+ inferred_obj_type = var_type_map[base_obj]
427
+ elif call_node.type == 'object_creation_expression':
428
+ type_node = call_node.child_by_field_name('type')
429
+ if type_node:
430
+ full_name = self._get_node_text(type_node)
431
+
432
+ ctx_name, ctx_type, ctx_line = self._get_parent_context(node)
433
+
434
+ debug_log(f"Found call: {call_name} (full_name: {full_name}, inferred_obj_type: {inferred_obj_type}, args: {args}) in context {ctx_name}")
435
+
436
+ call_data = {
437
+ "name": call_name,
438
+ "full_name": full_name,
439
+ "line_number": line_number,
440
+ "args": args,
441
+ "inferred_obj_type": inferred_obj_type,
442
+ "context": (ctx_name, ctx_type, ctx_line),
443
+ "class_context": (ctx_name, ctx_line) if ctx_type and "class" in ctx_type else (None, None),
444
+ "lang": self.language_name,
445
+ "is_dependency": False,
446
+ }
447
+ calls.append(call_data)
448
+ except Exception as e:
449
+ error_logger(f"Error parsing call: {e}")
450
+ continue
451
+
452
+ return calls
453
+
454
+
455
+ def _extract_parameter_names(self, params_text: str) -> list[str]:
456
+ params = []
457
+ if not params_text or params_text.strip() == "()":
458
+ return params
459
+
460
+ params_content = params_text.strip("()")
461
+ if not params_content:
462
+ return params
463
+
464
+ for param in params_content.split(","):
465
+ param = param.strip()
466
+ if param:
467
+ parts = param.split()
468
+ if len(parts) >= 2:
469
+ param_name = parts[-1]
470
+ params.append(param_name)
471
+
472
+ return params
473
+
474
+
475
+ def pre_scan_java(files: list[Path], parser_wrapper) -> dict:
476
+ name_to_files = {}
477
+
478
+ for path in files:
479
+ try:
480
+ with open(path, 'r', encoding='utf-8', errors='ignore') as f:
481
+ content = f.read()
482
+
483
+ class_matches = re.finditer(r'\b(?:public\s+|private\s+|protected\s+)?(?:static\s+)?(?:abstract\s+)?(?:final\s+)?class\s+(\w+)', content)
484
+ for match in class_matches:
485
+ class_name = match.group(1)
486
+ if class_name not in name_to_files:
487
+ name_to_files[class_name] = []
488
+ name_to_files[class_name].append(str(path))
489
+
490
+ interface_matches = re.finditer(r'\b(?:public\s+|private\s+|protected\s+)?interface\s+(\w+)', content)
491
+ for match in interface_matches:
492
+ interface_name = match.group(1)
493
+ if interface_name not in name_to_files:
494
+ name_to_files[interface_name] = []
495
+ name_to_files[interface_name].append(str(path))
496
+
497
+ except Exception as e:
498
+ error_logger(f"Error pre-scanning Java file {path}: {e}")
499
+
500
+ return name_to_files