diff --git a/pylsp/plugins/jedi_completion.py b/pylsp/plugins/jedi_completion.py index 2ecf0bec..65f75239 100644 --- a/pylsp/plugins/jedi_completion.py +++ b/pylsp/plugins/jedi_completion.py @@ -88,6 +88,7 @@ def pylsp_completions(config, document, position): include_params=include_params if c.type in ["class", "function"] else False, resolve=resolve_eagerly, resolve_label_or_snippet=(i < max_to_resolve), + snippet_support=snippet_support, ) for i, c in enumerate(completions) ] @@ -102,6 +103,7 @@ def pylsp_completions(config, document, position): include_params=False, resolve=resolve_eagerly, resolve_label_or_snippet=(i < max_to_resolve), + snippet_support=snippet_support, ) completion_dict["kind"] = lsp.CompletionItemKind.TypeParameter completion_dict["label"] += " object" @@ -116,6 +118,7 @@ def pylsp_completions(config, document, position): include_params=False, resolve=resolve_eagerly, resolve_label_or_snippet=(i < max_to_resolve), + snippet_support=snippet_support, ) completion_dict["kind"] = lsp.CompletionItemKind.TypeParameter completion_dict["label"] += " object" @@ -226,6 +229,7 @@ def _format_completion( include_params=True, resolve=False, resolve_label_or_snippet=False, + snippet_support=False, ): completion = { "label": _label(d, resolve_label_or_snippet), @@ -240,16 +244,20 @@ def _format_completion( # Adjustments for file completions if d.type == "path": path = os.path.normpath(d.name) - path = path.replace("\\", "\\\\") - path = path.replace("/", "\\/") - # If the completion ends with os.sep, it means it's a directory. So we add an escaped os.sep - # at the end to ease additional file completions. + # If the completion ends with os.sep, it means it's a directory. So we add os.sep at the end + # to ease additional file completions. if d.name.endswith(os.sep): if os.name == "nt": - path = path + "\\\\" + path = path + "\\" else: - path = path + "\\/" + path = path + "/" + + # Escape to prevent conflicts with the code snippets grammer + # See also https://github.com/python-lsp/python-lsp-server/issues/373 + if snippet_support: + path = path.replace("\\", "\\\\") + path = path.replace("/", "\\/") completion["insertText"] = path diff --git a/test/plugins/test_completion.py b/test/plugins/test_completion.py index 00a54eb7..f64e81ed 100644 --- a/test/plugins/test_completion.py +++ b/test/plugins/test_completion.py @@ -583,9 +583,18 @@ def test_file_completions(workspace, tmpdir): # Check completions assert len(completions) == 2 assert [c["kind"] == lsp.CompletionItemKind.File for c in completions] - assert ( - completions[0]["insertText"] == ("bar" + "\\\\") - if os.name == "nt" - else ("bar" + "\\/") + assert completions[0]["insertText"] == ( + ("bar" + "\\") if os.name == "nt" else ("bar" + "/") + ) + assert completions[1]["insertText"] == 'foo.txt"' + + # When snippets are supported, ensure that path separators are escaped. + support_snippet = { + "textDocument": {"completion": {"completionItem": {"snippetSupport": True}}} + } + doc._config.capabilities.update(support_snippet) + completions = pylsp_jedi_completions(doc._config, doc, com_position) + assert completions[0]["insertText"] == ( + ("bar" + "\\\\") if os.name == "nt" else ("bar" + "\\/") ) assert completions[1]["insertText"] == 'foo.txt"'