julia-vscode / LanguageServer.jl

An implementation of the Microsoft Language Server Protocol for the Julia language.
Other
369 stars 81 forks source link

`ERROR: KeyError: key LanguageServer.URI2("...") not found`` #951

Closed jebej closed 3 years ago

jebej commented 3 years ago

Error trying to find definitions for a base function in Sumblime with the Sublime LSP client. The various definition locations are returned and displayed properly, but the LSP crashes right after (I assume) trying to ask for more details.

:: --> julials textDocument/hover(78): {'position': {'line': 21, 'character': 116}, 'textDocument': {'uri': 'file:///C:/Users/jebej/somefile.jl'}}
:: <<< julials 78: {'contents': {'kind': 'markdown', 'value': '```\ninv(x)\n```\n\nReturn the multiplicative inverse of `x`, such that `x*inv(x)` or `inv(x)*x` yields [`one(x)`](@ref) (the multiplicative identity) up to roundoff errors.\n\nIf `x` is a number, this is essentially the same as `one(x)/x`, but for some types `inv(x)` may be slightly more efficient.\n\n### Examples\n\n```julia\njulia> inv(2)\n0.5\n\njulia> inv(1 + 2im)\n0.2 - 0.4im\n\njulia> inv(1 + 2im) * (1 + 2im)\n1.0 + 0.0im\n\njulia> inv(2//3)\n3//2\n```\n\n!!! compat "Julia 1.2"\n    `inv(::Missing)` requires at least Julia 1.2.\n\n\n```\ninv(M)\n```\n\nMatrix inverse. Computes matrix `N` such that `M * N = I`, where `I` is the identity matrix. Computed by solving the left-division `N = M \\ I`.\n\n### Examples\n\n```julia\njulia> M = [2 5; 1 3]\n2×2 Matrix{Int64}:\n 2  5\n 1  3\n\njulia> N = inv(M)\n2×2 Matrix{Float64}:\n  3.0  -5.0\n -1.0   2.0\n\njulia> M*N == N*M == Matrix(I, 2, 2)\ntrue\n```\n\n`Base.inv` is a `Function`.\n**9** methods for function `Base.inv`\n- `inv(x::Core.Integer)` in `Base` at [C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\int.jl:90](C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\int.jl:90)\n- `inv(z::Base.Complex where #s79<:Core.Integer)` in `Base` at [C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\complex.jl:271](C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\complex.jl:271)\n- `inv(x::Base.AbstractIrrational)` in `Base` at [C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\irrationals.jl:208](C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\irrationals.jl:208)\n- `inv(x::Base.Rational{T})` in `Base` at [C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\rational.jl:323](C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\rational.jl:323)\n- `inv(::Base.Missing)` in `Base` at [C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\missing.jl:101](C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\missing.jl:101)\n- `inv(w::Base.Complex{Core.Float64})` in `Base` at [C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\complex.jl:439](C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\complex.jl:439)\n- `inv(z::Base.Complex where #s79<:Union{Core.Float16,Core.Float32})` in `Base` at [C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\complex.jl:356](C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\complex.jl:356)\n- `inv(z::Base.Complex where T<:Core.Real)` in `Base` at [C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\complex.jl:266](C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\complex.jl:266)\n- `inv(x::Core.Number)` in `Base` at [C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\number.jl:217](C:\\\\Users\\\\jebej\\\\AppData\\\\Local\\\\Julia-1.6.1\\\\share\\\\julia\\\\base\\\\number.jl:217)\n'}}
:: --> julials textDocument/definition(79): {'position': {'line': 21, 'character': 116}, 'textDocument': {'uri': 'file:///C:/Users/jebej/somefile.jl'}, 'workDoneToken': 'wd79'}
:: <<< julials 79: [{'range': {'start': {'line': 89, 'character': 0}, 'end': {'line': 89, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/int.jl'}, {'range': {'start': {'line': 270, 'character': 0}, 'end': {'line': 270, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/complex.jl'}, {'range': {'start': {'line': 207, 'character': 0}, 'end': {'line': 207, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/irrationals.jl'}, {'range': {'start': {'line': 322, 'character': 0}, 'end': {'line': 322, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/rational.jl'}, {'range': {'start': {'line': 100, 'character': 0}, 'end': {'line': 100, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/missing.jl'}, {'range': {'start': {'line': 438, 'character': 0}, 'end': {'line': 438, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/complex.jl'}, {'range': {'start': {'line': 355, 'character': 0}, 'end': {'line': 355, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/complex.jl'}, {'range': {'start': {'line': 265, 'character': 0}, 'end': {'line': 265, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/complex.jl'}, {'range': {'start': {'line': 216, 'character': 0}, 'end': {'line': 216, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/number.jl'}, {'range': {'start': {'line': 89, 'character': 0}, 'end': {'line': 89, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/int.jl'}, {'range': {'start': {'line': 270, 'character': 0}, 'end': {'line': 270, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/complex.jl'}, {'range': {'start': {'line': 207, 'character': 0}, 'end': {'line': 207, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/irrationals.jl'}, {'range': {'start': {'line': 322, 'character': 0}, 'end': {'line': 322, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/rational.jl'}, {'range': {'start': {'line': 100, 'character': 0}, 'end': {'line': 100, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/missing.jl'}, {'range': {'start': {'line': 438, 'character': 0}, 'end': {'line': 438, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/complex.jl'}, {'range': {'start': {'line': 355, 'character': 0}, 'end': {'line': 355, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/complex.jl'}, {'range': {'start': {'line': 265, 'character': 0}, 'end': {'line': 265, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/complex.jl'}, {'range': {'start': {'line': 216, 'character': 0}, 'end': {'line': 216, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/number.jl'}, {'range': {'start': {'line': 464, 'character': 0}, 'end': {'line': 464, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/mpfr.jl'}, {'range': {'start': {'line': 171, 'character': 0}, 'end': {'line': 171, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/.julia/packages/Unitful/PcVKX/src/quantities.jl'}, {'range': {'start': {'line': 87, 'character': 0}, 'end': {'line': 87, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/.julia/packages/Unitful/PcVKX/src/dimensions.jl'}, {'range': {'start': {'line': 149, 'character': 0}, 'end': {'line': 149, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/.julia/packages/Unitful/PcVKX/src/units.jl'}, {'range': {'start': {'line': 166, 'character': 0}, 'end': {'line': 166, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/.julia/packages/Unitful/PcVKX/src/units.jl'}, {'range': {'start': {'line': 157, 'character': 0}, 'end': {'line': 157, 'character': 0}}, 'uri': 'file:///C%3A/Users/jebej/.julia/packages/Unitful/PcVKX/src/units.jl'}]
:: --> julials textDocument/codeAction(80): {'range': {'start': {'line': 89, 'character': 0}, 'end': {'line': 89, 'character': 0}}, 'textDocument': {'uri': 'file:///C:/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/int.jl'}, 'context': {'diagnostics': []}}
julials: ERROR: KeyError: key LanguageServer.URI2("file:///C:/Users/jebej/AppData/Local/Julia-1.6.1/share/julia/base/int.jl") not found
julials: Stacktrace:
julials:  [1] getindex
julials:    @ .\dict.jl:482 [inlined]
julials:  [2] getdocument
julials:    @ C:\Users\jebej\.julia\packages\LanguageServer\jiDTR\src\languageserverinstance.jl:109 [inlined]
julials:  [3] textDocument_codeAction_request(params::LanguageServer.CodeActionParams, server::LanguageServerInstance, conn::JSONRPC.JSONRPCEndpoint{Base.PipeEndpoint, Base.PipeEndpoint})
julials:    @ LanguageServer C:\Users\jebej\.julia\packages\LanguageServer\jiDTR\src\requests\actions.jl:9
julials:  [4] (::LanguageServer.var"#96#97"{typeof(LanguageServer.textDocument_codeAction_request), LanguageServerInstance})(conn::JSONRPC.JSONRPCEndpoint{Base.PipeEndpoint, Base.PipeEndpoint}, params::LanguageServer.CodeActionParams)
julials:    @ LanguageServer C:\Users\jebej\.julia\packages\LanguageServer\jiDTR\src\languageserverinstance.jl:245
julials:  [5] dispatch_msg(x::JSONRPC.JSONRPCEndpoint{Base.PipeEndpoint, Base.PipeEndpoint}, dispatcher::JSONRPC.MsgDispatcher, msg::Dict{String, Any})
julials:    @ JSONRPC C:\Users\jebej\.julia\packages\JSONRPC\yu0G3\src\typed.jl:67
julials:  [6] run(server::LanguageServerInstance)
julials:    @ LanguageServer C:\Users\jebej\.julia\packages\LanguageServer\jiDTR\src\languageserverinstance.jl:353
julials:  [7] runserver
julials:    @ C:\Users\jebej\.julia\packages\LanguageServer\jiDTR\src\runserver.jl:46 [inlined]
julials:  [8] runserver(pipe_in::Base.PipeEndpoint, pipe_out::Base.PipeEndpoint)
julials:    @ LanguageServer C:\Users\jebej\.julia\packages\LanguageServer\jiDTR\src\runserver.jl:44
jebej commented 3 years ago

This is on Windows with julia 1.6.1, and up to date packages: LS.jl @ 4.0.0, SymbolServer.jl @7.0.0 & JSONRPC @ 1.3.3.

jwortmann commented 3 years ago

When using "Goto Definition..." Sublime Text shows a list with the files for the returned definition locations and immediately displays a preview of the selected file (i.e. the first item in the quick panel list), and then asks for possible code actions at that position via textDocument/codeAction. While this request for code actions probably makes not much sense here for files in Julia's "base" or "stdlib", I don't see any mention in the LSP specs that the URI for textDocument/codeAction would be restricted to the workspace.

But on the other hand, these file previews for a selected file in the quick panel are very volatile and these files are not really opened until the user confirms the selected item with Enter, and only after that happened, the textDocument/didOpen notification is sent from Sublime Text. So this error from the server is thrown because the file URI for such file previews are not yet registered in the server.

I'm not sure if this is a bug in the server (i.e. it should not error if a file URI is outside of the workspace), or in the Sublime Text client (i.e. textDocument/codeAction should not be requested for file previews, and instead only after the file was really opened). Perhaps @rwols has an opinion whether this should be handled by the server, of fixed in Sublime's LSP?

Regardlessly, the following would be a possible fix here in LanguageServer.jl, which would prevent a crash for file URIs outside of the workspace (such a check would probably be useful for other requests too then):

diff --git a/src/languageserverinstance.jl b/src/languageserverinstance.jl
index 7cb1525..3a89cfe 100644
--- a/src/languageserverinstance.jl
+++ b/src/languageserverinstance.jl
@@ -106,7 +106,7 @@ function hasdocument(server::LanguageServerInstance, uri::URI2)
 end

 function getdocument(server::LanguageServerInstance, uri::URI2)
-    return server._documents[uri]
+    return get(server._documents, uri, nothing)
 end

 function getdocuments_key(server::LanguageServerInstance)
diff --git a/src/requests/actions.jl b/src/requests/actions.jl
index 02c10cc..45383ec 100644
--- a/src/requests/actions.jl
+++ b/src/requests/actions.jl
@@ -7,6 +7,7 @@ end
 function textDocument_codeAction_request(params::CodeActionParams, server::LanguageServerInstance, conn)
     commands = Command[]
     doc = getdocument(server, URI2(params.textDocument.uri))
+    !isnothing(doc) || return nothing
     offset = get_offset(doc, params.range.start) # Should usef get_offset2?
     x = get_expr(getcst(doc), offset)
     arguments = Any[params.textDocument.uri, offset] # use the same arguments for all commands
pfitzseb commented 3 years ago

Hm, I'm pretty sure VSCode sends textDocument/didOpen even for previews, which would explain the differing behaviour. I wouldn't mind adding these checks to the server, but basically all of our code assumes that only URIs of didOpened text documents are ever used in requests, I think.

rwols commented 3 years ago

This was a bug in the ST client and is now fixed.

jebej commented 3 years ago

Yep, it works now, thanks!