1 module Hcompta.LCC.Write.Table where
4 import Data.Char (Char)
5 import Data.Eq (Eq(..))
6 import Data.Foldable (any, foldr)
7 import Data.Functor ((<$>))
8 import Data.Maybe (Maybe(..), fromMaybe)
9 import Data.Ord (Ord(..))
10 import Data.Semigroup (Semigroup(..))
11 import Data.Text (Text)
12 import Data.Tuple (curry)
13 import Prelude (($), (.), Int, Integral(..), Num(..), fromIntegral, id, zipWith, toInteger)
14 import Text.Show (Show)
15 import qualified Data.List as L
16 import qualified Data.Text as T
17 import qualified Data.Text.Lazy as TL
19 import Hcompta.LCC.Write.Compta
20 import qualified Language.Symantic.Document as D
21 import qualified Language.Symantic.Document.Term.Dimension as Dim
24 type Table d = [Column d]
26 instance (D.Textable d, D.Colorable d, D.Indentable d) => Writeable d (Table d) where
28 let cols = refreshWidthCol <$> cols' in
29 let rows = L.transpose $ column_rows <$> cols in
30 let has_title = any (not . T.null . column_title) cols in
31 let titles :: d = D.catH $ L.intersperse (d_sep '|') $ d_title <$> cols in
33 (if has_title then (:) titles else id) $
36 L.intersperse (D.space <> d_sep '|') $
38 zipWith (curry $ alignCell Nothing) cols row
40 (case cols of { [] -> D.empty; _ -> D.newline })
42 refreshWidthCol col@Column{column_width=w} =
44 then col{column_width = widthCol col}
47 widthCol :: Column d -> Int
51 max (T.length column_title) $
52 foldr (max . cell_width) 0 column_rows
53 d_title :: Column d -> d
54 d_title col@Column{column_title} = do
55 let pad len = D.blacker $ D.text $ T.pack $ L.replicate len '_'
56 alignCell (Just pad) (col, Cell
57 { cell_width = T.length column_title
58 , cell_content = d_under <> d_underline column_title <> d_under
59 , cell_align = Just AlignC
61 d_sep = D.blacker . D.charH
64 D.catH $ (<$> T.unpack t) $ \case
70 D.Colorable d => D.Indentable d =>
72 (Column d, Cell d) -> d
74 ( Column{column_align, column_width}
75 , Cell{cell_width, cell_content, cell_align} ) =
76 let pad = column_width - cell_width in
77 case column_align `fromMaybe` cell_align of
78 AlignL -> cell_content <> padding pad
79 AlignC -> padding half <> cell_content <> padding (pad - half)
80 where half = fromInteger $ quot (toInteger pad) 2
81 AlignR -> padding pad <> cell_content
82 where padding = (D.spaces . D.Nat . toInteger) `fromMaybe` may_padding
84 ( Column{column_width}
85 , Cell_Line{cell_pad} ) =
86 D.blacker $ D.ltextH $
87 TL.replicate (fromIntegral column_width) $
91 class TableOf a d where
92 tableOf :: a -> Table d
97 { column_title :: Text
98 , column_align :: Align
100 , column_rows :: [Cell d]
101 } deriving (Eq, Show)
103 column :: Text -> Align -> [Cell d] -> Column d
119 -- ** Class 'columnOf'
120 class ColumnOf a d where
121 columnOf :: a -> Column d
125 = Cell { cell_align :: Maybe Align
129 | Cell_Line { cell_pad :: Char
135 class CellOf a d where
136 cellOf :: a -> Cell d
138 Writeable Dim.Dimension a =>
143 instance D.Textable d => CellOf () d where
146 , cell_align = Nothing
147 , cell_content = D.empty
151 Writeable Dim.Dimension a =>
156 { cell_width = fromIntegral $ D.unNat $ Dim.dim_width $ Dim.dim $ write a
157 , cell_align = Nothing
158 , cell_content = write a
167 instance ToDoc Column Cell where
168 toDoc = alignCell Nothing
171 class CellOf context x where
172 cellOf :: context -> x -> Cell
174 instance CellOf context x => CellOf context (Maybe x) where
175 cellOf ctx = maybe cell (cellOf ctx)
177 -- ** Class 'Cell_of_forall_param'
179 -- | A class useful when using a context of kind '*' is not wanted
180 -- for example in a class instance constraint
181 -- to keep the instance decidable (i.e. avoid UndecidableInstances).
182 class Cell_of_forall_param f x where
183 cell_of_forall_param :: forall m. f m -> x -> Cell
184 -- instance Cell_of_forall_param f x => CellOf (f m) x where
185 -- cellOf = cell_of_forall_param
186 instance Cell_of_forall_param context x => Cell_of_forall_param context (Maybe x) where
187 cell_of_forall_param ctx = maybe cell (cell_of_forall_param ctx)