mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-07 14:34:31 +08:00
introduced a dynamic completion for params
This commit is contained in:
68
dynamic.py
Normal file
68
dynamic.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
"""
|
||||||
|
For dynamic completion.
|
||||||
|
"""
|
||||||
|
import parsing
|
||||||
|
import evaluate
|
||||||
|
|
||||||
|
# This is something like the sys.path, but only for searching params. It means
|
||||||
|
# that this is the order in which Jedi searches params.
|
||||||
|
search_param_modules = ['.']
|
||||||
|
|
||||||
|
def search_params(param):
|
||||||
|
def scan_array(arr):
|
||||||
|
""" Returns the function Calls that match func_name """
|
||||||
|
result = []
|
||||||
|
for sub in arr:
|
||||||
|
for s in sub:
|
||||||
|
if isinstance(s, parsing.Array):
|
||||||
|
result += scan_array(s)
|
||||||
|
elif isinstance(s, parsing.Call):
|
||||||
|
if str(s.name) == func_name:
|
||||||
|
result.append(s)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_params_for_module(module):
|
||||||
|
result = []
|
||||||
|
try:
|
||||||
|
possible_stmts = current_module.used_names[func_name]
|
||||||
|
except KeyError:
|
||||||
|
return []
|
||||||
|
|
||||||
|
calls = []
|
||||||
|
for stmt in possible_stmts:
|
||||||
|
calls += scan_array(stmt.get_assignment_calls())
|
||||||
|
|
||||||
|
for c in calls:
|
||||||
|
if not c.execution:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# now check if the call is actually the same method
|
||||||
|
c.execution, temp = None, c.execution
|
||||||
|
possible_executions = evaluate.follow_call(c)
|
||||||
|
is_same_method = False
|
||||||
|
for e in possible_executions:
|
||||||
|
is_same_method = e == func \
|
||||||
|
or isinstance(e, evaluate.Function) and e.base_func == func
|
||||||
|
if not is_same_method:
|
||||||
|
continue
|
||||||
|
c.execution = temp
|
||||||
|
|
||||||
|
try:
|
||||||
|
p = c.execution[param_nr]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
result += evaluate.follow_call_list([p])
|
||||||
|
return result
|
||||||
|
|
||||||
|
func = param.get_parent_until(parsing.Function)
|
||||||
|
func_name = str(func.name)
|
||||||
|
|
||||||
|
current_module = param.get_parent_until()
|
||||||
|
for i, p in enumerate(func.params):
|
||||||
|
param_nr = i
|
||||||
|
|
||||||
|
result = get_params_for_module(current_module)
|
||||||
|
|
||||||
|
# TODO check other modules
|
||||||
|
return result
|
||||||
10
evaluate.py
10
evaluate.py
@@ -31,6 +31,7 @@ import debug
|
|||||||
import builtin
|
import builtin
|
||||||
import imports
|
import imports
|
||||||
import helpers
|
import helpers
|
||||||
|
import dynamic
|
||||||
|
|
||||||
memoize_caches = []
|
memoize_caches = []
|
||||||
statement_path = []
|
statement_path = []
|
||||||
@@ -476,6 +477,7 @@ class Execution(Executable):
|
|||||||
new_param.parent = parent_stmt
|
new_param.parent = parent_stmt
|
||||||
new_param._assignment_calls_calculated = True
|
new_param._assignment_calls_calculated = True
|
||||||
new_param._assignment_calls = calls
|
new_param._assignment_calls = calls
|
||||||
|
new_param.is_generated = True
|
||||||
name = copy.copy(param.get_name())
|
name = copy.copy(param.get_name())
|
||||||
name.parent = new_param
|
name.parent = new_param
|
||||||
return name
|
return name
|
||||||
@@ -887,6 +889,14 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False):
|
|||||||
res_new += get_scopes_for_name(r.parent,
|
res_new += get_scopes_for_name(r.parent,
|
||||||
str(token_name))
|
str(token_name))
|
||||||
else:
|
else:
|
||||||
|
# generated objects are used within executions, where
|
||||||
|
if isinstance(r, parsing.Param) and not r.is_generated:
|
||||||
|
res_new += dynamic.search_params(r)
|
||||||
|
if not r.assignment_details:
|
||||||
|
# this means that there are no default params, so
|
||||||
|
# just ignore it.
|
||||||
|
continue
|
||||||
|
|
||||||
scopes = follow_statement(r, seek_name=name_str)
|
scopes = follow_statement(r, seek_name=name_str)
|
||||||
res_new += remove_statements(scopes)
|
res_new += remove_statements(scopes)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import evaluate
|
|||||||
import modules
|
import modules
|
||||||
import debug
|
import debug
|
||||||
import imports
|
import imports
|
||||||
|
# TODO use os.path.sep and similar things
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
17
parsing.py
17
parsing.py
@@ -712,6 +712,7 @@ class Statement(Simple):
|
|||||||
raise ParserError("Brackets don't match: %s. This is not normal "
|
raise ParserError("Brackets don't match: %s. This is not normal "
|
||||||
"behaviour. Please submit a bug" % level)
|
"behaviour. Please submit a bug" % level)
|
||||||
|
|
||||||
|
self._assignment_calls_calculated = True
|
||||||
self._assignment_calls = top
|
self._assignment_calls = top
|
||||||
return top
|
return top
|
||||||
|
|
||||||
@@ -729,6 +730,7 @@ class Param(Statement):
|
|||||||
# this is defined by the parser later on, not at the initialization
|
# this is defined by the parser later on, not at the initialization
|
||||||
# it is the position in the call (first argument, second...)
|
# it is the position in the call (first argument, second...)
|
||||||
self.position = None
|
self.position = None
|
||||||
|
self.is_generated = False
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
""" get the name of the param """
|
""" get the name of the param """
|
||||||
@@ -929,16 +931,6 @@ class Name(Simple):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.get_code()
|
return self.get_code()
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return self.names == other.names and self.start_pos == other.end_pos
|
|
||||||
|
|
||||||
def __ne__(self, other):
|
|
||||||
return not self.__eq__(other)
|
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
return hash(self.names) + hash(self.start_pos[0]) \
|
|
||||||
+ hash(self.start_pos[1])
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.names)
|
return len(self.names)
|
||||||
|
|
||||||
@@ -1276,7 +1268,10 @@ class PyFuzzyParser(object):
|
|||||||
self._check_user_stmt(stmt)
|
self._check_user_stmt(stmt)
|
||||||
if not isinstance(stmt, Param):
|
if not isinstance(stmt, Param):
|
||||||
for tok_name in self.module.temp_used_names:
|
for tok_name in self.module.temp_used_names:
|
||||||
self.module.used_names[tok_name] = stmt
|
try:
|
||||||
|
self.module.used_names[tok_name].append(stmt)
|
||||||
|
except KeyError:
|
||||||
|
self.module.used_names[tok_name] = [stmt]
|
||||||
self.module.temp_used_names = []
|
self.module.temp_used_names = []
|
||||||
if is_return:
|
if is_return:
|
||||||
# add returns to the scope
|
# add returns to the scope
|
||||||
|
|||||||
14
test/completion/dynamic.py
Normal file
14
test/completion/dynamic.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
"""
|
||||||
|
This is used for dynamic object completion.
|
||||||
|
Jedi tries to guess the types with a backtracking approach.
|
||||||
|
"""
|
||||||
|
def func(a):
|
||||||
|
#? int() str()
|
||||||
|
return a
|
||||||
|
|
||||||
|
#? int()
|
||||||
|
func(1)
|
||||||
|
|
||||||
|
func
|
||||||
|
|
||||||
|
int(1) + (int(2))+ func('')
|
||||||
@@ -70,6 +70,12 @@ def a(a=3):
|
|||||||
a(2)
|
a(2)
|
||||||
#? []
|
#? []
|
||||||
a(2).func
|
a(2).func
|
||||||
|
|
||||||
|
a_param = 3
|
||||||
|
def func(a_param):
|
||||||
|
# should not be int
|
||||||
|
#? []
|
||||||
|
a_param.
|
||||||
# -----------------
|
# -----------------
|
||||||
# class
|
# class
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|||||||
Reference in New Issue
Block a user