-
-
Notifications
You must be signed in to change notification settings - Fork 511
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
415 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
""" | ||
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._compatibility import unicode | ||
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 = u'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_bracket_leaf = bracket_leaf.get_previous_leaf() | ||
if before_bracket_leaf.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, (unicode, 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 _get_string_quote(string): | ||
return _get_string_prefix_and_quote(string)[1] | ||
|
||
|
||
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_quote(string) | ||
# Add a quote only if it's not already there. | ||
if _matches_quote_at_position(code_lines, quote, position) != invert_result: | ||
return '' | ||
return quote |
Oops, something went wrong.