Format: text/x-python
⬇ Download File (3.6 KB)
   1  """
   2  This module is here for string completions. This means mostly stuff where
   3  strings are returned, like `foo = dict(bar=3); foo["ba` would complete to
   4  `"bar"]`.
   5  
   6  It however does the same for numbers. The difference between string completions
   7  and other completions is mostly that this module doesn't return defined
   8  names in a module, but pretty much an arbitrary string.
   9  """
  10  import re
  11  
  12  from jedi.inference.names import AbstractArbitraryName
  13  from jedi.inference.helpers import infer_call_of_leaf
  14  from jedi.api.classes import Completion
  15  from jedi.parser_utils import cut_value_at_position
  16  
  17  _sentinel = object()
  18  
  19  
  20  class StringName(AbstractArbitraryName):
  21      api_type = 'string'
  22      is_value_name = False
  23  
  24  
  25  def complete_dict(module_context, code_lines, leaf, position, string, fuzzy):
  26      bracket_leaf = leaf
  27      if bracket_leaf != '[':
  28          bracket_leaf = leaf.get_previous_leaf()
  29  
  30      cut_end_quote = ''
  31      if string:
  32          cut_end_quote = get_quote_ending(string, code_lines, position, invert_result=True)
  33  
  34      if bracket_leaf == '[':
  35          if string is None and leaf is not bracket_leaf:
  36              string = cut_value_at_position(leaf, position)
  37  
  38          context = module_context.create_context(bracket_leaf)
  39  
  40          before_node = before_bracket_leaf = bracket_leaf.get_previous_leaf()
  41          if before_node in (')', ']', '}'):
  42              before_node = before_node.parent
  43          if before_node.type in ('atom', 'trailer', 'name'):
  44              values = infer_call_of_leaf(context, before_bracket_leaf)
  45              return list(_completions_for_dicts(
  46                  module_context.inference_state,
  47                  values,
  48                  '' if string is None else string,
  49                  cut_end_quote,
  50                  fuzzy=fuzzy,
  51              ))
  52      return []
  53  
  54  
  55  def _completions_for_dicts(inference_state, dicts, literal_string, cut_end_quote, fuzzy):
  56      for dict_key in sorted(_get_python_keys(dicts), key=lambda x: repr(x)):
  57          dict_key_str = _create_repr_string(literal_string, dict_key)
  58          if dict_key_str.startswith(literal_string):
  59              name = StringName(inference_state, dict_key_str[:-len(cut_end_quote) or None])
  60              yield Completion(
  61                  inference_state,
  62                  name,
  63                  stack=None,
  64                  like_name_length=len(literal_string),
  65                  is_fuzzy=fuzzy
  66              )
  67  
  68  
  69  def _create_repr_string(literal_string, dict_key):
  70      if not isinstance(dict_key, (str, bytes)) or not literal_string:
  71          return repr(dict_key)
  72  
  73      r = repr(dict_key)
  74      prefix, quote = _get_string_prefix_and_quote(literal_string)
  75      if quote is None:
  76          return r
  77      if quote == r[0]:
  78          return prefix + r
  79      return prefix + quote + r[1:-1] + quote
  80  
  81  
  82  def _get_python_keys(dicts):
  83      for dct in dicts:
  84          if dct.array_type == 'dict':
  85              for key in dct.get_key_values():
  86                  dict_key = key.get_safe_value(default=_sentinel)
  87                  if dict_key is not _sentinel:
  88                      yield dict_key
  89  
  90  
  91  def _get_string_prefix_and_quote(string):
  92      match = re.match(r'(\w*)("""|\'{3}|"|\')', string)
  93      if match is None:
  94          return None, None
  95      return match.group(1), match.group(2)
  96  
  97  
  98  def _matches_quote_at_position(code_lines, quote, position):
  99      string = code_lines[position[0] - 1][position[1]:position[1] + len(quote)]
 100      return string == quote
 101  
 102  
 103  def get_quote_ending(string, code_lines, position, invert_result=False):
 104      _, quote = _get_string_prefix_and_quote(string)
 105      if quote is None:
 106          return ''
 107  
 108      # Add a quote only if it's not already there.
 109      if _matches_quote_at_position(code_lines, quote, position) != invert_result:
 110          return ''
 111      return quote
 112