From 3008b96e44efa3bb2d8ed5e33793c874aad0e289 Mon Sep 17 00:00:00 2001 From: David Halter Date: Sun, 9 Dec 2012 17:58:22 +0100 Subject: [PATCH] follow_definition function for completions. fixes #54 --- jedi/api_classes.py | 37 +++++++++++++++++++++++++++++++------ test/regression.py | 10 ++++++---- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/jedi/api_classes.py b/jedi/api_classes.py index 3fb3d6f0..711a7c81 100644 --- a/jedi/api_classes.py +++ b/jedi/api_classes.py @@ -5,11 +5,12 @@ import os import settings import evaluate +import imports import parsing import keywords -class BaseOutput(object): +class BaseDefinition(object): _mapping = {'posixpath': 'os.path', 'riscospath': 'os.path', 'ntpath': 'os.path', @@ -41,7 +42,7 @@ class BaseOutput(object): stripped = self.definition if isinstance(self.definition, evaluate.InstanceElement): stripped = self.definition.var - self.type = type(stripped).__name__ + return type(stripped).__name__ @property def path(self): @@ -92,7 +93,7 @@ class BaseOutput(object): @property def description(self): - raise NotImplementedError('Base Class') + return str(self.definition) @property def full_name(self): @@ -115,7 +116,7 @@ class BaseOutput(object): return "<%s %s>" % (type(self).__name__, self.description) -class Completion(BaseOutput): +class Completion(BaseDefinition): """ `Completion` objects are returned from `Script.complete`. Providing some useful functions for IDE's. """ def __init__(self, name, needs_dot, like_name_length, base): @@ -126,6 +127,8 @@ class Completion(BaseOutput): self.like_name_length = like_name_length self.base = base + self._followed_definitions = None + @property def complete(self): """ Delievers the rest of the word, e.g. completing `isinstance` @@ -172,11 +175,33 @@ class Completion(BaseOutput): line_nr = '' if self.in_builtin_module else '@%s' % self.line_nr return '%s: %s%s' % (t, desc, line_nr) + def follow_definition(self): + """ Returns you the original definitions. I strongly recommend not + using it for your completions, because it might slow down Jedi. If you + want to read only a few objects (<=20). I think it might be useful, + especially to get the original docstrings. + The basic problem of this function is that it follows all results. This + means with 1000 completions (e.g. numpy), it's just PITA slow. + """ + if self._followed_definitions is None: + if self.definition.isinstance(parsing.Statement): + defs = evaluate.follow_statement(self.definition) + elif self.definition.isinstance(parsing.Import): + defs = imports.strip_imports([self.definition]) + else: + return [self] + + self._followed_definitions = \ + [BaseDefinition(d, start_pos=None) for d in defs] + evaluate.clear_caches() + + return self._followed_definitions + def __repr__(self): return '<%s: %s>' % (type(self).__name__, self.name) -class Definition(BaseOutput): +class Definition(BaseDefinition): """ These are the objects returned by either `Script.goto` or `Script.get_definition`. """ def __init__(self, definition): @@ -223,7 +248,7 @@ class Definition(BaseOutput): return "%s:%s%s" % (self.module_name, self.description, position) -class RelatedName(BaseOutput): +class RelatedName(BaseDefinition): def __init__(self, name_part, scope): super(RelatedName, self).__init__(scope, name_part.start_pos) self.name_part = name_part diff --git a/test/regression.py b/test/regression.py index e4c64db4..2c4e8d50 100755 --- a/test/regression.py +++ b/test/regression.py @@ -6,6 +6,7 @@ import unittest from os.path import abspath, dirname import time import functools +import itertools sys.path.insert(0, abspath(dirname(abspath(__file__)) + '/../jedi')) os.chdir(os.path.dirname(os.path.abspath(__file__)) + '/../jedi') @@ -244,13 +245,14 @@ class TestRegression(Base): s = self.complete("import os; os.P_") assert 'P_NOWAIT' in [i.word for i in s] - def test_follow_imports_if_possible(self): + def test_follow_definition(self): """ github issue #45 """ - s = self.complete("from datetime import timedelta; timedelta") + c = self.complete("from datetime import timedelta; timedelta") # type can also point to import, but there will be additional # attributes - types = [r.type for r in s] - #assert 'Import' not in types and 'Class' in types + objs = itertools.chain.from_iterable(r.follow_definition() for r in c) + types = [o.type for o in objs] + assert 'Import' not in types and 'Class' in types class TestFeature(Base):