diff --git a/jedi/api/completion.py b/jedi/api/completion.py index 23fac432..7562c49b 100644 --- a/jedi/api/completion.py +++ b/jedi/api/completion.py @@ -88,7 +88,24 @@ class Completion: def completions(self): leaf = self._module_node.get_leaf_for_position(self._position, include_prefixes=True) string, start_leaf = _extract_string_while_in_string(leaf, self._position) - if string is not None: + + prefixed_completions = [] + if string is None: + string = '' + bracket_leaf = leaf + if bracket_leaf.type == 'number': + string = bracket_leaf.value + 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) + + if string is not None and not prefixed_completions: completions = list(file_name_completions( self._inference_state, self._module_context, start_leaf, string, self._like_name, self._call_signatures_callback, @@ -102,9 +119,12 @@ class Completion: completions = filter_names(self._inference_state, completion_names, self.stack, self._like_name) - return sorted(completions, key=lambda x: (x.name.startswith('__'), - x.name.startswith('_'), - x.name.lower())) + return ( + prefixed_completions + + sorted(completions, key=lambda x: (x.name.startswith('__'), + x.name.startswith('_'), + x.name.lower())) + ) def _get_context_completions(self, leaf): """ @@ -178,24 +198,13 @@ class Completion: if not current_line or current_line[-1] in ' \t.;': completion_names += self._get_keyword_completion_names(allowed_transitions) - nodes = _gather_nodes(stack) - if any(n.nonterminal == 'trailer' and n.nodes[0] == '[' for n in stack): - bracket = self._module_node.get_leaf_for_position(self._position, include_prefixes=True) - string = '' - if bracket.type == 'number': - string = bracket.value - bracket = bracket.get_previous_leaf() - context = self._module_context.create_context(bracket) - - values = infer_call_of_leaf(context, bracket.get_previous_leaf()) - completion_names += completions_for_dicts(values, string) - if any(t in allowed_transitions for t in (PythonTokenTypes.NAME, PythonTokenTypes.INDENT)): # This means that we actually have to do type inference. nonterminals = [stack_node.nonterminal for stack_node in stack] + nodes = _gather_nodes(stack) if nodes and nodes[-1] in ('as', 'def', 'class'): # No completions for ``with x as foo`` and ``import x as foo``. # Also true for defining names as a class or function. diff --git a/jedi/api/dicts.py b/jedi/api/dicts.py index e5a203ac..c25f7dd9 100644 --- a/jedi/api/dicts.py +++ b/jedi/api/dicts.py @@ -1,4 +1,5 @@ from jedi.inference.names import AbstractArbitraryName +from jedi.api.classes import Completion _sentinel = object() @@ -8,12 +9,18 @@ class F(AbstractArbitraryName): is_value_name = False -def completions_for_dicts(dicts, literal_string): +def completions_for_dicts(inference_state, dicts, literal_string): + for dict_key in sorted(_get_python_keys(dicts)): + dict_key_str = repr(dict_key) + if dict_key_str.startswith(literal_string): + name = F(inference_state, dict_key_str[len(literal_string):]) + yield Completion(inference_state, name, stack=None, like_name_length=0) + + +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: - dict_key_str = str(dict_key) - if dict_key_str.startswith(literal_string): - yield F(dct.inference_state, dict_key_str[len(literal_string):]) + yield dict_key diff --git a/test/test_api/test_completion.py b/test/test_api/test_completion.py index 92e79b85..425f8932 100644 --- a/test/test_api/test_completion.py +++ b/test/test_api/test_completion.py @@ -283,12 +283,12 @@ def test_file_path_completions(Script, file, code, column, expected): ('ints[50', 6, ['']),#TODO ['0']), ('ints[50', 7, ['']), - ('strs[', 5, ["'asdf'", "'foo'", "'fbar'", Ellipsis]), - ('strs[]', 5, ["'asdf'", "'foo'", "'fbar'", Ellipsis]), - ("strs[']", 6, ["asdf'", "foo'", "fbar'"]), - ('strs["]', 6, ['asdf"', 'foo"', 'fbar"']), - ('strs["""]', 6, ['asdf', 'foo', 'fbar']), - ('strs["""]', 8, ['asdf"""', 'foo"""', 'fbar"""']), + ('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', 'bar', 'foo']), + ('strs["""]', 8, ['asdf"""', 'fbar"""', 'foo"""']), ('strs[b"]', 8, []), ('strs[r"asd', 11, ['f"']), ('strs[R"asd', 11, ['f"']),