8385ed00af
Fix error in powerline (ascii can't decode) Get vim plugins directly from git
311 lines
10 KiB
Python
311 lines
10 KiB
Python
""" The classes returned by the api """
|
|
|
|
import re
|
|
import os
|
|
|
|
import settings
|
|
import evaluate
|
|
import imports
|
|
import parsing
|
|
import keywords
|
|
|
|
|
|
class BaseDefinition(object):
|
|
_mapping = {'posixpath': 'os.path',
|
|
'riscospath': 'os.path',
|
|
'ntpath': 'os.path',
|
|
'os2emxpath': 'os.path',
|
|
'macpath': 'os.path',
|
|
'genericpath': 'os.path',
|
|
'_io': 'io',
|
|
'__builtin__': '',
|
|
'builtins': '',
|
|
}
|
|
|
|
_tuple_mapping = dict((tuple(k.split('.')), v) for (k, v) in {
|
|
'argparse._ActionsContainer': 'argparse.ArgumentParser',
|
|
'_sre.SRE_Match': 're.MatchObject',
|
|
'_sre.SRE_Pattern': 're.RegexObject',
|
|
}.items())
|
|
|
|
def __init__(self, definition, start_pos):
|
|
self.start_pos = start_pos
|
|
self.definition = definition
|
|
self.is_keyword = isinstance(definition, keywords.Keyword)
|
|
|
|
# generate a path to the definition
|
|
self.module_path = str(definition.get_parent_until().path)
|
|
|
|
@property
|
|
def type(self):
|
|
# generate the type
|
|
stripped = self.definition
|
|
if isinstance(self.definition, evaluate.InstanceElement):
|
|
stripped = self.definition.var
|
|
return type(stripped).__name__
|
|
|
|
@property
|
|
def path(self):
|
|
path = []
|
|
if not isinstance(self.definition, keywords.Keyword):
|
|
par = self.definition
|
|
while par is not None:
|
|
try:
|
|
path.insert(0, par.name)
|
|
except AttributeError:
|
|
pass
|
|
par = par.parent
|
|
return path
|
|
|
|
@property
|
|
def module_name(self):
|
|
path = self.module_path
|
|
sep = os.path.sep
|
|
p = re.sub(r'^.*?([\w\d]+)(%s__init__)?.py$' % sep, r'\1', path)
|
|
return p
|
|
|
|
def in_builtin_module(self):
|
|
return not self.module_path.endswith('.py')
|
|
|
|
@property
|
|
def line_nr(self):
|
|
return self.start_pos[0]
|
|
|
|
@property
|
|
def column(self):
|
|
return self.start_pos[1]
|
|
|
|
@property
|
|
def doc(self):
|
|
""" Return a document string for this completion object. """
|
|
try:
|
|
return self.definition.doc
|
|
except AttributeError:
|
|
return self.raw_doc
|
|
|
|
@property
|
|
def raw_doc(self):
|
|
""" Returns the raw docstring `__doc__` for any object """
|
|
try:
|
|
return str(self.definition.docstr)
|
|
except AttributeError:
|
|
return ''
|
|
|
|
@property
|
|
def description(self):
|
|
return str(self.definition)
|
|
|
|
@property
|
|
def full_name(self):
|
|
"""
|
|
Returns the path to a certain class/function, see #61.
|
|
"""
|
|
path = [str(p) for p in self.path]
|
|
# TODO add further checks, the mapping should only occur on stdlib.
|
|
try:
|
|
path[0] = self._mapping[path[0]]
|
|
except KeyError:
|
|
pass
|
|
for key, repl in self._tuple_mapping.items():
|
|
if tuple(path[:len(key)]) == key:
|
|
path = [repl] + path[len(key):]
|
|
|
|
return '.'.join(path if path[0] else path[1:])
|
|
|
|
def __repr__(self):
|
|
return "<%s %s>" % (type(self).__name__, self.description)
|
|
|
|
|
|
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):
|
|
super(Completion, self).__init__(name.parent, name.start_pos)
|
|
|
|
self.name = name
|
|
self.needs_dot = needs_dot
|
|
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`
|
|
>>> isinstan
|
|
|
|
would return the string 'ce'. It also adds additional stuff, depending
|
|
on your `settings.py`
|
|
"""
|
|
dot = '.' if self.needs_dot else ''
|
|
append = ''
|
|
if settings.add_bracket_after_function \
|
|
and self.type == 'Function':
|
|
append = '('
|
|
|
|
if settings.add_dot_after_module:
|
|
if isinstance(self.base, parsing.Module):
|
|
append += '.'
|
|
if isinstance(self.base, parsing.Param):
|
|
append += '='
|
|
return dot + self.name.names[-1][self.like_name_length:] + append
|
|
|
|
@property
|
|
def word(self):
|
|
""" In contrary to `complete` returns the whole word, e.g.
|
|
>>> isinstan
|
|
|
|
would return 'isinstance'.
|
|
"""
|
|
return str(self.name.names[-1])
|
|
|
|
@property
|
|
def description(self):
|
|
""" Provides a description of the completion object
|
|
TODO return value is just __repr__ of some objects, improve! """
|
|
parent = self.name.parent
|
|
if parent is None:
|
|
return ''
|
|
t = self.type
|
|
if t == 'Statement' or t == 'Import':
|
|
desc = self.definition.get_code(False)
|
|
else:
|
|
desc = '.'.join(str(p) for p in self.path)
|
|
|
|
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, d.start_pos) for d in defs]
|
|
evaluate.clear_caches()
|
|
|
|
return self._followed_definitions
|
|
|
|
def __repr__(self):
|
|
return '<%s: %s>' % (type(self).__name__, self.name)
|
|
|
|
|
|
class Definition(BaseDefinition):
|
|
""" These are the objects returned by either `Script.goto` or
|
|
`Script.get_definition`. """
|
|
def __init__(self, definition):
|
|
super(Definition, self).__init__(definition, definition.start_pos)
|
|
|
|
@property
|
|
def description(self):
|
|
""" A description of the Definition object, which is heavily used in
|
|
testing. e.g. for `isinstance` it returns 'def isinstance' """
|
|
d = self.definition
|
|
if isinstance(d, evaluate.InstanceElement):
|
|
d = d.var
|
|
if isinstance(d, evaluate.parsing.Name):
|
|
d = d.parent
|
|
|
|
if isinstance(d, evaluate.Array):
|
|
d = 'class ' + d.type
|
|
elif isinstance(d, (parsing.Class, evaluate.Class, evaluate.Instance)):
|
|
d = 'class ' + str(d.name)
|
|
elif isinstance(d, (evaluate.Function, evaluate.parsing.Function)):
|
|
d = 'def ' + str(d.name)
|
|
elif isinstance(d, evaluate.parsing.Module):
|
|
# only show module name
|
|
d = 'module %s' % self.module_name
|
|
elif self.is_keyword:
|
|
d = 'keyword %s' % d.name
|
|
else:
|
|
d = d.get_code().replace('\n', '')
|
|
return d
|
|
|
|
@property
|
|
def desc_with_module(self):
|
|
""" In addition to the Definition, it also returns the module. Don't
|
|
use it yet, its behaviour may change. If you really need it, talk to me
|
|
TODO add full path. This function is should return a
|
|
module.class.function path. """
|
|
if self.module_path.endswith('.py') \
|
|
and not isinstance(self.definition, parsing.Module):
|
|
position = '@%s' % (self.line_nr)
|
|
else:
|
|
# is a builtin or module
|
|
position = ''
|
|
return "%s:%s%s" % (self.module_name, self.description, position)
|
|
|
|
|
|
class RelatedName(BaseDefinition):
|
|
def __init__(self, name_part, scope):
|
|
super(RelatedName, self).__init__(scope, name_part.start_pos)
|
|
self.name_part = name_part
|
|
self.text = str(name_part)
|
|
self.end_pos = name_part.end_pos
|
|
|
|
@property
|
|
def description(self):
|
|
return "%s@%s,%s" % (self.text, self.start_pos[0], self.start_pos[1])
|
|
|
|
def __eq__(self, other):
|
|
return self.start_pos == other.start_pos \
|
|
and self.module_path == other.module_path
|
|
|
|
def __hash__(self):
|
|
return hash((self.start_pos, self.module_path))
|
|
|
|
|
|
class CallDef(object):
|
|
""" `CallDef` objects is the return value of `Script.get_in_function_call`.
|
|
It knows what functions you are currently in. e.g. `isinstance(` would
|
|
return the `isinstance` function. without `(` it would return nothing."""
|
|
def __init__(self, executable, index, call):
|
|
self.executable = executable
|
|
self.index = index
|
|
self.call = call
|
|
|
|
@property
|
|
def params(self):
|
|
if self.executable.isinstance(evaluate.Function):
|
|
if isinstance(self.executable, evaluate.InstanceElement):
|
|
return self.executable.params[1:]
|
|
return self.executable.params
|
|
else:
|
|
try:
|
|
sub = self.executable.get_subscope_by_name('__init__')
|
|
return sub.params[1:] # ignore self
|
|
except KeyError:
|
|
return []
|
|
|
|
@property
|
|
def bracket_start(self):
|
|
""" The indent of the bracket that is responsible for the last function
|
|
call. """
|
|
c = self.call
|
|
while c.next is not None:
|
|
c = c.next
|
|
return c.name.end_pos
|
|
|
|
@property
|
|
def call_name(self):
|
|
""" The name (e.g. 'isinstance') as a string. """
|
|
return str(self.executable.name)
|
|
|
|
@property
|
|
def module(self):
|
|
return self.executable.get_parent_until()
|
|
|
|
def __repr__(self):
|
|
return '<%s: %s index %s>' % (type(self).__name__, self.executable,
|
|
self.index)
|