{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NoImplicitPrelude #-} module Language.Haskell.Brittany.Internal.Layouters.Type where import qualified Data.Text as Text import GHC (AnnKeywordId(..), GenLocated(L)) import GHC.Hs import qualified GHC.OldList as List import GHC.Types.Basic import GHC.Utils.Outputable (ftext, showSDocUnsafe) import Language.Haskell.Brittany.Internal.LayouterBasics import Language.Haskell.Brittany.Internal.Prelude import Language.Haskell.Brittany.Internal.PreludeUtils import Language.Haskell.Brittany.Internal.Types import Language.Haskell.Brittany.Internal.Utils (FirstLastView(..), splitFirstLast) layoutType :: ToBriDoc HsType layoutType ltype@(L _ typ) = docWrapNode ltype $ case typ of -- _ | traceShow (ExactPrint.Types.mkAnnKey ltype) False -> error "impossible" HsTyVar _ promoted name -> do t <- lrdrNameToTextAnnTypeEqualityIsSpecial name case promoted of IsPromoted -> docSeq [ docSeparator , docTick , docWrapNode name $ docLit t ] NotPromoted -> docWrapNode name $ docLit t HsForAllTy _ hsf (L _ (HsQualTy _ (L _ cntxts) typ2)) -> do let bndrs = getBinders hsf typeDoc <- docSharedWrapper layoutType typ2 tyVarDocs <- layoutTyVarBndrs bndrs cntxtDocs <- cntxts `forM` docSharedWrapper layoutType let maybeForceML = case typ2 of (L _ HsFunTy{}) -> docForceMultiline _ -> id let tyVarDocLineList = processTyVarBndrsSingleline tyVarDocs forallDoc = docAlt [ let open = docLit $ Text.pack "forall" in docSeq ([open]++tyVarDocLineList) , docPar (docLit (Text.pack "forall")) (docLines $ tyVarDocs <&> \case (tname, Nothing) -> docEnsureIndent BrIndentRegular $ docLit tname (tname, Just doc) -> docEnsureIndent BrIndentRegular $ docLines [ docCols ColTyOpPrefix [ docParenLSep , docLit tname ] , docCols ColTyOpPrefix [ docLit $ Text.pack ":: " , doc ] , docLit $ Text.pack ")" ]) ] contextDoc = case cntxtDocs of [] -> docLit $ Text.pack "()" [x] -> x _ -> docAlt [ let open = docLit $ Text.pack "(" close = docLit $ Text.pack ")" list = List.intersperse docCommaSep $ docForceSingleline <$> cntxtDocs in docSeq ([open]++list++[close]) , let open = docCols ColTyOpPrefix [ docParenLSep , docAddBaseY (BrIndentSpecial 2) $ head cntxtDocs ] close = docLit $ Text.pack ")" list = List.tail cntxtDocs <&> \cntxtDoc -> docCols ColTyOpPrefix [ docCommaSep , docAddBaseY (BrIndentSpecial 2) cntxtDoc ] in docPar open $ docLines $ list ++ [close] ] docAlt -- :: forall a b c . (Foo a b c) => a b -> c [ docSeq [ if null bndrs then docEmpty else let open = docLit $ Text.pack "forall" close = docLit $ Text.pack " . " in docSeq ([open, docSeparator]++tyVarDocLineList++[close]) , docForceSingleline contextDoc , docLit $ Text.pack " => " , docForceSingleline typeDoc ] -- :: forall a b c -- . (Foo a b c) -- => a b -- -> c , docPar forallDoc ( docLines [ docCols ColTyOpPrefix [ docWrapNodeRest ltype $ docLit $ Text.pack " . " , docAddBaseY (BrIndentSpecial 3) $ contextDoc ] , docCols ColTyOpPrefix [ docLit $ Text.pack "=> " , docAddBaseY (BrIndentSpecial 3) $ maybeForceML $ typeDoc ] ] ) ] HsForAllTy _ hsf typ2 -> do let bndrs = getBinders hsf typeDoc <- layoutType typ2 tyVarDocs <- layoutTyVarBndrs bndrs let maybeForceML = case typ2 of (L _ HsFunTy{}) -> docForceMultiline _ -> id let tyVarDocLineList = processTyVarBndrsSingleline tyVarDocs docAlt -- forall x . x [ docSeq [ if null bndrs then docEmpty else let open = docLit $ Text.pack "forall" close = docLit $ Text.pack " . " in docSeq ([open]++tyVarDocLineList++[close]) , docForceSingleline $ return $ typeDoc ] -- :: forall x -- . x , docPar (docSeq $ docLit (Text.pack "forall") : tyVarDocLineList) ( docCols ColTyOpPrefix [ docWrapNodeRest ltype $ docLit $ Text.pack " . " , maybeForceML $ return typeDoc ] ) -- :: forall -- (x :: *) -- . x , docPar (docLit (Text.pack "forall")) (docLines $ (tyVarDocs <&> \case (tname, Nothing) -> docEnsureIndent BrIndentRegular $ docLit tname (tname, Just doc) -> docEnsureIndent BrIndentRegular $ docLines [ docCols ColTyOpPrefix [ docParenLSep , docLit tname ] , docCols ColTyOpPrefix [ docLit $ Text.pack ":: " , doc ] , docLit $ Text.pack ")" ] ) ++[ docCols ColTyOpPrefix [ docWrapNodeRest ltype $ docLit $ Text.pack " . " , maybeForceML $ return typeDoc ] ] ) ] HsQualTy _ lcntxts@(L _ cntxts) typ1 -> do typeDoc <- docSharedWrapper layoutType typ1 cntxtDocs <- cntxts `forM` docSharedWrapper layoutType let contextDoc = docWrapNode lcntxts $ case cntxtDocs of [] -> docLit $ Text.pack "()" [x] -> x _ -> docAlt [ let open = docLit $ Text.pack "(" close = docLit $ Text.pack ")" list = List.intersperse docCommaSep $ docForceSingleline <$> cntxtDocs in docSeq ([open]++list++[close]) , let open = docCols ColTyOpPrefix [ docParenLSep , docAddBaseY (BrIndentSpecial 2) $ head cntxtDocs ] close = docLit $ Text.pack ")" list = List.tail cntxtDocs <&> \cntxtDoc -> docCols ColTyOpPrefix [ docCommaSep , docAddBaseY (BrIndentSpecial 2) $ cntxtDoc ] in docPar open $ docLines $ list ++ [close] ] let maybeForceML = case typ1 of (L _ HsFunTy{}) -> docForceMultiline _ -> id docAlt -- (Foo a b c) => a b -> c [ docSeq [ docForceSingleline contextDoc , docLit $ Text.pack " => " , docForceSingleline typeDoc ] -- (Foo a b c) -- => a b -- -> c , docPar (docForceSingleline contextDoc) ( docCols ColTyOpPrefix [ docLit $ Text.pack "=> " , docAddBaseY (BrIndentSpecial 3) $ maybeForceML typeDoc ] ) ] HsFunTy _ _ typ1 typ2 -> do typeDoc1 <- docSharedWrapper layoutType typ1 typeDoc2 <- docSharedWrapper layoutType typ2 let maybeForceML = case typ2 of (L _ HsFunTy{}) -> docForceMultiline _ -> id hasComments <- hasAnyCommentsBelow ltype docAlt $ [ docSeq [ appSep $ docForceSingleline typeDoc1 , appSep $ docLit $ Text.pack "->" , docForceSingleline typeDoc2 ] | not hasComments ] ++ [ docPar (docNodeAnnKW ltype Nothing typeDoc1) ( docCols ColTyOpPrefix [ docWrapNodeRest ltype $ appSep $ docLit $ Text.pack "->" , docAddBaseY (BrIndentSpecial 3) $ maybeForceML typeDoc2 ] ) ] HsParTy _ typ1 -> do typeDoc1 <- docSharedWrapper layoutType typ1 docAlt [ docSeq [ docWrapNodeRest ltype $ docLit $ Text.pack "(" , docForceSingleline typeDoc1 , docLit $ Text.pack ")" ] , docPar ( docCols ColTyOpPrefix [ docWrapNodeRest ltype $ docParenLSep , docAddBaseY (BrIndentSpecial 2) $ typeDoc1 ]) (docLit $ Text.pack ")") ] HsAppTy _ typ1@(L _ HsAppTy{}) typ2 -> do let gather :: [LHsType GhcPs] -> LHsType GhcPs -> (LHsType GhcPs, [LHsType GhcPs]) gather list = \case L _ (HsAppTy _ ty1 ty2) -> gather (ty2:list) ty1 final -> (final, list) let (typHead, typRest) = gather [typ2] typ1 docHead <- docSharedWrapper layoutType typHead docRest <- docSharedWrapper layoutType `mapM` typRest docAlt [ docSeq $ docForceSingleline docHead : (docRest >>= \d -> [ docSeparator, docForceSingleline d ]) , docPar docHead (docLines $ docEnsureIndent BrIndentRegular <$> docRest) ] HsAppTy _ typ1 typ2 -> do typeDoc1 <- docSharedWrapper layoutType typ1 typeDoc2 <- docSharedWrapper layoutType typ2 docAlt [ docSeq [ docForceSingleline typeDoc1 , docSeparator , docForceSingleline typeDoc2 ] , docPar typeDoc1 (docEnsureIndent BrIndentRegular typeDoc2) ] HsListTy _ typ1 -> do typeDoc1 <- docSharedWrapper layoutType typ1 docAlt [ docSeq [ docWrapNodeRest ltype $ docLit $ Text.pack "[" , docForceSingleline typeDoc1 , docLit $ Text.pack "]" ] , docPar ( docCols ColTyOpPrefix [ docWrapNodeRest ltype $ docLit $ Text.pack "[ " , docAddBaseY (BrIndentSpecial 2) $ typeDoc1 ]) (docLit $ Text.pack "]") ] HsTupleTy _ tupleSort typs -> case tupleSort of HsUnboxedTuple -> unboxed HsBoxedTuple -> simple HsConstraintTuple -> simple HsBoxedOrConstraintTuple -> simple where unboxed = if null typs then error "brittany internal error: unboxed unit" else unboxedL simple = if null typs then unitL else simpleL unitL = docLit $ Text.pack "()" simpleL = do docs <- docSharedWrapper layoutType `mapM` typs let end = docLit $ Text.pack ")" lines = List.tail docs <&> \d -> docAddBaseY (BrIndentSpecial 2) $ docCols ColTyOpPrefix [docCommaSep, d] commaDocs = List.intersperse docCommaSep (docForceSingleline <$> docs) docAlt [ docSeq $ [docLit $ Text.pack "("] ++ docWrapNodeRest ltype commaDocs ++ [end] , let line1 = docCols ColTyOpPrefix [docParenLSep, head docs] in docPar (docAddBaseY (BrIndentSpecial 2) $ line1) (docLines $ docWrapNodeRest ltype lines ++ [end]) ] unboxedL = do docs <- docSharedWrapper layoutType `mapM` typs let start = docParenHashLSep end = docParenHashRSep docAlt [ docSeq $ [start] ++ docWrapNodeRest ltype (List.intersperse docCommaSep docs) ++ [end] , let line1 = docCols ColTyOpPrefix [start, head docs] lines = List.tail docs <&> \d -> docAddBaseY (BrIndentSpecial 2) $ docCols ColTyOpPrefix [docCommaSep, d] in docPar (docAddBaseY (BrIndentSpecial 2) line1) (docLines $ lines ++ [end]) ] HsOpTy{} -> -- TODO briDocByExactInlineOnly "HsOpTy{}" ltype -- HsOpTy typ1 opName typ2 -> do -- -- TODO: these need some proper fixing. precedences don't add up. -- -- maybe the parser just returns some trivial right recursion -- -- parse result for any type level operators. -- -- need to check how things are handled on the expression level. -- let opStr = lrdrNameToText opName -- let opLen = Text.length opStr -- layouter1@(Layouter desc1 _ _) <- layoutType typ1 -- layouter2@(Layouter desc2 _ _) <- layoutType typ2 -- let line = do -- Maybe -- l1 <- _ldesc_line desc1 -- l2 <- _ldesc_line desc2 -- let len1 = _lColumns_min l1 -- let len2 = _lColumns_min l2 -- let len = 2 + opLen + len1 + len2 -- return $ LayoutColumns -- { _lColumns_key = ColumnKeyUnique -- , _lColumns_lengths = [len] -- , _lColumns_min = len -- } -- let block = do -- Maybe -- rol1 <- descToBlockStart desc1 -- (min2, max2) <- descToMinMax (1+opLen) desc2 -- let (minR, maxR) = case descToBlockMinMax desc1 of -- Nothing -> (min2, max2) -- Just (min1, max1) -> (max min1 min2, max max1 max2) -- return $ BlockDesc -- { _bdesc_blockStart = rol1 -- , _bdesc_min = minR -- , _bdesc_max = maxR -- , _bdesc_opIndentFloatUp = Just (1+opLen) -- } -- return $ Layouter -- { _layouter_desc = LayoutDesc -- { _ldesc_line = line -- , _ldesc_block = block -- } -- , _layouter_func = \params -> do -- remaining <- getCurRemaining -- let allowSameLine = _params_sepLines params /= SepLineTypeOp -- case line of -- Just (LayoutColumns _ _ m) | m <= remaining && allowSameLine -> do -- applyLayouterRestore layouter1 defaultParams -- layoutWriteAppend $ Text.pack " " <> opStr <> Text.pack " " -- applyLayouterRestore layouter2 defaultParams -- _ -> do -- let upIndent = maybe (1+opLen) (max (1+opLen)) $ _params_opIndent params -- let downIndent = maybe upIndent (max upIndent) $ _bdesc_opIndentFloatUp =<< _ldesc_block desc2 -- layoutWithAddIndentN downIndent $ applyLayouterRestore layouter1 defaultParams -- layoutWriteNewline -- layoutWriteAppend $ opStr <> Text.pack " " -- layoutWriteEnsureBlockPlusN downIndent -- applyLayouterRestore layouter2 defaultParams -- { _params_sepLines = SepLineTypeOp -- , _params_opIndent = Just downIndent -- } -- , _layouter_ast = ltype -- } HsIParamTy _ (L _ (HsIPName ipName)) typ1 -> do typeDoc1 <- docSharedWrapper layoutType typ1 docAlt [ docSeq [ docWrapNodeRest ltype $ docLit $ Text.pack ("?" ++ showSDocUnsafe (ftext ipName) ++ "::") , docForceSingleline typeDoc1 ] , docPar ( docLit $ Text.pack ("?" ++ showSDocUnsafe (ftext ipName)) ) (docCols ColTyOpPrefix [ docWrapNodeRest ltype $ docLit $ Text.pack ":: " , docAddBaseY (BrIndentSpecial 2) typeDoc1 ]) ] -- TODO: test KindSig HsKindSig _ typ1 kind1 -> do typeDoc1 <- docSharedWrapper layoutType typ1 kindDoc1 <- docSharedWrapper layoutType kind1 hasParens <- hasAnnKeyword ltype AnnOpenP docAlt [ if hasParens then docSeq [ docLit $ Text.pack "(" , docForceSingleline typeDoc1 , docSeparator , docLit $ Text.pack "::" , docSeparator , docForceSingleline kindDoc1 , docLit $ Text.pack ")" ] else docSeq [ docForceSingleline typeDoc1 , docSeparator , docLit $ Text.pack "::" , docSeparator , docForceSingleline kindDoc1 ] , if hasParens then docLines [ docCols ColTyOpPrefix [ docWrapNodeRest ltype $ docParenLSep , docAddBaseY (BrIndentSpecial 3) $ typeDoc1 ] , docCols ColTyOpPrefix [ docWrapNodeRest ltype $ docLit $ Text.pack ":: " , docAddBaseY (BrIndentSpecial 3) kindDoc1 ] , (docLit $ Text.pack ")") ] else docPar typeDoc1 ( docCols ColTyOpPrefix [ docWrapNodeRest ltype $ docLit $ Text.pack ":: " , docAddBaseY (BrIndentSpecial 3) kindDoc1 ] ) ] HsBangTy{} -> -- TODO briDocByExactInlineOnly "HsBangTy{}" ltype -- HsBangTy bang typ1 -> do -- let bangStr = case bang of -- HsSrcBang _ unpackness strictness -> -- (++) -- (case unpackness of -- SrcUnpack -> "{-# UNPACK -#} " -- SrcNoUnpack -> "{-# NOUNPACK -#} " -- NoSrcUnpack -> "" -- ) -- (case strictness of -- SrcLazy -> "~" -- SrcStrict -> "!" -- NoSrcStrict -> "" -- ) -- let bangLen = length bangStr -- layouter@(Layouter desc _ _) <- layoutType typ1 -- let line = do -- Maybe -- l <- _ldesc_line desc -- let len = bangLen + _lColumns_min l -- return $ LayoutColumns -- { _lColumns_key = ColumnKeyUnique -- , _lColumns_lengths = [len] -- , _lColumns_min = len -- } -- let block = do -- Maybe -- rol <- descToBlockStart desc -- (minR,maxR) <- descToBlockMinMax desc -- return $ BlockDesc -- { _bdesc_blockStart = rol -- , _bdesc_min = minR -- , _bdesc_max = maxR -- , _bdesc_opIndentFloatUp = Nothing -- } -- return $ Layouter -- { _layouter_desc = LayoutDesc -- { _ldesc_line = line -- , _ldesc_block = block -- } -- , _layouter_func = \_params -> do -- remaining <- getCurRemaining -- case line of -- Just (LayoutColumns _ _ m) | m <= remaining -> do -- layoutWriteAppend $ Text.pack $ bangStr -- applyLayouterRestore layouter defaultParams -- _ -> do -- layoutWriteAppend $ Text.pack $ bangStr -- layoutWritePostCommentsRestore ltype -- applyLayouterRestore layouter defaultParams -- , _layouter_ast = ltype -- } HsSpliceTy{} -> -- TODO briDocByExactInlineOnly "HsSpliceTy{}" ltype HsDocTy{} -> -- TODO briDocByExactInlineOnly "HsDocTy{}" ltype HsRecTy{} -> -- TODO briDocByExactInlineOnly "HsRecTy{}" ltype HsExplicitListTy _ _ typs -> do typDocs <- docSharedWrapper layoutType `mapM` typs hasComments <- hasAnyCommentsBelow ltype let specialCommaSep = appSep $ docLit $ Text.pack " ," docAlt [ docSeq $ [docLit $ Text.pack "'["] ++ List.intersperse specialCommaSep (docForceSingleline <$> typDocs) ++ [docLit $ Text.pack "]"] , case splitFirstLast typDocs of FirstLastEmpty -> docSeq [ docLit $ Text.pack "'[" , docNodeAnnKW ltype (Just AnnOpenS) $ docLit $ Text.pack "]" ] FirstLastSingleton e -> docAlt [ docSeq [ docLit $ Text.pack "'[" , docNodeAnnKW ltype (Just AnnOpenS) $ docForceSingleline e , docLit $ Text.pack "]" ] , docSetBaseY $ docLines [ docSeq [ docLit $ Text.pack "'[" , docSeparator , docSetBaseY $ docNodeAnnKW ltype (Just AnnOpenS) e ] , docLit $ Text.pack " ]" ] ] FirstLast e1 ems eN -> runFilteredAlternative $ do addAlternativeCond (not hasComments) $ docSeq $ [docLit $ Text.pack "'["] ++ List.intersperse specialCommaSep (docForceSingleline <$> (e1:ems ++ [docNodeAnnKW ltype (Just AnnOpenS) eN])) ++ [docLit $ Text.pack " ]"] addAlternative $ let start = docCols ColList [appSep $ docLit $ Text.pack "'[", e1] linesM = ems <&> \d -> docCols ColList [specialCommaSep, d] lineN = docCols ColList [specialCommaSep, docNodeAnnKW ltype (Just AnnOpenS) eN] end = docLit $ Text.pack " ]" in docSetBaseY $ docLines $ [start] ++ linesM ++ [lineN] ++ [end] ] HsExplicitTupleTy{} -> -- TODO briDocByExactInlineOnly "HsExplicitTupleTy{}" ltype HsTyLit _ lit -> case lit of HsNumTy (SourceText srctext) _ -> docLit $ Text.pack srctext HsNumTy NoSourceText _ -> error "overLitValBriDoc: literal with no SourceText" HsStrTy (SourceText srctext) _ -> docLit $ Text.pack srctext HsStrTy NoSourceText _ -> error "overLitValBriDoc: literal with no SourceText" HsWildCardTy _ -> docLit $ Text.pack "_" HsSumTy{} -> -- TODO briDocByExactInlineOnly "HsSumTy{}" ltype HsStarTy _ isUnicode -> do if isUnicode then docLit $ Text.pack "\x2605" -- Unicode star else docLit $ Text.pack "*" XHsType{} -> error "brittany internal error: XHsType" HsAppKindTy _ ty kind -> do t <- docSharedWrapper layoutType ty k <- docSharedWrapper layoutType kind docAlt [ docSeq [ docForceSingleline t , docSeparator , docLit $ Text.pack "@" , docForceSingleline k ] , docPar t (docSeq [docLit $ Text.pack "@", k ]) ] layoutTyVarBndrs :: [LHsTyVarBndr () GhcPs] -> ToBriDocM [(Text, Maybe (ToBriDocM BriDocNumbered))] layoutTyVarBndrs = mapM $ \case (L _ (UserTyVar _ _ name)) -> return $ (lrdrNameToText name, Nothing) (L _ (KindedTyVar _ _ lrdrName kind)) -> do d <- docSharedWrapper layoutType kind return $ (lrdrNameToText lrdrName, Just $ d) -- there is no specific reason this returns a list instead of a single -- BriDoc node. processTyVarBndrsSingleline :: [(Text, Maybe (ToBriDocM BriDocNumbered))] -> [ToBriDocM BriDocNumbered] processTyVarBndrsSingleline bndrDocs = bndrDocs >>= \case (tname, Nothing) -> [docSeparator, docLit tname] (tname, Just doc) -> [ docSeparator , docLit $ Text.pack "(" <> tname <> Text.pack " :: " , docForceSingleline $ doc , docLit $ Text.pack ")" ] getBinders :: HsForAllTelescope pass -> [LHsTyVarBndr () pass] getBinders x = case x of HsForAllVis _ b -> b HsForAllInvis _ b -> fmap withoutSpecificity b XHsForAllTelescope _ -> [] withoutSpecificity :: LHsTyVarBndr flag pass -> LHsTyVarBndr () pass withoutSpecificity = fmap $ \case UserTyVar a _ c -> UserTyVar a () c KindedTyVar a _ c d -> KindedTyVar a () c d XTyVarBndr a -> XTyVarBndr a