|
import os |
|
|
|
from jedi.api import classes |
|
from jedi.api.strings import StringName, get_quote_ending |
|
from jedi.api.helpers import match |
|
from jedi.inference.helpers import get_str_or_none |
|
|
|
|
|
class PathName(StringName): |
|
api_type = 'path' |
|
|
|
|
|
def complete_file_name(inference_state, module_context, start_leaf, quote, string, |
|
like_name, signatures_callback, code_lines, position, fuzzy): |
|
|
|
like_name_length = len(os.path.basename(string)) |
|
|
|
addition = _get_string_additions(module_context, start_leaf) |
|
if string.startswith('~'): |
|
string = os.path.expanduser(string) |
|
if addition is None: |
|
return |
|
string = addition + string |
|
|
|
|
|
|
|
must_start_with = os.path.basename(string) |
|
string = os.path.dirname(string) |
|
|
|
sigs = signatures_callback(*position) |
|
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(inference_state.project.path, string) |
|
try: |
|
listed = sorted(os.scandir(base_path), key=lambda e: e.name) |
|
|
|
except (FileNotFoundError, OSError): |
|
return |
|
quote_ending = get_quote_ending(quote, code_lines, position) |
|
for entry in listed: |
|
name = entry.name |
|
if match(name, must_start_with, fuzzy=fuzzy): |
|
if is_in_os_path_join or not entry.is_dir(): |
|
name += quote_ending |
|
else: |
|
name += os.path.sep |
|
|
|
yield classes.Completion( |
|
inference_state, |
|
PathName(inference_state, name[len(must_start_with) - like_name_length:]), |
|
stack=None, |
|
like_name_length=like_name_length, |
|
is_fuzzy=fuzzy, |
|
) |
|
|
|
|
|
def _get_string_additions(module_context, start_leaf): |
|
def iterate_nodes(): |
|
node = addition.parent |
|
was_addition = True |
|
for child_node in reversed(node.children[:node.children.index(addition)]): |
|
if was_addition: |
|
was_addition = False |
|
yield child_node |
|
continue |
|
|
|
if child_node != '+': |
|
break |
|
was_addition = True |
|
|
|
addition = start_leaf.get_previous_leaf() |
|
if addition != '+': |
|
return '' |
|
context = module_context.create_context(start_leaf) |
|
return _add_strings(context, reversed(list(iterate_nodes()))) |
|
|
|
|
|
def _add_strings(context, nodes, add_slash=False): |
|
string = '' |
|
first = True |
|
for child_node in nodes: |
|
values = context.infer_node(child_node) |
|
if len(values) != 1: |
|
return None |
|
c, = values |
|
s = get_str_or_none(c) |
|
if s is None: |
|
return None |
|
if not first and add_slash: |
|
string += os.path.sep |
|
string += s |
|
first = False |
|
return string |
|
|
|
|
|
def _add_os_path_join(module_context, start_leaf, bracket_start): |
|
def check(maybe_bracket, nodes): |
|
if maybe_bracket.start_pos != bracket_start: |
|
return None |
|
|
|
if not nodes: |
|
return '' |
|
context = module_context.create_context(nodes[0]) |
|
return _add_strings(context, nodes, add_slash=True) or '' |
|
|
|
if start_leaf.type == 'error_leaf': |
|
|
|
value_node = start_leaf.parent |
|
index = value_node.children.index(start_leaf) |
|
if index > 0: |
|
error_node = value_node.children[index - 1] |
|
if error_node.type == 'error_node' and len(error_node.children) >= 2: |
|
index = -2 |
|
if error_node.children[-1].type == 'arglist': |
|
arglist_nodes = error_node.children[-1].children |
|
index -= 1 |
|
else: |
|
arglist_nodes = [] |
|
|
|
return check(error_node.children[index + 1], arglist_nodes[::2]) |
|
return None |
|
|
|
|
|
searched_node_child = start_leaf |
|
while searched_node_child.parent is not None \ |
|
and searched_node_child.parent.type not in ('arglist', 'trailer', 'error_node'): |
|
searched_node_child = searched_node_child.parent |
|
|
|
if searched_node_child.get_first_leaf() is not start_leaf: |
|
return None |
|
searched_node = searched_node_child.parent |
|
if searched_node is None: |
|
return None |
|
|
|
index = searched_node.children.index(searched_node_child) |
|
arglist_nodes = searched_node.children[:index] |
|
if searched_node.type == 'arglist': |
|
trailer = searched_node.parent |
|
if trailer.type == 'error_node': |
|
trailer_index = trailer.children.index(searched_node) |
|
assert trailer_index >= 2 |
|
assert trailer.children[trailer_index - 1] == '(' |
|
return check(trailer.children[trailer_index - 1], arglist_nodes[::2]) |
|
elif trailer.type == 'trailer': |
|
return check(trailer.children[0], arglist_nodes[::2]) |
|
elif searched_node.type == 'trailer': |
|
return check(searched_node.children[0], []) |
|
elif searched_node.type == 'error_node': |
|
|
|
return check(arglist_nodes[-1], []) |
|
|