| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- """
- This module is here for string completions. This means mostly stuff where
- strings are returned, like `foo = dict(bar=3); foo["ba` would complete to
- `"bar"]`.
- It however does the same for numbers. The difference between string completions
- and other completions is mostly that this module doesn't return defined
- names in a module, but pretty much an arbitrary string.
- """
- import re
- from jedi.inference.names import AbstractArbitraryName
- from jedi.inference.helpers import infer_call_of_leaf
- from jedi.api.classes import Completion
- from jedi.parser_utils import cut_value_at_position
- _sentinel = object()
- class StringName(AbstractArbitraryName):
- api_type = 'string'
- is_value_name = False
- def complete_dict(module_context, code_lines, leaf, position, string, fuzzy):
- bracket_leaf = leaf
- if bracket_leaf != '[':
- bracket_leaf = leaf.get_previous_leaf()
- cut_end_quote = ''
- if string:
- cut_end_quote = get_quote_ending(string, code_lines, position, invert_result=True)
- if bracket_leaf == '[':
- if string is None and leaf is not bracket_leaf:
- string = cut_value_at_position(leaf, position)
- context = module_context.create_context(bracket_leaf)
- before_node = before_bracket_leaf = bracket_leaf.get_previous_leaf()
- if before_node in (')', ']', '}'):
- before_node = before_node.parent
- if before_node.type in ('atom', 'trailer', 'name'):
- values = infer_call_of_leaf(context, before_bracket_leaf)
- return list(_completions_for_dicts(
- module_context.inference_state,
- values,
- '' if string is None else string,
- cut_end_quote,
- fuzzy=fuzzy,
- ))
- return []
- def _completions_for_dicts(inference_state, dicts, literal_string, cut_end_quote, fuzzy):
- for dict_key in sorted(_get_python_keys(dicts), key=lambda x: repr(x)):
- dict_key_str = _create_repr_string(literal_string, dict_key)
- if dict_key_str.startswith(literal_string):
- name = StringName(inference_state, dict_key_str[:-len(cut_end_quote) or None])
- yield Completion(
- inference_state,
- name,
- stack=None,
- like_name_length=len(literal_string),
- is_fuzzy=fuzzy
- )
- def _create_repr_string(literal_string, dict_key):
- if not isinstance(dict_key, (str, bytes)) or not literal_string:
- return repr(dict_key)
- r = repr(dict_key)
- prefix, quote = _get_string_prefix_and_quote(literal_string)
- if quote is None:
- return r
- if quote == r[0]:
- return prefix + r
- return prefix + quote + r[1:-1] + quote
- def _get_python_keys(dicts):
- for dct in dicts:
- if dct.array_type == 'dict':
- for key in dct.get_key_values():
- dict_key = key.get_safe_value(default=_sentinel)
- if dict_key is not _sentinel:
- yield dict_key
- def _get_string_prefix_and_quote(string):
- match = re.match(r'(\w*)("""|\'{3}|"|\')', string)
- if match is None:
- return None, None
- return match.group(1), match.group(2)
- def _matches_quote_at_position(code_lines, quote, position):
- string = code_lines[position[0] - 1][position[1]:position[1] + len(quote)]
- return string == quote
- def get_quote_ending(string, code_lines, position, invert_result=False):
- _, quote = _get_string_prefix_and_quote(string)
- if quote is None:
- return ''
- # Add a quote only if it's not already there.
- if _matches_quote_at_position(code_lines, quote, position) != invert_result:
- return ''
- return quote
|