Skip to content

Commit 3fcf4df

Browse files
committed
Portage.EBuild: Switch to LinesBuilder
LinesBuilder is a custom Monad that works by building up a list of prettyprinter Docs and then concats them with vcat. It is helpful for when a logical piece of the ebuild may use a variable number of lines, including no lines at all (for instance when no CABAL_HACKAGE_REVISION or CABAL_PN are used). Improve some formatting Signed-off-by: hololeap <[email protected]>
1 parent 62fc029 commit 3fcf4df

File tree

1 file changed

+91
-52
lines changed

1 file changed

+91
-52
lines changed

src/Portage/EBuild.hs

Lines changed: 91 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ Functions and types related to interpreting and manipulating an ebuild,
77
as understood by the Portage package manager.
88
-}
99
{-# LANGUAGE CPP #-}
10+
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1011
{-# LANGUAGE OverloadedStrings #-}
12+
{-# LANGUAGE TypeFamilies #-} -- Needed to get OverloadedStrings to work
1113
module Portage.EBuild
1214
( EBuild(..)
1315
, ebuildTemplate
@@ -29,8 +31,12 @@ import qualified Data.Function as F
2931
import qualified Data.List as L
3032
import qualified Data.List.Split as LS
3133
import Data.Version(Version(..))
32-
import Data.Maybe (catMaybes, maybeToList)
34+
import Control.Monad.Trans.Writer.CPS
35+
import Data.Foldable (toList)
36+
import Data.Maybe (catMaybes)
37+
import Data.Monoid (Endo(..)) -- more efficient list concat
3338
import Hackport.Pretty
39+
import GHC.Exts (IsString(..))
3440

3541
import Network.URI
3642
import qualified Paths_hackport(version)
@@ -100,66 +106,65 @@ ebuildTemplate = EBuild {
100106

101107
-- | Pretty-print an 'EBuild' as a 'String'.
102108
showEBuild :: TC.UTCTime -> EBuild -> String
103-
showEBuild now ebuild = renderDoc $ vsep $
104-
[ "# Copyright 1999-" <> this_year <> " Gentoo Authors"
105-
, "# Distributed under the terms of the GNU General Public License v2"
106-
, emptyDoc
107-
, "EAPI=8"
108-
, emptyDoc
109-
, "# ebuild generated by hackport" <+> pretty (hackportVersion ebuild)
110-
, vsep $ map
111-
(\(k,v) -> "#hackport: " <> pretty k <> ": " <> pretty v)
112-
(used_options ebuild)
113-
, emptyDoc
114-
, beforeInherit
115-
, "CABAL_FEATURES=" <> dquotes (hsep (map (pretty . render) (features ebuild)))
116-
, "inherit haskell-cabal"
117-
, emptyDoc
118-
, "DESCRIPTION=" <> dquotes (pretty (drop_tdot (description ebuild)))
119-
, "HOMEPAGE=" <> dquotes (pretty (toHttps $ expandVars (homepage ebuild)))
120-
, emptyDoc
121-
, "LICENSE=" <> (either (\err -> dquotes emptyDoc <> "\t# FIXME: " <> pretty err)
122-
(dquotes . pretty)
123-
(license ebuild))
124-
, "SLOT=" <> dquotes (pretty (slot ebuild))
125-
, "KEYWORDS=" <> dquotes (hsep (map pretty (keywords ebuild)))
126-
, let iuseList = hsep $ map pretty $ sort_iuse $ L.nub $ iuse ebuild
127-
x = if null (iuse ebuild)
128-
then Nothing
129-
else Just $ "IUSE=" <> dquotes iuseList
130-
-- IUSE line if needed, then an empty line
131-
in vsep $ maybeToList x ++ [emptyDoc]
132-
, dep_str "RDEPEND" (rdepend_extra ebuild) (rdepend ebuild)
133-
, dep_str "DEPEND" ( depend_extra ebuild) ( depend ebuild)
134-
] ++ catMaybes
135-
[ verbatim
109+
showEBuild now ebuild = replaceLeadingSpaces $ renderDoc $ execLinesBuilder $ do
110+
fromDoc $ "# Copyright 1999-" <> this_year <> " Gentoo Authors"
111+
"# Distributed under the terms of the GNU General Public License v2"
112+
emptyLine
113+
"EAPI=8"
114+
emptyLine
115+
fromDoc $ "# ebuild generated by hackport" <+> pretty (hackportVersion ebuild)
116+
let optLine (k,v) = "#hackport: " <> pretty k <> ": " <> pretty v
117+
fromDocs $ map optLine (used_options ebuild)
118+
emptyLine
119+
preFeatures
120+
fromDoc $ "CABAL_FEATURES=" <> dquotes (hsep (map (pretty . render) (features ebuild)))
121+
"inherit haskell-cabal"
122+
emptyLine
123+
fromDoc $ "DESCRIPTION=" <> dquotes (pretty (drop_tdot (description ebuild)))
124+
fromDoc $ "HOMEPAGE=" <> dquotes (pretty (toHttps (expandVars (homepage ebuild))))
125+
emptyLine
126+
fromDoc $ "LICENSE=" <> (either (\err -> dquotes emptyDoc <> "\t# FIXME: " <> pretty err)
127+
(dquotes . pretty)
128+
(license ebuild) )
129+
fromDoc $ "SLOT=" <> dquotes (pretty (slot ebuild))
130+
fromDoc $ "KEYWORDS=" <> dquotes (hsep (map pretty (keywords ebuild)))
131+
fromDocs $
132+
if null (iuse ebuild)
133+
then Nothing
134+
else
135+
let iuseList = hsep $ map pretty $ sort_iuse $ L.nub $ iuse ebuild
136+
in Just $ "IUSE=" <> dquotes iuseList
137+
emptyLine
138+
dep_str "RDEPEND" (rdepend_extra ebuild) (rdepend ebuild)
139+
dep_str "DEPEND" ( depend_extra ebuild) ( depend ebuild)
140+
verbatim
136141
(vsep [emptyDoc, "src_prepare() {"])
137-
(src_prepare ebuild)
142+
(map pretty (src_prepare ebuild))
138143
"}"
139-
, verbatim
144+
verbatim
140145
(vsep [emptyDoc, "src_configure() {"])
141-
(src_configure ebuild)
146+
(map pretty (src_configure ebuild))
142147
"}"
143-
]
144148
where
145149
expandVars = replaceMultiVars [ ( name ebuild, "${PN}")
146150
, (hackage_name ebuild, "${HACKAGE_N}")
147151
]
148152

149-
150153
this_year :: Doc ann
151154
this_year = pretty $ TC.formatTime TC.defaultTimeLocale "%Y" now
152155

153-
beforeInherit =
154-
let rev = revision ebuild
155-
mpn = cabal_pn ebuild
156-
-- Any needed lines then an empty line
157-
in vsep $ catMaybes [revLine rev, pnLine <$> mpn] ++ [emptyDoc]
156+
preFeatures =
157+
let rev = revLine (revision ebuild)
158+
pn = pnLine <$> cabal_pn ebuild
159+
preList = catMaybes [rev, pn]
160+
in if null preList
161+
then pure ()
162+
else fromDocs $ preList ++ [emptyDoc]
158163

159164
revLine "0" = Nothing
160-
revLine r = Just $ hcat ["CABAL_HACKAGE_REVISION=", pretty r]
165+
revLine r = Just $ "CABAL_HACKAGE_REVISION=" <> pretty r
161166

162-
pnLine pn = hcat ["CABAL_PN=", dquotes (pretty pn)]
167+
pnLine pn = "CABAL_PN=" <> dquotes (pretty pn)
163168

164169
-- | Convert http urls into https urls, unless whitelisted as http-only.
165170
--
@@ -203,14 +208,21 @@ sort_iuse = L.sortBy (compare `F.on` dropWhile ( `elem` ("+" :: String)))
203208
drop_tdot :: String -> String
204209
drop_tdot = reverse . dropWhile (== '.') . reverse
205210

206-
verbatim :: Doc ann -> [String] -> Doc ann -> Maybe (Doc ann)
211+
-- | Print the @pre@ line, each line in the list and the @post@ line, _only_
212+
-- if the list is not empty.
213+
verbatim :: Doc ann -> [Doc ann] -> Doc ann -> LinesBuilder ann ()
207214
verbatim pre s post
208-
| null s = Nothing
209-
| otherwise = Just $ vsep $ [pre] ++ map (hang 1 . pretty) s ++ [post]
215+
| null s = pure ()
216+
| otherwise = fromDocs $ [pre] ++ s ++ [post]
210217

211-
dep_str :: Doc ann -> [String] -> Dependency -> Doc ann
212-
dep_str var extra dep = var <> "=" <> dquotes (hang 1 inner)
213-
where inner = vsep $ map pretty extra ++ [showDepend (PN.normalize_depend dep)]
218+
dep_str :: Doc ann -> [String] -> Dependency -> LinesBuilder ann ()
219+
dep_str var extra dep = fromDoc $ vsep
220+
[ var <> "=\""
221+
, indent 1 (
222+
vsep (map pretty extra ++ [showDepend (PN.normalize_depend dep)])
223+
)
224+
, "\""
225+
]
214226

215227
getRestIfPrefix :: String -- ^ the prefix
216228
-> String -- ^ the string
@@ -236,3 +248,30 @@ replaceMultiVars [] str = str
236248
replaceMultiVars whole@((pname,cont):rest) str = case subStr cont str of
237249
Nothing -> replaceMultiVars rest str
238250
Just (pre,post) -> (replaceMultiVars rest pre)++pname++(replaceMultiVars whole post)
251+
252+
253+
emptyLine :: LinesBuilder ann ()
254+
emptyLine = fromDoc emptyDoc
255+
256+
fromDoc :: Doc ann -> LinesBuilder ann ()
257+
fromDoc = fromDocs . (:[])
258+
259+
fromDocs :: Foldable t => t (Doc ann) -> LinesBuilder ann ()
260+
fromDocs = LinesBuilder . tell . Endo . (\x -> (toList x ++))
261+
262+
newtype LinesBuilder ann a = LinesBuilder (Writer (Endo [Doc ann]) a)
263+
deriving (Functor, Applicative, Monad)
264+
265+
instance (a ~ ()) => IsString (LinesBuilder ann a) where
266+
fromString = fromDoc . fromString
267+
268+
execLinesBuilder :: LinesBuilder ann a -> Doc ann
269+
execLinesBuilder (LinesBuilder w) = vsep (appEndo (execWriter w) [])
270+
271+
-- | Replace any leading spaces on each line with tabs
272+
replaceLeadingSpaces :: String -> String
273+
replaceLeadingSpaces = unlines . map go . lines
274+
where
275+
go (' ' : rest) = '\t' : go rest
276+
go rest = rest
277+

0 commit comments

Comments
 (0)