@@ -7,7 +7,7 @@
77-- | Rendering of declarations.
88module Ormolu.Printer.Meat.Declaration
99 ( p_hsDecls,
10− hasSeparatedDecls,
10+ p_hsDeclsRespectGrouping,
1111 )
1212where
1313
@@ -36,18 +36,52 @@ import Ormolu.Printer.Meat.Type
3636import Ormolu.Utils
3737import RdrName (rdrNameOcc)
3838
39+data UserGrouping
40+ = -- | Always put newlines where we think they should be
41+ Disregard
42+ | -- | Respect user preferences regarding grouping
43+ Respect
44+ deriving (Eq, Show)
45+
3946p_hsDecls :: FamilyStyle -> [LHsDecl GhcPs] -> R ()
40−p_hsDecls style decls = sepSemi id $
47+p_hsDecls = p_hsDecls' Disregard
48+
49+-- | Like 'p_hsDecls' but respects user choices regarding grouping. If the
50+-- user omits newlines between declarations, we also omit them in most
51+-- cases, except when said declarations have associated Haddocks.
52+--
53+-- Does some normalization (compress subsequent newlines into a single one)
54+p_hsDeclsRespectGrouping :: FamilyStyle -> [LHsDecl GhcPs] -> R ()
55+p_hsDeclsRespectGrouping = p_hsDecls' Respect
56+
57+p_hsDecls' :: UserGrouping -> FamilyStyle -> [LHsDecl GhcPs] -> R ()
58+p_hsDecls' grouping style decls = sepSemi id $
4159 -- Return a list of rendered declarations, adding a newline to separate
4260 -- groups.
4361 case groupDecls decls of
4462 [] -> []
45− (x : xs) ->
46− NE.toList (renderGroup x)
47− ++ concatMap (NE.toList . separateGroup . renderGroup) xs
63+ (x : xs) -> renderGroup x ++ concat (zipWith renderGroupWithPrev (x : xs) xs)
4864 where
49− renderGroup = fmap (located' $ dontUseBraces . p_hsDecl style)
50− separateGroup (x :| xs) = (breakpoint' >> x) :| xs
65+ renderGroup = NE.toList . fmap (located' $ dontUseBraces . p_hsDecl style)
66+ renderGroupWithPrev prev curr =
67+ -- We can omit a blank line when the user didn't add one, but we must
68+ -- ensure we always add blank lines around documented declarations
69+ if or
70+ [ grouping == Disregard,
71+ separatedByBlank getLoc prev curr,
72+ isDocumented prev,
73+ isDocumented curr
74+ ]
75+ then breakpoint : renderGroup curr
76+ else renderGroup curr
77+
78+-- | Is a declaration group documented?
79+isDocumented :: NonEmpty (LHsDecl GhcPs) -> Bool
80+isDocumented = any (isHaddock . unLoc)
81+ where
82+ isHaddock DocNext = True
83+ isHaddock DocPrev = True
84+ isHaddock _ = False
5185
5286-- | Group relevant declarations together.
5387--
@@ -139,6 +173,9 @@ groupedDecls ::
139173 HsDecl GhcPs ->
140174 Bool
141175groupedDecls (TypeSignature ns) (FunctionBody ns') = ns `intersects` ns'
176+groupedDecls (TypeSignature ns) (DefaultSignature ns') = ns `intersects` ns'
177+groupedDecls (DefaultSignature ns) (TypeSignature ns') = ns `intersects` ns'
178+groupedDecls (DefaultSignature ns) (FunctionBody ns') = ns `intersects` ns'
142179groupedDecls x (FunctionBody ns) | Just ns' <- isPragma x = ns `intersects` ns'
143180groupedDecls (FunctionBody ns) x | Just ns' <- isPragma x = ns `intersects` ns'
144181groupedDecls x (DataDeclaration n) | Just ns <- isPragma x = n `elem` ns
@@ -149,6 +186,7 @@ groupedDecls x y | Just ns <- isPragma x, Just ns' <- isPragma y = ns `intersect
149186groupedDecls x (TypeSignature ns) | Just ns' <- isPragma x = ns `intersects` ns'
150187groupedDecls (TypeSignature ns) x | Just ns' <- isPragma x = ns `intersects` ns'
151188groupedDecls (PatternSignature ns) (Pattern n) = n `elem` ns
189+-- This looks only at Haddocks, normal comments are handled elsewhere
152190groupedDecls DocNext _ = True
153191groupedDecls _ DocPrev = True
154192groupedDecls _ _ = False
@@ -164,13 +202,6 @@ intersects a b = go (sort a) (sort b)
164202 | x > y = go (x : xs) ys
165203 | otherwise = True
166204
167−-- | Checks if given list of declarations contain a pair which should
168−-- be separated by a blank line.
169−hasSeparatedDecls :: [LHsDecl GhcPs] -> Bool
170−hasSeparatedDecls xs = case groupDecls xs of
171− _ : _ : _ -> True
172− _ -> False
173−
174205isPragma ::
175206 HsDecl GhcPs ->
176207 Maybe [RdrName]
@@ -206,11 +237,13 @@ pattern DataDeclaration n <- TyClD NoExt (DataDecl NoExt (L _ n) _ _ _)
206237
207238pattern
208239 TypeSignature,
240+ DefaultSignature,
209241 FunctionBody,
210242 PatternSignature,
211243 WarningPragma ::
212244 [RdrName] -> HsDecl GhcPs
213245pattern TypeSignature n <- (sigRdrNames -> Just n)
246+pattern DefaultSignature n <- (defSigRdrNames -> Just n)
214247pattern FunctionBody n <- (funRdrNames -> Just n)
215248pattern PatternSignature n <- (patSigRdrNames -> Just n)
216249pattern WarningPragma n <- (warnSigRdrNames -> Just n)
@@ -225,6 +258,10 @@ sigRdrNames (SigD NoExt (ClassOpSig NoExt _ ns _)) = Just $ map unLoc ns
225258sigRdrNames (SigD NoExt (PatSynSig NoExt ns _)) = Just $ map unLoc ns
226259sigRdrNames _ = Nothing
227260
261+defSigRdrNames :: HsDecl GhcPs -> Maybe [RdrName]
262+defSigRdrNames (SigD NoExt (ClassOpSig NoExt True ns _)) = Just $ map unLoc ns
263+defSigRdrNames _ = Nothing
264+
228265funRdrNames :: HsDecl GhcPs -> Maybe [RdrName]
229266funRdrNames (ValD NoExt (FunBind NoExt (L _ n) _ _ _)) = Just [n]
230267funRdrNames (ValD NoExt (PatBind NoExt n _ _)) = Just $ patBindNames n