{-# LANGUAGE RankNTypes #-}
module Text.Layout.Table.Primitives.ColumnModifier where

import Control.Arrow ((&&&))
import Data.List
import Data.Semigroup (Max(..))

import Text.Layout.Table.Cell
import Text.Layout.Table.Primitives.AlignInfo
import Text.Layout.Table.Spec.AlignSpec
import Text.Layout.Table.Spec.ColSpec
import Text.Layout.Table.Spec.CutMark
import Text.Layout.Table.Spec.LenSpec
import Text.Layout.Table.Spec.OccSpec
import Text.Layout.Table.Spec.Position
import Text.Layout.Table.Spec.Util
import Text.Layout.Table.StringBuilder

-- | Specifies how a column should be modified. Values of this type are derived
-- in a traversal over the input columns by using 'deriveColModInfosFromGrid'.
-- Finally, 'columnModifier' will interpret them and apply the appropriate
-- modification function to the cells of the column.
data ColModInfo
    = FillAligned OccSpec AlignInfo
    | FillTo Int
    | FitTo Int (Maybe (OccSpec, AlignInfo))

-- | Private show function.
showCMI :: ColModInfo -> String
showCMI :: ColModInfo -> String
showCMI ColModInfo
cmi = case ColModInfo
cmi of
    FillAligned OccSpec
_ AlignInfo
ai -> String
"FillAligned .. " String -> String -> String
forall a. [a] -> [a] -> [a]
++ AlignInfo -> String
showAI AlignInfo
ai
    FillTo Int
i         -> String
"FillTo " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
i
    FitTo Int
i Maybe (OccSpec, AlignInfo)
_        -> String
"FitTo " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
i String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".."

-- | Get the exact width of a 'ColModInfo' after applying it with
-- 'columnModifier'.
widthCMI :: ColModInfo -> Int
widthCMI :: ColModInfo -> Int
widthCMI ColModInfo
cmi = case ColModInfo
cmi of
    FillAligned OccSpec
_ AlignInfo
ai -> AlignInfo -> Int
widthAI AlignInfo
ai
    FillTo Int
maxLen    -> Int
maxLen
    FitTo Int
lim Maybe (OccSpec, AlignInfo)
_      -> Int
lim

-- | Remove alignment from a 'ColModInfo'. This is used to change alignment of
-- headers while using the combined width information.
unalignedCMI :: ColModInfo -> ColModInfo
unalignedCMI :: ColModInfo -> ColModInfo
unalignedCMI ColModInfo
cmi = case ColModInfo
cmi of
    FillAligned OccSpec
_ AlignInfo
ai -> Int -> ColModInfo
FillTo (Int -> ColModInfo) -> Int -> ColModInfo
forall a b. (a -> b) -> a -> b
$ AlignInfo -> Int
widthAI AlignInfo
ai
    FitTo Int
i Maybe (OccSpec, AlignInfo)
_        -> Int -> Maybe (OccSpec, AlignInfo) -> ColModInfo
FitTo Int
i Maybe (OccSpec, AlignInfo)
forall a. Maybe a
Nothing
    ColModInfo
_                -> ColModInfo
cmi

-- | Ensures that the modification provides a minimum width but only if it is
-- not limited.
ensureWidthCMI :: Int -> Position H -> ColModInfo -> ColModInfo
ensureWidthCMI :: Int -> Position H -> ColModInfo -> ColModInfo
ensureWidthCMI Int
w Position H
pos ColModInfo
cmi = case ColModInfo
cmi of
    FillAligned OccSpec
oS ai :: AlignInfo
ai@(AlignInfo Int
lw Maybe Int
optRW)
                  ->
        let neededW :: Int
neededW = Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- AlignInfo -> Int
widthAI AlignInfo
ai
        in if Int
neededW Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0
           then ColModInfo
cmi
           else OccSpec -> AlignInfo -> ColModInfo
FillAligned OccSpec
oS (AlignInfo -> ColModInfo) -> AlignInfo -> ColModInfo
forall a b. (a -> b) -> a -> b
$ case Position H
pos of
               Position H
Start  -> case Maybe Int
optRW of
                   Just Int
rw -> Int -> Maybe Int -> AlignInfo
AlignInfo Int
lw (Maybe Int -> AlignInfo) -> Maybe Int -> AlignInfo
forall a b. (a -> b) -> a -> b
$ Int -> Maybe Int
forall a. a -> Maybe a
Just (Int
rw Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
neededW)
                   Maybe Int
Nothing -> Int -> Maybe Int -> AlignInfo
AlignInfo (Int
lw Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
neededW) Maybe Int
optRW
               Position H
End    -> Int -> Maybe Int -> AlignInfo
AlignInfo (Int
lw Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
neededW) Maybe Int
optRW
               Position H
Center -> case Maybe Int
optRW of
                   Just Int
_  -> let (Int
q, Int
r) = Int
w Int -> Int -> (Int, Int)
forall a. Integral a => a -> a -> (a, a)
`divMod` Int
2 
                              -- Calculate a new distribution.
                              in Int -> Maybe Int -> AlignInfo
AlignInfo Int
q (Maybe Int -> AlignInfo) -> Maybe Int -> AlignInfo
forall a b. (a -> b) -> a -> b
$ Int -> Maybe Int
forall a. a -> Maybe a
Just (Int
q Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
r)
                   Maybe Int
Nothing -> Int -> Maybe Int -> AlignInfo
AlignInfo (Int
lw Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
neededW) Maybe Int
optRW
    FillTo Int
maxLen -> Int -> ColModInfo
FillTo (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
maxLen Int
w)
    ColModInfo
_             -> ColModInfo
cmi

-- | Ensures that the given 'String' will fit into the modified columns.
ensureWidthOfCMI :: Cell a => a -> Position H -> ColModInfo -> ColModInfo
ensureWidthOfCMI :: forall a. Cell a => a -> Position H -> ColModInfo -> ColModInfo
ensureWidthOfCMI = Int -> Position H -> ColModInfo -> ColModInfo
ensureWidthCMI (Int -> Position H -> ColModInfo -> ColModInfo)
-> (a -> Int) -> a -> Position H -> ColModInfo -> ColModInfo
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Int
forall a. Cell a => a -> Int
visibleLength

-- | Fit titles of a header column into the derived 'ColModInfo'.
fitTitlesCMI :: Cell a => [a] -> [Position H] -> [ColModInfo] -> [ColModInfo]
fitTitlesCMI :: forall a.
Cell a =>
[a] -> [Position H] -> [ColModInfo] -> [ColModInfo]
fitTitlesCMI = (a -> Position H -> ColModInfo -> ColModInfo)
-> [a] -> [Position H] -> [ColModInfo] -> [ColModInfo]
forall a b c d. (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
zipWith3 a -> Position H -> ColModInfo -> ColModInfo
forall a. Cell a => a -> Position H -> ColModInfo -> ColModInfo
ensureWidthOfCMI

-- | Generates a function which modifies a given cell according to
-- 'Text.Layout.Table.Position.Position', 'CutMark' and 'ColModInfo'. This is
-- used to modify a single cell of a column to bring all cells of a column to
-- the same width.
columnModifier
    :: (Cell a, StringBuilder b)
    => Position H
    -> CutMark
    -> ColModInfo
    -> (a -> b)
columnModifier :: forall a b.
(Cell a, StringBuilder b) =>
Position H -> CutMark -> ColModInfo -> a -> b
columnModifier Position H
pos CutMark
cms ColModInfo
colModInfo = CutMark -> CellMod a -> b
forall c s. (Cell c, StringBuilder s) => CutMark -> CellMod c -> s
buildCellMod CutMark
cms (CellMod a -> b) -> (a -> CellMod a) -> a -> b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. case ColModInfo
colModInfo of
    FillAligned OccSpec
oS AlignInfo
ai -> OccSpec -> AlignInfo -> a -> CellMod a
forall a. Cell a => OccSpec -> AlignInfo -> a -> CellMod a
align OccSpec
oS AlignInfo
ai
    FillTo Int
maxLen     -> Position H -> Int -> a -> CellMod a
forall a o. Cell a => Position o -> Int -> a -> CellMod a
pad Position H
pos Int
maxLen
    FitTo Int
lim Maybe (OccSpec, AlignInfo)
mT      ->
        (a -> CellMod a)
-> ((OccSpec, AlignInfo) -> a -> CellMod a)
-> Maybe (OccSpec, AlignInfo)
-> a
-> CellMod a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Position H -> CutMark -> Int -> a -> CellMod a
forall a o.
Cell a =>
Position o -> CutMark -> Int -> a -> CellMod a
trimOrPad Position H
pos CutMark
cms Int
lim) ((OccSpec -> AlignInfo -> a -> CellMod a)
-> (OccSpec, AlignInfo) -> a -> CellMod a
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry ((OccSpec -> AlignInfo -> a -> CellMod a)
 -> (OccSpec, AlignInfo) -> a -> CellMod a)
-> (OccSpec -> AlignInfo -> a -> CellMod a)
-> (OccSpec, AlignInfo)
-> a
-> CellMod a
forall a b. (a -> b) -> a -> b
$ Position H
-> CutMark -> Int -> OccSpec -> AlignInfo -> a -> CellMod a
forall a o.
Cell a =>
Position o
-> CutMark -> Int -> OccSpec -> AlignInfo -> a -> CellMod a
alignFixed Position H
pos CutMark
cms Int
lim) Maybe (OccSpec, AlignInfo)
mT

-- | Generate the 'AlignInfo' of a cell by using the 'OccSpec'.
deriveAlignInfo :: Cell a => OccSpec -> a -> AlignInfo
deriveAlignInfo :: forall a. Cell a => OccSpec -> a -> AlignInfo
deriveAlignInfo OccSpec
occSpec = (Char -> Bool) -> a -> AlignInfo
forall a. Cell a => (Char -> Bool) -> a -> AlignInfo
measureAlignment (OccSpec -> Char -> Bool
predicate OccSpec
occSpec)

unpackColSpecs :: [ColSpec] -> [(LenSpec, AlignSpec)]
unpackColSpecs :: [ColSpec] -> [(LenSpec, AlignSpec)]
unpackColSpecs = (ColSpec -> (LenSpec, AlignSpec))
-> [ColSpec] -> [(LenSpec, AlignSpec)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((ColSpec -> (LenSpec, AlignSpec))
 -> [ColSpec] -> [(LenSpec, AlignSpec)])
-> (ColSpec -> (LenSpec, AlignSpec))
-> [ColSpec]
-> [(LenSpec, AlignSpec)]
forall a b. (a -> b) -> a -> b
$ ColSpec -> LenSpec
lenSpec (ColSpec -> LenSpec)
-> (ColSpec -> AlignSpec) -> ColSpec -> (LenSpec, AlignSpec)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& ColSpec -> AlignSpec
alignSpec

-- | Derive the 'ColModInfo' for each column of a list of rows by using the
-- corresponding specifications.
deriveColModInfosFromGridLA :: Cell a => [(LenSpec, AlignSpec)] -> [Row a] -> [ColModInfo]
deriveColModInfosFromGridLA :: forall a.
Cell a =>
[(LenSpec, AlignSpec)] -> [Row a] -> [ColModInfo]
deriveColModInfosFromGridLA [(LenSpec, AlignSpec)]
specs = [(LenSpec, AlignSpec)] -> [[a]] -> [ColModInfo]
forall (col :: * -> *) a.
(Foldable col, Cell a) =>
[(LenSpec, AlignSpec)] -> [col a] -> [ColModInfo]
deriveColModInfosFromColumnsLA [(LenSpec, AlignSpec)]
specs ([[a]] -> [ColModInfo])
-> ([[a]] -> [[a]]) -> [[a]] -> [ColModInfo]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [[a]] -> [[a]]
forall a. [[a]] -> [[a]]
transpose

-- | Derive the 'ColModInfo' for each column of a list of rows by using the
-- corresponding 'ColSpec'.
deriveColModInfosFromGrid :: Cell a => [ColSpec] -> [Row a] -> [ColModInfo]
deriveColModInfosFromGrid :: forall a. Cell a => [ColSpec] -> [Row a] -> [ColModInfo]
deriveColModInfosFromGrid = [(LenSpec, AlignSpec)] -> [Row a] -> [ColModInfo]
forall a.
Cell a =>
[(LenSpec, AlignSpec)] -> [Row a] -> [ColModInfo]
deriveColModInfosFromGridLA ([(LenSpec, AlignSpec)] -> [Row a] -> [ColModInfo])
-> ([ColSpec] -> [(LenSpec, AlignSpec)])
-> [ColSpec]
-> [Row a]
-> [ColModInfo]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ColSpec] -> [(LenSpec, AlignSpec)]
unpackColSpecs

-- | Derive the 'ColModInfo' of a single column by using the 'LenSpec' and the
-- 'AlignSpec'.
deriveColModInfoFromColumnLA :: (Foldable col, Cell a) => (LenSpec, AlignSpec) -> col a -> ColModInfo
deriveColModInfoFromColumnLA :: forall (col :: * -> *) a.
(Foldable col, Cell a) =>
(LenSpec, AlignSpec) -> col a -> ColModInfo
deriveColModInfoFromColumnLA (LenSpec
lenS, AlignSpec
alignS) = case AlignSpec
alignS of
    AlignSpec
NoAlign     -> let expandFun :: Int -> ColModInfo
expandFun = Int -> ColModInfo
FillTo
                       fixedFun :: Int -> b -> ColModInfo
fixedFun Int
i = ColModInfo -> b -> ColModInfo
forall a b. a -> b -> a
const (ColModInfo -> b -> ColModInfo) -> ColModInfo -> b -> ColModInfo
forall a b. (a -> b) -> a -> b
$ Int -> Maybe (OccSpec, AlignInfo) -> ColModInfo
FitTo Int
i Maybe (OccSpec, AlignInfo)
forall a. Maybe a
Nothing
                       measureMaximumWidth :: col a -> Int
measureMaximumWidth = Max Int -> Int
forall a. Max a -> a
getMax (Max Int -> Int) -> (col a -> Max Int) -> col a -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Max Int) -> col a -> Max Int
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap (Int -> Max Int
forall a. a -> Max a
Max (Int -> Max Int) -> (a -> Int) -> a -> Max Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Int
forall a. Cell a => a -> Int
visibleLength)
                       lengthFun :: a -> a
