diff --git a/pyls/plugins/jedi_completion.py b/pyls/plugins/jedi_completion.py index 34c90e68..8ec57b31 100644 --- a/pyls/plugins/jedi_completion.py +++ b/pyls/plugins/jedi_completion.py @@ -1,5 +1,6 @@ # Copyright 2017 Palantir Technologies, Inc. import logging +from itertools import chain import parso from pyls import hookimpl, lsp, _utils @@ -56,7 +57,8 @@ def pyls_completions(config, document, position): should_include_params = settings.get('include_params') include_params = (snippet_support and should_include_params and use_snippets(document, position)) - return [_format_completion(d, include_params) for d in definitions] or None + + return list(chain.from_iterable(_format_completions(d, include_params) for d in definitions)) def use_snippets(document, position): @@ -81,9 +83,15 @@ def use_snippets(document, position): return tokens.children[0].type not in _IMPORTS -def _format_completion(d, include_params=True): +def _format_completions(d, include_params): + yield _format_completion(d, False) + if include_params and hasattr(d, 'params'): + yield _format_completion(d, True) + + +def _format_completion(d, include_params): completion = { - 'label': _label(d), + 'label': d.name, 'kind': _TYPE_MAP.get(d.type), 'detail': _detail(d), 'documentation': _utils.format_docstring(d.docstring()), @@ -91,29 +99,21 @@ def _format_completion(d, include_params=True): 'insertText': d.name } - if include_params and hasattr(d, 'params') and d.params: - positional_args = [param for param in d.params if '=' not in param.description] - + if include_params: # For completions with params, we can generate a snippet instead - completion['insertTextFormat'] = lsp.InsertTextFormat.Snippet - snippet = d.name + '(' - for i, param in enumerate(positional_args): - name = param.name if param.name != '/' else '\\/' - snippet += '${%s:%s}' % (i + 1, name) - if i < len(positional_args) - 1: - snippet += ', ' - snippet += ')$0' - completion['insertText'] = snippet - - return completion + def fix_name(name): + return '\\/' if name == '/' else name -def _label(definition): - if definition.type in ('function', 'method') and hasattr(definition, 'params'): - params = ', '.join([param.name for param in definition.params]) - return '{}({})'.format(definition.name, params) + names = [fix_name(param.name) for param in d.params if '=' not in param.description] + snippets = ('${{{}:{}}}'.format(i + 1, p) for i, p in enumerate(names)) + completion.update( + insertTextFormat=lsp.InsertTextFormat.Snippet, + label='{}({})'.format(d.name, ', '.join(names)), + insertText='{}({})$0'.format(d.name, ', '.join(snippets)) + ) - return definition.name + return completion def _detail(definition): diff --git a/test/plugins/test_completion.py b/test/plugins/test_completion.py index 3f09eab7..c6f80b5e 100644 --- a/test/plugins/test_completion.py +++ b/test/plugins/test_completion.py @@ -58,7 +58,7 @@ def test_jedi_completion(config): items = pyls_jedi_completions(config, doc, com_position) assert items - assert items[0]['label'] == 'isabs(path)' + assert items[0]['label'] == 'isabs' # Test we don't throw with big character pyls_jedi_completions(config, doc, {'line': 1, 'character': 1000}) @@ -84,7 +84,7 @@ def test_jedi_completion_ordering(config): items = {c['label']: c['sortText'] for c in completions} # And that 'hidden' functions come after unhidden ones - assert items['hello()'] < items['_a_hello()'] + assert items['hello'] < items['_a_hello'] def test_jedi_property_completion(config): @@ -108,7 +108,7 @@ def test_jedi_method_completion(config): config.update({'plugins': {'jedi_completion': {'include_params': True}}}) completions = pyls_jedi_completions(config, doc, com_position) - everyone_method = [completion for completion in completions if completion['label'] == 'everyone(a, b, c, d)'][0] + everyone_method = [completion for completion in completions if completion['label'] == 'everyone(a, b)'][0] # Ensure we only generate snippets for positional args assert everyone_method['insertTextFormat'] == lsp.InsertTextFormat.Snippet @@ -118,7 +118,7 @@ def test_jedi_method_completion(config): config.update({'plugins': {'jedi_completion': {'include_params': False}}}) completions = pyls_jedi_completions(config, doc, com_position) - everyone_method = [completion for completion in completions if completion['label'] == 'everyone(a, b, c, d)'][0] + everyone_method = [completion for completion in completions if completion['label'] == 'everyone'][0] assert 'insertTextFormat' not in everyone_method assert everyone_method['insertText'] == 'everyone' @@ -170,8 +170,11 @@ def test_snippets_completion(config): com_position = {'line': 1, 'character': len(doc_snippets)} completions = pyls_jedi_completions(config, doc, com_position) - out = 'defaultdict(${1:default_factory}, ${2:iterable}, ${3:kwargs})$0' - assert completions[0]['insertText'] == out + assert len(completions) == 2 + assert completions[0]['insertText'] == 'defaultdict' + assert completions[0]['label'] == 'defaultdict' + assert completions[1]['insertText'] == 'defaultdict(${1:default_factory}, ${2:iterable}, ${3:kwargs})$0' + assert completions[1]['label'] == 'defaultdict(default_factory, iterable, kwargs)' def test_snippet_parsing(config): @@ -183,7 +186,7 @@ def test_snippet_parsing(config): config.update({'plugins': {'jedi_completion': {'include_params': True}}}) completions = pyls_jedi_completions(config, doc, completion_position) out = 'logical_and(${1:x1}, ${2:x2}, ${3:\\/}, ${4:*})$0' - assert completions[0]['insertText'] == out + assert completions[1]['insertText'] == out def test_multiline_snippets(config): @@ -217,7 +220,7 @@ def test_multistatement_snippet(config): doc = Document(DOC_URI, document) position = {'line': 0, 'character': len(document)} completions = pyls_jedi_completions(config, doc, position) - assert completions[0]['insertText'] == 'date(${1:year}, ${2:month}, ${3:day})$0' + assert completions[1]['insertText'] == 'date(${1:year}, ${2:month}, ${3:day})$0' def test_jedi_completion_extra_paths(config, tmpdir): @@ -248,7 +251,7 @@ def spam(): # After 'foo.s' with extra paths com_position = {'line': 1, 'character': 5} completions = pyls_jedi_completions(config, doc, com_position) - assert completions[0]['label'] == 'spam()' + assert completions[0]['label'] == 'spam' @pytest.mark.skipif(PY2 or not LINUX or not CI, reason="tested on linux and python 3 only")