haskell / haskell/haskell-language-server

Misleading error messages after receiving invalid URI

Open
#3,821 1 comment 0 reactions 0 assignees View on GitHub
component: ghcide type: enhancement
Dominant language
Haskell
Stars
3k
Forks
455
Avg merge
3d 21h
Merged PRs (30d)
13

Description

## 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`:
```json
{"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](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#uri)).

## Additional context

I ran into this issue while implementing a [LSP plugin](https://github.com/Andriamanitra/mlsp) for [micro-editor](https://github.com/zyedidia/micro). 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"
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by running the reported haskell-language-server-wrapper --debug --lsp flow and tracing how the initialize and textDocument/didOpen URI values are handled. Compare validation with the LSP specification and make invalid URIs produce an explicit error instead of later plugin warnings; reproduce the µ character example to verify the behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
haskell
Domain
api, devtools
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.