Shougo / neosnippet.vim

neo-snippet plugin
Other
1.12k stars 108 forks source link

Snippets from LSP: `textEdit` instead of `insertText` #487

Closed przemkovv closed 4 years ago

przemkovv commented 4 years ago

I encountered two issues with expanding a snippet from one of the LSP servers (rust-analyzer). At first, according to the documentation (https://microsoft.github.io/language-server-protocol/specification#textDocument_completion) there are at least two ways to find what was completed:

  • The insertText is subject to interpretation by the client-side.
  • Some tools might not take the string literally. For example
  • VS Code when code complete is requested in this example con<cursor position>
  • and a completion item with an insertText of console is provided it
  • will only insert sole. Therefore it is recommended to use textEdit instead
  • since it avoids additional client side interpretation.

textEdit field is also possible. The rust-analyzer follows the other latter way, hence, I receive an error that insertText is not in a dictionary here: https://github.com/Shougo/neosnippet.vim/blob/master/autoload/neosnippet/mappings.vim#L188 . The solution, I think would be to add checking if textEdit exists:

    if has_key(lspitem, 'textEdit')
      let snippet = lspitem.textEdit.newText
      let snippet_trigger = lspitem.textEdit.newText
      let cur_text = cur_text[: -1-len(snippet_trigger)]
      return [cur_text, snippet, {'lspitem': has_lspitem}]
    elseif get(lspitem, 'insertTextFormat', -1) == 2
      let snippet = lspitem.insertText
      let snippet_trigger = lspitem.insertText
      let cur_text = cur_text[: -1-len(snippet_trigger)]
      return [cur_text, snippet, {'lspitem': has_lspitem}]
    endif

That solves the first issue. There is also a second. according to the LSP documentation, $0 defines the final tab stop:

 * 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.

and when completion is done I receive this:

transformation.index_axis_move(axis, index)<|0|>

where axis and index are correct, but some garbage left at the end. Probably because the snippet ends with $0. Based on some other solution (https://github.com/thomasfaingnaert/vim-lsp-neosnippet/blob/master/autoload/lsp_neosnippet.vim), I modified the above code in the following way:

  if has_key(lspitem, 'textEdit')
      let snippet = lspitem.textEdit.newText
      let snippet = substitute(snippet, '\$\(\d\+\)', '${\1}', 'g')
      let snippet = substitute(snippet, "'", "''", 'g')
      let snippet_trigger = lspitem.textEdit.newText
      let cur_text = cur_text[: -1-len(snippet_trigger)]
      return [cur_text, snippet, {'lspitem': has_lspitem}]

and the completion with expanding snippets works correctly. All $0, $1, etc. will be replaced by version with braces. Do you think it is possible to fix it that way? Or maybe you have another idea?

edit: fix bug in the last snippet.

Shougo commented 4 years ago

OK. I have fixed them. Please test it.

przemkovv commented 4 years ago

It works like a charm! Thanks!

Shougo commented 4 years ago

OK.