Make sure to avoid duplicates in completions

This commit is contained in:
Dave Halter
2019-12-31 11:15:23 +01:00
parent 94a97ff8e8
commit ca13c44788
3 changed files with 27 additions and 18 deletions

View File

@@ -60,6 +60,11 @@ def filter_names(inference_state, completion_names, stack, like_name, fuzzy):
yield new 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): def get_user_context(module_context, position):
""" """
Returns the scope in which the user resides. This includes flows. Returns the scope in which the user resides. This includes flows.
@@ -128,14 +133,15 @@ class Completion:
completion_names = self._complete_python(leaf) completion_names = self._complete_python(leaf)
completions = filter_names(self._inference_state, completion_names, completions = list(filter_names(self._inference_state, completion_names,
self.stack, self._like_name, fuzzy) self.stack, self._like_name, fuzzy))
return ( return (
prefixed_completions + # Removing duplicates mostly to remove False/True/None duplicates.
sorted(completions, key=lambda x: (x.name.startswith('__'), _remove_duplicates(prefixed_completions, completions)
x.name.startswith('_'), + sorted(completions, key=lambda x: (x.name.startswith('__'),
x.name.lower())) x.name.startswith('_'),
x.name.lower()))
) )
def _complete_python(self, leaf): def _complete_python(self, leaf):
@@ -211,9 +217,12 @@ class Completion:
completion_names = [] completion_names = []
current_line = self._code_lines[self._position[0] - 1][:self._position[1]] 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(
completion_names += self._complete_keywords(allowed_transitions) 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, if any(t in allowed_transitions for t in (PythonTokenTypes.NAME,
PythonTokenTypes.INDENT)): PythonTokenTypes.INDENT)):
@@ -293,10 +302,11 @@ class Completion:
return complete_param_names(context, function_name.value, decorators) return complete_param_names(context, function_name.value, decorators)
return [] return []
def _complete_keywords(self, allowed_transitions): def _complete_keywords(self, allowed_transitions, only_values):
for k in allowed_transitions: for k in allowed_transitions:
if isinstance(k, str) and k.isalpha(): 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): def _complete_global_scope(self):
context = get_user_context(self._module_context, self._position) context = get_user_context(self._module_context, self._position)

View File

@@ -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) dict_key_str = _create_repr_string(literal_string, dict_key)
if dict_key_str.startswith(literal_string): if dict_key_str.startswith(literal_string):
n = dict_key_str[len(literal_string):-len(cut_end_quote) or None] 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( yield Completion(
inference_state, inference_state,
name, name,
stack=None, stack=None,
like_name_length=0, like_name_length=len(literal_string),
is_fuzzy=fuzzy is_fuzzy=fuzzy
) )

View File

@@ -305,8 +305,9 @@ _dict_keys_completion_tests = [
('strs["f]', 7, ['bar"', 'oo"']), ('strs["f]', 7, ['bar"', 'oo"']),
('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[1', 7, ['', '.1']),
('mixed[Non', 9, ['e']),
('casted["f', 9, ['3"', 'bar"', 'oo"']), ('casted["f', 9, ['3"', 'bar"', 'oo"']),
('casted["f"', 9, ['3', 'bar', 'oo']), ('casted["f"', 9, ['3', 'bar', 'oo']),
@@ -317,6 +318,7 @@ _dict_keys_completion_tests = [
('keywords["', None, ['a"']), ('keywords["', None, ['a"']),
('keywords[Non', None, ['e']), ('keywords[Non', None, ['e']),
('keywords[Fa', None, ['lse']), ('keywords[Fa', None, ['lse']),
('keywords[Tr', None, ['ue']),
('keywords[str', None, ['', 's']), ('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} keywords = {None: 1, False: 2, "a": 3}
''') ''')
line = None line = None
if isinstance(column, tuple):
raise NotImplementedError
line, column = column
comps = Script(code + added_code, line=line, column=column).completions() comps = Script(code + added_code, line=line, column=column).completions()
if Ellipsis in expected: if Ellipsis in expected:
# This means that global completions are part of this, so filter all of # This means that global completions are part of this, so filter all of
# that out. # 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] expected = [e for e in expected if e is not Ellipsis]
assert [c.complete for c in comps] == expected assert [c.complete for c in comps] == expected