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 368 forks source link

Minor "import action" formatting bug #2414

Closed googleson78 closed 2 years ago

googleson78 commented 2 years ago

HLS --version

tauren :: /tmp » haskell-language-server-wrapper --version
haskell-language-server version: 1.5.0.0 (GHC: 8.10.7) (PATH: /home/googleson78/.ghcup/bin/haskell-language-server-wrapper-1.5.0) (GIT hash: 311107eabbf0537e0c192b2c377d282505b4eff1)

Your environment

Output of haskell-language-server --probe-tools or haskell-language-server-wrapper --probe-tools:

tauren :: /tmp » haskell-language-server-wrapper --probe-tools
haskell-language-server version: 1.5.0.0 (GHC: 8.10.7) (PATH: /home/googleson78/.ghcup/bin/haskell-language-server-wrapper-1.5.0) (GIT hash: 311107eabbf0537e0c192b2c377d282505b4eff1)
Tool versions found on the $PATH
cabal:          3.6.2.0
stack:          2.7.3
ghc:            8.10.7

Which OS do you use: Debian

Which lsp-client do you use: nvim, LanguageClient-neovim

Describe your project: Reproduces with a standalone file and in a stack project

Steps to reproduce

Try to use the import action for Int64 with these file contents.

module Asdf
  (f)
  where

f :: Int64 -> Int64
f = id

Expected behaviour

module Asdf
  (f)
  where

import Data.Int (Int64)

f :: Int64 -> Int64
f = id

(or some other valid result)

Actual behaviour

module Lol
  (f)
import Data.Int (Int64)
  where

f :: Int64 -> Int64
f = id

Include debug information

