mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 14:04:26 +08:00
Fix some more dict tests
This commit is contained in:
@@ -10,7 +10,7 @@ from jedi import settings
|
|||||||
from jedi.api import classes
|
from jedi.api import classes
|
||||||
from jedi.api import helpers
|
from jedi.api import helpers
|
||||||
from jedi.api import keywords
|
from jedi.api import keywords
|
||||||
from jedi.api.strings import completions_for_dicts
|
from jedi.api.strings import complete_dict
|
||||||
from jedi.api.file_name import complete_file_name
|
from jedi.api.file_name import complete_file_name
|
||||||
from jedi.inference import imports
|
from jedi.inference import imports
|
||||||
from jedi.inference.base_value import ValueSet
|
from jedi.inference.base_value import ValueSet
|
||||||
@@ -105,21 +105,13 @@ class Completion:
|
|||||||
leaf = self._module_node.get_leaf_for_position(self._position, include_prefixes=True)
|
leaf = self._module_node.get_leaf_for_position(self._position, include_prefixes=True)
|
||||||
string, start_leaf = _extract_string_while_in_string(leaf, self._position)
|
string, start_leaf = _extract_string_while_in_string(leaf, self._position)
|
||||||
|
|
||||||
prefixed_completions = []
|
prefixed_completions = complete_dict(
|
||||||
#if string is None:
|
self._module_context,
|
||||||
#string = ''
|
leaf,
|
||||||
bracket_leaf = leaf
|
self._original_position,
|
||||||
#if bracket_leaf.type in ('number', 'error_leaf'):
|
string,
|
||||||
#string = bracket_leaf.value
|
fuzzy=fuzzy,
|
||||||
#bracket_leaf = bracket_leaf.get_previous_leaf()
|
)
|
||||||
|
|
||||||
if bracket_leaf == '[':
|
|
||||||
context = self._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)
|
|
||||||
prefixed_completions += completions_for_dicts(
|
|
||||||
self._inference_state, values, string, fuzzy=fuzzy)
|
|
||||||
|
|
||||||
if string is not None and not prefixed_completions:
|
if string is not None and not prefixed_completions:
|
||||||
prefixed_completions = list(complete_file_name(
|
prefixed_completions = list(complete_file_name(
|
||||||
@@ -128,8 +120,6 @@ class Completion:
|
|||||||
self._code_lines, self._original_position,
|
self._code_lines, self._original_position,
|
||||||
fuzzy
|
fuzzy
|
||||||
))
|
))
|
||||||
if prefixed_completions:
|
|
||||||
return prefixed_completions
|
|
||||||
if string is not None:
|
if string is not None:
|
||||||
return prefixed_completions
|
return prefixed_completions
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,13 @@ It however does the same for numbers. The difference between string completions
|
|||||||
and other completions is mostly that this module doesn't return defined
|
and other completions is mostly that this module doesn't return defined
|
||||||
names in a module, but pretty much an arbitrary string.
|
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.names import AbstractArbitraryName
|
||||||
|
from jedi.inference.helpers import infer_call_of_leaf
|
||||||
from jedi.api.classes import Completion
|
from jedi.api.classes import Completion
|
||||||
from jedi.parser_utils import get_string_quote
|
from jedi.parser_utils import cut_value_at_position
|
||||||
|
|
||||||
_sentinel = object()
|
_sentinel = object()
|
||||||
|
|
||||||
@@ -19,11 +23,46 @@ class StringName(AbstractArbitraryName):
|
|||||||
is_value_name = False
|
is_value_name = False
|
||||||
|
|
||||||
|
|
||||||
def completions_for_dicts(inference_state, dicts, literal_string, fuzzy):
|
def complete_dict(module_context, leaf, position, string, fuzzy):
|
||||||
for dict_key in sorted(_get_python_keys(dicts)):
|
if string is None:
|
||||||
dict_key_str = repr(dict_key)
|
string = ''
|
||||||
|
bracket_leaf = leaf
|
||||||
|
end_quote = ''
|
||||||
|
if bracket_leaf.type in ('number', 'error_leaf'):
|
||||||
|
string = cut_value_at_position(bracket_leaf, position)
|
||||||
|
if bracket_leaf.end_pos > position:
|
||||||
|
end_quote = _get_string_quote(string) or ''
|
||||||
|
if end_quote:
|
||||||
|
ending = cut_value_at_position(
|
||||||
|
bracket_leaf,
|
||||||
|
(position[0], position[1] + len(end_quote))
|
||||||
|
)
|
||||||
|
if not ending.endswith(end_quote):
|
||||||
|
end_quote = ''
|
||||||
|
|
||||||
|
bracket_leaf = bracket_leaf.get_previous_leaf()
|
||||||
|
|
||||||
|
if bracket_leaf == '[':
|
||||||
|
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,
|
||||||
|
end_quote,
|
||||||
|
fuzzy=fuzzy,
|
||||||
|
))
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def _completions_for_dicts(inference_state, dicts, literal_string, 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):
|
if dict_key_str.startswith(literal_string):
|
||||||
name = StringName(inference_state, dict_key_str[len(literal_string):])
|
n = dict_key_str[len(literal_string):-len(end_quote) or None]
|
||||||
|
name = StringName(inference_state, n)
|
||||||
yield Completion(
|
yield Completion(
|
||||||
inference_state,
|
inference_state,
|
||||||
name,
|
name,
|
||||||
@@ -33,6 +72,17 @@ def completions_for_dicts(inference_state, dicts, literal_string, 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 == r[0]:
|
||||||
|
return prefix + r
|
||||||
|
return prefix + quote + r[1:-1] + quote
|
||||||
|
|
||||||
|
|
||||||
def _get_python_keys(dicts):
|
def _get_python_keys(dicts):
|
||||||
for dct in dicts:
|
for dct in dicts:
|
||||||
if dct.array_type == 'dict':
|
if dct.array_type == 'dict':
|
||||||
@@ -42,9 +92,20 @@ def _get_python_keys(dicts):
|
|||||||
yield dict_key
|
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 get_quote_ending(start_leaf, code_lines, position):
|
def get_quote_ending(start_leaf, code_lines, position):
|
||||||
if start_leaf.type == 'string':
|
if start_leaf.type == 'string':
|
||||||
quote = get_string_quote(start_leaf)
|
quote = _get_string_quote(start_leaf)
|
||||||
else:
|
else:
|
||||||
assert start_leaf.type == 'error_leaf'
|
assert start_leaf.type == 'error_leaf'
|
||||||
quote = start_leaf.value
|
quote = start_leaf.value
|
||||||
|
|||||||
@@ -354,6 +354,9 @@ class TreeInstance(_BaseTreeInstance):
|
|||||||
def get_annotated_class_object(self):
|
def get_annotated_class_object(self):
|
||||||
return self._get_annotated_class_object() or self.class_value
|
return self._get_annotated_class_object() or self.class_value
|
||||||
|
|
||||||
|
def get_key_values(self):
|
||||||
|
return NO_VALUES
|
||||||
|
|
||||||
def py__simple_getitem__(self, index):
|
def py__simple_getitem__(self, index):
|
||||||
if self.array_type == 'dict':
|
if self.array_type == 'dict':
|
||||||
# Logic for dict({'foo': bar}) and dict(foo=bar)
|
# Logic for dict({'foo': bar}) and dict(foo=bar)
|
||||||
|
|||||||
@@ -293,10 +293,6 @@ def cut_value_at_position(leaf, position):
|
|||||||
return ''.join(lines)
|
return ''.join(lines)
|
||||||
|
|
||||||
|
|
||||||
def get_string_quote(leaf):
|
|
||||||
return re.match(r'\w*("""|\'{3}|"|\')', leaf.value).group(1)
|
|
||||||
|
|
||||||
|
|
||||||
def _function_is_x_method(method_name):
|
def _function_is_x_method(method_name):
|
||||||
def wrapper(function_node):
|
def wrapper(function_node):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ current_dirname = os.path.basename(dirname(dirname(dirname(__file__))))
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'file, code, column, expected', [
|
'file, code, column, expected', [
|
||||||
# General tests / relative paths
|
# General tests / relative paths
|
||||||
(None, '"comp', None, ['ile', 'lex']), # No files like comp
|
(None, '"comp', None, []), # No files like comp
|
||||||
(None, '"test', None, [s]),
|
(None, '"test', None, [s]),
|
||||||
(None, '"test', 4, ['t' + s]),
|
(None, '"test', 4, ['t' + s]),
|
||||||
('example.py', '"test%scomp' % s, None, ['letion' + s]),
|
('example.py', '"test%scomp' % s, None, ['letion' + s]),
|
||||||
@@ -273,8 +273,7 @@ def test_file_path_completions(Script, file, code, column, expected):
|
|||||||
assert [c.complete for c in comps] == expected
|
assert [c.complete for c in comps] == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
_dict_keys_completion_tests = [
|
||||||
'added_code, column, expected', [
|
|
||||||
('ints[', 5, ['1', '50', Ellipsis]),
|
('ints[', 5, ['1', '50', Ellipsis]),
|
||||||
('ints[]', 5, ['1', '50', Ellipsis]),
|
('ints[]', 5, ['1', '50', Ellipsis]),
|
||||||
('ints[1]', 5, ['1', '50', Ellipsis]),
|
('ints[1]', 5, ['1', '50', Ellipsis]),
|
||||||
@@ -286,19 +285,20 @@ def test_file_path_completions(Script, file, code, column, expected):
|
|||||||
('ints[5]', 6, ['0']),
|
('ints[5]', 6, ['0']),
|
||||||
('ints[50', 5, ['1', '50', Ellipsis]),
|
('ints[50', 5, ['1', '50', Ellipsis]),
|
||||||
('ints[5', 6, ['0']),
|
('ints[5', 6, ['0']),
|
||||||
('ints[50', 6, ['']),#TODO ['0']),
|
('ints[50', 6, ['0']),
|
||||||
('ints[50', 7, ['']),
|
('ints[50', 7, ['']),
|
||||||
|
|
||||||
('strs[', 5, ["'asdf'", "'fbar'", "'foo'", Ellipsis]),
|
('strs[', 5, ["'asdf'", "'fbar'", "'foo'", Ellipsis]),
|
||||||
('strs[]', 5, ["'asdf'", "'fbar'", "'foo'", Ellipsis]),
|
('strs[]', 5, ["'asdf'", "'fbar'", "'foo'", Ellipsis]),
|
||||||
|
("strs['", 6, ["asdf'", "fbar'", "foo'"]),
|
||||||
("strs[']", 6, ["asdf'", "fbar'", "foo'"]),
|
("strs[']", 6, ["asdf'", "fbar'", "foo'"]),
|
||||||
('strs["]', 6, ['asdf"', 'fbar"', 'foo"']),
|
('strs["]', 6, ['asdf"', 'fbar"', 'foo"']),
|
||||||
('strs["""]', 6, ['asdf', 'bar', 'foo']),
|
('strs["""]', 6, ['asdf', 'fbar', 'foo']),
|
||||||
('strs["""]', 8, ['asdf"""', 'fbar"""', 'foo"""']),
|
('strs["""]', 8, ['asdf"""', 'fbar"""', 'foo"""']),
|
||||||
('strs[b"]', 8, []),
|
('strs[b"]', 8, []),
|
||||||
('strs[r"asd', 11, ['f"']),
|
('strs[r"asd', 10, ['f"']),
|
||||||
('strs[R"asd', 11, ['f"']),
|
('strs[R"asd', 10, ['f"']),
|
||||||
('strs[f"asd', 11, ['f"']),
|
('strs[f"asd', 10, ['f"']),
|
||||||
|
|
||||||
('strs["f', 7, ['oo"]']),
|
('strs["f', 7, ['oo"]']),
|
||||||
('strs["f"', 7, ['oo']),
|
('strs["f"', 7, ['oo']),
|
||||||
@@ -310,7 +310,11 @@ def test_file_path_completions(Script, file, code, column, expected):
|
|||||||
|
|
||||||
('casted["f', 9, ['3"', 'bar"', 'oo"']),
|
('casted["f', 9, ['3"', 'bar"', 'oo"']),
|
||||||
('casted_mod["f', 13, ['3"', 'bar"', 'oo"', 'uuu"', 'ull"']),
|
('casted_mod["f', 13, ['3"', 'bar"', 'oo"', 'uuu"', 'ull"']),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'added_code, column, expected', _dict_keys_completion_tests
|
||||||
)
|
)
|
||||||
def test_dict_keys_completions(Script, added_code, column, expected, skip_pre_python35):
|
def test_dict_keys_completions(Script, added_code, column, expected, skip_pre_python35):
|
||||||
code = dedent(r'''
|
code = dedent(r'''
|
||||||
|
|||||||
Reference in New Issue
Block a user