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)