deoplete-plugins / deoplete-lsp

LSP Completion source for deoplete

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`deoplete-lsp` incorrectly processes candidates when using `textEdit`

kdheepak opened this issue · comments

Thank you for writing deoplete-lsp! With the built in neovim lsp source, deoplete will become an even more powerful completion framework ( if that's even possible :) )!

I think some fields in the response for deoplete-lsp are not being processed properly. This is an example of a response when I type pri from LanguageServer.jl in a Julia buffer.

2020-06-10 06:46:38 [100] INFO {'isIncomplete': True, 'items': [{'label': 'primitive', 'textEdit': {'range': {'end': {'character': 3, 'line': 27}, 'start': {'character': 3, 'line': 27}}, 'newText': 'mitive type $1\n    $0\nend'}, 'documentation': 'primitive', 'kind': 14, 'insertTextFormat': 2}, {'label': 'printstyled', 'textEdit': {'range': {'end': {'character': 3, 'line': 27}, 'start': {'character': 3, 'line': 27}}, 'newText': 'ntstyled'}, 'documentation': {'kind': 'markdown', 'value': '```\nprintstyled([io], xs...; bold::Bool=false, color::Union{Symbol,Int}=:normal)\n```\n\nPrint `xs` in a color specified as a symbol or integer, optionally in bold.\n\n`color` may take any of the values `:normal`, `:default`, `:bold`, `:black`, `:blink`, `:blue`, `:cyan`, `:green`, `:hidden`, `:light_black`, `:light_blue`, `:light_cyan`, `:light_green`, `:light_magenta`, `:light_red`, `:light_yellow`, `:magenta`, `:nothing`, `:red`, `:reverse`, `:underline`, `:white`, or  `:yellow` or an integer between 0 and 255 inclusive. Note that not all terminals support 256 colors. If the keyword `bold` is given as `true`, the result will be printed in bold.\n'}, 'kind': 3, 'insertTextFormat': 2}, {'label': 'println', 'textEdit': {'range': {'end': {'character': 3, 'line': 27}, 'start': {'character': 3, 'line': 27}}, 'newText': 'ntln'}, 'documentation': {'kind': 'markdown', 'value': '```\nprintln([io::IO], xs...)\n```\n\nPrint (using [`print`](@ref)) `xs` followed by a newline. If `io` is not supplied, prints to [`stdout`](@ref).\n\n### Examples\n\n```julia\njulia> println("Hello, world")\nHello, world\n\njulia> io = IOBuffer();\n\njulia> println(io, "Hello, world")\n\njulia> String(take!(io))\n"Hello, world\\n"\n```\n'}, 'kind': 3, 'insertTextFormat': 2}, {'label': 'print', 'textEdit': {'range': {'end': {'character': 3, 'line': 27}, 'start': {'character': 3, 'line': 27}}, 'newText': 'nt'}, 'documentation': {'kind': 'markdown', 'value': '```\nprint([io::IO], xs...)\n```\n\nWrite to `io` (or to the default output stream [`stdout`](@ref) if `io` is not given) a canonical (un-decorated) text representation. The representation used by `print` includes minimal formatting and tries to avoid Julia-specific details.\n\n`print` falls back to calling `show`, so most types should just define `show`. Define `print` if your type has a separate "plain" representation. For example, `show` displays strings with quotes, and `print` displays strings without quotes.\n\n[`string`](@ref) returns the output of `print` as a string.\n\n### Examples\n\n```julia\njulia> print("Hello World!")\nHello World!\njulia> io = IOBuffer();\n\njulia> print(io, "Hello", \' \', :World!)\n\njulia> String(take!(io))\n"Hello World!"\n```\n'}, 'kind': 3, 'insertTextFormat': 2}]}
2020-06-10 06:46:38 [102] INFO {'label': 'primitive', 'textEdit': {'range': {'end': {'character': 3, 'line': 27}, 'start': {'character': 3, 'line': 27}}, 'newText': 'mitive type $1\n    $0\nend'}, 'documentation': 'primitive', 'kind': 14, 'insertTextFormat': 2}
2020-06-10 06:46:38 [102] INFO {'label': 'printstyled', 'textEdit': {'range': {'end': {'character': 3, 'line': 27}, 'start': {'character': 3, 'line': 27}}, 'newText': 'ntstyled'}, 'documentation': {'kind': 'markdown', 'value': '```\nprintstyled([io], xs...; bold::Bool=false, color::Union{Symbol,Int}=:normal)\n```\n\nPrint `xs` in a color specified as a symbol or integer, optionally in bold.\n\n`color` may take any of the values `:normal`, `:default`, `:bold`, `:black`, `:blink`, `:blue`, `:cyan`, `:green`, `:hidden`, `:light_black`, `:light_blue`, `:light_cyan`, `:light_green`, `:light_magenta`, `:light_red`, `:light_yellow`, `:magenta`, `:nothing`, `:red`, `:reverse`, `:underline`, `:white`, or  `:yellow` or an integer between 0 and 255 inclusive. Note that not all terminals support 256 colors. If the keyword `bold` is given as `true`, the result will be printed in bold.\n'}, 'kind': 3, 'insertTextFormat': 2}
2020-06-10 06:46:38 [102] INFO {'label': 'println', 'textEdit': {'range': {'end': {'character': 3, 'line': 27}, 'start': {'character': 3, 'line': 27}}, 'newText': 'ntln'}, 'documentation': {'kind': 'markdown', 'value': '```\nprintln([io::IO], xs...)\n```\n\nPrint (using [`print`](@ref)) `xs` followed by a newline. If `io` is not supplied, prints to [`stdout`](@ref).\n\n### Examples\n\n```julia\njulia> println("Hello, world")\nHello, world\n\njulia> io = IOBuffer();\n\njulia> println(io, "Hello, world")\n\njulia> String(take!(io))\n"Hello, world\\n"\n```\n'}, 'kind': 3, 'insertTextFormat': 2}
2020-06-10 06:46:38 [102] INFO {'label': 'print', 'textEdit': {'range': {'end': {'character': 3, 'line': 27}, 'start': {'character': 3, 'line': 27}}, 'newText': 'nt'}, 'documentation': {'kind': 'markdown', 'value': '```\nprint([io::IO], xs...)\n```\n\nWrite to `io` (or to the default output stream [`stdout`](@ref) if `io` is not given) a canonical (un-decorated) text representation. The representation used by `print` includes minimal formatting and tries to avoid Julia-specific details.\n\n`print` falls back to calling `show`, so most types should just define `show`. Define `print` if your type has a separate "plain" representation. For example, `show` displays strings with quotes, and `print` displays strings without quotes.\n\n[`string`](@ref) returns the output of `print` as a string.\n\n### Examples\n\n```julia\njulia> print("Hello World!")\nHello World!\njulia> io = IOBuffer();\n\njulia> print(io, "Hello", \' \', :World!)\n\njulia> String(take!(io))\n"Hello World!"\n```\n'}, 'kind': 3, 'insertTextFormat': 2}

This is an example of a single completion candidate:

{'label': 'println', 'textEdit': {'range': {'end': {'character': 3, 'line': 27}, 'start': {'character': 3, 'line': 27}}, 'newText': 'ntln'}, 'documentation': {'kind': 'markdown', 'value': '```\nprintln([io::IO], xs...)\n```\n\nPrint (using [`print`](@ref)) `xs` followed by a newline. If `io` is not supplied, prints to [`stdout`](@ref).\n\n### Examples\n\n```julia\njulia> println("Hello, world")\nHello, world\n\njulia> io = IOBuffer();\n\njulia> println(io, "Hello, world")\n\njulia> String(take!(io))\n"Hello, world\\n"\n```\n'}, 'kind': 3, 'insertTextFormat': 2}

In Julia println is the function to print a string to stdout with a new line.

You'll notice that rec['textEdit']['newText'] == 'ntln' because the user has already typed in pri in the buffer. The Language Server Protocol says the following in the TextEdit interface:

To insert text into a document create a range where start === end.

https://github.com/microsoft/language-server-protocol/blob/de8e4859fd9a8065486d268fc3562fd5a4813fee/_specifications/specification-3-15.md#textEdit

Which I'm understanding as rec['textEdit']['newText'] to contain the string that needs to be appended to the existing string.

In the current implementation of deoplete-lsp, this is currently not the case.

https://github.com/Shougo/deoplete-lsp/blob/2994bf57fed476a5b9878e842b14b5b5c5b22211/rplugin/python3/deoplete/source/lsp.py#L92

This causes word to be set to ntln. Which then gets filter out when using the fuzzy matcher filter in deoplete.

https://github.com/Shougo/deoplete.nvim/blob/46ba9b23f750c5e7fe0fb1c89abee4b6fe670c66/rplugin/python3/deoplete/child.py#L371

So these suggestions never appear in deoplete completions.

I've submitted a PR that resolves this particular issue when using the Julia LanguageServer.

This change falls back to the previous implementation if the range is different. I think something more sophisticated would need to be done in that case though. This is just a small improvement over the current implementation.

Thanks.

Thanks for merging!