Debug output: ``` 2021-11-29 16:18:45.503918081 [ThreadId 4] INFO hls: Starting LSP server... 2021-11-29 16:18:45.504138425 [ThreadId 4] INFO hls: If you are seeing this in a terminal, you probably should have run WITHOUT the --lsp option! 2021-11-29 16:18:45.505988303 [ThreadId 9] INFO hls: Started LSP server in 0.00s 2021-11-29 16:18:45.509847454 [ThreadId 9] DEBUG hls: setInitialDynFlags cradle: Cradle {cradleRootDir = "/tmp", cradleOptsProg = CradleAction: Default} 2021-11-29 16:18:45.577045973 [ThreadId 9] DEBUG hls: runSubset: True 2021-11-29 16:18:45.577382109 [ThreadId 15] DEBUG hls: Initializing exports map from hiedb 2021-11-29 16:18:45.577841878 [ThreadId 9] INFO hls: Registering ide configuration: IdeConfiguration {workspaceFolders = fromList [NormalizedUri (-5929415118987624406) "file:///tmp"], clientSettings = hashed Nothing} 2021-11-29 16:18:45.58235361 [ThreadId 15] DEBUG hls: Done initializing exports map from hiedb (6) 2021-11-29 16:18:45.588551282 [ThreadId 17] DEBUG hls: Set files of interest to: fromList [(NormalizedFilePath "/tmp/Asdf.hs",Modified {firstOpen = True})] 2021-11-29 16:18:45.588979378 [ThreadId 20] DEBUG hls: Finishing build session(exception: AsyncCancelled) 2021-11-29 16:18:45.588970064 [ThreadId 17] DEBUG hls: Restarting build session due to /tmp/Asdf.hs (modified) for keys [IsFileOfInterest; /tmp/Asdf.hs,GetModificationTime; /tmp/Asdf.hs] (aborting the previous one took 0.00s) 2021-11-29 16:18:45.589206787 [ThreadId 17] DEBUG hls: Opened text document: file:///tmp/Asdf.hs 2021-11-29 16:18:45.589613032 [ThreadId 48] DEBUG hls: src/Ide/Plugin/Eval/CodeLens.hs:121:15 "fp" "/tmp/Asdf.hs" 2021-11-29 16:18:45.590174096 [ThreadId 46] DEBUG hls: hlint:getIdeas:file:NormalizedFilePath "/tmp/Asdf.hs" 2021-11-29 16:18:45.591205815 [ThreadId 76] INFO hls: Consulting the cradle for "Asdf.hs" 2021-11-29 16:18:45.591353652 [ThreadId 76] WARNING hls: No [cradle](https://github.com/mpickering/hie-bios#hie-bios) found for Asdf.hs. Proceeding with [implicit cradle](https://hackage.haskell.org/package/implicit-hie). You should ignore this message, unless you see a 'Multi Cradle: No prefixes matched' error. 2021-11-29 16:18:45.599495908 [ThreadId 76] DEBUG hls: Output from setting up the cradle Cradle {cradleRootDir = "/tmp", cradleOptsProg = CradleAction: Default} 2021-11-29 16:18:45.652059369 [ThreadId 76] DEBUG hls: Session loading result: Right (ComponentOptions {componentOptions = [], componentRoot = "/tmp", componentDependencies = []},"/home/googleson78/.ghcup/ghc/8.10.7/lib/ghc-8.10.7") 2021-11-29 16:18:45.704462603 [ThreadId 76] INFO hls: Using interface files cache dir: /home/googleson78/.cache/ghcide/main-da39a3ee5e6b4b0d3255bfef95601890afd80709 2021-11-29 16:18:45.704665031 [ThreadId 76] INFO hls: Making new HscEnv[main] 2021-11-29 16:18:45.714310798 [ThreadId 76] DEBUG hls: New Component Cache HscEnvEq: (([],Just HscEnvEq 11),fromList []) 2021-11-29 16:18:45.714474583 [ThreadId 76] DEBUG hls: Known files updated: fromList [(TargetFile NormalizedFilePath "/tmp/Asdf.hs",fromList ["/tmp/Asdf.hs"])] 2021-11-29 16:18:45.714859624 [ThreadId 34] DEBUG hls: Finishing build session(exception: AsyncCancelled) 2021-11-29 16:18:45.714866003 [ThreadId 76] DEBUG hls: Restarting build session due to new component for keys [GetKnownTargets; ,GhcSessionIO; ,IsFileOfInterest; /tmp/Asdf.hs,GetModificationTime; /tmp/Asdf.hs] (aborting the previous one took 0.00s) 2021-11-29 16:18:45.715228884 [ThreadId 96] DEBUG hls: hlint:getIdeas:file:NormalizedFilePath "/tmp/Asdf.hs" 2021-11-29 16:18:45.715791723 [ThreadId 106] INFO hls: finish: ModuleName.ghcSession (took 0.00s) 2021-11-29 16:18:45.716335976 [ThreadId 108] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:45.716779901 [ThreadId 133] INFO hls: finish: ModuleName.GetParsedModule (took 0.00s) 2021-11-29 16:18:45.725405939 [ThreadId 99] INFO hls: finish: eval.GetParsedModuleWithComments (took 0.01s) 2021-11-29 16:18:45.725444715 [ThreadId 140] INFO hls: finish: Wingman.codeLensProvider.GetAnnotatedParsedSource (took 0.01s) 2021-11-29 16:18:45.725562284 [ThreadId 48] DEBUG hls: src/Ide/Plugin/Eval/CodeLens.hs:121:15 "comments" "Comments {lineComments = fromList [], blockComments = fromList []}" 2021-11-29 16:18:45.725681588 [ThreadId 48] DEBUG hls: src/Ide/Plugin/Eval/CodeLens.hs:121:15 "Tests" "0 tests in 0 sections 0 setups 0 lenses." 2021-11-29 16:18:45.725770187 [ThreadId 48] DEBUG hls: src/Ide/Plugin/Eval/CodeLens.hs:121:15 "tests" "0.00s" 2021-11-29 16:18:45.725846182 [ThreadId 48] DEBUG hls: src/Ide/Plugin/Eval/CodeLens.hs:121:15 "codeLens" "0.14s" 2021-11-29 16:18:45.734794591 [ThreadId 104] DEBUG hls: LOOKUP UP PERSISTENT FOR: MinimalImports 2021-11-29 16:18:45.73468359 [ThreadId 102] INFO hls: finish: codeLens.TypeCheck (took 0.02s) 2021-11-29 16:18:45.734914479 [ThreadId 104] INFO hls: finish: (took 0.02s) 2021-11-29 16:18:45.735052352 [ThreadId 95] DEBUG hls: finish: InitialLoad (took 0.02s) 2021-11-29 16:18:45.73514659 [ThreadId 153] DEBUG hls: LOOKUP UP PERSISTENT FOR: GetBindings 2021-11-29 16:18:45.735282234 [ThreadId 153] INFO hls: finish: Wingman.codeLensProvider.GetBindings (took 0.01s) 2021-11-29 16:18:45.741916557 [ThreadId 97] DEBUG hls: LOOKUP UP PERSISTENT FOR: RefineImports 2021-11-29 16:18:45.741976617 [ThreadId 165] INFO hls: finish: codeLens.GetBindings (took 0.00s) 2021-11-29 16:18:45.742113155 [ThreadId 97] INFO hls: finish: RefineImports (took 0.03s) 2021-11-29 16:18:45.742085019 [ThreadId 166] INFO hls: finish: codeLens.GetGlobalBindingTypeSigs (took 0.00s) 2021-11-29 16:18:47.020325415 [ThreadId 189] INFO hls: finish: Pragmas.GetParsedModule (took 0.00s) 2021-11-29 16:18:47.020686942 [ThreadId 194] INFO hls: finish: GhcideCodeActions.GetFileContents (took 0.00s) 2021-11-29 16:18:47.020930723 [ThreadId 192] INFO hls: finish: GhcideCodeActions.GetGlobalBindingTypeSigs (took 0.00s) 2021-11-29 16:18:47.020982282 [ThreadId 193] INFO hls: finish: importLens (took 0.00s) 2021-11-29 16:18:47.021021902 [ThreadId 191] INFO hls: finish: GhcideCodeActions.GhcSession (took 0.00s) 2021-11-29 16:18:47.021065723 [ThreadId 188] INFO hls: finish: HaddockComments.GetAnnotatedParsedSource (took 0.00s) 2021-11-29 16:18:47.021110904 [ThreadId 195] INFO hls: finish: RefineImports (took 0.00s) 2021-11-29 16:18:47.021134507 [ThreadId 196] DEBUG hls: LOOKUP UP PERSISTENT FOR: TypeCheck 2021-11-29 16:18:47.021310453 [ThreadId 197] INFO hls: finish: splice.codeAction.GitHieAst (took 0.00s) 2021-11-29 16:18:47.021411391 [ThreadId 196] INFO hls: finish: retrie (took 0.00s) 2021-11-29 16:18:47.021568873 [ThreadId 198] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:47.021581856 [ThreadId 200] INFO hls: finish: GhcideCodeActions.GetAnnotatedParsedSource (took 0.00s) 2021-11-29 16:18:47.021748922 [ThreadId 199] INFO hls: finish: Pragmas.GetFileContents (took 0.00s) 2021-11-29 16:18:47.021770461 [ThreadId 203] DEBUG hls: LOOKUP UP PERSISTENT FOR: GetHieAst 2021-11-29 16:18:47.021997242 [ThreadId 203] INFO hls: finish: Wingman.judgementForHole.GetHieAst (took 0.00s) 2021-11-29 16:18:47.022150358 [ThreadId 202] INFO hls: finish: GhcideCodeActions.TypeCheck (took 0.00s) 2021-11-29 16:18:47.022173304 [ThreadId 205] INFO hls: finish: GhcideCodeActions.getIdeOptions (took 0.00s) 2021-11-29 16:18:47.022195331 [ThreadId 204] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:47.022349951 [ThreadId 206] INFO hls: finish: GhcideCodeActions.GetParsedModule (took 0.00s) 2021-11-29 16:18:47.031731933 [ThreadId 208] INFO hls: finish: GhcideCodeActions.GetBindings (took 0.00s) 2021-11-29 16:18:47.031785339 [ThreadId 207] INFO hls: finish: Wingman.judgementForHole.GetHieAst (took 0.00s) 2021-11-29 16:18:47.032155635 [ThreadId 209] INFO hls: finish: GhcideCodeActions.GetParsedModule (took 0.00s) 2021-11-29 16:18:47.032217652 [ThreadId 210] INFO hls: finish: GhcideCodeActions.GetAnnotatedParsedSource (took 0.00s) 2021-11-29 16:18:47.03231561 [ThreadId 211] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:47.03239962 [ThreadId 212] INFO hls: finish: GhcideCodeActions.GetFileContents (took 0.00s) 2021-11-29 16:18:47.032502556 [ThreadId 213] INFO hls: finish: Wingman.judgementForHole.GetHieAst (took 0.00s) 2021-11-29 16:18:47.032690687 [ThreadId 214] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:47.032777152 [ThreadId 215] INFO hls: finish: Wingman.judgementForHole.GetHieAst (took 0.00s) 2021-11-29 16:18:47.032920183 [ThreadId 216] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:47.032951129 [ThreadId 217] INFO hls: finish: Wingman.judgementForHole.GetHieAst (took 0.00s) 2021-11-29 16:18:47.033130259 [ThreadId 218] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:47.033149592 [ThreadId 219] INFO hls: finish: Wingman.judgementForHole.GetHieAst (took 0.00s) 2021-11-29 16:18:47.033321651 [ThreadId 220] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:47.033338299 [ThreadId 221] INFO hls: finish: Wingman.judgementForHole.GetHieAst (took 0.00s) 2021-11-29 16:18:47.033508217 [ThreadId 222] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:47.033526714 [ThreadId 223] INFO hls: finish: Wingman.judgementForHole.GetHieAst (took 0.00s) 2021-11-29 16:18:47.033682785 [ThreadId 224] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:47.033700196 [ThreadId 225] INFO hls: finish: Wingman.judgementForHole.GetHieAst (took 0.00s) 2021-11-29 16:18:47.033857388 [ThreadId 226] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:47.033876639 [ThreadId 227] INFO hls: finish: Wingman.judgementForHole.GetHieAst (took 0.00s) 2021-11-29 16:18:47.034034575 [ThreadId 228] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:47.03405384 [ThreadId 229] INFO hls: finish: Wingman.judgementForHole.GetHieAst (took 0.00s) 2021-11-29 16:18:47.034213631 [ThreadId 230] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:47.034242933 [ThreadId 231] INFO hls: finish: Wingman.judgementForHole.GetHieAst (took 0.00s) 2021-11-29 16:18:47.034385398 [ThreadId 232] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:47.034404932 [ThreadId 233] INFO hls: finish: Wingman.judgementForHole.GetHieAst (took 0.00s) 2021-11-29 16:18:47.902467354 [ThreadId 235] INFO hls: finish: GhcideCodeActions.GetAnnotatedParsedSource (took 0.00s) 2021-11-29 16:18:47.903372264 [ThreadId 237] INFO hls: finish: GhcideCodeActions.GetFileContents (took 0.00s) 2021-11-29 16:18:47.903274645 [ThreadId 236] INFO hls: finish: GhcideCodeActions.GetParsedModule (took 0.00s) 2021-11-29 16:18:48.02778124 [ThreadId 239] INFO hls: finish: GhcideCodeActions.GetHieAst (took 0.00s) 2021-11-29 16:18:48.027679602 [ThreadId 238] INFO hls: finish: GhcideCodeActions.TypeCheck (took 0.00s) 2021-11-29 16:18:48.029025618 [ThreadId 240] INFO hls: finish: GhcideCodeActions.getParsedModule (took 0.00s) 2021-11-29 16:18:49.184630154 [ThreadId 17] DEBUG hls: Set files of interest to: fromList [(NormalizedFilePath "/tmp/Asdf.hs",Modified {firstOpen = False})] 2021-11-29 16:18:49.185322555 [ThreadId 17] DEBUG hls: Restarting build session due to /tmp/Asdf.hs (modified) for keys [IsFileOfInterest; /tmp/Asdf.hs,GetModificationTime; /tmp/Asdf.hs] (aborting the previous one took 0.00s) 2021-11-29 16:18:49.185384635 [ThreadId 86] DEBUG hls: Finishing build session(exception: AsyncCancelled) 2021-11-29 16:18:49.185493777 [ThreadId 17] DEBUG hls: Modified text document: file:///tmp/Asdf.hs 2021-11-29 16:18:49.185893736 [ThreadId 266] DEBUG hls: src/Ide/Plugin/Eval/CodeLens.hs:121:15 "fp" "/tmp/Asdf.hs" 2021-11-29 16:18:49.186038136 [ThreadId 284] INFO hls: finish: ModuleName.ghcSession (took 0.00s) 2021-11-29 16:18:49.187013721 [ThreadId 283] INFO hls: finish: Wingman.getIdeDynflags.GetModSummaryWithoutTimestamps (took 0.00s) 2021-11-29 16:18:49.18717815 [ThreadId 257] DEBUG hls: hlint:getIdeas:file:NormalizedFilePath "/tmp/Asdf.hs" 2021-11-29 16:18:49.187172395 [ThreadId 289] INFO hls: finish: ModuleName.GetParsedModule (took 0.00s) 2021-11-29 16:18:49.187356376 [ThreadId 288] INFO hls: finish: eval.GetParsedModuleWithComments (took 0.00s) 2021-11-29 16:18:49.187358783 [ThreadId 266] DEBUG hls: src/Ide/Plugin/Eval/CodeLens.hs:121:15 "comments" "Comments {lineComments = fromList [], blockComments = fromList []}" 2021-11-29 16:18:49.187479664 [ThreadId 304] INFO hls: finish: Wingman.codeLensProvider.GetAnnotatedParsedSource (took 0.00s) 2021-11-29 16:18:49.187520316 [ThreadId 266] DEBUG hls: src/Ide/Plugin/Eval/CodeLens.hs:121:15 "Tests" "0 tests in 0 sections 0 setups 0 lenses." 2021-11-29 16:18:49.187668303 [ThreadId 266] DEBUG hls: src/Ide/Plugin/Eval/CodeLens.hs:121:15 "tests" "0.00s" 2021-11-29 16:18:49.187733534 [ThreadId 285] INFO hls: finish: codeLens.TypeCheck (took 0.00s) 2021-11-29 16:18:49.187806245 [ThreadId 266] DEBUG hls: src/Ide/Plugin/Eval/CodeLens.hs:121:15 "codeLens" "0.00s" 2021-11-29 16:18:49.18814162 [ThreadId 286] INFO hls: finish: RefineImports (took 0.00s) 2021-11-29 16:18:49.18819052 [ThreadId 310] INFO hls: finish: Wingman.codeLensProvider.GetBindings (took 0.00s) 2021-11-29 16:18:49.188194653 [ThreadId 315] INFO hls: finish: codeLens.GetBindings (took 0.00s) 2021-11-29 16:18:49.188271745 [ThreadId 287] INFO hls: finish: (took 0.00s) 2021-11-29 16:18:49.188314529 [ThreadId 320] INFO hls: finish: codeLens.GetGlobalBindingTypeSigs (took 0.00s) ```
jneira commented 2 years ago

