width provided by showMixed. Also refactor renderTable to be a bit clearer, and to avoid duplicate calculations.
		
			
				
	
	
		
			209 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Haskell
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Haskell
		
	
	
	
	
	
| -- | Text.Tabular.AsciiArt from tabular-0.2.2.7, modified to treat
 | |
| -- wide characters as double width.
 | |
| 
 | |
| module Text.Tabular.AsciiWide where
 | |
| 
 | |
| import Data.List (intersperse, transpose)
 | |
| import Text.Tabular
 | |
| import Hledger.Utils.String
 | |
| 
 | |
| 
 | |
| -- | Render a table according to common options, for backwards compatibility
 | |
| render :: Bool -> (rh -> String) -> (ch -> String) -> (a -> String) -> Table rh ch a -> String
 | |
| render pretty fr fc f = renderTable True pretty (rightCell . fr) (rightCell . fc) (rightCell . f)
 | |
| 
 | |
| -- | Render a table according to various cell specifications
 | |
| renderTable :: Bool              -- ^ Whether to display the outer borders
 | |
|             -> Bool              -- ^ Pretty tables
 | |
|             -> (rh -> CellSpec)  -- ^ Rendering function for row headers
 | |
|             -> (ch -> CellSpec)  -- ^ Rendering function for column headers
 | |
|             -> (a -> CellSpec)   -- ^ Function determining the string and width of a cell
 | |
|             -> Table rh ch a
 | |
|             -> String
 | |
| renderTable borders pretty fr fc f (Table rh ch cells) =
 | |
|   unlines . addBorders $
 | |
|     renderColumns borders pretty sizes ch2
 | |
|     : bar VM DoubleLine   -- +======================================+
 | |
|     : renderRs (fmap renderR $ zipHeader [] cellContents rowHeaders)
 | |
|  where
 | |
|   renderR (cs,h) = renderColumns borders pretty sizes $ Group DoubleLine
 | |
|                      [ Header h
 | |
|                      , fmap fst $ zipHeader emptyCell cs colHeaders
 | |
|                      ]
 | |
| 
 | |
|   rowHeaders   = fmap fr rh
 | |
|   colHeaders   = fmap fc ch
 | |
|   cellContents = map (map f) cells
 | |
| 
 | |
|   -- ch2 and cell2 include the row and column labels
 | |
|   ch2 = Group DoubleLine [Header emptyCell, colHeaders]
 | |
|   cells2 = headerContents ch2 : zipWith (:) (headerContents rowHeaders) cellContents
 | |
| 
 | |
|   -- maximum width for each column
 | |
|   sizes   = map (maximum . map csWidth) $ transpose cells2
 | |
|   renderRs (Header s)   = [s]
 | |
|   renderRs (Group p hs) = concat . intersperse sep $ map renderRs hs
 | |
|     where sep = renderHLine VM borders pretty sizes ch2 p
 | |
| 
 | |
|   -- borders and bars
 | |
|   addBorders xs = if borders then bar VT SingleLine : xs ++ [bar VB SingleLine] else xs
 | |
|   bar vpos prop = concat $ renderHLine vpos borders pretty sizes ch2 prop
 | |
| 
 | |
| 
 | |
| data CellSpec = CellSpec
 | |
|     { csString :: String
 | |
|     , csAlign  :: Align
 | |
|     , csWidth  :: Int
 | |
|     } deriving (Show)
 | |
| 
 | |
| emptyCell :: CellSpec
 | |
| emptyCell = CellSpec "" AlignRight 0
 | |
| 
 | |
| rightCell :: String -> CellSpec
 | |
| rightCell x = CellSpec x AlignRight (strWidth x)
 | |
| 
 | |
| leftCell :: String -> CellSpec
 | |
| leftCell x = CellSpec x AlignLeft (strWidth x)
 | |
| 
 | |
| data Align = AlignLeft | AlignRight
 | |
|   deriving (Show)
 | |
| 
 | |
| 
 | |
| verticalBar :: Bool -> Char
 | |
| verticalBar pretty = if pretty then '│' else '|'
 | |
| 
 | |
| leftBar :: Bool -> String
 | |
| leftBar pretty = verticalBar pretty : " "
 | |
| 
 | |
