diff --git a/dynamic.py b/dynamic.py index 684fed1a..50e65073 100644 --- a/dynamic.py +++ b/dynamic.py @@ -14,6 +14,23 @@ import helpers # that this is the order in which Jedi searches params. search_param_modules = ['.'] +search_param_cache = {} + +def search_param_memoize(func): + """ + Is only good for search params memoize, respectively the closure, + because it just caches the input, not the func, like normal memoize does. + """ + def wrapper(*args, **kwargs): + key = (args, frozenset(kwargs.items())) + if key in search_param_cache: + return search_param_cache[key] + else: + rv = func(*args, **kwargs) + search_param_cache[key] = rv + return rv + return wrapper + class ParamListener(object): """ @@ -43,16 +60,19 @@ def search_params(param): """ Returns the values of a param, or an empty array. """ - try: - possible_stmts = current_module.used_names[func_name] - except KeyError: - return [] + @search_param_memoize + def get_posibilities(module, func_name): + try: + possible_stmts = module.used_names[func_name] + except KeyError: + return [] - for stmt in possible_stmts: - evaluate.follow_statement(stmt) + for stmt in possible_stmts: + evaluate.follow_statement(stmt) + return listener.param_possibilities result = [] - for params in listener.param_possibilities: + for params in get_posibilities(module, func_name): for p in params: if str(p) == param_name: result += evaluate.follow_statement(p.parent()) @@ -81,7 +101,7 @@ def search_params(param): result = get_params_for_module(current_module) # TODO check other modules - # cleanup: remove the listener + # cleanup: remove the listener, important should not stick. func.listeners.remove(listener) return result @@ -145,7 +165,7 @@ def _check_array_additions(compare_array, module, is_list): result += evaluate.follow_call_list([second_param]) elif add_name in ['extend', 'update']: iterators = evaluate.follow_call_list(params) - result += evaluate.handle_iterators(iterators) + result += evaluate.get_iterator_types(iterators) return result search_names = ['append', 'extend', 'insert'] if is_list else \ @@ -191,7 +211,7 @@ class ArrayInstance(parsing.Base): if isinstance(temp, ArrayInstance): items += temp.iter_content() continue - items += evaluate.handle_iterators([array]) + items += evaluate.get_iterator_types([array]) module = self.var_args.parent_stmt().get_parent_until() is_list = str(self.instance.name) == 'list' diff --git a/evaluate.py b/evaluate.py index f6159f92..a281e362 100644 --- a/evaluate.py +++ b/evaluate.py @@ -75,6 +75,8 @@ def clear_caches(): for m in memoize_caches: m.clear() + dynamic.search_param_cache.clear() + # memorize_caches must never be deleted, because the dicts will get lost in # the wrappers. statement_path = [] @@ -951,7 +953,7 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False): def handle_for_loops(loop): # Take the first statement (for has always only # one, remember `in`). And follow it. - result = handle_iterators(follow_statement(loop.inits[0])) + result = get_iterator_types(follow_statement(loop.inits[0])) if len(loop.set_vars) > 1: var_arr = loop.set_stmt.get_assignment_calls() result = assign_tuples(var_arr, result, name_str) @@ -1036,7 +1038,7 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False): return descriptor_check(remove_statements(filter_name(scope_generator))) -def handle_iterators(inputs): +def get_iterator_types(inputs): iterators = [] # Take the first statement (for has always only # one, remember `in`). And follow it. diff --git a/parsing.py b/parsing.py index 6bd13339..e2bf7f90 100644 --- a/parsing.py +++ b/parsing.py @@ -132,7 +132,6 @@ class Scope(Simple): def add_docstr(self, string): """ Clean up a docstring """ self.docstr = cleandoc(literal_eval(string)) - debug.dbg("Scope(%s)::docstr = %s" % (self, self.docstr)) def add_import(self, imp): self.imports.append(imp) @@ -1297,7 +1296,7 @@ class PyFuzzyParser(object): type, tok, self._tokenize_start_pos, self._tokenize_end_pos, \ self.parserline = next(self.gen) if self.user_position and self.start_pos[0] == self.user_position[0]: - debug.dbg('user scope found [%s] =%s' % \ + debug.dbg('user scope found [%s] = %s' % \ (self.parserline.replace('\n', ''), repr(self.scope))) self.user_scope = self.scope self.last_token = self.current diff --git a/test/completion/dynamic.py b/test/completion/dynamic.py index 082833f3..7d1c9837 100644 --- a/test/completion/dynamic.py +++ b/test/completion/dynamic.py @@ -227,13 +227,29 @@ iter(lst)[0] # functions # ----------------- -def arr_append(arr, a): - arr.append(a) +def arr_append(arr4, a): + arr4.append(a) -def add_to_arr(arr, a): - arr.append(a) - return arr +def add_to_arr(arr2, a): + arr2.append(a) + return arr2 -a = [1.0] -##? float() int() -add_to_arr(a, 1)[0] +def app(a): + arr3.append(a) + +arr3 = [1.0] +res = add_to_arr(arr3, 1) +arr_append(arr3, 'str') +app(set()) + +#? float() str() int() set() +arr3[10] + +#? float() str() int() set() +res[10] + +def blub(): + a = [] + a.append(1.0) + #? float() + a[0]