Minor but interesting bug, have you any intuition about what triggers it, the place of the where keyword?

googleson78 commented 2 years ago

I think so, now that I tried it out again the explicit export doesn't seem to matter. I also feel like this is a regression, but not 100% sure.

ilyakooo0 commented 2 years ago

I have a very strong feeling that this happens when when is not on the same line as ):

  )
  where

and does not happen when they are on the name line:

  ) where

ormolu formats export lists the first way and the initial import is broken. If you already have imports, the positioning of the where keyword doesn't seem to matter.

kokobd commented 2 years ago

A quick experiment shows that for a file like this, nothing represents the where keyword in HsModule. (quite weird, though)

module Asdf
  (f)

  where

f :: Int64 -> Int64
f = id

When there is no existing import declaration, the current implementation uses hsmodName and hsmodExports to infer the position to insert a new one. hsmodName represents the word Asdf in the example, and hsmodExports represents the letter f in the export list. This explains the bug: it just insert after hsmodName and hsmodExports, not taking the location of where into account.

So if there is no way to extract the location of where from the AST, maybe we should insert the import declaration before the first hsmodDecls (or if hsmodDecls is empty too, just append at the end of file).

kokobd commented 2 years ago

pm_annotations in ParsedSource might help, I will investigate it more.

By the way, linking a related issue #2425 to make the context clearer.

pepeiborra commented 2 years ago

The recommended solution is to use ghc-exactprint to modify the AST and then pretty-print it, instead of doing it manually.

We already have an exactprint function to extend imports that can serve as example:

https://github.com/haskell/haskell-language-server/blob/4386396624180de112d04335a75334d04781110c/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs#L329-L335