balance: wide-char-aware multicolumn reports (#242)
This commit is contained in:
parent
ef27e5c427
commit
58b98faf36
75
hledger-lib/Text/Tabular/AsciiWide.hs
Normal file
75
hledger-lib/Text/Tabular/AsciiWide.hs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
-- | 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
|
||||||
|
|
||||||
|
-- | for simplicity, we assume that each cell is rendered
|
||||||
|
-- on a single line
|
||||||
|
render :: (rh -> String)
|
||||||
|
-> (ch -> String)
|
||||||
|
-> (a -> String)
|
||||||
|
-> Table rh ch a
|
||||||
|
-> String
|
||||||
|
render fr fc f (Table rh ch cells) =
|
||||||
|
unlines $ [ bar SingleLine -- +--------------------------------------+
|
||||||
|
, renderColumns sizes ch2
|
||||||
|
, bar DoubleLine -- +======================================+
|
||||||
|
] ++
|
||||||
|
(renderRs $ fmap renderR $ zipHeader [] cells $ fmap fr rh) ++
|
||||||
|
[ bar SingleLine ] -- +--------------------------------------+
|
||||||
|
where
|
||||||
|
bar = concat . renderHLine sizes ch2
|
||||||
|
-- ch2 and cell2 include the row and column labels
|
||||||
|
ch2 = Group DoubleLine [Header "", fmap fc ch]
|
||||||
|
cells2 = headerContents ch2
|
||||||
|
: zipWith (\h cs -> h : map f cs) rhStrings cells
|
||||||
|
--
|
||||||
|
renderR (cs,h) = renderColumns sizes $ Group DoubleLine
|
||||||
|
[ Header h
|
||||||
|
, fmap fst $ zipHeader "" (map f cs) ch]
|
||||||
|
rhStrings = map fr $ headerContents rh
|
||||||
|
-- maximum width for each column
|
||||||
|
sizes = map (maximum . map strWidth) . transpose $ cells2
|
||||||
|
renderRs (Header s) = [s]
|
||||||
|
renderRs (Group p hs) = concat . intersperse sep . map renderRs $ hs
|
||||||
|
where sep = renderHLine sizes ch2 p
|
||||||
|
|
||||||
|
-- | We stop rendering on the shortest list!
|
||||||
|
renderColumns :: [Int] -- ^ max width for each column
|
||||||
|
-> Header String
|
||||||
|
-> String
|
||||||
|
renderColumns is h = "| " ++ coreLine ++ " |"
|
||||||
|
where
|
||||||
|
coreLine = concatMap helper $ flattenHeader $ zipHeader 0 is h
|
||||||
|
helper = either hsep (uncurry padLeftWide)
|
||||||
|
hsep :: Properties -> String
|
||||||
|
hsep NoLine = " "
|
||||||
|
hsep SingleLine = " | "
|
||||||
|
hsep DoubleLine = " || "
|
||||||
|
|
||||||
|
renderHLine :: [Int] -- ^ width specifications
|
||||||
|
-> Header String
|
||||||
|
-> Properties
|
||||||
|
-> [String]
|
||||||
|
renderHLine _ _ NoLine = []
|
||||||
|
renderHLine w h SingleLine = [renderHLine' w '-' h]
|
||||||
|
renderHLine w h DoubleLine = [renderHLine' w '=' h]
|
||||||
|
|
||||||
|
renderHLine' :: [Int] -> Char -> Header String -> String
|
||||||
|
renderHLine' is sep h = [ '+', sep ] ++ coreLine ++ [sep, '+']
|
||||||
|
where
|
||||||
|
coreLine = concatMap helper $ flattenHeader $ zipHeader 0 is h
|
||||||
|
helper = either vsep dashes
|
||||||
|
dashes (i,_) = replicate i sep
|
||||||
|
vsep NoLine = [sep]
|
||||||
|
vsep SingleLine = sep : "+" ++ [sep]
|
||||||
|
vsep DoubleLine = sep : "++" ++ [sep]
|
||||||
|
|
||||||
|
-- padLeft :: Int -> String -> String
|
||||||
|
-- padLeft l s = padding ++ s
|
||||||
|
-- where padding = replicate (l - length s) ' '
|
||||||
|
|
||||||
@ -250,7 +250,7 @@ import Text.CSV
|
|||||||
import Test.HUnit
|
import Test.HUnit
|
||||||
import Text.Printf (printf)
|
import Text.Printf (printf)
|
||||||
import Text.Tabular as T
|
import Text.Tabular as T
|
||||||
import Text.Tabular.AsciiArt
|
import Text.Tabular.AsciiWide
|
||||||
|
|
||||||
import Hledger
|
import Hledger
|
||||||
import Hledger.Cli.CliOptions
|
import Hledger.Cli.CliOptions
|
||||||
@ -470,7 +470,7 @@ periodBalanceReportAsText opts r@(MultiBalanceReport (colspans, items, (coltotal
|
|||||||
render id (" "++) showMixedAmountOneLineWithoutPrice $
|
render id (" "++) showMixedAmountOneLineWithoutPrice $
|
||||||
addtotalrow $
|
addtotalrow $
|
||||||
Table
|
Table
|
||||||
(T.Group NoLine $ map (Header . padright acctswidth) accts)
|
(T.Group NoLine $ map (Header . padRightWide acctswidth) accts)
|
||||||
(T.Group NoLine $ map Header colheadings)
|
(T.Group NoLine $ map Header colheadings)
|
||||||
(map rowvals items')
|
(map rowvals items')
|
||||||
where
|
where
|
||||||
@ -484,7 +484,7 @@ periodBalanceReportAsText opts r@(MultiBalanceReport (colspans, items, (coltotal
|
|||||||
renderacct ((a,a',i),_,_,_)
|
renderacct ((a,a',i),_,_,_)
|
||||||
| tree_ opts = replicate ((i-1)*2) ' ' ++ a'
|
| tree_ opts = replicate ((i-1)*2) ' ' ++ a'
|
||||||
| otherwise = maybeAccountNameDrop opts a
|
| otherwise = maybeAccountNameDrop opts a
|
||||||
acctswidth = maximum' $ map length $ accts
|
acctswidth = maximum' $ map strWidth accts
|
||||||
rowvals (_,as,rowtot,rowavg) = as
|
rowvals (_,as,rowtot,rowavg) = as
|
||||||
++ (if row_total_ opts then [rowtot] else [])
|
++ (if row_total_ opts then [rowtot] else [])
|
||||||
++ (if average_ opts then [rowavg] else [])
|
++ (if average_ opts then [rowavg] else [])
|
||||||
@ -504,7 +504,7 @@ cumulativeBalanceReportAsText opts r@(MultiBalanceReport (colspans, items, (colt
|
|||||||
render id (" "++) showMixedAmountOneLineWithoutPrice $
|
render id (" "++) showMixedAmountOneLineWithoutPrice $
|
||||||
addtotalrow $
|
addtotalrow $
|
||||||
Table
|
Table
|
||||||
(T.Group NoLine $ map (Header . padright acctswidth) accts)
|
(T.Group NoLine $ map (Header . padRightWide acctswidth) accts)
|
||||||
(T.Group NoLine $ map Header colheadings)
|
(T.Group NoLine $ map Header colheadings)
|
||||||
(map rowvals items)
|
(map rowvals items)
|
||||||
where
|
where
|
||||||
@ -516,7 +516,7 @@ cumulativeBalanceReportAsText opts r@(MultiBalanceReport (colspans, items, (colt
|
|||||||
renderacct ((a,a',i),_,_,_)
|
renderacct ((a,a',i),_,_,_)
|
||||||
| tree_ opts = replicate ((i-1)*2) ' ' ++ a'
|
| tree_ opts = replicate ((i-1)*2) ' ' ++ a'
|
||||||
| otherwise = maybeAccountNameDrop opts a
|
| otherwise = maybeAccountNameDrop opts a
|
||||||
acctswidth = maximum' $ map length $ accts
|
acctswidth = maximum' $ map strWidth accts
|
||||||
rowvals (_,as,rowtot,rowavg) = as
|
rowvals (_,as,rowtot,rowavg) = as
|
||||||
++ (if row_total_ opts then [rowtot] else [])
|
++ (if row_total_ opts then [rowtot] else [])
|
||||||
++ (if average_ opts then [rowavg] else [])
|
++ (if average_ opts then [rowavg] else [])
|
||||||
@ -536,7 +536,7 @@ historicalBalanceReportAsText opts r@(MultiBalanceReport (colspans, items, (colt
|
|||||||
render id (" "++) showMixedAmountOneLineWithoutPrice $
|
render id (" "++) showMixedAmountOneLineWithoutPrice $
|
||||||
addtotalrow $
|
addtotalrow $
|
||||||
Table
|
Table
|
||||||
(T.Group NoLine $ map (Header . padright acctswidth) accts)
|
(T.Group NoLine $ map (Header . padRightWide acctswidth) accts)
|
||||||
(T.Group NoLine $ map Header colheadings)
|
(T.Group NoLine $ map Header colheadings)
|
||||||
(map rowvals items)
|
(map rowvals items)
|
||||||
where
|
where
|
||||||
@ -548,7 +548,7 @@ historicalBalanceReportAsText opts r@(MultiBalanceReport (colspans, items, (colt
|
|||||||
renderacct ((a,a',i),_,_,_)
|
renderacct ((a,a',i),_,_,_)
|
||||||
| tree_ opts = replicate ((i-1)*2) ' ' ++ a'
|
| tree_ opts = replicate ((i-1)*2) ' ' ++ a'
|
||||||
| otherwise = maybeAccountNameDrop opts a
|
| otherwise = maybeAccountNameDrop opts a
|
||||||
acctswidth = maximum' $ map length $ accts
|
acctswidth = maximum' $ map strWidth accts
|
||||||
rowvals (_,as,rowtot,rowavg) = as
|
rowvals (_,as,rowtot,rowavg) = as
|
||||||
++ (if row_total_ opts then [rowtot] else [])
|
++ (if row_total_ opts then [rowtot] else [])
|
||||||
++ (if average_ opts then [rowavg] else [])
|
++ (if average_ opts then [rowavg] else [])
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user