4848SUBSTRING = 'substring'
4949FUZZY = 'fuzzy'
5050
51- def attr_complete (text , namespace = None , config = None ):
51+ MAGIC_METHODS = ["__%s__" % s for s in [
52+ "init" , "repr" , "str" , "lt" , "le" , "eq" , "ne" , "gt" , "ge" , "cmp" , "hash" ,
53+ "nonzero" , "unicode" , "getattr" , "setattr" , "get" , "set" ,"call" , "len" ,
54+ "getitem" , "setitem" , "iter" , "reversed" , "contains" , "add" , "sub" , "mul" ,
55+ "floordiv" , "mod" , "divmod" , "pow" , "lshift" , "rshift" , "and" , "xor" , "or" ,
56+ "div" , "truediv" , "neg" , "pos" , "abs" , "invert" , "complex" , "int" , "float" ,
57+ "oct" , "hex" , "index" , "coerce" , "enter" , "exit" ]]
58+
59+
60+ def attr_complete (text , namespace = None , mode = SIMPLE ):
5261 """Return list of matches """
5362 if namespace is None :
54- namespace = __main__ .__dict__ #TODO figure out if this __main__ still makes sense
63+ namespace = __main__ .__dict__
64+
65+ assert '.' in text
66+
67+ for i in range (1 , len (text ) + 1 ):
68+ if text [- i ] == '[' :
69+ i -= 1
70+ break
71+ methodtext = text [- i :]
72+ matches = ['' .join ([text [:- i ], m ]) for m in
73+ attr_matches (methodtext , namespace , mode )]
74+
75+ # unless the first character is a _ filter out all attributes starting with a _
76+ if not text .split ('.' )[- 1 ].startswith ('_' ):
77+ matches = [match for match in matches
78+ if not match .split ('.' )[- 1 ].startswith ('_' )]
79+ return matches
5580
56- if hasattr (config , 'autocomplete_mode' ):
57- autocomplete_mode = config .autocomplete_mode
58- else :
59- autocomplete_mode = SUBSTRING
60-
61- if "." in text :
62- # Examples: 'foo.b' or 'foo[bar.'
63- for i in range (1 , len (text ) + 1 ):
64- if text [- i ] == '[' :
65- i -= 1
66- break
67- methodtext = text [- i :]
68- return ['' .join ([text [:- i ], m ]) for m in
69- attr_matches (methodtext , namespace , autocomplete_mode )]
70- else :
71- return global_matches (text , namespace , autocomplete_mode )
7281
7382class SafeEvalFailed (Exception ):
7483 """If this object is returned, safe_eval failed"""
@@ -155,6 +164,8 @@ def global_matches(text, namespace, autocomplete_mode):
155164 matches .sort ()
156165 return matches
157166
167+ #TODO use method_match everywhere instead of startswith to implement other completion modes
168+ # will also need to rewrite checking mode so cseq replace doesn't happen in frontends
158169def method_match (word , size , text , autocomplete_mode ):
159170 if autocomplete_mode == SIMPLE :
160171 return word [:size ] == text
@@ -187,57 +198,45 @@ def last_part_of_filename(filename):
187198def after_last_dot (name ):
188199 return name .rstrip ('.' ).rsplit ('.' )[- 1 ]
189200
190- def dict_key_format (filename ):
191- # dictionary key suggestions
192- #items = [x.rstrip(']') for x in items]
193- #if current_item:
194- # current_item = current_item.rstrip(']')
195- pass
196-
197- def get_completer (cursor_offset , current_line , locals_ , argspec , config , magic_methods ):
201+ def get_completer (cursor_offset , current_line , locals_ , argspec , full_code , mode , complete_magic_methods ):
198202 """Returns a list of matches and a class for what kind of completion is happening
199203
200204 If no completion type is relevant, returns None, None"""
201205
202- #TODO use the smarter current_string() in Repl that knows about the buffer
203- #TODO don't pass in config, pass in the settings themselves
206+ kwargs = { 'locals_' : locals_ , 'argspec' : argspec , 'full_code' : full_code ,
207+ 'mode' : mode , 'complete_magic_methods' : complete_magic_methods }
204208
205- matches = ImportCompletion .matches (cursor_offset , current_line )
206- if matches is not None :
207- return sorted (set (matches )), ImportCompletion
209+ # mutually exclusive matchers: if one returns [], don't go on
210+ for completer in [ImportCompletion , FilenameCompletion ,
211+ MagicMethodCompletion , GlobalCompletion ]:
212+ matches = completer .matches (cursor_offset , current_line , ** kwargs )
213+ if matches is not None :
214+ return sorted (set (matches )), completer
208215
209- matches = FilenameCompletion .matches (cursor_offset , current_line )
210- if matches is not None :
211- return sorted (set (matches )), FilenameCompletion
216+ # mutually exclusive if matches: If one of these returns [], try the next one
217+ for completer in [DictKeyCompletion ]:
218+ matches = completer .matches (cursor_offset , current_line , ** kwargs )
219+ if matches :
220+ return sorted (set (matches )), completer
212221
213- matches = DictKeyCompletion .matches (cursor_offset , current_line , locals_ = locals_ , config = config )
214- if matches :
215- return sorted (set (matches )), DictKeyCompletion
222+ matches = AttrCompletion .matches (cursor_offset , current_line , ** kwargs )
216223
217- matches = AttrCompletion .matches (cursor_offset , current_line , locals_ = locals_ , config = config )
218- if matches is not None :
219- cw = AttrCompletion .locate (cursor_offset , current_line )[2 ]
220- matches .extend (magic_methods (cw ))
221- if argspec :
222- matches .extend (name + '=' for name in argspec [1 ][0 ]
223- if isinstance (name , basestring ) and name .startswith (cw ))
224- if py3 :
225- matches .extend (name + '=' for name in argspec [1 ][4 ]
226- if name .startswith (cw ))
224+ # cumulative completions - try them all
225+ # They all use current_word replacement and formatting
226+ current_word_matches = []
227+ for completer in [AttrCompletion , ParameterNameCompletion ]:
228+ matches = completer .matches (cursor_offset , current_line , ** kwargs )
229+ if matches is not None :
230+ current_word_matches .extend (matches )
227231
228- # unless the first character is a _ filter out all attributes starting with a _
229- if not cw .split ('.' )[- 1 ].startswith ('_' ):
230- matches = [match for match in matches
231- if not match .split ('.' )[- 1 ].startswith ('_' )]
232-
233- return sorted (set (matches )), AttrCompletion
234-
235- return None , None
232+ if len (current_word_matches ) == 0 :
233+ return None , None
234+ return sorted (set (current_word_matches )), AttrCompletion
236235
237236
238237class BaseCompletionType (object ):
239238 """Describes different completion types"""
240- def matches (cls , cursor_offset , line ):
239+ def matches (cls , cursor_offset , line , ** kwargs ):
241240 """Returns a list of possible matches given a line and cursor, or None
242241 if this completion type isn't applicable.
243242
@@ -268,14 +267,16 @@ def substitute(cls, cursor_offset, line, match):
268267 return result
269268
270269class ImportCompletion (BaseCompletionType ):
271- matches = staticmethod (importcompletion .complete )
270+ @classmethod
271+ def matches (cls , cursor_offset , current_line , ** kwargs ):
272+ return importcompletion .complete (cursor_offset , current_line )
272273 locate = staticmethod (lineparts .current_word )
273274 format = staticmethod (after_last_dot )
274275
275276class FilenameCompletion (BaseCompletionType ):
276277 shown_before_tab = False
277278 @classmethod
278- def matches (cls , cursor_offset , current_line ):
279+ def matches (cls , cursor_offset , current_line , ** kwargs ):
279280 cs = lineparts .current_string (cursor_offset , current_line )
280281 if cs is None :
281282 return None
@@ -285,19 +286,19 @@ def matches(cls, cursor_offset, current_line):
285286
286287class AttrCompletion (BaseCompletionType ):
287288 @classmethod
288- def matches (cls , cursor_offset , line , locals_ , config ):
289+ def matches (cls , cursor_offset , line , locals_ , mode , ** kwargs ):
289290 r = cls .locate (cursor_offset , line )
290291 if r is None :
291292 return None
292293 cw = r [2 ]
293- return attr_complete (cw , namespace = locals_ , config = config )
294- locate = staticmethod (lineparts .current_word )
294+ return attr_complete (cw , namespace = locals_ , mode = mode )
295+ locate = staticmethod (lineparts .current_dotted_attribute )
295296 format = staticmethod (after_last_dot )
296297
297298class DictKeyCompletion (BaseCompletionType ):
298299 locate = staticmethod (lineparts .current_dict_key )
299300 @classmethod
300- def matches (cls , cursor_offset , line , locals_ , config ):
301+ def matches (cls , cursor_offset , line , locals_ , ** kwargs ):
301302 r = cls .locate (cursor_offset , line )
302303 if r is None :
303304 return None
@@ -313,3 +314,43 @@ def matches(cls, cursor_offset, line, locals_, config):
313314 @classmethod
314315 def format (cls , match ):
315316 return match [:- 1 ]
317+
318+ class MagicMethodCompletion (BaseCompletionType ):
319+ locate = staticmethod (lineparts .current_method_definition_name )
320+ @classmethod
321+ def matches (cls , cursor_offset , line , full_code , ** kwargs ):
322+ r = cls .locate (cursor_offset , line )
323+ if r is None :
324+ return None
325+ if 'class' not in full_code :
326+ return None
327+ start , end , word = r
328+ return [name for name in MAGIC_METHODS if name .startswith (word )]
329+
330+ class GlobalCompletion (BaseCompletionType ):
331+ @classmethod
332+ def matches (cls , cursor_offset , line , locals_ , mode , ** kwargs ):
333+ r = cls .locate (cursor_offset , line )
334+ if r is None :
335+ return None
336+ start , end , word = r
337+ return global_matches (word , locals_ , mode )
338+ locate = staticmethod (lineparts .current_single_word )
339+
340+ class ParameterNameCompletion (BaseCompletionType ):
341+ @classmethod
342+ def matches (cls , cursor_offset , line , argspec , ** kwargs ):
343+ if not argspec :
344+ return None
345+ r = cls .locate (cursor_offset , line )
346+ if r is None :
347+ return None
348+ start , end , word = r
349+ if argspec :
350+ matches = [name + '=' for name in argspec [1 ][0 ]
351+ if isinstance (name , basestring ) and name .startswith (word )]
352+ if py3 :
353+ matches .extend (name + '=' for name in argspec [1 ][4 ]
354+ if name .startswith (word ))
355+ return matches
356+ locate = staticmethod (lineparts .current_word )
0 commit comments