reg: --width/-w option to adjust register overall output width

This commit is contained in:
Simon Michael 2012-12-21 21:56:11 +00:00
parent 58edc161c9
commit 552a15a1ba
5 changed files with 92 additions and 45 deletions

View File

@ -755,6 +755,11 @@ summary postings within each interval:
$ hledger register --monthly rent $ hledger register --monthly rent
$ hledger register --monthly -E food --depth 4 $ hledger register --monthly -E food --depth 4
The `--width`/`-w` option adjusts the width of the output. By default,
this is 80 characters. To allow more space for descriptions and account
names, use `-w` to increase the width to 120 characters, or `-wN` to set
any desired width (at least 50 recommended).
#### balance #### balance
The balance command displays accounts and their balances, indented to show the account hierarchy. The balance command displays accounts and their balances, indented to show the account hierarchy.

View File

@ -38,7 +38,6 @@ module Hledger.Data.Posting (
sumPostings, sumPostings,
-- * rendering -- * rendering
showPosting, showPosting,
showPostingForRegister,
-- * misc. -- * misc.
showComment, showComment,
tests_Hledger_Data_Posting tests_Hledger_Data_Posting
@ -95,20 +94,6 @@ showPosting p@Posting{paccount=a,pamount=amt,ptype=t} =
showComment :: String -> String showComment :: String -> String
showComment s = if null s then "" else " ;" ++ s showComment s = if null s then "" else " ;" ++ s
-- XXX refactor
showPostingForRegister :: Posting -> String
showPostingForRegister (Posting{paccount=a,pamount=amt,ptype=t}) =
concatTopPadded [showaccountname a ++ " ", showamount amt]
where
ledger3ishlayout = False
acctnamewidth = if ledger3ishlayout then 25 else 22
showaccountname = printf ("%-"++(show acctnamewidth)++"s") . bracket . elideAccountName width
(bracket,width) = case t of
BalancedVirtualPosting -> (\s -> "["++s++"]", acctnamewidth-2)
VirtualPosting -> (\s -> "("++s++")", acctnamewidth-2)
_ -> (id,acctnamewidth)
showamount = padleft 12 . showMixedAmountWithoutPrice
isReal :: Posting -> Bool isReal :: Posting -> Bool
isReal p = ptype p == RegularPosting isReal p = ptype p == RegularPosting

View File

@ -223,8 +223,10 @@ registerFromString :: String -> IO String
registerFromString s = do registerFromString s = do
d <- getCurrentDay d <- getCurrentDay
j <- readJournal' s j <- readJournal' s
return $ postingsReportAsText opts $ postingsReport opts (queryFromOpts d opts) j return $ postingsReportAsText opts $ postingsReport ropts (queryFromOpts d ropts) j
where opts = defreportopts{empty_=True} where
ropts = defreportopts{empty_=True}
opts = defcliopts{reportopts_=ropts}
-- | Return a similarity measure, from 0 to 1, for two strings. -- | Return a similarity measure, from 0 to 1, for two strings.
-- This is Simon White's letter pairs algorithm from -- This is Simon White's letter pairs algorithm from

View File

@ -7,7 +7,7 @@ Command-line options for the hledger program, and option-parsing utilities.
module Hledger.Cli.Options module Hledger.Cli.Options
where where
import Control.Exception as C import qualified Control.Exception as C
import Data.List import Data.List
import Data.List.Split import Data.List.Split
import Data.Maybe import Data.Maybe
@ -19,7 +19,7 @@ import System.Console.CmdArgs.Text
import System.Directory import System.Directory
import System.Environment import System.Environment
import Test.HUnit import Test.HUnit
import Text.ParserCombinators.Parsec import Text.ParserCombinators.Parsec as P
import Text.Printf import Text.Printf
import Hledger import Hledger
@ -189,7 +189,7 @@ accountsmode = (commandmode ["balance","bal","accounts"]) {
,modeGroupFlags = Group { ,modeGroupFlags = Group {
groupUnnamed = [ groupUnnamed = [
flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show full account names, unindented" flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show full account names, unindented"
,flagReq ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "with --flat, omit this many leading account name components" ,flagReq ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "with --flat, omit this many leading account name components"
,flagReq ["format"] (\s opts -> Right $ setopt "format" s opts) "FORMATSTR" "use this custom line format" ,flagReq ["format"] (\s opts -> Right $ setopt "format" s opts) "FORMATSTR" "use this custom line format"
,flagNone ["no-elide"] (\opts -> setboolopt "no-elide" opts) "no eliding at all, stronger than --empty" ,flagNone ["no-elide"] (\opts -> setboolopt "no-elide" opts) "no eliding at all, stronger than --empty"
,flagNone ["no-total"] (\opts -> setboolopt "no-total" opts) "don't show the final total" ,flagNone ["no-total"] (\opts -> setboolopt "no-total" opts) "don't show the final total"
@ -213,7 +213,9 @@ postingsmode = (commandmode ["register","postings"]) {
modeHelp = "(or postings) show matched postings and running total" modeHelp = "(or postings) show matched postings and running total"
,modeArgs = ([], Just commandargsflag) ,modeArgs = ([], Just commandargsflag)
,modeGroupFlags = Group { ,modeGroupFlags = Group {
groupUnnamed = [] groupUnnamed = [
flagOpt (show defaultWidthWithFlag) ["width","w"] (\s opts -> Right $ setopt "width" s opts) "N" "increase or set the output width (default: 80)"
]
,groupHidden = [] ,groupHidden = []
,groupNamed = [(generalflagstitle, generalflags1)] ,groupNamed = [(generalflagstitle, generalflags1)]
} }
@ -293,6 +295,7 @@ data CliOpts = CliOpts {
,alias_ :: [String] ,alias_ :: [String]
,debug_ :: Bool ,debug_ :: Bool
,no_new_accounts_ :: Bool -- add ,no_new_accounts_ :: Bool -- add
,width_ :: Maybe String -- register
,reportopts_ :: ReportOpts ,reportopts_ :: ReportOpts
} deriving (Show) } deriving (Show)
@ -305,6 +308,7 @@ defcliopts = CliOpts
def def
def def
def def
def
instance Default CliOpts where def = defcliopts instance Default CliOpts where def = defcliopts
@ -322,6 +326,7 @@ toCliOpts rawopts = do
,alias_ = map stripquotes $ listofstringopt "alias" rawopts ,alias_ = map stripquotes $ listofstringopt "alias" rawopts
,debug_ = boolopt "debug" rawopts ,debug_ = boolopt "debug" rawopts
,no_new_accounts_ = boolopt "no-new-accounts" rawopts -- add ,no_new_accounts_ = boolopt "no-new-accounts" rawopts -- add
,width_ = maybestringopt "width" rawopts -- register
,reportopts_ = defreportopts { ,reportopts_ = defreportopts {
begin_ = maybesmartdateopt d "begin" rawopts begin_ = maybesmartdateopt d "begin" rawopts
,end_ = maybesmartdateopt d "end" rawopts ,end_ = maybesmartdateopt d "end" rawopts
@ -441,10 +446,13 @@ checkCliOpts opts@CliOpts{reportopts_=ropts} = do
case formatFromOpts ropts of case formatFromOpts ropts of
Left err -> optserror $ "could not parse format option: "++err Left err -> optserror $ "could not parse format option: "++err
Right _ -> return () Right _ -> return ()
case widthFromOpts opts of
Left err -> optserror $ "could not parse width option: "++err
Right _ -> return ()
return opts return opts
-- | Parse any format option provided, possibly raising an error, or get -- | Parse the format option if provided, possibly returning an error,
-- the default value. -- otherwise get the default value.
formatFromOpts :: ReportOpts -> Either String [FormatString] formatFromOpts :: ReportOpts -> Either String [FormatString]
formatFromOpts = maybe (Right defaultBalanceFormatString) parseFormatString . format_ formatFromOpts = maybe (Right defaultBalanceFormatString) parseFormatString . format_
@ -457,6 +465,35 @@ defaultBalanceFormatString = [
, FormatField True Nothing Nothing AccountField , FormatField True Nothing Nothing AccountField
] ]
data OutputWidth = TotalWidth Width | FieldWidths [Width] deriving Show
data Width = Width Int | Auto deriving Show
defaultWidth = 80
defaultWidthWithFlag = 120
-- | Parse the width option if provided, possibly returning an error,
-- otherwise get the default value.
widthFromOpts :: CliOpts -> Either String OutputWidth
widthFromOpts CliOpts{width_=Nothing} = Right $ TotalWidth $ Width defaultWidth
widthFromOpts CliOpts{width_=Just ""} = Right $ TotalWidth $ Width defaultWidthWithFlag
widthFromOpts CliOpts{width_=Just s} = parseWidth s
parseWidth :: String -> Either String OutputWidth
parseWidth s = case (runParser outputwidth () "(unknown)") s of
Left e -> Left $ show e
Right x -> Right x
outputwidth :: GenParser Char st OutputWidth
outputwidth =
try (do w <- width
ws <- many1 (char ',' >> width)
return $ FieldWidths $ w:ws)
<|> TotalWidth `fmap` width
width :: GenParser Char st Width
width = (string "auto" >> return Auto)
<|> (Width . read) `fmap` many1 digit
-- | Get the (tilde-expanded, absolute) journal file path from options, an environment variable, or a default. -- | Get the (tilde-expanded, absolute) journal file path from options, an environment variable, or a default.
journalFilePathFromOpts :: CliOpts -> IO String journalFilePathFromOpts :: CliOpts -> IO String
journalFilePathFromOpts opts = do journalFilePathFromOpts opts = do

View File

@ -24,12 +24,12 @@ import Hledger.Cli.Options
-- | Print a (posting) register report. -- | Print a (posting) register report.
register :: CliOpts -> Journal -> IO () register :: CliOpts -> Journal -> IO ()
register CliOpts{reportopts_=ropts} j = do register opts@CliOpts{reportopts_=ropts} j = do
d <- getCurrentDay d <- getCurrentDay
putStr $ postingsReportAsText ropts $ postingsReport ropts (queryFromOpts d ropts) j putStr $ postingsReportAsText opts $ postingsReport ropts (queryFromOpts d ropts) j
-- | Render a register report as plain text suitable for console output. -- | Render a register report as plain text suitable for console output.
postingsReportAsText :: ReportOpts -> PostingsReport -> String postingsReportAsText :: CliOpts -> PostingsReport -> String
postingsReportAsText opts = unlines . map (postingsReportItemAsText opts) . snd postingsReportAsText opts = unlines . map (postingsReportItemAsText opts) . snd
tests_postingsReportAsText = [ tests_postingsReportAsText = [
@ -38,32 +38,50 @@ tests_postingsReportAsText = [
j <- readJournal' j <- readJournal'
"2009/01/01 * медвежья шкура\n расходы:покупки 100\n актив:наличные\n" "2009/01/01 * медвежья шкура\n расходы:покупки 100\n актив:наличные\n"
let opts = defreportopts let opts = defreportopts
(postingsReportAsText opts $ postingsReport opts (queryFromOpts (parsedate "2008/11/26") opts) j) `is` unlines (postingsReportAsText defcliopts $ postingsReport opts (queryFromOpts (parsedate "2008/11/26") opts) j) `is` unlines
["2009/01/01 медвежья шкура расходы:покупки 100 100" ["2009/01/01 медвежья шкура расходы:покупки 100 100"
," актив:наличные -100 0"] ," актив:наличные -100 0"]
] ]
-- | Render one register report line item as plain text. Eg: -- | Render one register report line item as plain text. Layout is like so:
-- @ -- @
-- date (10) description (20) account (22) amount (11) balance (12) -- <----------------------------- width (default: 80) ----------------------------->
-- DDDDDDDDDD dddddddddddddddddddd aaaaaaaaaaaaaaaaaaaaaa AAAAAAAAAAA AAAAAAAAAAAA -- date (10) description (50%) account (50%) amount (12) balance (12)
-- ^ displayed for first postings^ -- DDDDDDDDDD dddddddddddddddddddd aaaaaaaaaaaaaaaaaaa AAAAAAAAAAAA AAAAAAAAAAAA
-- only, otherwise blank --
-- date and description are shown for the first posting of a transaction only.
-- @ -- @
postingsReportItemAsText :: ReportOpts -> PostingsReportItem -> String postingsReportItemAsText :: CliOpts -> PostingsReportItem -> String
postingsReportItemAsText _ (dd, p, b) = concatTopPadded [datedesc, pstr, " ", bal] postingsReportItemAsText opts (dd, p, b) =
concatTopPadded [date, " ", desc, " ", acct, " ", amt, " ", bal]
where where
datedesc = case dd of Nothing -> replicate datedescwidth ' ' totalwidth = case widthFromOpts opts of
Just (da, de) -> printf "%s %s " date desc Left _ -> defaultWidth -- shouldn't happen
where Right (TotalWidth (Width w)) -> w
date = showDate da Right (TotalWidth Auto) -> defaultWidth -- XXX
desc = printf ("%-"++(show descwidth)++"s") $ elideRight descwidth de :: String Right (FieldWidths _) -> defaultWidth -- XXX
where datewidth = 10
descwidth = datedescwidth - datewidth - 2 amtwidth = 12
datedescwidth = 32 balwidth = 12
datewidth = 10 remaining = totalwidth - (datewidth + 2 + 2 + amtwidth + 2 + balwidth)
pstr = showPostingForRegister p (descwidth, acctwidth) | even r = (r', r')
bal = padleft 12 (showMixedAmountWithoutPrice b) | otherwise = (r', r'+1)
where r = remaining - 2
r' = r `div` 2
(date, desc) = case dd of
Just (da, de) -> (printf ("%-"++show datewidth++"s") (showDate da)
,printf ("%-"++show descwidth++"s") (take descwidth $ elideRight descwidth de :: String)
)
Nothing -> (replicate datewidth ' ', replicate descwidth ' ')
acct = printf ("%-"++(show acctwidth)++"s") a
where
a = bracket $ elideAccountName awidth $ paccount p
(bracket, awidth) = case ptype p of
BalancedVirtualPosting -> (\s -> "["++s++"]", acctwidth-2)
VirtualPosting -> (\s -> "("++s++")", acctwidth-2)
_ -> (id,acctwidth)
amt = padleft amtwidth $ showMixedAmountWithoutPrice $ pamount p
bal = padleft balwidth $ showMixedAmountWithoutPrice b
-- XXX -- XXX
-- showPostingWithBalanceForVty showtxninfo p b = postingsReportItemAsText defreportopts $ mkpostingsReportItem showtxninfo p b -- showPostingWithBalanceForVty showtxninfo p b = postingsReportItemAsText defreportopts $ mkpostingsReportItem showtxninfo p b