lengthFun = a -> a
forall a. a -> a
id
                    in (Int -> ColModInfo)
-> (Int -> Int -> ColModInfo)
-> (col a -> Int)
-> (Int -> Int)
-> col a
-> ColModInfo
forall a w (col :: * -> *).
(Cell a, Foldable col) =>
(w -> ColModInfo)
-> (Int -> w -> ColModInfo)
-> (col a -> w)
-> (w -> Int)
-> col a
-> ColModInfo
go Int -> ColModInfo
expandFun Int -> Int -> ColModInfo
forall {b}. Int -> b -> ColModInfo
fixedFun col a -> Int
measureMaximumWidth Int -> Int
forall a. a -> a
lengthFun

    AlignOcc OccSpec
oS -> let expandFun :: AlignInfo -> ColModInfo
expandFun = OccSpec -> AlignInfo -> ColModInfo
FillAligned OccSpec
oS
                       fixedFun :: Int -> AlignInfo -> ColModInfo
fixedFun Int
i = Int -> Maybe (OccSpec, AlignInfo) -> ColModInfo
FitTo Int
i (Maybe (OccSpec, AlignInfo) -> ColModInfo)
-> (AlignInfo -> Maybe (OccSpec, AlignInfo))
-> AlignInfo
-> ColModInfo
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (OccSpec, AlignInfo) -> Maybe (OccSpec, AlignInfo)
forall a. a -> Maybe a
Just ((OccSpec, AlignInfo) -> Maybe (OccSpec, AlignInfo))
-> (AlignInfo -> (OccSpec, AlignInfo))
-> AlignInfo
-> Maybe (OccSpec, AlignInfo)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (,) OccSpec
oS
                       measureMaximumWidth :: col a -> AlignInfo
