diff --git a/jedi/api/strings.py b/jedi/api/strings.py index a2f1ee46..42ad10ec 100644 --- a/jedi/api/strings.py +++ b/jedi/api/strings.py @@ -54,7 +54,6 @@ def _completions_for_dicts(inference_state, dicts, literal_string, cut_end_quote 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): - n = dict_key_str[len(literal_string):-len(cut_end_quote) or None] name = StringName(inference_state, dict_key_str[:-len(cut_end_quote) or None]) yield Completion( inference_state, diff --git a/jedi/inference/compiled/access.py b/jedi/inference/compiled/access.py index 20a19698..d76fad84 100644 --- a/jedi/inference/compiled/access.py +++ b/jedi/inference/compiled/access.py @@ -417,6 +417,23 @@ class DirectObjectAccess(object): def get_api_type(self): return get_api_type(self._obj) + def get_array_type(self): + if isinstance(self._obj, dict): + return 'dict' + return None + + def get_key_paths(self): + def iter_partial_keys(): + # We could use list(keys()), but that might take a lot more memory. + for (i, k) in enumerate(self._obj.keys()): + # Limit key listing at some point. This is artificial, but this + # way we don't get stalled because of slow completions + if i > 50: + break + yield k + + return [self._create_access_path(k) for k in iter_partial_keys()] + def get_access_path_tuples(self): accesses = [create_access(self._inference_state, o) for o in self._get_objects_path()] return [(access.py__name__(), access) for access in accesses] diff --git a/jedi/inference/compiled/value.py b/jedi/inference/compiled/value.py index 9a7dffa3..10bba56c 100644 --- a/jedi/inference/compiled/value.py +++ b/jedi/inference/compiled/value.py @@ -281,6 +281,16 @@ class CompiledObject(Value): return CompiledModuleContext(self) return CompiledContext(self) + @property + def array_type(self): + return self.access_handle.get_array_type() + + def get_key_values(self): + return [ + create_from_access_path(self.inference_state, k) + for k in self.access_handle.get_key_paths() + ] + class CompiledName(AbstractNameDefinition): def __init__(self, inference_state, parent_context, name): diff --git a/test/test_api/test_completion.py b/test/test_api/test_completion.py index 4ff2bb4f..38463f1b 100644 --- a/test/test_api/test_completion.py +++ b/test/test_api/test_completion.py @@ -342,7 +342,7 @@ def test_dict_keys_completions(Script, added_code, column, expected, skip_pre_py keywords = {None: 1, False: 2, "a": 3} ''') line = None - comps = Script(code + added_code, line=line, column=column).completions() + comps = Script(code + added_code).complete(line=line, column=column) if Ellipsis in expected: # This means that global completions are part of this, so filter all of # that out. @@ -365,4 +365,4 @@ def test_fuzzy_match(): def test_ellipsis_completion(Script): - assert Script('...').completions() == [] + assert Script('...').complete() == [] diff --git a/test/test_api/test_interpreter.py b/test/test_api/test_interpreter.py index 8b5343be..3f5aeb49 100644 --- a/test/test_api/test_interpreter.py +++ b/test/test_api/test_interpreter.py @@ -575,3 +575,34 @@ def test_param_annotation_completion(class_is_findable): code = 'def CallFoo(x: Foo):\n x.ba' def_, = jedi.Interpreter(code, [locals()]).complete() assert def_.name == 'bar' + + +@pytest.mark.skipif(sys.version_info[0] == 2, reason="Ignore Python 2, because EOL") +@pytest.mark.parametrize( + 'code, column, expected', [ + ('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"']), + + ('mixed[', 6, [r"'a\\sdf'", '1', '1.1', "b'foo'", Ellipsis]), + ('mixed[1', 7, ['', '.1']), + ('mixed[Non', 9, ['e']), + + ('implicit[10', None, ['00']), + ] +) +def test_dict_completion(code, column, expected): + strs = {'asdf': 1, u"""foo""": 2, r'fbar': 3} + mixed = {1: 2, 1.10: 4, None: 6, r'a\sdf': 8, b'foo': 9} + + namespaces = [locals(), {'implicit': {1000: 3}}] + comps = jedi.Interpreter(code, namespaces).complete(column=column) + 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 and not c.is_keyword] + expected = [e for e in expected if e is not Ellipsis] + + assert [c.complete for c in comps] == expected