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,491 @@
|
|
|
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
|
+
SWIFT_QUERIES = {
|
|
8
|
+
"functions": """
|
|
9
|
+
[
|
|
10
|
+
(function_declaration
|
|
11
|
+
name: (simple_identifier) @name
|
|
12
|
+
) @function_node
|
|
13
|
+
(init_declaration) @init_node
|
|
14
|
+
]
|
|
15
|
+
""",
|
|
16
|
+
"classes": """
|
|
17
|
+
[
|
|
18
|
+
(class_declaration
|
|
19
|
+
declaration_kind: "class"
|
|
20
|
+
name: (type_identifier) @name
|
|
21
|
+
) @class
|
|
22
|
+
(class_declaration
|
|
23
|
+
declaration_kind: "struct"
|
|
24
|
+
name: (type_identifier) @name
|
|
25
|
+
) @struct
|
|
26
|
+
(class_declaration
|
|
27
|
+
declaration_kind: "enum"
|
|
28
|
+
name: (type_identifier) @name
|
|
29
|
+
) @enum
|
|
30
|
+
(class_declaration
|
|
31
|
+
declaration_kind: "protocol"
|
|
32
|
+
name: (type_identifier) @name
|
|
33
|
+
) @protocol
|
|
34
|
+
(class_declaration
|
|
35
|
+
declaration_kind: "actor"
|
|
36
|
+
name: (type_identifier) @name
|
|
37
|
+
) @class
|
|
38
|
+
]
|
|
39
|
+
""",
|
|
40
|
+
"imports": """
|
|
41
|
+
(import_declaration) @import
|
|
42
|
+
""",
|
|
43
|
+
"calls": """
|
|
44
|
+
(call_expression) @call_node
|
|
45
|
+
""",
|
|
46
|
+
"variables": """
|
|
47
|
+
[
|
|
48
|
+
(property_declaration
|
|
49
|
+
name: (pattern
|
|
50
|
+
bound_identifier: (simple_identifier) @name
|
|
51
|
+
)
|
|
52
|
+
) @variable
|
|
53
|
+
(property_declaration
|
|
54
|
+
name: (pattern) @pattern
|
|
55
|
+
) @variable
|
|
56
|
+
]
|
|
57
|
+
""",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class SwiftTreeSitterParser:
|
|
61
|
+
def __init__(self, generic_parser_wrapper: Any):
|
|
62
|
+
self.generic_parser_wrapper = generic_parser_wrapper
|
|
63
|
+
self.language_name = "swift"
|
|
64
|
+
self.language = generic_parser_wrapper.language
|
|
65
|
+
self.parser = generic_parser_wrapper.parser
|
|
66
|
+
|
|
67
|
+
def parse(self, path: Path, is_dependency: bool = False, index_source: bool = False) -> Dict[str, Any]:
|
|
68
|
+
try:
|
|
69
|
+
self.index_source = index_source
|
|
70
|
+
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
71
|
+
source_code = f.read()
|
|
72
|
+
|
|
73
|
+
if not source_code.strip():
|
|
74
|
+
warning_logger(f"Empty or whitespace-only file: {path}")
|
|
75
|
+
return {
|
|
76
|
+
"path": str(path),
|
|
77
|
+
"functions": [],
|
|
78
|
+
"classes": [],
|
|
79
|
+
"structs": [],
|
|
80
|
+
"enums": [],
|
|
81
|
+
"protocols": [],
|
|
82
|
+
"variables": [],
|
|
83
|
+
"imports": [],
|
|
84
|
+
"function_calls": [],
|
|
85
|
+
"is_dependency": is_dependency,
|
|
86
|
+
"lang": self.language_name,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
tree = self.parser.parse(bytes(source_code, "utf8"))
|
|
90
|
+
|
|
91
|
+
parsed_functions = []
|
|
92
|
+
parsed_classes = []
|
|
93
|
+
parsed_structs = []
|
|
94
|
+
parsed_enums = []
|
|
95
|
+
parsed_protocols = []
|
|
96
|
+
parsed_variables = []
|
|
97
|
+
parsed_imports = []
|
|
98
|
+
parsed_calls = []
|
|
99
|
+
|
|
100
|
+
# Parse Variables first to populate for inference
|
|
101
|
+
if 'variables' in SWIFT_QUERIES:
|
|
102
|
+
results = execute_query(self.language, SWIFT_QUERIES['variables'], tree.root_node)
|
|
103
|
+
parsed_variables = self._parse_variables(results, source_code, path)
|
|
104
|
+
|
|
105
|
+
for capture_name, query in SWIFT_QUERIES.items():
|
|
106
|
+
if capture_name == 'variables': continue # Already done
|
|
107
|
+
results = execute_query(self.language, query, tree.root_node)
|
|
108
|
+
|
|
109
|
+
if capture_name == "functions":
|
|
110
|
+
parsed_functions.extend(self._parse_functions(results, source_code, path))
|
|
111
|
+
elif capture_name == "classes":
|
|
112
|
+
classes, structs, enums, protocols = self._parse_classes(results, source_code, path)
|
|
113
|
+
parsed_classes.extend(classes)
|
|
114
|
+
parsed_structs.extend(structs)
|
|
115
|
+
parsed_enums.extend(enums)
|
|
116
|
+
parsed_protocols.extend(protocols)
|
|
117
|
+
elif capture_name == "imports":
|
|
118
|
+
parsed_imports.extend(self._parse_imports(results, source_code))
|
|
119
|
+
elif capture_name == "calls":
|
|
120
|
+
parsed_calls.extend(self._parse_calls(results, source_code, path, parsed_variables))
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
"path": str(path),
|
|
124
|
+
"functions": parsed_functions,
|
|
125
|
+
"classes": parsed_classes,
|
|
126
|
+
"structs": parsed_structs,
|
|
127
|
+
"enums": parsed_enums,
|
|
128
|
+
"protocols": parsed_protocols,
|
|
129
|
+
"variables": parsed_variables,
|
|
130
|
+
"imports": parsed_imports,
|
|
131
|
+
"function_calls": parsed_calls,
|
|
132
|
+
"is_dependency": is_dependency,
|
|
133
|
+
"lang": self.language_name,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
except Exception as e:
|
|
137
|
+
error_logger(f"Error parsing Swift file {path}: {e}")
|
|
138
|
+
return {
|
|
139
|
+
"path": str(path),
|
|
140
|
+
"functions": [],
|
|
141
|
+
"classes": [],
|
|
142
|
+
"structs": [],
|
|
143
|
+
"enums": [],
|
|
144
|
+
"protocols": [],
|
|
145
|
+
"variables": [],
|
|
146
|
+
"imports": [],
|
|
147
|
+
"function_calls": [],
|
|
148
|
+
"is_dependency": is_dependency,
|
|
149
|
+
"lang": self.language_name,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
def _get_parent_context(self, node: Any) -> Tuple[Optional[str], Optional[str], Optional[int]]:
|
|
153
|
+
curr = node.parent
|
|
154
|
+
while curr:
|
|
155
|
+
if curr.type == "function_declaration":
|
|
156
|
+
name_node = None
|
|
157
|
+
for child in curr.children:
|
|
158
|
+
if child.type == "simple_identifier":
|
|
159
|
+
name_node = child
|
|
160
|
+
break
|
|
161
|
+
return (
|
|
162
|
+
self._get_node_text(name_node) if name_node else None,
|
|
163
|
+
curr.type,
|
|
164
|
+
curr.start_point[0] + 1,
|
|
165
|
+
)
|
|
166
|
+
if curr.type in ("class_declaration", "struct_declaration", "enum_declaration", "protocol_declaration"):
|
|
167
|
+
for child in curr.children:
|
|
168
|
+
if child.type == "type_identifier":
|
|
169
|
+
return (
|
|
170
|
+
self._get_node_text(child),
|
|
171
|
+
curr.type,
|
|
172
|
+
curr.start_point[0] + 1,
|
|
173
|
+
)
|
|
174
|
+
if curr.type == "init_declaration":
|
|
175
|
+
# For initializers, return the parent class/struct name
|
|
176
|
+
parent = curr.parent
|
|
177
|
+
if parent and parent.type in ("class_body", "struct_body"):
|
|
178
|
+
grandparent = parent.parent
|
|
179
|
+
if grandparent:
|
|
180
|
+
for child in grandparent.children:
|
|
181
|
+
if child.type == "type_identifier":
|
|
182
|
+
return (
|
|
183
|
+
self._get_node_text(child),
|
|
184
|
+
grandparent.type,
|
|
185
|
+
grandparent.start_point[0] + 1,
|
|
186
|
+
)
|
|
187
|
+
return ("init", curr.type, curr.start_point[0] + 1)
|
|
188
|
+
curr = curr.parent
|
|
189
|
+
return None, None, None
|
|
190
|
+
|
|
191
|
+
def _get_node_text(self, node: Any) -> str:
|
|
192
|
+
if not node: return ""
|
|
193
|
+
return node.text.decode("utf-8")
|
|
194
|
+
|
|
195
|
+
def _parse_functions(self, captures: list, source_code: str, path: Path) -> list[Dict[str, Any]]:
|
|
196
|
+
functions = []
|
|
197
|
+
seen_nodes = set()
|
|
198
|
+
|
|
199
|
+
for node, capture_name in captures:
|
|
200
|
+
if capture_name in ("function_node", "init_node"):
|
|
201
|
+
node_id = (node.start_byte, node.end_byte, node.type)
|
|
202
|
+
if node_id in seen_nodes:
|
|
203
|
+
continue
|
|
204
|
+
seen_nodes.add(node_id)
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
start_line = node.start_point[0] + 1
|
|
208
|
+
end_line = node.end_point[0] + 1
|
|
209
|
+
|
|
210
|
+
# Get function name
|
|
211
|
+
func_name = "init" if capture_name == "init_node" else None
|
|
212
|
+
if capture_name == "function_node":
|
|
213
|
+
for child in node.children:
|
|
214
|
+
if child.type == "simple_identifier":
|
|
215
|
+
func_name = self._get_node_text(child)
|
|
216
|
+
break
|
|
217
|
+
|
|
218
|
+
if not func_name:
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
# Extract parameters
|
|
222
|
+
parameters = []
|
|
223
|
+
for child in node.children:
|
|
224
|
+
if child.type == "parameter":
|
|
225
|
+
param_name = self._extract_parameter_name(child)
|
|
226
|
+
if param_name:
|
|
227
|
+
parameters.append(param_name)
|
|
228
|
+
|
|
229
|
+
source_text = self._get_node_text(node)
|
|
230
|
+
context_name, context_type, context_line = self._get_parent_context(node)
|
|
231
|
+
|
|
232
|
+
func_data = {
|
|
233
|
+
"name": func_name,
|
|
234
|
+
"args": parameters,
|
|
235
|
+
"line_number": start_line,
|
|
236
|
+
"end_line": end_line,
|
|
237
|
+
"path": str(path),
|
|
238
|
+
"lang": self.language_name,
|
|
239
|
+
"context": context_name,
|
|
240
|
+
"class_context": context_name if context_type and ("class" in context_type or "struct" in context_type) else None
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if self.index_source:
|
|
244
|
+
func_data["source"] = source_text
|
|
245
|
+
|
|
246
|
+
functions.append(func_data)
|
|
247
|
+
|
|
248
|
+
except Exception as e:
|
|
249
|
+
error_logger(f"Error parsing function in {path}: {e}")
|
|
250
|
+
continue
|
|
251
|
+
|
|
252
|
+
return functions
|
|
253
|
+
|
|
254
|
+
def _parse_classes(self, captures: list, source_code: str, path: Path) -> Tuple[list, list, list, list]:
|
|
255
|
+
classes = []
|
|
256
|
+
structs = []
|
|
257
|
+
enums = []
|
|
258
|
+
protocols = []
|
|
259
|
+
seen_nodes = set()
|
|
260
|
+
|
|
261
|
+
for node, capture_name in captures:
|
|
262
|
+
if capture_name in ("class", "struct", "enum", "protocol"):
|
|
263
|
+
node_id = (node.start_byte, node.end_byte, node.type)
|
|
264
|
+
if node_id in seen_nodes:
|
|
265
|
+
continue
|
|
266
|
+
seen_nodes.add(node_id)
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
start_line = node.start_point[0] + 1
|
|
270
|
+
end_line = node.end_point[0] + 1
|
|
271
|
+
|
|
272
|
+
# Find name
|
|
273
|
+
type_name = "Anonymous"
|
|
274
|
+
for child in node.children:
|
|
275
|
+
if child.type == "type_identifier":
|
|
276
|
+
type_name = self._get_node_text(child)
|
|
277
|
+
break
|
|
278
|
+
|
|
279
|
+
source_text = self._get_node_text(node)
|
|
280
|
+
|
|
281
|
+
# Extract inheritance/protocol conformance
|
|
282
|
+
bases = []
|
|
283
|
+
for child in node.children:
|
|
284
|
+
if child.type == "type_inheritance_clause":
|
|
285
|
+
for subchild in child.children:
|
|
286
|
+
if subchild.type == "type_identifier":
|
|
287
|
+
bases.append(self._get_node_text(subchild))
|
|
288
|
+
|
|
289
|
+
type_data = {
|
|
290
|
+
"name": type_name,
|
|
291
|
+
"line_number": start_line,
|
|
292
|
+
"end_line": end_line,
|
|
293
|
+
"bases": bases,
|
|
294
|
+
"path": str(path),
|
|
295
|
+
"lang": self.language_name,
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if self.index_source:
|
|
299
|
+
type_data["source"] = source_text
|
|
300
|
+
|
|
301
|
+
if capture_name == "class":
|
|
302
|
+
classes.append(type_data)
|
|
303
|
+
elif capture_name == "struct":
|
|
304
|
+
structs.append(type_data)
|
|
305
|
+
elif capture_name == "enum":
|
|
306
|
+
enums.append(type_data)
|
|
307
|
+
elif capture_name == "protocol":
|
|
308
|
+
protocols.append(type_data)
|
|
309
|
+
|
|
310
|
+
except Exception as e:
|
|
311
|
+
error_logger(f"Error parsing type in {path}: {e}")
|
|
312
|
+
continue
|
|
313
|
+
|
|
314
|
+
return classes, structs, enums, protocols
|
|
315
|
+
|
|
316
|
+
def _parse_variables(self, captures: list, source_code: str, path: Path) -> list[Dict[str, Any]]:
|
|
317
|
+
variables = []
|
|
318
|
+
seen_vars = set()
|
|
319
|
+
|
|
320
|
+
for node, capture_name in captures:
|
|
321
|
+
if capture_name in ("variable", "constant", "pattern"):
|
|
322
|
+
try:
|
|
323
|
+
start_line = node.start_point[0] + 1
|
|
324
|
+
ctx_name, ctx_type, ctx_line = self._get_parent_context(node)
|
|
325
|
+
|
|
326
|
+
var_name = "unknown"
|
|
327
|
+
var_type = "Unknown"
|
|
328
|
+
|
|
329
|
+
# Try to extract variable name
|
|
330
|
+
if capture_name == "pattern":
|
|
331
|
+
var_name = self._get_node_text(node)
|
|
332
|
+
else:
|
|
333
|
+
for child in node.children:
|
|
334
|
+
if child.type == "simple_identifier":
|
|
335
|
+
var_name = self._get_node_text(child)
|
|
336
|
+
break
|
|
337
|
+
elif child.type == "pattern_binding":
|
|
338
|
+
for subchild in child.children:
|
|
339
|
+
if subchild.type == "simple_identifier":
|
|
340
|
+
var_name = self._get_node_text(subchild)
|
|
341
|
+
break
|
|
342
|
+
|
|
343
|
+
# Try to extract type annotation
|
|
344
|
+
for child in node.children:
|
|
345
|
+
if child.type == "type_annotation":
|
|
346
|
+
for subchild in child.children:
|
|
347
|
+
if subchild.type == "type_identifier":
|
|
348
|
+
var_type = self._get_node_text(subchild)
|
|
349
|
+
break
|
|
350
|
+
|
|
351
|
+
if var_name != "unknown":
|
|
352
|
+
var_key = (var_name, start_line)
|
|
353
|
+
if var_key not in seen_vars:
|
|
354
|
+
seen_vars.add(var_key)
|
|
355
|
+
variables.append({
|
|
356
|
+
"name": var_name,
|
|
357
|
+
"type": var_type,
|
|
358
|
+
"line_number": start_line,
|
|
359
|
+
"path": str(path),
|
|
360
|
+
"lang": self.language_name,
|
|
361
|
+
"context": ctx_name,
|
|
362
|
+
"class_context": ctx_name if ctx_type and ("class" in ctx_type or "struct" in ctx_type) else None
|
|
363
|
+
})
|
|
364
|
+
except Exception as e:
|
|
365
|
+
continue
|
|
366
|
+
|
|
367
|
+
return variables
|
|
368
|
+
|
|
369
|
+
def _parse_imports(self, captures: list, source_code: str) -> list[dict]:
|
|
370
|
+
imports = []
|
|
371
|
+
|
|
372
|
+
for node, capture_name in captures:
|
|
373
|
+
if capture_name == "import":
|
|
374
|
+
try:
|
|
375
|
+
text = self._get_node_text(node)
|
|
376
|
+
# import Foundation
|
|
377
|
+
# import UIKit
|
|
378
|
+
parts = text.replace('import ', '').strip().split()
|
|
379
|
+
module_name = parts[0] if parts else ""
|
|
380
|
+
|
|
381
|
+
if module_name:
|
|
382
|
+
imports.append({
|
|
383
|
+
"name": module_name,
|
|
384
|
+
"full_import_name": module_name,
|
|
385
|
+
"line_number": node.start_point[0] + 1,
|
|
386
|
+
"alias": None,
|
|
387
|
+
"context": (None, None),
|
|
388
|
+
"lang": self.language_name,
|
|
389
|
+
"is_dependency": False,
|
|
390
|
+
})
|
|
391
|
+
except Exception as e:
|
|
392
|
+
continue
|
|
393
|
+
|
|
394
|
+
return imports
|
|
395
|
+
|
|
396
|
+
def _parse_calls(self, captures: list, source_code: str, path: Path, variables: list[Dict[str, Any]] = []) -> list[Dict[str, Any]]:
|
|
397
|
+
calls = []
|
|
398
|
+
seen_calls = set()
|
|
399
|
+
|
|
400
|
+
# Index variables for fast lookup
|
|
401
|
+
var_map = {}
|
|
402
|
+
for v in variables:
|
|
403
|
+
key = (v['name'], v['context'])
|
|
404
|
+
var_map[key] = v['type']
|
|
405
|
+
|
|
406
|
+
for node, capture_name in captures:
|
|
407
|
+
if capture_name == "call_node":
|
|
408
|
+
try:
|
|
409
|
+
start_line = node.start_point[0] + 1
|
|
410
|
+
|
|
411
|
+
call_name = "unknown"
|
|
412
|
+
base_obj = None
|
|
413
|
+
|
|
414
|
+
# Extract function name from call expression
|
|
415
|
+
# call_expression can have various structures
|
|
416
|
+
first_child = node.children[0] if node.children else None
|
|
417
|
+
|
|
418
|
+
if first_child:
|
|
419
|
+
if first_child.type == "simple_identifier":
|
|
420
|
+
call_name = self._get_node_text(first_child)
|
|
421
|
+
elif first_child.type == "navigation_expression":
|
|
422
|
+
# obj.method() pattern
|
|
423
|
+
for child in first_child.children:
|
|
424
|
+
if child.type == "simple_identifier":
|
|
425
|
+
if not base_obj:
|
|
426
|
+
base_obj = self._get_node_text(child)
|
|
427
|
+
else:
|
|
428
|
+
call_name = self._get_node_text(child)
|
|
429
|
+
|
|
430
|
+
if call_name == "unknown":
|
|
431
|
+
continue
|
|
432
|
+
|
|
433
|
+
full_name = f"{base_obj}.{call_name}" if base_obj else call_name
|
|
434
|
+
ctx_name, ctx_type, ctx_line = self._get_parent_context(node)
|
|
435
|
+
|
|
436
|
+
# Type inference
|
|
437
|
+
inferred_type = None
|
|
438
|
+
if base_obj:
|
|
439
|
+
inferred_type = var_map.get((base_obj, ctx_name))
|
|
440
|
+
if not inferred_type:
|
|
441
|
+
inferred_type = var_map.get((base_obj, None))
|
|
442
|
+
if not inferred_type:
|
|
443
|
+
for (vname, vctx), vtype in var_map.items():
|
|
444
|
+
if vname == base_obj:
|
|
445
|
+
inferred_type = vtype
|
|
446
|
+
break
|
|
447
|
+
|
|
448
|
+
calls.append({
|
|
449
|
+
"name": call_name,
|
|
450
|
+
"full_name": full_name,
|
|
451
|
+
"line_number": start_line,
|
|
452
|
+
"args": [],
|
|
453
|
+
"inferred_obj_type": inferred_type,
|
|
454
|
+
"context": [None, ctx_type, ctx_line],
|
|
455
|
+
"class_context": [None, None],
|
|
456
|
+
"lang": self.language_name,
|
|
457
|
+
"is_dependency": False
|
|
458
|
+
})
|
|
459
|
+
except Exception as e:
|
|
460
|
+
continue
|
|
461
|
+
return calls
|
|
462
|
+
|
|
463
|
+
def _extract_parameter_name(self, param_node: Any) -> Optional[str]:
|
|
464
|
+
"""Extract parameter name from a parameter node."""
|
|
465
|
+
# Swift parameters can have external and internal names
|
|
466
|
+
# parameter: external_name? internal_name: type
|
|
467
|
+
for child in param_node.children:
|
|
468
|
+
if child.type == "simple_identifier":
|
|
469
|
+
return self._get_node_text(child)
|
|
470
|
+
return None
|
|
471
|
+
|
|
472
|
+
def pre_scan_swift(files: list[Path], parser_wrapper) -> dict:
|
|
473
|
+
"""Pre-scan Swift files to build a map of class/struct/enum/protocol names to file paths."""
|
|
474
|
+
name_to_files = {}
|
|
475
|
+
for path in files:
|
|
476
|
+
try:
|
|
477
|
+
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
478
|
+
content = f.read()
|
|
479
|
+
|
|
480
|
+
# Extract classes, structs, enums, protocols
|
|
481
|
+
matches = re.finditer(r'\b(class|struct|enum|protocol)\s+(\w+)', content)
|
|
482
|
+
|
|
483
|
+
for match in matches:
|
|
484
|
+
name = match.group(2)
|
|
485
|
+
if name not in name_to_files:
|
|
486
|
+
name_to_files[name] = []
|
|
487
|
+
name_to_files[name].append(str(path))
|
|
488
|
+
|
|
489
|
+
except Exception:
|
|
490
|
+
pass
|
|
491
|
+
return name_to_files
|