measureMaximumWidth = (a -> AlignInfo) -> col a -> AlignInfo
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap ((a -> AlignInfo) -> col a -> AlignInfo)
-> (a -> AlignInfo) -> col a -> AlignInfo
forall a b. (a -> b) -> a -> b
$ OccSpec -> a -> AlignInfo
forall a. Cell a => OccSpec -> a -> AlignInfo
deriveAlignInfo OccSpec
oS
                       lengthFun :: AlignInfo -> Int
lengthFun = AlignInfo -> Int
widthAI
                    in (AlignInfo -> ColModInfo)
-> (Int -> AlignInfo -> ColModInfo)
-> (col a -> AlignInfo)
-> (AlignInfo -> Int)
-> col a
-> ColModInfo
forall a w (col :: * -> *).
(Cell a, Foldable col) =>
(w -> ColModInfo)
-> (Int -> w -> ColModInfo)
-> (col a -> w)
-> (w -> Int)
-> col a
-> ColModInfo
go AlignInfo -> ColModInfo
expandFun Int -> AlignInfo -> ColModInfo
fixedFun col a -> AlignInfo
measureMaximumWidth AlignInfo -> Int
lengthFun
  where
    go :: forall a w col. (Cell a, Foldable col)
       => (w -> ColModInfo)
       -> (Int -> w -> ColModInfo)
       -> (col a -> w)
       -> (w -> Int)
       -> col a
       -> ColModInfo
    go :: forall a w (col :: * -> *).
