Repository: palantir/python-language-server
Pull request #811 (2020-05-14 14:25:27)

Fix completion with one arg and add optional class objects to completion list
1. Fixes the issue of 1 arg completion snippet.
Before the `$0` would be inserted literally, as insertTextFormat was missing.

2. Allows adding class objects to completion. 
When you want to call a classmethod or staticmethod, class name is completed with snippet as if we are instantiating it. This is super annoying, especially if init has a lot of args.  

goanpeca: Awesome @mpanarin !

- Did a quick pass and looks good. Just one question should the `should_include_class_objects` option be `False` by default?
- And does jedi offers such configuration or is something we would be doing just on the PyLS side?

Thanks!
mpanarin: @goanpeca 
1. I updated that part. I think the only people who would be annoyed by this features are the ones, who have snippets disabled. So I added a check for that and made `should_include_class_objects` enabled by default
2. No, I don't think so. We submit an issue or PR to jedi, but is it worth it?
goanpeca: 1 👍🏼
2 No need, just wondering about that.

Thanks!
benmezger: Thanks for this. Been having issues with the annoying arg completion, just tried out your fork and seems to be working fine over here. 
mpanarin: @goanpeca @ccordoba12 
As this one is ready, can it be merged?
Thanks!

Patch:
diff --git a/pyls/lsp.py b/pyls/lsp.py
index 36a8d84..a3f88d3 100644
--- a/pyls/lsp.py
+++ b/pyls/lsp.py
@@ -24,6 +24,13 @@ class CompletionItemKind(object):
     Color = 16
     File = 17
     Reference = 18
+    Folder = 19
+    EnumMember = 20
+    Constant = 21
+    Struct = 22
+    Event = 23
+    Operator = 24
+    TypeParameter = 25
 
 
 class DocumentHighlightKind(object):
diff --git a/pyls/plugins/jedi_completion.py b/pyls/plugins/jedi_completion.py
index 94a8b4e..921df62 100644
--- a/pyls/plugins/jedi_completion.py
+++ b/pyls/plugins/jedi_completion.py
@@ -66,8 +66,25 @@ def pyls_completions(config, document, position):
     snippet_support = completion_capabilities.get('completionItem', {}).get('snippetSupport')
 
     should_include_params = settings.get('include_params')
+    should_include_class_objects = settings.get('include_class_objects', True)
+
     include_params = snippet_support and should_include_params and use_snippets(document, position)
-    return [_format_completion(c, include_params) for c in completions] or None
+    include_class_objects = snippet_support and should_include_class_objects and use_snippets(document, position)
+
+    ready_completions = [
+        _format_completion(c, include_params)
+        for c in completions
+    ]
+
+    if include_class_objects:
+        for c in completions:
+            if c.type == 'class':
+                completion_dict = _format_completion(c, False)
+                completion_dict['kind'] = lsp.CompletionItemKind.TypeParameter
+                completion_dict['label'] += ' object'
+                ready_completions.append(completion_dict)
+
+    return ready_completions or None
 
 
 def is_exception_class(name):
@@ -153,6 +170,7 @@ def _format_completion(d, include_params=True):
             snippet += ')$0'
             completion['insertText'] = snippet
         elif len(positional_args) == 1:
+            completion['insertTextFormat'] = lsp.InsertTextFormat.Snippet
             completion['insertText'] = d.name + '($0)'
         else:
             completion['insertText'] = d.name + '()'
diff --git a/vscode-client/package.json b/vscode-client/package.json
index b5f6590..7e4ee59 100644
--- a/vscode-client/package.json
+++ b/vscode-client/package.json
@@ -55,6 +55,11 @@
                     "default": true,
                     "description": "Auto-completes methods and classes with tabstops for each parameter."
                 },
+                "pyls.plugins.jedi_completion.include_class_objects": {
+                    "type": "boolean",
+                    "default": true,
+                    "description": "Adds class objects as a separate completion item."
+                },
                 "pyls.plugins.jedi_completion.fuzzy": {
                     "type": "boolean",
                     "default": false,


Test patch:
diff --git a/test/plugins/test_completion.py b/test/plugins/test_completion.py
index c83eca0..2b8847f 100644
--- a/test/plugins/test_completion.py
+++ b/test/plugins/test_completion.py
@@ -193,6 +193,27 @@ def test_snippets_completion(config, workspace):
     com_position = {'line': 1, 'character': len(doc_snippets)}
     completions = pyls_jedi_completions(config, doc, com_position)
     assert completions[0]['insertText'] == 'defaultdict($0)'
+    assert completions[0]['insertTextFormat'] == lsp.InsertTextFormat.Snippet
+
+
+def test_completion_with_class_objects(config, workspace):
+    doc_text = 'class FOOBAR(Object): pass\nFOOB'
+    com_position = {'line': 1, 'character': 4}
+    doc = Document(DOC_URI, workspace, doc_text)
+    config.capabilities['textDocument'] = {
+        'completion': {'completionItem': {'snippetSupport': True}}}
+    config.update({'plugins': {'jedi_completion': {
+        'include_params': True,
+        'include_class_objects': True,
+    }}})
+    completions = pyls_jedi_completions(config, doc, com_position)
+    assert len(completions) == 2
+
+    assert completions[0]['label'] == 'FOOBAR'
+    assert completions[0]['kind'] == lsp.CompletionItemKind.Class
+
+    assert completions[1]['label'] == 'FOOBAR object'
+    assert completions[1]['kind'] == lsp.CompletionItemKind.TypeParameter
 
 
 def test_snippet_parsing(config, workspace):