diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index c571863b..14bbf9c4 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -237,7 +237,8 @@ class Script(object): name = self._get_module_node().name_for_position(self._pos) if name is None: return [] - return list(self._evaluator.goto(name)) + context = self._evaluator.create_context(self._get_module(), name) + return list(self._evaluator.goto(context, name)) def usages(self, additional_module_paths=()): """ diff --git a/jedi/api/classes.py b/jedi/api/classes.py index a91f95af..9ac26bbb 100644 --- a/jedi/api/classes.py +++ b/jedi/api/classes.py @@ -145,10 +145,7 @@ class BaseDefinition(object): 'function' """ - try: - return self._name.parent_context.api_type - except AttributeError: - return '' + return self._name.api_type def _path(self): """The path to a module/class/function definition.""" @@ -418,7 +415,7 @@ class Completion(BaseDefinition): """ Return the rest of the word, e.g. completing ``isinstance``:: - isinstan# <-- Cursor is here + # <-- Cursor is here would return the string 'ce'. It also adds additional stuff, depending on your `settings.py`. @@ -561,6 +558,36 @@ class Definition(use_metaclass(CachedMetaClass, BaseDefinition)): 'class C' """ + typ = self.type + if typ in ('statement', 'param'): + definition = self._name.tree_name.get_definition() + + try: + first_leaf = definition.first_leaf() + except AttributeError: + # `d` is already a Leaf (Name). + first_leaf = definition + # Remove the prefix, because that's not what we want for get_code + # here. + old, first_leaf.prefix = first_leaf.prefix, '' + try: + txt = definition.get_code() + finally: + first_leaf.prefix = old + # Delete comments: + txt = re.sub('#[^\n]+\n', ' ', txt) + # Delete multi spaces/newlines + txt = re.sub('\s+', ' ', txt).strip() + if typ == 'param': + txt = typ + ' ' + txt + return txt + if typ == 'function': + # For the description we want a short and a pythonic way. + typ = 'def' + return typ + ' ' + self._name.string_name + + # TODO DELETE + d = self._definition if isinstance(d, compiled.CompiledObject): diff --git a/jedi/api/keywords.py b/jedi/api/keywords.py index 82a16cdf..461cc088 100644 --- a/jedi/api/keywords.py +++ b/jedi/api/keywords.py @@ -76,7 +76,7 @@ class KeywordName(AbstractNameDefinition): class Keyword(object): - type = 'completion_keyword' + api_type = 'keyword' def __init__(self, evaluator, name, pos): self.name = KeywordName(evaluator, name) diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 850dce7c..735c6596 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -66,6 +66,7 @@ from itertools import chain from jedi.parser import tree from jedi import debug +from jedi.common import unite from jedi.evaluate import representation as er from jedi.evaluate import imports from jedi.evaluate import recursion @@ -77,7 +78,7 @@ from jedi.evaluate import compiled from jedi.evaluate import precedence from jedi.evaluate import param from jedi.evaluate import helpers -from jedi.evaluate.context import Context +from jedi.evaluate.filters import TreeNameDefinition from jedi.evaluate.instance import AnonymousInstance, AnonymousInstanceFunctionExecution @@ -461,36 +462,43 @@ class Evaluator(object): param_names = [] for typ in types: try: - params = typ.params + get_param_names = typ.get_param_names except AttributeError: pass else: - param_names += [param.name for param in params - if param.name.value == name.value] + for param_name in get_param_names(): + if param_name.string_name == name.value: + param_names.append(param_name) return param_names elif isinstance(par, tree.ExprStmt) and name in par.get_defined_names(): # Only take the parent, because if it's more complicated than just # a name it's something you can "goto" again. - return [name] + return [TreeNameDefinition(context, name)] elif isinstance(par, (tree.Param, tree.Function, tree.Class)) and par.name is name: - return [name] + return [TreeNameDefinition(context, name)] elif isinstance(stmt, tree.Import): - modules = imports.ImportWrapper(context, name).follow(is_goto=True) - return list(resolve_implicit_imports(modules)) + module_names = imports.ImportWrapper(context, name).follow(is_goto=True) + return module_names + return list(resolve_implicit_imports(module_names)) elif par.type == 'dotted_name': # Is a decorator. index = par.children.index(name) if index > 0: new_dotted = helpers.deep_ast_copy(par) new_dotted.children[index - 1:] = [] - types = self.eval_element(context, new_dotted) + values = self.eval_element(context, new_dotted) + return unite( + self.find_types(value, name, is_goto=True) for value in values + ) return resolve_implicit_imports(iterable.unite( self.find_types(typ, name, is_goto=True) for typ in types )) - scope = name.get_parent_scope() if tree.is_node(par, 'trailer') and par.children[0] == '.': call = helpers.call_of_leaf(name, cut_own_trailer=True) - types = self.eval_element(context, call) + values = self.eval_element(context, call) + return unite( + self.find_types(value, name, is_goto=True) for value in values + ) return resolve_implicit_imports(iterable.unite( self.find_types(typ, name, is_goto=True) for typ in types )) @@ -499,7 +507,7 @@ class Evaluator(object): # We only need to adjust the start_pos for statements, because # there the name cannot be used. stmt = name - return self.find_types(scope, name, stmt.start_pos, + return self.find_types(context, name, stmt.start_pos, search_global=True, is_goto=True) def wrap(self, element, parent_context): diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index 2e1de682..b1571dce 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -85,7 +85,8 @@ class CompiledObject(Context): return inspect.getdoc(self.obj) or '' @property - def params(self): + def get_params(self): + return [] # TODO Fix me. params_str, ret = self._parse_function_doc() tokens = params_str.split(',') if inspect.ismethoddescriptor(self.obj): @@ -280,6 +281,9 @@ class CompiledName(AbstractNameDefinition): name = None return '<%s: (%s).%s>' % (self.__class__.__name__, name, self.string_name) + def api_type(self): + return self.infer()[0].api_type + @underscore_memoization def infer(self): module = self.parent_context.get_root_context() @@ -291,6 +295,10 @@ class CompiledContextName(AbstractNameDefinition): self.string_name = name self.parent_context = parent_context + @property + def api_type(self): + return self.parent_context.api_type + def infer(self): return [self.parent_context] diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index c326589c..1b584671 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -51,6 +51,10 @@ class ContextName(AbstractNameDefinition): def infer(self): return [self.parent_context] + @property + def api_type(self): + return self.parent_context.api_type + class TreeNameDefinition(ContextName): def get_parent_flow_context(self): @@ -61,8 +65,20 @@ class TreeNameDefinition(ContextName): from jedi.evaluate.finder import _name_to_types return _name_to_types(self.parent_context.evaluator, self.parent_context, self.tree_name) + @property + def api_type(self): + definition = self.tree_name.get_definition() + return dict( + import_name='import', + funcdef='function', + param='param', + classdef='class', + ).get(definition.type, 'statement') + class ParamName(ContextName): + api_type = 'param' + def __init__(self, parent_context, tree_name): self.parent_context = parent_context self.tree_name = tree_name diff --git a/jedi/evaluate/helpers.py b/jedi/evaluate/helpers.py index 914a5886..77e6d2fa 100644 --- a/jedi/evaluate/helpers.py +++ b/jedi/evaluate/helpers.py @@ -144,7 +144,6 @@ class FakeName(tree.Name): In case is_definition is defined (not None), that bool value will be returned. """ - raise NotImplementedError super(FakeName, self).__init__(name_str, start_pos) self.parent = parent self._is_definition = is_definition diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index 893879a1..6c50af43 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -68,6 +68,8 @@ class SpecialMethodFilter(DictFilter): classes like Generator (for __next__, etc). """ class SpecialMethodName(AbstractNameDefinition): + api_type = 'function' + def __init__(self, parent_context, string_name, callable_, builtin_context): self.parent_context = parent_context self.string_name = string_name diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index a500e2a9..0425cead 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -54,7 +54,8 @@ from jedi.evaluate import param from jedi.evaluate import flow_analysis from jedi.evaluate import imports from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \ - GlobalNameFilter, DictFilter, ContextName, AbstractNameDefinition + GlobalNameFilter, DictFilter, ContextName, AbstractNameDefinition, \ + ParamName, AnonymousInstanceParamName from jedi.evaluate.dynamic import search_params from jedi.evaluate import context @@ -470,12 +471,10 @@ class ClassContext(use_metaclass(CachedMetaClass, context.TreeContext, Wrapper)) def py__class__(self): return compiled.create(self.evaluator, type) - @property - def params(self): - try: - return self.get_subscope_by_name('__init__').params - except KeyError: - return [] # object.__init__ + def get_params(self): + from jedi.evaluate.instance import AnonymousInstance + anon = AnonymousInstance(self.evaluator, self.parent_context, self) + return [AnonymousInstanceParamName(anon, param.name) for param in self.funcdef.params] def names_dicts(self, search_global, is_instance=False): if search_global: @@ -509,6 +508,13 @@ class ClassContext(use_metaclass(CachedMetaClass, context.TreeContext, Wrapper)) return sub raise KeyError("Couldn't find subscope.") + def get_function_slot_names(self, name): + for filter in self.get_filters(search_global=False): + names = filter.get(name) + if names: + return names + return [] + def __repr__(self): return "<%s of %s>" % (self.__class__.__name__, self.classdef) @@ -582,6 +588,14 @@ class FunctionContext(use_metaclass(CachedMetaClass, context.TreeContext, Wrappe def name(self): return ContextName(self, self.funcdef.name) + def get_param_names(self): + anon = AnonymousFunctionExecution( + self.evaluator, + self.parent_context, + self.funcdef + ) + return [ParamName(anon, param.name) for param in self.funcdef.params] + class LambdaWrapper(FunctionContext): def get_decorated_func(self): @@ -747,6 +761,8 @@ class ModuleAttributeName(AbstractNameDefinition): """ For module attributes like __file__, __str__ and so on. """ + api_type = 'instance' + def __init__(self, parent_module, string_name): self.parent_context = parent_module self.string_name = string_name @@ -758,8 +774,9 @@ class ModuleAttributeName(AbstractNameDefinition): class SubModuleName(AbstractNameDefinition): - """ - """ + api_type = 'module' + start_pos = (1, 0) + def __init__(self, parent_module, string_name): self.parent_context = parent_module self.string_name = string_name @@ -774,6 +791,7 @@ class SubModuleName(AbstractNameDefinition): class ModuleContext(use_metaclass(CachedMetaClass, context.TreeContext, Wrapper)): + api_type = 'module' parent_context = None def __init__(self, evaluator, module_node): diff --git a/test/completion/goto.py b/test/completion/goto.py index ef9938cf..eb9fc114 100644 --- a/test/completion/goto.py +++ b/test/completion/goto.py @@ -44,7 +44,7 @@ class C(object): self.b #! 14 ['def b'] self.b() - #! 11 ['self'] + #! 11 ['param self'] self.b return 1 @@ -99,7 +99,7 @@ ClassVar().x = '' # Recurring use of the same var name, github #315 def f(t=None): - #! 9 ['t=None'] + #! 9 ['param t=None'] t = t or 1 @@ -180,9 +180,9 @@ class ClassDef(): # ----------------- param = ClassDef -#! 8 ['param'] +#! 8 ['param param'] def ab1(param): pass -#! 9 ['param'] +#! 9 ['param param'] def ab2(param): pass #! 11 ['param = ClassDef'] def ab3(a=param): pass @@ -211,7 +211,7 @@ for i in []: def dec(dec_param=3): pass -#! 8 ['dec_param=3'] +#! 8 ['param dec_param=3'] @dec(dec_param=5) def y(): pass