"Go To Underlying Type" HLS Plugin draft

Hello everyone,

For a long time I’ve been annoyed at looking at a binding’s type but not having a convenient way to navigate to that type’s definition.

For example:

es <- fetchEntitiesFromDb

Hovering reveals es :: [Entity], but there’s no quick way to jump to the definition of Entity.

Sure, you can use typed holes, but the process is clunky:

  1. Add a typed hole (es :: _)

  2. Maybe add ScopedTypeVariables

  3. Wait for GHC to infer the type

  4. Realize Entity isn’t in scope

  5. Wait for the import suggestion

  6. Import it

  7. Then go to its definition

Most often I will just resort to searching for Entity = throughout the code base because it’s faster.

That’s why I drafted the hls-underlying-type-plugin.

For the example above, the plugin will generate a Go to definition of Entity (inferred from es's type) action which will take you straight to Entity’s definition. It works on nested types too - if you have a ContainerType Entity you will see two code actions - Go to definition of ContainerType and Go to definition of Entity.

Here it is in action:

underlying-typess

The PR has just been put up and it probably has a lot of quirks to iron out but it was very fun implementing it and I encourage everyone to go mess around with the HLS code base.

19 Likes

Looks great! What does it do with curried functions like Foo → Bar → Baz ? Will it jump to Baz? And what about Foo → Bar → IO Baz?

1 Like

Could you link the pr?

1 Like

Hovering reveals es :: [Entity], but there’s no quick way to jump to the definition of Entity.

Doesn’t “Go to Type Definition” basically do what you are describing? This also works for multiple types under the cursor

Afaict, you are introducing a CodeAction in addition to `Go to Type Definition`

goto_type_def

Looks great! What does it do with curried functions like Foo → Bar → Baz ? Will it jump to Baz? And what about Foo → Bar → IO Baz?

Iirc, all types are offered as possible locations.

1 Like

PR here - DRAFT: Add hls-underlying-type-plugin by dnikolovv · Pull Request #4685 · haskell/haskell-language-server · GitHub

1 Like

It will generate actions for all mentioned types:

It probably should be capped if you’re on a function that’s too large.

Doesn’t “Go to Type Definition” basically do what you are describing? This also works for multiple types under the cursor

Lol, I’ve never seen “Go to Type Definition”. Funnily, I discussed this plugin with colleagues before implementing it and none of them were aware of it too. People’s feedback on reddit seems to suggest that this is the case for many more.

I still find the code actions more convenient because the VS Code UI is clunkier than just selecting “Go to X"/Y”. Maybe this plugin should simply sit on top and generate those?

1 Like

I still find the code actions more convenient because the VS Code UI is clunkier than just selecting “Go to X"/Y”. Maybe this plugin should simply sit on top and generate those?

That’s exactly what your plugin does right now, as it uses the AtPoint.pointCommand and getTypeDefinition, which both power the go to Type Definition request.

I suppose we could add the CodeAction , but I personally would not use CodeActions for code navigation.
If users would like this kind of CodeAction, I think adding this to ghcide is preferable to a separate plugin.
However, Go to Type Definition is a standard LSP feature, thus likely the most discoverable way to navigate the code.

Also, well done with the plugin, it looks like it is mostly doing the right thing, which is pretty impressive on a first attempt!

2 Likes

If nothing else, this is some good marketing for the seemingly obscure Go To Type Definition.

Not sure if users “want” this CodeAction as much as they’re not aware this feature exists. I’ve been using HLS since day 0 and never knew.

Also, well done with the plugin, it looks like it is mostly doing the right thing, which is pretty impressive on a first attempt!

Thanks! It was certainly fun.

I think adding this to ghcide is preferable to a separate plugin.

What would that look like?

I didn’t know HLS already had this functionality either, and it’s actually pretty awesome. It even properly presents choices when the expression has a compound type, showing each type that one can go to (i.e. those defined in local units). I didn’t expect the LSP spec to be flexible enough for that.

So I don’t think any new feature is needed.

For what it’s worth, in VSCode one can easily add a keybinding to “Go to type definition”. Most desktops also have some universal right-click keybinding, e.g. Shift-F10 in Windows and Gnome, at least. EDIT: Whoops, not actually true, but Shift-F10 is supported by most applications in practice one way or another.

3 Likes

I’m surprised not many people know about go to definition! if it’s any help, in helix the command is g-d and it works with bindings and types.

1 Like

Yeah, I also don’t think new functionality is needed so I’ll close this PR but very happy to find out HLS already supports this :smiley:

I know about standard standard go-to-definition. This discussion is about Helix’s g-y rather than g-d.

Didn’t know about that one! thanks :slight_smile: