Mercurial > repos > rliterman > csp2
comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/autocomplete.py @ 68:5028fdace37b
planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author | jpayne |
---|---|
date | Tue, 18 Mar 2025 16:23:26 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
67:0e9998148a16 | 68:5028fdace37b |
---|---|
1 """Complete either attribute names or file names. | |
2 | |
3 Either on demand or after a user-selected delay after a key character, | |
4 pop up a list of candidates. | |
5 """ | |
6 import __main__ | |
7 import os | |
8 import string | |
9 import sys | |
10 | |
11 # Two types of completions; defined here for autocomplete_w import below. | |
12 ATTRS, FILES = 0, 1 | |
13 from idlelib import autocomplete_w | |
14 from idlelib.config import idleConf | |
15 from idlelib.hyperparser import HyperParser | |
16 | |
17 # Tuples passed to open_completions. | |
18 # EvalFunc, Complete, WantWin, Mode | |
19 FORCE = True, False, True, None # Control-Space. | |
20 TAB = False, True, True, None # Tab. | |
21 TRY_A = False, False, False, ATTRS # '.' for attributes. | |
22 TRY_F = False, False, False, FILES # '/' in quotes for file name. | |
23 | |
24 # This string includes all chars that may be in an identifier. | |
25 # TODO Update this here and elsewhere. | |
26 ID_CHARS = string.ascii_letters + string.digits + "_" | |
27 | |
28 SEPS = f"{os.sep}{os.altsep if os.altsep else ''}" | |
29 TRIGGERS = f".{SEPS}" | |
30 | |
31 class AutoComplete: | |
32 | |
33 def __init__(self, editwin=None): | |
34 self.editwin = editwin | |
35 if editwin is not None: # not in subprocess or no-gui test | |
36 self.text = editwin.text | |
37 self.autocompletewindow = None | |
38 # id of delayed call, and the index of the text insert when | |
39 # the delayed call was issued. If _delayed_completion_id is | |
40 # None, there is no delayed call. | |
41 self._delayed_completion_id = None | |
42 self._delayed_completion_index = None | |
43 | |
44 @classmethod | |
45 def reload(cls): | |
46 cls.popupwait = idleConf.GetOption( | |
47 "extensions", "AutoComplete", "popupwait", type="int", default=0) | |
48 | |
49 def _make_autocomplete_window(self): # Makes mocking easier. | |
50 return autocomplete_w.AutoCompleteWindow(self.text) | |
51 | |
52 def _remove_autocomplete_window(self, event=None): | |
53 if self.autocompletewindow: | |
54 self.autocompletewindow.hide_window() | |
55 self.autocompletewindow = None | |
56 | |
57 def force_open_completions_event(self, event): | |
58 "(^space) Open completion list, even if a function call is needed." | |
59 self.open_completions(FORCE) | |
60 return "break" | |
61 | |
62 def autocomplete_event(self, event): | |
63 "(tab) Complete word or open list if multiple options." | |
64 if hasattr(event, "mc_state") and event.mc_state or\ | |
65 not self.text.get("insert linestart", "insert").strip(): | |
66 # A modifier was pressed along with the tab or | |
67 # there is only previous whitespace on this line, so tab. | |
68 return None | |
69 if self.autocompletewindow and self.autocompletewindow.is_active(): | |
70 self.autocompletewindow.complete() | |
71 return "break" | |
72 else: | |
73 opened = self.open_completions(TAB) | |
74 return "break" if opened else None | |
75 | |
76 def try_open_completions_event(self, event=None): | |
77 "(./) Open completion list after pause with no movement." | |
78 lastchar = self.text.get("insert-1c") | |
79 if lastchar in TRIGGERS: | |
80 args = TRY_A if lastchar == "." else TRY_F | |
81 self._delayed_completion_index = self.text.index("insert") | |
82 if self._delayed_completion_id is not None: | |
83 self.text.after_cancel(self._delayed_completion_id) | |
84 self._delayed_completion_id = self.text.after( | |
85 self.popupwait, self._delayed_open_completions, args) | |
86 | |
87 def _delayed_open_completions(self, args): | |
88 "Call open_completions if index unchanged." | |
89 self._delayed_completion_id = None | |
90 if self.text.index("insert") == self._delayed_completion_index: | |
91 self.open_completions(args) | |
92 | |
93 def open_completions(self, args): | |
94 """Find the completions and create the AutoCompleteWindow. | |
95 Return True if successful (no syntax error or so found). | |
96 If complete is True, then if there's nothing to complete and no | |
97 start of completion, won't open completions and return False. | |
98 If mode is given, will open a completion list only in this mode. | |
99 """ | |
100 evalfuncs, complete, wantwin, mode = args | |
101 # Cancel another delayed call, if it exists. | |
102 if self._delayed_completion_id is not None: | |
103 self.text.after_cancel(self._delayed_completion_id) | |
104 self._delayed_completion_id = None | |
105 | |
106 hp = HyperParser(self.editwin, "insert") | |
107 curline = self.text.get("insert linestart", "insert") | |
108 i = j = len(curline) | |
109 if hp.is_in_string() and (not mode or mode==FILES): | |
110 # Find the beginning of the string. | |
111 # fetch_completions will look at the file system to determine | |
112 # whether the string value constitutes an actual file name | |
113 # XXX could consider raw strings here and unescape the string | |
114 # value if it's not raw. | |
115 self._remove_autocomplete_window() | |
116 mode = FILES | |
117 # Find last separator or string start | |
118 while i and curline[i-1] not in "'\"" + SEPS: | |
119 i -= 1 | |
120 comp_start = curline[i:j] | |
121 j = i | |
122 # Find string start | |
123 while i and curline[i-1] not in "'\"": | |
124 i -= 1 | |
125 comp_what = curline[i:j] | |
126 elif hp.is_in_code() and (not mode or mode==ATTRS): | |
127 self._remove_autocomplete_window() | |
128 mode = ATTRS | |
129 while i and (curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127): | |
130 i -= 1 | |
131 comp_start = curline[i:j] | |
132 if i and curline[i-1] == '.': # Need object with attributes. | |
133 hp.set_index("insert-%dc" % (len(curline)-(i-1))) | |
134 comp_what = hp.get_expression() | |
135 if (not comp_what or | |
136 (not evalfuncs and comp_what.find('(') != -1)): | |
137 return None | |
138 else: | |
139 comp_what = "" | |
140 else: | |
141 return None | |
142 | |
143 if complete and not comp_what and not comp_start: | |
144 return None | |
145 comp_lists = self.fetch_completions(comp_what, mode) | |
146 if not comp_lists[0]: | |
147 return None | |
148 self.autocompletewindow = self._make_autocomplete_window() | |
149 return not self.autocompletewindow.show_window( | |
150 comp_lists, "insert-%dc" % len(comp_start), | |
151 complete, mode, wantwin) | |
152 | |
153 def fetch_completions(self, what, mode): | |
154 """Return a pair of lists of completions for something. The first list | |
155 is a sublist of the second. Both are sorted. | |
156 | |
157 If there is a Python subprocess, get the comp. list there. Otherwise, | |
158 either fetch_completions() is running in the subprocess itself or it | |
159 was called in an IDLE EditorWindow before any script had been run. | |
160 | |
161 The subprocess environment is that of the most recently run script. If | |
162 two unrelated modules are being edited some calltips in the current | |
163 module may be inoperative if the module was not the last to run. | |
164 """ | |
165 try: | |
166 rpcclt = self.editwin.flist.pyshell.interp.rpcclt | |
167 except: | |
168 rpcclt = None | |
169 if rpcclt: | |
170 return rpcclt.remotecall("exec", "get_the_completion_list", | |
171 (what, mode), {}) | |
172 else: | |
173 if mode == ATTRS: | |
174 if what == "": | |
175 namespace = {**__main__.__builtins__.__dict__, | |
176 **__main__.__dict__} | |
177 bigl = eval("dir()", namespace) | |
178 bigl.sort() | |
179 if "__all__" in bigl: | |
180 smalll = sorted(eval("__all__", namespace)) | |
181 else: | |
182 smalll = [s for s in bigl if s[:1] != '_'] | |
183 else: | |
184 try: | |
185 entity = self.get_entity(what) | |
186 bigl = dir(entity) | |
187 bigl.sort() | |
188 if "__all__" in bigl: | |
189 smalll = sorted(entity.__all__) | |
190 else: | |
191 smalll = [s for s in bigl if s[:1] != '_'] | |
192 except: | |
193 return [], [] | |
194 | |
195 elif mode == FILES: | |
196 if what == "": | |
197 what = "." | |
198 try: | |
199 expandedpath = os.path.expanduser(what) | |
200 bigl = os.listdir(expandedpath) | |
201 bigl.sort() | |
202 smalll = [s for s in bigl if s[:1] != '.'] | |
203 except OSError: | |
204 return [], [] | |
205 | |
206 if not smalll: | |
207 smalll = bigl | |
208 return smalll, bigl | |
209 | |
210 def get_entity(self, name): | |
211 "Lookup name in a namespace spanning sys.modules and __main.dict__." | |
212 return eval(name, {**sys.modules, **__main__.__dict__}) | |
213 | |
214 | |
215 AutoComplete.reload() | |
216 | |
217 if __name__ == '__main__': | |
218 from unittest import main | |
219 main('idlelib.idle_test.test_autocomplete', verbosity=2) |