(Cell a, Foldable col) =>
(w -> ColModInfo)
-> (Int -> w -> ColModInfo)
-> (col a -> w)
-> (w -> Int)
-> col a
-> ColModInfo
go w -> ColModInfo
expandFun Int -> w -> ColModInfo
fixedFun col a -> w
measureMaximumWidth w -> Int
lengthFun =
        let expandBetween' :: Int -> Int -> w -> ColModInfo
expandBetween' Int
i Int
j w
widthInfo | w -> Int
lengthFun w
widthInfo Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
j = Int -> w -> ColModInfo
fixedFun Int
j w
widthInfo
                                         | w -> Int
lengthFun w
widthInfo Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
i = Int -> w -> ColModInfo
fixedFun Int
i w
widthInfo
                                         | Bool
otherwise               = w -> ColModInfo
expandFun w
widthInfo
            expandUntil' :: (Bool -> Bool) -> Int -> w -> ColModInfo
expandUntil' Bool -> Bool
f Int
i w
widthInfo = if Bool -> Bool
f (w -> Int
lengthFun w
widthInfo Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
i)
                                         then w -> ColModInfo
expandFun w
widthInfo
                                         else Int -> w -> ColModInfo
fixedFun Int
i w
widthInfo
            interpretLenSpec :: w -> ColModInfo