| rightBar :: Bool -> String
 | |
| rightBar pretty = " " ++ [verticalBar pretty]
 | |
| 
 | |
| midBar :: Bool -> String
 | |
| midBar pretty = " " ++ verticalBar pretty : " "
 | |
| 
 | |
| doubleMidBar :: Bool -> String
 | |
| doubleMidBar pretty = if pretty then " ║ " else " || "
 | |
| 
 | |
| -- | We stop rendering on the shortest list!
 | |
| renderColumns :: Bool   -- ^ show outer borders
 | |
|               -> Bool   -- ^ pretty
 | |
|               -> [Int]  -- ^ max width for each column
 | |
|               -> Header CellSpec
 | |
|               -> String
 | |
| renderColumns borders pretty is h = addBorders coreLine
 | |
|  where
 | |
|   addBorders xs = if borders then leftBar pretty ++ xs ++ rightBar pretty else ' ' : xs ++ " "
 | |
|   coreLine = concatMap helper $ flattenHeader $ zipHeader 0 is h
 | |
|   helper = either hsep (\(w, cs) -> case csAlign cs of
 | |
|                             AlignLeft  -> csString cs ++ replicate (w - csWidth cs) ' '
 | |
|                             AlignRight -> replicate (w - csWidth cs) ' ' ++ csString cs
 | |
|                         )
 | |
|   hsep :: Properties -> String
 | |
|   hsep NoLine     = "  "
 | |
|   hsep SingleLine = midBar pretty
 | |
|   hsep DoubleLine = doubleMidBar pretty
 | |
| 
 | |
| renderHLine :: VPos
 | |
|             -> Bool  -- ^ show outer borders
 | |
|             -> Bool -- ^ pretty
 | |
|             -> [Int] -- ^ width specifications
 | |
|             -> Header a
 | |
|             -> Properties
 | |
|             -> [String]
 | |
| renderHLine _ _ _ _ _ NoLine = []
 | |
