chartflow 0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. chartflow/__init__.py +28 -0
  2. chartflow/_verify_importexport.py +169 -0
  3. chartflow/edit/__init__.py +119 -0
  4. chartflow/edit/code_editor.py +1007 -0
  5. chartflow/edit/code_folder.py +523 -0
  6. chartflow/edit/completer.py +2652 -0
  7. chartflow/edit/context_analyzer.py +521 -0
  8. chartflow/edit/demo.py +469 -0
  9. chartflow/edit/line_number_area.py +548 -0
  10. chartflow/edit/minimap.py +581 -0
  11. chartflow/edit/python_editor.py +509 -0
  12. chartflow/edit/syntax_highlighter.py +291 -0
  13. chartflow/edit/test_editor.py +190 -0
  14. chartflow/flow/__init__.py +31 -0
  15. chartflow/flow/_demo.py +98 -0
  16. chartflow/flow/chart.py +1101 -0
  17. chartflow/flow/editor.py +375 -0
  18. chartflow/flow/flow.py +6 -0
  19. chartflow/flow/qchart.py +1250 -0
  20. chartflow/flow/test/__init__.py +0 -0
  21. chartflow/flow/test/test_chart.py +537 -0
  22. chartflow/flow/test_decorator_sync.py +102 -0
  23. chartflow/fsm/__init__.py +84 -0
  24. chartflow/fsm/decorators.py +395 -0
  25. chartflow/fsm/demo.py +381 -0
  26. chartflow/fsm/demo_pyqt6.py +881 -0
  27. chartflow/fsm/demo_tree.py +46 -0
  28. chartflow/fsm/logic.py +447 -0
  29. chartflow/fsm/meta.py +569 -0
  30. chartflow/fsm/scheduler.py +427 -0
  31. chartflow/fsm/stage.py +2079 -0
  32. chartflow/fsm/stdio.py +66 -0
  33. chartflow/fsm/test/__init__.py +7 -0
  34. chartflow/fsm/test/test_decorators.py +210 -0
  35. chartflow/fsm/test/test_events.py +202 -0
  36. chartflow/fsm/test/test_integration.py +596 -0
  37. chartflow/fsm/test/test_logic.py +371 -0
  38. chartflow/fsm/test/test_meta.py +192 -0
  39. chartflow/fsm/test/test_runtime_param.py +336 -0
  40. chartflow/fsm/test/test_scheduler.py +280 -0
  41. chartflow/fsm/test/test_stage.py +314 -0
  42. chartflow/fsm/test_behavior_events.py +263 -0
  43. chartflow/fsm/test_nested_parallel.py +80 -0
  44. chartflow/fsm/test_stage_spec.py +448 -0
  45. chartflow/fsm/utils.py +34 -0
  46. chartflow/node/__init__.py +66 -0
  47. chartflow/node/_node_base.py +727 -0
  48. chartflow/node/_node_interaction.py +488 -0
  49. chartflow/node/_node_interface.py +426 -0
  50. chartflow/node/_node_markers.py +648 -0
  51. chartflow/node/_node_ports.py +536 -0
  52. chartflow/node/_port_interface.py +111 -0
  53. chartflow/node/arrow_item.py +287 -0
  54. chartflow/node/color_utils.py +113 -0
  55. chartflow/node/connection_item.py +939 -0
  56. chartflow/node/demo.py +258 -0
  57. chartflow/node/demo_arrow_drag.py +46 -0
  58. chartflow/node/demo_decorator.py +63 -0
  59. chartflow/node/demo_ports.py +69 -0
  60. chartflow/node/enums.py +35 -0
  61. chartflow/node/example.py +84 -0
  62. chartflow/node/layout_utils.py +307 -0
  63. chartflow/node/main_window.py +196 -0
  64. chartflow/node/menu_bar.py +240 -0
  65. chartflow/node/node_canvas.py +1730 -0
  66. chartflow/node/node_item.py +754 -0
  67. chartflow/node/popmenu.py +472 -0
  68. chartflow/node/port_item.py +591 -0
  69. chartflow/node/qnode_editor.py +73 -0
  70. chartflow/node/selection_rect.py +53 -0
  71. chartflow/node/style_panel.py +615 -0
  72. chartflow/node/styles.py +63 -0
  73. chartflow/node/test_qnode.py +2336 -0
  74. chartflow/showcase.py +528 -0
  75. chartflow/theme.py +508 -0
  76. chartflow-0.1.dist-info/METADATA +23 -0
  77. chartflow-0.1.dist-info/RECORD +79 -0
  78. chartflow-0.1.dist-info/WHEEL +5 -0
  79. chartflow-0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2652 @@
