blob: 6751928f045024ca8966ffd7b3bfffbfd3490b1c [file] [log] [blame]
wohlganger58fc71c2017-09-10 16:19:47 -05001"""Complete either attribute names or file names.
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00002
wohlganger58fc71c2017-09-10 16:19:47 -05003Either on demand or after a user-selected delay after a key character,
4pop up a list of candidates.
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00005"""
6import os
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00007import string
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -04008import sys
Kurt B. Kaiserb1754452005-11-18 22:05:48 +00009
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040010# These constants represent the two different types of completions.
11# They must be defined here so autocomple_w can import them.
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000012COMPLETE_ATTRIBUTES, COMPLETE_FILES = range(1, 2+1)
13
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040014from idlelib import autocomplete_w
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040015from idlelib.config import idleConf
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040016from idlelib.hyperparser import HyperParser
Kurt B. Kaisere1b4a162007-08-10 02:45:06 +000017
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040018# This string includes all chars that may be in an identifier.
19# TODO Update this here and elsewhere.
20ID_CHARS = string.ascii_letters + string.digits + "_"
21
Christian Heimes81ee3ef2008-05-04 22:42:01 +000022SEPS = os.sep
23if os.altsep: # e.g. '/' on Windows...
24 SEPS += os.altsep
25
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040026
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000027class AutoComplete:
28
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000029 def __init__(self, editwin=None):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000030 self.editwin = editwin
wohlganger58fc71c2017-09-10 16:19:47 -050031 if editwin is not None: # not in subprocess or test
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -040032 self.text = editwin.text
33 self.autocompletewindow = None
34 # id of delayed call, and the index of the text insert when
35 # the delayed call was issued. If _delayed_completion_id is
36 # None, there is no delayed call.
37 self._delayed_completion_id = None
38 self._delayed_completion_index = None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000039
wohlganger58fc71c2017-09-10 16:19:47 -050040 @classmethod
41 def reload(cls):
42 cls.popupwait = idleConf.GetOption(
43 "extensions", "AutoComplete", "popupwait", type="int", default=0)
44
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000045 def _make_autocomplete_window(self):
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040046 return autocomplete_w.AutoCompleteWindow(self.text)
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000047
48 def _remove_autocomplete_window(self, event=None):
49 if self.autocompletewindow:
50 self.autocompletewindow.hide_window()
51 self.autocompletewindow = None
52
53 def force_open_completions_event(self, event):
54 """Happens when the user really wants to open a completion list, even
55 if a function call is needed.
56 """
57 self.open_completions(True, False, True)
Serhiy Storchaka213ce122017-06-27 07:02:32 +030058 return "break"
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000059
60 def try_open_completions_event(self, event):
61 """Happens when it would be nice to open a completion list, but not
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +030062 really necessary, for example after a dot, so function
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000063 calls won't be made.
64 """
65 lastchar = self.text.get("insert-1c")
66 if lastchar == ".":
67 self._open_completions_later(False, False, False,
68 COMPLETE_ATTRIBUTES)
Christian Heimes81ee3ef2008-05-04 22:42:01 +000069 elif lastchar in SEPS:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000070 self._open_completions_later(False, False, False,
71 COMPLETE_FILES)
72
73 def autocomplete_event(self, event):
Mark Dickinson934896d2009-02-21 20:59:32 +000074 """Happens when the user wants to complete his word, and if necessary,
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000075 open a completion list after that (if there is more than one
76 completion)
77 """
Terry Jan Reedyc665dfd2016-07-24 23:01:28 -040078 if hasattr(event, "mc_state") and event.mc_state or\
79 not self.text.get("insert linestart", "insert").strip():
80 # A modifier was pressed along with the tab or
81 # there is only previous whitespace on this line, so tab.
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -040082 return None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000083 if self.autocompletewindow and self.autocompletewindow.is_active():
84 self.autocompletewindow.complete()
85 return "break"
86 else:
87 opened = self.open_completions(False, True, True)
Terry Jan Reedyc665dfd2016-07-24 23:01:28 -040088 return "break" if opened else None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000089
90 def _open_completions_later(self, *args):
91 self._delayed_completion_index = self.text.index("insert")
92 if self._delayed_completion_id is not None:
93 self.text.after_cancel(self._delayed_completion_id)
94 self._delayed_completion_id = \
95 self.text.after(self.popupwait, self._delayed_open_completions,
96 *args)
97
98 def _delayed_open_completions(self, *args):
99 self._delayed_completion_id = None
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -0400100 if self.text.index("insert") == self._delayed_completion_index:
101 self.open_completions(*args)
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000102
103 def open_completions(self, evalfuncs, complete, userWantsWin, mode=None):
104 """Find the completions and create the AutoCompleteWindow.
105 Return True if successful (no syntax error or so found).
106 if complete is True, then if there's nothing to complete and no
107 start of completion, won't open completions and return False.
108 If mode is given, will open a completion list only in this mode.
109 """
110 # Cancel another delayed call, if it exists.
111 if self._delayed_completion_id is not None:
112 self.text.after_cancel(self._delayed_completion_id)
113 self._delayed_completion_id = None
114
115 hp = HyperParser(self.editwin, "insert")
116 curline = self.text.get("insert linestart", "insert")
117 i = j = len(curline)
118 if hp.is_in_string() and (not mode or mode==COMPLETE_FILES):
Martin v. Löwis862d13a2012-06-03 11:55:32 +0200119 # Find the beginning of the string
120 # fetch_completions will look at the file system to determine whether the
121 # string value constitutes an actual file name
122 # XXX could consider raw strings here and unescape the string value if it's
123 # not raw.
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000124 self._remove_autocomplete_window()
125 mode = COMPLETE_FILES
Martin v. Löwis862d13a2012-06-03 11:55:32 +0200126 # Find last separator or string start
127 while i and curline[i-1] not in "'\"" + SEPS:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000128 i -= 1
129 comp_start = curline[i:j]
130 j = i
Martin v. Löwis862d13a2012-06-03 11:55:32 +0200131 # Find string start
132 while i and curline[i-1] not in "'\"":
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000133 i -= 1
134 comp_what = curline[i:j]
135 elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES):
136 self._remove_autocomplete_window()
137 mode = COMPLETE_ATTRIBUTES
Martin v. Löwis993fe3f2012-06-14 15:37:21 +0200138 while i and (curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000139 i -= 1
140 comp_start = curline[i:j]
141 if i and curline[i-1] == '.':
142 hp.set_index("insert-%dc" % (len(curline)-(i-1)))
143 comp_what = hp.get_expression()
144 if not comp_what or \
145 (not evalfuncs and comp_what.find('(') != -1):
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -0400146 return None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000147 else:
148 comp_what = ""
149 else:
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -0400150 return None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000151
152 if complete and not comp_what and not comp_start:
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -0400153 return None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000154 comp_lists = self.fetch_completions(comp_what, mode)
155 if not comp_lists[0]:
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -0400156 return None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000157 self.autocompletewindow = self._make_autocomplete_window()
Serhiy Storchakadd4754e2013-09-11 22:46:27 +0300158 return not self.autocompletewindow.show_window(
159 comp_lists, "insert-%dc" % len(comp_start),
160 complete, mode, userWantsWin)
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000161
162 def fetch_completions(self, what, mode):
163 """Return a pair of lists of completions for something. The first list
164 is a sublist of the second. Both are sorted.
165
166 If there is a Python subprocess, get the comp. list there. Otherwise,
167 either fetch_completions() is running in the subprocess itself or it
168 was called in an IDLE EditorWindow before any script had been run.
169
170 The subprocess environment is that of the most recently run script. If
171 two unrelated modules are being edited some calltips in the current
172 module may be inoperative if the module was not the last to run.
173 """
174 try:
175 rpcclt = self.editwin.flist.pyshell.interp.rpcclt
176 except:
177 rpcclt = None
178 if rpcclt:
179 return rpcclt.remotecall("exec", "get_the_completion_list",
180 (what, mode), {})
181 else:
182 if mode == COMPLETE_ATTRIBUTES:
183 if what == "":
Terry Jan Reedy2b751552019-03-23 03:50:15 -0400184 namespace = {**__builtins__.__dict__, **globals()}
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000185 bigl = eval("dir()", namespace)
186 bigl.sort()
187 if "__all__" in bigl:
Terry Jan Reedya77aa692012-02-05 14:31:16 -0500188 smalll = sorted(eval("__all__", namespace))
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000189 else:
Kurt B. Kaiserf2335a92007-08-10 02:41:21 +0000190 smalll = [s for s in bigl if s[:1] != '_']
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000191 else:
192 try:
193 entity = self.get_entity(what)
194 bigl = dir(entity)
195 bigl.sort()
196 if "__all__" in bigl:
Terry Jan Reedya77aa692012-02-05 14:31:16 -0500197 smalll = sorted(entity.__all__)
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000198 else:
Kurt B. Kaiserf2335a92007-08-10 02:41:21 +0000199 smalll = [s for s in bigl if s[:1] != '_']
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000200 except:
201 return [], []
202
203 elif mode == COMPLETE_FILES:
204 if what == "":
205 what = "."
206 try:
207 expandedpath = os.path.expanduser(what)
208 bigl = os.listdir(expandedpath)
209 bigl.sort()
Kurt B. Kaiserf2335a92007-08-10 02:41:21 +0000210 smalll = [s for s in bigl if s[:1] != '.']
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000211 except OSError:
212 return [], []
213
214 if not smalll:
215 smalll = bigl
216 return smalll, bigl
217
218 def get_entity(self, name):
Terry Jan Reedy2b751552019-03-23 03:50:15 -0400219 "Lookup name in a namespace spanning sys.modules and globals()."
220 return eval(name, {**sys.modules, **globals()})
Terry Jan Reedye3fcfc22014-06-03 20:54:21 -0400221
222
wohlganger58fc71c2017-09-10 16:19:47 -0500223AutoComplete.reload()
224
Terry Jan Reedye3fcfc22014-06-03 20:54:21 -0400225if __name__ == '__main__':
226 from unittest import main
227 main('idlelib.idle_test.test_autocomplete', verbosity=2)