mirror of
https://github.com/davidhalter/jedi.git
synced 2026-05-17 13:59:37 +08:00
Merge pull request #149 from tkf/definition-in-call
Fallback to callee definition when definition not found (fixes #131)
This commit is contained in:
+6
-1
@@ -5,7 +5,12 @@ python:
|
|||||||
- 2.7
|
- 2.7
|
||||||
- 3.2
|
- 3.2
|
||||||
install:
|
install:
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.5' ]]; then pip install --use-mirrors simplejson; fi
|
- if [[ $TRAVIS_PYTHON_VERSION == '2.5' ]]; then
|
||||||
|
pip install --use-mirrors simplejson unittest2;
|
||||||
|
fi
|
||||||
|
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then
|
||||||
|
pip install --use-mirrors unittest2;
|
||||||
|
fi
|
||||||
- pip install --use-mirrors nose
|
- pip install --use-mirrors nose
|
||||||
script:
|
script:
|
||||||
- cd test
|
- cd test
|
||||||
|
|||||||
+51
-28
@@ -231,13 +231,37 @@ class Script(object):
|
|||||||
goto_path = self._module.get_path_under_cursor()
|
goto_path = self._module.get_path_under_cursor()
|
||||||
|
|
||||||
context = self._module.get_context()
|
context = self._module.get_context()
|
||||||
|
scopes = set()
|
||||||
|
lower_priority_operators = ('()', '(', ',')
|
||||||
|
"""Operators that could hide callee."""
|
||||||
if next(context) in ('class', 'def'):
|
if next(context) in ('class', 'def'):
|
||||||
scopes = set([self._module.parser.user_scope])
|
scopes = set([self._module.parser.user_scope])
|
||||||
elif not goto_path:
|
elif not goto_path:
|
||||||
op = self._module.get_operator_under_cursor()
|
op = self._module.get_operator_under_cursor()
|
||||||
scopes = set([keywords.get_operator(op, self.pos)] if op else [])
|
if op and op not in lower_priority_operators:
|
||||||
else:
|
scopes = set([keywords.get_operator(op, self.pos)])
|
||||||
scopes = set(self._prepare_goto(goto_path))
|
|
||||||
|
# Fetch definition of callee
|
||||||
|
if not goto_path:
|
||||||
|
(call, _) = self._func_call_and_param_index()
|
||||||
|
if call is not None:
|
||||||
|
while call.next is not None:
|
||||||
|
call = call.next
|
||||||
|
# reset cursor position:
|
||||||
|
(row, col) = call.name.end_pos
|
||||||
|
self.pos = (row, max(col - 1, 0))
|
||||||
|
self._module = modules.ModuleWithCursor(
|
||||||
|
self._source_path,
|
||||||
|
source=self.source,
|
||||||
|
position=self.pos)
|
||||||
|
# then try to find the path again
|
||||||
|
goto_path = self._module.get_path_under_cursor()
|
||||||
|
|
||||||
|
if not scopes:
|
||||||
|
if goto_path:
|
||||||
|
scopes = set(self._prepare_goto(goto_path))
|
||||||
|
elif op in lower_priority_operators:
|
||||||
|
scopes = set([keywords.get_operator(op, self.pos)])
|
||||||
|
|
||||||
scopes = resolve_import_paths(scopes)
|
scopes = resolve_import_paths(scopes)
|
||||||
|
|
||||||
@@ -370,6 +394,25 @@ class Script(object):
|
|||||||
|
|
||||||
:rtype: :class:`api_classes.CallDef`
|
:rtype: :class:`api_classes.CallDef`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
(call, index) = self._func_call_and_param_index()
|
||||||
|
if call is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
user_stmt = self._parser.user_stmt
|
||||||
|
with common.scale_speed_settings(settings.scale_function_definition):
|
||||||
|
_callable = lambda: evaluate.follow_call(call)
|
||||||
|
origins = cache.cache_function_definition(_callable, user_stmt)
|
||||||
|
debug.speed('func_call followed')
|
||||||
|
|
||||||
|
if len(origins) == 0:
|
||||||
|
return None
|
||||||
|
# just take entry zero, because we need just one.
|
||||||
|
executable = origins[0]
|
||||||
|
|
||||||
|
return api_classes.CallDef(executable, index, call)
|
||||||
|
|
||||||
|
def _func_call_and_param_index(self):
|
||||||
def check_user_stmt(user_stmt):
|
def check_user_stmt(user_stmt):
|
||||||
if user_stmt is None \
|
if user_stmt is None \
|
||||||
or not isinstance(user_stmt, pr.Statement):
|
or not isinstance(user_stmt, pr.Statement):
|
||||||
@@ -404,37 +447,17 @@ class Script(object):
|
|||||||
if repr(old_call) == repr(call):
|
if repr(old_call) == repr(call):
|
||||||
# return the index of the part_parser
|
# return the index of the part_parser
|
||||||
return old_call, index
|
return old_call, index
|
||||||
return None, 0
|
return None, 0
|
||||||
else:
|
|
||||||
raise NotFoundError()
|
|
||||||
|
|
||||||
debug.speed('func_call start')
|
debug.speed('func_call start')
|
||||||
call = None
|
call = None
|
||||||
|
index = 0
|
||||||
if settings.use_function_definition_cache:
|
if settings.use_function_definition_cache:
|
||||||
try:
|
call, index = check_cache()
|
||||||
call, index = check_cache()
|
|
||||||
except NotFoundError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
user_stmt = self._parser.user_stmt
|
|
||||||
if call is None:
|
if call is None:
|
||||||
# This is a backup, if the above is not successful.
|
call, index = check_user_stmt(self._parser.user_stmt)
|
||||||
call, index = check_user_stmt(user_stmt)
|
|
||||||
if call is None:
|
|
||||||
return None
|
|
||||||
debug.speed('func_call parsed')
|
debug.speed('func_call parsed')
|
||||||
|
return call, index
|
||||||
with common.scale_speed_settings(settings.scale_function_definition):
|
|
||||||
_callable = lambda: evaluate.follow_call(call)
|
|
||||||
origins = cache.cache_function_definition(_callable, user_stmt)
|
|
||||||
debug.speed('func_call followed')
|
|
||||||
|
|
||||||
if len(origins) == 0:
|
|
||||||
return None
|
|
||||||
# just take entry zero, because we need just one.
|
|
||||||
executable = origins[0]
|
|
||||||
|
|
||||||
return api_classes.CallDef(executable, index, call)
|
|
||||||
|
|
||||||
def _get_on_import_stmt(self, is_like_search=False):
|
def _get_on_import_stmt(self, is_like_search=False):
|
||||||
""" Resolve the user statement, if it is an import. Only resolve the
|
""" Resolve the user statement, if it is an import. Only resolve the
|
||||||
|
|||||||
+4
-2
@@ -1,7 +1,9 @@
|
|||||||
import unittest
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
|
if sys.hexversion < 0x02070000:
|
||||||
|
import unittest2 as unittest
|
||||||
|
else:
|
||||||
|
import unittest
|
||||||
import os
|
import os
|
||||||
from os.path import abspath, dirname
|
from os.path import abspath, dirname
|
||||||
import functools
|
import functools
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
"""
|
||||||
|
Fallback to callee definition when definition not found.
|
||||||
|
- https://github.com/davidhalter/jedi/issues/131
|
||||||
|
- https://github.com/davidhalter/jedi/pull/149
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""Parenthesis closed at next line."""
|
||||||
|
|
||||||
|
#? isinstance
|
||||||
|
isinstance(
|
||||||
|
)
|
||||||
|
|
||||||
|
#? isinstance
|
||||||
|
isinstance(
|
||||||
|
)
|
||||||
|
|
||||||
|
#? isinstance
|
||||||
|
isinstance(None,
|
||||||
|
)
|
||||||
|
|
||||||
|
#? isinstance
|
||||||
|
isinstance(None,
|
||||||
|
)
|
||||||
|
|
||||||
|
"""Parenthesis closed at same line."""
|
||||||
|
|
||||||
|
# Note: len('isinstance(') == 11
|
||||||
|
#? 11 isinstance
|
||||||
|
isinstance()
|
||||||
|
|
||||||
|
# Note: len('isinstance(None,') == 16
|
||||||
|
##? 16 isinstance
|
||||||
|
isinstance(None,)
|
||||||
|
|
||||||
|
# Note: len('isinstance(None,') == 16
|
||||||
|
##? 16 isinstance
|
||||||
|
isinstance(None, )
|
||||||
|
|
||||||
|
# Note: len('isinstance(None, ') == 17
|
||||||
|
##? 17 isinstance
|
||||||
|
isinstance(None, )
|
||||||
|
|
||||||
|
# Note: len('isinstance( ') == 12
|
||||||
|
##? 12 isinstance
|
||||||
|
isinstance( )
|
||||||
|
|
||||||
|
"""Unclosed parenthesis."""
|
||||||
|
|
||||||
|
#? isinstance
|
||||||
|
isinstance(
|
||||||
|
|
||||||
|
def x(): pass # acts like EOF
|
||||||
|
|
||||||
|
##? isinstance
|
||||||
|
isinstance(
|
||||||
|
|
||||||
|
def x(): pass # acts like EOF
|
||||||
|
|
||||||
|
#? isinstance
|
||||||
|
isinstance(None,
|
||||||
|
|
||||||
|
def x(): pass # acts like EOF
|
||||||
|
|
||||||
|
#? isinstance
|
||||||
|
isinstance(None,
|
||||||
+53
-35
@@ -5,17 +5,18 @@ Unit tests to avoid errors of the past. Makes use of Python's ``unittest``
|
|||||||
module.
|
module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import unittest
|
|
||||||
import time
|
import time
|
||||||
import functools
|
import functools
|
||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
|
import textwrap
|
||||||
|
|
||||||
from base import TestBase, cwd_at
|
from base import TestBase, unittest, cwd_at
|
||||||
|
|
||||||
import jedi
|
import jedi
|
||||||
from jedi._compatibility import is_py25, utf8, unicode
|
from jedi._compatibility import is_py25, utf8, unicode
|
||||||
from jedi import api
|
from jedi import api
|
||||||
|
from jedi import api_classes
|
||||||
|
|
||||||
#jedi.set_debug_function(jedi.debug.print_to_stdout)
|
#jedi.set_debug_function(jedi.debug.print_to_stdout)
|
||||||
|
|
||||||
@@ -145,10 +146,15 @@ class TestRegression(TestBase):
|
|||||||
assert self.complete("from datetime import")[0].word == 'import'
|
assert self.complete("from datetime import")[0].word == 'import'
|
||||||
assert self.complete("from datetime import ")
|
assert self.complete("from datetime import ")
|
||||||
|
|
||||||
|
def assert_call_def(self, call_def, name, index):
|
||||||
|
self.assertEqual(
|
||||||
|
{'call_name': getattr(call_def, 'call_name', None),
|
||||||
|
'index': getattr(call_def, 'index', None)},
|
||||||
|
{'call_name': name, 'index': index},
|
||||||
|
)
|
||||||
|
|
||||||
def test_function_definition(self):
|
def test_function_definition(self):
|
||||||
def check(call_def, name, index):
|
check = self.assert_call_def
|
||||||
return call_def and call_def.call_name == name \
|
|
||||||
and call_def.index == index
|
|
||||||
|
|
||||||
# simple
|
# simple
|
||||||
s = "abs(a, str("
|
s = "abs(a, str("
|
||||||
@@ -161,13 +167,13 @@ class TestRegression(TestBase):
|
|||||||
s7 = "str().upper().center("
|
s7 = "str().upper().center("
|
||||||
s8 = "str(int[zip("
|
s8 = "str(int[zip("
|
||||||
|
|
||||||
assert check(self.function_definition(s, (1, 4)), 'abs', 0)
|
check(self.function_definition(s, (1, 4)), 'abs', 0)
|
||||||
assert check(self.function_definition(s, (1, 6)), 'abs', 1)
|
check(self.function_definition(s, (1, 6)), 'abs', 1)
|
||||||
assert check(self.function_definition(s, (1, 7)), 'abs', 1)
|
check(self.function_definition(s, (1, 7)), 'abs', 1)
|
||||||
assert check(self.function_definition(s, (1, 8)), 'abs', 1)
|
check(self.function_definition(s, (1, 8)), 'abs', 1)
|
||||||
assert check(self.function_definition(s, (1, 11)), 'str', 0)
|
check(self.function_definition(s, (1, 11)), 'str', 0)
|
||||||
|
|
||||||
assert check(self.function_definition(s2, (1, 4)), 'abs', 0)
|
check(self.function_definition(s2, (1, 4)), 'abs', 0)
|
||||||
assert self.function_definition(s2, (1, 5)) is None
|
assert self.function_definition(s2, (1, 5)) is None
|
||||||
assert self.function_definition(s2) is None
|
assert self.function_definition(s2) is None
|
||||||
|
|
||||||
@@ -175,43 +181,41 @@ class TestRegression(TestBase):
|
|||||||
assert self.function_definition(s3) is None
|
assert self.function_definition(s3) is None
|
||||||
|
|
||||||
assert self.function_definition(s4, (1, 3)) is None
|
assert self.function_definition(s4, (1, 3)) is None
|
||||||
assert check(self.function_definition(s4, (1, 4)), 'abs', 0)
|
check(self.function_definition(s4, (1, 4)), 'abs', 0)
|
||||||
assert check(self.function_definition(s4, (1, 8)), 'zip', 0)
|
check(self.function_definition(s4, (1, 8)), 'zip', 0)
|
||||||
assert check(self.function_definition(s4, (1, 9)), 'abs', 0)
|
check(self.function_definition(s4, (1, 9)), 'abs', 0)
|
||||||
#assert check(self.function_definition(s4, (1, 10)), 'abs', 1)
|
#check(self.function_definition(s4, (1, 10)), 'abs', 1)
|
||||||
|
|
||||||
assert check(self.function_definition(s5, (1, 4)), 'abs', 0)
|
check(self.function_definition(s5, (1, 4)), 'abs', 0)
|
||||||
assert check(self.function_definition(s5, (1, 6)), 'abs', 1)
|
check(self.function_definition(s5, (1, 6)), 'abs', 1)
|
||||||
|
|
||||||
assert check(self.function_definition(s6), 'center', 0)
|
check(self.function_definition(s6), 'center', 0)
|
||||||
assert check(self.function_definition(s6, (1, 4)), 'str', 0)
|
check(self.function_definition(s6, (1, 4)), 'str', 0)
|
||||||
|
|
||||||
assert check(self.function_definition(s7), 'center', 0)
|
check(self.function_definition(s7), 'center', 0)
|
||||||
assert check(self.function_definition(s8), 'zip', 0)
|
check(self.function_definition(s8), 'zip', 0)
|
||||||
assert check(self.function_definition(s8, (1, 8)), 'str', 0)
|
check(self.function_definition(s8, (1, 8)), 'str', 0)
|
||||||
|
|
||||||
s = "import time; abc = time; abc.sleep("
|
s = "import time; abc = time; abc.sleep("
|
||||||
assert check(self.function_definition(s), 'sleep', 0)
|
check(self.function_definition(s), 'sleep', 0)
|
||||||
|
|
||||||
# jedi-vim #9
|
# jedi-vim #9
|
||||||
s = "with open("
|
s = "with open("
|
||||||
assert check(self.function_definition(s), 'open', 0)
|
check(self.function_definition(s), 'open', 0)
|
||||||
|
|
||||||
# jedi-vim #11
|
# jedi-vim #11
|
||||||
s1 = "for sorted("
|
s1 = "for sorted("
|
||||||
assert check(self.function_definition(s1), 'sorted', 0)
|
check(self.function_definition(s1), 'sorted', 0)
|
||||||
s2 = "for s in sorted("
|
s2 = "for s in sorted("
|
||||||
assert check(self.function_definition(s2), 'sorted', 0)
|
check(self.function_definition(s2), 'sorted', 0)
|
||||||
|
|
||||||
# jedi #57
|
# jedi #57
|
||||||
s = "def func(alpha, beta): pass\n" \
|
s = "def func(alpha, beta): pass\n" \
|
||||||
"func(alpha='101',"
|
"func(alpha='101',"
|
||||||
assert check(self.function_definition(s, (2, 13)), 'func', 0)
|
check(self.function_definition(s, (2, 13)), 'func', 0)
|
||||||
|
|
||||||
def test_function_definition_complex(self):
|
def test_function_definition_complex(self):
|
||||||
def check(call_def, name, index):
|
check = self.assert_call_def
|
||||||
return call_def and call_def.call_name == name \
|
|
||||||
and call_def.index == index
|
|
||||||
|
|
||||||
s = """
|
s = """
|
||||||
def abc(a,b):
|
def abc(a,b):
|
||||||
@@ -223,18 +227,27 @@ class TestRegression(TestBase):
|
|||||||
if 1:
|
if 1:
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
assert check(self.function_definition(s, (6, 24)), 'abc', 0)
|
check(self.function_definition(s, (6, 24)), 'abc', 0)
|
||||||
s = """
|
s = """
|
||||||
import re
|
import re
|
||||||
def huhu(it):
|
def huhu(it):
|
||||||
re.compile(
|
re.compile(
|
||||||
return it * 2
|
return it * 2
|
||||||
"""
|
"""
|
||||||
assert check(self.function_definition(s, (4, 31)), 'compile', 0)
|
check(self.function_definition(s, (4, 31)), 'compile', 0)
|
||||||
# jedi-vim #70
|
# jedi-vim #70
|
||||||
s = """def foo("""
|
s = """def foo("""
|
||||||
assert self.function_definition(s) is None
|
assert self.function_definition(s) is None
|
||||||
|
|
||||||
|
@unittest.skip("function_definition at ``f( |)`` does not work")
|
||||||
|
def test_function_definition_empty_paren_pre_space(self):
|
||||||
|
s = textwrap.dedent("""\
|
||||||
|
def f(a, b):
|
||||||
|
pass
|
||||||
|
f( )""")
|
||||||
|
call_def = self.function_definition(s, (3, 3))
|
||||||
|
self.assert_call_def(call_def, 'f', 0)
|
||||||
|
|
||||||
@cwd_at('jedi')
|
@cwd_at('jedi')
|
||||||
def test_add_dynamic_mods(self):
|
def test_add_dynamic_mods(self):
|
||||||
api.settings.additional_dynamic_modules = ['dynamic.py']
|
api.settings.additional_dynamic_modules = ['dynamic.py']
|
||||||
@@ -384,9 +397,14 @@ class TestFeature(TestBase):
|
|||||||
""" feature request #61"""
|
""" feature request #61"""
|
||||||
assert self.complete('import os; os.path.join')[0].full_name \
|
assert self.complete('import os; os.path.join')[0].full_name \
|
||||||
== 'os.path.join'
|
== 'os.path.join'
|
||||||
# issue #94
|
|
||||||
defs = self.definition("""import os; os.path.join(""")
|
def test_keyword_full_name_should_be_none(self):
|
||||||
assert defs[0].full_name is None
|
"""issue #94"""
|
||||||
|
# Using `from jedi.keywords import Keyword` here does NOT work
|
||||||
|
# in Python 3. This is due to the import hack jedi using.
|
||||||
|
Keyword = api_classes.keywords.Keyword
|
||||||
|
d = api_classes.Definition(Keyword('(', (0, 0)))
|
||||||
|
assert d.full_name is None
|
||||||
|
|
||||||
def test_full_name_builtin(self):
|
def test_full_name_builtin(self):
|
||||||
self.assertEqual(self.complete('type')[0].full_name, 'type')
|
self.assertEqual(self.complete('type')[0].full_name, 'type')
|
||||||
|
|||||||
Reference in New Issue
Block a user