From 7d7c4d89c1f6416301a559b128e0687d0b5b9aa8 Mon Sep 17 00:00:00 2001 From: muffinmad Date: Mon, 18 Nov 2019 09:43:10 +0200 Subject: [PATCH 1/5] Show completions with and without params --- pyls/plugins/jedi_completion.py | 56 ++++++++++++++++++++------------- test/plugins/test_completion.py | 26 ++++++++++++--- 2 files changed, 55 insertions(+), 27 deletions(-) diff --git a/pyls/plugins/jedi_completion.py b/pyls/plugins/jedi_completion.py index b310e81f..72e5ecc7 100644 --- a/pyls/plugins/jedi_completion.py +++ b/pyls/plugins/jedi_completion.py @@ -1,6 +1,7 @@ # Copyright 2017 Palantir Technologies, Inc. import logging import parso +from itertools import chain from pyls import hookimpl, lsp, _utils log = logging.getLogger(__name__) @@ -42,6 +43,11 @@ # Types of parso nodes for which snippet is not included in the completion _IMPORTS = ('import_name', 'import_from') +# Provide completions with snippets +_USE_PARAMS_OFF = 0 +_USE_PARAMS_ON = 1 +_USE_PARAMS_BOTH = 2 + @hookimpl def pyls_completions(config, document, position): @@ -56,7 +62,15 @@ 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 + if include_params: + if settings.get('include_params_both'): + include_params = _USE_PARAMS_BOTH + else: + include_params = _USE_PARAMS_ON + else: + include_params = _USE_PARAMS_OFF + + return list(chain.from_iterable(_format_completions(d, include_params) for d in definitions)) def use_snippets(document, position): @@ -81,9 +95,18 @@ 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): + if include_params == _USE_PARAMS_BOTH: + yield _format_completion(d, False) + if hasattr(d, 'params'): + yield _format_completion(d, True) + else: + yield _format_completion(d, include_params == _USE_PARAMS_ON and hasattr(d, 'params')) + + +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,30 +114,19 @@ 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): - snippet += '${%s:%s}' % (i + 1, param.name) - if i < len(positional_args) - 1: - snippet += ', ' - snippet += ')$0' - completion['insertText'] = snippet + names = [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 completion -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) - - return definition.name - - def _detail(definition): try: return definition.parent().full_name or '' diff --git a/test/plugins/test_completion.py b/test/plugins/test_completion.py index 1980016c..6b875723 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' @@ -174,6 +174,22 @@ def test_snippets_completion(config): assert completions[0]['insertText'] == out +def test_snippets_both(config): + document = 'from datetime import date; a=date' + doc = Document(DOC_URI, document) + config.capabilities['textDocument'] = { + 'completion': {'completionItem': {'snippetSupport': True}}} + config.update({'plugins': {'jedi_completion': {'include_params': True, 'include_params_both': True}}}) + + position = {'line': 0, 'character': len(document)} + completions = pyls_jedi_completions(config, doc, position) + assert len(completions) == 2 + assert completions[0]['insertText'] == 'date' + assert completions[0]['label'] == 'date' + assert completions[1]['insertText'] == 'date(${1:year}, ${2:month}, ${3:day})$0' + assert completions[1]['label'] == 'date(year, month, day)' + + def test_multiline_snippets(config): document = 'from datetime import\\\n date,\\\n datetime \na=date' doc = Document(DOC_URI, document) @@ -236,7 +252,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") From 3290fe4c1afce6dc0cae4fdeb2d856dcfabef47d Mon Sep 17 00:00:00 2001 From: muffinmad Date: Mon, 18 Nov 2019 12:48:17 +0200 Subject: [PATCH 2/5] Fixed import order --- pyls/plugins/jedi_completion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyls/plugins/jedi_completion.py b/pyls/plugins/jedi_completion.py index 72e5ecc7..039eaaa5 100644 --- a/pyls/plugins/jedi_completion.py +++ b/pyls/plugins/jedi_completion.py @@ -1,7 +1,7 @@ # Copyright 2017 Palantir Technologies, Inc. import logging -import parso from itertools import chain +import parso from pyls import hookimpl, lsp, _utils log = logging.getLogger(__name__) From 4fc63a42727cdcddde342d3896949bf71176a79f Mon Sep 17 00:00:00 2001 From: muffinmad Date: Tue, 26 Nov 2019 12:33:51 +0200 Subject: [PATCH 3/5] Always include both completion with and without params --- pyls/plugins/jedi_completion.py | 21 +++------------------ test/plugins/test_completion.py | 23 +++++------------------ 2 files changed, 8 insertions(+), 36 deletions(-) diff --git a/pyls/plugins/jedi_completion.py b/pyls/plugins/jedi_completion.py index 039eaaa5..b1d08a13 100644 --- a/pyls/plugins/jedi_completion.py +++ b/pyls/plugins/jedi_completion.py @@ -43,11 +43,6 @@ # Types of parso nodes for which snippet is not included in the completion _IMPORTS = ('import_name', 'import_from') -# Provide completions with snippets -_USE_PARAMS_OFF = 0 -_USE_PARAMS_ON = 1 -_USE_PARAMS_BOTH = 2 - @hookimpl def pyls_completions(config, document, position): @@ -62,13 +57,6 @@ 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)) - if include_params: - if settings.get('include_params_both'): - include_params = _USE_PARAMS_BOTH - else: - include_params = _USE_PARAMS_ON - else: - include_params = _USE_PARAMS_OFF return list(chain.from_iterable(_format_completions(d, include_params) for d in definitions)) @@ -96,12 +84,9 @@ def use_snippets(document, position): def _format_completions(d, include_params): - if include_params == _USE_PARAMS_BOTH: - yield _format_completion(d, False) - if hasattr(d, 'params'): - yield _format_completion(d, True) - else: - yield _format_completion(d, include_params == _USE_PARAMS_ON and hasattr(d, 'params')) + yield _format_completion(d, False) + if hasattr(d, 'params'): + yield _format_completion(d, True) def _format_completion(d, include_params): diff --git a/test/plugins/test_completion.py b/test/plugins/test_completion.py index 6b875723..e1d21ae5 100644 --- a/test/plugins/test_completion.py +++ b/test/plugins/test_completion.py @@ -170,24 +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 - - -def test_snippets_both(config): - document = 'from datetime import date; a=date' - doc = Document(DOC_URI, document) - config.capabilities['textDocument'] = { - 'completion': {'completionItem': {'snippetSupport': True}}} - config.update({'plugins': {'jedi_completion': {'include_params': True, 'include_params_both': True}}}) - - position = {'line': 0, 'character': len(document)} - completions = pyls_jedi_completions(config, doc, position) assert len(completions) == 2 - assert completions[0]['insertText'] == 'date' - assert completions[0]['label'] == 'date' - assert completions[1]['insertText'] == 'date(${1:year}, ${2:month}, ${3:day})$0' - assert completions[1]['label'] == 'date(year, month, day)' + 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_multiline_snippets(config): @@ -221,7 +208,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): From d1c50f0c66c62bae28a259f58fbe070565c5ea33 Mon Sep 17 00:00:00 2001 From: muffinmad Date: Tue, 26 Nov 2019 13:45:39 +0200 Subject: [PATCH 4/5] fixed test: completion with param is second --- test/plugins/test_completion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/plugins/test_completion.py b/test/plugins/test_completion.py index a5e34889..c6f80b5e 100644 --- a/test/plugins/test_completion.py +++ b/test/plugins/test_completion.py @@ -186,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): From b34f6e113d3dcb9c65f15f8af0dc7a928b9991f2 Mon Sep 17 00:00:00 2001 From: muffinmad Date: Tue, 26 Nov 2019 14:05:04 +0200 Subject: [PATCH 5/5] Respect `include_params` --- pyls/plugins/jedi_completion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyls/plugins/jedi_completion.py b/pyls/plugins/jedi_completion.py index 2f8b6b79..8ec57b31 100644 --- a/pyls/plugins/jedi_completion.py +++ b/pyls/plugins/jedi_completion.py @@ -85,7 +85,7 @@ def use_snippets(document, position): def _format_completions(d, include_params): yield _format_completion(d, False) - if hasattr(d, 'params'): + if include_params and hasattr(d, 'params'): yield _format_completion(d, True)