mirror of
https://github.com/byteverse/colonnade.git
synced 2026-04-29 11:24:51 +02:00
some changes
This commit is contained in:
parent
9a14ce158a
commit
5d268119ce
@ -301,40 +301,69 @@ builderCell = lazyTextCell . TBuilder.toLazyText
|
|||||||
-- used to add attributes to the generated @\<tr\>@ elements.
|
-- used to add attributes to the generated @\<tr\>@ elements.
|
||||||
encodeTable ::
|
encodeTable ::
|
||||||
(Foldable f, Foldable h)
|
(Foldable f, Foldable h)
|
||||||
=> Maybe Attribute -- ^ Attributes of @\<thead\>@, pass 'Nothing' to omit @\<thead\>@
|
=> Maybe (Attribute,Attribute) -- ^ Attributes of @\<thead\>@ and its @\<tr\>@, pass 'Nothing' to omit @\<thead\>@
|
||||||
-> Attribute -- ^ Attributes of @\<tbody\>@ element
|
-> Attribute -- ^ Attributes of @\<tbody\>@ element
|
||||||
-> (a -> Attribute) -- ^ Attributes of each @\<tr\>@ element
|
-> (a -> Attribute) -- ^ Attributes of each @\<tr\>@ element
|
||||||
-> ((Html -> Html) -> c -> Html) -- ^ Wrap content and convert to 'Html'
|
-> ((Html -> Html) -> c -> Html) -- ^ Wrap content and convert to 'Html'
|
||||||
-> Attribute -- ^ Attributes of @\<table\>@ element
|
-> Attribute -- ^ Attributes of @\<table\>@ element
|
||||||
-> Colonnade h c a -- ^ How to encode data as a row
|
-> Colonnade h a c -- ^ How to encode data as a row
|
||||||
-> f a -- ^ Collection of data
|
-> f a -- ^ Collection of data
|
||||||
-> Html
|
-> Html
|
||||||
encodeTable mtheadAttrs tbodyAttrs trAttrs wrapContent tableAttrs colonnade xs =
|
encodeTable mtheadAttrs tbodyAttrs trAttrs wrapContent tableAttrs colonnade xs =
|
||||||
H.table ! tableAttrs $ do
|
H.table ! tableAttrs $ do
|
||||||
for_ mtheadAttrs $ \theadAttrs -> do
|
for_ mtheadAttrs $ \(theadAttrs,theadTrAttrs) -> do
|
||||||
H.thead ! theadAttrs $ do
|
H.thead ! theadAttrs $ H.tr ! theadTrAttrs $ do
|
||||||
Encode.headerMonoidalGeneral colonnade (wrapContent H.th)
|
Encode.headerMonoidalGeneral colonnade (wrapContent H.th)
|
||||||
H.tbody ! tbodyAttrs $ do
|
encodeBody trAttrs wrapContent tbodyAttrs colonnade xs
|
||||||
forM_ xs $ \x -> do
|
|
||||||
H.tr ! trAttrs x $ Encode.rowMonoidal colonnade (wrapContent H.td) x
|
encodeTieredHeaderTable :: Foldable f
|
||||||
|
=> Attribute -- ^ Attributes of @\<thead\>@
|
||||||
|
-> Attribute -- ^ Attributes of @\<tbody\>@ element
|
||||||
|
-> (a -> Attribute) -- ^ Attributes of each @\<tr\>@ element in the @\<tbody\>@
|
||||||
|
-> ((Html -> Html) -> c -> Html) -- ^ Wrap content and convert to 'Html'
|
||||||
|
-> Attribute -- ^ Attributes of @\<table\>@ element
|
||||||
|
-> Fascia p Attribute -- ^ Attributes for @\<tr\>@ elements in the @\<thead\>@
|
||||||
|
-> Cornice p a c
|
||||||
|
-> f a -- ^ Collection of data
|
||||||
|
-> Html
|
||||||
|
encodeTieredHeaderTable theadAttrs tbodyAttrs trAttrs wrapContent tableAttrs cornice xs = do
|
||||||
|
let colonnade = CE.discard cornice
|
||||||
|
annCornice = annotate cornice
|
||||||
|
H.table ! tableAttrs $ do
|
||||||
|
H.thead ! theadAttrs $ H.tr ! trAttrs $ do
|
||||||
|
Encode.headerMonoidalGeneral colonnade (wrapContent H.th)
|
||||||
|
encodeBody trAttrs wrapContent tbodyAttrs colonnade xs
|
||||||
|
|
||||||
|
encodeBody :: (Foldable h, Foldable f)
|
||||||
|
=> (a -> Attribute) -- ^ Attributes of each @\<tr\>@ element
|
||||||
|
-> ((Html -> Html) -> c -> Html) -- ^ Wrap content and convert to 'Html'
|
||||||
|
-> Attribute -- ^ Attributes of @\<tbody\>@ element
|
||||||
|
-> Colonnade h a c -- ^ How to encode data as a row
|
||||||
|
-> f a -- ^ Collection of data
|
||||||
|
-> Html
|
||||||
|
encodeBody trAttrs wrapContent tbodyAttrs colonnade xs = do
|
||||||
|
H.tbody ! tbodyAttrs $ do
|
||||||
|
forM_ xs $ \x -> do
|
||||||
|
H.tr ! trAttrs x $ Encode.rowMonoidal colonnade (wrapContent H.td) x
|
||||||
|
|
||||||
|
|
||||||
-- | Encode a table with a header. Table cells may have attributes
|
-- | Encode a table with a header. Table cells may have attributes
|
||||||
-- applied to them.
|
-- applied to them.
|
||||||
encodeHeadedCellTable ::
|
encodeHeadedCellTable ::
|
||||||
Foldable f
|
Foldable f
|
||||||
=> Attribute -- ^ Attributes of @\<table\>@ element
|
=> Attribute -- ^ Attributes of @\<table\>@ element
|
||||||
-> Colonnade Headed Cell a -- ^ How to encode data as columns
|
-> Colonnade Headed a Cell -- ^ How to encode data as columns
|
||||||
-> f a -- ^ Collection of data
|
-> f a -- ^ Collection of data
|
||||||
-> Html
|
-> Html
|
||||||
encodeHeadedCellTable = encodeTable
|
encodeHeadedCellTable = encodeTable
|
||||||
(Just mempty) mempty (const mempty) htmlFromCell
|
(Just (mempty,mempty)) mempty (const mempty) htmlFromCell
|
||||||
|
|
||||||
-- | Encode a table without a header. Table cells may have attributes
|
-- | Encode a table without a header. Table cells may have attributes
|
||||||
-- applied to them.
|
-- applied to them.
|
||||||
encodeHeadlessCellTable ::
|
encodeHeadlessCellTable ::
|
||||||
Foldable f
|
Foldable f
|
||||||
=> Attribute -- ^ Attributes of @\<table\>@ element
|
=> Attribute -- ^ Attributes of @\<table\>@ element
|
||||||
-> Colonnade Headless Cell a -- ^ How to encode data as columns
|
-> Colonnade Headless a Cell -- ^ How to encode data as columns
|
||||||
-> f a -- ^ Collection of data
|
-> f a -- ^ Collection of data
|
||||||
-> Html
|
-> Html
|
||||||
encodeHeadlessCellTable = encodeTable
|
encodeHeadlessCellTable = encodeTable
|
||||||
@ -345,18 +374,18 @@ encodeHeadlessCellTable = encodeTable
|
|||||||
encodeHeadedHtmlTable ::
|
encodeHeadedHtmlTable ::
|
||||||
Foldable f
|
Foldable f
|
||||||
=> Attribute -- ^ Attributes of @\<table\>@ element
|
=> Attribute -- ^ Attributes of @\<table\>@ element
|
||||||
-> Colonnade Headed Html a -- ^ How to encode data as columns
|
-> Colonnade Headed a Html -- ^ How to encode data as columns
|
||||||
-> f a -- ^ Collection of data
|
-> f a -- ^ Collection of data
|
||||||
-> Html
|
-> Html
|
||||||
encodeHeadedHtmlTable = encodeTable
|
encodeHeadedHtmlTable = encodeTable
|
||||||
(Just mempty) mempty (const mempty) ($)
|
(Just (mempty,mempty)) mempty (const mempty) ($)
|
||||||
|
|
||||||
-- | Encode a table without a header. Table cells cannot have attributes
|
-- | Encode a table without a header. Table cells cannot have attributes
|
||||||
-- applied to them.
|
-- applied to them.
|
||||||
encodeHeadlessHtmlTable ::
|
encodeHeadlessHtmlTable ::
|
||||||
Foldable f
|
Foldable f
|
||||||
=> Attribute -- ^ Attributes of @\<table\>@ element
|
=> Attribute -- ^ Attributes of @\<table\>@ element
|
||||||
-> Colonnade Headless Html a -- ^ How to encode data as columns
|
-> Colonnade Headless a Html -- ^ How to encode data as columns
|
||||||
-> f a -- ^ Collection of data
|
-> f a -- ^ Collection of data
|
||||||
-> Html
|
-> Html
|
||||||
encodeHeadlessHtmlTable = encodeTable
|
encodeHeadlessHtmlTable = encodeTable
|
||||||
|
|||||||
@ -31,6 +31,7 @@ library
|
|||||||
Colonnade
|
Colonnade
|
||||||
Colonnade.Encode
|
Colonnade.Encode
|
||||||
Colonnade.Internal
|
Colonnade.Internal
|
||||||
|
Colonnade.Cornice.Encode
|
||||||
build-depends:
|
build-depends:
|
||||||
base >= 4.7 && < 5
|
base >= 4.7 && < 5
|
||||||
, contravariant >= 1.2 && < 1.5
|
, contravariant >= 1.2 && < 1.5
|
||||||
|
|||||||
@ -1,12 +1,19 @@
|
|||||||
|
|
||||||
|
|
||||||
-- | Build backend-agnostic columnar encodings that can be
|
-- | Build backend-agnostic columnar encodings that can be
|
||||||
-- used to visualize tabular data.
|
-- used to visualize tabular data.
|
||||||
module Colonnade
|
module Colonnade
|
||||||
( -- * Example
|
( -- * Example
|
||||||
-- $setup
|
-- $setup
|
||||||
-- * Types
|
-- * Types
|
||||||
|
-- ** Colonnade
|
||||||
Colonnade
|
Colonnade
|
||||||
, Headed
|
, Headed
|
||||||
, Headless
|
, Headless
|
||||||
|
-- ** Cornice
|
||||||
|
, Cornice
|
||||||
|
, Pillar(..)
|
||||||
|
, Fascia(..)
|
||||||
-- * Create
|
-- * Create
|
||||||
, headed
|
, headed
|
||||||
, headless
|
, headless
|
||||||
@ -17,18 +24,19 @@ module Colonnade
|
|||||||
, bool
|
, bool
|
||||||
, replaceWhen
|
, replaceWhen
|
||||||
, modifyWhen
|
, modifyWhen
|
||||||
, mapContent
|
-- * Cornice
|
||||||
|
, cap
|
||||||
|
, recap
|
||||||
-- * Ascii Table
|
-- * Ascii Table
|
||||||
, ascii
|
, ascii
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Colonnade.Internal
|
import Colonnade.Internal
|
||||||
import qualified Colonnade.Encode as Encode
|
|
||||||
import Data.Vector (Vector)
|
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
import Data.Monoid (Endo(..))
|
import Data.Monoid (Endo(..))
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Data.Functor.Contravariant
|
import qualified Colonnade.Encode as Encode
|
||||||
|
import qualified Colonnade.Cornice.Encode as CE
|
||||||
import qualified Data.Bool
|
import qualified Data.Bool
|
||||||
import qualified Data.Maybe
|
import qualified Data.Maybe
|
||||||
import qualified Data.List as List
|
import qualified Data.List as List
|
||||||
@ -40,7 +48,7 @@ import qualified Data.Vector as Vector
|
|||||||
-- used for the remainder of the examples in the docs:
|
-- used for the remainder of the examples in the docs:
|
||||||
--
|
--
|
||||||
-- >>> import Data.Monoid (mconcat,(<>))
|
-- >>> import Data.Monoid (mconcat,(<>))
|
||||||
-- >>> import Data.Functor.Contravariant (contramap)
|
-- >>> import Data.Profunctor (lmap)
|
||||||
--
|
--
|
||||||
-- The data types we wish to encode are:
|
-- The data types we wish to encode are:
|
||||||
--
|
--
|
||||||
@ -51,7 +59,7 @@ import qualified Data.Vector as Vector
|
|||||||
-- One potential columnar encoding of a @Person@ would be:
|
-- One potential columnar encoding of a @Person@ would be:
|
||||||
--
|
--
|
||||||
-- >>> :{
|
-- >>> :{
|
||||||
-- let colPerson :: Colonnade Headed String Person
|
-- let colPerson :: Colonnade Headed Person String
|
||||||
-- colPerson = mconcat
|
-- colPerson = mconcat
|
||||||
-- [ headed "Name" name
|
-- [ headed "Name" name
|
||||||
-- , headed "Age" (show . age)
|
-- , headed "Age" (show . age)
|
||||||
@ -76,7 +84,7 @@ import qualified Data.Vector as Vector
|
|||||||
--
|
--
|
||||||
-- >>> let showDollar = (('$':) . show) :: Int -> String
|
-- >>> let showDollar = (('$':) . show) :: Int -> String
|
||||||
-- >>> :{
|
-- >>> :{
|
||||||
-- let encodingHouse :: Colonnade Headed String House
|
-- let encodingHouse :: Colonnade Headed House String
|
||||||
-- encodingHouse = mconcat
|
-- encodingHouse = mconcat
|
||||||
-- [ headed "Color" (show . color)
|
-- [ headed "Color" (show . color)
|
||||||
-- , headed "Price" (showDollar . price)
|
-- , headed "Price" (showDollar . price)
|
||||||
@ -95,15 +103,15 @@ import qualified Data.Vector as Vector
|
|||||||
|
|
||||||
|
|
||||||
-- | A single column with a header.
|
-- | A single column with a header.
|
||||||
headed :: c -> (a -> c) -> Colonnade Headed c a
|
headed :: c -> (a -> c) -> Colonnade Headed a c
|
||||||
headed h = singleton (Headed h)
|
headed h = singleton (Headed h)
|
||||||
|
|
||||||
-- | A single column without a header.
|
-- | A single column without a header.
|
||||||
headless :: (a -> c) -> Colonnade Headless c a
|
headless :: (a -> c) -> Colonnade Headless a c
|
||||||
headless = singleton Headless
|
headless = singleton Headless
|
||||||
|
|
||||||
-- | A single column with any kind of header. This is not typically needed.
|
-- | A single column with any kind of header. This is not typically needed.
|
||||||
singleton :: f c -> (a -> c) -> Colonnade f c a
|
singleton :: f c -> (a -> c) -> Colonnade f a c
|
||||||
singleton h = Colonnade . Vector.singleton . OneColonnade h
|
singleton h = Colonnade . Vector.singleton . OneColonnade h
|
||||||
|
|
||||||
-- | Lift a column over a 'Maybe'. For example, if some people
|
-- | Lift a column over a 'Maybe'. For example, if some people
|
||||||
@ -123,10 +131,10 @@ singleton h = Colonnade . Vector.singleton . OneColonnade h
|
|||||||
-- the help of 'fromMaybe':
|
-- the help of 'fromMaybe':
|
||||||
--
|
--
|
||||||
-- >>> :{
|
-- >>> :{
|
||||||
-- let colOwners :: Colonnade Headed String (Person,Maybe House)
|
-- let colOwners :: Colonnade Headed (Person,Maybe House) String
|
||||||
-- colOwners = mconcat
|
-- colOwners = mconcat
|
||||||
-- [ contramap fst colPerson
|
-- [ lmap fst colPerson
|
||||||
-- , contramap snd (fromMaybe "" encodingHouse)
|
-- , lmap snd (fromMaybe "" encodingHouse)
|
||||||
-- ]
|
-- ]
|
||||||
-- :}
|
-- :}
|
||||||
--
|
--
|
||||||
@ -138,7 +146,7 @@ singleton h = Colonnade . Vector.singleton . OneColonnade h
|
|||||||
-- | Ruth | 25 | Red | $125000 |
|
-- | Ruth | 25 | Red | $125000 |
|
||||||
-- | Sonia | 12 | Green | $145000 |
|
-- | Sonia | 12 | Green | $145000 |
|
||||||
-- +--------+-----+-------+---------+
|
-- +--------+-----+-------+---------+
|
||||||
fromMaybe :: c -> Colonnade f c a -> Colonnade f c (Maybe a)
|
fromMaybe :: c -> Colonnade f a c -> Colonnade f (Maybe a) c
|
||||||
fromMaybe c (Colonnade v) = Colonnade $ flip Vector.map v $
|
fromMaybe c (Colonnade v) = Colonnade $ flip Vector.map v $
|
||||||
\(OneColonnade h encode) -> OneColonnade h (maybe c encode)
|
\(OneColonnade h encode) -> OneColonnade h (maybe c encode)
|
||||||
|
|
||||||
@ -150,10 +158,10 @@ fromMaybe c (Colonnade v) = Colonnade $ flip Vector.map v $
|
|||||||
-- >>> let allColors = [Red,Green,Blue]
|
-- >>> let allColors = [Red,Green,Blue]
|
||||||
-- >>> let encColor = columns (\c1 c2 -> if c1 == c2 then "✓" else "") (Headed . show) allColors
|
-- >>> let encColor = columns (\c1 c2 -> if c1 == c2 then "✓" else "") (Headed . show) allColors
|
||||||
-- >>> :t encColor
|
-- >>> :t encColor
|
||||||
-- encColor :: Colonnade Headed [Char] Color
|
-- encColor :: Colonnade Headed Color [Char]
|
||||||
-- >>> let encHouse = headed "Price" (showDollar . price) <> contramap color encColor
|
-- >>> let encHouse = headed "Price" (showDollar . price) <> lmap color encColor
|
||||||
-- >>> :t encHouse
|
-- >>> :t encHouse
|
||||||
-- encHouse :: Colonnade Headed [Char] House
|
-- encHouse :: Colonnade Headed House [Char]
|
||||||
-- >>> putStr (ascii encHouse houses)
|
-- >>> putStr (ascii encHouse houses)
|
||||||
-- +---------+-----+-------+------+
|
-- +---------+-----+-------+------+
|
||||||
-- | Price | Red | Green | Blue |
|
-- | Price | Red | Green | Blue |
|
||||||
@ -166,7 +174,7 @@ columns :: Foldable g
|
|||||||
=> (b -> a -> c) -- ^ Cell content function
|
=> (b -> a -> c) -- ^ Cell content function
|
||||||
-> (b -> f c) -- ^ Header content function
|
-> (b -> f c) -- ^ Header content function
|
||||||
-> g b -- ^ Basis for column encodings
|
-> g b -- ^ Basis for column encodings
|
||||||
-> Colonnade f c a
|
-> Colonnade f a c
|
||||||
columns getCell getHeader = id
|
columns getCell getHeader = id
|
||||||
. Colonnade
|
. Colonnade
|
||||||
. Vector.map (\b -> OneColonnade (getHeader b) (getCell b))
|
. Vector.map (\b -> OneColonnade (getHeader b) (getCell b))
|
||||||
@ -178,7 +186,7 @@ bool ::
|
|||||||
-> (a -> Bool) -- ^ Predicate
|
-> (a -> Bool) -- ^ Predicate
|
||||||
-> (a -> c) -- ^ Contents when predicate is false
|
-> (a -> c) -- ^ Contents when predicate is false
|
||||||
-> (a -> c) -- ^ Contents when predicate is true
|
-> (a -> c) -- ^ Contents when predicate is true
|
||||||
-> Colonnade f c a
|
-> Colonnade f a c
|
||||||
bool h p onTrue onFalse = singleton h (Data.Bool.bool <$> onFalse <*> onTrue <*> p)
|
bool h p onTrue onFalse = singleton h (Data.Bool.bool <$> onFalse <*> onTrue <*> p)
|
||||||
|
|
||||||
-- | Modify the contents of cells in rows whose values satisfy the
|
-- | Modify the contents of cells in rows whose values satisfy the
|
||||||
@ -188,8 +196,8 @@ bool h p onTrue onFalse = singleton h (Data.Bool.bool <$> onFalse <*> onTrue <*>
|
|||||||
modifyWhen ::
|
modifyWhen ::
|
||||||
(c -> c) -- ^ Content change
|
(c -> c) -- ^ Content change
|
||||||
-> (a -> Bool) -- ^ Row predicate
|
-> (a -> Bool) -- ^ Row predicate
|
||||||
-> Colonnade f c a -- ^ Original 'Colonnade'
|
-> Colonnade f a c -- ^ Original 'Colonnade'
|
||||||
-> Colonnade f c a
|
-> Colonnade f a c
|
||||||
modifyWhen changeContent p (Colonnade v) = Colonnade
|
modifyWhen changeContent p (Colonnade v) = Colonnade
|
||||||
( Vector.map
|
( Vector.map
|
||||||
(\(OneColonnade h encode) -> OneColonnade h $ \a ->
|
(\(OneColonnade h encode) -> OneColonnade h $ \a ->
|
||||||
@ -202,8 +210,8 @@ modifyWhen changeContent p (Colonnade v) = Colonnade
|
|||||||
replaceWhen ::
|
replaceWhen ::
|
||||||
c -- ^ New content
|
c -- ^ New content
|
||||||
-> (a -> Bool) -- ^ Row predicate
|
-> (a -> Bool) -- ^ Row predicate
|
||||||
-> Colonnade f c a -- ^ Original 'Colonnade'
|
-> Colonnade f a c -- ^ Original 'Colonnade'
|
||||||
-> Colonnade f c a
|
-> Colonnade f a c
|
||||||
replaceWhen newContent p (Colonnade v) = Colonnade
|
replaceWhen newContent p (Colonnade v) = Colonnade
|
||||||
( Vector.map
|
( Vector.map
|
||||||
(\(OneColonnade h encode) -> OneColonnade h $ \a ->
|
(\(OneColonnade h encode) -> OneColonnade h $ \a ->
|
||||||
@ -211,69 +219,69 @@ replaceWhen newContent p (Colonnade v) = Colonnade
|
|||||||
) v
|
) v
|
||||||
)
|
)
|
||||||
|
|
||||||
-- | 'Colonnade' is covariant in its content type. Consequently, it can be
|
toCornice :: Colonnade Headed a c -> Cornice Base a c
|
||||||
-- mapped over. There is no standard typeclass for types that are covariant
|
toCornice = CorniceBase
|
||||||
-- in their second-to-last argument, so this function is provided for
|
|
||||||
-- situations that require this.
|
cap :: c -> Cornice p a c -> Cornice (Cap p) a c
|
||||||
mapContent :: Functor f => (c1 -> c2) -> Colonnade f c1 a -> Colonnade f c2 a
|
cap h cor = CorniceCap (V.singleton (OneCornice h cor))
|
||||||
mapContent f (Colonnade v) = Colonnade
|
|
||||||
$ Vector.map (\(OneColonnade h c) -> (OneColonnade (fmap f h) (f . c))) v
|
|
||||||
|
asciiMulti :: Foldable f
|
||||||
|
=> Cornice p a String -- ^ columnar encoding
|
||||||
|
-> f a -- ^ rows
|
||||||
|
-> String
|
||||||
|
asciiMulti cor xs =
|
||||||
|
let annCor = CE.annotateFinely (\x y -> x + y + 3) id
|
||||||
|
List.length xs cor
|
||||||
|
in CE.headersMonoidal (Right (\s -> s ++ "\n")) (\sz c -> rightPad sz ' ' c) annCor
|
||||||
|
|
||||||
|
|
||||||
-- | Render a collection of rows as an ascii table. The table\'s columns are
|
-- | Render a collection of rows as an ascii table. The table\'s columns are
|
||||||
-- specified by the given 'Colonnade'. This implementation is inefficient and
|
-- specified by the given 'Colonnade'. This implementation is inefficient and
|
||||||
-- does not provide any wrapping behavior. It is provided so that users can
|
-- does not provide any wrapping behavior. It is provided so that users can
|
||||||
-- try out @colonnade@ in ghci and so that @doctest@ can verify examples
|
-- try out @colonnade@ in ghci and so that @doctest@ can verify example
|
||||||
-- code in the haddocks.
|
-- code in the haddocks.
|
||||||
ascii :: Foldable f
|
ascii :: Foldable f
|
||||||
=> Colonnade Headed String a -- ^ columnar encoding
|
=> Colonnade Headed a String -- ^ columnar encoding
|
||||||
-> f a -- ^ rows
|
-> f a -- ^ rows
|
||||||
-> String
|
-> String
|
||||||
ascii enc xs =
|
ascii col xs =
|
||||||
let theHeader :: [(Int,String)]
|
let sizedCol = Encode.sizeColumns List.length xs col
|
||||||
theHeader = (zip (enumFrom 0) . map (\s -> " " ++ s ++ " ")) (toList (Encode.header id enc))
|
divider = concat
|
||||||
theBody :: [[(Int,String)]]
|
[ "+"
|
||||||
theBody = map (zip (enumFrom 0) . map (\s -> " " ++ s ++ " ") . toList . Encode.row id enc) (toList xs)
|
, Encode.headerMonoidalFull sizedCol
|
||||||
sizes :: [Int]
|
(\(Sized sz _) -> hyphens (sz + 2) ++ "+")
|
||||||
sizes = ($ replicate (length theHeader) 1) $ appEndo $ mconcat
|
, "\n"
|
||||||
[ foldMap (\(i,str) -> Endo (replaceAt i (length str))) theHeader
|
|
||||||
, (foldMap . foldMap) (\(i,str) -> Endo (replaceAt i (length str))) theBody
|
|
||||||
]
|
]
|
||||||
paddedHeader :: [String]
|
rowContents = foldMap
|
||||||
paddedHeader = map (\(i,str) -> rightPad (atDef 1 sizes i) ' ' str) theHeader
|
(\x -> concat
|
||||||
paddedBody :: [[String]]
|
[ "|"
|
||||||
paddedBody = (map . map) (\(i,str) -> rightPad (atDef 1 sizes i) ' ' str) theBody
|
, Encode.rowMonoidalHeader
|
||||||
divider :: String
|
sizedCol
|
||||||
divider = "+" ++ join (List.intersperse "+" (map (\i -> replicate i '-') sizes)) ++ "+"
|
(\(Sized sz _) c -> " " ++ rightPad sz ' ' c ++ " |")
|
||||||
headerStr :: String
|
x
|
||||||
headerStr = "|" ++ join (List.intersperse "|" paddedHeader) ++ "|"
|
, "\n"
|
||||||
bodyStr :: String
|
]
|
||||||
bodyStr = List.unlines (map ((\s -> "|" ++ s ++ "|") . join . List.intersperse "|") paddedBody)
|
) xs
|
||||||
in divider ++ "\n" ++ headerStr
|
in List.concat
|
||||||
++ "\n" ++ divider
|
[ divider
|
||||||
++ "\n" ++ bodyStr ++ divider ++ "\n"
|
, concat
|
||||||
|
[ "|"
|
||||||
|
, Encode.headerMonoidalFull sizedCol
|
||||||
-- this has no effect if the index is out of bounds
|
(\(Sized s (Headed h)) -> " " ++ rightPad s ' ' h ++ " |")
|
||||||
replaceAt :: Ord a => Int -> a -> [a] -> [a]
|
, "\n"
|
||||||
replaceAt _ _ [] = []
|
]
|
||||||
replaceAt n v (a:as) = if n > 0
|
, divider
|
||||||
then a : replaceAt (n - 1) v as
|
, rowContents
|
||||||
else (max v a) : as
|
, divider
|
||||||
|
]
|
||||||
|
|
||||||
|
hyphens :: Int -> String
|
||||||
|
hyphens n = List.replicate n '-'
|
||||||
|
|
||||||
rightPad :: Int -> a -> [a] -> [a]
|
rightPad :: Int -> a -> [a] -> [a]
|
||||||
rightPad m a xs = take m $ xs ++ repeat a
|
rightPad m a xs = take m $ xs ++ repeat a
|
||||||
|
|
||||||
atDef :: a -> [a] -> Int -> a
|
|
||||||
atDef def = Data.Maybe.fromMaybe def .^ atMay where
|
|
||||||
(.^) f g x1 x2 = f (g x1 x2)
|
|
||||||
atMay = eitherToMaybe .^ at_
|
|
||||||
eitherToMaybe = either (const Nothing) Just
|
|
||||||
at_ xs o | o < 0 = Left $ "index must not be negative, index=" ++ show o
|
|
||||||
| otherwise = f o xs
|
|
||||||
where f 0 (z:_) = Right z
|
|
||||||
f i (_:zs) = f (i-1) zs
|
|
||||||
f i [] = Left $ "index too large, index=" ++ show o ++ ", length=" ++ show (o-i)
|
|
||||||
|
|
||||||
-- data Company = Company String String Int
|
-- data Company = Company String String Int
|
||||||
--
|
--
|
||||||
-- data Company = Company
|
-- data Company = Company
|
||||||
|
|||||||
203
colonnade/src/Colonnade/Cornice/Encode.hs
Normal file
203
colonnade/src/Colonnade/Cornice/Encode.hs
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
{-# LANGUAGE RankNTypes #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE GADTs #-}
|
||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
|
||||||
|
{-# OPTIONS_GHC -Wall -fno-warn-unused-imports -fno-warn-unticked-promoted-constructors -Werror #-}
|
||||||
|
module Colonnade.Cornice.Encode
|
||||||
|
( annotate
|
||||||
|
, annotateFinely
|
||||||
|
, size
|
||||||
|
, endow
|
||||||
|
, discard
|
||||||
|
, headersMonoidal
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Colonnade.Internal
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import Control.Monad.ST (ST,runST)
|
||||||
|
import Data.Monoid
|
||||||
|
import qualified Data.Vector as V
|
||||||
|
import qualified Colonnade.Encode as E
|
||||||
|
|
||||||
|
discard :: Cornice p a c -> Colonnade Headed a c
|
||||||
|
discard = go where
|
||||||
|
go :: forall p a c. Cornice p a c -> Colonnade Headed a c
|
||||||
|
go (CorniceBase c) = c
|
||||||
|
go (CorniceCap children) = Colonnade (getColonnade . go . oneCorniceBody =<< children)
|
||||||
|
|
||||||
|
endow :: forall p a c. (c -> c -> c) -> Cornice p a c -> Colonnade Headed a c
|
||||||
|
endow f x = case x of
|
||||||
|
CorniceBase colonnade -> colonnade
|
||||||
|
CorniceCap v -> Colonnade (V.concatMap (\(OneCornice h b) -> go h b) v)
|
||||||
|
where
|
||||||
|
go :: forall p'. c -> Cornice p' a c -> Vector (OneColonnade Headed a c)
|
||||||
|
go c (CorniceBase (Colonnade v)) = V.map (mapOneColonnadeHeader (f c)) v
|
||||||
|
go c (CorniceCap v) = V.concatMap (\(OneCornice h b) -> go (f c h) b) v
|
||||||
|
|
||||||
|
annotate :: Cornice p a c -> AnnotatedCornice p a c
|
||||||
|
annotate = go where
|
||||||
|
go :: forall p a c. Cornice p a c -> AnnotatedCornice p a c
|
||||||
|
go (CorniceBase c) = AnnotatedCorniceBase (mapHeadedness (Sized 1) c)
|
||||||
|
go (CorniceCap children) =
|
||||||
|
let annChildren = fmap (mapOneCorniceBody go) children
|
||||||
|
in AnnotatedCorniceCap
|
||||||
|
( ( ( V.foldl' (combineJustInt (+))
|
||||||
|
) Nothing . V.map (size . oneCorniceBody)
|
||||||
|
) annChildren
|
||||||
|
)
|
||||||
|
annChildren
|
||||||
|
|
||||||
|
combineJustInt :: (Int -> Int -> Int) -> Maybe Int -> Maybe Int -> Maybe Int
|
||||||
|
combineJustInt f acc el = case acc of
|
||||||
|
Nothing -> case el of
|
||||||
|
Nothing -> Nothing
|
||||||
|
Just i -> Just i
|
||||||
|
Just i -> case el of
|
||||||
|
Nothing -> Just i
|
||||||
|
Just j -> Just (f i j)
|
||||||
|
|
||||||
|
mapJustInt :: (Int -> Int) -> Maybe Int -> Maybe Int
|
||||||
|
mapJustInt _ Nothing = Nothing
|
||||||
|
mapJustInt f (Just i) = Just (f i)
|
||||||
|
|
||||||
|
annotateFinely :: Foldable f
|
||||||
|
=> (Int -> Int -> Int) -- ^ fold function
|
||||||
|
-> (Int -> Int) -- ^ finalize
|
||||||
|
-> (c -> Int) -- ^ Get size from content
|
||||||
|
-> f a
|
||||||
|
-> Cornice p a c
|
||||||
|
-> AnnotatedCornice p a c
|
||||||
|
annotateFinely g finish toSize xs cornice = runST $ do
|
||||||
|
m <- newMutableSizedCornice cornice
|
||||||
|
sizeColonnades toSize xs m
|
||||||
|
freezeMutableSizedCornice g finish m
|
||||||
|
|
||||||
|
sizeColonnades :: forall f s p a c.
|
||||||
|
Foldable f
|
||||||
|
=> (c -> Int) -- ^ Get size from content
|
||||||
|
-> f a
|
||||||
|
-> MutableSizedCornice s p a c
|
||||||
|
-> ST s ()
|
||||||
|
sizeColonnades toSize xs cornice = do
|
||||||
|
goHeader cornice
|
||||||
|
mapM_ (goRow cornice) xs
|
||||||
|
where
|
||||||
|
goRow :: forall p'. MutableSizedCornice s p' a c -> a -> ST s ()
|
||||||
|
goRow (MutableSizedCorniceBase c) a = E.rowUpdateSize toSize c a
|
||||||
|
goRow (MutableSizedCorniceCap children) a = mapM_ (flip goRow a . oneCorniceBody) children
|
||||||
|
goHeader :: forall p'. MutableSizedCornice s p' a c -> ST s ()
|
||||||
|
goHeader (MutableSizedCorniceBase c) = E.headerUpdateSize toSize c
|
||||||
|
goHeader (MutableSizedCorniceCap children) = mapM_ (goHeader . oneCorniceBody) children
|
||||||
|
|
||||||
|
freezeMutableSizedCornice :: forall s p a c.
|
||||||
|
(Int -> Int -> Int) -- ^ fold function
|
||||||
|
-> (Int -> Int) -- ^ finalize
|
||||||
|
-> MutableSizedCornice s p a c
|
||||||
|
-> ST s (AnnotatedCornice p a c)
|
||||||
|
freezeMutableSizedCornice step finish = go
|
||||||
|
where
|
||||||
|
go :: forall p' a' c'. MutableSizedCornice s p' a' c' -> ST s (AnnotatedCornice p' a' c')
|
||||||
|
go (MutableSizedCorniceBase msc) =
|
||||||
|
fmap AnnotatedCorniceBase (E.freezeMutableSizedColonnade msc)
|
||||||
|
go (MutableSizedCorniceCap v1) = do
|
||||||
|
v2 <- V.mapM (traverseOneCorniceBody go) v1
|
||||||
|
let sz = (mapJustInt finish . V.foldl' (combineJustInt step) Nothing . V.map (size . oneCorniceBody)) v2
|
||||||
|
return $ AnnotatedCorniceCap sz v2
|
||||||
|
|
||||||
|
newMutableSizedCornice :: forall s p a c.
|
||||||
|
Cornice p a c
|
||||||
|
-> ST s (MutableSizedCornice s p a c)
|
||||||
|
newMutableSizedCornice = go where
|
||||||
|
go :: forall p'. Cornice p' a c -> ST s (MutableSizedCornice s p' a c)
|
||||||
|
go (CorniceBase c) = fmap MutableSizedCorniceBase (E.newMutableSizedColonnade c)
|
||||||
|
go (CorniceCap v) = fmap MutableSizedCorniceCap (V.mapM (traverseOneCorniceBody go) v)
|
||||||
|
|
||||||
|
traverseOneCorniceBody :: Monad m => (k p a c -> m (j p a c)) -> OneCornice k p a c -> m (OneCornice j p a c)
|
||||||
|
traverseOneCorniceBody f (OneCornice h b) = fmap (OneCornice h) (f b)
|
||||||
|
|
||||||
|
mapHeadedness :: (forall x. h x -> h' x) -> Colonnade h a c -> Colonnade h' a c
|
||||||
|
mapHeadedness f (Colonnade v) =
|
||||||
|
Colonnade (V.map (\(OneColonnade h c) -> OneColonnade (f h) c) v)
|
||||||
|
|
||||||
|
-- annotate ::
|
||||||
|
-- Int -- ^ initial
|
||||||
|
-- -> (Int -> Int -> Int) -- ^ fold function
|
||||||
|
-- -> (Int -> Int) -- ^ finalize
|
||||||
|
-- -> Cornice p a c
|
||||||
|
-- -> AnnotatedCornice p a c
|
||||||
|
-- annotate i0 g finish = go where
|
||||||
|
-- go :: forall p a c. Cornice p a c -> AnnotatedCornice p a c
|
||||||
|
-- go (CorniceBase c) = AnnotatedCorniceBase c
|
||||||
|
-- go (CorniceCap children) =
|
||||||
|
-- let annChildren = fmap (mapOneCorniceBody go) children
|
||||||
|
-- in AnnotatedCorniceCap ((finish . V.foldl' g i0 . V.map (size . oneCorniceBody)) annChildren) annChildren
|
||||||
|
|
||||||
|
-- | This is an O(1) operation, sort of
|
||||||
|
size :: AnnotatedCornice p a c -> Maybe Int
|
||||||
|
size x = case x of
|
||||||
|
AnnotatedCorniceBase (Colonnade v) -> if V.length v > 0
|
||||||
|
then Just ((V.sum . V.map (sizedSize . oneColonnadeHead)) v)
|
||||||
|
else Nothing
|
||||||
|
AnnotatedCorniceCap sz _ -> sz
|
||||||
|
|
||||||
|
mapOneCorniceBody :: (forall p' a' c'. k p' a' c' -> j p' a' c') -> OneCornice k p a c -> OneCornice j p a c
|
||||||
|
mapOneCorniceBody f (OneCornice h b) = OneCornice h (f b)
|
||||||
|
|
||||||
|
mapOneColonnadeHeader :: Functor h => (c -> c) -> OneColonnade h a c -> OneColonnade h a c
|
||||||
|
mapOneColonnadeHeader f (OneColonnade h b) = OneColonnade (fmap f h) b
|
||||||
|
|
||||||
|
headersMonoidal :: forall r m c p a.
|
||||||
|
Monoid m
|
||||||
|
=> Either (Fascia p r, r -> m -> m) (m -> m) -- ^ Apply the Fascia header row content
|
||||||
|
-> (Int -> c -> m) -- ^ Build content from cell content and size
|
||||||
|
-> AnnotatedCornice p a c
|
||||||
|
-> m
|
||||||
|
headersMonoidal wrapRow fromContent = go wrapRow
|
||||||
|
where
|
||||||
|
go :: forall p'. Either (Fascia p' r, r -> m -> m) (m -> m) -> AnnotatedCornice p' a c -> m
|
||||||
|
go ef (AnnotatedCorniceBase (Colonnade v)) =
|
||||||
|
let g :: m -> m
|
||||||
|
g m = case ef of
|
||||||
|
Right f -> f m
|
||||||
|
Left (FasciaBase r, f) -> f r m
|
||||||
|
in foldMap (\(OneColonnade (Sized sz (Headed h)) _) ->
|
||||||
|
g (fromContent sz h)) v
|
||||||
|
go ef (AnnotatedCorniceCap _ v) =
|
||||||
|
let g :: m -> m
|
||||||
|
g m = case ef of
|
||||||
|
Right f -> f m
|
||||||
|
Left (FasciaCap r _, f) -> f r m
|
||||||
|
in foldMap (\(OneCornice h b) ->
|
||||||
|
(case size b of
|
||||||
|
Nothing -> mempty
|
||||||
|
Just sz -> g (fromContent sz h))
|
||||||
|
) v
|
||||||
|
<> case ef of
|
||||||
|
Right f -> case flattenAnnotated v of
|
||||||
|
Nothing -> mempty
|
||||||
|
Just annCoreNext -> go (Right f) annCoreNext
|
||||||
|
Left (FasciaCap _ fn, f) -> case flattenAnnotated v of
|
||||||
|
Nothing -> mempty
|
||||||
|
Just annCoreNext -> go (Left (fn,f)) annCoreNext
|
||||||
|
|
||||||
|
flattenAnnotated :: Vector (OneCornice AnnotatedCornice p a c) -> Maybe (AnnotatedCornice p a c)
|
||||||
|
flattenAnnotated v = case v V.!? 0 of
|
||||||
|
Nothing -> Nothing
|
||||||
|
Just (OneCornice _ x) -> Just $ case x of
|
||||||
|
AnnotatedCorniceBase _ -> flattenAnnotatedBase v
|
||||||
|
AnnotatedCorniceCap m _ -> flattenAnnotatedCap m v
|
||||||
|
|
||||||
|
flattenAnnotatedBase :: Vector (OneCornice AnnotatedCornice Base a c) -> AnnotatedCornice Base a c
|
||||||
|
flattenAnnotatedBase = AnnotatedCorniceBase
|
||||||
|
. Colonnade
|
||||||
|
. V.concatMap
|
||||||
|
(\(OneCornice _ (AnnotatedCorniceBase (Colonnade v))) -> v)
|
||||||
|
|
||||||
|
flattenAnnotatedCap :: Maybe Int -> Vector (OneCornice AnnotatedCornice (Cap p) a c) -> AnnotatedCornice (Cap p) a c
|
||||||
|
flattenAnnotatedCap m = AnnotatedCorniceCap m . V.concatMap getTheVector
|
||||||
|
|
||||||
|
getTheVector :: OneCornice AnnotatedCornice (Cap p) a c -> Vector (OneCornice AnnotatedCornice p a c)
|
||||||
|
getTheVector (OneCornice _ (AnnotatedCorniceCap _ v)) = v
|
||||||
|
|
||||||
|
|
||||||
@ -30,30 +30,43 @@ module Colonnade.Encode
|
|||||||
, rowMonadic_
|
, rowMonadic_
|
||||||
, rowMonadicWith
|
, rowMonadicWith
|
||||||
, rowMonoidal
|
, rowMonoidal
|
||||||
|
, rowMonoidalHeader
|
||||||
, header
|
, header
|
||||||
, headerMonadic
|
, headerMonadic
|
||||||
, headerMonadic_
|
, headerMonadic_
|
||||||
, headerMonadicGeneral
|
, headerMonadicGeneral
|
||||||
, headerMonadicGeneral_
|
, headerMonadicGeneral_
|
||||||
, headerMonoidalGeneral
|
, headerMonoidalGeneral
|
||||||
|
, headerMonoidalFull
|
||||||
, bothMonadic_
|
, bothMonadic_
|
||||||
|
, freezeMutableSizedColonnade
|
||||||
|
, newMutableSizedColonnade
|
||||||
|
, rowUpdateSize
|
||||||
|
, headerUpdateSize
|
||||||
|
, sizeColumns
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Colonnade.Internal
|
import Colonnade.Internal
|
||||||
import Data.Vector (Vector)
|
import Data.Vector (Vector)
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
|
import Control.Monad.ST (ST,runST)
|
||||||
|
import Data.Monoid
|
||||||
import qualified Data.Vector as Vector
|
import qualified Data.Vector as Vector
|
||||||
|
import qualified Data.Vector.Unboxed.Mutable as MVU
|
||||||
|
import qualified Data.Vector.Unboxed as VU
|
||||||
|
import qualified Data.Vector as V
|
||||||
|
import qualified Data.Vector.Generic as GV
|
||||||
|
|
||||||
-- | Consider providing a variant the produces a list
|
-- | Consider providing a variant the produces a list
|
||||||
-- instead. It may allow more things to get inlined
|
-- instead. It may allow more things to get inlined
|
||||||
-- in to a loop.
|
-- in to a loop.
|
||||||
row :: (c1 -> c2) -> Colonnade f c1 a -> a -> Vector c2
|
row :: (c1 -> c2) -> Colonnade f a c1 -> a -> Vector c2
|
||||||
row g (Colonnade v) a = flip Vector.map v $
|
row g (Colonnade v) a = flip Vector.map v $
|
||||||
\(OneColonnade _ encode) -> g (encode a)
|
\(OneColonnade _ encode) -> g (encode a)
|
||||||
|
|
||||||
bothMonadic_ :: Monad m
|
bothMonadic_ :: Monad m
|
||||||
=> Colonnade Headed content a
|
=> Colonnade Headed a c
|
||||||
-> (content -> content -> m b)
|
-> (c -> c -> m b)
|
||||||
-> a
|
-> a
|
||||||
-> m ()
|
-> m ()
|
||||||
bothMonadic_ (Colonnade v) g a =
|
bothMonadic_ (Colonnade v) g a =
|
||||||
@ -61,8 +74,8 @@ bothMonadic_ (Colonnade v) g a =
|
|||||||
|
|
||||||
rowMonadic ::
|
rowMonadic ::
|
||||||
(Monad m, Monoid b)
|
(Monad m, Monoid b)
|
||||||
=> Colonnade f content a
|
=> Colonnade f a c
|
||||||
-> (content -> m b)
|
-> (c -> m b)
|
||||||
-> a
|
-> a
|
||||||
-> m b
|
-> m b
|
||||||
rowMonadic (Colonnade v) g a =
|
rowMonadic (Colonnade v) g a =
|
||||||
@ -71,8 +84,8 @@ rowMonadic (Colonnade v) g a =
|
|||||||
|
|
||||||
rowMonadic_ ::
|
rowMonadic_ ::
|
||||||
Monad m
|
Monad m
|
||||||
=> Colonnade f content a
|
=> Colonnade f a c
|
||||||
-> (content -> m b)
|
-> (c -> m b)
|
||||||
-> a
|
-> a
|
||||||
-> m ()
|
-> m ()
|
||||||
rowMonadic_ (Colonnade v) g a =
|
rowMonadic_ (Colonnade v) g a =
|
||||||
@ -80,19 +93,75 @@ rowMonadic_ (Colonnade v) g a =
|
|||||||
|
|
||||||
rowMonoidal ::
|
rowMonoidal ::
|
||||||
Monoid m
|
Monoid m
|
||||||
=> Colonnade h c a
|
=> Colonnade h a c
|
||||||
-> (c -> m)
|
-> (c -> m)
|
||||||
-> a
|
-> a
|
||||||
-> m
|
-> m
|
||||||
rowMonoidal (Colonnade v) g a =
|
rowMonoidal (Colonnade v) g a =
|
||||||
foldMap (\e -> g (oneColonnadeEncode e a)) v
|
foldMap (\(OneColonnade h encode) -> g (encode a)) v
|
||||||
|
|
||||||
|
rowMonoidalHeader ::
|
||||||
|
Monoid m
|
||||||
|
=> Colonnade h a c
|
||||||
|
-> (h c -> c -> m)
|
||||||
|
-> a
|
||||||
|
-> m
|
||||||
|
rowMonoidalHeader (Colonnade v) g a =
|
||||||
|
foldMap (\(OneColonnade h encode) -> g h (encode a)) v
|
||||||
|
|
||||||
|
rowUpdateSize ::
|
||||||
|
(c -> Int) -- ^ Get size from content
|
||||||
|
-> MutableSizedColonnade s h a c
|
||||||
|
-> a
|
||||||
|
-> ST s ()
|
||||||
|
rowUpdateSize toSize (MutableSizedColonnade v mv) a = if MVU.length mv /= V.length v
|
||||||
|
then error "rowMonoidalSize: vector sizes mismatched"
|
||||||
|
else V.imapM_ (\ix (OneColonnade _ encode) ->
|
||||||
|
MVU.modify mv (\oldSize -> max oldSize (toSize (encode a))) ix
|
||||||
|
) v
|
||||||
|
|
||||||
|
headerUpdateSize :: Foldable h
|
||||||
|
=> (c -> Int) -- ^ Get size from content
|
||||||
|
-> MutableSizedColonnade s h a c
|
||||||
|
-> ST s ()
|
||||||
|
headerUpdateSize toSize (MutableSizedColonnade v mv) = if MVU.length mv /= V.length v
|
||||||
|
then error "rowMonoidalSize: vector sizes mismatched"
|
||||||
|
else V.imapM_ (\ix (OneColonnade h _) ->
|
||||||
|
MVU.modify mv (\oldSize -> max oldSize (foldl' (\sz c -> max sz (toSize c)) 0 h)) ix
|
||||||
|
) v
|
||||||
|
|
||||||
|
sizeColumns :: (Foldable f, Foldable h)
|
||||||
|
=> (c -> Int) -- ^ Get size from content
|
||||||
|
-> f a
|
||||||
|
-> Colonnade h a c
|
||||||
|
-> Colonnade (Sized h) a c
|
||||||
|
sizeColumns toSize rows colonnade = runST $ do
|
||||||
|
mcol <- newMutableSizedColonnade colonnade
|
||||||
|
headerUpdateSize toSize mcol
|
||||||
|
mapM_ (rowUpdateSize toSize mcol) rows
|
||||||
|
freezeMutableSizedColonnade mcol
|
||||||
|
|
||||||
|
newMutableSizedColonnade :: Colonnade h a c -> ST s (MutableSizedColonnade s h a c)
|
||||||
|
newMutableSizedColonnade (Colonnade v) = do
|
||||||
|
mv <- MVU.replicate (V.length v) 0
|
||||||
|
return (MutableSizedColonnade v mv)
|
||||||
|
|
||||||
|
freezeMutableSizedColonnade :: MutableSizedColonnade s h a c -> ST s (Colonnade (Sized h) a c)
|
||||||
|
freezeMutableSizedColonnade (MutableSizedColonnade v mv) =
|
||||||
|
if MVU.length mv /= V.length v
|
||||||
|
then error "rowMonoidalSize: vector sizes mismatched"
|
||||||
|
else do
|
||||||
|
sizeVec <- VU.freeze mv
|
||||||
|
return $ Colonnade
|
||||||
|
$ V.map (\(OneColonnade h enc,sz) -> OneColonnade (Sized sz h) enc)
|
||||||
|
$ V.zip v (GV.convert sizeVec)
|
||||||
|
|
||||||
rowMonadicWith ::
|
rowMonadicWith ::
|
||||||
(Monad m)
|
(Monad m)
|
||||||
=> b
|
=> b
|
||||||
-> (b -> b -> b)
|
-> (b -> b -> b)
|
||||||
-> Colonnade f content a
|
-> Colonnade f a c
|
||||||
-> (content -> m b)
|
-> (c -> m b)
|
||||||
-> a
|
-> a
|
||||||
-> m b
|
-> m b
|
||||||
rowMonadicWith bempty bappend (Colonnade v) g a =
|
rowMonadicWith bempty bappend (Colonnade v) g a =
|
||||||
@ -101,15 +170,15 @@ rowMonadicWith bempty bappend (Colonnade v) g a =
|
|||||||
return (bappend bl br)
|
return (bappend bl br)
|
||||||
) bempty v
|
) bempty v
|
||||||
|
|
||||||
header :: (c1 -> c2) -> Colonnade Headed c1 a -> Vector c2
|
header :: (c1 -> c2) -> Colonnade Headed a c1 -> Vector c2
|
||||||
header g (Colonnade v) =
|
header g (Colonnade v) =
|
||||||
Vector.map (g . getHeaded . oneColonnadeHead) v
|
Vector.map (g . getHeaded . oneColonnadeHead) v
|
||||||
|
|
||||||
-- | This function is a helper for abusing 'Foldable' to optionally
|
-- | This function is a helper for abusing 'Foldable' to optionally
|
||||||
-- render a header. Its future is uncertain.
|
-- render a header. Its future is uncertain.
|
||||||
headerMonadicGeneral :: (Monad m, Monoid b, Foldable h)
|
headerMonadicGeneral :: (Monad m, Monoid b, Foldable h)
|
||||||
=> Colonnade h content a
|
=> Colonnade h a c
|
||||||
-> (content -> m b)
|
-> (c -> m b)
|
||||||
-> m b
|
-> m b
|
||||||
headerMonadicGeneral (Colonnade v) g = id
|
headerMonadicGeneral (Colonnade v) g = id
|
||||||
$ fmap (mconcat . Vector.toList)
|
$ fmap (mconcat . Vector.toList)
|
||||||
@ -117,36 +186,43 @@ headerMonadicGeneral (Colonnade v) g = id
|
|||||||
|
|
||||||
headerMonadic ::
|
headerMonadic ::
|
||||||
(Monad m, Monoid b)
|
(Monad m, Monoid b)
|
||||||
=> Colonnade Headed content a
|
=> Colonnade Headed a c
|
||||||
-> (content -> m b)
|
-> (c -> m b)
|
||||||
-> m b
|
-> m b
|
||||||
headerMonadic (Colonnade v) g =
|
headerMonadic (Colonnade v) g =
|
||||||
fmap (mconcat . Vector.toList) $ Vector.mapM (g . getHeaded . oneColonnadeHead) v
|
fmap (mconcat . Vector.toList) $ Vector.mapM (g . getHeaded . oneColonnadeHead) v
|
||||||
|
|
||||||
headerMonadicGeneral_ ::
|
headerMonadicGeneral_ ::
|
||||||
(Monad m, Foldable h)
|
(Monad m, Foldable h)
|
||||||
=> Colonnade h content a
|
=> Colonnade h a c
|
||||||
-> (content -> m b)
|
-> (c -> m b)
|
||||||
-> m ()
|
-> m ()
|
||||||
headerMonadicGeneral_ (Colonnade v) g =
|
headerMonadicGeneral_ (Colonnade v) g =
|
||||||
Vector.mapM_ (mapM_ g . oneColonnadeHead) v
|
Vector.mapM_ (mapM_ g . oneColonnadeHead) v
|
||||||
|
|
||||||
headerMonoidalGeneral ::
|
headerMonoidalGeneral ::
|
||||||
(Monoid m, Foldable h)
|
(Monoid m, Foldable h)
|
||||||
=> Colonnade h c a
|
=> Colonnade h a c
|
||||||
-> (c -> m)
|
-> (c -> m)
|
||||||
-> m
|
-> m
|
||||||
headerMonoidalGeneral (Colonnade v) g =
|
headerMonoidalGeneral (Colonnade v) g =
|
||||||
foldMap (foldMap g . oneColonnadeHead) v
|
foldMap (foldMap g . oneColonnadeHead) v
|
||||||
|
|
||||||
|
headerMonoidalFull ::
|
||||||
|
Monoid m
|
||||||
|
=> Colonnade h a c
|
||||||
|
-> (h c -> m)
|
||||||
|
-> m
|
||||||
|
headerMonoidalFull (Colonnade v) g = foldMap (g . oneColonnadeHead) v
|
||||||
|
|
||||||
headerMonadic_ ::
|
headerMonadic_ ::
|
||||||
(Monad m)
|
(Monad m)
|
||||||
=> Colonnade Headed content a
|
=> Colonnade Headed a c
|
||||||
-> (content -> m b)
|
-> (c -> m b)
|
||||||
-> m ()
|
-> m ()
|
||||||
headerMonadic_ (Colonnade v) g = Vector.mapM_ (g . getHeaded . oneColonnadeHead) v
|
headerMonadic_ (Colonnade v) g = Vector.mapM_ (g . getHeaded . oneColonnadeHead) v
|
||||||
|
|
||||||
foldlMapM :: (Foldable t, Monoid b, Monad m) => (a -> m b) -> t a -> m b
|
foldlMapM :: (Foldable t, Monoid b, Monad m) => (a -> m b) -> t a -> m b
|
||||||
foldlMapM f = foldlM (\b a -> fmap (mappend b) (f a)) mempty
|
foldlMapM f = foldlM (\b a -> fmap (mappend b) (f a)) mempty
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,30 @@
|
|||||||
{-# LANGUAGE DeriveFunctor #-}
|
{-# LANGUAGE DeriveFunctor #-}
|
||||||
{-# LANGUAGE DeriveFoldable #-}
|
{-# LANGUAGE DeriveFoldable #-}
|
||||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
{-# LANGUAGE KindSignatures #-}
|
||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE GADTs #-}
|
||||||
|
|
||||||
{-# OPTIONS_HADDOCK not-home #-}
|
{-# OPTIONS_HADDOCK not-home #-}
|
||||||
|
{-# OPTIONS_GHC -Wall -fno-warn-unused-imports -fno-warn-unticked-promoted-constructors -Werror #-}
|
||||||
|
|
||||||
module Colonnade.Internal
|
module Colonnade.Internal
|
||||||
( Colonnade(..)
|
( -- * Colonnade
|
||||||
|
Colonnade(..)
|
||||||
, OneColonnade(..)
|
, OneColonnade(..)
|
||||||
, Headed(..)
|
, Headed(..)
|
||||||
, Headless(..)
|
, Headless(..)
|
||||||
|
-- * Cornice
|
||||||
|
, Cornice(..)
|
||||||
|
, AnnotatedCornice(..)
|
||||||
|
, OneCornice(..)
|
||||||
|
, Pillar(..)
|
||||||
|
, ToEmptyCornice(..)
|
||||||
|
, Fascia(..)
|
||||||
|
-- * Sizing
|
||||||
|
, Sized(..)
|
||||||
|
, MutableSizedColonnade(..)
|
||||||
|
, MutableSizedCornice(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Vector (Vector)
|
import Data.Vector (Vector)
|
||||||
@ -17,14 +33,20 @@ import Data.Functor.Contravariant.Divisible (Divisible(..))
|
|||||||
import Control.Exception (Exception)
|
import Control.Exception (Exception)
|
||||||
import Data.Typeable (Typeable)
|
import Data.Typeable (Typeable)
|
||||||
import Data.Profunctor (Profunctor(..))
|
import Data.Profunctor (Profunctor(..))
|
||||||
|
import Data.Semigroup (Semigroup)
|
||||||
|
import Data.List.NonEmpty (NonEmpty((:|)))
|
||||||
|
import Data.Foldable (toList)
|
||||||
|
import qualified Data.Vector.Unboxed.Mutable as MVU
|
||||||
|
import qualified Data.Semigroup as Semigroup
|
||||||
import qualified Data.Vector as Vector
|
import qualified Data.Vector as Vector
|
||||||
|
import qualified Data.Vector.Generic as VG
|
||||||
|
|
||||||
-- | As the first argument to the 'Colonnade' type
|
-- | As the first argument to the 'Colonnade' type
|
||||||
-- constructor, this indictates that the columnar encoding has
|
-- constructor, this indictates that the columnar encoding has
|
||||||
-- a header. This type is isomorphic to 'Identity' but is
|
-- a header. This type is isomorphic to 'Identity' but is
|
||||||
-- given a new name to clarify its intent:
|
-- given a new name to clarify its intent:
|
||||||
--
|
--
|
||||||
-- > example :: Colonnade Headed Text Foo
|
-- > example :: Colonnade Headed Foo Text
|
||||||
--
|
--
|
||||||
-- The term @example@ represents a columnar encoding of @Foo@
|
-- The term @example@ represents a columnar encoding of @Foo@
|
||||||
-- in which the columns have headings.
|
-- in which the columns have headings.
|
||||||
@ -36,13 +58,18 @@ newtype Headed a = Headed { getHeaded :: a }
|
|||||||
-- a header. This type is isomorphic to 'Proxy' but is
|
-- a header. This type is isomorphic to 'Proxy' but is
|
||||||
-- given a new name to clarify its intent:
|
-- given a new name to clarify its intent:
|
||||||
--
|
--
|
||||||
-- > example :: Colonnade Headless Text Foo
|
-- > example :: Colonnade Headless Foo Text
|
||||||
--
|
--
|
||||||
-- The term @example@ represents a columnar encoding of @Foo@
|
-- The term @example@ represents a columnar encoding of @Foo@
|
||||||
-- in which the columns do not have headings.
|
-- in which the columns do not have headings.
|
||||||
data Headless a = Headless
|
data Headless a = Headless
|
||||||
deriving (Eq,Ord,Functor,Show,Read,Foldable)
|
deriving (Eq,Ord,Functor,Show,Read,Foldable)
|
||||||
|
|
||||||
|
data Sized f a = Sized
|
||||||
|
{ sizedSize :: {-# UNPACK #-} !Int
|
||||||
|
, sizedContent :: !(f a)
|
||||||
|
} deriving (Functor, Foldable)
|
||||||
|
|
||||||
instance Contravariant Headless where
|
instance Contravariant Headless where
|
||||||
contramap _ Headless = Headless
|
contramap _ Headless = Headless
|
||||||
|
|
||||||
@ -66,13 +93,13 @@ instance Functor h => Profunctor (OneColonnade h) where
|
|||||||
-- that represent HTML with element attributes are provided that serve
|
-- that represent HTML with element attributes are provided that serve
|
||||||
-- as the content type. Presented more visually:
|
-- as the content type. Presented more visually:
|
||||||
--
|
--
|
||||||
-- > +---- Content (Text, ByteString, Html, etc.)
|
-- > +---- Value consumed to build a row
|
||||||
-- > |
|
-- > |
|
||||||
-- > v
|
-- > v
|
||||||
-- > Colonnade h c a
|
-- > Colonnade h a c
|
||||||
-- > ^ ^
|
-- > ^ ^
|
||||||
-- > | |
|
-- > | |
|
||||||
-- > | +-- Value consumed to build a row
|
-- > | +-- Content (Text, ByteString, Html, etc.)
|
||||||
-- > |
|
-- > |
|
||||||
-- > +------ Headedness (Headed or Headless)
|
-- > +------ Headedness (Headed or Headless)
|
||||||
--
|
--
|
||||||
@ -89,6 +116,82 @@ newtype Colonnade h a c = Colonnade
|
|||||||
|
|
||||||
instance Functor h => Profunctor (Colonnade h) where
|
instance Functor h => Profunctor (Colonnade h) where
|
||||||
rmap = fmap
|
rmap = fmap
|
||||||
lmap f (Colonnade v) = Colonnade
|
lmap f (Colonnade v) = Colonnade (Vector.map (lmap f) v)
|
||||||
(Vector.map (lmap f) v)
|
|
||||||
|
instance Semigroup (Colonnade h a c) where
|
||||||
|
Colonnade a <> Colonnade b = Colonnade (a Vector.++ b)
|
||||||
|
sconcat xs = Colonnade (vectorConcatNE (fmap getColonnade xs))
|
||||||
|
|
||||||
|
data MutableSizedColonnade s h a c = MutableSizedColonnade
|
||||||
|
{ mutableSizedColonnadeColumns :: {-# UNPACK #-} !(Vector (OneColonnade h a c))
|
||||||
|
, mutableSizedColonnadeSizes :: {-# UNPACK #-} !(MVU.STVector s Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Isomorphic to the natural numbers. Only the promoted version of
|
||||||
|
-- this type is used.
|
||||||
|
data Pillar = Cap !Pillar | Base
|
||||||
|
|
||||||
|
class ToEmptyCornice (p :: Pillar) where
|
||||||
|
toEmptyCornice :: Cornice p a c
|
||||||
|
|
||||||
|
instance ToEmptyCornice Base where
|
||||||
|
toEmptyCornice = CorniceBase mempty
|
||||||
|
|
||||||
|
instance ToEmptyCornice (Cap p) where
|
||||||
|
toEmptyCornice = CorniceCap Vector.empty
|
||||||
|
|
||||||
|
data Fascia (p :: Pillar) r where
|
||||||
|
FasciaBase :: !r -> Fascia Base r
|
||||||
|
FasciaCap :: !r -> Fascia p r -> Fascia (Cap p) r
|
||||||
|
|
||||||
|
data OneCornice k (p :: Pillar) a c = OneCornice
|
||||||
|
{ oneCorniceHead :: !c
|
||||||
|
, oneCorniceBody :: !(k p a c)
|
||||||
|
}
|
||||||
|
|
||||||
|
data Cornice (p :: Pillar) a c where
|
||||||
|
CorniceBase :: !(Colonnade Headed a c) -> Cornice Base a c
|
||||||
|
CorniceCap :: {-# UNPACK #-} !(Vector (OneCornice Cornice p a c)) -> Cornice (Cap p) a c
|
||||||
|
|
||||||
|
instance Semigroup (Cornice p a c) where
|
||||||
|
CorniceBase a <> CorniceBase b = CorniceBase (mappend a b)
|
||||||
|
CorniceCap a <> CorniceCap b = CorniceCap (a Vector.++ b)
|
||||||
|
sconcat xs@(x :| _) = case x of
|
||||||
|
CorniceBase _ -> CorniceBase (Colonnade (vectorConcatNE (fmap (getColonnade . getCorniceBase) xs)))
|
||||||
|
CorniceCap _ -> CorniceCap (vectorConcatNE (fmap getCorniceCap xs))
|
||||||
|
|
||||||
|
instance ToEmptyCornice p => Monoid (Cornice p a c) where
|
||||||
|
mempty = toEmptyCornice
|
||||||
|
mappend = (Semigroup.<>)
|
||||||
|
mconcat xs1 = case xs1 of
|
||||||
|
[] -> toEmptyCornice
|
||||||
|
x : xs2 -> Semigroup.sconcat (x :| xs2)
|
||||||
|
|
||||||
|
getCorniceBase :: Cornice Base a c -> Colonnade Headed a c
|
||||||
|
getCorniceBase (CorniceBase c) = c
|
||||||
|
|
||||||
|
getCorniceCap :: Cornice (Cap p) a c -> Vector (OneCornice Cornice p a c)
|
||||||
|
getCorniceCap (CorniceCap c) = c
|
||||||
|
|
||||||
|
data AnnotatedCornice (p :: Pillar) a c where
|
||||||
|
AnnotatedCorniceBase :: !(Colonnade (Sized Headed) a c) -> AnnotatedCornice Base a c
|
||||||
|
AnnotatedCorniceCap ::
|
||||||
|
!(Maybe Int)
|
||||||
|
-> {-# UNPACK #-} !(Vector (OneCornice AnnotatedCornice p a c))
|
||||||
|
-> AnnotatedCornice (Cap p) a c
|
||||||
|
|
||||||
|
data MutableSizedCornice s (p :: Pillar) a c where
|
||||||
|
MutableSizedCorniceBase ::
|
||||||
|
{-# UNPACK #-} !(MutableSizedColonnade s Headed a c)
|
||||||
|
-> MutableSizedCornice s Base a c
|
||||||
|
MutableSizedCorniceCap ::
|
||||||
|
{-# UNPACK #-} !(Vector (OneCornice (MutableSizedCornice s) p a c))
|
||||||
|
-> MutableSizedCornice s (Cap p) a c
|
||||||
|
|
||||||
|
-- data MaybeInt = JustInt {-# UNPACK #-} !Int | NothingInt
|
||||||
|
|
||||||
|
-- | This is provided with vector-0.12, but we include a copy here
|
||||||
|
-- for compatibility.
|
||||||
|
vectorConcatNE :: NonEmpty (Vector a) -> Vector a
|
||||||
|
vectorConcatNE = Vector.concat . toList
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
# resolver:
|
# resolver:
|
||||||
# name: custom-snapshot
|
# name: custom-snapshot
|
||||||
# location: "./custom-snapshot.yaml"
|
# location: "./custom-snapshot.yaml"
|
||||||
resolver: lts-7.18
|
resolver: lts-8.0
|
||||||
|
|
||||||
# User packages to be built.
|
# User packages to be built.
|
||||||
# Various formats can be used as shown in the example below.
|
# Various formats can be used as shown in the example below.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user