@@ -9,13 +9,13 @@ module Ide.Plugin.Export.ExactPrint
99 , appendIE
1010 , addCtorUnderParent
1111 , printExportList
12- , toDeltaExportList
12+ , printIE
13+ , freshCtorEntry
1314 ) where
1415
1516import Control.Lens (_last , over )
1617import Data.Bifunctor (first )
1718import Data.List.NonEmpty (NonEmpty (.. ))
18- import qualified Data.List.NonEmpty as NE
1919import Data.Text (Text )
2020import qualified Data.Text as T
2121import Development.IDE.GHC.Compat
@@ -40,7 +40,6 @@ import GHC (DeltaPos (..),
4040
4141import Language.Haskell.GHC.ExactPrint (addComma ,
4242 exactPrint ,
43- makeDeltaAst ,
4443 setEntryDP )
4544
4645#if MIN_VERSION_ghc(9,11,0)
@@ -167,9 +166,8 @@ mkTypeWithIE parent ctors =
167166 Nothing
168167#endif
169168 where
170- children = case NE. toList ctors of
171- [] -> [] -- impossible
172- (c: cs) -> mkIEName c : map (\ x -> first addComma (mkIEName x)) cs
169+ children = mkIEName c : map (first addComma . mkIEName) cs
170+ c :| cs = ctors
173171
174172-- | Map over an @IEThingWith@'s listed constructors, a no-op for any other item.
175173overThingWithChildren :: ([LIEWrappedName GhcPs ] -> [LIEWrappedName GhcPs ]) -> IE GhcPs -> IE GhcPs
@@ -226,58 +224,70 @@ separatorComma items =
226224
227225-- | 'Nothing' iff @ctor@ is already exported (via @T(..)@ or @T(...,ctor,...)@).
228226addCtorUnderParent ::
229- RdrName {- ^ parent -} ->
230- RdrName {- ^ ctor -} ->
227+ -- | parent
228+ RdrName ->
229+ -- | ctor
230+ RdrName ->
231231 LExportList ->
232232 Maybe LExportList
233233addCtorUnderParent parent ctor lst@ (L l items) =
234- case findParent items of
235- ParentNotFound -> Just $ appendIE (mkTypeWithIE parent (ctor :| [] )) lst
236- FoundIEThingAll -> Nothing
237- FoundIEThingWith CtorPresent -> Nothing
238- FoundIEThingWith CtorAbsent -> Just (L l (map (transformParent extendThingWith) items))
239- FoundIEThingAbs ->
240- let upgraded = unLoc (mkTypeWithIE parent (ctor :| [] ))
241- in Just (L l (map (transformParent (const upgraded)) items))
234+ case ctorExportEdit parent ctor items of
235+ AlreadyExported -> Nothing
236+ AppendParent -> Just (appendIE newThing lst)
237+ UpgradeBare -> Just (L l (map (transformParent (const (unLoc newThing))) items))
238+ AddChild -> Just (L l (map (transformParent (addCtorChildren ctor)) items))
242239 where
243- parentFS = rdrNameFS parent
244- ctorFS = rdrNameFS ctor
245-
246- ctorPresence cs
247- | any ((== ctorFS) . lieWrappedNameFS) cs = CtorPresent
248- | otherwise = CtorAbsent
249-
250- findParent [] = ParentNotFound
251- findParent (L _ ie : rest)
252- | parentNameIs parentFS ie =
253- case ie of
254- IEThingAll {} -> FoundIEThingAll
255- IEThingAbs {} -> FoundIEThingAbs
256- _ | Just cs <- ieThingWithChildren ie -> FoundIEThingWith (ctorPresence cs)
257- | otherwise -> findParent rest
258- | otherwise = findParent rest
259-
240+ newThing = mkTypeWithIE parent (ctor :| [] )
260241 transformParent f (L itemLoc ie)
261- | parentNameIs parentFS ie = L itemLoc (f ie)
242+ | parentNameIs (rdrNameFS parent) ie = L itemLoc (f ie)
262243 | otherwise = L itemLoc ie
263244
264- extendThingWith :: IE GhcPs -> IE GhcPs
265- extendThingWith = overThingWithChildren $ \ cs ->
266- let hasSibling = not (null cs)
267- newChild = setEntryDP (mkIEName ctor) (SameLine (if hasSibling then 1 else 0 ))
268- in (if hasSibling then map (first ensureTrailingComma) cs else cs) ++ [newChild]
245+ -- | Append @ctor@ to an @IEThingWith@'s children, reusing the sibling separator
246+ -- comma. No-op for other items.
247+ addCtorChildren :: RdrName -> IE GhcPs -> IE GhcPs
248+ addCtorChildren ctor = overThingWithChildren $ \ cs ->
249+ let hasSibling = not (null cs)
250+ newChild = setEntryDP (mkIEName ctor) (SameLine (if hasSibling then 1 else 0 ))
251+ in (if hasSibling then map (first ensureTrailingComma) cs else cs) ++ [newChild]
269252
270253printExportList :: LExportList -> Text
271254printExportList l = T. pack (exactPrint (setEntryDP l (SameLine 0 )))
272255
273- toDeltaExportList :: LExportList -> LExportList
274- toDeltaExportList = makeDeltaAst
256+ -- | Exactprint a single item, without the surrounding list layout. The
257+ -- trailing separator comma counts as layout: dropping it keeps a spliced item
258+ -- from carrying a stray comma into text that already supplies its own.
259+ printIE :: LIE GhcPs -> Text
260+ printIE item = T. pack (exactPrint (setEntryDP (first removeTrailingCommaAnn item) (SameLine 0 )))
275261
276- data FindParentResult
277- = ParentNotFound
278- | FoundIEThingAll
279- | FoundIEThingWith CtorPresence
280- | FoundIEThingAbs
262+ -- | A fresh @T(ctor)@ export entry rendered as text, or 'Nothing' if @ctor@ is
263+ -- already exported in the parsed list. Under CPP this adds a standalone entry so
264+ -- the splice never reprints an existing @T(...)@ span, which can straddle a
265+ -- directive.
266+ freshCtorEntry :: RdrName -> RdrName -> [LIE GhcPs ] -> Maybe Text
267+ freshCtorEntry parent ctor items = case ctorExportEdit parent ctor items of
268+ AlreadyExported -> Nothing
269+ _ -> Just (printIE (mkTypeWithIE parent (ctor :| [] )))
281270
282- data CtorPresence = CtorAbsent | CtorPresent
283- deriving Eq
271+ -- | How to add @ctor@ to an export list so its parent type @T@ exports it.
272+ data CtorEdit
273+ = AlreadyExported -- ^ @T(..)@ or @T(..., ctor, ...)@, nothing to do
274+ | AppendParent -- ^ no entry for @T@ yet, add a fresh @T(ctor)@
275+ | UpgradeBare -- ^ replace the bare @T@ entry with @T(ctor)@
276+ | AddChild -- ^ add @ctor@ to the existing @T(...)@ entry
277+
278+ -- | Decide how @ctor@ should be added under @parent@, classifying the first
279+ -- matching export item by its constructor-carrying shape.
280+ ctorExportEdit :: RdrName -> RdrName -> [LIE GhcPs ] -> CtorEdit
281+ ctorExportEdit parent ctor = go
282+ where
283+ parentFS = rdrNameFS parent
284+ ctorFS = rdrNameFS ctor
285+ go [] = AppendParent
286+ go (L _ ie : rest)
287+ | parentNameIs parentFS ie = case ie of
288+ IEThingAll {} -> AlreadyExported
289+ IEThingAbs {} -> UpgradeBare
290+ _ | Just cs <- ieThingWithChildren ie ->
291+ if any ((== ctorFS) . lieWrappedNameFS) cs then AlreadyExported else AddChild
292+ | otherwise -> go rest
293+ | otherwise = go rest
0 commit comments