blob: edf445f08b586909bdd00910fd08046b65045db4 [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 +000017import __main__
18
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040019# This string includes all chars that may be in an identifier.
20# TODO Update this here and elsewhere.
21ID_CHARS = string.ascii_letters + string.digits + "_"
22
Christian Heimes81ee3ef2008-05-04 22:42:01 +000023SEPS = os.sep
24if os.altsep: # e.g. '/' on Windows...
25 SEPS += os.altsep
26
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -040027
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000028class AutoComplete:
29
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000030 def __init__(self, editwin=None):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000031 self.editwin = editwin
wohlganger58fc71c2017-09-10 16:19:47 -050032 if editwin is not None: # not in subprocess or test
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -040033 self.text = editwin.text
34 self.autocompletewindow = None
35 # id of delayed call, and the index of the text insert when
36 # the delayed call was issued. If _delayed_completion_id is
37 # None, there is no delayed call.
38 self._delayed_completion_id = None
39 self._delayed_completion_index = None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000040
wohlganger58fc71c2017-09-10 16:19:47 -050041 @classmethod
42 def reload(cls):
43 cls.popupwait = idleConf.GetOption(
44 "extensions", "AutoComplete", "popupwait", type="int", default=0)
45
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000046 def _make_autocomplete_window(self):
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -040047 return autocomplete_w.AutoCompleteWindow(self.text)
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000048
49 def _remove_autocomplete_window(self, event=None):
50 if self.autocompletewindow:
51 self.autocompletewindow.hide_window()
52 self.autocompletewindow = None
53
54 def force_open_completions_event(self, event):
55 """Happens when the user really wants to open a completion list, even
56 if a function call is needed.
57 """
58 self.open_completions(True, False, True)
Serhiy Storchaka213ce122017-06-27 07:02:32 +030059 return "break"
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000060
61 def try_open_completions_event(self, event):
62 """Happens when it would be nice to open a completion list, but not
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +030063 really necessary, for example after a dot, so function
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000064 calls won't be made.
65 """
66 lastchar = self.text.get("insert-1c")
67 if lastchar == ".":
68 self._open_completions_later(False, False, False,
69 COMPLETE_ATTRIBUTES)
Christian Heimes81ee3ef2008-05-04 22:42:01 +000070 elif lastchar in SEPS:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000071 self._open_completions_later(False, False, False,
72 COMPLETE_FILES)
73
74 def autocomplete_event(self, event):
Mark Dickinson934896d2009-02-21 20:59:32 +000075 """Happens when the user wants to complete his word, and if necessary,
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000076 open a completion list after that (if there is more than one
77 completion)
78 """
Terry Jan Reedyc665dfd2016-07-24 23:01:28 -040079 if hasattr(event, "mc_state") and event.mc_state or\
80 not self.text.get("insert linestart", "insert").strip():
81 # A modifier was pressed along with the tab or
82 # there is only previous whitespace on this line, so tab.
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -040083 return None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000084 if self.autocompletewindow and self.autocompletewindow.is_active():
85 self.autocompletewindow.complete()
86 return "break"
87 else:
88 opened = self.open_completions(False, True, True)
Terry Jan Reedyc665dfd2016-07-24 23:01:28 -040089 return "break" if opened else None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +000090
91 def _open_completions_later(self, *args):
92 self._delayed_completion_index = self.text.index("insert")
93 if self._delayed_completion_id is not None:
94 self.text.after_cancel(self._delayed_completion_id)
95 self._delayed_completion_id = \
96 self.text.after(self.popupwait, self._delayed_open_completions,
97 *args)
98
99 def _delayed_open_completions(self, *args):
100 self._delayed_completion_id = None
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -0400101 if self.text.index("insert") == self._delayed_completion_index:
102 self.open_completions(*args)
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000103
104 def open_completions(self, evalfuncs, complete, userWantsWin, mode=None):
105 """Find the completions and create the AutoCompleteWindow.
106 Return True if successful (no syntax error or so found).
107 if complete is True, then if there's nothing to complete and no
108 start of completion, won't open completions and return False.
109 If mode is given, will open a completion list only in this mode.
110 """
111 # Cancel another delayed call, if it exists.
112 if self._delayed_completion_id is not None:
113 self.text.after_cancel(self._delayed_completion_id)
114 self._delayed_completion_id = None
115
116 hp = HyperParser(self.editwin, "insert")
117 curline = self.text.get("insert linestart", "insert")
118 i = j = len(curline)
119 if hp.is_in_string() and (not mode or mode==COMPLETE_FILES):
Martin v. Löwis862d13a2012-06-03 11:55:32 +0200120 # Find the beginning of the string
121 # fetch_completions will look at the file system to determine whether the
122 # string value constitutes an actual file name
123 # XXX could consider raw strings here and unescape the string value if it's
124 # not raw.
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000125 self._remove_autocomplete_window()
126 mode = COMPLETE_FILES
Martin v. Löwis862d13a2012-06-03 11:55:32 +0200127 # Find last separator or string start
128 while i and curline[i-1] not in "'\"" + SEPS:
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000129 i -= 1
130 comp_start = curline[i:j]
131 j = i
Martin v. Löwis862d13a2012-06-03 11:55:32 +0200132 # Find string start
133 while i and curline[i-1] not in "'\"":
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000134 i -= 1
135 comp_what = curline[i:j]
136 elif hp.is_in_code() and (not mode or mode==COMPLETE_ATTRIBUTES):
137 self._remove_autocomplete_window()
138 mode = COMPLETE_ATTRIBUTES
Martin v. Löwis993fe3f2012-06-14 15:37:21 +0200139 while i and (curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127):
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000140 i -= 1
141 comp_start = curline[i:j]
142 if i and curline[i-1] == '.':
143 hp.set_index("insert-%dc" % (len(curline)-(i-1)))
144 comp_what = hp.get_expression()
145 if not comp_what or \
146 (not evalfuncs and comp_what.find('(') != -1):
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -0400147 return None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000148 else:
149 comp_what = ""
150 else:
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -0400151 return None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000152
153 if complete and not comp_what and not comp_start:
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -0400154 return None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000155 comp_lists = self.fetch_completions(comp_what, mode)
156 if not comp_lists[0]:
Terry Jan Reedyc74fb9c2016-07-24 20:35:43 -0400157 return None
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000158 self.autocompletewindow = self._make_autocomplete_window()
Serhiy Storchakadd4754e2013-09-11 22:46:27 +0300159 return not self.autocompletewindow.show_window(
160 comp_lists, "insert-%dc" % len(comp_start),
161 complete, mode, userWantsWin)
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000162
163 def fetch_completions(self, what, mode):
164 """Return a pair of lists of completions for something. The first list
165 is a sublist of the second. Both are sorted.
166
167 If there is a Python subprocess, get the comp. list there. Otherwise,
168 either fetch_completions() is running in the subprocess itself or it
169 was called in an IDLE EditorWindow before any script had been run.
170
171 The subprocess environment is that of the most recently run script. If
172 two unrelated modules are being edited some calltips in the current
173 module may be inoperative if the module was not the last to run.
174 """
175 try:
176 rpcclt = self.editwin.flist.pyshell.interp.rpcclt
177 except:
178 rpcclt = None
179 if rpcclt:
180 return rpcclt.remotecall("exec", "get_the_completion_list",
181 (what, mode), {})
182 else:
183 if mode == COMPLETE_ATTRIBUTES:
184 if what == "":
185 namespace = __main__.__dict__.copy()
186 namespace.update(__main__.__builtins__.__dict__)
187 bigl = eval("dir()", namespace)
188 bigl.sort()
189 if "__all__" in bigl:
Terry Jan Reedya77aa692012-02-05 14:31:16 -0500190 smalll = sorted(eval("__all__", namespace))
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000191 else:
Kurt B. Kaiserf2335a92007-08-10 02:41:21 +0000192 smalll = [s for s in bigl if s[:1] != '_']
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000193 else:
194 try:
195 entity = self.get_entity(what)
196 bigl = dir(entity)
197 bigl.sort()
198 if "__all__" in bigl:
Terry Jan Reedya77aa692012-02-05 14:31:16 -0500199 smalll = sorted(entity.__all__)
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000200 else:
Kurt B. Kaiserf2335a92007-08-10 02:41:21 +0000201 smalll = [s for s in bigl if s[:1] != '_']
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000202 except:
203 return [], []
204
205 elif mode == COMPLETE_FILES:
206 if what == "":
207 what = "."
208 try:
209 expandedpath = os.path.expanduser(what)
210 bigl = os.listdir(expandedpath)
211 bigl.sort()
Kurt B. Kaiserf2335a92007-08-10 02:41:21 +0000212 smalll = [s for s in bigl if s[:1] != '.']
Kurt B. Kaiserb1754452005-11-18 22:05:48 +0000213 except OSError:
214 return [], []
215
216 if not smalll:
217 smalll = bigl
218 return smalll, bigl
219
220 def get_entity(self, name):
221 """Lookup name in a namespace spanning sys.modules and __main.dict__"""
222 namespace = sys.modules.copy()
223 namespace.update(__main__.__dict__)
224 return eval(name, namespace)
Terry Jan Reedye3fcfc22014-06-03 20:54:21 -0400225
226
wohlganger58fc71c2017-09-10 16:19:47 -0500227AutoComplete.reload()
228
229
Terry Jan Reedye3fcfc22014-06-03 20:54:21 -0400230if __name__ == '__main__':
231 from unittest import main
232 main('idlelib.idle_test.test_autocomplete', verbosity=2)