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

Support Inlay hints for record wildcards #4351

Merged
merged 60 commits into from
Aug 28, 2024

Conversation

jetjinser
Copy link
Contributor

@jetjinser jetjinser commented Jul 7, 2024

resolve #4211.

This PR adds the feature to provide explicit records by inlay hints.

The inlay hints are located after dotdot and have left padding, It also provides textEdit, which is consistent with the textEdit of code actions.

e.g

data MyRec = MyRec
  { foo :: Int
  , bar :: Int
  , baz :: Char
  }

convertMe :: MyRec -> String
convertMe MyRec {foo, ..<inlay> bar, baz</inlay>} = show foo ++ show bar ++ show baz
MyRec {foo, .. bar, baz}
  _paddingLeft^  | |  |
        part  |1 |2|3 |

part1: bar
part2: ,
part3: baz

image

image

to writing test before new release of haskell/lsp.
and remove `instance PluginRequestMethod Method_InlayHintResolve`
since have not decide how to combine.
@jetjinser
Copy link
Contributor Author

I pushed some commits but it's broken on GHC 9.10.1, I'm working on fixing it.

@jetjinser
Copy link
Contributor Author

jetjinser commented Aug 10, 2024

I have spent a lot of time trying to fix the problem in higher GHC versions, but so far I can't figure out how to solve it.

The failed tests are HsExpanded1 and HsExpanded2.

I tested it in GHC 9.6.5 and 9.10.1. In 9.6.5, there are MyRec { .. } and MyRec { foo = foo } in tcg_binds, but in 9.10.1 there is only MyRec { foo = foo }.

MyRec { .. } should be processed in this function. But without MyRec { .. } (9.10.1), no records will be found.

getRecPatterns :: LPat GhcTc -> ([RecordInfo], Bool)
getRecPatterns conPat@(conPatDetails . unLoc -> Just (RecCon flds))
| isJust (rec_dotdot flds) = (mkRecInfo conPat, False)
where
mkRecInfo :: LPat GhcTc -> [RecordInfo]
mkRecInfo pat =
[ RecordInfoPat realSpan' (unLoc pat) | RealSrcSpan realSpan' _ <- [ getLoc pat ]]
getRecPatterns _ = ([], False)

Please take a look, thanks! @wz1000, @fendor.

@fendor
Copy link
Collaborator

fendor commented Aug 14, 2024

@jetjinser Sorry for the delay! I spent a little bit of time on this and while I cannot really tell you what's going wrong, I think this is either a bug in our HLS parsing/type checking pipeline or straight-up a GHC bug.

For example, the following Unused2.hs works completely fine:

{-# LANGUAGE RecordWildCards #-}
-- {-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE NamedFieldPuns #-}

module HsExpanded2 where
import Prelude

-- ifThenElse :: Int -> Int -> Int ->  Int
-- ifThenElse x y z = x + y + z

data MyRec = MyRec
  { foo :: Int }

data YourRec = YourRec
  { bar :: Bool }

myRecExample = MyRec 5

yourRecExample = YourRec True

convertMe :: Int
convertMe =
  if (let MyRec {..} = myRecExample
          YourRec {..} = yourRecExample
      in bar) then 1 else 2

However, this does also not work:

convertMe :: Int
convertMe =
    (let MyRec {..} = myRecExample
         YourRec {..} = yourRecExample
     in bar) + 1 + 2

I tested the file with ghc-exactprint and that the AST contains these nodes (which is what we are looking for):

                             (RecCon
                             (HsRecFields
                              []
                              (Just
                               (L
                                (EpaSpan { 24:20-21 })
                                (RecFieldsDotDot
                                 (0))))))))

That means that at least the parser is accurate.

My suggestion is to mark this test as broken for GHC 9.10.1 and move on for now.

@fendor
Copy link
Collaborator

fendor commented Aug 14, 2024

I found something else, take this ast representation of the tcg_binds

Found an expr: if (let
      MyRec {..} = myRecExample
      YourRec {..} = yourRecExample
    in bar_azh) then
    1
else
    2
