Repository: sublimelsp/LSP

Issue:
Snippets Clangd C++
**Describe the bug**
C++ completion using clangd is giving snippets despite these settings:

Main User Preferences
```json
"auto_complete_include_snippets": false,
"auto_complete_include_snippets_when_typing": false,
"ignored_snippets": [
    "C++/*",		
],

```

LSP Settings
```json
"inhibit_snippet_completions": false,
"clients": {
    "clangd": {
	"enabled": true,
	"command": [
			"clangd",
			"--background-index"
	],
    },
}
```


**To Reproduce**
You could for example type: `for<tab>` and you will be snippetted! (<- that's should be correct English! :)

**Expected behavior**
Not heaving snippets in the auto completion list!

**Screenshots**
![image](https://user-images.githubusercontent.com/34722701/152798817-0571e7b1-793f-4ea8-83d9-f582372258b6.png)


**Environment**
- OS: Windows 10 x64
- Sublime Text version: 4126
- LSP version: 1.15.0
- Language servers used: clangd: 13.0.0


**Additional context**
If I would turn of clangd in the clients
```json
"clangd": {
    "enabled": false,
}
```
then these specific snippets will not show up. At this point I am assuming that they are provided by clangd. The default c++ snippets provided in the default Sublime packages - if turned on - have a different layout.



Discussion:
These snippets from clangd come from Sema:

https://github.com/llvm/llvm-project/blob/d3606a3375d258c67c061a97901faca53c4032da/clang-tools-extra/clangd/CodeComplete.h#L52

It's enabled when the editor advertises snippet support:

https://github.com/llvm/llvm-project/blob/bd6c6974f5ea1626a6bb1b6adea59616d70ff3a8/clang-tools-extra/clangd/ClangdLSPServer.cpp#L497

https://github.com/llvm/llvm-project/blob/ce94432702bf42a0b95a2693aa47177f37dd0bb3/clang-tools-extra/clangd/Protocol.h#L420-L422

Although "snippet support" isn't exactly the same as "I want code snippets like for-loops". "snippet support" only means the editor is able to handle placeholder values for things like functions. I see there is already a feature request for that here https://github.com/clangd/clangd/issues/727
Note that this client always advertises snippet support: https://github.com/sublimelsp/LSP/blob/95323277afd7c51376eeb7a9044b2205655f3c1c/plugin/core/sessions.py#L231
Thanks for all the feedback. For now there does not seem to be a simple option for this other than changing either clangd source or the LSP source. I do hope this gets configurable in the future.!
Should LSP just follow ST's global "auto_complete_include_snippets" setting? It would require only a small change in the plugin code:
```diff
diff --git a/plugin/documents.py b/plugin/documents.py
index e233550..8c11fd1 100644
--- a/plugin/documents.py
+++ b/plugin/documents.py
@@ -698,6 +698,7 @@ class DocumentSyncListener(sublime_plugin.ViewEventListener, AbstractViewListene
         errors = []  # type: List[Error]
         flags = 0  # int
         prefs = userprefs()
+        include_snippets = self.view.settings().get("auto_complete_include_snippets")
         if prefs.inhibit_snippet_completions:
             flags |= sublime.INHIBIT_EXPLICIT_COMPLETIONS
         if prefs.inhibit_word_completions:
@@ -721,7 +722,7 @@ class DocumentSyncListener(sublime_plugin.ViewEventListener, AbstractViewListene
             can_resolve_completion_items = session.has_capability('completionProvider.resolveProvider')
             items.extend(
                 format_completion(response_item, index, can_resolve_completion_items, session.config.name)
-                for index, response_item in enumerate(response_items))
+                for index, response_item in enumerate(response_items) if include_snippets or response_item.get("kind", 0) != 15)
         if items:
             flags |= sublime.INHIBIT_REORDER
         if errors:
```

Snippets from the language server would still be shown if someone has only disabled the "auto_complete_include_snippets_when_typing" setting, but I don't think there is a feasible way to distinguish whether the `on_query_completions` API method was called during typing, or from manually triggering completions via the keybinding. But for people who don't want to see any snippets at all and have `"auto_complete_include_snippets": false`, it should work (assuming the language server uses the correct "kind" for the completion items).
I'm a bit worried about handling it that way because I think that the meaning of a snippet is a bit different when we talk about LSP snippets and ST snippets. The ST snippets are typically context-free snippets of code that you can insert anywhere while LSP snippet-type completions can still be context sensitive. The only thing that makes them have snippet type is the fact they they include a placeholder.

So if you are completing a method name and it happens to be of a snippet type, you would still expect to see it even with `auto_complete_include_snippets` disabled because it's a very relevant completion in this case and not something that gets in the way like context-free completions.
Now that I look back at this thread, that's basically what @rwols said in https://github.com/sublimelsp/LSP/issues/1939#issuecomment-1043529647 and in the linked clang issue. So I would say that it's something that should be controlled by a server option and not us.
I think the "snippet support" syntax with `$0`, `$1`, .... tab stops and placeholders, and the "Snippet" (15) CompletionItemKind are two different things. Your example of a method name containing a tab stop like `foo($0)` should still get `"kind": CompletionItemKind.Method` (2), right? The specs describe the "kind" to be used for the icon in the completions popup, and not to designate whether it contains snippet syntax or not:

```js
         /**
	 * The kind of this completion item. Based of the kind
	 * an icon is chosen by the editor. The standardized set
	 * of available values is defined in `CompletionItemKind`.
	 */
	kind?: [CompletionItemKind](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItemKind);
```

I think the meaning of the icons (= CompletionItem.kind) in VS Code are quite similar to ST; a "snippet" is usually a chunk of code, often containing multiple lines and also often - but not necessarily - containing tab stops and/or placeholders. These are usually things like "if"/"for" blocks. But for example if a language server wants to provide a "lorem ipsum" text block, it should probably also use the CompletionItemKind.Snippet, even when there is no tab stop and even if the server doesn't have the "snippet support" capability.
You are right, I've confused those two.
In that case I agree with your proposed solution.

Resolving patch:
diff --git a/plugin/core/protocol.py b/plugin/core/protocol.py
--- a/plugin/core/protocol.py
+++ b/plugin/core/protocol.py
@@ -23,6 +23,34 @@ class DiagnosticTag:
     Deprecated = 2
 
 
+class CompletionItemKind:
+    Text = 1
+    Method = 2
+    Function = 3
+    Constructor = 4
+    Field = 5
+    Variable = 6
+    Class = 7
+    Interface = 8
+    Module = 9
+    Property = 10
+    Unit = 11
+    Value = 12
+    Enum = 13
+    Keyword = 14
+    Snippet = 15
+    Color = 16
+    File = 17
+    Reference = 18
+    Folder = 19
+    EnumMember = 20
+    Constant = 21
+    Struct = 22
+    Event = 23
+    Operator = 24
+    TypeParameter = 25
+
+
 class CompletionItemTag:
     Deprecated = 1
 
diff --git a/plugin/documents.py b/plugin/documents.py
--- a/plugin/documents.py
+++ b/plugin/documents.py
@@ -5,6 +5,7 @@
 from .core.logging import debug
 from .core.promise import Promise
 from .core.protocol import CompletionItem
+from .core.protocol import CompletionItemKind
 from .core.protocol import CompletionList
 from .core.protocol import Diagnostic
 from .core.protocol import DiagnosticSeverity
@@ -702,6 +703,7 @@ def _on_all_settled(
             flags |= sublime.INHIBIT_EXPLICIT_COMPLETIONS
         if prefs.inhibit_word_completions:
             flags |= sublime.INHIBIT_WORD_COMPLETIONS
+        include_snippets = self.view.settings().get("auto_complete_include_snippets")
         for response, session_name in responses:
             if isinstance(response, Error):
                 errors.append(response)
@@ -721,7 +723,8 @@ def _on_all_settled(
             can_resolve_completion_items = session.has_capability('completionProvider.resolveProvider')
             items.extend(
                 format_completion(response_item, index, can_resolve_completion_items, session.config.name)
-                for index, response_item in enumerate(response_items))
+                for index, response_item in enumerate(response_items)
+                if include_snippets or response_item.get("kind") != CompletionItemKind.Snippet)
         if items:
             flags |= sublime.INHIBIT_REORDER
         if errors:


Test patch: