haskell / haskell-language-server

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

Goto definition of local library seems not working until I load the library once. #4362

Closed BurningLutz closed 4 months ago

BurningLutz commented 4 months ago

Your environment

Which OS do you use?

Ubuntu 24.04 LTS

Which version of GHC do you use and how did you install it?

9.10.1 from ghcup

How is your project built (alternative: link to the project)? I initialized a new project with executable and library via "cabal init".

Which LSP client (editor/plugin) do you use?

neovim v0.10.0 + lspconfig

Which version of HLS do you use and how did you install it?

2.9.0.1 from ghcup

Have you configured HLS in any way (especially: a hie.yaml file)?

No hie.yaml file. HLS is configured with:

{ haskell =
  { plugin =
    { ["ghcide-completions"] =
      { config =
        { snippetsOn = false
        , autoExtendOn = false
        }
      }
    , semanticTokens = { globalOn = true }
    }
  , maxCompletions = 100
  , sessionLoading = "multipleComponents"
  , formattingProvider = "stylish-haskell"
  }
}

Steps to reproduce

  1. Init a new project with executable and library via "cabal init".
  2. Open Main.hs with editor and wait HLS to get ready.
  3. Try goto-definition on someFunc in Main.hs, it won't work.

Expected behaviour

The goto-definition should work.

Actual behaviour

The goto-definition doesn't work.

Debug information

If I opened the library file MyLib.hs once, goto-definition will work even after I quit neovim, do "cabal clean" and then reopen Main.hs. The issue seems only affect the newly created and not yet opened local library.

BurningLutz commented 4 months ago

more information, If the new project is created inside a directory that is already indexed by HLS, the goto-definiation seems to work even I didn't open the MyLib.hs file once.

fendor commented 4 months ago

Hi, thank you for your bug report!

This is expected behaviour, we do not have a notion to eagerly load components directly. We still load components lazily, e.g. only when you actually open them. The reason it works on subsequent start is, we have a sqlite database (hiedb) which caches the relevant information (e.g. definitions), so it works once it has been indexed.

There is one work-around, tell cabal to load all components with this hie.yaml:

cradle:
  cabal:
    component: all

If tests and benchmarks are enabled in your cabal.project, this is equivalent to a fully eager load of the project.

Anrock commented 4 months ago

IMO eagerly loading everything sounds like a desirable behaviour. IDE that works only for some symbols or shows only some diagnostics depending on what files user have touched before sounds pretty flawed to me. Perhaps HLS could index other files in low-priority background thread? Or maybe not all files but all current file imports and reverse imports?

michaelpj commented 4 months ago

See https://github.com/haskell/haskell-language-server/issues/3873.

I do agree that it would be great if we at least indexed everything. However I think at the moment that requires loading everything into our session, which is a big, global choice. So I don't know that we can easily do this in the background.

I think probably what we would ideally do is get cabal to build the components out-of-process and get it to write the HIE files, which should then let us index things?

BurningLutz commented 4 months ago

Thanks for all your replies! The workaround @fendor provided works great!

As @fendor and @michaelpj pointed, this is currently by design and there is #3873 tracking this problem, I'm going to close this now.