haskell / haskell-language-server

Official haskell ide support via language server (LSP). Successor of ghcide & haskell-ide-engine.
Apache License 2.0
2.71k stars 367 forks source link

Misleading error messages after receiving invalid URI #3821

Open Andriamanitra opened 1 year ago

Andriamanitra commented 1 year ago

Is your enhancement request related to a problem? Please describe.

When a client send the server URI (most commonly textDocument.uri) with Unicode characters that are not properly percent-encoded there is no error, HLS keeps running but will not work as it should. The user will later receive confusing error messages such as this one when requesting textDocument/hover:

{"error":{"code":-32601,"message":"No plugin enabled for SMethod_TextDocumentHover, potentially available: ghcide-hover-and-symbols, explicit-fixity"},"id":1,"jsonrpc":"2.0"}

Describe the solution you'd like

I would like the language server to respond with an error when it receives an invalid request such as initializing / opening a document with an URI that contains characters that are not allowed according to RFC3986 (which the LSP specification says URIs should follow).

Additional context

I ran into this issue while implementing a LSP plugin for micro-editor. I forgot to percent-encode file URIs in outgoing communication which resulted in HLS (silently!) failing. I thought it was an error in my initializationParameters for a long time since all of the errors said "No plugin enabled" (and HLS also doesn't give any confirmation of whether you passed it invalid initializationParameters so it was a bit of guessing game).

Communication between the client and HLS when this issue happened ``` --> Content-Length: 433 {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":13986,"rootUri":"file:///home/mikko/koodit/µlsp","workspaceFolders":[{"name":"root","uri":"file:///home/mikko/koodit/µlsp"}],"initializationOptions":{"haskell":{"plugin":{"ghcide-hover-and-symbols":{"globalOn":true,"hoverOn":true}}}},"capabilities":{"textDocument":{"hover":{"contentFormat":["plaintext","markdown"]},"formatting":{"dynamicRegistration":false}}}}} <-- Content-Length: 1779 {"id":0,"jsonrpc":"2.0","result":{"capabilities":{"callHierarchyProvider":true,"codeActionProvider":{"resolveProvider":true},"codeLensProvider":{"resolveProvider":true,"workDoneProgress":false},"colorProvider":false,"completionProvider":{"resolveProvider":true,"triggerCharacters":["."]},"declarationProvider":false,"definitionProvider":true,"documentFormattingProvider":true,"documentHighlightProvider":true,"documentRangeFormattingProvider":true,"documentSymbolProvider":true,"executeCommandProvider":{"commands":["14011:retrie:retrieCommand","14011:retrie:retrieInlineThisCommand","14011:explicit-fields:codeActionResolve","14011:ghcide-extend-import-action:extendImport","14011:importLens:ImportLensCommand","14011:moduleName:updateModuleName","14011:splice:expandTHSpliceInplace","14011:hlint:codeActionResolve","14011:class:classplugin.codeaction","14011:class:classplugin.typelens","14011:ghcide-type-lenses:typesignature.add","14011:eval:evalCommand","14011:gadt:GADT.toGADT"]},"foldingRangeProvider":true,"hoverProvider":true,"implementationProvider":false,"referencesProvider":true,"renameProvider":true,"selectionRangeProvider":true,"semanticTokensProvider":{"legend":{"tokenModifiers":["declaration","definition","readonly","static","deprecated","abstract","async","modification","documentation","defaultLibrary"],"tokenTypes":["namespace","type","class","enum","interface","struct","typeParameter","parameter","variable","property","enumMember","event","function","method","macro","keyword","modifier","comment","string","number","regexp","operator","decorator"]}},"textDocumentSync":{"change":2,"openClose":true,"save":{}},"typeDefinitionProvider":true,"workspace":{"workspaceFolders":{"changeNotifications":true,"supported":true}},"workspaceSymbolProvider":true}}} --> Content-Length: 52 {"jsonrpc":"2.0","method":"initialized","params":{}} --> Content-Length: 202 {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///home/mikko/koodit/µlsp/hello.hs","languageId":"haskell","version":1,"text":"main = print \"Hello world!\"\n"}}} --> Content-Length: 167 {"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///home/mikko/koodit/µlsp/hello.hs"},"position":{"line":0,"character":8}}} <-- Content-Length: 174 {"error":{"code":-32601,"message":"No plugin enabled for SMethod_TextDocumentHover, potentially available: ghcide-hover-and-symbols, explicit-fixity"},"id":1,"jsonrpc":"2.0"} ```
stderr output from haskell-language-server-wrapper --debug --lsp ``` No 'hie.yaml' found. Try to discover the project type! Run entered for haskell-language-server-wrapper(haskell-language-server-wrapper) Version 2.3.0.0 x86_64 ghc-9.0.2 Current directory: /home/mikko/nvme_ssd/Koodit/µlsp Operating system: linux Arguments: ["--debug","--lsp"] Cradle directory: /home/mikko/nvme_ssd/Koodit/µlsp Cradle type: Default Tool versions found on the $PATH cabal: 3.10.1.0 stack: Not found ghc: 9.6.2 Consulting the cradle to get project GHC version... 2023-10-01T14:47:45.039470Z | Debug | executing command: ghc --numeric-version Project GHC version: 9.6.2 haskell-language-server exe candidates: ["haskell-language-server-9.6.2","haskell-language-server"] Launching haskell-language-server exe at:/home/mikko/.local/bin/haskell-language-server-9.6.2 2023-10-01T14:47:45.069781Z | Debug | executing command: ghc -v0 -package-env=- -ignore-dot-ghci -e Control.Monad.join (Control.Monad.fmap System.IO.putStr System.Environment.getExecutablePath) 2023-10-01T14:47:45.155095Z | Debug | executing command: ghc --print-libdir 2023-10-01T14:47:46.242208Z | Info | haskell-language-server version: 2.3.0.0 (GHC: 9.6.2) (PATH: /home/mikko/.local/share/ghcup/hls/2.3.0.0/lib/haskell-language-server-2.3.0.0/bin/haskell-language-server-9.6.2) 2023-10-01T14:47:46.242866Z | Info | Directory: /home/mikko/nvme_ssd/Koodit/µlsp 2023-10-01T14:47:46.243021Z | Info | Starting (haskell-language-server) LSP server... GhcideArguments {argsCommand = LSP, argsCwd = Nothing, argsShakeProfiling = Nothing, argsTesting = False, argsExamplePlugin = False, argsLogLevel = Debug, argsLogFile = Nothing, argsLogStderr = True, argsLogClient = False, argsThreads = 0, argsProjectGhcVersion = False} PluginIds: [ pragmas-suggest , pragmas-completion , retrie , ghcide-completions , alternateNumberFormat , ghcide-code-actions-bindings , explicit-fields , ghcide-hover-and-symbols , ghcide-code-actions-type-signatures , fourmolu , ghcide-extend-import-action , ghcide-code-actions-fill-holes , importLens , LSPRecorderCallback , cabal , qualifyImportedNames , moduleName , splice , stylish-haskell , changeTypeSignature , hlint , class , ormolu , callHierarchy , ghcide-type-lenses , codeRange , cabal-fmt , eval , rename , ghcide-code-actions-imports-exports , gadt , overloaded-record-dot , ghcide-core , explicit-fixity , pragmas-disable ] 2023-10-01T14:47:46.245229Z | Info | Logging heap statistics every 60.00s 2023-10-01T14:47:46.262395Z | Info | Starting LSP server... If you are seeing this in a terminal, you probably should have run WITHOUT the --lsp option! PluginIds: [ pragmas-suggest , pragmas-completion , retrie , ghcide-completions , alternateNumberFormat , ghcide-code-actions-bindings , explicit-fields , ghcide-hover-and-symbols , ghcide-code-actions-type-signatures , fourmolu , ghcide-extend-import-action , ghcide-code-actions-fill-holes , importLens , LSPRecorderCallback , cabal , qualifyImportedNames , moduleName , splice , stylish-haskell , changeTypeSignature , hlint , class , ormolu , callHierarchy , ghcide-type-lenses , codeRange , cabal-fmt , eval , rename , ghcide-code-actions-imports-exports , gadt , overloaded-record-dot , ghcide-core , explicit-fixity , pragmas-disable ] 2023-10-01T14:47:46.262656Z | Info | Starting server 2023-10-01T14:47:46.263918Z | Info | Started LSP server in 0.00s 2023-10-01T14:47:46.268418Z | Debug | executing command: ghc --print-libdir 2023-10-01T14:47:46.305081Z | Debug | Setting initial dynflags... 2023-10-01T14:47:46.305197Z | Debug | shouldRunSubset: False 2023-10-01T14:47:46.305330Z | Debug | Initializing exports map from hiedb 2023-10-01T14:47:46.305672Z | Info | Registering IDE configuration: IdeConfiguration {workspaceFolders = fromList [NormalizedUri (-4500577356667336057) "file:///home/mikko/koodit/%C2%B5lsp"], clientSettings = hashed (Just (Object (fromList [("haskell",Object (fromList [("plugin",Object (fromList [("ghcide-hover-and-symbols",Object (fromList [("globalOn",Bool True),("hoverOn",Bool True)]))]))]))])))} 2023-10-01T14:47:46.307915Z | Debug | Done initializing exports map from hiedb. Size: 1 2023-10-01T14:47:46.307994Z | Debug | LSP: not requesting configuration since the client does not support workspace/configuration 2023-10-01T14:47:46.308077Z | Debug | Shake session initialized 2023-10-01T14:47:46.308097Z | Debug | VFS: opening file:///home/mikko/koodit/%C2%B5lsp/hello.hs 2023-10-01T14:47:46.308129Z | Debug | Warning: Client does not support watched files. Falling back to OS polling 2023-10-01T14:47:46.308290Z | Warning | No plugin enabled for "textDocument/didOpen" 2023-10-01T14:47:46.346831Z | Warning | No plugin enabled for "textDocument/hover" ```
joyfulmantis commented 1 year ago

We have some options for giving better errors:

  1. Plugins can give a reason why they are rejecting a request. (This we should probably do anyway)
  2. We can possibly find a better way of determining the file type that doesn't involve parsing the URI
  3. We can give a specific error for an invalid URI