diff -Nru haskell-hledger-0.21.3/Hledger/Cli/Add.hs haskell-hledger-0.22/Hledger/Cli/Add.hs --- haskell-hledger-0.21.3/Hledger/Cli/Add.hs 2013-06-23 23:53:04.000000000 +0000 +++ haskell-hledger-0.22/Hledger/Cli/Add.hs 2013-12-14 03:32:24.000000000 +0000 @@ -264,7 +264,7 @@ journalAddTransaction j@Journal{jtxns=ts} opts t = do let f = journalFilePath j appendToJournalFileOrStdout f $ showTransaction t - when (debug_ opts) $ do + when (debug_ opts > 0) $ do putStrLn $ printf "\nAdded transaction to %s:" f putStrLn =<< registerFromString (show t) return j{jtxns=ts++[t]} diff -Nru haskell-hledger-0.21.3/Hledger/Cli/Balance.hs haskell-hledger-0.22/Hledger/Cli/Balance.hs --- haskell-hledger-0.21.3/Hledger/Cli/Balance.hs 2013-06-23 23:53:04.000000000 +0000 +++ haskell-hledger-0.22/Hledger/Cli/Balance.hs 2013-12-14 03:32:24.000000000 +0000 @@ -1,10 +1,19 @@ {-| -A ledger-compatible @balance@ command. +A ledger-compatible @balance@ command, with additional support for +multi-column reports. -ledger's balance command is easy to use but not easy to describe -precisely. In the examples below we'll use sample.journal, which has the -following account tree: +Here is a description/specification for the balance command. See also +"Hledger.Reports" -> \"Balance reports\". + + +/Basic balance report/ + +With no reporting interval (@--monthly@ etc.), hledger's balance +command emulates ledger's, showing accounts indented according to +hierarchy, along with their total amount posted (including subaccounts). + +Here's an example. With @data/sample.journal@, which defines the following account tree: @ assets @@ -22,10 +31,7 @@ debts @ -The balance command shows accounts with their aggregate balances. -Subaccounts are displayed indented below their parent. Each balance is the -sum of any transactions in that account plus any balances from -subaccounts: +the basic @balance@ command gives this output: @ $ hledger -f sample.journal balance @@ -39,16 +45,44 @@ $-1 gifts $-1 salary $1 liabilities:debts +-------------------- + 0 @ -Usually, the non-interesting accounts are elided or omitted. Above, -@checking@ is omitted because it has no subaccounts and a zero balance. -@bank@ is elided because it has only a single displayed subaccount -(@saving@) and it would be showing the same balance as that ($1). Ditto -for @liabilities@. We will return to this in a moment. +Subaccounts are displayed indented below their parent. Only the account leaf name (the final part) is shown. +(With @--flat@, account names are shown in full and unindented.) + +Each account's \"balance\" is the sum of postings in that account and any subaccounts during the report period. +When the report period includes all transactions, this is equivalent to the account's current balance. + +The overall total of the highest-level displayed accounts is shown below the line. +(The @--no-total/-N@ flag prevents this.) + +/Eliding and omitting/ -The --depth argument can be used to limit the depth of the balance report. -So, to see just the top level accounts: +Accounts which have a zero balance, and no non-zero subaccount +balances, are normally omitted from the report. +(The @--empty/-E@ flag forces such accounts to be displayed.) +Eg, above @checking@ is omitted because it has a zero balance and no subaccounts. + +Accounts which have a single subaccount also being displayed, with the same balance, +are normally elided into the subaccount's line. +(The @--no-elide@ flag prevents this.) +Eg, above @bank@ is elided to @bank:saving@ because it has only a +single displayed subaccount (@saving@) and their balance is the same +($1). Similarly, @liabilities@ is elided to @liabilities:debts@. + +/Date limiting/ + +The default report period is that of the whole journal, including all +known transactions. The @--begin\/-b@, @--end\/-e@, @--period\/-p@ +options or @date:@/@date2:@ patterns can be used to report only +on transactions before and/or after specified dates. + +/Depth limiting/ + +The @--depth@ option can be used to limit the depth of the balance report. +Eg, to see just the top level accounts (still including their subaccount balances): @ $ hledger -f sample.journal balance --depth 1 @@ -56,14 +90,15 @@ $2 expenses $-2 income $1 liabilities +-------------------- + 0 @ -This time liabilities has no displayed subaccounts (due to --depth) and -is not elided. +/Account limiting/ -With one or more account pattern arguments, the balance command shows -accounts whose name matches one of the patterns, plus their parents -(elided) and subaccounts. So with the pattern o we get: +With one or more account pattern arguments, the report is restricted +to accounts whose name matches one of the patterns, plus their parents +and subaccounts. Eg, adding the pattern @o@ to the first example gives: @ $ hledger -f sample.journal balance o @@ -75,33 +110,142 @@ $-1 @ -The o pattern matched @food@ and @income@, so they are shown. Unmatched -parents of matched accounts are also shown (elided) for context (@expenses@). +* The @o@ pattern matched @food@ and @income@, so they are shown. + +* @food@'s parent (@expenses@) is shown even though the pattern didn't + match it, to clarify the hierarchy. The usual eliding rules cause it to be elided here. + +* @income@'s subaccounts are also shown. + +/Multi-column balance report/ + +hledger's balance command will show multiple columns when a reporting +interval is specified (eg with @--monthly@), one column for each sub-period. + +There are three kinds of multi-column balance report, indicated by the heading: + +* A \"period balance\" (or \"flow\") report (the default) shows the change of account + balance in each period, which is equivalent to the sum of postings in each + period. Here, checking's balance increased by 10 in Feb: + + > Change of balance (flow): + > + > Jan Feb Mar + > assets:checking 20 10 -5 + +* A \"cumulative balance\" report (with @--cumulative@) shows the accumulated ending balance + across periods, starting from zero at the report's start date. + Here, 30 is the sum of checking postings during Jan and Feb: + + > Ending balance (cumulative): + > + > Jan Feb Mar + > assets:checking 20 30 25 + +* A \"historical balance\" report (with @--historical/-H@) also shows ending balances, + but it includes the starting balance from any postings before the report start date. + Here, 130 is the balance from all checking postings at the end of Feb, including + pre-Jan postings which created a starting balance of 100: + + > Ending balance (historical): + > + > Jan Feb Mar + > assets:checking 120 130 125 + +/Eliding and omitting, 2/ + +Here's a (imperfect?) specification for the eliding/omitting behaviour: + +* Each account is normally displayed on its own line. + +* An account less deep than the report's max depth, with just one +interesting subaccount, and the same balance as the subaccount, is +non-interesting, and prefixed to the subaccount's line, unless +@--no-elide@ is in effect. + +* An account with a zero inclusive balance and less than two interesting +subaccounts is not displayed at all, unless @--empty@ is in effect. + +* Multi-column balance reports show full account names with no eliding + (like @--flat@). Accounts (and periods) are omitted as described below. + +/Which accounts to show in balance reports/ -Also, the balance report shows the total of all displayed accounts, when -that is non-zero. Here, it is displayed because the accounts shown add up -to $-1. - -Also, non-interesting accounts may be elided. Here's an imperfect -description of the ledger balance command's eliding behaviour: -\"Interesting\" accounts are displayed on their own line. An account less -deep than the report's max depth, with just one interesting subaccount, -and the same balance as the subaccount, is non-interesting, and prefixed -to the subaccount's line, unless (hledger's) --no-elide is in effect. -An account with a zero inclusive balance and less than two interesting -subaccounts is not displayed at all, unless --empty is in effect. +By default: + +* single-column: accounts with non-zero balance in report period. + (With @--flat@: accounts with non-zero balance and postings.) + +* periodic: accounts with postings and non-zero period balance in any period + +* cumulative: accounts with non-zero cumulative balance in any period + +* historical: accounts with non-zero historical balance in any period + +With @-E/--empty@: + +* single-column: accounts with postings in report period + +* periodic: accounts with postings in report period + +* cumulative: accounts with postings in report period + +* historical: accounts with non-zero starting balance + + accounts with postings in report period + +/Which periods (columns) to show in balance reports/ + +An empty period/column is one where no report account has any postings. +A zero period/column is one where no report account has a non-zero period balance. + +Currently, + +by default: + +* single-column: N/A + +* periodic: all periods within the overall report period, + except for leading and trailing empty periods + +* cumulative: all periods within the overall report period, + except for leading and trailing empty periods + +* historical: all periods within the overall report period, + except for leading and trailing empty periods + +With @-E/--empty@: + +* single-column: N/A + +* periodic: all periods within the overall report period + +* cumulative: all periods within the overall report period + +* historical: all periods within the overall report period + +/What to show in empty cells/ + +An empty periodic balance report cell is one which has no corresponding postings. +An empty cumulative/historical balance report cell is one which has no correponding +or prior postings, ie the account doesn't exist yet. +Currently, empty cells show 0. -} module Hledger.Cli.Balance ( balance - ,accountsReportAsText + ,balanceReportAsText + ,periodBalanceReportAsText + ,cumulativeBalanceReportAsText + ,historicalBalanceReportAsText ,tests_Hledger_Cli_Balance ) where import Data.List import Data.Maybe import Test.HUnit +import Text.Tabular +import Text.Tabular.AsciiArt import Hledger import Prelude hiding (putStr) @@ -114,17 +258,24 @@ balance :: CliOpts -> Journal -> IO () balance CliOpts{reportopts_=ropts} j = do d <- getCurrentDay - let lines = case formatFromOpts ropts of - Left err -> [err] - Right _ -> accountsReportAsText ropts $ accountsReport ropts (queryFromOpts d ropts) j - putStr $ unlines lines - --- | Render a balance report as plain text suitable for console output. -accountsReportAsText :: ReportOpts -> AccountsReport -> [String] -accountsReportAsText opts (items, total) = concat lines ++ t - where + let output = + case formatFromOpts ropts of + Left err -> [err] + Right _ -> + case (intervalFromOpts ropts, balancetype_ ropts) of + (NoInterval,_) -> balanceReportAsText ropts $ balanceReport ropts (queryFromOpts d ropts) j + (_,PeriodBalance) -> periodBalanceReportAsText ropts $ periodBalanceReport ropts (queryFromOpts d ropts) j + (_,CumulativeBalance) -> cumulativeBalanceReportAsText ropts $ cumulativeOrHistoricalBalanceReport ropts (queryFromOpts d ropts) j + (_,HistoricalBalance) -> historicalBalanceReportAsText ropts $ cumulativeOrHistoricalBalanceReport ropts (queryFromOpts d ropts) j + + putStr $ unlines output + +-- | Render an old-style single-column balance report as plain text. +balanceReportAsText :: ReportOpts -> BalanceReport -> [String] +balanceReportAsText opts ((items, total)) = concat lines ++ t + where lines = case formatFromOpts opts of - Right f -> map (accountsReportItemAsText opts f) items + Right f -> map (balanceReportItemAsText opts f) items Left err -> [[err]] t = if no_total_ opts then [] @@ -133,13 +284,13 @@ ,padleft 20 $ showMixedAmountWithoutPrice total ] -tests_accountsReportAsText = [ - "accountsReportAsText" ~: do +tests_balanceReportAsText = [ + "balanceReportAsText" ~: do -- "unicode in balance layout" ~: do j <- readJournal' "2009/01/01 * медвежья шкура\n расходы:покупки 100\n актив:наличные\n" let opts = defreportopts - accountsReportAsText opts (accountsReport opts (queryFromOpts (parsedate "2008/11/26") opts) j) `is` + balanceReportAsText opts (balanceReport opts (queryFromOpts (parsedate "2008/11/26") opts) j) `is` [" -100 актив:наличные" ," 100 расходы:покупки" ,"--------------------" @@ -157,27 +308,27 @@ EUR -1 b USD -1 ; Account 'b' has two amounts. The account name is printed on the last line. -} --- | Render one balance report line item as plain text. -accountsReportItemAsText :: ReportOpts -> [FormatString] -> AccountsReportItem -> [String] -accountsReportItemAsText opts format (_, accountName, depth, Mixed amounts) = +-- | Render one balance report line item as plain text suitable for console output. +balanceReportItemAsText :: ReportOpts -> [FormatString] -> BalanceReportItem -> [String] +balanceReportItemAsText opts format (_, accountName, depth, Mixed amounts) = -- 'amounts' could contain several quantities of the same commodity with different price. -- In order to combine them into single value (which is expected) we take the first price and -- use it for the whole mixed amount. This could be suboptimal. XXX let Mixed normAmounts = normaliseMixedAmountPreservingFirstPrice (Mixed amounts) in case normAmounts of [] -> [] - [a] -> [formatAccountsReportItem opts (Just accountName) depth a format] + [a] -> [formatBalanceReportItem opts (Just accountName) depth a format] (as) -> multiline as where multiline :: [Amount] -> [String] multiline [] = [] - multiline [a] = [formatAccountsReportItem opts (Just accountName) depth a format] - multiline (a:as) = (formatAccountsReportItem opts Nothing depth a format) : multiline as + multiline [a] = [formatBalanceReportItem opts (Just accountName) depth a format] + multiline (a:as) = (formatBalanceReportItem opts Nothing depth a format) : multiline as -formatAccountsReportItem :: ReportOpts -> Maybe AccountName -> Int -> Amount -> [FormatString] -> String -formatAccountsReportItem _ _ _ _ [] = "" -formatAccountsReportItem opts accountName depth amount (fmt:fmts) = - s ++ (formatAccountsReportItem opts accountName depth amount fmts) +formatBalanceReportItem :: ReportOpts -> Maybe AccountName -> Int -> Amount -> [FormatString] -> String +formatBalanceReportItem _ _ _ _ [] = "" +formatBalanceReportItem opts accountName depth amount (fmt:fmts) = + s ++ (formatBalanceReportItem opts accountName depth amount fmts) where s = case fmt of FormatLiteral l -> l @@ -192,5 +343,75 @@ TotalField -> formatValue ljust min max $ showAmountWithoutPrice total _ -> "" +-- | Render a multi-column period balance report as plain text suitable for console output. +periodBalanceReportAsText :: ReportOpts -> MultiBalanceReport -> [String] +periodBalanceReportAsText opts (MultiBalanceReport (colspans, items, coltotals)) = + (["Change of balance (flow):"] ++) $ + trimborder $ lines $ + render + id + ((" "++) . showDateSpan) + showMixedAmountWithoutPrice + $ Table + (Group NoLine $ map (Header . padright acctswidth) accts) + (Group NoLine $ map Header colspans) + (map snd items') + +----+ + totalrow + where + trimborder = ("":) . (++[""]) . drop 1 . init . map (drop 1 . init) + items' | empty_ opts = items + | otherwise = items -- dbg "2" $ filter (any (not . isZeroMixedAmount) . snd) $ dbg "1" items + accts = map renderacct items' + renderacct ((a,a',_i),_) + | flat_ opts = a + | otherwise = a' -- replicate i ' ' ++ + acctswidth = maximum $ map length $ accts + totalrow | no_total_ opts = row "" [] + | otherwise = row "" coltotals + +-- | Render a multi-column cumulative balance report as plain text suitable for console output. +cumulativeBalanceReportAsText :: ReportOpts -> MultiBalanceReport -> [String] +cumulativeBalanceReportAsText opts (MultiBalanceReport (colspans, items, coltotals)) = + (["Ending balance (cumulative):"] ++) $ + trimborder $ lines $ + render id ((" "++) . maybe "" (showDate . prevday) . spanEnd) showMixedAmountWithoutPrice $ + addtotalrow $ + Table + (Group NoLine $ map (Header . padright acctswidth) accts) + (Group NoLine $ map Header colspans) + (map snd items) + where + trimborder = ("":) . (++[""]) . drop 1 . init . map (drop 1 . init) + accts = map renderacct items + renderacct ((a,a',_),_) + | flat_ opts = a + | otherwise = a' -- replicate i ' ' ++ + acctswidth = maximum $ map length $ accts + addtotalrow | no_total_ opts = id + | otherwise = (+----+ row "" coltotals) + +-- | Render a multi-column historical balance report as plain text suitable for console output. +historicalBalanceReportAsText :: ReportOpts -> MultiBalanceReport -> [String] +historicalBalanceReportAsText opts (MultiBalanceReport (colspans, items, coltotals)) = + (["Ending balance (historical):"] ++) $ + trimborder $ lines $ + render id ((" "++) . maybe "" (showDate . prevday) . spanEnd) showMixedAmountWithoutPrice $ + addtotalrow $ + Table + (Group NoLine $ map (Header . padright acctswidth) accts) + (Group NoLine $ map Header colspans) + (map snd items) + where + trimborder = ("":) . (++[""]) . drop 1 . init . map (drop 1 . init) + accts = map renderacct items + renderacct ((a,a',_),_) + | flat_ opts = a + | otherwise = a' -- replicate i ' ' ++ + acctswidth = maximum $ map length $ accts + addtotalrow | no_total_ opts = id + | otherwise = (+----+ row "" coltotals) + + tests_Hledger_Cli_Balance = TestList - tests_accountsReportAsText + tests_balanceReportAsText diff -Nru haskell-hledger-0.21.3/Hledger/Cli/Balancesheet.hs haskell-hledger-0.22/Hledger/Cli/Balancesheet.hs --- haskell-hledger-0.21.3/Hledger/Cli/Balancesheet.hs 2013-06-23 23:53:04.000000000 +0000 +++ haskell-hledger-0.22/Hledger/Cli/Balancesheet.hs 2013-12-14 03:32:24.000000000 +0000 @@ -25,18 +25,15 @@ -- let lines = case formatFromOpts ropts of Left err, Right ... d <- getCurrentDay let q = queryFromOpts d (withoutBeginDate ropts) - assetreport@(_,assets) = accountsReport ropts (And [q, journalAssetAccountQuery j]) j - liabilityreport@(_,liabilities) = accountsReport ropts (And [q, journalLiabilityAccountQuery j]) j - equityreport@(_,equity) = accountsReport ropts (And [q, journalEquityAccountQuery j]) j - total = assets + liabilities + equity + assetreport@(_,assets) = balanceReport ropts (And [q, journalAssetAccountQuery j]) j + liabilityreport@(_,liabilities) = balanceReport ropts (And [q, journalLiabilityAccountQuery j]) j + total = assets + liabilities LT.putStr $ [lt|Balance Sheet Assets: -#{unlines $ accountsReportAsText ropts assetreport} +#{unlines $ balanceReportAsText ropts assetreport} Liabilities: -#{unlines $ accountsReportAsText ropts liabilityreport} -Equity: -#{unlines $ accountsReportAsText ropts equityreport} +#{unlines $ balanceReportAsText ropts liabilityreport} Total: -------------------- diff -Nru haskell-hledger-0.21.3/Hledger/Cli/Cashflow.hs haskell-hledger-0.22/Hledger/Cli/Cashflow.hs --- haskell-hledger-0.21.3/Hledger/Cli/Cashflow.hs 2013-06-23 23:53:04.000000000 +0000 +++ haskell-hledger-0.22/Hledger/Cli/Cashflow.hs 2013-12-14 03:32:24.000000000 +0000 @@ -27,27 +27,22 @@ cashflow CliOpts{reportopts_=ropts} j = do -- let lines = case formatFromOpts ropts of Left err, Right ... d <- getCurrentDay - let q = queryFromOpts d (withoutBeginDate ropts) - cashreport@(_,total) = accountsReport ropts (And [q, journalCashAccountQuery j]) j - -- operatingreport@(_,operating) = accountsReport ropts (And [q, journalOperatingAccountMatcher j]) j - -- investingreport@(_,investing) = accountsReport ropts (And [q, journalInvestingAccountMatcher j]) j - -- financingreport@(_,financing) = accountsReport ropts (And [q, journalFinancingAccountMatcher j]) j + let q = queryFromOpts d ropts + cashreport@(_,total) = balanceReport ropts (And [q, journalCashAccountQuery j]) j + -- operatingreport@(_,operating) = balanceReport ropts (And [q, journalOperatingAccountMatcher j]) j + -- investingreport@(_,investing) = balanceReport ropts (And [q, journalInvestingAccountMatcher j]) j + -- financingreport@(_,financing) = balanceReport ropts (And [q, journalFinancingAccountMatcher j]) j -- total = operating + investing + financing LT.putStr $ [lt|Cashflow Statement Cash flows: -#{unlines $ accountsReportAsText ropts cashreport} +#{unlines $ balanceReportAsText ropts cashreport} Total: -------------------- #{padleft 20 $ showMixedAmountWithoutPrice total} |] -withoutBeginDate :: ReportOpts -> ReportOpts -withoutBeginDate ropts@ReportOpts{..} = ropts{begin_=Nothing, period_=p} - where p = case period_ of Nothing -> Nothing - Just (i, DateSpan _ e) -> Just (i, DateSpan Nothing e) - tests_Hledger_Cli_Cashflow = TestList [ ] diff -Nru haskell-hledger-0.21.3/Hledger/Cli/Incomestatement.hs haskell-hledger-0.22/Hledger/Cli/Incomestatement.hs --- haskell-hledger-0.21.3/Hledger/Cli/Incomestatement.hs 2013-06-23 23:53:04.000000000 +0000 +++ haskell-hledger-0.22/Hledger/Cli/Incomestatement.hs 2013-12-14 03:32:24.000000000 +0000 @@ -23,15 +23,15 @@ incomestatement CliOpts{reportopts_=ropts} j = do d <- getCurrentDay let q = queryFromOpts d ropts - incomereport@(_,income) = accountsReport ropts (And [q, journalIncomeAccountQuery j]) j - expensereport@(_,expenses) = accountsReport ropts (And [q, journalExpenseAccountQuery j]) j + incomereport@(_,income) = balanceReport ropts (And [q, journalIncomeAccountQuery j]) j + expensereport@(_,expenses) = balanceReport ropts (And [q, journalExpenseAccountQuery j]) j total = income + expenses LT.putStr $ [lt|Income Statement Revenues: -#{unlines $ accountsReportAsText ropts incomereport} +#{unlines $ balanceReportAsText ropts incomereport} Expenses: -#{unlines $ accountsReportAsText ropts expensereport} +#{unlines $ balanceReportAsText ropts expensereport} Total: -------------------- diff -Nru haskell-hledger-0.21.3/Hledger/Cli/Main.hs haskell-hledger-0.22/Hledger/Cli/Main.hs --- haskell-hledger-0.21.3/Hledger/Cli/Main.hs 2013-06-23 23:53:04.000000000 +0000 +++ haskell-hledger-0.22/Hledger/Cli/Main.hs 2013-12-14 03:32:24.000000000 +0000 @@ -41,6 +41,8 @@ import Control.Monad import Data.List import Safe +import System.Console.CmdArgs.Explicit (modeHelp) +-- import System.Console.CmdArgs.Helper import System.Environment import System.Exit import System.Process @@ -66,62 +68,110 @@ main :: IO () main = do + + -- Choose and run the appropriate internal or external command based + -- on the raw command-line arguments, cmdarg's interpretation of + -- same, and hledger-* executables in the user's PATH. A somewhat + -- complex mishmash of cmdargs and custom processing, hence all the + -- debugging support and tests. See also Hledger.Cli.Options and + -- command-line.test. + + -- some preliminary (imperfect) argument parsing to supplement cmdargs args <- getArgs + let + args' = moveFlagsAfterCommand args + isFlag = ("-" `isPrefixOf`) + isNonEmptyNonFlag s = not (isFlag s) && not (null s) + rawcmd = headDef "" $ takeWhile isNonEmptyNonFlag args' + isNullCommand = null rawcmd + (argsbeforecmd, argsaftercmd') = break (==rawcmd) args + argsaftercmd = drop 1 argsaftercmd' + when (debugLevel > 0) $ do + printf "running: %s\n" prognameandversion + printf "raw args: %s\n" (show args) + printf "raw args rearranged for cmdargs: %s\n" (show args') + printf "raw command might be: %s\n" (show rawcmd) + printf "raw args before command: %s\n" (show argsbeforecmd) + printf "raw args after command: %s\n" (show argsaftercmd) + + -- search PATH for add-ons addons <- getHledgerAddonCommands - opts <- getHledgerCliOpts addons - when (debug_ opts) $ do - printf "%s\n" prognameandversion - printf "args: %s\n" (show args) - printf "opts: %s\n" (show opts) + + -- parse arguments with cmdargs + opts <- argsToCliOpts args addons + + -- select an action and run it. + let + cmd = command_ opts -- the full matched internal or external command name, if any + isInternalCommand = not (null cmd) && not (cmd `elem` addons) -- probably + isExternalCommand = not (null cmd) && cmd `elem` addons -- probably + isBadCommand = not (null rawcmd) && null cmd + hasHelp args = any (`elem` args) ["--help","-h","-?"] + hasVersion = ("--version" `elem`) + mainmode' = mainmode addons + generalHelp = putStr $ showModeHelp mainmode' + version = putStrLn prognameandversion + badCommandError = error' ("command "++rawcmd++" is not recognized, run with no command to see a list") >> exitFailure + f `orShowHelp` mode = if hasHelp args then putStr (showModeHelp mode) else f + when (debug_ opts > 0) $ do + putStrLn $ "processed opts:\n" ++ ppShow opts + putStrLn $ "command matched: " ++ show cmd + putStrLn $ "isNullCommand: " ++ show isNullCommand + putStrLn $ "isInternalCommand: " ++ show isInternalCommand + putStrLn $ "isExternalCommand: " ++ show isExternalCommand + putStrLn $ "isBadCommand: " ++ show isBadCommand d <- getCurrentDay - printf "query: %s\n" (show $ queryFromOpts d $ reportopts_ opts) + putStrLn $ "date span from opts: " ++ (show $ dateSpanFromOpts d $ reportopts_ opts) + putStrLn $ "interval from opts: " ++ (show $ intervalFromOpts $ reportopts_ opts) + putStrLn $ "query from opts & args: " ++ (show $ queryFromOpts d $ reportopts_ opts) + let + dbg s = if debug_ opts > 0 then trace s else id + runHledgerCommand + -- high priority flags and situations. --help should be highest priority. + | hasHelp argsbeforecmd = dbg "--help before command, showing general help" generalHelp + | not (hasHelp argsaftercmd) && (hasVersion argsbeforecmd || (hasVersion argsaftercmd && isInternalCommand)) + = version + -- \| (null externalcmd) && "binary-filename" `inRawOpts` rawopts = putStrLn $ binaryfilename progname + -- \| "--browse-args" `elem` args = System.Console.CmdArgs.Helper.execute "cmdargs-browser" mainmode' args >>= (putStr . show) + | isNullCommand = dbg "no command, showing general help" generalHelp + | isBadCommand = badCommandError + + -- internal commands + | cmd == "activity" = withJournalDo opts histogram `orShowHelp` activitymode + | cmd == "add" = (journalFilePathFromOpts opts >>= ensureJournalFileExists >> withJournalDo opts add) `orShowHelp` addmode + | cmd == "balance" = withJournalDo opts balance `orShowHelp` balancemode + | cmd == "balancesheet" = withJournalDo opts balancesheet `orShowHelp` balancesheetmode + | cmd == "cashflow" = withJournalDo opts cashflow `orShowHelp` cashflowmode + | cmd == "incomestatement" = withJournalDo opts incomestatement `orShowHelp` incomestatementmode + | cmd == "print" = withJournalDo opts print' `orShowHelp` printmode + | cmd == "register" = withJournalDo opts register `orShowHelp` registermode + | cmd == "stats" = withJournalDo opts stats `orShowHelp` statsmode + | cmd == "test" = test' opts `orShowHelp` testmode + + -- an external command + | isExternalCommand = do + let shellcmd = printf "%s-%s %s" progname cmd (unwords' argsaftercmd) + when (debug_ opts > 0) $ do + printf "external command selected: %s\n" cmd + printf "external command arguments: %s\n" (show argsaftercmd) + printf "running shell command: %s\n" (show shellcmd) + system shellcmd >>= exitWith + + -- deprecated commands + | cmd == "convert" = error' (modeHelp convertmode) >> exitFailure + + -- shouldn't reach here + | otherwise = optserror ("could not understand the arguments "++show args) >> exitFailure + + runHledgerCommand + + +-- tests_runHledgerCommand = [ +-- -- "runHledgerCommand" ~: do +-- -- let opts = defreportopts{query_="expenses"} +-- -- d <- getCurrentDay +-- -- runHledgerCommand addons opts@CliOpts{command_=cmd} args + +-- ] - run' opts addons args - where - run' opts@CliOpts{command_=cmd} addons args - -- delicate, add tests before changing (eg --version, ADDONCMD --version, INTERNALCMD --version) - | (null matchedaddon) && "version" `in_` (rawopts_ opts) = putStrLn prognameandversion - | (null matchedaddon) && "binary-filename" `in_` (rawopts_ opts) = putStrLn $ binaryfilename progname - | null cmd = putStr $ showModeHelp mainmode' - | cmd `isPrefixOf` "add" = showModeHelpOr addmode $ journalFilePathFromOpts opts >>= ensureJournalFileExists >> withJournalDo opts add - | cmd `isPrefixOf` "test" = showModeHelpOr testmode $ test' opts - | any (cmd `isPrefixOf`) ["accounts","balance"] = showModeHelpOr accountsmode $ withJournalDo opts balance - | any (cmd `isPrefixOf`) ["entries","print"] = showModeHelpOr entriesmode $ withJournalDo opts print' - | any (cmd `isPrefixOf`) ["postings","register"] = showModeHelpOr postingsmode $ withJournalDo opts register - | any (cmd `isPrefixOf`) ["activity","histogram"] = showModeHelpOr activitymode $ withJournalDo opts histogram - | any (cmd `isPrefixOf`) ["incomestatement","is"] = showModeHelpOr incomestatementmode $ withJournalDo opts incomestatement - | any (cmd `isPrefixOf`) ["balancesheet","bs"] = showModeHelpOr balancesheetmode $ withJournalDo opts balancesheet - | any (cmd `isPrefixOf`) ["cashflow","cf"] = showModeHelpOr cashflowmode $ withJournalDo opts cashflow - | cmd `isPrefixOf` "stats" = showModeHelpOr statsmode $ withJournalDo opts stats - | not (null matchedaddon) = do - when (debug_ opts) $ printf "running %s\n" shellcmd - system shellcmd >>= exitWith - | cmd == "convert" = optserror ("convert is no longer needed, just use -f FILE.csv") >> exitFailure - | otherwise = optserror ("command "++cmd++" is not recognized") >> exitFailure - where - mainmode' = mainmode addons - showModeHelpOr mode f | "help" `in_` (rawopts_ opts) = putStr $ showModeHelp mode - | otherwise = f - matchedaddon | null cmd = "" - | otherwise = headDef "" $ filter (cmd `isPrefixOf`) addons - shellcmd = printf "%s-%s %s" progname matchedaddon (unwords' subcmdargs) - subcmdargs = args1 ++ drop 1 args2 where (args1,args2) = break (== cmd) $ filter (/="--") args - -{- tests: - -hledger -> main help -hledger --help -> main help -hledger --help command -> command help -hledger command --help -> command help -hledger badcommand -> unrecognized command, try --help (non-zero exit) -hledger badcommand --help -> main help -hledger --help badcommand -> main help -hledger --mainflag command -> works -hledger command --mainflag -> works -hledger command --commandflag -> works -hledger command --mainflag --commandflag -> works -XX hledger --mainflag command --commandflag -> works -XX hledger --commandflag command -> works -XX hledger --commandflag command --mainflag -> works --} \ No newline at end of file diff -Nru haskell-hledger-0.21.3/Hledger/Cli/Options.hs haskell-hledger-0.22/Hledger/Cli/Options.hs --- haskell-hledger-0.21.3/Hledger/Cli/Options.hs 2013-06-23 23:53:04.000000000 +0000 +++ haskell-hledger-0.22/Hledger/Cli/Options.hs 2013-12-14 03:32:24.000000000 +0000 @@ -1,13 +1,93 @@ -{-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, DeriveDataTypeable #-} {-| -Command-line options for the hledger program, and option-parsing utilities. +Command-line options for the hledger program, and related utilities. -} -module Hledger.Cli.Options +module Hledger.Cli.Options ( + + -- * cmdargs modes & flags + -- | These tell cmdargs how to parse the command line arguments. + -- There's one mode for each internal subcommand, plus a main mode. + mainmode, + activitymode, + addmode, + balancemode, + balancesheetmode, + cashflowmode, + incomestatementmode, + printmode, + registermode, + statsmode, + testmode, + convertmode, + defCommandMode, + argsFlag, + helpflags, + inputflags, + reportflags, + generalflagsgroup1, + generalflagsgroup2, + generalflagsgroup3, + + -- * raw options + -- | To allow the cmdargs modes to be reused and extended by other + -- packages (eg, add-ons which want to mimic the standard hledger + -- options), we parse the command-line arguments to a simple + -- association list, not a fixed ADT. + RawOpts, + inRawOpts, + boolopt, + intopt, + maybeintopt, + stringopt, + maybestringopt, + listofstringopt, + setopt, + setboolopt, + + -- * CLI options + -- | Raw options are converted to a more convenient, + -- package-specific options structure. This is the \"opts\" used + -- throughout hledger CLI code. + CliOpts(..), + defcliopts, + + -- * CLI option accessors + -- | Some options require more processing. Possibly these should be merged into argsToCliOpts. + aliasesFromOpts, + formatFromOpts, + journalFilePathFromOpts, + rulesFilePathFromOpts, + OutputWidth(..), + Width(..), + defaultWidth, + defaultWidthWithFlag, + widthFromOpts, + + -- * utilities + getHledgerAddonCommands, + argsToCliOpts, + moveFlagsAfterCommand, + decodeRawOpts, + checkCliOpts, + rawOptsToCliOpts, + optserror, + showModeHelp, + debugArgs, + getCliOpts, + + -- * tests + tests_Hledger_Cli_Options + +) where + import qualified Control.Exception as C +-- import Control.Monad (filterM) +import Control.Monad (when) +import Data.Char (isDigit) import Data.List import Data.List.Split import Data.Maybe @@ -18,6 +98,7 @@ import System.Console.CmdArgs.Text import System.Directory import System.Environment +import System.Exit import Test.HUnit import Text.ParserCombinators.Parsec as P import Text.Printf @@ -26,14 +107,63 @@ import Hledger.Data.FormatStrings as Format import Hledger.Cli.Version +-- +-- 1. cmdargs mode and flag (option) definitions for the hledger CLI, +-- can be reused by other packages as well. +-- --- 1. cmdargs mode and flag definitions, for the main and subcommand modes. --- Flag values are parsed initially to a simple association list to allow reuse. - +-- | Our cmdargs modes parse arguments into an association list for better reuse. type RawOpts = [(String,String)] -defmode :: Mode RawOpts -defmode = Mode { +-- common flags and flag groups + +-- | Common help flags: --help, --debug, --version... +helpflags = [ + flagNone ["help","h","?"] (setboolopt "help") "Display general help or (with --help after COMMAND) command help." + -- ,flagNone ["browse-args"] (setboolopt "browse-args") "use a web UI to select options and build up a command line" + ,flagOpt "1" ["debug"] (\s opts -> Right $ setopt "debug" s opts) "N" "Show debug output (optional argument sets debug level)" + ,flagNone ["version"] (setboolopt "version") "Print version information" + ] + +-- | Common input-related flags: --file, --rules-file, --alias... +inputflags = [ + flagReq ["file","f"] (\s opts -> Right $ setopt "file" s opts) "FILE" "use a different journal file; - means stdin" + ,flagReq ["rules-file"] (\s opts -> Right $ setopt "rules-file" s opts) "RULESFILE" "conversion rules for CSV (default: FILE.rules)" + ,flagReq ["alias"] (\s opts -> Right $ setopt "alias" s opts) "ACCT=ALIAS" "convert ACCT's name to ALIAS" + ] + +-- | Common report-related flags: --period, --cost, --display etc. +reportflags = [ + flagReq ["begin","b"] (\s opts -> Right $ setopt "begin" s opts) "DATE" "report on transactions on or after this date" + ,flagReq ["end","e"] (\s opts -> Right $ setopt "end" s opts) "DATE" "report on transactions before this date" + ,flagReq ["period","p"] (\s opts -> Right $ setopt "period" s opts) "PERIODEXP" "report on transactions during the specified period and/or with the specified reporting interval" + ,flagNone ["daily","D"] (\opts -> setboolopt "daily" opts) "report by day" + ,flagNone ["weekly","W"] (\opts -> setboolopt "weekly" opts) "report by week" + ,flagNone ["monthly","M"] (\opts -> setboolopt "monthly" opts) "report by month" + ,flagNone ["quarterly","Q"] (\opts -> setboolopt "quarterly" opts) "report by quarter" + ,flagNone ["yearly","Y"] (\opts -> setboolopt "yearly" opts) "report by year" + ,flagNone ["cleared","C"] (\opts -> setboolopt "cleared" opts) "report only on cleared transactions" + ,flagNone ["uncleared","U"] (\opts -> setboolopt "uncleared" opts) "report only on uncleared transactions" + ,flagNone ["cost","B"] (\opts -> setboolopt "cost" opts) "report cost of commodities" + ,flagReq ["depth"] (\s opts -> Right $ setopt "depth" s opts) "N" "hide accounts/transactions deeper than this" + ,flagReq ["display","d"] (\s opts -> Right $ setopt "display" s opts) "DISPLAYEXP" "show only transactions matching the expression, which is 'dOP[DATE]' where OP is <, <=, =, >=, >" + ,flagNone ["date2","aux-date","effective"] (\opts -> setboolopt "date2" opts) "use transactions' secondary dates, if any" + ,flagNone ["empty","E"] (\opts -> setboolopt "empty" opts) "show empty/zero things which are normally elided" + ,flagNone ["real","R"] (\opts -> setboolopt "real" opts) "report only on real (non-virtual) transactions" + ] + +argsFlag desc = flagArg (\s opts -> Right $ setopt "args" s opts) desc + +generalflagstitle = "\nGeneral flags" +generalflagsgroup1 = (generalflagstitle, inputflags ++ reportflags ++ helpflags) +generalflagsgroup2 = (generalflagstitle, inputflags ++ helpflags) +generalflagsgroup3 = (generalflagstitle, helpflags) + +-- cmdargs modes + +-- | A basic mode template. +defMode :: Mode RawOpts +defMode = Mode { modeNames = [] ,modeHelp = "" ,modeHelpSuffix = [] @@ -41,264 +171,259 @@ ,modeCheck = Right ,modeReform = const Nothing ,modeExpandAt = True - ,modeGroupFlags = toGroup [] + ,modeGroupFlags = Group { + groupNamed = [] + ,groupUnnamed = [ + flagNone ["help","h","?"] (setboolopt "help") "Display command help." + ] + ,groupHidden = [] + } ,modeArgs = ([], Nothing) ,modeGroupModes = toGroup [] } -mainmode addons = defmode { +-- | A basic subcommand mode with the given command name(s). +defCommandMode names = defMode { + modeNames=names + ,modeValue=[("command", headDef "" names)] + ,modeArgs = ([], Just $ argsFlag "[PATTERNS]") + } + +-- | A basic subcommand mode suitable for an add-on command. +defAddonCommandMode addon = defMode { + modeNames = [addon] + ,modeHelp = printf "run %s-%s" progname addon + ,modeValue=[("command",addon)] + ,modeGroupFlags = Group { + groupUnnamed = [] + ,groupHidden = [] + ,groupNamed = [generalflagsgroup1] + } + ,modeArgs = ([], Just $ argsFlag "[ARGS]") + } + +-- | Add command aliases to the command's help string. +withAliases :: String -> [String] -> String +s `withAliases` [] = s +s `withAliases` (a:[]) = s ++ " (alias: " ++ a ++ ")" +s `withAliases` as = s ++ " (aliases: " ++ intercalate ", " as ++ ")" + + +-- | The top-level cmdargs mode for hledger. +mainmode addons = defMode { modeNames = [progname] - ,modeHelp = "run the specified hledger command. hledger COMMAND --help for more detail. \nIn general, COMMAND should precede OPTIONS." + ,modeHelp = unlines [ + "run the specified hledger command. Commands:" + ] ,modeHelpSuffix = [""] - ,modeGroupFlags = Group { - groupUnnamed = helpflags - ,groupHidden = [flagNone ["binary-filename"] (setboolopt "binary-filename") "show the download filename for this executable, and exit"] - ++ fileflags -- quietly permit these flags before COMMAND as well - ,groupNamed = [] - } - ,modeArgs = ([], Just mainargsflag) + ,modeArgs = ([], Just $ argsFlag "[ARGS]") ,modeGroupModes = Group { - groupUnnamed = [ - ] - ,groupHidden = [ - convertmode - ] - ,groupNamed = [ - ("Misc commands", [ + -- modes (commands) in named groups: + groupNamed = [ + ("Adding data", [ addmode - ,testmode ]) - ,("\nReport commands", [ - accountsmode - ,entriesmode - ,postingsmode + ,("\nBasic reports", [ + printmode + ,balancemode + ,registermode -- ,transactionsmode - ,activitymode + ]) + ,("\nMore reports", [ + activitymode ,incomestatementmode ,balancesheetmode ,cashflowmode ,statsmode ]) + ,("\nMiscellaneous", [ + testmode + ]) ] ++ case addons of [] -> [] - cs -> [("\nAdd-on commands found", map addonmode cs)] - } - } - --- backwards compatibility - allow cmdargs to recognise this command so we can detect and warn -convertmode = (commandmode ["convert"]) { - modeValue = [("command","convert")] - ,modeHelp = "" - ,modeArgs = ([], Just $ flagArg (\s opts -> Right $ setopt "args" s opts) "[CSVFILE]") - ,modeGroupFlags = Group { - groupUnnamed = [] - ,groupHidden = [] - ,groupNamed = [] - } - } --- - -addonmode name = defmode { - modeNames = [name] - ,modeHelp = printf "[-- OPTIONS] run the %s-%s program" progname name - ,modeValue=[("command",name)] + cs -> [("\nAdd-on commands found", map defAddonCommandMode cs)] + -- modes in the unnamed group, shown first without a heading: + ,groupUnnamed = [ + ] + -- modes handled but not shown + ,groupHidden = [ + convertmode + ] + } ,modeGroupFlags = Group { - groupUnnamed = [] - ,groupHidden = [] - ,groupNamed = [(generalflagstitle, generalflags1)] + -- flags in named groups: + groupNamed = [generalflagsgroup3] + -- flags in the unnamed group, shown last without a heading: + ,groupUnnamed = [] + -- flags accepted but not shown in the help: + ,groupHidden = inputflags -- included here so they'll not raise a confusing error if present with no COMMAND } - ,modeArgs = ([], Just addonargsflag) } -help_postscript = [ - -- "DATES can be Y/M/D or smart dates like \"last month\"." - -- ,"PATTERNS are regular" - -- ,"expressions which filter by account name. Prefix a pattern with desc: to" - -- ,"filter by transaction description instead, prefix with not: to negate it." - -- ,"When using both, not: comes last." - ] - -generalflagstitle = "\nGeneral flags" -generalflags1 = fileflags ++ reportflags ++ helpflags -generalflags2 = fileflags ++ helpflags -generalflags3 = helpflags - -fileflags = [ - flagReq ["file","f"] (\s opts -> Right $ setopt "file" s opts) "FILE" "use a different journal file; - means stdin" - ,flagReq ["rules-file"] (\s opts -> Right $ setopt "rules-file" s opts) "RULESFILE" "conversion rules for CSV (default: FILE.rules)" - ,flagReq ["alias"] (\s opts -> Right $ setopt "alias" s opts) "ACCT=ALIAS" "display ACCT's name as ALIAS in reports" - ] +-- help_postscript = [ +-- -- "DATES can be Y/M/D or smart dates like \"last month\"." +-- -- ,"PATTERNS are regular" +-- -- ,"expressions which filter by account name. Prefix a pattern with desc: to" +-- -- ,"filter by transaction description instead, prefix with not: to negate it." +-- -- ,"When using both, not: comes last." +-- ] -reportflags = [ - flagReq ["begin","b"] (\s opts -> Right $ setopt "begin" s opts) "DATE" "report on transactions on or after this date" - ,flagReq ["end","e"] (\s opts -> Right $ setopt "end" s opts) "DATE" "report on transactions before this date" - ,flagReq ["period","p"] (\s opts -> Right $ setopt "period" s opts) "PERIODEXP" "report on transactions during the specified period and/or with the specified reporting interval" - ,flagNone ["daily","D"] (\opts -> setboolopt "daily" opts) "report by day" - ,flagNone ["weekly","W"] (\opts -> setboolopt "weekly" opts) "report by week" - ,flagNone ["monthly","M"] (\opts -> setboolopt "monthly" opts) "report by month" - ,flagNone ["quarterly","Q"] (\opts -> setboolopt "quarterly" opts) "report by quarter" - ,flagNone ["yearly","Y"] (\opts -> setboolopt "yearly" opts) "report by year" - ,flagNone ["cleared","C"] (\opts -> setboolopt "cleared" opts) "report only on cleared transactions" - ,flagNone ["uncleared","U"] (\opts -> setboolopt "uncleared" opts) "report only on uncleared transactions" - ,flagNone ["cost","B"] (\opts -> setboolopt "cost" opts) "report cost of commodities" - ,flagReq ["depth"] (\s opts -> Right $ setopt "depth" s opts) "N" "hide accounts/transactions deeper than this" - ,flagReq ["display","d"] (\s opts -> Right $ setopt "display" s opts) "DISPLAYEXP" "show only transactions matching the expression, which is 'dOP[DATE]' where OP is <, <=, =, >=, >" - ,flagNone ["date2","aux-date","effective"] (\opts -> setboolopt "date2" opts) "use transactions' secondary dates, if any" - ,flagNone ["empty","E"] (\opts -> setboolopt "empty" opts) "show empty/zero things which are normally elided" - ,flagNone ["real","R"] (\opts -> setboolopt "real" opts) "report only on real (non-virtual) transactions" - ] +-- visible subcommand modes -helpflags = [ - flagHelpSimple (setboolopt "help") - ,flagNone ["debug"] (setboolopt "debug") "Show extra debug output" - ,flagVersion (setboolopt "version") - ] - -mainargsflag = flagArg (\s opts -> Right $ setopt "args" s opts) "" -commandargsflag = flagArg (\s opts -> Right $ setopt "args" s opts) "[PATTERNS]" -addonargsflag = flagArg (\s opts -> Right $ setopt "args" s opts) "[ARGS]" - -commandmode names = defmode {modeNames=names, modeValue=[("command",headDef "" names)]} - -addmode = (commandmode ["add"]) { - modeHelp = "prompt for new transactions and append them to the journal" +addmode = (defCommandMode ["add"]) { + modeHelp = "prompt for new transaction entries and add them to the journal" ,modeHelpSuffix = ["Defaults come from previous similar transactions; use query patterns to restrict these."] - ,modeArgs = ([], Just commandargsflag) ,modeGroupFlags = Group { groupUnnamed = [ flagNone ["no-new-accounts"] (\opts -> setboolopt "no-new-accounts" opts) "don't allow creating new accounts" ] ,groupHidden = [] - ,groupNamed = [(generalflagstitle, generalflags2)] - } - } - -testmode = (commandmode ["test"]) { - modeHelp = "run self-tests, or just the ones matching REGEXPS" - ,modeArgs = ([], Just $ flagArg (\s opts -> Right $ setopt "args" s opts) "[REGEXPS]") - ,modeGroupFlags = Group { - groupUnnamed = [] - ,groupHidden = [] - ,groupNamed = [(generalflagstitle, generalflags3)] + ,groupNamed = [generalflagsgroup2] } } -accountsmode = (commandmode ["balance","bal","accounts"]) { - modeHelp = "(or accounts) show matched accounts and their balances" - ,modeArgs = ([], Just commandargsflag) +balancemode = (defCommandMode $ ["balance"] ++ aliases) { + modeHelp = "show matched accounts and their balances" `withAliases` aliases ,modeGroupFlags = Group { groupUnnamed = [ - flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show full account names, unindented" + flagNone ["cumulative"] (\opts -> setboolopt "cumulative" opts) "with a reporting interval, show accumulated totals starting from 0" + ,flagNone ["historical","H"] (\opts -> setboolopt "historical" opts) "with a reporting interval, show accurate historical ending balances" + ,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 ["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-total"] (\opts -> setboolopt "no-total" opts) "don't show the final total" ] ,groupHidden = [] - ,groupNamed = [(generalflagstitle, generalflags1)] + ,groupNamed = [generalflagsgroup1] } } + where aliases = ["b","bal"] -entriesmode = (commandmode ["print","entries"]) { - modeHelp = "(or entries) show matched journal entries" - ,modeArgs = ([], Just commandargsflag) +printmode = (defCommandMode $ ["print"] ++ aliases) { + modeHelp = "show matched journal entries" `withAliases` aliases ,modeGroupFlags = Group { groupUnnamed = [] ,groupHidden = [] - ,groupNamed = [(generalflagstitle, generalflags1)] + ,groupNamed = [generalflagsgroup1] } } + where aliases = ["p"] -postingsmode = (commandmode ["register","postings"]) { - modeHelp = "(or postings) show matched postings and running total" - ,modeArgs = ([], Just commandargsflag) +registermode = (defCommandMode $ ["register"] ++ aliases) { + modeHelp = "show matched postings and running total" `withAliases` aliases ,modeGroupFlags = Group { groupUnnamed = [ flagOpt (show defaultWidthWithFlag) ["width","w"] (\s opts -> Right $ setopt "width" s opts) "N" "increase or set the output width (default: 80)" + ,flagNone ["average","A"] (\opts -> setboolopt "average" opts) "show the running average instead of the running total" ,flagNone ["related","r"] (\opts -> setboolopt "related" opts) "show the other postings in the transactions of those that would have been shown" ] ,groupHidden = [] - ,groupNamed = [(generalflagstitle, generalflags1)] + ,groupNamed = [generalflagsgroup1] } } + where aliases = ["r","reg"] -transactionsmode = (commandmode ["transactions"]) { - modeHelp = "show matched transactions and balance in some account(s)" - ,modeArgs = ([], Just commandargsflag) +-- transactionsmode = (defCommandMode ["transactions"]) { +-- modeHelp = "show matched transactions and balance in some account(s)" +-- ,modeGroupFlags = Group { +-- groupUnnamed = [] +-- ,groupHidden = [] +-- ,groupNamed = [generalflagsgroup1] +-- } +-- } + +activitymode = (defCommandMode ["activity"]) { + modeHelp = "show a barchart of transactions per interval" + ,modeHelpSuffix = ["The default interval is daily."] ,modeGroupFlags = Group { groupUnnamed = [] ,groupHidden = [] - ,groupNamed = [(generalflagstitle, generalflags1)] + ,groupNamed = [generalflagsgroup1] } } -activitymode = (commandmode ["activity","histogram"]) { - modeHelp = "show a barchart of transactions per interval" - ,modeHelpSuffix = ["The default interval is daily."] - ,modeArgs = ([], Just commandargsflag) +incomestatementmode = (defCommandMode $ ["incomestatement"]++aliases) { + modeHelp = "show a simple income statement" `withAliases` aliases ,modeGroupFlags = Group { groupUnnamed = [] ,groupHidden = [] - ,groupNamed = [(generalflagstitle, generalflags1)] + ,groupNamed = [generalflagsgroup1] } } + where aliases = ["is","pl"] -incomestatementmode = (commandmode ["incomestatement","is"]) { - modeHelp = "show a standard income statement" - ,modeArgs = ([], Just commandargsflag) +balancesheetmode = (defCommandMode $ ["balancesheet"]++aliases) { + modeHelp = "show a simple balance sheet" `withAliases` aliases ,modeGroupFlags = Group { groupUnnamed = [] ,groupHidden = [] - ,groupNamed = [(generalflagstitle, generalflags1)] + ,groupNamed = [generalflagsgroup1] } } + where aliases = ["bs"] -balancesheetmode = (commandmode ["balancesheet","bs"]) { - modeHelp = "show a standard balance sheet" - ,modeArgs = ([], Just commandargsflag) +cashflowmode = (defCommandMode ["cashflow","cf"]) { + modeHelp = "show a simple cashflow statement" `withAliases` ["cf"] ,modeGroupFlags = Group { groupUnnamed = [] ,groupHidden = [] - ,groupNamed = [(generalflagstitle, generalflags1)] + ,groupNamed = [generalflagsgroup1] } } -cashflowmode = (commandmode ["cashflow","cf"]) { - modeHelp = "show a simple cashflow statement" - ,modeArgs = ([], Just commandargsflag) +statsmode = (defCommandMode $ ["stats"] ++ aliases) { + modeHelp = "show quick statistics for a journal" `withAliases` aliases ,modeGroupFlags = Group { groupUnnamed = [] ,groupHidden = [] - ,groupNamed = [(generalflagstitle, generalflags1)] + ,groupNamed = [generalflagsgroup1] } } + where aliases = ["s"] -statsmode = (commandmode ["stats"]) { - modeHelp = "show quick statistics for a journal (or part of it)" - ,modeArgs = ([], Just commandargsflag) +testmode = (defCommandMode ["test"]) { + modeHelp = "run self-tests, or just the ones matching REGEXPS" + ,modeArgs = ([], Just $ argsFlag "[REGEXPS]") ,modeGroupFlags = Group { groupUnnamed = [] ,groupHidden = [] - ,groupNamed = [(generalflagstitle, generalflags1)] + ,groupNamed = [generalflagsgroup3] + } + } + +-- hidden commands + +convertmode = (defCommandMode ["convert"]) { + modeValue = [("command","convert")] + ,modeHelp = "convert is no longer needed, just use -f FILE.csv" + ,modeArgs = ([], Just $ argsFlag "[CSVFILE]") + ,modeGroupFlags = Group { + groupUnnamed = [] + ,groupHidden = helpflags + ,groupNamed = [] } } --- 2. ADT holding options used in this package and above, parsed from RawOpts. --- This represents the command-line options that were provided, with all --- parsing completed, but before adding defaults or derived values (XXX add) +-- +-- 2. A package-specific data structure holding options used in this +-- package and above, parsed from RawOpts. This represents the +-- command-line options that were provided, with all parsing +-- completed, but before adding defaults or derived values (XXX add) +-- --- cli options, used in hledger and above +-- | Command line options. Used in the @hledger@ package and above. data CliOpts = CliOpts { rawopts_ :: RawOpts ,command_ :: String ,file_ :: Maybe FilePath ,rules_file_ :: Maybe FilePath ,alias_ :: [String] - ,debug_ :: Bool + ,debug_ :: Int -- ^ debug level, set by @--debug[=N]@. See also 'Hledger.Utils.debugLevel'. ,no_new_accounts_ :: Bool -- add ,width_ :: Maybe String -- register ,reportopts_ :: ReportOpts - } deriving (Show) + } deriving (Show, Data, Typeable) defcliopts = CliOpts def @@ -316,8 +441,8 @@ -- | Parse raw option string values to the desired final data types. -- Any relative smart dates will be converted to fixed dates based on -- today's date. Parsing failures will raise an error. -toCliOpts :: RawOpts -> IO CliOpts -toCliOpts rawopts = do +rawOptsToCliOpts :: RawOpts -> IO CliOpts +rawOptsToCliOpts rawopts = do d <- getCurrentDay return defcliopts { rawopts_ = rawopts @@ -325,7 +450,7 @@ ,file_ = maybestringopt "file" rawopts ,rules_file_ = maybestringopt "rules-file" rawopts ,alias_ = map stripquotes $ listofstringopt "alias" rawopts - ,debug_ = boolopt "debug" rawopts + ,debug_ = intopt "debug" rawopts ,no_new_accounts_ = boolopt "no-new-accounts" rawopts -- add ,width_ = maybestringopt "width" rawopts -- register ,reportopts_ = defreportopts { @@ -341,6 +466,7 @@ ,empty_ = boolopt "empty" rawopts ,no_elide_ = boolopt "no-elide" rawopts ,real_ = boolopt "real" rawopts + ,balancetype_ = balancetypeopt rawopts -- balance ,flat_ = boolopt "flat" rawopts -- balance ,drop_ = intopt "drop" rawopts -- balance ,no_total_ = boolopt "no-total" rawopts -- balance @@ -350,58 +476,113 @@ ,quarterly_ = boolopt "quarterly" rawopts ,yearly_ = boolopt "yearly" rawopts ,format_ = maybestringopt "format" rawopts + ,average_ = boolopt "average" rawopts -- register ,related_ = boolopt "related" rawopts -- register ,query_ = unwords $ listofstringopt "args" rawopts } } --- | Get all command-line options, specifying any extra commands that are allowed, or fail on parse errors. -getHledgerCliOpts :: [String] -> IO CliOpts -getHledgerCliOpts addons = do - args <- getArgs - toCliOpts (decodeRawOpts $ processValue (mainmode addons) $ rearrangeForCmdArgs args) >>= checkCliOpts +-- | Parse hledger CLI options from these command line arguments and +-- add-on command names, or raise any error. +argsToCliOpts :: [String] -> [String] -> IO CliOpts +argsToCliOpts args addons = do + let + args' = moveFlagsAfterCommand args + cmdargsopts = System.Console.CmdArgs.Explicit.processValue (mainmode addons) args' + cmdargsopts' = decodeRawOpts cmdargsopts + rawOptsToCliOpts cmdargsopts' >>= checkCliOpts + +-- | A hacky workaround for cmdargs not accepting flags before the +-- subcommand name: try to detect and move such flags after the +-- command. This allows the user to put them in either position. +-- The order of options is not preserved, but this should be ok. +-- +-- Since we're not parsing flags as precisely as cmdargs here, this is +-- imperfect. We make a decent effort to: +-- - move all no-argument help and input flags +-- - move all required-argument help and input flags along with their values, space-separated or not +-- - not confuse things further or cause misleading errors. +moveFlagsAfterCommand :: [String] -> [String] +moveFlagsAfterCommand args = move args + where + move (f:a:as) | isMovableNoArgFlag f = (move $ a:as) ++ [f] + move (f:v:a:as) | isMovableReqArgFlag f = (move $ a:as) ++ [f,v] + move (fv:a:as) | isMovableReqArgFlagAndValue fv = (move $ a:as) ++ [fv] + move ("--debug":v:a:as) | not (null v) && all isDigit v = (move $ a:as) ++ ["--debug",v] + move ("--debug":a:as) = (move $ a:as) ++ ["--debug"] + move (fv@('-':'-':'d':'e':'b':'u':'g':'=':_):a:as) = (move $ a:as) ++ [fv] + move as = as + + isMovableNoArgFlag a = "-" `isPrefixOf` a && dropWhile (=='-') a `elem` noargflagstomove + isMovableReqArgFlag a = "-" `isPrefixOf` a && dropWhile (=='-') a `elem` reqargflagstomove + isMovableReqArgFlagAndValue ('-':'-':a:as) = case break (== '=') (a:as) of (f:fs,_) -> (f:fs) `elem` reqargflagstomove + _ -> False + isMovableReqArgFlagAndValue ('-':f:_:_) = [f] `elem` reqargflagstomove + isMovableReqArgFlagAndValue _ = False + + noargflagstomove = concatMap flagNames $ filter ((==FlagNone).flagInfo) flagstomove + reqargflagstomove = concatMap flagNames $ filter ((==FlagReq ).flagInfo) flagstomove + flagstomove = inputflags ++ helpflags + +-- | Convert possibly encoded option values to regular unicode strings. +decodeRawOpts = map (\(name,val) -> (name, fromSystemString val)) +-- | Do final validation of processed opts, raising an error if there is trouble. +checkCliOpts :: CliOpts -> IO CliOpts -- or pure.. +checkCliOpts opts@CliOpts{reportopts_=ropts} = do + case formatFromOpts ropts of + Left err -> optserror $ "could not parse format option: "++err + Right _ -> return () + case widthFromOpts opts of + Left err -> optserror $ "could not parse width option: "++err + Right _ -> return () + return opts + +-- -- utils +-- -- | Get the unique suffixes (without hledger-) of hledger-* executables -- found in the current user's PATH, or the empty list if there is any -- problem. getHledgerAddonCommands :: IO [String] -getHledgerAddonCommands = map (drop (length progname + 1)) `fmap` getHledgerProgramsInPath +getHledgerAddonCommands = map (drop (length progname + 1)) `fmap` getHledgerExesInPath --- | Get the unique names of hledger-* executables found in the current +-- | Get the unique names of hledger-*{,.hs} executables found in the current -- user's PATH, or the empty list if there is any problem. -getHledgerProgramsInPath :: IO [String] -getHledgerProgramsInPath = do +getHledgerExesInPath :: IO [String] +getHledgerExesInPath = do pathdirs <- splitOn ":" `fmap` getEnvSafe "PATH" - pathexes <- concat `fmap` mapM getDirectoryContentsSafe pathdirs - return $ nub $ sort $ filter (isRight . parsewith hledgerprog) pathexes - where - hledgerprog = string progname >> char '-' >> many1 (letter <|> char '-') >> eof + pathfiles <- concat `fmap` mapM getDirectoryContentsSafe pathdirs + let hledgernamed = nub $ sort $ filter isHledgerNamed pathfiles + -- hledgerexes <- filterM isExecutable hledgernamed + return hledgernamed + +-- isExecutable f = getPermissions f >>= (return . executable) + +isHledgerNamed = isRight . parsewith (do + string progname + char '-' + many1 (letter <|> char '-') + optional $ (string ".hs" <|> string ".lhs") + eof + ) getEnvSafe v = getEnv v `C.catch` (\(_::C.IOException) -> return "") getDirectoryContentsSafe d = getDirectoryContents d `C.catch` (\(_::C.IOException) -> return []) --- | Convert possibly encoded option values to regular unicode strings. -decodeRawOpts = map (\(name,val) -> (name, fromSystemString val)) - --- A hacky workaround for http://code.google.com/p/ndmitchell/issues/detail?id=470 : --- we'd like to permit options before COMMAND as well as after it. --- Here we make sure at least -f FILE will be accepted in either position. -rearrangeForCmdArgs (fopt@('-':'f':_:_):cmd:rest) = cmd:fopt:rest -rearrangeForCmdArgs ("-f":fval:cmd:rest) = cmd:"-f":fval:rest -rearrangeForCmdArgs as = as - +-- | Raise an error, showing the specified message plus a hint about --help. optserror = error' . (++ " (run with --help for usage)") setopt name val = (++ [(name,singleQuoteIfNeeded val)]) setboolopt name = (++ [(name,"")]) -in_ :: String -> RawOpts -> Bool -in_ name = isJust . lookup name +-- | Is the named option present ? +inRawOpts :: String -> RawOpts -> Bool +inRawOpts name = isJust . lookup name -boolopt = in_ +boolopt = inRawOpts maybestringopt name = maybe Nothing (Just . stripquotes) . lookup name @@ -442,16 +623,15 @@ Just $ parsePeriodExpr d s --- | Do final validation of processed opts, raising an error if there is trouble. -checkCliOpts :: CliOpts -> IO CliOpts -- or pure.. -checkCliOpts opts@CliOpts{reportopts_=ropts} = do - case formatFromOpts ropts of - Left err -> optserror $ "could not parse format option: "++err - Right _ -> return () - case widthFromOpts opts of - Left err -> optserror $ "could not parse width option: "++err - Right _ -> return () - return opts +balancetypeopt :: RawOpts -> BalanceType +balancetypeopt rawopts + | length [o | o <- ["cumulative","historical"], isset o] > 1 + = optserror "please specify at most one of --cumulative and --historical" + | isset "cumulative" = CumulativeBalance + | isset "historical" = HistoricalBalance + | otherwise = PeriodBalance + where + isset = flip boolopt rawopts -- | Parse the format option if provided, possibly returning an error, -- otherwise get the default value. @@ -467,10 +647,22 @@ , FormatField True Nothing Nothing AccountField ] -data OutputWidth = TotalWidth Width | FieldWidths [Width] deriving Show -data Width = Width Int | Auto deriving Show +-- | Output width configuration (for register). +data OutputWidth = + TotalWidth Width -- ^ specify the overall width + | FieldWidths [Width] -- ^ specify each field's width + deriving Show + +-- | A width value. +data Width = + Width Int -- ^ set width to exactly this number of characters + | Auto -- ^ set width automatically from available space + deriving Show +-- | Default width of hledger console output. defaultWidth = 80 + +-- | Width of hledger console output when the -w flag is used with no value. defaultWidthWithFlag = 120 -- | Parse the width option if provided, possibly returning an error, @@ -481,34 +673,22 @@ widthFromOpts CliOpts{width_=Just s} = parseWidth s parseWidth :: String -> Either String OutputWidth -parseWidth s = case (runParser outputwidth () "(unknown)") s of +parseWidth s = case (runParser outputwidthp () "(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) +outputwidthp :: GenParser Char st OutputWidth +outputwidthp = + try (do w <- widthp + ws <- many1 (char ',' >> widthp) return $ FieldWidths $ w:ws) - <|> TotalWidth `fmap` width + <|> TotalWidth `fmap` widthp -width :: GenParser Char st Width -width = (string "auto" >> return Auto) +widthp :: GenParser Char st Width +widthp = (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. -journalFilePathFromOpts :: CliOpts -> IO String -journalFilePathFromOpts opts = do - f <- defaultJournalPath - d <- getCurrentDirectory - expandPath d $ fromMaybe f $ file_ opts - --- | Get the (tilde-expanded) rules file path from options, if any. -rulesFilePathFromOpts :: CliOpts -> IO (Maybe FilePath) -rulesFilePathFromOpts opts = do - d <- getCurrentDirectory - maybe (return Nothing) (fmap Just . expandPath d) $ rules_file_ opts - +-- | Get the account name aliases from options, if any. aliasesFromOpts :: CliOpts -> [(AccountName,AccountName)] aliasesFromOpts = map parseAlias . alias_ where @@ -521,12 +701,56 @@ alias' = case alias of ('=':rest) -> rest _ -> orig +-- | Get the (tilde-expanded, absolute) journal file path from +-- 1. options, 2. an environment variable, or 3. the default. +journalFilePathFromOpts :: CliOpts -> IO String +journalFilePathFromOpts opts = do + f <- defaultJournalPath + d <- getCurrentDirectory + expandPath d $ fromMaybe f $ file_ opts + +-- | Get the (tilde-expanded) rules file path from options, if any. +rulesFilePathFromOpts :: CliOpts -> IO (Maybe FilePath) +rulesFilePathFromOpts opts = do + d <- getCurrentDirectory + maybe (return Nothing) (fmap Just . expandPath d) $ rules_file_ opts + +-- | Get a mode's help message as a nicely wrapped string. showModeHelp :: Mode a -> String showModeHelp = (showText defaultWrap :: [Text] -> String) . (helpText [] HelpFormatDefault :: Mode a -> [Text]) +-- not used: + +-- | Print debug info about arguments and options if --debug is present. +debugArgs :: [String] -> CliOpts -> IO () +debugArgs args opts = + when ("--debug" `elem` args) $ do + progname <- getProgName + putStrLn $ "running: " ++ progname + putStrLn $ "raw args: " ++ show args + putStrLn $ "processed opts:\n" ++ show opts + d <- getCurrentDay + putStrLn $ "search query: " ++ (show $ queryFromOpts d $ reportopts_ opts) + +-- not used: + +-- | Parse hledger CLI options from the command line using the given +-- cmdargs mode, and either return them or, if a help flag is present, +-- print the mode help and exit the program. +getCliOpts :: Mode RawOpts -> IO CliOpts +getCliOpts mode = do + args <- getArgs + let rawopts = decodeRawOpts $ processValue mode args + opts <- rawOptsToCliOpts rawopts >>= checkCliOpts + debugArgs args opts + -- if any (`elem` args) ["--help","-h","-?"] + when ("help" `inRawOpts` rawopts_ opts) $ + putStr (showModeHelp mode) >> exitSuccess + return opts + tests_Hledger_Cli_Options = TestList [ ] diff -Nru haskell-hledger-0.21.3/debian/changelog haskell-hledger-0.22/debian/changelog --- haskell-hledger-0.21.3/debian/changelog 2013-08-03 16:49:37.000000000 +0000 +++ haskell-hledger-0.22/debian/changelog 2013-12-28 18:53:02.000000000 +0000 @@ -1,3 +1,14 @@ +haskell-hledger (0.22-1) unstable; urgency=medium + + [ Joachim Breitner ] + * Adjust watch file to new hackage layout + * Fix path to test whether -threaded is supported by GHC + + [ Clint Adams ] + * New upstream version. + + -- Clint Adams Sat, 28 Dec 2013 13:52:46 -0500 + haskell-hledger (0.21.3-1) unstable; urgency=low * New upstream version. diff -Nru haskell-hledger-0.21.3/debian/control haskell-hledger-0.22/debian/control --- haskell-hledger-0.21.3/debian/control 2013-08-03 16:50:19.000000000 +0000 +++ haskell-hledger-0.22/debian/control 2013-12-28 18:56:29.000000000 +0000 @@ -12,11 +12,14 @@ , libghc-cmdargs-dev (>> 0.10) , libghc-cmdargs-dev (<< 0.11) , libghc-cmdargs-prof + , libghc-data-pprint-dev (>= 0.2.1) + , libghc-data-pprint-dev (<< 0.3) + , libghc-data-pprint-prof , libghc-haskeline-dev (>> 0.6) , libghc-haskeline-dev (<< 0.8) , libghc-haskeline-prof - , libghc-hledger-lib-dev (>> 0.21.3) - , libghc-hledger-lib-dev (<< 0.21.3-999) + , libghc-hledger-lib-dev (>= 0.22) + , libghc-hledger-lib-dev (<< 0.22-999) , libghc-hledger-lib-prof , libghc-hunit-dev , libghc-hunit-prof @@ -34,14 +37,18 @@ , libghc-split-dev (>> 0.1) , libghc-split-dev (<< 0.3) , libghc-split-prof + , libghc-tabular-dev (>= 0.2) + , libghc-tabular-dev (<< 0.3) + , libghc-tabular-prof , libghc-text-dev (>> 0.11) - , libghc-text-dev (<< 0.12) + , libghc-text-dev (<< 1.1) , libghc-text-prof , libghc-utf8-string-dev (>> 0.3.5) , libghc-utf8-string-dev (<< 0.4) , libghc-utf8-string-prof Build-Depends-Indep: ghc-doc , libghc-cmdargs-doc + , libghc-data-pprint-doc , libghc-haskeline-doc , libghc-hledger-lib-doc , libghc-hunit-doc @@ -49,11 +56,12 @@ , libghc-parsec3-doc , libghc-regexpr-doc , libghc-safe-doc + , libghc-tabular-doc , libghc-shakespeare-text-doc , libghc-split-doc , libghc-text-doc , libghc-utf8-string-doc -Standards-Version: 3.9.4 +Standards-Version: 3.9.5 Homepage: http://hackage.haskell.org/package/hledger Vcs-Browser: http://darcs.debian.org/cgi-bin/darcsweb.cgi?r=pkg-haskell/haskell-hledger Vcs-Darcs: http://darcs.debian.org/pkg-haskell/haskell-hledger diff -Nru haskell-hledger-0.21.3/debian/rules haskell-hledger-0.22/debian/rules --- haskell-hledger-0.21.3/debian/rules 2013-08-03 16:49:03.000000000 +0000 +++ haskell-hledger-0.22/debian/rules 2013-12-28 18:52:40.000000000 +0000 @@ -1,6 +1,6 @@ #!/usr/bin/make -f -DEB_SETUP_GHC_CONFIGURE_ARGS := $(shell test -e /usr/lib/ghc-$(GHC_VERSION)/libHSrts_thr.a || echo --flags=-threaded) +DEB_SETUP_GHC_CONFIGURE_ARGS := $(shell test -e /usr/lib/ghc/libHSrts_thr.a || echo --flags=-threaded) include /usr/share/cdbs/1/rules/debhelper.mk include /usr/share/cdbs/1/class/hlibrary.mk diff -Nru haskell-hledger-0.21.3/debian/watch haskell-hledger-0.22/debian/watch --- haskell-hledger-0.21.3/debian/watch 2013-08-03 16:49:03.000000000 +0000 +++ haskell-hledger-0.22/debian/watch 2013-12-28 18:52:40.000000000 +0000 @@ -1,5 +1,2 @@ version=3 -opts="downloadurlmangle=s|archive/([\w\d_-]+)/([\d\.]+)/|archive/$1/$2/$1-$2.tar.gz|,\ -filenamemangle=s|(.*)/$|hledger-$1.tar.gz|" \ - http://hackage.haskell.org/packages/archive/hledger \ - ([\d\.]*\d)/ +http://hackage.haskell.org/package/hledger/distro-monitor .*-([0-9\.]+).(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz))) diff -Nru haskell-hledger-0.21.3/hledger.cabal haskell-hledger-0.22/hledger.cabal --- haskell-hledger-0.21.3/hledger.cabal 2013-06-23 23:53:04.000000000 +0000 +++ haskell-hledger-0.22/hledger.cabal 2013-12-14 03:32:24.000000000 +0000 @@ -1,6 +1,7 @@ name: hledger -- also in cpp-options below -version: 0.21.3 +version: 0.22 +stability: beta category: Finance synopsis: The main command-line interface for the hledger accounting tool. description: @@ -17,8 +18,7 @@ maintainer: Simon Michael homepage: http://hledger.org bug-reports: http://hledger.org/bugs -stability: beta -tested-with: GHC==7.2.2, GHC==7.4.2, GHC==7.6.1 +tested-with: GHC==7.4.2, GHC==7.6.3 cabal-version: >= 1.10 build-type: Simple -- data-dir: data @@ -41,7 +41,7 @@ Default: True library - cpp-options: -DVERSION="0.21.3" + cpp-options: -DVERSION="0.22" ghc-options: -W -- should be the same as below exposed-modules: @@ -62,11 +62,12 @@ Hledger.Cli.Stats -- should be the same as below build-depends: - hledger-lib == 0.21.3 + hledger-lib == 0.22 ,base >= 4.3 && < 5 -- ,cabal-file-th ,containers ,cmdargs >= 0.10 && < 0.11 + ,data-pprint >= 0.2.1 && < 0.3 ,directory ,filepath ,haskeline >= 0.6 && <= 0.8 @@ -80,7 +81,8 @@ ,safe >= 0.2 ,shakespeare-text == 1.0.* ,split >= 0.1 && < 0.3 - ,text == 0.11.* + ,text >= 0.11 && < 1.1 + ,tabular >= 0.2 && < 0.3 ,time ,utf8-string >= 0.3.5 && < 0.4 default-language: Haskell2010 @@ -111,16 +113,17 @@ Hledger.Cli.Print Hledger.Cli.Register Hledger.Cli.Stats - cpp-options: -DVERSION="0.21.3" + cpp-options: -DVERSION="0.22" ghc-options: -W if flag(threaded) ghc-options: -threaded -- should be the same as above build-depends: - hledger-lib == 0.21.3 + hledger-lib == 0.22 ,base >= 4.3 && < 5 ,containers ,cmdargs >= 0.10 && < 0.11 + ,data-pprint >= 0.2.1 && < 0.3 ,directory ,filepath ,haskeline >= 0.6 && <= 0.8 @@ -134,7 +137,8 @@ ,safe >= 0.2 ,shakespeare-text == 1.0.* ,split >= 0.1 && < 0.3 - ,text == 0.11.* + ,tabular >= 0.2 && < 0.3 + ,text >= 0.11 && < 1.1 ,time ,utf8-string >= 0.3.5 && < 0.4 default-language: Haskell2010 @@ -149,6 +153,7 @@ , cmdargs , containers , csv + , data-pprint >= 0.2.1 && < 0.3 , directory , filepath , haskeline @@ -159,11 +164,12 @@ , parsec , pretty-show , process - , regex-compat + , regex-compat-tdfa , regexpr , safe , shakespeare-text , split + ,tabular >= 0.2 && < 0.3 , test-framework , test-framework-hunit , text