interpretLenSpec = case LenSpec
lenS of
                LenSpec
Expand            -> w -> ColModInfo
expandFun
                Fixed Int
i           -> Int -> w -> ColModInfo
fixedFun Int
i
                ExpandUntil Int
i     -> (Bool -> Bool) -> Int -> w -> ColModInfo
expandUntil' Bool -> Bool
forall a. a -> a
id Int
i
                FixedUntil Int
i      -> (Bool -> Bool) -> Int -> w -> ColModInfo
expandUntil' Bool -> Bool
not Int
i
                ExpandBetween Int
i Int
j -> Int -> Int -> w -> ColModInfo
expandBetween' Int
i Int
j
        in w -> ColModInfo
interpretLenSpec (w -> ColModInfo) -> (col a -> w) -> col a -> ColModInfo
forall b c a. (b -> c) -> (a -> b) -> a -> c
. col a -> w
measureMaximumWidth

-- | Derive the 'ColModInfo' for each column of a list of columns by using the
-- corresponding specifications.
deriveColModInfosFromColumnsLA :: (Foldable col, Cell a) => [(LenSpec, AlignSpec)] -> [col a] -> [ColModInfo]
deriveColModInfosFromColumnsLA :: forall (col :: * -> *) a.
(Foldable col, Cell a) =>
[(LenSpec, AlignSpec)] -> [col a] -> [ColModInfo]
deriveColModInfosFromColumnsLA [(LenSpec, AlignSpec)]
specs = ((col a -> ColModInfo) -> col a -> ColModInfo)
-> [col a -> ColModInfo] -> [col a] -> [ColModInfo]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (col a -> ColModInfo) -> col a -> ColModInfo
forall a b. (a -> b) -> a -> b
($) (((LenSpec, AlignSpec) -> col a -> ColModInfo)
-> [(LenSpec, AlignSpec)] -> [col a -> ColModInfo]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (LenSpec, AlignSpec) -> col a -> ColModInfo
forall (col :: * -> *) a.
(Foldable col, Cell a) =>
(LenSpec, AlignSpec) -> col a -> ColModInfo
deriveColModInfoFromColumnLA [(LenSpec, AlignSpec)]
specs)

