Skip to content

Commit 8cac5fb

Browse files
committed
fix: produce valid code when adding constraint to context
This closes #4648. When adding constraint to a context which is followed by a comment, such as: ``` foo :: (Monad m) => -- | This is a comment m () ``` The comment annotation is anchored to the previous token, which is `=>` in this context. If we add a new constraint in the context, the newly generated content goes beyond the anchor and, depending on GHC version, or ghc-exactprint (the reason is not fully understood), the comment is printed BEFORE the new constraint, leading to invalid syntax, such as `(Monad m -- | This is a comment , Applicative m =>)` This commit moves all the comment of the block at the end of the block using the `followingComments` of `EpAnnComments`. It seems super adhoc, but actually, consider the following example: ```haskell bar :: -- BEFORE {- yoto -} (Monad m {- yiti -}){- yutu -} => {- yete -} -- Trailing -- After m () ``` Comment `BEFORE` and `yoto` are attached to the previous block. Comment `yiti` is attached to `Monad m`. The comments `yiti`, `yutu`, `yete`, `Trailing` and `After` are all attached to this block and will hence be moved after the block. However this is not an easy task, all the associated comments should be moved by the relevant offset. TODO: do that instead.
1 parent dc4e674 commit 8cac5fb

File tree

2 files changed

+25
-4
lines changed

2 files changed

+25
-4
lines changed

plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import GHC (addAnns, ann)
6363

6464
#if MIN_VERSION_ghc(9,9,0)
6565
import GHC (NoAnn (..))
66+
import GHC (EpAnnComments (..))
6667
#endif
6768

6869
------------------------------------------------------------------------------
@@ -170,7 +171,7 @@ appendConstraint constraintT = go . traceAst "appendConstraint"
170171
constraint <- liftParseAST df constraintT
171172
constraint <- pure $ setEntryDP constraint (SameLine 1)
172173
#if MIN_VERSION_ghc(9,9,0)
173-
let l'' = fmap (addParensToCtxt close_dp) l'
174+
let l'' = moveCommentsToTheEnd $ fmap (addParensToCtxt close_dp) l'
174175
#else
175176
let l'' = (fmap.fmap) (addParensToCtxt close_dp) l'
176177
#endif
@@ -205,6 +206,26 @@ appendConstraint constraintT = go . traceAst "appendConstraint"
205206

206207
return $ reLocA $ L lTop $ HsQualTy noExtField context ast
207208

209+
#if MIN_VERSION_ghc(9,9,0)
210+
-- | This moves comment annotation toward the end of the block
211+
-- This is useful when extending a block, so the comment correctly appears
212+
-- after.
213+
--
214+
-- See https://github.com/haskell/haskell-language-server/issues/4648 for
215+
-- discussion.
216+
--
217+
-- For example, the following element, @(Foo) => -- hello@, when introducing an
218+
-- additionnal constraint, `Bar`, instead of getting `@(Foo, Bar) => -- hello@,
219+
-- we get @(Foo, -- hello Bar) =>@
220+
--
221+
-- This is a bit painful that the pretty printer is not able to realize that it
222+
-- introduces the token `=>` inside the comment and instead does something with
223+
-- meaning, but that's another story.
224+
moveCommentsToTheEnd :: EpAnn ann -> EpAnn ann
225+
moveCommentsToTheEnd (EpAnn entry anns (EpaComments priors)) = EpAnn entry anns (EpaCommentsBalanced { priorComments = [], followingComments = priors})
226+
moveCommentsToTheEnd (EpAnn entry anns (EpaCommentsBalanced priors following)) = EpAnn entry anns (EpaCommentsBalanced { priorComments = [], followingComments = priors <> following})
227+
#endif
228+
208229
liftParseAST
209230
:: forall ast l. (ASTElement l ast, ExactPrint (LocatedAn l ast))
210231
=> DynFlags -> String -> TransformT (Either String) (LocatedAn l ast)
@@ -500,7 +521,7 @@ extendHiding symbol (L l idecls) mlies df = do
500521
Nothing -> do
501522
#if MIN_VERSION_ghc(9,11,0)
502523
let ann :: EpAnn (AnnList (EpToken "hiding", [EpToken ","]))
503-
ann = noAnnSrcSpanDP0
524+
ann = noAnnSrcSpanDP0
504525
#elif MIN_VERSION_ghc(9,9,0)
505526
let ann = noAnnSrcSpanDP0
506527
#else

plugins/hls-refactor-plugin/test/Main.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3045,7 +3045,7 @@ addFunctionConstraintTests = let
30453045

30463046
[ "module Testing where"
30473047
, "foo "
3048-
, " :: (" <> constraint <> ") =>"
3048+
, " :: ("<> constraint <> ") =>"
30493049
, " -- This is a comment"
30503050
, " m ()"
30513051
, "foo = pure ()"
@@ -3098,7 +3098,7 @@ addFunctionConstraintTests = let
30983098
"preexisting constraint, with haddock comment in type signature"
30993099
"Add `Applicative m` to the context of the type signature for `foo`"
31003100
(incompleteConstraintSourceCodeWithCommentInTypeSignature "")
3101-
(incompleteConstraintSourceCodeWithCommentInTypeSignature "Applicative m")
3101+
(incompleteConstraintSourceCodeWithCommentInTypeSignature " Applicative m")
31023102
, checkCodeAction
31033103
"missing Monad constraint"
31043104
"Add `Monad m` to the context of the type signature for `f`"

0 commit comments

Comments
 (0)