88import string
99import sys
1010
11- # These constants represent the two different types of completions.
12- # They must be defined here so autocomple_w can import them.
13- COMPLETE_ATTRIBUTES , COMPLETE_FILES = range (1 , 2 + 1 )
14-
11+ # Two types of completions; defined here for autocomplete_w import below.
12+ ATTRS , FILES = 0 , 1
1513from idlelib import autocomplete_w
1614from idlelib .config import idleConf
1715from idlelib .hyperparser import HyperParser
1816
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+
1924# This string includes all chars that may be in an identifier.
2025# TODO Update this here and elsewhere.
2126ID_CHARS = string .ascii_letters + string .digits + "_"
2227
23- SEPS = os .sep
24- if os .altsep : # e.g. '/' on Windows...
25- SEPS += os .altsep
26-
28+ SEPS = f"{ os .sep } { os .altsep if os .altsep else '' } "
29+ TRIGGERS = f".{ SEPS } "
2730
2831class AutoComplete :
2932
3033 def __init__ (self , editwin = None ):
3134 self .editwin = editwin
32- if editwin is not None : # not in subprocess or test
35+ if editwin is not None : # not in subprocess or no-gui test
3336 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
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
4043
4144 @classmethod
4245 def reload (cls ):
4346 cls .popupwait = idleConf .GetOption (
4447 "extensions" , "AutoComplete" , "popupwait" , type = "int" , default = 0 )
4548
46- def _make_autocomplete_window (self ):
49+ def _make_autocomplete_window (self ): # Makes mocking easier.
4750 return autocomplete_w .AutoCompleteWindow (self .text )
4851
4952 def _remove_autocomplete_window (self , event = None ):
@@ -52,30 +55,12 @@ def _remove_autocomplete_window(self, event=None):
5255 self .autocompletewindow = None
5356
5457 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 )
58+ "(^space) Open completion list, even if a function call is needed."
59+ self .open_completions (FORCE )
5960 return "break"
6061
61- def try_open_completions_event (self , event ):
62- """Happens when it would be nice to open a completion list, but not
63- really necessary, for example after a dot, so function
64- 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 )
70- elif lastchar in SEPS :
71- self ._open_completions_later (False , False , False ,
72- COMPLETE_FILES )
73-
7462 def autocomplete_event (self , event ):
75- """Happens when the user wants to complete his word, and if necessary,
76- open a completion list after that (if there is more than one
77- completion)
78- """
63+ "(tab) Complete word or open list if multiple options."
7964 if hasattr (event , "mc_state" ) and event .mc_state or \
8065 not self .text .get ("insert linestart" , "insert" ).strip ():
8166 # A modifier was pressed along with the tab or
@@ -85,34 +70,34 @@ def autocomplete_event(self, event):
8570 self .autocompletewindow .complete ()
8671 return "break"
8772 else :
88- opened = self .open_completions (False , True , True )
73+ opened = self .open_completions (TAB )
8974 return "break" if opened else None
9075
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 ):
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."
10089 self ._delayed_completion_id = None
10190 if self .text .index ("insert" ) == self ._delayed_completion_index :
102- self .open_completions (* args )
91+ self .open_completions (args )
10392
104- def open_completions (self , evalfuncs , complete , userWantsWin , mode = None ):
93+ def open_completions (self , args ):
10594 """Find the completions and create the AutoCompleteWindow.
10695 Return True if successful (no syntax error or so found).
10796 If complete is True, then if there's nothing to complete and no
10897 start of completion, won't open completions and return False.
10998 If mode is given, will open a completion list only in this mode.
110-
111- Action Function Eval Complete WantWin Mode
112- ^space force_open_completions True, False, True no
113- . or / try_open_completions False, False, False yes
114- tab autocomplete False, True, True no
11599 """
100+ evalfuncs , complete , wantwin , mode = args
116101 # Cancel another delayed call, if it exists.
117102 if self ._delayed_completion_id is not None :
118103 self .text .after_cancel (self ._delayed_completion_id )
@@ -121,14 +106,14 @@ def open_completions(self, evalfuncs, complete, userWantsWin, mode=None):
121106 hp = HyperParser (self .editwin , "insert" )
122107 curline = self .text .get ("insert linestart" , "insert" )
123108 i = j = len (curline )
124- if hp .is_in_string () and (not mode or mode == COMPLETE_FILES ):
109+ if hp .is_in_string () and (not mode or mode == FILES ):
125110 # Find the beginning of the string.
126111 # fetch_completions will look at the file system to determine
127112 # whether the string value constitutes an actual file name
128113 # XXX could consider raw strings here and unescape the string
129114 # value if it's not raw.
130115 self ._remove_autocomplete_window ()
131- mode = COMPLETE_FILES
116+ mode = FILES
132117 # Find last separator or string start
133118 while i and curline [i - 1 ] not in "'\" " + SEPS :
134119 i -= 1
@@ -138,17 +123,17 @@ def open_completions(self, evalfuncs, complete, userWantsWin, mode=None):
138123 while i and curline [i - 1 ] not in "'\" " :
139124 i -= 1
140125 comp_what = curline [i :j ]
141- elif hp .is_in_code () and (not mode or mode == COMPLETE_ATTRIBUTES ):
126+ elif hp .is_in_code () and (not mode or mode == ATTRS ):
142127 self ._remove_autocomplete_window ()
143- mode = COMPLETE_ATTRIBUTES
128+ mode = ATTRS
144129 while i and (curline [i - 1 ] in ID_CHARS or ord (curline [i - 1 ]) > 127 ):
145130 i -= 1
146131 comp_start = curline [i :j ]
147- if i and curline [i - 1 ] == '.' :
132+ if i and curline [i - 1 ] == '.' : # Need object with attributes.
148133 hp .set_index ("insert-%dc" % (len (curline )- (i - 1 )))
149134 comp_what = hp .get_expression ()
150- if not comp_what or \
151- (not evalfuncs and comp_what .find ('(' ) != - 1 ):
135+ if ( not comp_what or
136+ (not evalfuncs and comp_what .find ('(' ) != - 1 )) :
152137 return None
153138 else :
154139 comp_what = ""
@@ -163,7 +148,7 @@ def open_completions(self, evalfuncs, complete, userWantsWin, mode=None):
163148 self .autocompletewindow = self ._make_autocomplete_window ()
164149 return not self .autocompletewindow .show_window (
165150 comp_lists , "insert-%dc" % len (comp_start ),
166- complete , mode , userWantsWin )
151+ complete , mode , wantwin )
167152
168153 def fetch_completions (self , what , mode ):
169154 """Return a pair of lists of completions for something. The first list
@@ -185,7 +170,7 @@ def fetch_completions(self, what, mode):
185170 return rpcclt .remotecall ("exec" , "get_the_completion_list" ,
186171 (what , mode ), {})
187172 else :
188- if mode == COMPLETE_ATTRIBUTES :
173+ if mode == ATTRS :
189174 if what == "" :
190175 namespace = {** __main__ .__builtins__ .__dict__ ,
191176 ** __main__ .__dict__ }
@@ -207,7 +192,7 @@ def fetch_completions(self, what, mode):
207192 except :
208193 return [], []
209194
210- elif mode == COMPLETE_FILES :
195+ elif mode == FILES :
211196 if what == "" :
212197 what = "."
213198 try :
0 commit comments