Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Server returns snippets on completion request even if client doesn't support snippets #130

Closed
@bstaletic

Description

@bstaletic

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;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions