Add an option to use unicode in balance tables (#528)
* Add an option to use unicode in balance tables fixes #522 * Add a test for unicode tables * Document --pretty-tables * Support --pretty-tables in BalanceView
This commit is contained in:
parent
7d0734f1ed
commit
f4b3f1c094
@ -91,6 +91,7 @@ data ReportOpts = ReportOpts {
|
||||
,row_total_ :: Bool
|
||||
,no_total_ :: Bool
|
||||
,value_ :: Bool
|
||||
,pretty_tables_ :: Bool
|
||||
} deriving (Show, Data, Typeable)
|
||||
|
||||
instance Default ReportOpts where def = defreportopts
|
||||
@ -118,6 +119,7 @@ defreportopts = ReportOpts
|
||||
def
|
||||
def
|
||||
def
|
||||
def
|
||||
|
||||
rawOptsToReportOpts :: RawOpts -> IO ReportOpts
|
||||
rawOptsToReportOpts rawopts = checkReportOpts <$> do
|
||||
@ -144,6 +146,7 @@ rawOptsToReportOpts rawopts = checkReportOpts <$> do
|
||||
,row_total_ = boolopt "row-total" rawopts'
|
||||
,no_total_ = boolopt "no-total" rawopts'
|
||||
,value_ = boolopt "value" rawopts'
|
||||
,pretty_tables_ = boolopt "pretty-tables" rawopts'
|
||||
}
|
||||
|
||||
-- | Do extra validation of raw option values, raising an error if there's a problem.
|
||||
|
||||
@ -280,6 +280,7 @@ balancemode = (defCommandMode $ ["balance"] ++ aliases) { -- also accept but don
|
||||
,flagReq ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "omit N leading account name parts (in flat mode)"
|
||||
,flagNone ["no-elide"] (\opts -> setboolopt "no-elide" opts) "don't squash boring parent accounts (in tree mode)"
|
||||
,flagReq ["format"] (\s opts -> Right $ setopt "format" s opts) "FORMATSTR" "use this custom line format (in simple reports)"
|
||||
,flagNone ["pretty-tables"] (\opts -> setboolopt "pretty-tables" opts) "use unicode when displaying tables"
|
||||
]
|
||||
++ outputflags
|
||||
,groupHidden = []
|
||||
@ -475,7 +476,7 @@ multiBalanceReportAsText :: ReportOpts -> MultiBalanceReport -> String
|
||||
multiBalanceReportAsText opts r =
|
||||
printf "%s in %s:" typeStr (showDateSpan $ multiBalanceReportSpan r)
|
||||
++ "\n"
|
||||
++ renderBalanceReportTable tabl
|
||||
++ renderBalanceReportTable opts tabl
|
||||
where
|
||||
tabl = balanceReportAsTable opts r
|
||||
typeStr :: String
|
||||
@ -487,9 +488,9 @@ multiBalanceReportAsText opts r =
|
||||
-- | Given a table representing a multi-column balance report (for example,
|
||||
-- made using 'balanceReportAsTable'), render it in a format suitable for
|
||||
-- console output.
|
||||
renderBalanceReportTable :: Table String String MixedAmount -> String
|
||||
renderBalanceReportTable = unlines . trimborder . lines
|
||||
. render id (" " ++) showMixedAmountOneLineWithoutPrice
|
||||
renderBalanceReportTable :: ReportOpts -> Table String String MixedAmount -> String
|
||||
renderBalanceReportTable (ReportOpts { pretty_tables_ = pretty }) = unlines . trimborder . lines
|
||||
. render pretty id (" " ++) showMixedAmountOneLineWithoutPrice
|
||||
. align
|
||||
where
|
||||
trimborder = ("":) . (++[""]) . drop 1 . init . map (drop 1 . init)
|
||||
|
||||
@ -60,6 +60,7 @@ balanceviewmode BalanceView{..} = (defCommandMode $ bvmode : bvaliases) {
|
||||
,flagNone ["row-total","T"] (\opts -> setboolopt "row-total" opts) "show a row total column (in multicolumn reports)"
|
||||
,flagNone ["no-elide"] (\opts -> setboolopt "no-elide" opts) "don't squash boring parent accounts (in tree mode)"
|
||||
,flagReq ["format"] (\s opts -> Right $ setopt "format" s opts) "FORMATSTR" "use this custom line format (in simple reports)"
|
||||
,flagNone ["pretty-tables"] (\opts -> setboolopt "pretty-tables" opts) "use unicode when displaying tables"
|
||||
]
|
||||
,groupHidden = []
|
||||
,groupNamed = [generalflagsgroup1]
|
||||
@ -152,7 +153,7 @@ balanceviewReport BalanceView{..} CliOpts{reportopts_=ropts, rawopts_=raw} j = d
|
||||
)
|
||||
putStrLn bvtitle
|
||||
mapM_ putStrLn balanceclarification
|
||||
putStrLn $ renderBalanceReportTable totTabl
|
||||
putStrLn $ renderBalanceReportTable ropts totTabl
|
||||
where
|
||||
overwriteBalanceType =
|
||||
case reverse $ filter (`elem` ["change","cumulative","historical"]) $ map fst raw of
|
||||
|
||||
@ -9,26 +9,27 @@ import Hledger.Utils.String
|
||||
|
||||
-- | for simplicity, we assume that each cell is rendered
|
||||
-- on a single line
|
||||
render :: (rh -> String)
|
||||
render :: Bool -- ^ pretty tables
|
||||
-> (rh -> String)
|
||||
-> (ch -> String)
|
||||
-> (a -> String)
|
||||
-> Table rh ch a
|
||||
-> String
|
||||
render fr fc f (Table rh ch cells) =
|
||||
render pretty fr fc f (Table rh ch cells) =
|
||||
unlines $ [ bar SingleLine -- +--------------------------------------+
|
||||
, renderColumns sizes ch2
|
||||
, renderColumns pretty sizes ch2
|
||||
, bar DoubleLine -- +======================================+
|
||||
] ++
|
||||
(renderRs $ fmap renderR $ zipHeader [] cells $ fmap fr rh) ++
|
||||
[ bar SingleLine ] -- +--------------------------------------+
|
||||
where
|
||||
bar = concat . renderHLine sizes ch2
|
||||
bar = concat . renderHLine pretty 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
|
||||
renderR (cs,h) = renderColumns pretty sizes $ Group DoubleLine
|
||||
[ Header h
|
||||
, fmap fst $ zipHeader "" (map f cs) ch]
|
||||
rhStrings = map fr $ headerContents rh
|
||||
@ -36,38 +37,73 @@ render fr fc f (Table rh ch cells) =
|
||||
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
|
||||
where sep = renderHLine pretty sizes ch2 p
|
||||
|
||||
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 " || "
|
||||
|
||||
horizontalBar :: Bool -> Char
|
||||
horizontalBar pretty = if pretty then '─' else '-'
|
||||
|
||||
doubleHorizontalBar :: Bool -> Char
|
||||
doubleHorizontalBar pretty = if pretty then '═' else '='
|
||||
|
||||
-- | We stop rendering on the shortest list!
|
||||
renderColumns :: [Int] -- ^ max width for each column
|
||||
renderColumns :: Bool -- ^ pretty
|
||||
-> [Int] -- ^ max width for each column
|
||||
-> Header String
|
||||
-> String
|
||||
renderColumns is h = "| " ++ coreLine ++ " |"
|
||||
renderColumns pretty is h = leftBar pretty ++ coreLine ++ rightBar pretty
|
||||
where
|
||||
coreLine = concatMap helper $ flattenHeader $ zipHeader 0 is h
|
||||
helper = either hsep (uncurry padLeftWide)
|
||||
hsep :: Properties -> String
|
||||
hsep NoLine = " "
|
||||
hsep SingleLine = " | "
|
||||
hsep DoubleLine = " || "
|
||||
hsep SingleLine = midBar pretty
|
||||
hsep DoubleLine = doubleMidBar pretty
|
||||
|
||||
renderHLine :: [Int] -- ^ width specifications
|
||||
renderHLine :: Bool -- ^ pretty
|
||||
-> [Int] -- ^ width specifications
|
||||
-> Header String
|
||||
-> Properties
|
||||
-> [String]
|
||||
renderHLine _ _ NoLine = []
|
||||
renderHLine w h SingleLine = [renderHLine' w '-' h]
|
||||
renderHLine w h DoubleLine = [renderHLine' w '=' h]
|
||||
renderHLine _ _ _ NoLine = []
|
||||
renderHLine pretty w h SingleLine = [renderHLine' pretty SingleLine w (horizontalBar pretty) h]
|
||||
renderHLine pretty w h DoubleLine = [renderHLine' pretty DoubleLine w (doubleHorizontalBar pretty) h]
|
||||
|
||||
renderHLine' :: [Int] -> Char -> Header String -> String
|
||||
renderHLine' is sep h = [ '+', sep ] ++ coreLine ++ [sep, '+']
|
||||
doubleCross :: Bool -> String
|
||||
doubleCross pretty = if pretty then "╬" else "++"
|
||||
|
||||
doubleVerticalCross :: Bool -> String
|
||||
doubleVerticalCross pretty = if pretty then "╫" else "++"
|
||||
|
||||
cross :: Bool -> Char
|
||||
cross pretty = if pretty then '┼' else '+'
|
||||
|
||||
renderHLine' :: Bool -> Properties -> [Int] -> Char -> Header String -> String
|
||||
renderHLine' pretty prop is sep h = [ cross pretty, sep ] ++ coreLine ++ [sep, cross pretty]
|
||||
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]
|
||||
vsep SingleLine = sep : cross pretty : [sep]
|
||||
vsep DoubleLine = sep : cross' ++ [sep]
|
||||
cross' = case prop of
|
||||
DoubleLine -> doubleCross pretty
|
||||
_ -> doubleVerticalCross pretty
|
||||
|
||||
-- padLeft :: Int -> String -> String
|
||||
-- padLeft l s = padding ++ s
|
||||
|
||||
@ -41,6 +41,9 @@ txt, csv.
|
||||
`-o FILE --output-file=FILE`
|
||||
: write output to FILE. A file extension matching one of the above formats selects that format.
|
||||
|
||||
`--pretty-tables`
|
||||
: Use unicode to display prettier tables.
|
||||
|
||||
The balance command displays accounts and balances.
|
||||
It is hledger's most featureful and most useful command.
|
||||
|
||||
|
||||
13
tests/balance/pretty.test
Normal file
13
tests/balance/pretty.test
Normal file
@ -0,0 +1,13 @@
|
||||
hledger -f balance-multicol.journal balance --pretty-tables -M
|
||||
>>>
|
||||
Balance changes in 2012/12/01-2013/03/31:
|
||||
|
||||
║ 2012/12 2013/01 2013/02 2013/03
|
||||
═════════════════╬═════════════════════════════════════
|
||||
assets ║ 0 0 1 0
|
||||
assets:cash ║ 0 0 1 0
|
||||
assets:checking ║ 10 0 0 1
|
||||
─────────────────╫─────────────────────────────────────
|
||||
║ 10 0 2 1
|
||||
|
||||
>>>=0
|
||||
27
tests/balancesheet/pretty.test
Normal file
27
tests/balancesheet/pretty.test
Normal file
@ -0,0 +1,27 @@
|
||||
# 1.
|
||||
hledger -f - balancesheet -M --pretty-tables
|
||||
<<<
|
||||
2016/1/1
|
||||
assets 1
|
||||
b
|
||||
>>>
|
||||
Balance Sheet
|
||||
|
||||
║ 2016/01/31
|
||||
═════════════╬═════════════
|
||||
Assets ║
|
||||
─────────────╫─────────────
|
||||
assets ║ 1
|
||||
─────────────╫─────────────
|
||||
║ 1
|
||||
═════════════╬═════════════
|
||||
Liabilities ║
|
||||
─────────────╫─────────────
|
||||
─────────────╫─────────────
|
||||
║
|
||||
═════════════╬═════════════
|
||||
Total ║
|
||||
|
||||
|
||||
>>>2
|
||||
>>>= 0
|
||||
Loading…
Reference in New Issue
Block a user