diff --git a/jedi/api/completion.py b/jedi/api/completion.py index 0602d489a..d0aff2200 100644 --- a/jedi/api/completion.py +++ b/jedi/api/completion.py @@ -157,6 +157,12 @@ def _get_context_completions(self): elif nodes and nodes[-1] in ('as', 'def', 'class'): # No completions for ``with x as foo`` and ``import x as foo``. # Also true for defining names as a class or function. + leaf = self._module.get_leaf_for_position(self._position) + cls = leaf.get_parent_until(tree.Class) + if isinstance(cls, (tree.Class, tree.Function)): + # Complete the methods that are defined in the super classes. + cls = self._evaluator.wrap(cls) + return list(self._get_class_context_completions(cls)) return [] elif symbol_names[-1] == 'trailer' and nodes[-1] == '.': dot = self._module.get_leaf_for_position(self._position) @@ -229,3 +235,16 @@ def _get_importer_names(self, names, level=0, only_modules=True): names = [str(n) for n in names] i = imports.Importer(self._evaluator, names, self._module, level) return i.completion_names(self._evaluator, only_modules=only_modules) + + def _get_class_context_completions(self, cls): + """ + Autocomplete inherited methods when overriding in child class. + """ + names_dicts = cls.names_dicts(search_global=False, is_instance=True) + # The first dict is the dictionary of class itself. + next(names_dicts) + for names_dict in names_dicts: + for values in names_dict.values(): + for value in values: + if value.parent.type == 'funcdef': + yield value diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index 2660734bc..bc32e8edb 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -123,8 +123,8 @@ def type(self): return 'classdef' elif inspect.ismodule(cls): return 'file_input' - elif inspect.isbuiltin(cls) or inspect.ismethod(cls) \ - or inspect.ismethoddescriptor(cls): + elif inspect.isbuiltin(cls) or inspect.ismethod(cls) or \ + inspect.ismethoddescriptor(cls): return 'funcdef' @underscore_memoization @@ -137,7 +137,8 @@ def _cls(self): return self def _get_class(self): - if not fake.is_class_instance(self.obj): + if not fake.is_class_instance(self.obj) or \ + inspect.ismethoddescriptor(self.obj): # slots return self.obj try: diff --git a/test/blabla_test_documentation.py b/test/blabla_test_documentation.py new file mode 100644 index 000000000..db31bf0ed --- /dev/null +++ b/test/blabla_test_documentation.py @@ -0,0 +1,30 @@ +# todo probably remove test_integration_keyword + +def test_keyword_doc(): + r = list(Script("or", 1, 1).goto_definitions()) + assert len(r) == 1 + assert len(r[0].doc) > 100 + + r = list(Script("asfdasfd", 1, 1).goto_definitions()) + assert len(r) == 0 + + k = Script("fro").completions()[0] + imp_start = '\nThe ``import' + assert k.raw_doc.startswith(imp_start) + assert k.doc.startswith(imp_start) + + +def test_blablabla(): + defs = Script("import").goto_definitions() + assert len(defs) == 1 and [1 for d in defs if d.doc] + # unrelated to #44 + + +def test_operator_doc(self): + r = list(Script("a == b", 1, 3).goto_definitions()) + assert len(r) == 1 + assert len(r[0].doc) > 100 + +def test_lambda(): + defs = Script('lambda x: x', column=0).goto_definitions() + assert [d.type for d in defs] == ['keyword'] diff --git a/test/completion/context.py b/test/completion/context.py new file mode 100644 index 000000000..2566b4deb --- /dev/null +++ b/test/completion/context.py @@ -0,0 +1,22 @@ +class X(): + def func(self, foo): + pass + + +class Y(X): + def actual_function(self): + pass + + #? [] + def actual_function + #? ['func'] + def f + + #? [] + def __class__ + + #? ['__repr__'] + def __repr__ + + #? [] + def mro