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.
- chartflow/__init__.py +28 -0
- chartflow/_verify_importexport.py +169 -0
- chartflow/edit/__init__.py +119 -0
- chartflow/edit/code_editor.py +1007 -0
- chartflow/edit/code_folder.py +523 -0
- chartflow/edit/completer.py +2652 -0
- chartflow/edit/context_analyzer.py +521 -0
- chartflow/edit/demo.py +469 -0
- chartflow/edit/line_number_area.py +548 -0
- chartflow/edit/minimap.py +581 -0
- chartflow/edit/python_editor.py +509 -0
- chartflow/edit/syntax_highlighter.py +291 -0
- chartflow/edit/test_editor.py +190 -0
- chartflow/flow/__init__.py +31 -0
- chartflow/flow/_demo.py +98 -0
- chartflow/flow/chart.py +1101 -0
- chartflow/flow/editor.py +375 -0
- chartflow/flow/flow.py +6 -0
- chartflow/flow/qchart.py +1250 -0
- chartflow/flow/test/__init__.py +0 -0
- chartflow/flow/test/test_chart.py +537 -0
- chartflow/flow/test_decorator_sync.py +102 -0
- chartflow/fsm/__init__.py +84 -0
- chartflow/fsm/decorators.py +395 -0
- chartflow/fsm/demo.py +381 -0
- chartflow/fsm/demo_pyqt6.py +881 -0
- chartflow/fsm/demo_tree.py +46 -0
- chartflow/fsm/logic.py +447 -0
- chartflow/fsm/meta.py +569 -0
- chartflow/fsm/scheduler.py +427 -0
- chartflow/fsm/stage.py +2079 -0
- chartflow/fsm/stdio.py +66 -0
- chartflow/fsm/test/__init__.py +7 -0
- chartflow/fsm/test/test_decorators.py +210 -0
- chartflow/fsm/test/test_events.py +202 -0
- chartflow/fsm/test/test_integration.py +596 -0
- chartflow/fsm/test/test_logic.py +371 -0
- chartflow/fsm/test/test_meta.py +192 -0
- chartflow/fsm/test/test_runtime_param.py +336 -0
- chartflow/fsm/test/test_scheduler.py +280 -0
- chartflow/fsm/test/test_stage.py +314 -0
- chartflow/fsm/test_behavior_events.py +263 -0
- chartflow/fsm/test_nested_parallel.py +80 -0
- chartflow/fsm/test_stage_spec.py +448 -0
- chartflow/fsm/utils.py +34 -0
- chartflow/node/__init__.py +66 -0
- chartflow/node/_node_base.py +727 -0
- chartflow/node/_node_interaction.py +488 -0
- chartflow/node/_node_interface.py +426 -0
- chartflow/node/_node_markers.py +648 -0
- chartflow/node/_node_ports.py +536 -0
- chartflow/node/_port_interface.py +111 -0
- chartflow/node/arrow_item.py +287 -0
- chartflow/node/color_utils.py +113 -0
- chartflow/node/connection_item.py +939 -0
- chartflow/node/demo.py +258 -0
- chartflow/node/demo_arrow_drag.py +46 -0
- chartflow/node/demo_decorator.py +63 -0
- chartflow/node/demo_ports.py +69 -0
- chartflow/node/enums.py +35 -0
- chartflow/node/example.py +84 -0
- chartflow/node/layout_utils.py +307 -0
- chartflow/node/main_window.py +196 -0
- chartflow/node/menu_bar.py +240 -0
- chartflow/node/node_canvas.py +1730 -0
- chartflow/node/node_item.py +754 -0
- chartflow/node/popmenu.py +472 -0
- chartflow/node/port_item.py +591 -0
- chartflow/node/qnode_editor.py +73 -0
- chartflow/node/selection_rect.py +53 -0
- chartflow/node/style_panel.py +615 -0
- chartflow/node/styles.py +63 -0
- chartflow/node/test_qnode.py +2336 -0
- chartflow/showcase.py +528 -0
- chartflow/theme.py +508 -0
- chartflow-0.1.dist-info/METADATA +23 -0
- chartflow-0.1.dist-info/RECORD +79 -0
- chartflow-0.1.dist-info/WHEEL +5 -0
- 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) # 减少等待时间
|