Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sig inlay hints for where clause #4368

Open
wants to merge 30 commits into
base: master
Choose a base branch
from

Conversation

jetjinser
Copy link
Contributor

ref #2966.

Image_1722433614391

@jetjinser jetjinser requested a review from wz1000 as a code owner August 1, 2024 06:44
@jetjinser jetjinser requested a review from fendor as a code owner August 9, 2024 09:37
Copy link
Collaborator

@fendor fendor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it is in a rather good shape, I have only a couple of nitpicks and the suggestion to move the syb traversal code into a defineRule to avoid expensive recomputations when nothing changes.

ghcide/src/Development/IDE/Plugin/TypeLenses.hs Outdated Show resolved Hide resolved
ghcide/src/Development/IDE/Plugin/TypeLenses.hs Outdated Show resolved Hide resolved
ghcide/src/Development/IDE/Plugin/TypeLenses.hs Outdated Show resolved Hide resolved
ghcide/src/Development/IDE/Plugin/TypeLenses.hs Outdated Show resolved Hide resolved
ghcide/src/Development/IDE/Plugin/TypeLenses.hs Outdated Show resolved Hide resolved
ghcide/src/Development/IDE/Plugin/TypeLenses.hs Outdated Show resolved Hide resolved
ghcide/src/Development/IDE/Plugin/TypeLenses.hs Outdated Show resolved Hide resolved
ghcide/src/Development/IDE/Plugin/TypeLenses.hs Outdated Show resolved Hide resolved
@fendor fendor self-requested a review August 21, 2024 19:24
Copy link
Collaborator

@fendor fendor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks good to me, Ill try to give it a spin tomorrow as well.
I do need the latest version of vscode-haskell to try this out, right?

@jetjinser
Copy link
Contributor Author

The code looks good to me, Ill try to give it a spin tomorrow as well. I do need the latest version of vscode-haskell to try this out, right?

Yes! The latest vscode-haskell can enable inlay-hints.

@fendor
Copy link
Collaborator

fendor commented Aug 23, 2024

Thanks, I was able to try it out and it works as advertised, very handy!

Personally, I kind of dislike the inlay hints and would prefer them as lenses as inlay hints may make the code even less aligned for definitions with multiple function heads. See this example where at least I was briefly confused.

image

This is obviously unrelated to your PR and something we need to hash out in the future, I am mainly wondering what the default behaviour should be.

@jetjinser
Copy link
Contributor Author

inlay hints may make the code even less aligned for definitions with multiple function heads.

Yes, that is indeed a problem.
A better look I can think of is to make the font size of the inlay hints smaller in the editor config, which will better highlight the original code. But this still doesn't solve the problem of it breaking the alignment.

BTW, the main problem with code lens is that its position is not predictable and is determined by the editor.

Copy link
Collaborator

@michaelpj michaelpj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking pretty good, I think!

I wonder if we should roll in some other cases. I think the top-level code lenses should also be inlay hints (if that's supported), and we could maybe do let-bindings too, since I'm sure that's very similar to where-bindings?


whereBindingType :: Maybe TcGblEnv -> Maybe HscEnv -> IO (Maybe WhereBindingTypeSigsResult)
whereBindingType (Just gblEnv) (Just hsc) = do
let wheres = findWhereQ (tcg_binds gblEnv)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should just go ahead and do lets at the same time? I would imagine it's very similar code?

where
col = srcSpanStartCol . realSrcSpan

-- | Example: Find `a` and `b` from @(a,b) = (1,True)@
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem quite right, it's only finding one thing, not multiple things?

nfp <- getNormalizedFilePathE uri
(WhereBindingTypeSigsResult (localBindings, sigMap), pm)
<- runActionE "InlayHint.GetWhereBindingTypeSigs" state $ useWithStaleE GetWhereBindingTypeSigs nfp
let bindingToInlayHints id sig = generateWhereInlayHints (T.pack $ printName (idName id)) (maybe "_" T.pack sig)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should probably just omit things where we don't have a signature, rather than putting in _?

, WhereBinding{..} <- bindings
, let bindingSpan = getSrcSpan (idName bindingId)
, let bindingSig = Map.lookup bindingId sigMap
, bindingSpan `notElem` sigSpans
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a little complicated? I wonder if we could just track the Ids that have signatures already?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that Sig GhcTc does not exists (not sure), that is to say, Id (IdP GhcTc) does not exist in HsLocalBinds, and currently only Name (IdP GhcRn) can be obtained from Sig GhcRn.
ref: https://hackage.haskell.org/package/ghc-9.10.1/docs/GHC-Hs-Binds.html#v:NValBinds

On the other hand, the pass of LHsBinds is GhcTc, and the Name obtained from bindingId is different from theName in Sig because their Unique is different.
ref: https://hackage.haskell.org/package/ghc-9.10.1/docs/GHC-Types-Unique.html#v:nonDetCmpUnique

If the Ids that have signatures is not easily available, I've tried comparing via OccName, but maybe SrcSpan is still better?

, let bindingSig = Map.lookup bindingId sigMap
, bindingSpan `notElem` sigSpans
, Just bindingRange <- maybeToList $ toCurrentRange pm <$> srcSpanToRange bindingLoc
-- , Just bindingRange <- [srcSpanToRange bindingLoc]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commented out


editTest :: String -> TestTree
editTest file =
testWithDummyPlugin (file <> " (InlayHint EditText)") (mkIdeTestFs [copyDir "local-sig-lens"]) $ do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the directory should be "local-sig-hint", perhaps?

, testGroup "apply EditText"
[ hintTest "Simple" $ (@=?)
[defInlayHint { _position = Position 5 9
, _label = InL ":: Bool"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should display the hints on the previous line rather than right after the binding. i.e. make the hint look like the edit currently does.

Advantages:

  1. It makes the effect of applying the hint align with how the hint looks, which is I think what we're supposed to do.
  2. It makes the situation less bad if the type is very long. That might already be annoying, but it could be even more annoying if it's right in the middle of something you're trying to read. Maybe we should look at what other language servers do?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think inlay hints can do this.
I found this issue: microsoft/language-server-protocol#1821.

AFAIK, standard inlay hints cannot be in virtual line.
It's more like a fixed position code lens?

@jetjinser
Copy link
Contributor Author

jetjinser commented Sep 2, 2024

findLetStmt :: ExprStmt GhcTc -> [HsLocalBinds GhcTc]
findLetStmt (LetStmt _ binds) = [binds]
-- TODO(jinser): why `foo <- expr` does not exist
-- findLetStmt (BindStmt _ _ expr) = findLetExpr (unLoc expr)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering why this doesn't work, expr in BindStmt contains Var but not Bind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants