Closed
Description
Some clients don't support snippets and returning snippets in that case is a violation of the protocol.
Specifically, ycmd's capabilities don't mention textDocument.completion.completionItem.snippetSupport
, which is an optional capability. Not mentioning an optional capability should be taken to mean "not supported". In ycmd, violating protocol like this triggers an assertion.
Here are the messages exchanged between ycmd and the server:
initialize
request:
2019-11-25 01:12:07,180 - DEBUG - TX: Sending message: b'Content-Length: 906\r\n\r\n{"id":1,"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{"textDocument":{"codeAction":{"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"completion":{"completionItem":{"documentationFormat":["plaintext","markdown"]},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"contentFormat":["plaintext","markdown"]},"signatureHelp":{"signatureInformation":{"documentationFormat":["plaintext","markdown"],"parameterInformation":{"labelOffsetSupport":false}}}},"workspace":{"applyEdit":true}},"initializationOptions":{},"processId":14976,"rootPath":"/home/bstaletic/.vim/pack/bundle/start/YouCompleteMe/third_party/ycmd","rootUri":"file:///home/bstaletic/.vim/pack/bundle/start/YouCompleteMe/third_party/ycmd"}}'
2019-11-25 01:12:07,319 - DEBUG - RX: Received message: b'{"jsonrpc":"2.0","id":1,"result":{"capabilities":{"textDocumentSync":2,"completionProvider":{"triggerCharacters":[".","\\"","\'","/","@","<"],"resolveProvider":true},"codeActionProvider":true,"definitionProvider":true,"documentFormattingProvider":true,"documentHighlightProvider":true,"documentSymbolProvider":true,"executeCommandProvider":{"commands":["_typescript.applyWorkspaceEdit","_typescript.applyCodeAction","_typescript.applyRefactoring","_typescript.organizeImports","_typescript.applyRenameFile"]},"hoverProvider":true,"renameProvider":true,"referencesProvider":true,"signatureHelpProvider":{"triggerCharacters":["(",",","<"]},"workspaceSymbolProvider":true,"implementationProvider":true,"typeDefinitionProvider":true,"foldingRangeProvider":true,"callsProvider":true}}}'
initialized
notification:
2019-11-25 01:12:07,321 - DEBUG - TX: Sending notification: b'Content-Length: 52\r\n\r\n{"jsonrpc":"2.0","method":"initialized","params":{}}'
didChangeConfiguration
notification
2019-11-25 01:12:07,321 - DEBUG - TX: Sending notification: b'Content-Length: 86\r\n\r\n{"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{}}}'
didOpen
notification
2019-11-25 01:12:07,321 - DEBUG - TX: Sending notification: b'Content-Length: 702\r\n\r\n{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"languageId":"typescript","text":"\\nclass Foo {\\n /** Unicode string: \\u8bf4\\u8bdd */\\n methodA() {}\\n methodB() {}\\n methodC(\\n a: {\\n foo: string;\\n bar: number;\\n }\\n ) {}\\n}\\n\\nvar foo = new Foo();\\n\\n// line 17, column 6\\nfoo.mA\\n\\n\\n/**\\n * Class documentation\\n *\\n * Multi-line\\n */\\nclass Bar {\\n\\n /**\\n * Method documentation\\n */\\n testMethod() {}\\n}\\n\\nvar bar = new Bar();\\nbar.testMethod();\\nbar.nonExistingMethod();\\n\\nBar.apply()\\n\\nB\\u00e5r\\n","uri":"file:///home/bstaletic/.vim/pack/bundle/start/YouCompleteMe/third_party/ycmd/ycmd/tests/typescript/testdata/test.ts","version":1}}}'
publishDiagnostics
notification
2019-11-25 01:12:08,924 - DEBUG - RX: Received message: b'{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///home/bstaletic/.vim/pack/bundle/start/YouCompleteMe/third_party/ycmd/ycmd/tests/typescript/testdata/test.ts","diagnostics":[{"range":{"start":{"line":16,"character":4},"end":{"line":16,"character":6}},"message":"Property \'mA\' does not exist on type \'Foo\'.","severity":1,"code":2339,"source":"typescript"},{"range":{"start":{"line":34,"character":4},"end":{"line":34,"character":21}},"message":"Property \'nonExistingMethod\' does not exist on type \'Bar\'.","severity":1,"code":2339,"source":"typescript"},{"range":{"start":{"line":36,"character":4},"end":{"line":36,"character":11}},"message":"Expected 1-2 arguments, but got 0.","severity":1,"code":2554,"source":"typescript","relatedInformation":[{"location":{"uri":"file:///usr/lib/node_modules/typescript/lib/lib.es5.d.ts","range":{"start":{"line":277,"character":26},"end":{"line":277,"character":38}}},"message":"An argument for \'thisArg\' was not provided."}]},{"range":{"start":{"line":38,"character":0},"end":{"line":38,"character":3}},"message":"Cannot find name \'B\xc3\xa5r\'.","severity":1,"code":2304,"source":"typescript"},{"range":{"start":{"line":6,"character":4},"end":{"line":6,"character":5}},"message":"\'a\' is declared but its value is never read.","severity":4,"code":6133,"source":"typescript"}]}}'
textDocument/completion
request
2019-11-25 01:12:11,204 - DEBUG - TX: Sending message: b'Content-Length: 248\r\n\r\n{"id":2,"jsonrpc":"2.0","method":"textDocument/completion","params":{"position":{"character":6,"line":16},"textDocument":{"uri":"file:///home/bstaletic/.vim/pack/bundle/start/YouCompleteMe/third_party/ycmd/ycmd/tests/typescript/testdata/test.ts"}}}'
2019-11-25 01:12:11,233 - DEBUG - RX: Received message: b'{"jsonrpc":"2.0","id":2,"result":[{"label":"methodA","kind":2,"sortText":"0","commitCharacters":[".",",","("],"data":{"file":"/home/bstaletic/.vim/pack/bundle/start/YouCompleteMe/third_party/ycmd/ycmd/tests/typescript/testdata/test.ts","line":17,"offset":7,"entryNames":["methodA"]},"insertTextFormat":2},{"label":"methodB","kind":2,"sortText":"0","commitCharacters":[".",",","("],"data":{"file":"/home/bstaletic/.vim/pack/bundle/start/YouCompleteMe/third_party/ycmd/ycmd/tests/typescript/testdata/test.ts","line":17,"offset":7,"entryNames":["methodB"]},"insertTextFormat":2},{"label":"methodC","kind":2,"sortText":"0","commitCharacters":[".",",","("],"data":{"file":"/home/bstaletic/.vim/pack/bundle/start/YouCompleteMe/third_party/ycmd/ycmd/tests/typescript/testdata/test.ts","line":17,"offset":7,"entryNames":["methodC"]},"insertTextFormat":2}]}'
As you can see, insertTextFormat
always equals 2
, which indicates a snippet according to the following piece of the protocol specification.
/**
* Defines whether the insert text in a completion item should be interpreted as
* plain text or a snippet.
*/
namespace InsertTextFormat {
/**
* The primary text to be inserted is treated as a plain string.
*/
export const PlainText = 1;
/**
* The primary text to be inserted is treated as a snippet.
*
* A snippet can define tab stops and placeholders with `$1`, `$2`
* and `${3:foo}`. `$0` defines the final tab stop, it defaults to
* the end of the snippet. Placeholders with equal identifiers are linked,
* that is typing in one will update others too.
*/
export const Snippet = 2;
}