-- | Derive the 'ColModInfo' for each column of a list of columns by using the
-- corresponding 'ColSpec'.
deriveColModInfosFromColumns :: (Foldable col, Cell a) => [ColSpec] -> [col a] -> [ColModInfo]
deriveColModInfosFromColumns :: forall (col :: * -> *) a.
(Foldable col, Cell a) =>
[ColSpec] -> [col a] -> [ColModInfo]
deriveColModInfosFromColumns = [(LenSpec, AlignSpec)] -> [col a] -> [ColModInfo]
forall (col :: * -> *) a.
(Foldable col, Cell a) =>
[(LenSpec, AlignSpec)] -> [col a] -> [ColModInfo]
deriveColModInfosFromColumnsLA ([(LenSpec, AlignSpec)] -> [col a] -> [ColModInfo])
-> ([ColSpec] -> [(LenSpec, AlignSpec)])
-> [ColSpec]
-> [col a]
-> [ColModInfo]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ColSpec] -> [(LenSpec, AlignSpec)]
unpackColSpecs

-- | Derive the 'ColModInfo' and generate functions without any intermediate
-- steps.
deriveColumnModifiers
    :: (Cell a, StringBuilder b)
    => [ColSpec]
    -> [Row a]
    -> [a -> b]
deriveColumnModifiers :: forall a b.
(Cell a, StringBuilder b) =>
[ColSpec] -> [Row a] -> [a -> b]
deriveColumnModifiers [ColSpec]
specs [Row a]
tab =
    ((Position H, CutMark) -> ColModInfo -> a -> b)
-> [(Position H, CutMark)] -> [ColModInfo] -> [a -> b]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith ((Position H -> CutMark -> ColModInfo -> a -> b)
-> (Position H, CutMark) -> ColModInfo -> a -> b
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Position H -> CutMark -> ColModInfo -> a -> b
forall a b.
(Cell a, StringBuilder b) =>
Position H -> CutMark -> ColModInfo -> a -> b
columnModifier) ((ColSpec -> (Position H, CutMark))
-> [ColSpec] -> [(Position H, CutMark)]
forall a b. (a -> b) -> [a] -> [b]
map (ColSpec -> Position H
position (ColSpec -> Position H)
-> (ColSpec -> CutMark) -> ColSpec -> (Position H, CutMark)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& ColSpec -> CutMark
cutMark) [ColSpec]
specs) [ColModInfo]
cmis
  where
    cmis :: [ColModInfo]
cmis = [ColSpec] -> [Row a] -> [ColModInfo]
forall a. Cell a => [ColSpec] -> [Row a] -> [ColModInfo]
deriveColModInfosFromGrid [ColSpec]
specs [Row a]
tab