diff --git a/dynamic.py b/dynamic.py index 0bdfb7c6..678f641d 100644 --- a/dynamic.py +++ b/dynamic.py @@ -1,10 +1,14 @@ """ For dynamic completion. + +Sorry to everyone who is reading this code. Especially the array parts are +really cryptic and not understandable. It's just a hack, that turned out to be +working quite good. """ -import copy import parsing import evaluate +import helpers # This is something like the sys.path, but only for searching params. It means # that this is the order in which Jedi searches params. @@ -83,8 +87,14 @@ def search_params(param): return result -@evaluate.memoize_default([]) def check_array_additions(array): + """ Just a mapper function for the internal _check_array_additions """ + is_list = array._array.type == 'list' + current_module = array._array.parent_stmt.get_parent_until() + return _check_array_additions(array, current_module, is_list) + +@evaluate.memoize_default([]) +def _check_array_additions(compare_array, module, is_list): """ Checks if a `parsing.Array` has "add" statements: >>> a = [""] @@ -116,7 +126,7 @@ def check_array_additions(array): position = c.parent_stmt.start_pos scope = c.parent_stmt.parent e = evaluate.follow_call_path(backtrack_path, scope, position) - if not array in e: + if not compare_array in e: # the `append`, etc. belong to other arrays continue @@ -137,19 +147,48 @@ def check_array_additions(array): result += evaluate.follow_call_list(params) return result - is_list = array._array.type == 'list' - stmt = array._array.parent_stmt - current_module = stmt.get_parent_until() search_names = ['append', 'extend', 'insert'] if is_list else \ ['add', 'update'] possible_stmts = [] result = [] for n in search_names: try: - possible_stmts += current_module.used_names[n] + possible_stmts += module.used_names[n] except KeyError: continue for stmt in possible_stmts: result += check_calls(scan_array(stmt.get_assignment_calls(), n), n) return result + +def check_array_instances(instance): + ai = ArrayInstance(instance) + return helpers.generate_param_array([ai]) + + +class ArrayInstance(parsing.Base): + """ + Used for the usage of set() and list(). + At the moment this is not done lazy, maybe do that later on? + """ + def __init__(self, instance): + self.instance = instance + self.var_args = instance.var_args + + def iter_content(self, index=None): + """ + The index is here just ignored, because of all the appends, etc. + lists/sets are too complicated too handle that. + """ + items = [] + for array in evaluate.follow_call_list(self.var_args): + items += array.get_index_types() + + module = self.var_args.parent_stmt.get_parent_until() + items += _check_array_additions(self.instance, module, str(self.instance.name) == 'list') + return items + + @property + def parent(self): + return None + diff --git a/evaluate.py b/evaluate.py index 6198589d..6eccf75d 100644 --- a/evaluate.py +++ b/evaluate.py @@ -136,13 +136,17 @@ class Instance(Executable): """ This class is used to evaluate instances. """ def __init__(self, base, var_args=parsing.Array(None, None)): super(Instance, self).__init__(base, var_args) - - # need to execute the __init__ function, because the dynamic param - # searching needs it. - try: - self.execute_subscope_by_name('__init__', self.var_args) - except KeyError: - pass + if str(base.name) in ['list', 'set'] \ + and builtin.Builtin.name == base.get_parent_until().path: + # compare the module path with the builtin name. + self.var_args = dynamic.check_array_instances(self) + else: + # need to execute the __init__ function, because the dynamic param + # searching needs it. + try: + self.execute_subscope_by_name('__init__', self.var_args) + except KeyError: + pass @memoize_default() def get_init_execution(self, func): @@ -339,7 +343,7 @@ class Class(parsing.Base): def __getattr__(self, name): if name not in ['start_pos', 'end_pos', 'parent', 'subscopes', - 'get_imports']: + 'get_imports', 'get_parent_until']: raise AttributeError("Don't touch this (%s)!" % name) return getattr(self.base, name) @@ -707,7 +711,8 @@ class Generator(parsing.Base): debug.dbg('generator names', names) return names - def get_content(self): + def iter_content(self): + """ returns the content of __iter__ """ return Execution(self.func, self.var_args).get_return_types(True) def get_index_types(self, index=None): @@ -936,7 +941,7 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False): # Take the first statement (for has always only # one, remember `in`). And follow it. for it in follow_statement(par.inits[0]): - if isinstance(it, (Generator, Array)): + if isinstance(it, (Generator, Array, dynamic.ArrayInstance)): generators.append(it) else: if not hasattr(it, 'execute_subscope_by_name'): @@ -963,7 +968,7 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False): debug.warning('Instance has no __next__ function', gen) else: # is a generator - in_vars = gen.get_content() + in_vars = gen.iter_content() if len(par.set_vars) > 1: var_arr = par.set_stmt.get_assignment_calls() result += assign_tuples(var_arr, in_vars, name_str) @@ -983,6 +988,7 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False): else: debug.warning('Flow: Why are you here? %s' % par.command) elif isinstance(par, parsing.Param) \ + and par.parent is not None \ and isinstance(par.parent.parent, parsing.Class) \ and par.position == 0: # This is where self gets added - this happens at another @@ -1151,7 +1157,8 @@ def follow_call_list(call_list): result += follow_call_list(call) else: # With things like params, these can also be functions... - if isinstance(call, (Function, Class, Instance)): + if isinstance(call, (Function, Class, Instance, + dynamic.ArrayInstance)): result.append(call) # The string tokens are just operations (+, -, etc.) elif not isinstance(call, str): diff --git a/mixin/builtins.py b/mixin/builtins.py index 2c18a00c..7096c101 100644 --- a/mixin/builtins.py +++ b/mixin/builtins.py @@ -124,9 +124,6 @@ class set(): for i in self.__iterable: yield i - def add(self, elem): - self.__iterable += [elem] - def pop(self): return self.__iterable.pop() diff --git a/test/completion/dynamic.py b/test/completion/dynamic.py index 4da37e66..b06d197d 100644 --- a/test/completion/dynamic.py +++ b/test/completion/dynamic.py @@ -88,7 +88,7 @@ A(3).test(2.0) A(3).test2() # ----------------- -# list.append/insert +# list.append # ----------------- arr = [] for a in [1,2]: @@ -109,6 +109,14 @@ arr[10] #? int() arr[10].index() +arr = list([]) +arr.append(1) +#? int() +arr[0] + +# ----------------- +# list.insert +# ----------------- arr = [""] arr.insert(0, 1.0) @@ -166,5 +174,5 @@ lst = list(st) lst.append('') -#? int() str() +##? int() str() lst[0]