haskell / haskell-language-server

Official haskell ide support via language server (LSP). Successor of ghcide & haskell-ide-engine.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Can not go to definition of non local libraries (i.e. dependencies)

i-am-the-slime opened this issue · comments

Is this because it is downloaded in some binary form?

Currently goto definition only works for the local package. Where should it take you for the base library? Typically people don't even have a copy of the source installed. The Haddock documentation is one option, but it's no longer really goto definition...

fwiw we do have some logic for cross-package goto definition based on HIE files that works for DAML. It allows you to configure the location of the sources so it would be possible to point it to something like ~/.cabal/packages/hackage.haskell.org. But it definitely requires more work and the location of the sources should probably come from hie-bios as there is no way we can figure out where they are located (if they are present at all).

The Haddock documentation is one option, but it's no longer really goto definition...

Could we add support for goto documentation?

Currently goto definition only works for the local package. Where should it take you for the base library? Typically people don't even have a copy of the source installed. The Haddock documentation is one option, but it's no longer really goto definition...

This doesn't help in the short term, but it's good to remember these aren't immutable restrictions.

haskell-code-explorer allows cross-package jumping (so long as you've indexed each dependency) and it is insanely nice. Seriously, go to a random function and try it out. I really, really want this in my editor as well.

That said, I know that ghcide is new, and at the moment I'm just delighted that it works so well. But I'd like to keep the sights for Haskell editor tooling set high in the long run. I'd be 100% OK with downloading the source for each of my dependencies (and waiting X hours for them all to be indexed) if it meant jump-to-definition would always work in my editor.

PS In the meantime jumping to the Haddocks would be totally fine with me.

commented

I recommend using codex meanwhile. It can generate ctags for all the dependencies

@arthurborisow: Great point as a short term solution.

In the long run I'm really hoping for something better than codex though. Lots of terms (especially ones with common names like Success) appear in many different packages, and it's jarring to be taken to the wrong one so often.

Note that we have the fully qualified name, as base-version.Prelude.Just or whatever, it's just where to send you that we need to figure out.

This feature is included in @wz1000 hiedb branch of ghcide and merge it would bring it to master

@wz1000 commented in a duplicate issue in how to use its hiedb branch, that includes this feature: https://github.com/haskell/ghcide/issues/745#issuecomment-683334743

The new pull request that will add hiedb support in hls is #704

This will not be addressed in #704 but in an upcoming PR.

@wz1000 hi! had you the chance to prepare that pr finally?

There is a closely related ghc proposal: https://discourse.haskell.org/t/extended-dependency-generation-support/2811
Not sure if it is necessary to implement the feature (as there is a wip since quite time ago)

@pepeiborra @wz1000 it would be great to have some idea about the plans for this, as there are lot of people eager to see this land. Otoh i am not sure if there is a wip about or not.
thanks!

I had a somewhat working implementation of this, but there were some issues. In principle, most of what needs to be done is provide an implementation for lookupMod here - possibly taking inspiration from this commit which reverted support for this feature from my WIP branch (there were some unresolved problems with the feature and I decided to focus on getting the rest of the branch merged first).

The feature as before it was reverted by that commit worked in the following fashion

  • The user to build their dependencies with -fwrite-ide-info and then index the resulting .hie files with ghcide/HLS. This can be done by cding to the project root directory and calling ghcide hiedb index dir/with/hie/files.

  • Once the dependencies are indexed into the database, when we get a go to definition request for something in an indexed module, we check if we have a source file registered for the module in the database. If so, we can just return that location. (this is still the code path as implemented today)

    Otherwise lookupMod is called. Currently lookupMod always fails. To implement the non-local definition feature, lookupMod needs to get the contents of the module from the indexed .hie files and then write out a file somewhere onto the filesystem with those contents. It then needs to register this file in the database as the source file for this module (so that future lookups would directly jump to the file we wrote). Finally, it returns the location of this file so the client can jump to it.

    The file has to be written somewhere in the users project directory or we won't be able to handle LSP requests for it from the same server.

The major problems which need to be solved are:

  • A nicer workflow for allowing the user to access this feature. Currently they need to add something like the following to their cabal.project:

    package *
      ghc-options: -fwrite-ide-info -hiedir /some/absolute/directory
    

    And then manually index /some/absolute/directory/ whenever it changes.

  • We have a new class of read-only haskell source files which we can't compile. We have to somehow ensure that most rules
    and plugins cannot try to run on these files (#2047 seems to be facing similar issues, a solution could be shared between these). However, rules that only depend on GetHieAST and direct derivatives can probably run just fine. For example, hover, references, document symbols and go to definition should work on these files.

    Also, we must ignore all edits to these files. This ties into the previous point about not trying to compile them with GHC, which we must not do. Unfortunately LSP doesn't support read only files (microsoft/language-server-protocol#1150) or virtual documents (microsoft/language-server-protocol#336) or our life would be much easier. We can mark the source files for dependencies as write-protected on the file system, but this doesn't prevent most editors from editing them in memory without saving them on disk (which we will get change notifications for).

  • There are also inconsistencies with how cabal handles .hie files, the assumption is the same for stack as well, but has not been explored. For example, on first compilation using cabal, it is possible to generate the exhaustive .hie files for all external dependencies. However cabal will not track the state of .hie files and will not recompile an external dependency if the .hie is removed.

    - This is most likely due to error, but @drsooch encountered strange results attempting to capture external dependencies with -fwrite-ide-info. In most scenarios, it was almost impossible to regenerate external library .hie files, other than the first compilation. User Error :) -- use absolute paths.

  • The main problem faced was the time it took to index a large project. For example HLS generated ~2500 .hie files, and it took ~20 minutes to index all of them. There is no great solution to abstract this away from the user. If it is possible to do it internally, is it possible to spin off a long-running process for that long?

many thanks for the detailed description of the state of the festure, I hope it will help to make progress on this

The comment about the plan to get the feature has been updated by @drsooch after work in its analysis, thanks!

I wonder how other langs has resolved the issue (for example ruts), maybe it would worth to have a look.

@wz1000 Several thoughts come to mind. You've listed several blockers:

  1. No way to advertise file as read-only with LSP (or virtual document)
  2. It will take 20+ minutes to index large projects.
  3. cabal and stack do not respect .hie files as build obligations like it does .hi and .o binaries.
  4. Users shouldn't have to include specific ghc-options because that's confusing.

These may all be facile points coming from someone who doesn't maintain any of this software, but here are the thoughts that jump out at me:

  1. Why not chmod a=r files extracted from HIE's (or equivalent on Windows resp.) since we are already the sole user of these extracted files? It doesn't look like a read-only notify interface is coming from the looks of microsoft/language-server-protocol#1150. Most editors respect read-only settings of files anyways though there is always the option to override. This in-memory read-only override behaviour should be considered out of scope as far as HLS is concerned because users must explicitly do this and more than likely realize that their tooling isn't obligated to update its internal model according to those changes. With respect to the virtual document proposal, there is no timeline for that being merged and shouldn't be anticipated.
  2. This is then a question of bottlenecks in indexing. Why does it take half a second on average to index a single HIE in a project like HLS? Does the serialization of writes to SQLite in hiedb also inhibit parallel indexing? To resolve some of these bottlenecks, we may want to take a page from rust-analyzer which I believe doesn't even have the concept of "ahead-of-time indexing" and begs the question: what end is indexing achieving that couldn't be achieve by lazily evaluating queries through information already provided to hiedb? Beyond performance bottlenecks though, there are some behaviours we should expect from indexing in HLS: backgrounding this indexing work and prioritizing indexing of pertinent files. These two suggestions would make waiting for such indexing seem more reasonable, though this only pertain to HLS if ahead-of-time indexing continues to be necessary.
  3. This is clearly a blocker, but it seems like it is out of scope for HLS. I think the best course of action is to bug folks working on stack and cabal to get them to add HIE's to their build obligations if -fwrite-ide-info is known to be a GHC build option.
  4. Can't HLS just add custom GHC command-line options through hie-bios? What is stopping us from amending GHC options this way? Is it that hie-bios options only enter the picture after a build has complete?

With regarding to this issue, do any of the circumstances change to make go-to-definition work for packages added via source-repository-package in cabal.project?

I've developed this - https://github.com/kr3v/haskell-gtd-nl - as an attempt to implement the requested feature ('go to definition for non-local libraries'). It is independent from HLS / vscode-haskell (separate VS Code extension, separate server (low memory usage mostly, can be used with HLS on low-memory machines)). I hope it will fill the gap until the feature is added to HLS.