Open Dessix opened 2 years ago
Worth noting that we can probably do this for records easily enough, but I'm not sure how easy it would be to handle arbitrary "fields" created by user-provided HasField
instances.
@michaelpj Maybe it's possible to find all instances of HasField
in scope for a particular type? That would handle both record fields and user defined fields, I believe.
(Just to clarify, I'm talking about completions, hovers should be fine in all cases.)
Well, typeclass instances can apply non-obviously, can depend on what's in scope from local type signatures, etc. In the limit, that amounts to "run the typechecker in some special way", I think.
So in increasing order of difficulty:
HasField
with literal Symbol
s and no superclass constraintsHasField
instances.That implementation order would cover the largest portion of use cases with just part 1, while covering niche cases like Haskell-Methods in part 3. I think "superrecord"-style types and such fall in part 2?
Regardless- part 1 would bring support to the most obvious case of record dot syntax, and also covers a lot of scenarios like the ReaderIO / RIO pattern. I'd suggest splitting this into three implementation/feature parts, if it helps close on part 1 more easily.
This is going to be tackled as part of GSoC by @coltenwebb.
I'll be addressing these features in separate PRs, and track them here.
I've started brainstorming how to do the record field completions, so I'll write my thoughts here:
For the record field completions, it looks like I might be able to extend this to get the name of the record that is dotted. From there, I should be able to calculate the type by searching the HieAST for the text that's dotted. My question then is how to get a fresh HieAST while calculating the completions, and whether computing the types on the fly will be fast enough. Perhaps there is a way to pass the HieAST to this, or to access it with LspM. Thoughts/feedback on this would be appreciated.
Edit: It turns out that types get inferred top-down, so using HieAst directly will cause completions not to show for some edge cases. For example in y = "hello" ++ (x.c)
, the x.c
will have [Char] type no matter what c actually should be, so completions for x.c.
will break. A standalone record will always have the correct type it seems, so x.
completions will still work.
If that works, I need a way to get the fields for the record. I don't think HieDB provides a way to read the record fields. One workaround could be querying with hiedb for functions of type MyRecord -> *
but this doesn't seem robust, especially since NoFieldSelectors
would ruin it. Thoughts would be appreciated here too.
A few note about the status of hover and error message recording OverloadedRecordDot:
Since https://github.com/haskell/haskell-language-server/pull/3016 we got support for hover on record dot, but:
x.foo
, we can hover on foo
and get informations. However map (.foo) someX
, hovering on foo
does not give any informationSee how when I have my point on x
, all the other reference to x
are highlighted:
Could not deduce ‘HasField "patients" MyType
and most of the time the solution is as simple as importing the constructors for this type (e.g. adding MyType(..)
to the import list). However there is no code action for that, which is painful. Could it be as simple as matching this kind of error and adding a suggestion to add the import? Is this something which should be upstreamed to GHC so they add a suggestion we can use?Is this something which should be upstreamed to GHC so they add a suggestion we can use?
I think that would be good. We very much rely on GHC to drive our import suggestions. Since HasField
is a magic typeclass, it would be very helpful if GHC could suggest imports that could be added to make that constraint solvable!
There are already an issue opened on GHC side related to suggesting import or using another name in case of typo: https://gitlab.haskell.org/ghc/ghc/-/issues/18776
Given the following (GHC 9.2+) snippet:
HLS now understands (without record-dot-preprocessor!) that
y
is aString
andz
is anInteger
, seen in both above-line hints and on hover.Unfortunately, it has no type information available when hovering over the
a
inx.a
or theb
inx.b
. Additionally, it lacks the ability to suggest- when entering a dot after a record type- the fields accessible on that type.Describe the solution you'd like
The ability to "dot into" types is both a small form of IDE-driven API exploration and a way to easily manage larger applications without needing a Haddock window on the side at all times.
Pressing '.' after a type (or manually opening auto-completion with the cursor directly following a dot following a type) should summon auto-completion with the context narrowed to the prior type's fields.
For a step further, but one worth considering when designing a solution, a useful addition would be allowing automatic import of the type information required if necessary, similar to when accepting a completion of an unimported
trait
function in rust-analyzer. Documentation on the HasField requirements is available here (HasField constraints) and here (OverloadedRecordDot).NoFieldSelectors
may complicate this functionality.Describe alternatives you've considered
I've built my glue-code in Rust instead, so far, to avoid having to manage large numbers of one-off record types in Haskell, but I'd love to unify my codebase, and Record Dot Syntax has been the selling point that would get me there, assuming the presence of IDE support. It appears that 9.2.1 support is now functioning, but dot-accesses appear opaque to the language server.