diff --git a/jedi/api/completion.py b/jedi/api/completion.py index 4c281cc1..31d0a479 100644 --- a/jedi/api/completion.py +++ b/jedi/api/completion.py @@ -100,9 +100,11 @@ class Completion: def completions(self): leaf = self._module_node.get_leaf_for_position(self._position, include_prefixes=True) - string = _extract_string_while_in_string(leaf, self._position) + string, start_leaf = _extract_string_while_in_string(leaf, self._position) if string is not None: - completions = list(file_name_completions(self._evaluator, string, self._like_name)) + completions = list(file_name_completions( + self._evaluator, self._module_context, start_leaf, string, self._like_name + )) if completions: return completions @@ -307,15 +309,15 @@ def _extract_string_while_in_string(leaf, position): match = re.match(r'^\w*(\'{3}|"{3}|\'|")', leaf.value) quote = match.group(1) if leaf.line == position[0] and position[1] < leaf.column + match.end(): - return None + return None, None if leaf.end_pos[0] == position[0] and position[1] > leaf.end_pos[1] - len(quote): - return None - return cut_value_at_position(leaf, position)[match.end():] + return None, None + return cut_value_at_position(leaf, position)[match.end():], leaf leaves = [] while leaf is not None and leaf.line == position[0]: if leaf.type == 'error_leaf' and ('"' in leaf.value or "'" in leaf.value): - return ''.join(l.get_code() for l in leaves) + return ''.join(l.get_code() for l in leaves), leaf leaves.insert(0, leaf) leaf = leaf.get_previous_leaf() - return None + return None, None diff --git a/jedi/api/file_name.py b/jedi/api/file_name.py index 1c375842..cf0663c6 100644 --- a/jedi/api/file_name.py +++ b/jedi/api/file_name.py @@ -3,13 +3,15 @@ import os from jedi._compatibility import FileNotFoundError from jedi.evaluate.names import AbstractArbitraryName from jedi.api import classes +from jedi.evaluate.helpers import get_str_or_none -def file_name_completions(evaluator, string, like_name): +def file_name_completions(evaluator, module_context, start_leaf, string, like_name): base_name = os.path.basename(string) like_name = base_name + like_name string = os.path.dirname(string) + string = _get_string_additions(module_context, start_leaf) + string base_path = os.path.join(evaluator.project._path, string) try: listed = os.listdir(base_path) @@ -29,6 +31,35 @@ def file_name_completions(evaluator, string, like_name): ) +def _get_string_additions(module_context, start_leaf): + addition = start_leaf.get_previous_leaf() + if addition != '+': + return '' + node = addition.parent + string = '' + was_addition = True + for child_node in reversed(node.children[:node.children.index(addition)]): + if was_addition: + was_addition = False + context = module_context.create_context(node) + contexts = context.eval_node(child_node) + if len(contexts) != 1: + return string + c, = contexts + s = get_str_or_none(c) + if s is None: + return string + string = s + string + continue + + if child_node != '+': + break + was_addition = True + child_node + module_context + return string + + class FileName(AbstractArbitraryName): api_type = u'path' is_context_name = False diff --git a/test/test_api/test_completion.py b/test/test_api/test_completion.py index f907b8c6..1d333996 100644 --- a/test/test_api/test_completion.py +++ b/test/test_api/test_completion.py @@ -185,6 +185,11 @@ def test_keyword_completion(Script, code, has_keywords): ('example.py', 'r"""\ntest', None, []), ('example.py', 'u"""tes\n', (1, 7), ['t' + s]), ('example.py', '"""test%stest_cache.p"""' % s, 20, ['y']), + + # Adding + ('example.py', '"test" + "%stest_cac' % s, None, ['he.py']), + ('example.py', '"test" + "%s" + "test_cac' % s, None, ['he.py']), + ('example.py', 'x = 1 + "test', None, [s]), ] ) def test_file_path_completions(Script, file, code, column, expected):