| renderHLine vpos borders pretty w h prop = [renderHLine' vpos borders pretty prop w h]
 | |
| 
 | |
| renderHLine' :: VPos -> Bool -> Bool -> Properties -> [Int] -> Header a -> String
 | |
| renderHLine' vpos borders pretty prop is h = addBorders $ sep ++ coreLine ++ sep
 | |
|  where
 | |
|   addBorders xs   = if borders then edge HL ++ xs ++ edge HR else xs
 | |
|   edge hpos       = boxchar vpos hpos SingleLine prop pretty
 | |
|   coreLine        = concatMap helper $ flattenHeader $ zipHeader 0 is h
 | |
|   helper          = either vsep dashes
 | |
|   dashes (i,_)    = concat (replicate i sep)
 | |
|   sep             = boxchar vpos HM NoLine prop pretty
 | |
|   vsep v          = case v of
 | |
|                       NoLine -> sep ++ sep
 | |
|                       _      -> sep ++ cross v prop ++ sep
 | |
|   cross v h       = boxchar vpos HM v h pretty
 | |
| 
 | |
| data VPos = VT | VM | VB -- top middle bottom
 | |
| data HPos = HL | HM | HR -- left middle right
 | |
| 
 | |
| boxchar :: VPos -> HPos -> Properties -> Properties -> Bool -> String
 | |
| boxchar vpos hpos vert horiz = lineart u d l r
 | |
|   where
 | |
|     u =
 | |
|       case vpos of
 | |
|         VT -> NoLine
 | |
|         _  -> vert
 | |
|     d =
 | |
|       case vpos of
 | |
|         VB -> NoLine
 | |
|         _  -> vert
 | |
|     l =
 | |
|       case hpos of
 | |
|         HL -> NoLine
 | |
|         _  -> horiz
 | |
|     r =
 | |
|       case hpos of
 | |
|         HR -> NoLine
 | |
|         _  -> horiz
 | |
| 
 | |
| pick :: String -> String -> Bool -> String
 | |
| pick x _ True  = x
 | |
| pick _ x False = x
 | |
| 
 | |
| lineart :: Properties -> Properties -> Properties -> Properties -> Bool -> String
 | |
| --      up         down       left      right
 | |
| lineart SingleLine SingleLine SingleLine SingleLine = pick "┼" "+"
 | |
| lineart SingleLine SingleLine SingleLine NoLine     = pick "┤" "+"
 | |
| lineart SingleLine SingleLine NoLine     SingleLine = pick "├" "+"
 | |
| lineart SingleLine NoLine     SingleLine SingleLine = pick "┴" "+"
 | |
| lineart NoLine     SingleLine SingleLine SingleLine = pick "┬" "+"
 | |
| lineart SingleLine NoLine     NoLine     SingleLine = pick "└" "+"
 | |
| lineart SingleLine NoLine     SingleLine NoLine     = pick "┘" "+"
 | |
| lineart NoLine     SingleLine SingleLine NoLine     = pick "┐" "+"
 | |
| lineart NoLine     SingleLine NoLine     SingleLine = pick "┌" "+"
 | |
| lineart SingleLine SingleLine NoLine     NoLine     = pick "│" "|"
 | |
| lineart NoLine     NoLine     SingleLine SingleLine = pick "─" "-"
 | |
| 
 | |
| lineart DoubleLine DoubleLine DoubleLine DoubleLine = pick "╬" "++"
 | |
| lineart DoubleLine DoubleLine DoubleLine NoLine     = pick "╣" "++"
 | |
| lineart DoubleLine DoubleLine NoLine     DoubleLine = pick "╠" "++"
 | |
| lineart DoubleLine NoLine     DoubleLine DoubleLine = pick "╩" "++"
 | |
| lineart NoLine     DoubleLine DoubleLine DoubleLine = pick "╦" "++"
 | |
| lineart DoubleLine NoLine     NoLine     DoubleLine = pick "╚" "++"
 | |
| lineart DoubleLine NoLine     DoubleLine NoLine     = pick "╝" "++"
 | |
| lineart NoLine     DoubleLine DoubleLine NoLine     = pick "╗" "++"
 | |
| lineart NoLine     DoubleLine NoLine     DoubleLine = pick "╔" "++"
 | |
| lineart DoubleLine DoubleLine NoLine     NoLine     = pick "║" "||"
 | |
| lineart NoLine     NoLine     DoubleLine DoubleLine = pick "═" "="
 | |
| 
 | |
| lineart DoubleLine NoLine     NoLine     SingleLine = pick "╙" "++"
 | |
| lineart DoubleLine NoLine     SingleLine NoLine     = pick "╜" "++"
 | |
| lineart NoLine     DoubleLine SingleLine NoLine     = pick "╖" "++"
 | |
| lineart NoLine     DoubleLine NoLine     SingleLine = pick "╓" "++"
 | |
| 
 | |
| lineart SingleLine NoLine     NoLine     DoubleLine = pick "╘" "+"
 | |
| lineart SingleLine NoLine     DoubleLine NoLine     = pick "╛" "+"
 | |
| lineart NoLine     SingleLine DoubleLine NoLine     = pick "╕" "+"
 | |
| lineart NoLine     SingleLine NoLine     DoubleLine = pick "╒" "+"
 | |
| 
 | |
| lineart DoubleLine DoubleLine SingleLine NoLine     = pick "╢" "++"
 | |
| lineart DoubleLine DoubleLine NoLine     SingleLine = pick "╟" "++"
 | |
| lineart DoubleLine NoLine     SingleLine SingleLine = pick "╨" "++"
 | |
| lineart NoLine     DoubleLine SingleLine SingleLine = pick "╥" "++"
 | |
| 
 | |
| lineart SingleLine SingleLine DoubleLine NoLine     = pick "╡" "+"
 | |
| lineart SingleLine SingleLine NoLine     DoubleLine = pick "╞" "+"
 | |
| lineart SingleLine NoLine     DoubleLine DoubleLine = pick "╧" "+"
 | |
| lineart NoLine     SingleLine DoubleLine DoubleLine = pick "╤" "+"
 | |
| 
 | |
| lineart SingleLine SingleLine DoubleLine DoubleLine = pick "╪" "+"
 | |
| lineart DoubleLine DoubleLine SingleLine SingleLine = pick "╫" "++"
 | |
| 
 | |
| lineart _          _          _          _          = const ""
 | |
| 
 | |
| -- 
 |