Skip to content

Commit 2c1799f

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 bf75a82 commit 2c1799f

File tree

2 files changed

+23
-5
lines changed

2 files changed

+23
-5
lines changed

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

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import GHC (AnnContext (..),
4343
IsUnicodeSyntax (NormalSyntax),
4444
NameAdornment (NameParens),
4545
TrailingAnn (AddCommaAnn),
46-
emptyComments, reAnnL)
46+
emptyComments, reAnnL, EpAnnComments (..))
4747

4848

4949
-- See Note [Guidelines For Using CPP In GHCIDE Import Statements]
@@ -170,7 +170,7 @@ appendConstraint constraintT = go . traceAst "appendConstraint"
170170
constraint <- liftParseAST df constraintT
171171
constraint <- pure $ setEntryDP constraint (SameLine 1)
172172
#if MIN_VERSION_ghc(9,9,0)
173-
let l'' = fmap (addParensToCtxt close_dp) l'
173+
let l'' = moveCommentsToTheEnd $ fmap (addParensToCtxt close_dp) l'
174174
#else
175175
let l'' = (fmap.fmap) (addParensToCtxt close_dp) l'
176176
#endif
@@ -205,6 +205,24 @@ appendConstraint constraintT = go . traceAst "appendConstraint"
205205

206206
return $ reLocA $ L lTop $ HsQualTy noExtField context ast
207207

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