(L
 (EpAnn
  (EpaSpan { (23,3)-(25,27) })
  (AnnListItem
   [])
  (EpaComments
   []))
 (XExpr
  (ExpandedThingTc
   (OrigExpr
    (HsIf
     (NoExtField)
     (L
      (EpAnn
       (EpaSpan { (23,6)-(25,13) })
       (AnnListItem
        [])
       (EpaComments
        []))
      (HsPar
       (NoExtField)
       (L
        (EpAnn
         (EpaSpan { (23,7)-(25,12) })
         (AnnListItem
          [])
         (EpaComments
          []))
        (HsLet
         (NoExtField)
         (HsValBinds
          (EpAnn
           (EpaSpan { (23,11)-(24,39) })
           (AnnList
            (Just
             (EpaSpan { (23,11)-(24,39) }))
            (Nothing)
            (Nothing)
            []
            [])
           (EpaComments
            []))
          (XValBindsLR
           (NValBinds
            [((,)
              (NonRecursive)
              {Bag(LocatedA (HsBind Name)):
               [(L
                 (EpAnn
                  (EpaSpan { 24:11-39 })
                  (AnnListItem
                   [])
                  (EpaComments
                   []))
                 (PatBind
                  {NameSet:
                   [{Name: YourRec}
                   ,{Name: bar}
                   ,{Name: yourRecExample}]}
                  (L
                   (EpAnn
                    (EpaSpan { 24:11-22 })
                    (AnnListItem
                     [])
                    (EpaComments
                     []))
                   (ConPat
                    (NoExtField)
                    (L
                     (EpAnn
                      (EpaSpan { 24:11-17 })
                      (NameAnnTrailing
                       [])
                      (EpaComments
                       []))
                     {Name: YourRec})
                    (RecCon
                     (HsRecFields
                      [(L
                        (EpAnn
                         (EpaSpan { 24:20-21 })
                         (AnnListItem
                          [])
                         (EpaComments
                          []))
                        (HsFieldBind
                         []
                         (L
                          (EpAnn
                           (EpaSpan { 24:20-21 })
                           (AnnListItem
                            [])
                           (EpaComments
                            []))
                          (FieldOcc
                           {Name: bar}
                           (L
                            (EpAnn
                             (EpaSpan { 24:20-21 })
                             (NameAnnTrailing
                              [])
                             (EpaComments
                              []))
                            (Unqual
                             {OccName: bar}))))
                         (L
                          (EpAnn
                           (EpaSpan { 24:20-21 })
                           (AnnListItem
                            [])
                           (EpaComments
                            []))
                          (VarPat
                           (NoExtField)
                           (L
                            (EpAnn
                             (EpaSpan { 24:20-21 })
                             (NameAnnTrailing
                              [])
                             (EpaComments
                              []))
                            {Name: bar_azh})))
                         (False)))]
                      (Just
                       (L
                        (EpaSpan { 24:20-21 })
                        (RecFieldsDotDot
                         (0))))))))

It looks like getRecCons (unLoc -> XExpr (ExpandedThingTc a _)) = (collectRecords a, True) doesn't quite work as intended. Changing it to collectRecords a, False) seems to be working.

FYI, I am using ghc-exactprints showAst to create these lisp like structures.

Thus, I am taking back my prior conjecture, this is unlikely to be a HLS or GHC bug :)

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.

Thanks, this looks quite nice already! Some small nitpicks and proposed changes :)

@jetjinser jetjinser requested a review from fendor August 21, 2024 18:16
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 good to me now :)

@michaelpj michaelpj added the merge me Label to trigger pull request merge label Aug 28, 2024
@mergify mergify bot merged commit 9f4d673 into haskell:master Aug 28, 2024
38 checks passed
soulomoon pushed a commit to soulomoon/haskell-language-server that referenced this pull request Sep 23, 2024
* Provide explicit import in inlay hints

* Filter explict imports inlay hints by visible range

* Update lsp dep by source-repository-package

to writing test before new release of haskell/lsp.

* Add test for hls-explicit-imports-plugin inlay hints

* Comment inlay hints start position

* Use `isSubrangeOf` to test if the range is visible

* Remove inlayHintsResolveProvider placeholder for now

* Use explicit InlayHintKind_Type

* Revert "Update lsp dep by source-repository-package"

This reverts commit 245049a.

* Combine InlayHints by sconcat them

and remove `instance PluginRequestMethod Method_InlayHintResolve`
since have not decide how to combine.

* compress multiple spaces in abbr import tilte

* update test to match inlay hints kind

* rename squashedAbbreviateImportTitle to abbreviateImportTitleWithoutModule

* Request inlay hints with testEdits

* ExplicitImports fallback to codelens when inlay hints not support

* fix explicitImports inlayHints test

* simplify isInlayHintsSupported

* comment fallback

* empty list instead of null codeLens

* clearify name `paddingLeft`

* fix clientCapabilities

* add test for inlay hints without its client caps

* use codeActionNoInlayHintsCaps to avoid error

* simplify isInlayHintSupported

* comment about paddingLeft

* use null as inlay hints kind

* add tooltip for explicit imports inlay hints to improve UX

* chore comments

* refactor

* comment InL [] to indicate no info

* ignore refine inlay hints

* add plcInlayHintsOn config

* update func-test

* keep order to make Parser works

* always provide refine in code lens

* init explicit record fields inlay hints

* dotdot location in label part

* update test for dotdot location in label part

* get(Type)Definition with its Identifier

* add flipped filterByRange

* filter label with name

* update test

* re-generate schema

* fix explict-record-fields plugin in GHC 910

* fix use correct currentPosition

* comment

* rename flippedFilterByRange to elementsInRange

* refactor: lift

* refactor: break pointfree

* refactor

* recover accidentally deleted macros

---------

Co-authored-by: Michael Peyton Jones <me@michaelpj.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merge me Label to trigger pull request merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Inlay hints for record wildcards
3 participants