forked from VimPlug/jedi
Make join detection much easier
This commit is contained in:
@@ -85,7 +85,7 @@ def get_flow_scope_node(module_node, position):
|
|||||||
|
|
||||||
|
|
||||||
class Completion:
|
class Completion:
|
||||||
def __init__(self, evaluator, module, code_lines, position, call_signatures_method):
|
def __init__(self, evaluator, module, code_lines, position, call_signatures_callback):
|
||||||
self._evaluator = evaluator
|
self._evaluator = evaluator
|
||||||
self._module_context = module
|
self._module_context = module
|
||||||
self._module_node = module.tree_node
|
self._module_node = module.tree_node
|
||||||
@@ -96,14 +96,15 @@ class Completion:
|
|||||||
# The actual cursor position is not what we need to calculate
|
# The actual cursor position is not what we need to calculate
|
||||||
# everything. We want the start of the name we're on.
|
# everything. We want the start of the name we're on.
|
||||||
self._position = position[0], position[1] - len(self._like_name)
|
self._position = position[0], position[1] - len(self._like_name)
|
||||||
self._call_signatures_method = call_signatures_method
|
self._call_signatures_callback = call_signatures_callback
|
||||||
|
|
||||||
def completions(self):
|
def completions(self):
|
||||||
leaf = self._module_node.get_leaf_for_position(self._position, include_prefixes=True)
|
leaf = self._module_node.get_leaf_for_position(self._position, include_prefixes=True)
|
||||||
string, start_leaf = _extract_string_while_in_string(leaf, self._position)
|
string, start_leaf = _extract_string_while_in_string(leaf, self._position)
|
||||||
if string is not None:
|
if string is not None:
|
||||||
completions = list(file_name_completions(
|
completions = list(file_name_completions(
|
||||||
self._evaluator, self._module_context, start_leaf, string, self._like_name
|
self._evaluator, self._module_context, start_leaf, string,
|
||||||
|
self._like_name, self._call_signatures_callback
|
||||||
))
|
))
|
||||||
if completions:
|
if completions:
|
||||||
return completions
|
return completions
|
||||||
@@ -223,7 +224,7 @@ class Completion:
|
|||||||
completion_names += self._get_class_context_completions(is_function=False)
|
completion_names += self._get_class_context_completions(is_function=False)
|
||||||
|
|
||||||
if 'trailer' in nonterminals:
|
if 'trailer' in nonterminals:
|
||||||
call_signatures = self._call_signatures_method()
|
call_signatures = self._call_signatures_callback()
|
||||||
completion_names += get_call_signature_param_names(call_signatures)
|
completion_names += get_call_signature_param_names(call_signatures)
|
||||||
|
|
||||||
return completion_names
|
return completion_names
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ from jedi.evaluate.names import AbstractArbitraryName
|
|||||||
from jedi.api import classes
|
from jedi.api import classes
|
||||||
from jedi.evaluate.helpers import get_str_or_none
|
from jedi.evaluate.helpers import get_str_or_none
|
||||||
from jedi.parser_utils import get_string_quote
|
from jedi.parser_utils import get_string_quote
|
||||||
from jedi.evaluate.syntax_tree import eval_trailer
|
|
||||||
|
|
||||||
|
|
||||||
def file_name_completions(evaluator, module_context, start_leaf, string, like_name):
|
def file_name_completions(evaluator, module_context, start_leaf, string,
|
||||||
|
like_name, call_signatures_callback):
|
||||||
# First we want to find out what can actually be changed as a name.
|
# First we want to find out what can actually be changed as a name.
|
||||||
like_name_length = len(os.path.basename(string) + like_name)
|
like_name_length = len(os.path.basename(string) + like_name)
|
||||||
|
|
||||||
@@ -22,7 +22,14 @@ def file_name_completions(evaluator, module_context, start_leaf, string, like_na
|
|||||||
must_start_with = os.path.basename(string) + like_name
|
must_start_with = os.path.basename(string) + like_name
|
||||||
string = os.path.dirname(string)
|
string = os.path.dirname(string)
|
||||||
|
|
||||||
string, is_in_os_path_join = _maybe_add_os_path_join(module_context, start_leaf, string)
|
sigs = call_signatures_callback()
|
||||||
|
is_in_os_path_join = sigs and all(s.full_name == 'os.path.join' for s in sigs)
|
||||||
|
if is_in_os_path_join:
|
||||||
|
to_be_added = _add_os_path_join(module_context, start_leaf, sigs[0].bracket_start)
|
||||||
|
if to_be_added is None:
|
||||||
|
is_in_os_path_join = False
|
||||||
|
else:
|
||||||
|
string = to_be_added + string
|
||||||
base_path = os.path.join(evaluator.project._path, string)
|
base_path = os.path.join(evaluator.project._path, string)
|
||||||
try:
|
try:
|
||||||
listed = os.listdir(base_path)
|
listed = os.listdir(base_path)
|
||||||
@@ -92,29 +99,15 @@ class FileName(AbstractArbitraryName):
|
|||||||
is_context_name = False
|
is_context_name = False
|
||||||
|
|
||||||
|
|
||||||
def _maybe_add_os_path_join(module_context, start_leaf, string):
|
def _add_os_path_join(module_context, start_leaf, bracket_start):
|
||||||
def check_for_power(atom_or_trailer, nodes):
|
def check(maybe_bracket, nodes):
|
||||||
trailers = []
|
if maybe_bracket.start_pos != bracket_start:
|
||||||
if atom_or_trailer.type not in ('name', 'atom', 'trailer'):
|
return None
|
||||||
return string, False
|
|
||||||
while atom_or_trailer.type == 'trailer':
|
|
||||||
trailers.insert(0, atom_or_trailer)
|
|
||||||
atom_or_trailer = atom_or_trailer.get_previous_sibling()
|
|
||||||
|
|
||||||
context = module_context.create_context(atom_or_trailer)
|
if not nodes:
|
||||||
contexts = context.eval_node(atom_or_trailer)
|
return ''
|
||||||
for trailer in trailers:
|
context = module_context.create_context(nodes[0])
|
||||||
contexts = eval_trailer(context, contexts, trailer)
|
return _add_strings(context, nodes, add_slash=True) or ''
|
||||||
if any([c.name.get_qualified_names(include_module_names=True)
|
|
||||||
!= ('os', 'path', 'join') for c in contexts]):
|
|
||||||
return string, False
|
|
||||||
return _add_strings(context, nodes, add_slash=True) or string, True
|
|
||||||
|
|
||||||
def check_trailer(trailer, arglist_nodes):
|
|
||||||
if trailer.children[0] == '(':
|
|
||||||
atom_or_trailer = trailer.get_previous_sibling()
|
|
||||||
return check_for_power(atom_or_trailer, arglist_nodes)
|
|
||||||
return string, False
|
|
||||||
|
|
||||||
# Maybe an arglist or some weird error case. Therefore checked below.
|
# Maybe an arglist or some weird error case. Therefore checked below.
|
||||||
arglist = start_leaf.parent
|
arglist = start_leaf.parent
|
||||||
@@ -132,25 +125,20 @@ def _maybe_add_os_path_join(module_context, start_leaf, string):
|
|||||||
else:
|
else:
|
||||||
arglist_nodes = []
|
arglist_nodes = []
|
||||||
|
|
||||||
if error_node.children[index + 1] == '(':
|
return check(error_node.children[index + 1], arglist_nodes[::2])
|
||||||
atom_or_trailer = error_node.children[index]
|
|
||||||
return check_for_power(atom_or_trailer, arglist_nodes[::2])
|
|
||||||
elif arglist.type == 'arglist':
|
elif arglist.type == 'arglist':
|
||||||
trailer = arglist.parent
|
trailer = arglist.parent
|
||||||
if trailer.type == 'error_node':
|
if trailer.type == 'error_node':
|
||||||
trailer_index = trailer.children.index(arglist)
|
trailer_index = trailer.children.index(arglist)
|
||||||
assert trailer_index >= 2
|
assert trailer_index >= 2
|
||||||
assert trailer.children[trailer_index - 1] == '('
|
assert trailer.children[trailer_index - 1] == '('
|
||||||
atom_or_trailer = trailer.children[trailer_index - 2]
|
return check(trailer.children[trailer_index - 1], arglist_nodes[::2])
|
||||||
return check_for_power(atom_or_trailer, arglist_nodes[::2])
|
|
||||||
elif trailer.type == 'trailer':
|
elif trailer.type == 'trailer':
|
||||||
return check_trailer(trailer, arglist_nodes[::2])
|
return check(trailer.children[0], arglist_nodes[::2])
|
||||||
elif arglist.type == 'trailer':
|
elif arglist.type == 'trailer':
|
||||||
return check_trailer(arglist, [])
|
return check(arglist.children[0], [])
|
||||||
elif arglist.type == 'error_node':
|
elif arglist.type == 'error_node':
|
||||||
# Stuff like `join(""`
|
# Stuff like `join(""`
|
||||||
if len(arglist_nodes) >= 2 and arglist_nodes[-1] == '(':
|
return check(arglist_nodes[-1], [])
|
||||||
atom_or_trailer = arglist_nodes[-2]
|
|
||||||
return check_for_power(atom_or_trailer, [])
|
|
||||||
|
|
||||||
return string, False
|
return None
|
||||||
|
|||||||
@@ -242,6 +242,11 @@ os_path = 'from os.path import *\n'
|
|||||||
(f2, 'import os.path as p as p\np.join(p.dirname(__file__), "completi', None, ['on"']),
|
(f2, 'import os.path as p as p\np.join(p.dirname(__file__), "completi', None, ['on"']),
|
||||||
(f2, 'from os.path import dirname, join as j\nj(dirname(__file__), "completi',
|
(f2, 'from os.path import dirname, join as j\nj(dirname(__file__), "completi',
|
||||||
None, ['on"']),
|
None, ['on"']),
|
||||||
|
|
||||||
|
# Trying to break it
|
||||||
|
(f2, os_path + 'join(["tes', 10, ['t' + s]),
|
||||||
|
(f2, os_path + 'join(["tes"]', 10, ['t' + s]),
|
||||||
|
(f2, os_path + 'join(["tes"])', 10, ['t' + s]),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
def test_file_path_completions(Script, file, code, column, expected):
|
def test_file_path_completions(Script, file, code, column, expected):
|
||||||
|
|||||||
Reference in New Issue
Block a user