1
+ """
2
+ 代码补全系统 - 增强版
3
+ 提供类似PyCharm的智能代码补全功能
4
+
5
+ 增强特性:
6
+ - 智能self.属性/方法分析
7
+ - 支持from xxx import *导入
8
+ - 优化的响应速度(缓存机制)
9
+ """
10
+
11
+ import os
12
+ import glob
13
+
14
+
15
+ from PyQt6.QtWidgets import (
16
+ QCompleter, QListView, QFrame, QApplication, QWidget
17
+ )
18
+ from PyQt6.QtCore import (
19
+ Qt, QStringListModel, pyqtSignal, QObject,
20
+ QThread, QTimer, QRect
21
+ )
22
+ from PyQt6.QtGui import QColor, QFont, QKeyEvent
23
+ from typing import List, Dict, Set, Optional, Callable, Any, Tuple
24
+ import re
25
+ import ast
26
+ import inspect
27
+ import builtins
28
+ import importlib
29
+ import sys
30
+ import importlib.util
31
+ from functools import lru_cache
32
+ from time import time
33
+
34
+
35
+ class CompletionItem:
36
+ """补全项"""
37
+ def __init__(self, text: str, display: str = None,
38
+ item_type: str = 'variable', icon: str = '',
39
+ detail: str = '', priority: int = 0):
40
+ self.text = text
41
+ self.display = display or text
42
+ self.item_type = item_type # variable, function, class, keyword, builtin, attribute
43
+ self.icon = icon
44
+ self.detail = detail
45
+ self.priority = priority
46
+
47
+ def __repr__(self):
48
+ return f"CompletionItem({self.text}, type={self.item_type})"
49
+
50
+ def __eq__(self, other):
51
+ if isinstance(other, CompletionItem):
52
+ return self.text == other.text and self.item_type == other.item_type
53
+ return False
54
+
55
+ def __hash__(self):
56
+ return hash((self.text, self.item_type))
57
+
58
+
59
+ class ImportResolver:
60
+ """导入解析器 - 处理from xxx import *语句"""
61
+
62
+ def __init__(self):
63
+ self._import_cache: Dict[str, List[str]] = {}
64
+ self._module_cache: Dict[str, Any] = {}
65
+
66
+ def resolve_star_import(self, module_name: str) -> List[str]:
67
+ """
68
+ 解析from module_name import *导入的符号
69
+
70
+ Args:
71
+ module_name: 模块名称
72
+
73
+ Returns:
74
+ 导入的符号列表
75
+ """
76
+ if module_name in self._import_cache:
77
+ return self._import_cache[module_name]
78
+
79
+ try:
80
+ # 尝试导入模块
81
+ if module_name in sys.modules:
82
+ module = sys.modules[module_name]
83
+ else:
84
+ module = importlib.import_module(module_name)
85
+ self._module_cache[module_name] = module
86
+
87
+ # 获取__all__或使用dir()
88
+ if hasattr(module, '__all__'):
89
+ names = list(module.__all__)
90
+ else:
91
+ names = [name for name in dir(module) if not name.startswith('_')]
92
+
93
+ self._import_cache[module_name] = names
94
+ return names
95
+
96
+ except Exception:
97
+ # 导入失败(builtin有入口错误保护),尝试静态分析
98
+ return self._try_custom_import(module_name)
99
+
100
+ def _try_custom_import(self, module_name: str) -> List[str]:
101
+ """尝试导入自定义模块(如builtin)或从子模块解析"""
102
+ # 尝试在当前路径和常见路径中查找
103
+ try:
104
+ # 尝试直接导入
105
+ spec = importlib.util.find_spec(module_name)
106
+ if spec is None:
107
+ # 尝试在当前目录查找
108
+ current_dir = os.getcwd()
109
+ if current_dir not in sys.path:
110
+ sys.path.insert(0, current_dir)
111
+ spec = importlib.util.find_spec(module_name)
112
+
113
+ if spec is not None:
114
+ try:
115
+ module = importlib.util.module_from_spec(spec)
116
+ spec.loader.exec_module(module)
117
+
118
+ if hasattr(module, '__all__'):
119
+ names = list(module.__all__)
120
+ else:
121
+ names = [name for name in dir(module) if not name.startswith('_')]
122
+
123
+ self._import_cache[module_name] = names
124
+ self._module_cache[module_name] = module
125
+ return names
126
+ except Exception:
127
+ # 导入失败(如builtin有入口错误),尝试静态分析
128
+ return self._parse_module_static(spec.origin)
129
+
130
+ except Exception:
131
+ pass
132
+
133
+ return []
134
+
135
+ def _parse_module_static(self, file_path: str) -> List[str]:
136
+ """静态分析模块文件,提取导出的符号(不执行代码)"""
137
+ try:
138
+ with open(file_path, 'r', encoding='utf-8') as f:
139
+ content = f.read()
140
+
141
+ names = []
142
+ tree = ast.parse(content)
143
+
144
+ for node in ast.walk(tree):
145
+ if isinstance(node, ast.ImportFrom):
146
+ # from xxx import * -> 解析子模块
147
+ if any(alias.name == '*' for alias in node.names):
148
+ # 尝试解析子模块
149
+ module = node.module
150
+ if module:
151
+ sub_names = self._get_submodule_exports(module, file_path)
152
+ names.extend(sub_names)
153
+ else:
154
+ # from xxx import a, b, c
155
+ for alias in node.names:
156
+ names.append(alias.asname if alias.asname else alias.name)
157
+
158
+ return names
159
+
160
+ except Exception:
161
+ return []
162
+
163
+ def _get_submodule_exports(self, module: str, parent_file: str) -> List[str]:
164
+ """获取子模块导出的符号"""
165
+ try:
166
+ # 构建子模块路径
167
+ parent_dir = os.path.dirname(parent_file)
168
+ parts = module.split('.')
169
+ sub_path = os.path.join(parent_dir, *parts[1:]) + '.py'
170
+
171
+ if os.path.exists(sub_path):
172
+ return self._parse_submodule_file(sub_path)
173
+
174
+ # 尝试作为包导入
175
+ sub_init = os.path.join(parent_dir, *parts[1:], '__init__.py')
176
+ if os.path.exists(sub_init):
177
+ return self._parse_module_static(sub_init)
178
+
179
+ except Exception:
180
+ pass
181
+
182
+ return []
183
+
184
+ def _parse_submodule_file(self, file_path: str) -> List[str]:
185
+ """解析子模块文件,获取导出的类和函数"""
186
+ try:
187
+ with open(file_path, 'r', encoding='utf-8') as f:
188
+ content = f.read()
189
+
190
+ names = []
191
+ tree = ast.parse(content)
192
+
193
+ for node in ast.walk(tree):
194
+ # class XXX
195
+ if isinstance(node, ast.ClassDef):
196
+ if not node.name.startswith('_'):
197
+ names.append(node.name)
198
+ # def XXX
199
+ elif isinstance(node, ast.FunctionDef):
200
+ if not node.name.startswith('_'):
201
+ names.append(node.name)
202
+ # XXX = ...
203
+ elif isinstance(node, ast.Assign):
204
+ for target in node.targets:
205
+ if isinstance(target, ast.Name) and not target.id.startswith('_'):
206
+ names.append(target.id)
207
+
208
+ return names
209
+
210
+ except Exception:
211
+ return []
212
+
213
+ def get_module_members(self, module_name: str) -> List[Tuple[str, str, Any]]:
214
+ """
215
+ 获取模块成员及其类型
216
+
217
+ Returns:
218
+ [(name, type_str, obj), ...]
219
+ """
220
+ try:
221
+ if module_name in self._module_cache:
222
+ module = self._module_cache[module_name]
223
+ else:
224
+ module = importlib.import_module(module_name)
225
+ self._module_cache[module_name] = module
226
+
227
+ members = []
228
+ for name in dir(module):
229
+ if not name.startswith('_'):
230
+ try:
231
+ obj = getattr(module, name)
232
+ if inspect.isfunction(obj) or inspect.isbuiltin(obj):
233
+ members.append((name, 'function', obj))
234
+ elif inspect.isclass(obj):
235
+ members.append((name, 'class', obj))
236
+ else:
237
+ members.append((name, 'variable', obj))
238
+ except Exception:
239
+ members.append((name, 'unknown', None))
240
+ return members
241
+
242
+ except Exception:
243
+ return []
244
+
245
+ def clear_cache(self):
246
+ """清除缓存"""
247
+ self._import_cache.clear()
248
+ self._module_cache.clear()
249
+
250
+ def get_class_members(self, class_name: str, module_name: str = None) -> List[Tuple[str, str, str]]:
251
+ """
252
+ 获取类的成员(方法和属性)
253
+
254
+ Args:
255
+ class_name: 类名
256
+ module_name: 模块名(可选)
257
+
258
+ Returns:
259
+ [(name, type, doc), ...] type: 'method' 或 'attribute'
260
+ """
261
+ try:
262
+ # 如果提供了模块名,优先从该模块查找
263
+ if module_name and module_name in self._module_cache:
264
+ module = self._module_cache[module_name]
265
+ if hasattr(module, class_name):
266
+ cls = getattr(module, class_name)
267
+ if inspect.isclass(cls):
268
+ members = []
269
+ for name, obj in inspect.getmembers(cls):
270
+ if not name.startswith('_'):
271
+ if inspect.isfunction(obj) or inspect.ismethod(obj):
272
+ members.append((name, 'method', inspect.getdoc(obj) or ''))
273
+ else:
274
+ members.append((name, 'attribute', str(type(obj).__name__)))
275
+ return members
276
+ except Exception:
277
+ pass
278
+
279
+ # 尝试静态分析
280
+ try:
281
+ return self._get_class_members_static(class_name, module_name)
282
+ except Exception:
283
+ pass
284
+
285
+ return []
286
+
287
+ def _get_class_members_static(self, class_name: str, module_name: str = None) -> List[Tuple[str, str, str]]:
288
+ """静态分析获取类成员(包括基类成员)"""
289
+ try:
290
+
291
+ # 构建模块文件路径
292
+ if not module_name:
293
+ return []
294
+
295
+ base_dir = os.getcwd()
296
+
297
+ # 获取模块路径
298
+ if '.' in module_name:
299
+ parts = module_name.split('.')
300
+ module_dir = os.path.join(base_dir, *parts)
301
+ else:
302
+ module_dir = os.path.join(base_dir, module_name)
303
+
304
+ # 收集要搜索的文件列表
305
+ files_to_search = []
306
+
307
+ # 1. 直接文件: module_name.py
308
+ direct_file = module_dir + '.py'
309
+ if os.path.exists(direct_file):
310
+ files_to_search.append(direct_file)
311
+
312
+ # 2. 包目录: module_name/__init__.py
313
+ if os.path.isdir(module_dir):
314
+ init_file = os.path.join(module_dir, '__init__.py')
315
+ if os.path.exists(init_file):
316
+ files_to_search.append(init_file)
317
+
318
+ # 3. 子模块: module_name/*.py
319
+ sub_files = glob.glob(os.path.join(module_dir, '*.py'))
320
+ for sub_file in sub_files:
321
+ if sub_file not in files_to_search:
322
+ files_to_search.append(sub_file)
323
+
324
+ # 4. 子包: module_name/*/__init__.py
325
+ sub_dirs = [d for d in os.listdir(module_dir)
326
+ if os.path.isdir(os.path.join(module_dir, d)) and not d.startswith('_')]
327
+ for sub_dir in sub_dirs:
328
+ sub_init = os.path.join(module_dir, sub_dir, '__init__.py')
329
+ if os.path.exists(sub_init) and sub_init not in files_to_search:
330
+ files_to_search.append(sub_init)
331
+
332
+ print(f"[DEBUG TYPE] Searching for class '{class_name}' in {len(files_to_search)} files")
333
+
334
+ # 解析所有文件,构建类名到类定义的映射
335
+ class_map = {} # class_name -> (file_path, class_node)
336
+ for file_path in files_to_search:
337
+ try:
338
+ with open(file_path, 'r', encoding='utf-8') as f:
339
+ content = f.read()
340
+ tree = ast.parse(content)
341
+ for node in ast.walk(tree):
342
+ if isinstance(node, ast.ClassDef):
343
+ class_map[node.name] = (file_path, node)
344
+ except Exception as e:
345
+ continue
346
+
347
+ # 递归获取类及其基类的成员
348
+ visited_classes = set()
349
+ all_members = []
350
+
351
+ def collect_members(class_name_to_find):
352
+ """递归收集类及其基类的成员"""
353
+ if class_name_to_find in visited_classes:
354
+ return
355
+ visited_classes.add(class_name_to_find)
356
+
357
+ if class_name_to_find not in class_map:
358
+ return
359
+
360
+ file_path, class_node = class_map[class_name_to_find]
361
+ print(f"[DEBUG TYPE] Analyzing class '{class_name_to_find}' in {file_path}")
362
+
363
+ # 收集当前类的成员
364
+ for item in class_node.body:
365
+ if isinstance(item, ast.FunctionDef) and not item.name.startswith('_'):
366
+ doc = ast.get_docstring(item) or ''
367
+ # 检查是否是 @property 装饰的属性
368
+ is_property = any(
369
+ (isinstance(d, ast.Name) and d.id == 'property') or
370
+ (isinstance(d, ast.Attribute) and d.attr == 'property')
371
+ for d in item.decorator_list
372
+ )
373
+ if is_property:
374
+ all_members.append((item.name, 'attribute', doc))
375
+ else:
376
+ all_members.append((item.name, 'method', doc))
377
+ elif isinstance(item, ast.Assign):
378
+ for target in item.targets:
379
+ if isinstance(target, ast.Name) and not target.id.startswith('_'):
380
+ all_members.append((target.id, 'attribute', 'class attribute'))
381
+ elif isinstance(item, ast.AnnAssign):
382
+ if isinstance(item.target, ast.Name) and not item.target.id.startswith('_'):
383
+ all_members.append((item.target.id, 'attribute', 'class attribute'))
384
+
385
+ # 递归收集基类的成员
386
+ for base in class_node.bases:
387
+ if isinstance(base, ast.Name):
388
+ base_name = base.id
389
+ elif isinstance(base, ast.Attribute):
390
+ base_name = base.attr
391
+ else:
392
+ continue
393
+ collect_members(base_name)
394
+
395
+ # 开始收集
396
+ collect_members(class_name)
397
+
398
+ # 去重(保留第一个出现的)
399
+ seen = set()
400
+ unique_members = []
401
+ for member in all_members:
402
+ if member[0] not in seen:
403
+ seen.add(member[0])
404
+ unique_members.append(member)
405
+
406
+ print(f"[DEBUG TYPE] Found {len(unique_members)} unique members for class '{class_name}'")
407
+ return unique_members
408
+
409
+ except Exception as e:
410
+ print(f"[DEBUG TYPE] Exception in _get_class_members_static: {e}")
411
+ return []
412
+
413
+ def get_module_variable_type(self, module_name: str, var_name: str) -> Optional[str]:
414
+ """
415
+ 从模块文件中查找模块级变量的类型
416
+
417
+ Args:
418
+ module_name: 模块名(如 'builtin')
419
+ var_name: 变量名(如 'SPAWN')
420
+
421
+ Returns:
422
+ 变量类型名或 None
423
+ """
424
+ try:
425
+
426
+ # 构建模块文件路径
427
+ base_dir = os.getcwd()
428
+
429
+ if '.' in module_name:
430
+ parts = module_name.split('.')
431
+ module_dir = os.path.join(base_dir, *parts)
432
+ else:
433
+ module_dir = os.path.join(base_dir, module_name)
434
+
435
+ # 收集要搜索的文件列表
436
+ files_to_search = []
437
+
438
+ # 1. 直接文件: module_name.py
439
+ direct_file = module_dir + '.py'
440
+ if os.path.exists(direct_file):
441
+ files_to_search.append(direct_file)
442
+
443
+ # 2. 包目录: module_name/__init__.py
444
+ init_file = os.path.join(module_dir, '__init__.py')
445
+ if os.path.exists(init_file):
446
+ files_to_search.append(init_file)
447
+
448
+ # 解析文件查找变量定义
449
+ for file_path in files_to_search:
450
+ try:
451
+ with open(file_path, 'r', encoding='utf-8') as f:
452
+ content = f.read()
453
+ tree = ast.parse(content)
454
+
455
+ for node in ast.walk(tree):
456
+ # 查找模块级赋值: VAR = ...
457
+ if isinstance(node, ast.Assign):
458
+ for target in node.targets:
459
+ if isinstance(target, ast.Name) and target.id == var_name:
460
+ # 推断类型
461
+ var_type = self._infer_type_from_value(node.value)
462
+ if var_type:
463
+ return var_type
464
+ # 如果推断失败,检查是否是类实例化
465
+ if isinstance(node.value, ast.Call):
466
+ if isinstance(node.value.func, ast.Name):
467
+ return node.value.func.id
468
+ elif isinstance(node.value.func, ast.Attribute):
469
+ return node.value.func.attr
470
+ # 处理 or 表达式: A or B
471
+ elif isinstance(node.value, ast.BoolOp) and isinstance(node.value.op, ast.Or):
472
+ # 取最后一个值(通常是默认值)
473
+ if node.value.values:
474
+ last_value = node.value.values[-1]
475
+ if isinstance(last_value, ast.Call):
476
+ if isinstance(last_value.func, ast.Name):
477
+ return last_value.func.id
478
+ elif isinstance(last_value.func, ast.Attribute):
479
+ return last_value.func.attr
480
+ # 查找类型注解: VAR: Type
481
+ elif isinstance(node, ast.AnnAssign):
482
+ if isinstance(node.target, ast.Name) and node.target.id == var_name:
483
+ if isinstance(node.annotation, ast.Name):
484
+ return node.annotation.id
485
+ elif isinstance(node.annotation, ast.Attribute):
486
+ return node.annotation.attr
487
+
488
+ except Exception:
489
+ continue
490
+
491
+ except Exception:
492
+ pass
493
+
494
+ return None
495
+
496
+ def _infer_type_from_value(self, node: ast.AST) -> Optional[str]:
497
+ """从 AST 节点推断值的类型"""
498
+ if isinstance(node, ast.Num):
499
+ return 'int' if isinstance(node.n, int) else 'float'
500
+ elif isinstance(node, ast.Str):
501
+ return 'str'
502
+ elif isinstance(node, ast.Constant):
503
+ if isinstance(node.value, str):
504
+ return 'str'
505
+ elif isinstance(node.value, int):
506
+ return 'int'
507
+ elif isinstance(node.value, float):
508
+ return 'float'
509
+ elif isinstance(node.value, list):
510
+ return 'list'
511
+ elif isinstance(node.value, dict):
512
+ return 'dict'
513
+ elif isinstance(node, ast.List):
514
+ return 'list'
515
+ elif isinstance(node, ast.Dict):
516
+ return 'dict'
517
+ elif isinstance(node, ast.Tuple):
518
+ return 'tuple'
519
+ elif isinstance(node, ast.Set):
520
+ return 'set'
521
+ elif isinstance(node, ast.BoolOp):
522
+ # 处理 or 表达式: A or B,尝试推断右侧的类型(通常是默认值)
523
+ # 如: SPAWN = get.spawn(...) or Point(0, 0)
524
+ if node.values:
525
+ return self._infer_type_from_value(node.values[-1])
526
+ return None
527
+
528
+
529
+ class SelfAnalyzer:
530
+ """self分析器 - 专门分析类中的self属性和方法"""
531
+
532
+ def __init__(self):
533
+ self._class_cache: Dict[str, Dict[str, Any]] = {}
534
+
535
+ def analyze_class(self, code: str, class_name: str = None) -> Dict[str, Any]:
536
+ """
537
+ 分析类定义,提取属性和方法
538
+
539
+ Args:
540
+ code: 代码字符串
541
+ class_name: 类名(可选,用于指定分析哪个类)
542
+
543
+ Returns:
544
+ {
545
+ 'attributes': {name: {'type': str, 'line': int}},
546
+ 'methods': {name: {'args': [...], 'line': int, 'docstring': str}},
547
+ 'class_attributes': {name: {...}},
548
+ 'bases': [...]
549
+ }
550
+ """
551
+ cache_key = str(hash(code)) if not class_name else f"{hash(code)}_{class_name}"
552
+ if cache_key in self._class_cache:
553
+ return self._class_cache[cache_key]
554
+
555
+ result = {
556
+ 'attributes': {},
557
+ 'methods': {},
558
+ 'class_attributes': {},
559
+ 'bases': []
560
+ }
561
+
562
+ try:
563
+ tree = ast.parse(code)
564
+
565
+ for node in ast.walk(tree):
566
+ if isinstance(node, ast.ClassDef):
567
+ if class_name and node.name != class_name:
568
+ continue
569
+
570
+ # 记录基类
571
+ result['bases'] = [self._get_name(base) for base in node.bases]
572
+
573
+ # 分析类体
574
+ for item in node.body:
575
+ # 类属性(直接在类体中的赋值)
576
+ if isinstance(item, ast.Assign):
577
+ for target in item.targets:
578
+ if isinstance(target, ast.Name):
579
+ result['class_attributes'][target.id] = {
580
+ 'line': item.lineno,
581
+ 'value': self._get_value_repr(item.value)
582
+ }
583
+
584
+ # 类型注解的类属性
585
+ elif isinstance(item, ast.AnnAssign):
586
+ if isinstance(item.target, ast.Name):
587
+ result['class_attributes'][item.target.id] = {
588
+ 'line': item.lineno,
589
+ 'annotation': self._get_name(item.annotation) if item.annotation else None
590
+ }
591
+
592
+ # 方法
593
+ elif isinstance(item, ast.FunctionDef):
594
+ method_info = self._analyze_method(item)
595
+ result['methods'][item.name] = method_info
596
+
597
+ # 分析方法中的self赋值(实例属性)
598
+ self._analyze_self_attributes(item, result['attributes'])
599
+
600
+ break # 找到目标类后退出
601
+
602
+ except SyntaxError:
603
+ pass
604
+
605
+ self._class_cache[cache_key] = result
606
+ return result
607
+
608
+ def _analyze_method(self, node: ast.FunctionDef) -> Dict:
609
+ """分析方法定义"""
610
+ return {
611
+ 'name': node.name,
612
+ 'args': [arg.arg for arg in node.args.args],
613
+ 'defaults': len(node.args.defaults),
614
+ 'vararg': node.args.vararg.arg if node.args.vararg else None,
615
+ 'kwarg': node.args.kwarg.arg if node.args.kwarg else None,
616
+ 'line': node.lineno,
617
+ 'docstring': ast.get_docstring(node),
618
+ 'decorators': [self._get_name(d) for d in node.decorator_list]
619
+ }
620
+
621
+ def _analyze_self_attributes(self, method_node: ast.FunctionDef,
622
+ attributes: Dict[str, Dict]):
623
+ """分析方法中的self.xxx赋值"""
624
+ for node in ast.walk(method_node):
625
+ if isinstance(node, ast.Attribute):
626
+ if isinstance(node.value, ast.Name) and node.value.id == 'self':
627
+ if node.attr not in attributes:
628
+ attributes[node.attr] = {
629
+ 'line': getattr(node, 'lineno', 0),
630
+ 'type': 'unknown'
631
+ }
632
+
633
+ # 更精确的分析:找到self.xxx = ...的赋值
634
+ if isinstance(node, ast.Assign):
635
+ for target in node.targets:
636
+ if isinstance(target, ast.Attribute):
637
+ if isinstance(target.value, ast.Name) and target.value.id == 'self':
638
+ attr_name = target.attr
639
+ attr_type = self._infer_type(node.value)
640
+ attributes[attr_name] = {
641
+ 'line': getattr(node, 'lineno', 0),
642
+ 'type': attr_type
643
+ }
644
+
645
+ # 类型注解的实例属性
646
+ elif isinstance(node, ast.AnnAssign):
647
+ if isinstance(node.target, ast.Attribute):
648
+ if isinstance(node.target.value, ast.Name) and node.target.value.id == 'self':
649
+ attr_name = node.target.attr
650
+ annotation = self._get_name(node.annotation) if node.annotation else 'Any'
651
+ attributes[attr_name] = {
652
+ 'line': getattr(node, 'lineno', 0),
653
+ 'type': annotation
654
+ }
655
+
656
+ def _infer_type(self, node: Optional[ast.expr]) -> str:
657
+ """推断表达式类型"""
658
+ if node is None:
659
+ return "Any"
660
+
661
+ if isinstance(node, ast.Num):
662
+ return "int" if isinstance(node.n, int) else "float"
663
+ elif isinstance(node, ast.Str):
664
+ return "str"
665
+ elif isinstance(node, ast.List):
666
+ return "list"
667
+ elif isinstance(node, ast.Dict):
668
+ return "dict"
669
+ elif isinstance(node, ast.Set):
670
+ return "set"
671
+ elif isinstance(node, ast.Tuple):
672
+ return "tuple"
673
+ elif isinstance(node, ast.Call):
674
+ if isinstance(node.func, ast.Name):
675
+ return node.func.id
676
+ elif isinstance(node.func, ast.Attribute):
677
+ return node.func.attr
678
+ elif isinstance(node, ast.NameConstant):
679
+ return "bool" if isinstance(node.value, bool) else str(type(node.value).__name__)
680
+ elif isinstance(node, ast.Constant):
681
+ if isinstance(node.value, bool):
682
+ return "bool"
683
+ elif isinstance(node.value, (int, float)):
684
+ return "int" if isinstance(node.value, int) else "float"
685
+ elif isinstance(node.value, str):
686
+ return "str"
687
+ return type(node.value).__name__
688
+
689
+ return "Any"
690
+
691
+ def _get_name(self, node: ast.AST) -> str:
692
+ """获取节点名称"""
693
+ if isinstance(node, ast.Name):
694
+ return node.id
695
+ elif isinstance(node, ast.Attribute):
696
+ return f"{self._get_name(node.value)}.{node.attr}"
697
+ elif isinstance(node, ast.Subscript):
698
+ return self._get_name(node.value)
699
+ return str(node)
700
+
701
+ def _get_value_repr(self, node: ast.expr) -> str:
702
+ """获取值的字符串表示"""
703
+ if isinstance(node, ast.Num):
704
+ return str(node.n)
705
+ elif isinstance(node, ast.Str):
706
+ return repr(node.s)
707
+ elif isinstance(node, ast.NameConstant):
708
+ return str(node.value)
709
+ elif isinstance(node, ast.Constant):
710
+ return repr(node.value)
711
+ elif isinstance(node, ast.Name):
712
+ return node.id
713
+ return "..."
714
+
715
+ def get_self_completions(self, code: str, prefix: str = "") -> List[CompletionItem]:
716
+ """
717
+ 获取self.的补全项
718
+
719
+ Args:
720
+ code: 代码字符串
721
+ prefix: 前缀过滤
722
+
723
+ Returns:
724
+ 补全项列表
725
+ """
726
+ completions = []
727
+ class_info = self.analyze_class(code)
728
+
729
+ # 添加类属性
730
+ for name, info in class_info['class_attributes'].items():
731
+ if name.startswith(prefix) and not name.startswith('_'):
732
+ completions.append(CompletionItem(
733
+ text=name,
734
+ display=f"⚡ {name}",
735
+ item_type='attribute',
736
+ detail=f"Class attribute (line {info.get('line', 0)})",
737
+ priority=95
738
+ ))
739
+
740
+ # 添加实例属性
741
+ for name, info in class_info['attributes'].items():
742
+ if name.startswith(prefix) and not name.startswith('_'):
743
+ type_info = info.get('type', 'unknown')
744
+ completions.append(CompletionItem(
745
+ text=name,
746
+ display=f"🟢 {name}: {type_info}",
747
+ item_type='attribute',
748
+ detail=f"Instance attribute ({type_info})",
749
+ priority=90
750
+ ))
751
+
752
+ # 添加方法
753
+ for name, info in class_info['methods'].items():
754
+ if name.startswith(prefix) and not name.startswith('_'):
755
+ args_str = ', '.join(info['args'][1:]) # 排除self
756
+ completions.append(CompletionItem(
757
+ text=name,
758
+ display=f"🟠 {name}({args_str})",
759
+ item_type='method',
760
+ detail=info.get('docstring', '')[:50] if info.get('docstring') else None,
761
+ priority=85
762
+ ))
763
+
764
+ return completions
765
+
766
+ def clear_cache(self):
767
+ """清除缓存"""
768
+ self._class_cache.clear()
769
+
770
+
771
+ class CodeCompleter(QObject):
772
+ """代码补全管理器 - 增强版"""
773
+
774
+ completion_requested = pyqtSignal(str, int) # 补全请求信号 (text, cursor_pos)
775
+ completion_ready = pyqtSignal(list) # 补全结果就绪信号
776
+
777
+ # 类型图标映射
778
+ TYPE_ICONS = {
779
+ 'keyword': '🔷',
780
+ 'function': '🔶',
781
+ 'method': '🟠',
782
+ 'class': '🟦',
783
+ 'variable': '🟡',
784
+ 'attribute': '🟢',
785
+ 'builtin': '🔵',
786
+ 'module': '📦',
787
+ 'property': '⚡',
788
+ 'parameter': '🔹',
789
+ }
790
+
791
+ def __init__(self, parent=None):
792
+ super().__init__(parent)
793
+ self._context_code_a: str = "" # 隐藏代码A (类定义)
794
+ self._context_code_b: str = "" # 隐藏代码B (函数定义)
795
+ self._prefix_code: str = "" # 前缀代码(缩进等)
796
+
797
+ # Python关键字
798
+ self._keywords = [
799
+ 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue',
800
+ 'def', 'del', 'elif', 'else', 'except', 'False', 'finally', 'for',
801
+ 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'None',
802
+ 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'True', 'try',
803
+ 'while', 'with', 'yield'
804
+ ]
805
+
806
+ # Python内置函数
807
+ self._builtins = dir(builtins)
808
+
809
+ # 常用模块
810
+ self._common_modules = [
811
+ 'os', 'sys', 're', 'json', 'collections', 'itertools', 'functools',
812
+ 'datetime', 'time', 'math', 'random', 'typing', 'pathlib', 'inspect'
813
+ ]
814
+
815
+ # 缓存机制
816
+ self._cached_completions: Dict[str, List[CompletionItem]] = {}
817
+ self._cache_timestamp: Dict[str, float] = {}
818
+ self._cache_ttl = 30 # 缓存有效期(秒)
819
+
820
+ # 导入解析器
821
+ self._import_resolver = ImportResolver()
822
+
823
+ # self分析器
824
+ self._self_analyzer = SelfAnalyzer()
825
+
826
+ # 从CA解析的导入符号
827
+ self._star_imports: Dict[str, List[str]] = {}
828
+ self._imported_modules: Dict[str, str] = {} # name -> module
829
+
830
+ def set_context_code(self, code_a: str = "", code_b: str = "", prefix: str = ""):
831
+ """
832
+ 设置上下文代码
833
+
834
+ Args:
835
+ code_a: 隐藏的类定义代码 (CA)
836
+ code_b: 隐藏的函数定义代码 (CB)
837
+ prefix: 前缀代码(用于缩进)
838
+ """
839
+ print(f"[CodeCompleter.set_context_code] Setting context code")
840
+ print(f"[CodeCompleter.set_context_code] code_a length: {len(code_a)}")
841
+ print(f"[CodeCompleter.set_context_code] code_b length: {len(code_b)}")
842
+ print(f"[CodeCompleter.set_context_code] prefix: {repr(prefix)}")
843
+
844
+ self._context_code_a = code_a
845
+ self._context_code_b = code_b
846
+ self._prefix_code = prefix
847
+
848
+ # 清除缓存
849
+ self._cached_completions.clear()
850
+ self._cache_timestamp.clear()
851
+
852
+ # 解析CA中的导入
853
+ self._parse_imports_from_ca()
854
+ print(f"[CodeCompleter.set_context_code] Star imports: {list(self._star_imports.keys())}")
855
+ for module, names in self._star_imports.items():
856
+ print(f"[CodeCompleter.set_context_code] {module}: {len(names)} symbols")
857
+
858
+ # 清除分析器缓存
859
+ self._self_analyzer.clear_cache()
860
+ self._import_resolver.clear_cache()
861
+
862
+ def _parse_imports_from_ca(self):
863
+ """从CA代码中解析导入语句"""
864
+ self._star_imports.clear()
865
+ self._imported_modules.clear()
866
+
867
+ if not self._context_code_a:
868
+ print("[_parse_imports_from_ca] No CA code")
869
+ return
870
+
871
+ print(f"[_parse_imports_from_ca] Parsing CA code ({len(self._context_code_a)} chars)")
872
+ print(f"[_parse_imports_from_ca] CA first 100 chars: {repr(self._context_code_a[:100])}")
873
+
874
+ try:
875
+ # 替换 <__begin__> 占位符,使代码可解析
876
+ clean_code = self._context_code_a.replace('<__begin__>', ' pass')
877
+ tree = ast.parse(clean_code)
878
+ print(f"[_parse_imports_from_ca] AST parsed successfully")
879
+
880
+ import_count = 0
881
+ for node in ast.walk(tree):
882
+ if isinstance(node, ast.ImportFrom):
883
+ import_count += 1
884
+ module = node.module or ""
885
+ print(f"[_parse_imports_from_ca] Found ImportFrom: from {module} import {[a.name for a in node.names]}")
886
+
887
+ # 检查是否是from xxx import *
888
+ for alias in node.names:
889
+ if alias.name == '*':
890
+ print(f"[_parse_imports_from_ca] Resolving star import from: {module}")
891
+ # 解析星号导入
892
+ imported_names = self._import_resolver.resolve_star_import(module)
893
+ print(f"[_parse_imports_from_ca] Resolved {len(imported_names)} names from {module}")
894
+ self._star_imports[module] = imported_names
895
+ for name in imported_names:
896
+ self._imported_modules[name] = module
897
+ else:
898
+ # 普通导入
899
+ name = alias.asname if alias.asname else alias.name
900
+ self._imported_modules[name] = module
901
+
902
+ elif isinstance(node, ast.Import):
903
+ import_count += 1
904
+ print(f"[_parse_imports_from_ca] Found Import: {[a.name for a in node.names]}")
905
+ for alias in node.names:
906
+ name = alias.asname if alias.asname else alias.name
907
+ self._imported_modules[name] = alias.name
908
+
909
+ print(f"[_parse_imports_from_ca] Total imports found: {import_count}")
910
+
911
+ except SyntaxError as e:
912
+ print(f"[_parse_imports_from_ca] SyntaxError: {e}")
913
+ except Exception as e:
914
+ print(f"[_parse_imports_from_ca] Exception: {e}")
915
+
916
+ def get_completions(self, text: str, cursor_pos: int) -> List[CompletionItem]:
917
+ """
918
+ 获取补全建议 - 优化版本
919
+
920
+ Args:
921
+ text: 当前编辑器中的代码
922
+ cursor_pos: 光标位置
923
+
924
+ Returns:
925
+ 补全项列表
926
+ """
927
+ start_time = time()
928
+
929
+ # 构建完整代码用于分析
930
+ full_code = self._build_full_code(text)
931
+
932
+ # 获取当前词
933
+ current_word = self._get_current_word(text, cursor_pos)
934
+
935
+ # 获取光标前的上下文
936
+ context = self._get_context(text, cursor_pos)
937
+
938
+ # 检查缓存
939
+ cache_key = f"{hash(full_code)}_{current_word}_{context.get('object_name', '')}"
940
+ if cache_key in self._cached_completions:
941
+ timestamp = self._cache_timestamp.get(cache_key, 0)
942
+ if time() - timestamp < self._cache_ttl:
943
+ return self._cached_completions[cache_key]
944
+
945
+ completions = []
946
+
947
+ # 1. 如果是属性访问,优先处理
948
+ if context['after_dot'] and context['object_name']:
949
+ completions.extend(self._get_attribute_completions(
950
+ full_code, context['object_name'], current_word,
951
+ is_subscript=context.get('is_subscript', False),
952
+ is_chained_call=context.get('is_chained_call', False)
953
+ ))
954
+ else:
955
+ # 2. 添加关键字
956
+ completions.extend(self._get_keyword_completions(current_word))
957
+
958
+ # 3. 添加内置函数
959
+ completions.extend(self._get_builtin_completions(current_word))
960
+
961
+ # 4. 添加星号导入的符号
962
+ completions.extend(self._get_star_import_completions(current_word))
963
+
964
+ # 5. 从上下文代码分析补全
965
+ completions.extend(self._get_context_completions(full_code, current_word, context))
966
+
967
+ # 去重并排序
968
+ seen = set()
969
+ unique_completions = []
970
+ for item in completions:
971
+ key = (item.text, item.item_type)
972
+ if key not in seen:
973
+ seen.add(key)
974
+ unique_completions.append(item)
975
+
976
+ # 按优先级排序,优先显示完全匹配和开头匹配的项(区分大小写)
977
+ def sort_key(item):
978
+ # 完全匹配(区分大小写)排最前面
979
+ if item.text == current_word:
980
+ match_score = 0
981
+ # 完全匹配(不区分大小写)排第二
982
+ elif item.text.lower() == current_word.lower():
983
+ match_score = 1
984
+ # 开头匹配(区分大小写)排第三
985
+ elif item.text.startswith(current_word):
986
+ match_score = 2
987
+ # 开头匹配(不区分大小写)排第四
988
+ elif item.text.lower().startswith(current_word.lower()):
989
+ match_score = 3
990
+ # 其他
991
+ else:
992
+ match_score = 4
993
+
994
+ # 检查是否全大写(用于 self. 补全时的排序)
995
+ is_all_upper = 1 if item.text.isupper() and len(item.text) > 1 else 0
996
+
997
+ # 排序优先级:
998
+ # 1. match_score(匹配程度)
999
+ # 2. -priority(补全项优先级,高的在前)
1000
+ # 3. is_all_upper(全大写的排在最后)
1001
+ # 4. first_char(首字符字母顺序)
1002
+ # 5. len(text)(字符串长度,短的在前)
1003
+ # 6. text.lower()(字母顺序)
1004
+ first_char = item.text[0].lower() if item.text else ''
1005
+ return (match_score, -item.priority, is_all_upper, first_char, len(item.text), item.text.lower())
1006
+
1007
+ unique_completions.sort(key=sort_key)
1008
+
1009
+ # 缓存结果
1010
+ self._cached_completions[cache_key] = unique_completions
1011
+ self._cache_timestamp[cache_key] = time()
1012
+
1013
+ # 性能监控(调试用)
1014
+ elapsed = time() - start_time
1015
+ if elapsed > 0.1: # 超过100ms的慢查询
1016
+ print(f"[Completer] Slow completion: {elapsed*1000:.1f}ms")
1017
+
1018
+ return unique_completions
1019
+
1020
+ def _build_full_code(self, text: str) -> str:
1021
+ """构建完整代码(包含上下文,替换 <__begin__> 占位符)"""
1022
+ # 添加前缀缩进
1023
+ prefix = self._prefix_code if self._prefix_code else " "
1024
+ cc_lines = []
1025
+ for line in text.splitlines():
1026
+ cc_lines.append(prefix + line)
1027
+ cc_code = '\n'.join(cc_lines)
1028
+
1029
+ # 如果有 CA,直接替换其中的 <__begin__>
1030
+ if self._context_code_a:
1031
+ full_code = self._context_code_a.replace('<__begin__>', cc_code)
1032
+ elif self._context_code_b:
1033
+ # 兼容旧格式:使用 CB
1034
+ full_code = self._context_code_b.replace('<__begin__>', cc_code)
1035
+ else:
1036
+ full_code = cc_code
1037
+
1038
+ return full_code
1039
+
1040
+ def _get_current_word(self, text: str, cursor_pos: int) -> str:
1041
+ """获取光标处的当前词"""
1042
+ if cursor_pos <= 0:
1043
+ return ""
1044
+
1045
+ # 向前查找词的开始
1046
+ start = cursor_pos - 1
1047
+ while start >= 0 and (text[start].isalnum() or text[start] == '_'):
1048
+ start -= 1
1049
+ start += 1
1050
+
1051
+ return text[start:cursor_pos]
1052
+
1053
+ def _get_context(self, text: str, cursor_pos: int) -> Dict[str, Any]:
1054
+ """获取代码上下文信息"""
1055
+ context = {
1056
+ 'after_dot': False,
1057
+ 'object_name': '',
1058
+ 'in_string': False,
1059
+ 'in_comment': False,
1060
+ }
1061
+
1062
+ # 检查是否在字符串或注释中
1063
+ line_start = text.rfind('\n', 0, cursor_pos) + 1
1064
+ line = text[line_start:cursor_pos]
1065
+
1066
+ if '#' in line:
1067
+ comment_pos = line.find('#')
1068
+ if comment_pos >= 0:
1069
+ context['in_comment'] = True
1070
+
1071
+ # 检查点号访问(支持 self.attr 或 self. 后的输入)
1072
+ if cursor_pos > 0:
1073
+ # 向前查找,跳过当前词(如果有)
1074
+ check_pos = cursor_pos - 1
1075
+ while check_pos >= 0 and (text[check_pos].isalnum() or text[check_pos] == '_'):
1076
+ check_pos -= 1
1077
+
1078
+ # 跳过空白
1079
+ while check_pos >= 0 and text[check_pos].isspace():
1080
+ check_pos -= 1
1081
+
1082
+ # 检查是否是点号
1083
+ if check_pos >= 0 and text[check_pos] == '.':
1084
+ context['after_dot'] = True
1085
+ # 获取点号前的完整表达式(支持链式调用如 get.creep())
1086
+ expr_end = check_pos
1087
+ expr_start = expr_end - 1
1088
+
1089
+ # 处理括号平衡(支持方法调用)
1090
+ paren_count = 0
1091
+ bracket_count = 0
1092
+
1093
+ while expr_start >= 0:
1094
+ char = text[expr_start]
1095
+ if char == ')':
1096
+ paren_count += 1
1097
+ elif char == '(':
1098
+ if paren_count > 0:
1099
+ paren_count -= 1
1100
+ else:
1101
+ break # 不匹配的左括号
1102
+ elif char == ']':
1103
+ bracket_count += 1
1104
+ elif char == '[':
1105
+ if bracket_count > 0:
1106
+ bracket_count -= 1
1107
+ else:
1108
+ pass # 可能允许 [ 作为表达式的一部分
1109
+ elif char == '.' and paren_count == 0 and bracket_count == 0:
1110
+ pass # 作为链式调用的一部分
1111
+ elif not (char.isalnum() or char == '_' or char == '.' or (paren_count > 0 or bracket_count > 0)):
1112
+ # 遇到非标识符字符且不在括号内,停止
1113
+ if paren_count == 0 and bracket_count == 0:
1114
+ break
1115
+
1116
+ expr_start -= 1
1117
+
1118
+ expr_start += 1
1119
+ full_expr = text[expr_start:expr_end].strip()
1120
+ context['object_name'] = full_expr
1121
+ context['is_chained_call'] = '.' in full_expr and '(' in full_expr
1122
+
1123
+ print(f"[DEBUG CONTEXT] Expression before dot: '{full_expr}', chained: {context['is_chained_call']}")
1124
+
1125
+ # 检查是否是下标访问(如 refs[0]. 或 get.creeps()[0].)
1126
+ # 优先检查字面量,然后再处理复杂表达式
1127
+ lit_start = check_pos - 1
1128
+ while lit_start >= 0 and text[lit_start].isspace():
1129
+ lit_start -= 1
1130
+
1131
+ if lit_start >= 0:
1132
+ if text[lit_start] == ']':
1133
+ # 找到了 ],需要找到匹配的 [ 并提取前面的完整表达式
1134
+ bracket_count = 1
1135
+ i = lit_start - 1
1136
+ while i >= 0 and bracket_count > 0:
1137
+ if text[i] == ']':
1138
+ bracket_count += 1
1139
+ elif text[i] == '[':
1140
+ bracket_count -= 1
1141
+ i -= 1
1142
+
1143
+ if bracket_count == 0:
1144
+ # 找到了匹配的 [,提取 [ 前面的完整表达式
1145
+ # 使用与提取 object_name 相同的逻辑
1146
+ expr_end = i # i 指向 [ 前面的位置
1147
+ expr_start = expr_end
1148
+
1149
+ # 处理括号平衡
1150
+ paren_count = 0
1151
+ inner_bracket_count = 0
1152
+
1153
+ while expr_start >= 0:
1154
+ char = text[expr_start]
1155
+ if char == ')':
1156
+ paren_count += 1
1157
+ elif char == '(':
1158
+ if paren_count > 0:
1159
+ paren_count -= 1
1160
+ else:
1161
+ break
1162
+ elif char == ']':
1163
+ inner_bracket_count += 1
1164
+ elif char == '[':
1165
+ if inner_bracket_count > 0:
1166
+ inner_bracket_count -= 1
1167
+ elif char == '.' and paren_count == 0 and inner_bracket_count == 0:
1168
+ pass # 作为表达式的一部分
1169
+ elif not (char.isalnum() or char == '_' or char == '.' or paren_count > 0 or inner_bracket_count > 0):
1170
+ if paren_count == 0 and inner_bracket_count == 0:
1171
+ break
1172
+
1173
+ expr_start -= 1
1174
+
1175
+ expr_start += 1
1176
+ base_expr = text[expr_start:expr_end+1].strip()
1177
+
1178
+ if base_expr:
1179
+ context['object_name'] = base_expr
1180
+ context['is_subscript'] = True
1181
+ print(f"[DEBUG CONTEXT] Found subscript access: {base_expr}[...]")
1182
+ else:
1183
+ # 简单变量名
1184
+ i_end = i
1185
+ while i_end >= 0 and text[i_end].isspace():
1186
+ i_end -= 1
1187
+
1188
+ i_start = i_end
1189
+ while i_start >= 0 and (text[i_start].isalnum() or text[i_start] == '_'):
1190
+ i_start -= 1
1191
+ i_start += 1
1192
+
1193
+ var_name = text[i_start:i_end+1].strip()
1194
+ if var_name:
1195
+ context['object_name'] = var_name
1196
+ context['is_subscript'] = True
1197
+ print(f"[DEBUG CONTEXT] Found subscript access: {var_name}[...]")
1198
+
1199
+ # 如果没有找到对象名,检查是否是字面量(如 [], {}, "", 123 等)
1200
+ if not context['object_name']:
1201
+ lit_start = check_pos - 1
1202
+ while lit_start >= 0 and text[lit_start].isspace():
1203
+ lit_start -= 1
1204
+
1205
+ # 检查各种字面量
1206
+ if lit_start >= 0:
1207
+ char = text[lit_start]
1208
+ if char == ']':
1209
+ context['object_name'] = '__list_literal__'
1210
+ elif char == '}':
1211
+ context['object_name'] = '__dict_literal__'
1212
+ elif char == '"' or char == "'":
1213
+ context['object_name'] = '__str_literal__'
1214
+ elif char.isdigit():
1215
+ context['object_name'] = '__num_literal__'
1216
+ # 检查元组 () - 需要向前找到匹配的括号
1217
+ elif char == ')':
1218
+ # 简单处理,假设是元组
1219
+ context['object_name'] = '__tuple_literal__'
1220
+
1221
+ return context
1222
+
1223
+ def _get_keyword_completions(self, prefix: str) -> List[CompletionItem]:
1224
+ """获取关键字补全"""
1225
+ if not prefix:
1226
+ return []
1227
+
1228
+ completions = []
1229
+ prefix_lower = prefix.lower()
1230
+ for kw in self._keywords:
1231
+ if kw.startswith(prefix_lower):
1232
+ completions.append(CompletionItem(
1233
+ text=kw,
1234
+ display=f"{self.TYPE_ICONS['keyword']} {kw}",
1235
+ item_type='keyword',
1236
+ priority=100
1237
+ ))
1238
+ return completions
1239
+
1240
+ def _get_builtin_completions(self, prefix: str) -> List[CompletionItem]:
1241
+ """获取内置函数补全"""
1242
+ if not prefix:
1243
+ return []
1244
+
1245
+ completions = []
1246
+ for name in self._builtins:
1247
+ if name.startswith(prefix) and not name.startswith('_'):
1248
+ try:
1249
+ obj = getattr(builtins, name, None)
1250
+ item_type = 'function' if callable(obj) else 'variable'
1251
+ completions.append(CompletionItem(
1252
+ text=name,
1253
+ display=f"{self.TYPE_ICONS.get(item_type, '🔹')} {name}",
1254
+ item_type=item_type,
1255
+ priority=80
1256
+ ))
1257
+ except Exception:
1258
+ pass
1259
+ return completions
1260
+
1261
+ def _get_star_import_completions(self, prefix: str) -> List[CompletionItem]:
1262
+ """获取星号导入的补全"""
1263
+ if not prefix:
1264
+ return []
1265
+
1266
+ completions = []
1267
+ seen = set() # 去重,避免与内置函数重复
1268
+
1269
+ for module, names in self._star_imports.items():
1270
+ for name in names:
1271
+ if name.startswith(prefix) and name not in seen:
1272
+ seen.add(name)
1273
+ # 跳过 Python 内置函数(避免与 _get_builtin_completions 重复)
1274
+ if hasattr(builtins, name):
1275
+ continue
1276
+ # 尝试获取类型信息
1277
+ try:
1278
+ module_obj = sys.modules.get(module)
1279
+ if module_obj:
1280
+ obj = getattr(module_obj, name, None)
1281
+ if inspect.isfunction(obj) or inspect.isbuiltin(obj):
1282
+ item_type = 'function'
1283
+ display = f"{self.TYPE_ICONS['function']} {name}()"
1284
+ elif inspect.isclass(obj):
1285
+ item_type = 'class'
1286
+ display = f"{self.TYPE_ICONS['class']} {name}"
1287
+ else:
1288
+ item_type = 'variable'
1289
+ display = f"{self.TYPE_ICONS['variable']} {name}"
1290
+ else:
1291
+ item_type = 'variable'
1292
+ display = f"📦 {name}"
1293
+ except Exception:
1294
+ item_type = 'variable'
1295
+ display = f"📦 {name}"
1296
+
1297
+ completions.append(CompletionItem(
1298
+ text=name,
1299
+ display=display,
1300
+ item_type=item_type,
1301
+ detail=f"from {module} import *",
1302
+ priority=85
1303
+ ))
1304
+
1305
+ return completions
1306
+
1307
+ def _get_context_completions(self, code: str, prefix: str,
1308
+ context: Dict[str, Any]) -> List[CompletionItem]:
1309
+ """从代码上下文获取补全(包括CA中的代码)"""
1310
+ if not prefix:
1311
+ return []
1312
+
1313
+ completions = []
1314
+ seen_names = set() # 去重
1315
+
1316
+ # 解析的代码列表:当前编辑器代码 + CA代码
1317
+ codes_to_parse = [code]
1318
+ if self._context_code_a:
1319
+ # 用有效函数体替换 <__begin__> 使CA可解析
1320
+ clean_ca = self._context_code_a.replace('<__begin__>', ' pass')
1321
+ codes_to_parse.append(clean_ca)
1322
+
1323
+ for code_text in codes_to_parse:
1324
+ try:
1325
+ tree = ast.parse(code_text)
1326
+
1327
+ # 收集定义的变量、函数、类
1328
+ for node in ast.walk(tree):
1329
+ if isinstance(node, ast.FunctionDef):
1330
+ if node.name.startswith(prefix) and node.name not in seen_names:
1331
+ seen_names.add(node.name)
1332
+ completions.append(CompletionItem(
1333
+ text=node.name,
1334
+ display=f"{self.TYPE_ICONS['function']} {node.name}()",
1335
+ item_type='function',
1336
+ priority=90
1337
+ ))
1338
+ # 添加参数
1339
+ for arg in node.args.args:
1340
+ if arg.arg.startswith(prefix) and arg.arg not in seen_names:
1341
+ seen_names.add(arg.arg)
1342
+ completions.append(CompletionItem(
1343
+ text=arg.arg,
1344
+ display=f"{self.TYPE_ICONS['parameter']} {arg.arg}",
1345
+ item_type='parameter',
1346
+ priority=85
1347
+ ))
1348
+ # 添加 *args 和 **kwargs
1349
+ if node.args.vararg and node.args.vararg.arg.startswith(prefix):
1350
+ if node.args.vararg.arg not in seen_names:
1351
+ seen_names.add(node.args.vararg.arg)
1352
+ completions.append(CompletionItem(
1353
+ text=node.args.vararg.arg,
1354
+ display=f"{self.TYPE_ICONS['parameter']} *{node.args.vararg.arg}",
1355
+ item_type='parameter',
1356
+ priority=85
1357
+ ))
1358
+ if node.args.kwarg and node.args.kwarg.arg.startswith(prefix):
1359
+ if node.args.kwarg.arg not in seen_names:
1360
+ seen_names.add(node.args.kwarg.arg)
1361
+ completions.append(CompletionItem(
1362
+ text=node.args.kwarg.arg,
1363
+ display=f"{self.TYPE_ICONS['parameter']} **{node.args.kwarg.arg}",
1364
+ item_type='parameter',
1365
+ priority=85
1366
+ ))
1367
+
1368
+ elif isinstance(node, ast.ClassDef):
1369
+ if node.name.startswith(prefix) and node.name not in seen_names:
1370
+ seen_names.add(node.name)
1371
+ completions.append(CompletionItem(
1372
+ text=node.name,
1373
+ display=f"{self.TYPE_ICONS['class']} {node.name}",
1374
+ item_type='class',
1375
+ priority=90
1376
+ ))
1377
+
1378
+ # 类方法
1379
+ for item in node.body:
1380
+ if isinstance(item, ast.FunctionDef):
1381
+ if item.name.startswith(prefix) and item.name not in seen_names:
1382
+ seen_names.add(item.name)
1383
+ completions.append(CompletionItem(
1384
+ text=item.name,
1385
+ display=f"{self.TYPE_ICONS['method']} {item.name}()",
1386
+ item_type='method',
1387
+ priority=85
1388
+ ))
1389
+
1390
+ elif isinstance(node, ast.Assign):
1391
+ for target in node.targets:
1392
+ if isinstance(target, ast.Name):
1393
+ if target.id.startswith(prefix) and target.id not in seen_names:
1394
+ seen_names.add(target.id)
1395
+ completions.append(CompletionItem(
1396
+ text=target.id,
1397
+ display=f"{self.TYPE_ICONS['variable']} {target.id}",
1398
+ item_type='variable',
1399
+ priority=96 # 局部变量优先级最高
1400
+ ))
1401
+
1402
+ except SyntaxError:
1403
+ pass
1404
+
1405
+ return completions
1406
+
1407
+ def _get_subscript_element_completions(self, var_name: str, prefix: str) -> List[CompletionItem]:
1408
+ """
1409
+ 获取下标访问的元素类型补全(如 refs[0]. 或 get.creeps()[0]. 中的元素类型成员)
1410
+
1411
+ Args:
1412
+ var_name: 变量名或表达式(如 'refs' 或 'get.creeps()')
1413
+ prefix: 前缀过滤
1414
+
1415
+ Returns:
1416
+ 元素类型的成员补全
1417
+ """
1418
+ completions = []
1419
+ print(f"[DEBUG SUBSCRIPT] Analyzing subscript for: '{var_name}'")
1420
+
1421
+ # 系统性类型推断:先推断表达式的类型,再提取元素类型
1422
+ container_type = self._infer_expression_type_systematic(var_name)
1423
+
1424
+ if container_type:
1425
+ print(f"[DEBUG SUBSCRIPT] Container type for '{var_name}': {container_type}")
1426
+ # 从容器类型提取元素类型
1427
+ element_type = self._extract_element_type(container_type)
1428
+ if element_type:
1429
+ print(f"[DEBUG SUBSCRIPT] Element type: {element_type}")
1430
+ completions.extend(self._get_builtin_type_completions(element_type, prefix))
1431
+ print(f"[DEBUG SUBSCRIPT] Found {len(completions)} completions for element type '{element_type}'")
1432
+ else:
1433
+ # 无法提取元素类型,返回容器本身的补全(如 list 的方法)
1434
+ completions.extend(self._get_builtin_type_completions(container_type, prefix))
1435
+ else:
1436
+ # 回退到原来的逻辑:检查 vararg
1437
+ completions.extend(self._get_vararg_element_completions(var_name, prefix))
1438
+
1439
+ return completions
1440
+
1441
+ def _infer_expression_type_systematic(self, expr: str) -> Optional[str]:
1442
+ """
1443
+ 系统性类型推断引擎
1444
+
1445
+ 分析表达式并返回其类型,支持:
1446
+ - 简单变量(如 'refs', 'get')
1447
+ - 方法调用(如 'get.creeps()')
1448
+ - 属性访问(如 'get.creep')
1449
+
1450
+ Args:
1451
+ expr: 表达式字符串
1452
+
1453
+ Returns:
1454
+ 类型名称,如 'list', 'List[Creep]', 'CreepLogic' 等
1455
+ """
1456
+ expr = expr.strip()
1457
+ if not expr:
1458
+ return None
1459
+
1460
+ print(f"[TYPE ENGINE] Analyzing: '{expr}'")
1461
+
1462
+ # 解析表达式
1463
+ parsed = self._parse_expression_structure(expr)
1464
+ if not parsed:
1465
+ return None
1466
+
1467
+ # 根据解析结果推断类型
1468
+ return self._resolve_parsed_type(parsed)
1469
+
1470
+ def _parse_expression_structure(self, expr: str) -> Optional[Dict]:
1471
+ """
1472
+ 解析表达式为结构化表示
1473
+
1474
+ 例如:
1475
+ - 'refs' -> {'kind': 'variable', 'name': 'refs'}
1476
+ - 'get.creeps()' -> {'kind': 'call', 'receiver': 'get', 'method': 'creeps'}
1477
+ - 'get.creep' -> {'kind': 'attr', 'receiver': 'get', 'attr': 'creep'}
1478
+ """
1479
+ expr = expr.strip()
1480
+
1481
+ # 方法调用: xxx.yyy()
1482
+ if expr.endswith(')'):
1483
+ paren_depth = 0
1484
+ paren_pos = -1
1485
+ for i in range(len(expr) - 1, -1, -1):
1486
+ if expr[i] == ')':
1487
+ paren_depth += 1
1488
+ elif expr[i] == '(':
1489
+ paren_depth -= 1
1490
+ if paren_depth == 0:
1491
+ paren_pos = i
1492
+ break
1493
+
1494
+ if paren_pos > 0:
1495
+ before = expr[:paren_pos].strip()
1496
+ if '.' in before:
1497
+ dot_pos = before.rfind('.')
1498
+ return {
1499
+ 'kind': 'call',
1500
+ 'receiver': before[:dot_pos].strip(),
1501
+ 'method': before[dot_pos + 1:].strip()
1502
+ }
1503
+ else:
1504
+ return {'kind': 'call', 'receiver': None, 'method': before}
1505
+
1506
+ # 属性访问: xxx.yyy
1507
+ if '.' in expr:
1508
+ dot_pos = expr.rfind('.')
1509
+ return {
1510
+ 'kind': 'attr',
1511
+ 'receiver': expr[:dot_pos].strip(),
1512
+ 'attr': expr[dot_pos + 1:].strip()
1513
+ }
1514
+
1515
+ # 简单变量
1516
+ if expr.isidentifier():
1517
+ return {'kind': 'variable', 'name': expr}
1518
+
1519
+ return None
1520
+
1521
+ def _resolve_parsed_type(self, parsed: Dict) -> Optional[str]:
1522
+ """根据解析结果解析类型"""
1523
+ kind = parsed.get('kind')
1524
+
1525
+ if kind == 'variable':
1526
+ return self._resolve_variable_type_systematic(parsed['name'])
1527
+
1528
+ elif kind == 'call':
1529
+ receiver = parsed.get('receiver')
1530
+ method = parsed['method']
1531
+ if receiver:
1532
+ receiver_type = self._resolve_parsed_type(self._parse_expression_structure(receiver))
1533
+ if receiver_type:
1534
+ return self._resolve_method_return_type_systematic(receiver_type, method)
1535
+ return None
1536
+
1537
+ elif kind == 'attr':
1538
+ receiver = parsed.get('receiver')
1539
+ attr = parsed['attr']
1540
+ if receiver:
1541
+ receiver_type = self._resolve_parsed_type(self._parse_expression_structure(receiver))
1542
+ if receiver_type:
1543
+ return self._resolve_attribute_type_systematic(receiver_type, attr)
1544
+ return None
1545
+
1546
+ return None
1547
+
1548
+ def _resolve_variable_type_systematic(self, var_name: str) -> Optional[str]:
1549
+ """查找变量的类型注解(系统性版本)"""
1550
+ codes_to_check = []
1551
+ if self._context_code_a:
1552
+ codes_to_check.append(self._context_code_a.replace('<__begin__>', ' pass'))
1553
+ if self._context_code_b:
1554
+ codes_to_check.append(self._context_code_b.replace('<__begin__>', ' pass'))
1555
+
1556
+ # 1. 在上下文代码中查找参数注解
1557
+ for code_text in codes_to_check:
1558
+ try:
1559
+ tree = ast.parse(code_text)
1560
+ for node in ast.walk(tree):
1561
+ if isinstance(node, ast.FunctionDef):
1562
+ for arg in node.args.args:
1563
+ if arg.arg == var_name and arg.annotation:
1564
+ return self._extract_type_name_from_ast(arg.annotation)
1565
+ if node.args.vararg and node.args.vararg.arg == var_name and node.args.vararg.annotation:
1566
+ return self._extract_type_name_from_ast(node.args.vararg.annotation)
1567
+ if node.args.kwarg and node.args.kwarg.arg == var_name and node.args.kwarg.annotation:
1568
+ return self._extract_type_name_from_ast(node.args.kwarg.annotation)
1569
+ except SyntaxError:
1570
+ pass
1571
+
1572
+ # 2. 在 star imports 中查找类定义
1573
+ for module_name in self._star_imports.keys():
1574
+ members = self._import_resolver.get_class_members(var_name, module_name)
1575
+ if members:
1576
+ return var_name # 找到类定义
1577
+
1578
+ return None
1579
+
1580
+ def _resolve_method_return_type_systematic(self, class_type: str, method_name: str) -> Optional[str]:
1581
+ """查找类方法的返回类型(系统性版本)"""
1582
+ # 1. 首先在本地代码中查找
1583
+ codes_to_check = []
1584
+ if self._context_code_a:
1585
+ codes_to_check.append(self._context_code_a.replace('<__begin__>', ' pass'))
1586
+
1587
+ for code_text in codes_to_check:
1588
+ try:
1589
+ tree = ast.parse(code_text)
1590
+ for node in ast.walk(tree):
1591
+ if isinstance(node, ast.ClassDef):
1592
+ for item in node.body:
1593
+ if isinstance(item, ast.FunctionDef) and item.name == method_name:
1594
+ if item.returns:
1595
+ return self._extract_type_name_from_ast(item.returns)
1596
+ except SyntaxError:
1597
+ pass
1598
+
1599
+ # 2. 在 star imports 中查找
1600
+ return self._get_return_type_from_static_analysis(class_type, method_name, 'builtin')
1601
+
1602
+ def _resolve_attribute_type_systematic(self, class_type: str, attr_name: str) -> Optional[str]:
1603
+ """查找类属性的类型"""
1604
+ # 简化处理,返回类名(假设属性是类实例)
1605
+ return class_type
1606
+
1607
+ def _extract_element_type(self, container_type: str) -> Optional[str]:
1608
+ """
1609
+ 从容器类型中提取元素类型
1610
+ 例如: List[Creep] -> Creep, list[Creep] -> Creep
1611
+ """
1612
+ if not container_type:
1613
+ return None
1614
+
1615
+ # 匹配泛型类型
1616
+ match = re.match(r'(?:List|list|Set|set|FrozenSet)\[(\w+)\]', container_type)
1617
+ if match:
1618
+ return match.group(1)
1619
+
1620
+ # tuple[CreepLogic, ...] -> CreepLogic
1621
+ match = re.match(r'(?:Tuple|tuple)\[(\w+)', container_type)
1622
+ if match:
1623
+ return match.group(1)
1624
+
1625
+ # Dict[str, Creep] -> Creep (值类型)
1626
+ match = re.match(r'(?:Dict|dict)\[\w+,\s*(\w+)\]', container_type)
1627
+ if match:
1628
+ return match.group(1)
1629
+
1630
+ return None
1631
+
1632
+ def _extract_type_name_from_ast(self, annotation: ast.AST) -> Optional[str]:
1633
+ """从 AST 注解节点提取类型名称"""
1634
+ if isinstance(annotation, ast.Name):
1635
+ return annotation.id
1636
+ elif isinstance(annotation, ast.Constant):
1637
+ return annotation.value
1638
+ elif isinstance(annotation, ast.Str):
1639
+ return annotation.s
1640
+ elif isinstance(annotation, ast.Attribute):
1641
+ return f"{annotation.value.id}.{annotation.attr}"
1642
+ elif isinstance(annotation, ast.Subscript):
1643
+ if isinstance(annotation.value, ast.Name):
1644
+ container = annotation.value.id
1645
+ if isinstance(annotation.slice, ast.Name):
1646
+ return f"{container}[{annotation.slice.id}]"
1647
+ elif isinstance(annotation.slice, ast.Constant):
1648
+ return f"{container}[{annotation.slice.value}]"
1649
+ elif isinstance(annotation.slice, ast.Str):
1650
+ return f"{container}[{annotation.slice.s}]"
1651
+ else:
1652
+ return f"{container}[...]"
1653
+ elif isinstance(annotation, ast.BinOp):
1654
+ # 联合类型如 Creep | None
1655
+ if isinstance(annotation.left, ast.Name):
1656
+ return annotation.left.id
1657
+ elif isinstance(annotation.left, ast.Attribute):
1658
+ return f"{annotation.left.value.id}.{annotation.left.attr}"
1659
+ return None
1660
+
1661
+ def _get_vararg_element_completions(self, var_name: str, prefix: str) -> List[CompletionItem]:
1662
+ """
1663
+ 回退方法:检查 vararg(*args)的元素类型
1664
+ """
1665
+ completions = []
1666
+ codes_to_check = []
1667
+ if self._context_code_a:
1668
+ codes_to_check.append(self._context_code_a.replace('<__begin__>', ' pass'))
1669
+ if self._context_code_b:
1670
+ codes_to_check.append(self._context_code_b.replace('<__begin__>', ' pass'))
1671
+
1672
+ element_type = None
1673
+
1674
+ for code_text in codes_to_check:
1675
+ try:
1676
+ tree = ast.parse(code_text)
1677
+ for node in ast.walk(tree):
1678
+ if isinstance(node, ast.FunctionDef):
1679
+ if node.args.vararg and node.args.vararg.arg == var_name:
1680
+ annotation = node.args.vararg.annotation
1681
+ if annotation:
1682
+ if isinstance(annotation, ast.Constant):
1683
+ element_type = annotation.value
1684
+ elif isinstance(annotation, ast.Str):
1685
+ element_type = annotation.s
1686
+ elif isinstance(annotation, ast.Name):
1687
+ element_type = annotation.id
1688
+ break
1689
+ if element_type:
1690
+ break
1691
+ except SyntaxError:
1692
+ pass
1693
+
1694
+ if element_type:
1695
+ completions.extend(self._get_builtin_type_completions(element_type, prefix))
1696
+
1697
+ return completions
1698
+
1699
+ def _get_chained_call_completions(self, expr: str, prefix: str) -> List[CompletionItem]:
1700
+ """
1701
+ 获取链式调用的补全(如 get.creep().)
1702
+
1703
+ Args:
1704
+ expr: 表达式(如 'get.creep()')
1705
+ prefix: 前缀过滤
1706
+
1707
+ Returns:
1708
+ 方法返回类型的成员补全
1709
+ """
1710
+ print(f"[DEBUG CHAIN] Analyzing chained expression: '{expr}'")
1711
+ completions = []
1712
+
1713
+ # 尝试在上下文中查找方法定义
1714
+ codes_to_check = []
1715
+ if self._context_code_a:
1716
+ codes_to_check.append(self._context_code_a.replace('<__begin__>', ' pass'))
1717
+ if self._context_code_b:
1718
+ codes_to_check.append(self._context_code_b.replace('<__begin__>', ' pass'))
1719
+
1720
+ # 提取方法名(最后一个 . 后面到 ( 之前的部分)
1721
+ # 例如 get.creep() -> creep
1722
+ method_name = None
1723
+ receiver = None
1724
+ if '.' in expr and '(' in expr:
1725
+ # 找到最后一个 . 后面到 ( 之前的部分
1726
+ paren_pos = expr.rfind('(')
1727
+ dot_pos = expr.rfind('.', 0, paren_pos)
1728
+ if dot_pos > 0 and paren_pos > dot_pos:
1729
+ method_name = expr[dot_pos+1:paren_pos].strip()
1730
+ receiver = expr[:dot_pos].strip()
1731
+ print(f"[DEBUG CHAIN] Method: '{method_name}', Receiver: '{receiver}'")
1732
+
1733
+ if not method_name:
1734
+ return completions
1735
+
1736
+ return_type = None
1737
+
1738
+ # 1. 首先在本地代码中查找
1739
+ for code_text in codes_to_check:
1740
+ if return_type:
1741
+ break
1742
+ try:
1743
+ tree = ast.parse(code_text)
1744
+ for node in ast.walk(tree):
1745
+ if isinstance(node, ast.ClassDef):
1746
+ for item in node.body:
1747
+ if isinstance(item, ast.FunctionDef) and item.name == method_name:
1748
+ # 找到了方法,检查返回类型注解
1749
+ if item.returns:
1750
+ if isinstance(item.returns, ast.Name):
1751
+ return_type = item.returns.id
1752
+ elif isinstance(item.returns, ast.Constant):
1753
+ return_type = item.returns.value
1754
+ elif isinstance(item.returns, ast.Str):
1755
+ return_type = item.returns.s
1756
+ elif isinstance(item.returns, ast.Attribute):
1757
+ return_type = f"{item.returns.value.id}.{item.returns.attr}"
1758
+ print(f"[DEBUG CHAIN] Found return type for '{method_name}' in local code: {return_type}")
1759
+ break
1760
+ if return_type:
1761
+ break
1762
+ except SyntaxError:
1763
+ pass
1764
+
1765
+ # 2. 如果没有找到,尝试从 star imports 中查找
1766
+ if not return_type and receiver:
1767
+ print(f"[DEBUG CHAIN] Not found in local code, trying star imports for receiver '{receiver}'")
1768
+ return_type = self._get_method_return_type_from_star_imports(receiver, method_name)
1769
+ if return_type:
1770
+ print(f"[DEBUG CHAIN] Found return type from star imports: {return_type}")
1771
+
1772
+ if return_type:
1773
+ # 对于 List[Creep] 这样的返回类型,提供 list 的补全
1774
+ # 只有当用户输入 get.creeps()[0]. 时才提供 Creep 的补全
1775
+ completions.extend(self._get_builtin_type_completions(return_type, prefix))
1776
+ print(f"[DEBUG CHAIN] Found {len(completions)} completions for return type '{return_type}'")
1777
+
1778
+ return completions
1779
+
1780
+ def _get_method_return_type_from_star_imports(self, class_name: str, method_name: str) -> Optional[str]:
1781
+ """
1782
+ 从 star import 的模块中查找类的方法返回类型
1783
+
1784
+ Args:
1785
+ class_name: 类名(如 'get')
1786
+ method_name: 方法名(如 'creep')
1787
+
1788
+ Returns:
1789
+ 返回类型名称,如果没有找到则返回 None
1790
+ """
1791
+ print(f"[DEBUG STAR METHOD] Looking for {class_name}.{method_name} return type in star imports")
1792
+
1793
+ for module_name in self._star_imports.keys():
1794
+ print(f"[DEBUG STAR METHOD] Checking module: {module_name}")
1795
+ try:
1796
+ # 使用 ImportResolver 获取类成员
1797
+ members = self._import_resolver.get_class_members(class_name, module_name)
1798
+ if members:
1799
+ print(f"[DEBUG STAR METHOD] Found class '{class_name}' with {len(members)} members in '{module_name}'")
1800
+ # 类成员包含方法和属性,我们需要静态分析来获取方法的返回类型
1801
+ # 尝试通过静态分析获取
1802
+ return_type = self._get_return_type_from_static_analysis(class_name, method_name, module_name)
1803
+ if return_type:
1804
+ return return_type
1805
+ except Exception as e:
1806
+ print(f"[DEBUG STAR METHOD] Error checking module {module_name}: {e}")
1807
+
1808
+ return None
1809
+
1810
+ def _get_return_type_from_static_analysis(self, class_name: str, method_name: str, module_name: str) -> Optional[str]:
1811
+ """
1812
+ 静态分析模块文件获取方法的返回类型注解
1813
+
1814
+ Args:
1815
+ class_name: 类名
1816
+ method_name: 方法名
1817
+ module_name: 模块名
1818
+
1819
+ Returns:
1820
+ 返回类型名称,如果没有找到则返回 None
1821
+ """
1822
+ try:
1823
+
1824
+ # 构建模块文件路径
1825
+ base_dir = os.getcwd()
1826
+ if '.' in module_name:
1827
+ parts = module_name.split('.')
1828
+ module_path = os.path.join(base_dir, *parts) + '.py'
1829
+ else:
1830
+ module_path = os.path.join(base_dir, module_name + '.py')
1831
+
1832
+ # 检查是否存在包目录
1833
+ module_dir = os.path.join(base_dir, module_name)
1834
+ init_file = os.path.join(module_dir, '__init__.py')
1835
+
1836
+ files_to_search = []
1837
+ if os.path.exists(module_path):
1838
+ files_to_search.append(module_path)
1839
+ if os.path.exists(init_file):
1840
+ files_to_search.append(init_file)
1841
+
1842
+ # 也在模块目录下查找子模块
1843
+ if os.path.isdir(module_dir):
1844
+ for sub_file in os.listdir(module_dir):
1845
+ if sub_file.endswith('.py') and not sub_file.startswith('_'):
1846
+ files_to_search.append(os.path.join(module_dir, sub_file))
1847
+
1848
+ print(f"[DEBUG STATIC] Searching {class_name}.{method_name} in {len(files_to_search)} files")
1849
+
1850
+ for file_path in files_to_search:
1851
+ try:
1852
+ with open(file_path, 'r', encoding='utf-8') as f:
1853
+ content = f.read()
1854
+
1855
+ tree = ast.parse(content)
1856
+
1857
+ for node in ast.walk(tree):
1858
+ if isinstance(node, ast.ClassDef) and node.name == class_name:
1859
+ for item in node.body:
1860
+ if isinstance(item, ast.FunctionDef) and item.name == method_name:
1861
+ # 找到了方法,获取返回类型
1862
+ if item.returns:
1863
+ if isinstance(item.returns, ast.Name):
1864
+ return item.returns.id
1865
+ elif isinstance(item.returns, ast.Constant):
1866
+ return item.returns.value
1867
+ elif isinstance(item.returns, ast.Str):
1868
+ return item.returns.s
1869
+ elif isinstance(item.returns, ast.Subscript):
1870
+ # 处理 List[Type] 等情况
1871
+ if isinstance(item.returns.value, ast.Name):
1872
+ container_type = item.returns.value.id
1873
+ # 尝试获取元素类型
1874
+ element_type = None
1875
+ if isinstance(item.returns.slice, ast.Name):
1876
+ element_type = item.returns.slice.id
1877
+ elif isinstance(item.returns.slice, ast.Constant):
1878
+ element_type = item.returns.slice.value
1879
+ elif isinstance(item.returns.slice, ast.Str):
1880
+ element_type = item.returns.slice.s
1881
+
1882
+ if element_type:
1883
+ return f"{container_type}[{element_type}]"
1884
+ else:
1885
+ return f"{container_type}[...]"
1886
+ elif isinstance(item.returns, ast.Attribute):
1887
+ return f"{item.returns.value.id}.{item.returns.attr}"
1888
+ elif isinstance(item.returns, ast.BinOp):
1889
+ # 处理联合类型如 Creep | None
1890
+ # 返回左边的类型(通常是实际类型,不是 None)
1891
+ if isinstance(item.returns.left, ast.Name):
1892
+ left_type = item.returns.left.id
1893
+ # 如果左边是 None,返回右边
1894
+ if left_type == 'None' and isinstance(item.returns.right, ast.Name):
1895
+ return item.returns.right.id
1896
+ return left_type
1897
+ elif isinstance(item.returns.left, ast.Attribute):
1898
+ return f"{item.returns.left.value.id}.{item.returns.left.attr}"
1899
+ return None
1900
+ except Exception as e:
1901
+ continue
1902
+
1903
+ except Exception as e:
1904
+ print(f"[DEBUG STATIC] Error: {e}")
1905
+
1906
+ return None
1907
+
1908
+ def _extract_element_type_from_generic(self, type_str: str) -> Optional[str]:
1909
+ """
1910
+ 从泛型类型字符串中提取元素类型
1911
+ 例如:List[Creep] -> Creep, List[Point] -> Point
1912
+
1913
+ Args:
1914
+ type_str: 类型字符串(如 'List[...]')
1915
+
1916
+ Returns:
1917
+ 元素类型名称,如果不是泛型则返回 None
1918
+ """
1919
+ if not type_str:
1920
+ return None
1921
+
1922
+ # 匹配 List[...] 或 list[...]
1923
+ match = re.match(r'(?:List|list)\[(\w+)\]', type_str)
1924
+ if match:
1925
+ return match.group(1)
1926
+
1927
+ # 也处理 Set[...], Dict[...] 等
1928
+ match = re.match(r'(?:Set|set)\[(\w+)\]', type_str)
1929
+ if match:
1930
+ return match.group(1)
1931
+
1932
+ return None
1933
+
1934
+ def _get_attribute_completions(self, code: str, object_name: str,
1935
+ prefix: str, is_subscript: bool = False,
1936
+ is_chained_call: bool = False) -> List[CompletionItem]:
1937
+ """获取对象属性补全 - 增强版"""
1938
+ completions = []
1939
+
1940
+ # 如果是下标访问(如 refs[0].),需要获取元素类型的成员
1941
+ if is_subscript:
1942
+ element_completions = self._get_subscript_element_completions(object_name, prefix)
1943
+ if element_completions:
1944
+ return element_completions
1945
+
1946
+ # 处理链式调用(如 get.creep().)
1947
+ print(f"[DEBUG ATTR] object_name='{object_name}', is_chained_call={is_chained_call}, has_dot={'/' in object_name}, has_paren={'(' in object_name}")
1948
+ if is_chained_call or ('.' in object_name and '(' in object_name):
1949
+ print(f"[DEBUG ATTR] Calling _get_chained_call_completions for '{object_name}'")
1950
+ chained_completions = self._get_chained_call_completions(object_name, prefix)
1951
+ print(f"[DEBUG ATTR] _get_chained_call_completions returned {len(chained_completions)} completions")
1952
+ if chained_completions:
1953
+ return chained_completions
1954
+
1955
+ # 特殊对象处理:self
1956
+ if object_name == 'self':
1957
+ # 使用 CA (code_a) 来分析类结构
1958
+ # 不能用包含当前输入的完整代码,因为可能包含语法错误(如 self.)
1959
+ class_code = self._context_code_a if self._context_code_a else code
1960
+ # 用有效的函数体替换 <__begin__>,获得可解析的类定义
1961
+ clean_code = class_code.replace('<__begin__>', ' pass')
1962
+ # 清除缓存,确保分析最新代码
1963
+ self._self_analyzer.clear_cache()
1964
+ self_completions = self._self_analyzer.get_self_completions(clean_code, prefix)
1965
+ completions.extend(self_completions)
1966
+
1967
+ # 同时获取基类(如 CreepLogic)的成员
1968
+ try:
1969
+ tree = ast.parse(clean_code)
1970
+ for node in ast.walk(tree):
1971
+ if isinstance(node, ast.ClassDef):
1972
+ for base in node.bases:
1973
+ # 获取基类名称
1974
+ if isinstance(base, ast.Name):
1975
+ base_name = base.id
1976
+ elif isinstance(base, ast.Attribute):
1977
+ base_name = base.attr
1978
+ else:
1979
+ continue
1980
+ # 从 star imports 中查找基类
1981
+ for module in self._star_imports.keys():
1982
+ base_members = self._import_resolver.get_class_members(base_name, module)
1983
+ if base_members:
1984
+ print(f"[DEBUG SELF] Found base class '{base_name}' with {len(base_members)} members")
1985
+ for name, member_type, doc in base_members:
1986
+ if name.startswith(prefix) and not name.startswith('_') and not any(c.text == name for c in completions):
1987
+ if member_type == 'method':
1988
+ completions.append(CompletionItem(
1989
+ text=name,
1990
+ display=f"{self.TYPE_ICONS['method']} {name}()",
1991
+ item_type='method',
1992
+ detail=f"from {base_name}: {doc[:50] if doc else 'method'}",
1993
+ priority=90
1994
+ ))
1995
+ else:
1996
+ completions.append(CompletionItem(
1997
+ text=name,
1998
+ display=f"{self.TYPE_ICONS['attribute']} {name}",
1999
+ item_type='attribute',
2000
+ detail=f"from {base_name}: {doc if doc else 'attribute'}",
2001
+ priority=95
2002
+ ))
2003
+ break
2004
+ break
2005
+ except Exception as e:
2006
+ print(f"[DEBUG SELF] Error getting base class members: {e}")
2007
+
2008
+ # 字面量处理
2009
+ elif object_name == '__list_literal__':
2010
+ completions.extend(self._get_builtin_type_completions('list', prefix))
2011
+ elif object_name == '__dict_literal__':
2012
+ completions.extend(self._get_builtin_type_completions('dict', prefix))
2013
+ elif object_name == '__tuple_literal__':
2014
+ completions.extend(self._get_builtin_type_completions('tuple', prefix))
2015
+ elif object_name == '__str_literal__':
2016
+ completions.extend(self._get_builtin_type_completions('str', prefix))
2017
+
2018
+ # 额外的AST分析确保不遗漏
2019
+ try:
2020
+ # 使用 code_a 分析类(干净的类定义代码)
2021
+ analysis_code = self._context_code_a if self._context_code_a else code
2022
+ # 替换 <__begin__> 使代码可解析
2023
+ analysis_code = analysis_code.replace('<__begin__>', ' pass')
2024
+ tree = ast.parse(analysis_code)
2025
+ for node in ast.walk(tree):
2026
+ if isinstance(node, ast.ClassDef):
2027
+ # 收集类属性
2028
+ for item in node.body:
2029
+ if isinstance(item, ast.Assign):
2030
+ for target in item.targets:
2031
+ if isinstance(target, ast.Name):
2032
+ if target.id.startswith(prefix) and not any(c.text == target.id for c in completions):
2033
+ completions.append(CompletionItem(
2034
+ text=target.id,
2035
+ display=f"{self.TYPE_ICONS['attribute']} {target.id}",
2036
+ item_type='attribute',
2037
+ priority=95
2038
+ ))
2039
+ elif isinstance(item, ast.FunctionDef):
2040
+ if item.name.startswith(prefix) and not item.name.startswith('_') and not any(c.text == item.name for c in completions):
2041
+ completions.append(CompletionItem(
2042
+ text=item.name,
2043
+ display=f"{self.TYPE_ICONS['method']} {item.name}()",
2044
+ item_type='method',
2045
+ priority=90
2046
+ ))
2047
+ except SyntaxError:
2048
+ pass
2049
+
2050
+ # 处理导入的模块
2051
+ elif object_name in self._imported_modules:
2052
+ module_name = self._imported_modules[object_name]
2053
+ try:
2054
+ members = self._import_resolver.get_module_members(module_name)
2055
+ for name, type_str, obj in members:
2056
+ if name.startswith(prefix) and not name.startswith('_'):
2057
+ if type_str == 'function':
2058
+ display = f"{self.TYPE_ICONS['function']} {name}()"
2059
+ item_type = 'function'
2060
+ elif type_str == 'class':
2061
+ display = f"{self.TYPE_ICONS['class']} {name}"
2062
+ item_type = 'class'
2063
+ else:
2064
+ display = f"{self.TYPE_ICONS['variable']} {name}"
2065
+ item_type = 'variable'
2066
+
2067
+ completions.append(CompletionItem(
2068
+ text=name,
2069
+ display=display,
2070
+ item_type=item_type,
2071
+ detail=f"from {module_name}",
2072
+ priority=85
2073
+ ))
2074
+ except Exception:
2075
+ pass
2076
+
2077
+ # 尝试从代码中获取对象类型并提供类型推断补全
2078
+ if object_name != 'self':
2079
+ print(f"[DEBUG TYPE] Getting type infer for '{object_name}'")
2080
+ # 尝试使用编辑器代码推断类型,但处理不完整的语法
2081
+ type_completions = self._get_type_infer_completions(code, object_name, prefix)
2082
+ print(f"[DEBUG TYPE] Type infer returned {len(type_completions)} items")
2083
+ completions.extend(type_completions)
2084
+
2085
+ return completions
2086
+
2087
+ def _get_type_infer_completions(self, code: str, object_name: str, prefix: str) -> List[CompletionItem]:
2088
+ """基于类型推断获取补全"""
2089
+ completions = []
2090
+
2091
+ # 尝试解析代码,处理不完整的语法
2092
+ tree = None
2093
+ code_to_parse = code
2094
+
2095
+ # 尝试解析,如果失败则逐行去掉最后一行再试
2096
+ for attempt in range(5): # 最多尝试5次
2097
+ try:
2098
+ tree = ast.parse(code_to_parse)
2099
+ break
2100
+ except SyntaxError:
2101
+ # 去掉最后一行
2102
+ lines = code_to_parse.rstrip().split('\n')
2103
+ if len(lines) <= 1:
2104
+ break
2105
+ code_to_parse = '\n'.join(lines[:-1])
2106
+
2107
+ if tree is None:
2108
+ return completions
2109
+
2110
+ try:
2111
+ # 查找变量赋值或函数参数注解,推断类型
2112
+ var_type = None
2113
+ assign_count = 0
2114
+
2115
+ # 首先检查函数参数的类型注解(包括CA和CB代码中的)
2116
+ codes_to_check = [code_to_parse]
2117
+ if self._context_code_a:
2118
+ # 清理CA代码使其可解析
2119
+ clean_ca = self._context_code_a.replace('<__begin__>', ' pass')
2120
+ codes_to_check.append(clean_ca)
2121
+ if self._context_code_b:
2122
+ # 清理CB代码使其可解析
2123
+ clean_cb = self._context_code_b.replace('<__begin__>', ' pass')
2124
+ codes_to_check.append(clean_cb)
2125
+
2126
+ for code_text in codes_to_check:
2127
+ try:
2128
+ check_tree = ast.parse(code_text)
2129
+ for node in ast.walk(check_tree):
2130
+ if isinstance(node, ast.FunctionDef):
2131
+ # 检查普通参数
2132
+ for arg in node.args.args:
2133
+ if arg.arg == object_name and arg.annotation:
2134
+ # 获取类型注解的名称
2135
+ if isinstance(arg.annotation, ast.Name):
2136
+ var_type = arg.annotation.id
2137
+ print(f"[DEBUG TYPE] Found param annotation for '{object_name}': {var_type}")
2138
+ elif isinstance(arg.annotation, ast.Attribute):
2139
+ var_type = f"{arg.annotation.value.id}.{arg.annotation.attr}"
2140
+ print(f"[DEBUG TYPE] Found param annotation for '{object_name}': {var_type}")
2141
+ break
2142
+
2143
+ # 检查可变参数 *args
2144
+ if not var_type and node.args.vararg:
2145
+ if node.args.vararg.arg == object_name and node.args.vararg.annotation:
2146
+ annotation = node.args.vararg.annotation
2147
+ if isinstance(annotation, ast.Name):
2148
+ var_type = annotation.id
2149
+ print(f"[DEBUG TYPE] Found vararg annotation for '{object_name}': {var_type}")
2150
+ elif isinstance(annotation, ast.Constant):
2151
+ var_type = annotation.value
2152
+ print(f"[DEBUG TYPE] Found vararg annotation for '{object_name}': {var_type}")
2153
+ elif isinstance(annotation, ast.Str): # Python 3.7 compatibility
2154
+ var_type = annotation.s
2155
+ print(f"[DEBUG TYPE] Found vararg annotation for '{object_name}': {var_type}")
2156
+ elif isinstance(annotation, ast.Attribute):
2157
+ var_type = f"{annotation.value.id}.{annotation.attr}"
2158
+ print(f"[DEBUG TYPE] Found vararg annotation for '{object_name}': {var_type}")
2159
+
2160
+ # 检查关键字可变参数 **kwargs
2161
+ if not var_type and node.args.kwarg:
2162
+ if node.args.kwarg.arg == object_name and node.args.kwarg.annotation:
2163
+ annotation = node.args.kwarg.annotation
2164
+ if isinstance(annotation, ast.Name):
2165
+ var_type = annotation.id
2166
+ print(f"[DEBUG TYPE] Found kwarg annotation for '{object_name}': {var_type}")
2167
+ elif isinstance(annotation, ast.Constant):
2168
+ var_type = annotation.value
2169
+ print(f"[DEBUG TYPE] Found kwarg annotation for '{object_name}': {var_type}")
2170
+
2171
+ if var_type:
2172
+ break
2173
+ if var_type:
2174
+ break
2175
+ except SyntaxError:
2176
+ pass
2177
+
2178
+ # 如果没有找到参数注解,再检查赋值语句
2179
+ if not var_type:
2180
+ for node in ast.walk(tree):
2181
+ if isinstance(node, ast.Assign):
2182
+ assign_count += 1
2183
+ for target in node.targets:
2184
+ target_name = getattr(target, 'id', None)
2185
+ print(f"[DEBUG TYPE] Checking assignment #{assign_count}: target={target_name}")
2186
+ if isinstance(target, ast.Name) and target.id == object_name:
2187
+ var_type = self._infer_type(node.value)
2188
+ print(f"[DEBUG TYPE] Found assignment for '{object_name}', type={var_type}")
2189
+ break
2190
+ if var_type:
2191
+ break
2192
+
2193
+ print(f"[DEBUG TYPE] Total assignments found: {assign_count}")
2194
+
2195
+ # 根据类型获取内置方法/属性
2196
+ if var_type:
2197
+ # 检查是否是 vararg (*args) - 这种情况下类型是 tuple[ElementType]
2198
+ is_vararg = False
2199
+ for code_text in codes_to_check:
2200
+ try:
2201
+ check_tree = ast.parse(code_text)
2202
+ for node in ast.walk(check_tree):
2203
+ if isinstance(node, ast.FunctionDef):
2204
+ if node.args.vararg and node.args.vararg.arg == object_name:
2205
+ is_vararg = True
2206
+ print(f"[DEBUG TYPE] '{object_name}' is a vararg, type is tuple[{var_type}]")
2207
+ break
2208
+ if is_vararg:
2209
+ break
2210
+ except SyntaxError:
2211
+ pass
2212
+
2213
+ if is_vararg:
2214
+ # *args 是 tuple[ElementType],只提供元组方法
2215
+ # 要访问元素类型成员,应该用 refs[0].
2216
+ completions.extend(self._get_builtin_type_completions('tuple', prefix))
2217
+ else:
2218
+ # 普通类型
2219
+ completions.extend(self._get_builtin_type_completions(var_type, prefix))
2220
+ else:
2221
+ # 在本地代码中找不到类型,尝试从 star imports 中查找
2222
+ print(f"[DEBUG TYPE] Could not infer type for '{object_name}' from local code, trying star imports...")
2223
+ star_import_completions = self._get_star_import_class_completions(object_name, prefix)
2224
+ if star_import_completions:
2225
+ completions.extend(star_import_completions)
2226
+ print(f"[DEBUG TYPE] Found {len(star_import_completions)} completions from star imports for '{object_name}'")
2227
+ else:
2228
+ print(f"[DEBUG TYPE] Could not infer type for '{object_name}'")
2229
+
2230
+ except Exception as e:
2231
+ print(f"[DEBUG TYPE] Exception: {e}")
2232
+
2233
+ return completions
2234
+
2235
+ def _get_star_import_class_completions(self, class_name: str, prefix: str) -> List[CompletionItem]:
2236
+ """
2237
+ 从 star import 的模块中查找类定义或模块级变量并返回成员补全
2238
+
2239
+ Args:
2240
+ class_name: 类名或变量名(如 'get' 或 'SPAWN')
2241
+ prefix: 前缀过滤
2242
+
2243
+ Returns:
2244
+ 成员补全列表
2245
+ """
2246
+ completions = []
2247
+ print(f"[DEBUG STAR] Looking for class/variable '{class_name}' in star imports")
2248
+
2249
+ # 首先尝试查找类定义
2250
+ for module_name in self._star_imports.keys():
2251
+ print(f"[DEBUG STAR] Checking module: {module_name}")
2252
+ members = self._import_resolver.get_class_members(class_name, module_name)
2253
+ if members:
2254
+ print(f"[DEBUG STAR] Found {len(members)} members for class '{class_name}' in '{module_name}'")
2255
+ for name, member_type, doc in members:
2256
+ if name.startswith(prefix) and not name.startswith('_'):
2257
+ if member_type == 'method':
2258
+ completions.append(CompletionItem(
2259
+ text=name,
2260
+ display=f"{self.TYPE_ICONS['method']} {name}()",
2261
+ item_type='method',
2262
+ detail=doc[:50] if doc else 'method',
2263
+ priority=85
2264
+ ))
2265
+ else:
2266
+ completions.append(CompletionItem(
2267
+ text=name,
2268
+ display=f"{self.TYPE_ICONS['attribute']} {name}",
2269
+ item_type='attribute',
2270
+ detail=doc if doc else 'attribute',
2271
+ priority=95
2272
+ ))
2273
+ return completions # 找到了类,直接返回
2274
+
2275
+ # 如果不是类,尝试作为模块级变量查找其类型
2276
+ print(f"[DEBUG STAR] Class not found, trying to find variable type for '{class_name}'")
2277
+ var_type = self._get_variable_type_from_star_imports(class_name)
2278
+ print(f"[DEBUG STAR] Variable '{class_name}' type: {var_type}")
2279
+ if var_type:
2280
+ print(f"[DEBUG STAR] Variable '{class_name}' has type '{var_type}', getting completions")
2281
+ # 获取该类型的成员补全
2282
+ return self._get_type_completions_from_star_imports(var_type, prefix)
2283
+
2284
+ print(f"[DEBUG STAR] Could not find type for '{class_name}'")
2285
+ return completions
2286
+
2287
+ def _get_variable_type_from_star_imports(self, var_name: str) -> Optional[str]:
2288
+ """
2289
+ 从 star import 的模块中查找模块级变量的类型
2290
+
2291
+ Args:
2292
+ var_name: 变量名(如 'SPAWN')
2293
+
2294
+ Returns:
2295
+ 变量类型名或 None
2296
+ """
2297
+ for module_name in self._star_imports.keys():
2298
+ var_type = self._import_resolver.get_module_variable_type(module_name, var_name)
2299
+ if var_type:
2300
+ return var_type
2301
+ return None
2302
+
2303
+ def _get_type_completions_from_star_imports(self, type_name: str, prefix: str) -> List[CompletionItem]:
2304
+ """
2305
+ 从 star import 的模块中获取指定类型的成员补全
2306
+
2307
+ Args:
2308
+ type_name: 类型名(如 'Point')
2309
+ prefix: 前缀过滤
2310
+
2311
+ Returns:
2312
+ 成员补全列表
2313
+ """
2314
+ completions = []
2315
+
2316
+ # 首先尝试作为类查找成员
2317
+ for module_name in self._star_imports.keys():
2318
+ members = self._import_resolver.get_class_members(type_name, module_name)
2319
+ if members:
2320
+ for name, member_type, doc in members:
2321
+ if name.startswith(prefix) and not name.startswith('_'):
2322
+ if member_type == 'method':
2323
+ completions.append(CompletionItem(
2324
+ text=name,
2325
+ display=f"{self.TYPE_ICONS['method']} {name}()",
2326
+ item_type='method',
2327
+ detail=doc[:50] if doc else 'method',
2328
+ priority=85
2329
+ ))
2330
+ else:
2331
+ completions.append(CompletionItem(
2332
+ text=name,
2333
+ display=f"{self.TYPE_ICONS['attribute']} {name}",
2334
+ item_type='attribute',
2335
+ detail=doc if doc else 'attribute',
2336
+ priority=95
2337
+ ))
2338
+ return completions
2339
+
2340
+ # 如果不是类,尝试作为内置类型处理
2341
+ return self._get_builtin_type_completions(type_name, prefix)
2342
+
2343
+ def _infer_type(self, node: ast.AST) -> Optional[str]:
2344
+ """推断表达式类型"""
2345
+ node_type = type(node).__name__
2346
+ print(f"[DEBUG TYPE] _infer_type: node_type={node_type}")
2347
+
2348
+ if isinstance(node, ast.List):
2349
+ print(f"[DEBUG TYPE] Detected list")
2350
+ return 'list'
2351
+ elif isinstance(node, ast.Dict):
2352
+ return 'dict'
2353
+ elif isinstance(node, ast.Str):
2354
+ return 'str'
2355
+ elif isinstance(node, ast.Num):
2356
+ return 'int' if isinstance(node.n, int) else 'float'
2357
+ elif isinstance(node, ast.Constant):
2358
+ if isinstance(node.value, str):
2359
+ return 'str'
2360
+ elif isinstance(node.value, int):
2361
+ return 'int'
2362
+ elif isinstance(node.value, float):
2363
+ return 'float'
2364
+ elif isinstance(node.value, list):
2365
+ return 'list'
2366
+ elif isinstance(node.value, dict):
2367
+ return 'dict'
2368
+ elif isinstance(node, ast.Call):
2369
+ if isinstance(node.func, ast.Name):
2370
+ func_name = node.func.id
2371
+ type_map = {
2372
+ 'list': 'list',
2373
+ 'dict': 'dict',
2374
+ 'str': 'str',
2375
+ 'int': 'int',
2376
+ 'float': 'float',
2377
+ 'set': 'set',
2378
+ 'tuple': 'tuple',
2379
+ }
2380
+ return type_map.get(func_name)
2381
+ return None
2382
+
2383
+ def _get_builtin_type_completions(self, type_name: str, prefix: str) -> List[CompletionItem]:
2384
+ """获取内置类型的方法和属性"""
2385
+ completions = []
2386
+ prefix_lower = prefix.lower()
2387
+
2388
+ # list 类型的方法
2389
+ list_methods = [
2390
+ ('append', '添加元素到列表末尾'),
2391
+ ('extend', '扩展列表'),
2392
+ ('insert', '插入元素'),
2393
+ ('remove', '移除元素'),
2394
+ ('pop', '弹出元素'),
2395
+ ('clear', '清空列表'),
2396
+ ('index', '查找索引'),
2397
+ ('count', '计数'),
2398
+ ('sort', '排序'),
2399
+ ('reverse', '反转'),
2400
+ ('copy', '复制列表'),
2401
+ ]
2402
+
2403
+ # dict 类型的方法
2404
+ dict_methods = [
2405
+ ('keys', '获取所有键'),
2406
+ ('values', '获取所有值'),
2407
+ ('items', '获取所有键值对'),
2408
+ ('get', '获取值'),
2409
+ ('pop', '弹出键值'),
2410
+ ('popitem', '弹出最后键值'),
2411
+ ('update', '更新字典'),
2412
+ ('clear', '清空字典'),
2413
+ ('setdefault', '设置默认值'),
2414
+ ('copy', '复制字典'),
2415
+ ]
2416
+
2417
+ # str 类型的方法
2418
+ str_methods = [
2419
+ ('upper', '转大写'),
2420
+ ('lower', '转小写'),
2421
+ ('title', '标题化'),
2422
+ ('strip', '去除空白'),
2423
+ ('split', '分割'),
2424
+ ('join', '连接'),
2425
+ ('replace', '替换'),
2426
+ ('find', '查找'),
2427
+ ('startswith', '是否以...开头'),
2428
+ ('endswith', '是否以...结尾'),
2429
+ ('format', '格式化'),
2430
+ ]
2431
+
2432
+ # tuple 类型的方法
2433
+ tuple_methods = [
2434
+ ('count', '计数'),
2435
+ ('index', '查找索引'),
2436
+ ]
2437
+
2438
+ type_methods = {
2439
+ 'list': list_methods,
2440
+ 'dict': dict_methods,
2441
+ 'str': str_methods,
2442
+ 'tuple': tuple_methods,
2443
+ }
2444
+
2445
+ # 处理泛型类型如 List[Creep] -> 映射到 list
2446
+ actual_type = type_name
2447
+ if type_name and '[' in type_name:
2448
+ # 提取容器类型,如 List[Creep] -> List
2449
+ container = type_name.split('[')[0].lower()
2450
+ if container in ('list', 'dict', 'set', 'tuple'):
2451
+ actual_type = container
2452
+ print(f"[DEBUG BUILTIN] Mapped generic type '{type_name}' to '{actual_type}'")
2453
+
2454
+ methods = type_methods.get(actual_type, [])
2455
+ for name, detail in methods:
2456
+ if name.lower().startswith(prefix_lower):
2457
+ completions.append(CompletionItem(
2458
+ text=name,
2459
+ display=f"{self.TYPE_ICONS['method']} {name}()",
2460
+ item_type='method',
2461
+ detail=detail,
2462
+ priority=85
2463
+ ))
2464
+
2465
+ # 如果不是内置类型,尝试从 star imports 中查找类成员
2466
+ if not methods:
2467
+ print(f"[DEBUG TYPE] Looking for class '{type_name}' in star imports: {list(self._star_imports.keys())}")
2468
+ # 查找类型来自哪个模块
2469
+ type_module = None
2470
+ for module, names in self._star_imports.items():
2471
+ print(f"[DEBUG TYPE] Checking module '{module}' with {len(names)} names")
2472
+ if type_name in names:
2473
+ type_module = module
2474
+ print(f"[DEBUG TYPE] Found '{type_name}' in module '{module}'")
2475
+ break
2476
+
2477
+ # 尝试从模块中获取类成员
2478
+ if type_module:
2479
+ members = self._import_resolver.get_class_members(type_name, type_module)
2480
+ print(f"[DEBUG TYPE] get_class_members returned {len(members)} members")
2481
+ else:
2482
+ # 尝试所有 star import 的模块
2483
+ print(f"[DEBUG TYPE] Type '{type_name}' not found in star imports, trying all modules")
2484
+ members = []
2485
+ for module in self._star_imports.keys():
2486
+ members = self._import_resolver.get_class_members(type_name, module)
2487
+ if members:
2488
+ print(f"[DEBUG TYPE] Found {len(members)} members in module '{module}'")
2489
+ break
2490
+
2491
+ # 添加类成员到补全 - 属性(property)优先于方法
2492
+ for name, member_type, doc in members:
2493
+ if name.lower().startswith(prefix_lower):
2494
+ if member_type == 'method':
2495
+ completions.append(CompletionItem(
2496
+ text=name,
2497
+ display=f"{self.TYPE_ICONS['method']} {name}()",
2498
+ item_type='method',
2499
+ detail=doc[:50] if doc else 'method',
2500
+ priority=85 # 方法优先级较低
2501
+ ))
2502
+ else:
2503
+ # 属性(包括@property)优先级更高
2504
+ completions.append(CompletionItem(
2505
+ text=name,
2506
+ display=f"{self.TYPE_ICONS['attribute']} {name}",
2507
+ item_type='attribute',
2508
+ detail=doc if doc else 'attribute',
2509
+ priority=95 # 属性优先级更高,显示更靠前
2510
+ ))
2511
+
2512
+ return completions
2513
+
2514
+
2515
+ class SmartCompleter(QCompleter):
2516
+ """智能补全器 - 增强的QCompleter"""
2517
+
2518
+ activated_item = pyqtSignal(object) # 发送CompletionItem对象
2519
+
2520
+ def __init__(self, parent=None):
2521
+ super().__init__(parent)
2522
+
2523
+ # 创建自定义模型
2524
+ self._model = QStringListModel()
2525
+ self.setModel(self._model)
2526
+
2527
+ # 补全项列表
2528
+ self._completion_items: List[CompletionItem] = []
2529
+
2530
+ # 设置补全模式
2531
+ self.setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
2532
+ self.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
2533
+ self.setFilterMode(Qt.MatchFlag.MatchStartsWith)
2534
+
2535
+ # 设置弹出窗口样式
2536
+ self._setup_popup_style()
2537
+
2538
+ # 连接信号
2539
+ self.activated.connect(self._on_activated)
2540
+
2541
+ # 最大显示项数
2542
+ self.setMaxVisibleItems(15)
2543
+
2544
+ def _setup_popup_style(self):
2545
+ """设置弹出窗口样式"""
2546
+ popup = self.popup()
2547
+ if popup:
2548
+ popup.setStyleSheet("""
2549
+ QListView {
2550
+ background-color: #ffffff;
2551
+ border: 1px solid #cccccc;
2552
+ border-radius: 4px;
2553
+ padding: 2px;
2554
+ font-family: Consolas, monospace;
2555
+ font-size: 12px;
2556
+ }
2557
+ QListView::item {
2558
+ padding: 3px 8px;
2559
+ border-radius: 2px;
2560
+ }
2561
+ QListView::item:selected {
2562
+ background-color: #5a9bd5;
2563
+ color: white;
2564
+ }
2565
+ QListView::item:hover {
2566
+ background-color: #e8f4fc;
2567
+ }
2568
+ """)
2569
+ popup.setFrameShape(QFrame.Shape.StyledPanel)
2570
+ popup.setFrameShadow(QFrame.Shadow.Plain)
2571
+ popup.setMinimumWidth(220)
2572
+ popup.setMaximumWidth(400)
2573
+ popup.setMaximumHeight(350)
2574
+
2575
+ def set_completion_items(self, items: List[CompletionItem]):
2576
+ """设置补全项"""
2577
+ self._completion_items = items
2578
+
2579
+ # 更新模型
2580
+ display_list = [item.display for item in items]
2581
+ self._model.setStringList(display_list)
2582
+
2583
+ def get_current_item(self) -> Optional[CompletionItem]:
2584
+ """获取当前选中的补全项"""
2585
+ popup = self.popup()
2586
+ if popup is None:
2587
+ return None
2588
+ current_row = popup.currentIndex().row()
2589
+ if 0 <= current_row < len(self._completion_items):
2590
+ return self._completion_items[current_row]
2591
+ return None
2592
+
2593
+ def _on_activated(self, text: str):
2594
+ """补全项被激活"""
2595
+ item = self.get_current_item()
2596
+ if item:
2597
+ self.activated_item.emit(item)
2598
+
2599
+ def update_completions(self, items: List[CompletionItem], rect: QRect = None):
2600
+ """更新补全列表
2601
+
2602
+ Args:
2603
+ items: 补全项列表
2604
+ rect: 弹窗位置矩形,默认为空(由调用方后续调用 complete(rect))
2605
+ """
2606
+ self.set_completion_items(items)
2607
+
2608
+ # 如果有补全项,显示弹出窗口
2609
+ if items:
2610
+ self.complete(rect if rect else QRect())
2611
+ # 默认选中第一项
2612
+ popup = self.popup()
2613
+ if popup:
2614
+ popup.setCurrentIndex(self._model.index(0, 0))
2615
+ # 如果提供了位置,强制移动弹窗到该位置
2616
+ if rect:
2617
+ global_pos = self.widget().mapToGlobal(rect.topLeft())
2618
+ popup.move(global_pos)
2619
+ else:
2620
+ popup = self.popup()
2621
+ if popup:
2622
+ popup.hide()
2623
+
2624
+
2625
+ class CompletionWorker(QThread):
2626
+ """后台补全计算工作线程"""
2627
+
2628
+ completions_ready = pyqtSignal(list)
2629
+
2630
+ def __init__(self, completer: CodeCompleter):
2631
+ super().__init__()
2632
+ self._completer = completer
2633
+ self._text = ""
2634
+ self._cursor_pos = 0
2635
+ self._running = False
2636
+
2637
+ def set_params(self, text: str, cursor_pos: int):
2638
+ """设置参数"""
2639
+ self._text = text
2640
+ self._cursor_pos = cursor_pos
2641
+
2642
+ def run(self):
2643
+ """运行补全计算"""
2644
+ self._running = True
2645
+ completions = self._completer.get_completions(self._text, self._cursor_pos)
2646
+ if self._running:
2647
+ self.completions_ready.emit(completions)
2648
+
2649
+ def stop(self):
2650
+ """停止工作线程"""
2651
+ self._running = False
2652
+ self.wait(50) # 减少等待时间