diff --git a/jedi/api/completion.py b/jedi/api/completion.py index e155cc1a..18cd8082 100644 --- a/jedi/api/completion.py +++ b/jedi/api/completion.py @@ -60,6 +60,11 @@ def filter_names(inference_state, completion_names, stack, like_name, fuzzy): yield new +def _remove_duplicates(completions, other_completions): + names = {d.name for d in other_completions} + return [c for c in completions if c.name not in names] + + def get_user_context(module_context, position): """ Returns the scope in which the user resides. This includes flows. @@ -128,14 +133,15 @@ class Completion: completion_names = self._complete_python(leaf) - completions = filter_names(self._inference_state, completion_names, - self.stack, self._like_name, fuzzy) + completions = list(filter_names(self._inference_state, completion_names, + self.stack, self._like_name, fuzzy)) return ( - prefixed_completions + - sorted(completions, key=lambda x: (x.name.startswith('__'), - x.name.startswith('_'), - x.name.lower())) + # Removing duplicates mostly to remove False/True/None duplicates. + _remove_duplicates(prefixed_completions, completions) + + sorted(completions, key=lambda x: (x.name.startswith('__'), + x.name.startswith('_'), + x.name.lower())) ) def _complete_python(self, leaf): @@ -211,9 +217,12 @@ class Completion: completion_names = [] current_line = self._code_lines[self._position[0] - 1][:self._position[1]] - if not current_line or current_line[-1] in ' \t.;' \ - and current_line[-3:] != '...': - completion_names += self._complete_keywords(allowed_transitions) + + completion_names += self._complete_keywords( + allowed_transitions, + only_values=not (not current_line or current_line[-1] in ' \t.;' + and current_line[-3:] != '...') + ) if any(t in allowed_transitions for t in (PythonTokenTypes.NAME, PythonTokenTypes.INDENT)): @@ -293,10 +302,11 @@ class Completion: return complete_param_names(context, function_name.value, decorators) return [] - def _complete_keywords(self, allowed_transitions): + def _complete_keywords(self, allowed_transitions, only_values): for k in allowed_transitions: if isinstance(k, str) and k.isalpha(): - yield keywords.KeywordName(self._inference_state, k) + if not only_values or k in ('True', 'False', 'None'): + yield keywords.KeywordName(self._inference_state, k) def _complete_global_scope(self): context = get_user_context(self._module_context, self._position) diff --git a/jedi/api/strings.py b/jedi/api/strings.py index 774dd444..a2f1ee46 100644 --- a/jedi/api/strings.py +++ b/jedi/api/strings.py @@ -55,12 +55,12 @@ def _completions_for_dicts(inference_state, dicts, literal_string, cut_end_quote dict_key_str = _create_repr_string(literal_string, dict_key) if dict_key_str.startswith(literal_string): n = dict_key_str[len(literal_string):-len(cut_end_quote) or None] - name = StringName(inference_state, n) + name = StringName(inference_state, dict_key_str[:-len(cut_end_quote) or None]) yield Completion( inference_state, name, stack=None, - like_name_length=0, + like_name_length=len(literal_string), is_fuzzy=fuzzy ) diff --git a/test/test_api/test_completion.py b/test/test_api/test_completion.py index 23ab7496..4d6313d2 100644 --- a/test/test_api/test_completion.py +++ b/test/test_api/test_completion.py @@ -305,8 +305,9 @@ _dict_keys_completion_tests = [ ('strs["f]', 7, ['bar"', 'oo"']), ('strs["f"]', 7, ['bar', 'oo']), - ('mixed[', 6, [r"'a\\sdf'", '1', '1.1', 'None', "b'foo'", Ellipsis]), + ('mixed[', 6, [r"'a\\sdf'", '1', '1.1', "b'foo'", Ellipsis]), ('mixed[1', 7, ['', '.1']), + ('mixed[Non', 9, ['e']), ('casted["f', 9, ['3"', 'bar"', 'oo"']), ('casted["f"', 9, ['3', 'bar', 'oo']), @@ -317,6 +318,7 @@ _dict_keys_completion_tests = [ ('keywords["', None, ['a"']), ('keywords[Non', None, ['e']), ('keywords[Fa', None, ['lse']), + ('keywords[Tr', None, ['ue']), ('keywords[str', None, ['', 's']), ] @@ -337,14 +339,11 @@ def test_dict_keys_completions(Script, added_code, column, expected, skip_pre_py keywords = {None: 1, False: 2, "a": 3} ''') line = None - if isinstance(column, tuple): - raise NotImplementedError - line, column = column comps = Script(code + added_code, line=line, column=column).completions() if Ellipsis in expected: # This means that global completions are part of this, so filter all of # that out. - comps = [c for c in comps if not c._name.is_value_name] + comps = [c for c in comps if not c._name.is_value_name and not c.is_keyword] expected = [e for e in expected if e is not Ellipsis] assert [c.complete for c in comps] == expected