packages feed

hledger-lib 0.26 → 0.27

raw patch · 29 files changed

+1635/−1014 lines, 29 filesdep +deepseqdep +uglymemodep ~cmdargsdep ~regex-tdfadep ~safePVP ok

version bump matches the API change (PVP)

Dependencies added: deepseq, uglymemo

Dependency ranges changed: cmdargs, regex-tdfa, safe, split, transformers

API changes (from Hackage documentation)

- Hledger.Data.Account: instance Eq Account
- Hledger.Data.Account: instance Show Account
- Hledger.Data.Amount: instance Num Amount
- Hledger.Data.Amount: instance Num MixedAmount
- Hledger.Data.Amount: instance Show Amount
- Hledger.Data.Amount: instance Show HistoricalPrice
- Hledger.Data.Amount: instance Show MixedAmount
- Hledger.Data.Dates: instance Show DateSpan
- Hledger.Data.Journal: addHistoricalPrice :: HistoricalPrice -> Journal -> Journal
- Hledger.Data.Journal: instance Show Journal
- Hledger.Data.Ledger: instance Show Ledger
- Hledger.Data.OutputFormat: AccountField :: HledgerFormatField
- Hledger.Data.OutputFormat: DefaultDateField :: HledgerFormatField
- Hledger.Data.OutputFormat: DepthSpacerField :: HledgerFormatField
- Hledger.Data.OutputFormat: DescriptionField :: HledgerFormatField
- Hledger.Data.OutputFormat: FieldNo :: Int -> HledgerFormatField
- Hledger.Data.OutputFormat: FormatField :: Bool -> (Maybe Int) -> (Maybe Int) -> HledgerFormatField -> OutputFormat
- Hledger.Data.OutputFormat: FormatLiteral :: String -> OutputFormat
- Hledger.Data.OutputFormat: TotalField :: HledgerFormatField
- Hledger.Data.OutputFormat: data HledgerFormatField
- Hledger.Data.OutputFormat: data OutputFormat
- Hledger.Data.OutputFormat: formatValue :: Bool -> Maybe Int -> Maybe Int -> String -> String
- Hledger.Data.OutputFormat: formatsp :: Stream [Char] m Char => ParsecT [Char] st m [OutputFormat]
- Hledger.Data.OutputFormat: parseStringFormat :: String -> Either String [OutputFormat]
- Hledger.Data.OutputFormat: tests :: Test
- Hledger.Data.Posting: instance Show Posting
- Hledger.Data.TimeLog: instance Read TimeLogCode
- Hledger.Data.TimeLog: instance Show TimeLogCode
- Hledger.Data.TimeLog: instance Show TimeLogEntry
- Hledger.Data.Transaction: instance Show ModifierTransaction
- Hledger.Data.Transaction: instance Show PeriodicTransaction
- Hledger.Data.Transaction: instance Show Transaction
- Hledger.Data.Types: AccountField :: HledgerFormatField
- Hledger.Data.Types: DefaultDateField :: HledgerFormatField
- Hledger.Data.Types: DepthSpacerField :: HledgerFormatField
- Hledger.Data.Types: DescriptionField :: HledgerFormatField
- Hledger.Data.Types: FieldNo :: Int -> HledgerFormatField
- Hledger.Data.Types: FormatField :: Bool -> (Maybe Int) -> (Maybe Int) -> HledgerFormatField -> OutputFormat
- Hledger.Data.Types: FormatLiteral :: String -> OutputFormat
- Hledger.Data.Types: HistoricalPrice :: Day -> Commodity -> Amount -> HistoricalPrice
- Hledger.Data.Types: TotalField :: HledgerFormatField
- Hledger.Data.Types: aboring :: Account -> Bool
- Hledger.Data.Types: acommodity :: Amount -> Commodity
- Hledger.Data.Types: aebalance :: Account -> MixedAmount
- Hledger.Data.Types: aibalance :: Account -> MixedAmount
- Hledger.Data.Types: aname :: Account -> AccountName
- Hledger.Data.Types: anumpostings :: Account -> Int
- Hledger.Data.Types: aparent :: Account -> Maybe Account
- Hledger.Data.Types: aprice :: Amount -> Price
- Hledger.Data.Types: aquantity :: Amount -> Quantity
- Hledger.Data.Types: ascommodityside :: AmountStyle -> Side
- Hledger.Data.Types: ascommodityspaced :: AmountStyle -> Bool
- Hledger.Data.Types: asdecimalpoint :: AmountStyle -> Maybe Char
- Hledger.Data.Types: asdigitgroups :: AmountStyle -> Maybe DigitGroupStyle
- Hledger.Data.Types: asprecision :: AmountStyle -> Int
- Hledger.Data.Types: astyle :: Amount -> AmountStyle
- Hledger.Data.Types: asubs :: Account -> [Account]
- Hledger.Data.Types: ctxAccount :: JournalContext -> ![AccountName]
- Hledger.Data.Types: ctxAliases :: JournalContext -> ![AccountAlias]
- Hledger.Data.Types: ctxDefaultCommodityAndStyle :: JournalContext -> !(Maybe (Commodity, AmountStyle))
- Hledger.Data.Types: ctxYear :: JournalContext -> !(Maybe Year)
- Hledger.Data.Types: data HistoricalPrice
- Hledger.Data.Types: data HledgerFormatField
- Hledger.Data.Types: data OutputFormat
- Hledger.Data.Types: filereadtime :: Journal -> ClockTime
- Hledger.Data.Types: files :: Journal -> [(FilePath, String)]
- Hledger.Data.Types: final_comment_lines :: Journal -> String
- Hledger.Data.Types: hamount :: HistoricalPrice -> Amount
- Hledger.Data.Types: hcommodity :: HistoricalPrice -> Commodity
- Hledger.Data.Types: hdate :: HistoricalPrice -> Day
- Hledger.Data.Types: historical_prices :: Journal -> [HistoricalPrice]
- Hledger.Data.Types: instance Data (DecimalRaw Integer)
- Hledger.Data.Types: instance Data AccountAlias
- Hledger.Data.Types: instance Data Amount
- Hledger.Data.Types: instance Data AmountStyle
- Hledger.Data.Types: instance Data ClearedStatus
- Hledger.Data.Types: instance Data ClockTime
- Hledger.Data.Types: instance Data DateSpan
- Hledger.Data.Types: instance Data DigitGroupStyle
- Hledger.Data.Types: instance Data HistoricalPrice
- Hledger.Data.Types: instance Data Interval
- Hledger.Data.Types: instance Data Journal
- Hledger.Data.Types: instance Data JournalContext
- Hledger.Data.Types: instance Data MixedAmount
- Hledger.Data.Types: instance Data ModifierTransaction
- Hledger.Data.Types: instance Data PeriodicTransaction
- Hledger.Data.Types: instance Data Posting
- Hledger.Data.Types: instance Data PostingType
- Hledger.Data.Types: instance Data Price
- Hledger.Data.Types: instance Data Side
- Hledger.Data.Types: instance Data TimeLogCode
- Hledger.Data.Types: instance Data TimeLogEntry
- Hledger.Data.Types: instance Data Transaction
- Hledger.Data.Types: instance Eq AccountAlias
- Hledger.Data.Types: instance Eq Amount
- Hledger.Data.Types: instance Eq AmountStyle
- Hledger.Data.Types: instance Eq ClearedStatus
- Hledger.Data.Types: instance Eq DateSpan
- Hledger.Data.Types: instance Eq DigitGroupStyle
- Hledger.Data.Types: instance Eq HistoricalPrice
- Hledger.Data.Types: instance Eq HledgerFormatField
- Hledger.Data.Types: instance Eq Interval
- Hledger.Data.Types: instance Eq Journal
- Hledger.Data.Types: instance Eq JournalContext
- Hledger.Data.Types: instance Eq MixedAmount
- Hledger.Data.Types: instance Eq ModifierTransaction
- Hledger.Data.Types: instance Eq OutputFormat
- Hledger.Data.Types: instance Eq PeriodicTransaction
- Hledger.Data.Types: instance Eq Posting
- Hledger.Data.Types: instance Eq PostingType
- Hledger.Data.Types: instance Eq Price
- Hledger.Data.Types: instance Eq Side
- Hledger.Data.Types: instance Eq TimeLogCode
- Hledger.Data.Types: instance Eq TimeLogEntry
- Hledger.Data.Types: instance Eq Transaction
- Hledger.Data.Types: instance Eq WhichDate
- Hledger.Data.Types: instance Ord AccountAlias
- Hledger.Data.Types: instance Ord Amount
- Hledger.Data.Types: instance Ord AmountStyle
- Hledger.Data.Types: instance Ord ClearedStatus
- Hledger.Data.Types: instance Ord DateSpan
- Hledger.Data.Types: instance Ord DigitGroupStyle
- Hledger.Data.Types: instance Ord Interval
- Hledger.Data.Types: instance Ord MixedAmount
- Hledger.Data.Types: instance Ord Price
- Hledger.Data.Types: instance Ord Side
- Hledger.Data.Types: instance Ord TimeLogCode
- Hledger.Data.Types: instance Ord TimeLogEntry
- Hledger.Data.Types: instance Read AccountAlias
- Hledger.Data.Types: instance Read AmountStyle
- Hledger.Data.Types: instance Read DigitGroupStyle
- Hledger.Data.Types: instance Read JournalContext
- Hledger.Data.Types: instance Read Side
- Hledger.Data.Types: instance Show AccountAlias
- Hledger.Data.Types: instance Show AmountStyle
- Hledger.Data.Types: instance Show ClearedStatus
- Hledger.Data.Types: instance Show DigitGroupStyle
- Hledger.Data.Types: instance Show HledgerFormatField
- Hledger.Data.Types: instance Show Interval
- Hledger.Data.Types: instance Show JournalContext
- Hledger.Data.Types: instance Show OutputFormat
- Hledger.Data.Types: instance Show PostingType
- Hledger.Data.Types: instance Show Reader
- Hledger.Data.Types: instance Show Side
- Hledger.Data.Types: instance Show WhichDate
- Hledger.Data.Types: instance ToMarkup Quantity
- Hledger.Data.Types: instance Typeable AccountAlias
- Hledger.Data.Types: instance Typeable Amount
- Hledger.Data.Types: instance Typeable AmountStyle
- Hledger.Data.Types: instance Typeable ClearedStatus
- Hledger.Data.Types: instance Typeable ClockTime
- Hledger.Data.Types: instance Typeable DateSpan
- Hledger.Data.Types: instance Typeable DigitGroupStyle
- Hledger.Data.Types: instance Typeable HistoricalPrice
- Hledger.Data.Types: instance Typeable Interval
- Hledger.Data.Types: instance Typeable Journal
- Hledger.Data.Types: instance Typeable JournalContext
- Hledger.Data.Types: instance Typeable MixedAmount
- Hledger.Data.Types: instance Typeable ModifierTransaction
- Hledger.Data.Types: instance Typeable PeriodicTransaction
- Hledger.Data.Types: instance Typeable Posting
- Hledger.Data.Types: instance Typeable PostingType
- Hledger.Data.Types: instance Typeable Price
- Hledger.Data.Types: instance Typeable Side
- Hledger.Data.Types: instance Typeable TimeLogCode
- Hledger.Data.Types: instance Typeable TimeLogEntry
- Hledger.Data.Types: instance Typeable Transaction
- Hledger.Data.Types: jContext :: Journal -> JournalContext
- Hledger.Data.Types: jcommoditystyles :: Journal -> Map Commodity AmountStyle
- Hledger.Data.Types: jmodifiertxns :: Journal -> [ModifierTransaction]
- Hledger.Data.Types: jperiodictxns :: Journal -> [PeriodicTransaction]
- Hledger.Data.Types: jtxns :: Journal -> [Transaction]
- Hledger.Data.Types: laccounts :: Ledger -> [Account]
- Hledger.Data.Types: ljournal :: Ledger -> Journal
- Hledger.Data.Types: mtpostings :: ModifierTransaction -> [Posting]
- Hledger.Data.Types: mtvalueexpr :: ModifierTransaction -> String
- Hledger.Data.Types: open_timelog_entries :: Journal -> [TimeLogEntry]
- Hledger.Data.Types: paccount :: Posting -> AccountName
- Hledger.Data.Types: pamount :: Posting -> MixedAmount
- Hledger.Data.Types: pbalanceassertion :: Posting -> Maybe MixedAmount
- Hledger.Data.Types: pcomment :: Posting -> String
- Hledger.Data.Types: pdate :: Posting -> Maybe Day
- Hledger.Data.Types: pdate2 :: Posting -> Maybe Day
- Hledger.Data.Types: pstatus :: Posting -> ClearedStatus
- Hledger.Data.Types: ptags :: Posting -> [Tag]
- Hledger.Data.Types: ptperiodicexpr :: PeriodicTransaction -> String
- Hledger.Data.Types: ptpostings :: PeriodicTransaction -> [Posting]
- Hledger.Data.Types: ptransaction :: Posting -> Maybe Transaction
- Hledger.Data.Types: ptype :: Posting -> PostingType
- Hledger.Data.Types: rDetector :: Reader -> FilePath -> String -> Bool
- Hledger.Data.Types: rFormat :: Reader -> StorageFormat
- Hledger.Data.Types: rParser :: Reader -> Maybe FilePath -> Bool -> FilePath -> String -> ExceptT String IO Journal
- Hledger.Data.Types: tcode :: Transaction -> String
- Hledger.Data.Types: tcomment :: Transaction -> String
- Hledger.Data.Types: tdate :: Transaction -> Day
- Hledger.Data.Types: tdate2 :: Transaction -> Maybe Day
- Hledger.Data.Types: tdescription :: Transaction -> String
- Hledger.Data.Types: tlaccount :: TimeLogEntry -> String
- Hledger.Data.Types: tlcode :: TimeLogEntry -> TimeLogCode
- Hledger.Data.Types: tldatetime :: TimeLogEntry -> LocalTime
- Hledger.Data.Types: tldescription :: TimeLogEntry -> String
- Hledger.Data.Types: tlsourcepos :: TimeLogEntry -> SourcePos
- Hledger.Data.Types: tpostings :: Transaction -> [Posting]
- Hledger.Data.Types: tpreceding_comment_lines :: Transaction -> String
- Hledger.Data.Types: tsourcepos :: Transaction -> SourcePos
- Hledger.Data.Types: tstatus :: Transaction -> ClearedStatus
- Hledger.Data.Types: ttags :: Transaction -> [Tag]
- Hledger.Query: instance Data OrdPlus
- Hledger.Query: instance Data Query
- Hledger.Query: instance Data QueryOpt
- Hledger.Query: instance Eq OrdPlus
- Hledger.Query: instance Eq Query
- Hledger.Query: instance Eq QueryOpt
- Hledger.Query: instance Show OrdPlus
- Hledger.Query: instance Show Query
- Hledger.Query: instance Show QueryOpt
- Hledger.Query: instance Typeable OrdPlus
- Hledger.Query: instance Typeable Query
- Hledger.Query: instance Typeable QueryOpt
- Hledger.Read.CsvReader: instance Eq CsvRules
- Hledger.Read.CsvReader: instance Show CsvRules
- Hledger.Read.JournalReader: defaultyeardirective :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate
- Hledger.Read.JournalReader: directive :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate
- Hledger.Read.JournalReader: historicalpricedirective :: ParsecT [Char] JournalContext (ExceptT String IO) HistoricalPrice
- Hledger.Read.JournalReader: journal :: ParsecT [Char] JournalContext (ExceptT String IO) (JournalUpdate, JournalContext)
- Hledger.Read.JournalReader: modifiedaccountname :: Stream [Char] m Char => ParsecT [Char] JournalContext m AccountName
- Hledger.Read.JournalReader: parseJournalWith :: (ParsecT [Char] JournalContext (ExceptT String IO) (JournalUpdate, JournalContext)) -> Bool -> FilePath -> String -> ExceptT String IO Journal
- Hledger.Reports.MultiBalanceReports: instance Show MultiBalanceReport
- Hledger.Reports.ReportOptions: accountlistmode_ :: ReportOpts -> AccountListMode
- Hledger.Reports.ReportOptions: average_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: balancetype_ :: ReportOpts -> BalanceType
- Hledger.Reports.ReportOptions: begin_ :: ReportOpts -> Maybe Day
- Hledger.Reports.ReportOptions: cleared_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: cost_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: daily_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: date2_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: depth_ :: ReportOpts -> Maybe Int
- Hledger.Reports.ReportOptions: display_ :: ReportOpts -> Maybe DisplayExp
- Hledger.Reports.ReportOptions: drop_ :: ReportOpts -> Int
- Hledger.Reports.ReportOptions: empty_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: end_ :: ReportOpts -> Maybe Day
- Hledger.Reports.ReportOptions: format_ :: ReportOpts -> Maybe FormatStr
- Hledger.Reports.ReportOptions: instance Data AccountListMode
- Hledger.Reports.ReportOptions: instance Data BalanceType
- Hledger.Reports.ReportOptions: instance Data ReportOpts
- Hledger.Reports.ReportOptions: instance Default AccountListMode
- Hledger.Reports.ReportOptions: instance Default BalanceType
- Hledger.Reports.ReportOptions: instance Default ReportOpts
- Hledger.Reports.ReportOptions: instance Eq AccountListMode
- Hledger.Reports.ReportOptions: instance Eq BalanceType
- Hledger.Reports.ReportOptions: instance Show AccountListMode
- Hledger.Reports.ReportOptions: instance Show BalanceType
- Hledger.Reports.ReportOptions: instance Show ReportOpts
- Hledger.Reports.ReportOptions: instance Typeable AccountListMode
- Hledger.Reports.ReportOptions: instance Typeable BalanceType
- Hledger.Reports.ReportOptions: instance Typeable ReportOpts
- Hledger.Reports.ReportOptions: monthly_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: no_elide_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: no_total_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: pending_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: period_ :: ReportOpts -> Maybe (Interval, DateSpan)
- Hledger.Reports.ReportOptions: quarterly_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: query_ :: ReportOpts -> String
- Hledger.Reports.ReportOptions: real_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: related_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: row_total_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: uncleared_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: weekly_ :: ReportOpts -> Bool
- Hledger.Reports.ReportOptions: yearly_ :: ReportOpts -> Bool
- Hledger.Utils: T :: (Map a (FastTree a)) -> FastTree a
- Hledger.Utils: assertParse :: (Either ParseError a) -> Assertion
- Hledger.Utils: assertParseEqual :: (Show a, Eq a) => (Either ParseError a) -> a -> Assertion
- Hledger.Utils: assertParseFailure :: (Either ParseError a) -> Assertion
- Hledger.Utils: branches :: Tree a -> Forest a
- Hledger.Utils: choice' :: Stream s m t => [ParsecT s u m a] -> ParsecT s u m a
- Hledger.Utils: chomp :: String -> String
- Hledger.Utils: cliptopleft :: Int -> Int -> String -> String
- Hledger.Utils: concatBottomPadded :: [String] -> String
- Hledger.Utils: concatTopPadded :: [String] -> String
- Hledger.Utils: difforzero :: (Num a, Ord a) => a -> a -> a
- Hledger.Utils: elideLeft :: Int -> String -> String
- Hledger.Utils: elideRight :: Int -> String -> String
- Hledger.Utils: emptyTree :: FastTree a
- Hledger.Utils: eolof :: Stream [Char] m Char => ParsecT [Char] st m ()
- Hledger.Utils: escapeDoubleQuotes :: String -> String
- Hledger.Utils: escapeQuotes :: String -> String
- Hledger.Utils: escapeSingleQuotes :: String -> String
- Hledger.Utils: filterTests :: (Test -> Bool) -> Test -> Test
- Hledger.Utils: fitto :: Int -> Int -> String -> String
- Hledger.Utils: flattenTests :: Test -> [Test]
- Hledger.Utils: fromparse :: Either ParseError a -> a
- Hledger.Utils: instance Eq a => Eq (FastTree a)
- Hledger.Utils: instance Ord a => Ord (FastTree a)
- Hledger.Utils: instance Show a => Show (FastTree a)
- Hledger.Utils: is :: (Eq a, Show a) => a -> a -> Assertion
- Hledger.Utils: isDoubleQuoted :: [Char] -> Bool
- Hledger.Utils: isSingleQuoted :: [Char] -> Bool
- Hledger.Utils: leaves :: Tree a -> [a]
- Hledger.Utils: lowercase :: String -> String
- Hledger.Utils: lstrip :: String -> String
- Hledger.Utils: mergeTrees :: Ord a => FastTree a -> FastTree a -> FastTree a
- Hledger.Utils: newtype FastTree a
- Hledger.Utils: nonspace :: Stream [Char] m Char => ParsecT [Char] st m Char
- Hledger.Utils: padbottom :: Int -> String -> String
- Hledger.Utils: padleft :: Int -> String -> String
- Hledger.Utils: padright :: Int -> String -> String
- Hledger.Utils: padtop :: Int -> String -> String
- Hledger.Utils: parseWithCtx :: Stream s m t => u -> ParsecT s u m a -> s -> m (Either ParseError a)
- Hledger.Utils: parseerror :: ParseError -> a
- Hledger.Utils: parsewith :: Parsec [Char] () a -> String -> Either ParseError a
- Hledger.Utils: printParseError :: Show a => a -> IO ()
- Hledger.Utils: quoteIfNeeded :: String -> String
- Hledger.Utils: quoteIfSpaced :: String -> String
- Hledger.Utils: quotechars :: [Char]
- Hledger.Utils: restofline :: Stream [Char] m Char => ParsecT [Char] st m String
- Hledger.Utils: root :: Tree a -> a
- Hledger.Utils: rstrip :: String -> String
- Hledger.Utils: showDateParseError :: ParseError -> String
- Hledger.Utils: showParseError :: ParseError -> String
- Hledger.Utils: showforest :: Show a => Forest a -> String
- Hledger.Utils: showtree :: Show a => Tree a -> String
- Hledger.Utils: singleQuoteIfNeeded :: String -> String
- Hledger.Utils: spacenonewline :: Stream [Char] m Char => ParsecT [Char] st m Char
- Hledger.Utils: strip :: String -> String
- Hledger.Utils: stripbrackets :: String -> String
- Hledger.Utils: stripquotes :: String -> String
- Hledger.Utils: subs :: Tree a -> Forest a
- Hledger.Utils: subtreeat :: Eq a => a -> Tree a -> Maybe (Tree a)
- Hledger.Utils: subtreeinforest :: Eq a => a -> [Tree a] -> Maybe (Tree a)
- Hledger.Utils: testName :: Test -> String
- Hledger.Utils: treeFromPath :: [a] -> FastTree a
- Hledger.Utils: treeFromPaths :: Ord a => [[a]] -> FastTree a
- Hledger.Utils: treeany :: (a -> Bool) -> Tree a -> Bool
- Hledger.Utils: treefilter :: (a -> Bool) -> Tree a -> Tree a
- Hledger.Utils: treemap :: (a -> b) -> Tree a -> Tree b
- Hledger.Utils: treeprune :: Int -> Tree a -> Tree a
- Hledger.Utils: unbracket :: String -> String
- Hledger.Utils: underline :: String -> String
- Hledger.Utils: unwords' :: [String] -> String
- Hledger.Utils: uppercase :: String -> String
- Hledger.Utils: vConcatRightAligned :: [String] -> String
- Hledger.Utils: whitespacechars :: [Char]
- Hledger.Utils: words' :: String -> [String]
+ Hledger.Data.Account: instance GHC.Classes.Eq Hledger.Data.Types.Account
+ Hledger.Data.Account: instance GHC.Show.Show Hledger.Data.Types.Account
+ Hledger.Data.AccountName: accountSummarisedName :: AccountName -> String
+ Hledger.Data.Amount: instance GHC.Num.Num Hledger.Data.Types.Amount
+ Hledger.Data.Amount: instance GHC.Num.Num Hledger.Data.Types.MixedAmount
+ Hledger.Data.Amount: instance GHC.Show.Show Hledger.Data.Types.Amount
+ Hledger.Data.Amount: instance GHC.Show.Show Hledger.Data.Types.MarketPrice
+ Hledger.Data.Amount: instance GHC.Show.Show Hledger.Data.Types.MixedAmount
+ Hledger.Data.Amount: showMixedAmountOneLine :: MixedAmount -> String
+ Hledger.Data.Dates: instance GHC.Show.Show Hledger.Data.Types.DateSpan
+ Hledger.Data.Journal: addMarketPrice :: MarketPrice -> Journal -> Journal
+ Hledger.Data.Journal: instance GHC.Show.Show Hledger.Data.Types.Journal
+ Hledger.Data.Journal: journalNextTransaction :: Journal -> Transaction -> Maybe Transaction
+ Hledger.Data.Journal: journalPrevTransaction :: Journal -> Transaction -> Maybe Transaction
+ Hledger.Data.Journal: journalTransactionAt :: Journal -> Integer -> Maybe Transaction
+ Hledger.Data.Ledger: instance GHC.Show.Show Hledger.Data.Types.Ledger
+ Hledger.Data.Posting: accountNameApplyAliasesMemo :: [AccountAlias] -> AccountName -> AccountName
+ Hledger.Data.Posting: instance GHC.Show.Show Hledger.Data.Types.Posting
+ Hledger.Data.StringFormat: AccountField :: ReportItemField
+ Hledger.Data.StringFormat: BottomAligned :: [StringFormatComponent] -> StringFormat
+ Hledger.Data.StringFormat: DefaultDateField :: ReportItemField
+ Hledger.Data.StringFormat: DepthSpacerField :: ReportItemField
+ Hledger.Data.StringFormat: DescriptionField :: ReportItemField
+ Hledger.Data.StringFormat: FieldNo :: Int -> ReportItemField
+ Hledger.Data.StringFormat: FormatField :: Bool -> (Maybe Int) -> (Maybe Int) -> ReportItemField -> StringFormatComponent
+ Hledger.Data.StringFormat: FormatLiteral :: String -> StringFormatComponent
+ Hledger.Data.StringFormat: OneLine :: [StringFormatComponent] -> StringFormat
+ Hledger.Data.StringFormat: TopAligned :: [StringFormatComponent] -> StringFormat
+ Hledger.Data.StringFormat: TotalField :: ReportItemField
+ Hledger.Data.StringFormat: data ReportItemField
+ Hledger.Data.StringFormat: data StringFormat
+ Hledger.Data.StringFormat: data StringFormatComponent
+ Hledger.Data.StringFormat: defaultStringFormatStyle :: [StringFormatComponent] -> StringFormat
+ Hledger.Data.StringFormat: instance GHC.Classes.Eq Hledger.Data.StringFormat.ReportItemField
+ Hledger.Data.StringFormat: instance GHC.Classes.Eq Hledger.Data.StringFormat.StringFormat
+ Hledger.Data.StringFormat: instance GHC.Classes.Eq Hledger.Data.StringFormat.StringFormatComponent
+ Hledger.Data.StringFormat: instance GHC.Show.Show Hledger.Data.StringFormat.ReportItemField
+ Hledger.Data.StringFormat: instance GHC.Show.Show Hledger.Data.StringFormat.StringFormat
+ Hledger.Data.StringFormat: instance GHC.Show.Show Hledger.Data.StringFormat.StringFormatComponent
+ Hledger.Data.StringFormat: parseStringFormat :: String -> Either String StringFormat
+ Hledger.Data.StringFormat: tests :: Test
+ Hledger.Data.TimeLog: instance GHC.Read.Read Hledger.Data.Types.TimeLogCode
+ Hledger.Data.TimeLog: instance GHC.Show.Show Hledger.Data.Types.TimeLogCode
+ Hledger.Data.TimeLog: instance GHC.Show.Show Hledger.Data.Types.TimeLogEntry
+ Hledger.Data.Transaction: instance GHC.Show.Show Hledger.Data.Types.ModifierTransaction
+ Hledger.Data.Transaction: instance GHC.Show.Show Hledger.Data.Types.PeriodicTransaction
+ Hledger.Data.Transaction: instance GHC.Show.Show Hledger.Data.Types.Transaction
+ Hledger.Data.Transaction: showTransactionUnelidedOneLineAmounts :: Transaction -> String
+ Hledger.Data.Types: GenericSourcePos :: FilePath -> Int -> Int -> GenericSourcePos
+ Hledger.Data.Types: MarketPrice :: Day -> Commodity -> Amount -> MarketPrice
+ Hledger.Data.Types: [aboring] :: Account -> Bool
+ Hledger.Data.Types: [acommodity] :: Amount -> Commodity
+ Hledger.Data.Types: [aebalance] :: Account -> MixedAmount
+ Hledger.Data.Types: [aibalance] :: Account -> MixedAmount
+ Hledger.Data.Types: [aname] :: Account -> AccountName
+ Hledger.Data.Types: [anumpostings] :: Account -> Int
+ Hledger.Data.Types: [aparent] :: Account -> Maybe Account
+ Hledger.Data.Types: [aprice] :: Amount -> Price
+ Hledger.Data.Types: [aquantity] :: Amount -> Quantity
+ Hledger.Data.Types: [ascommodityside] :: AmountStyle -> Side
+ Hledger.Data.Types: [ascommodityspaced] :: AmountStyle -> Bool
+ Hledger.Data.Types: [asdecimalpoint] :: AmountStyle -> Maybe Char
+ Hledger.Data.Types: [asdigitgroups] :: AmountStyle -> Maybe DigitGroupStyle
+ Hledger.Data.Types: [asprecision] :: AmountStyle -> Int
+ Hledger.Data.Types: [astyle] :: Amount -> AmountStyle
+ Hledger.Data.Types: [asubs] :: Account -> [Account]
+ Hledger.Data.Types: [ctxAccount] :: JournalContext -> ![AccountName]
+ Hledger.Data.Types: [ctxAliases] :: JournalContext -> ![AccountAlias]
+ Hledger.Data.Types: [ctxDefaultCommodityAndStyle] :: JournalContext -> !(Maybe (Commodity, AmountStyle))
+ Hledger.Data.Types: [ctxTransactionIndex] :: JournalContext -> !Integer
+ Hledger.Data.Types: [ctxYear] :: JournalContext -> !(Maybe Year)
+ Hledger.Data.Types: [filereadtime] :: Journal -> ClockTime
+ Hledger.Data.Types: [files] :: Journal -> [(FilePath, String)]
+ Hledger.Data.Types: [final_comment_lines] :: Journal -> String
+ Hledger.Data.Types: [jContext] :: Journal -> JournalContext
+ Hledger.Data.Types: [jcommoditystyles] :: Journal -> Map Commodity AmountStyle
+ Hledger.Data.Types: [jmarketprices] :: Journal -> [MarketPrice]
+ Hledger.Data.Types: [jmodifiertxns] :: Journal -> [ModifierTransaction]
+ Hledger.Data.Types: [jperiodictxns] :: Journal -> [PeriodicTransaction]
+ Hledger.Data.Types: [jtxns] :: Journal -> [Transaction]
+ Hledger.Data.Types: [laccounts] :: Ledger -> [Account]
+ Hledger.Data.Types: [ljournal] :: Ledger -> Journal
+ Hledger.Data.Types: [mpamount] :: MarketPrice -> Amount
+ Hledger.Data.Types: [mpcommodity] :: MarketPrice -> Commodity
+ Hledger.Data.Types: [mpdate] :: MarketPrice -> Day
+ Hledger.Data.Types: [mtpostings] :: ModifierTransaction -> [Posting]
+ Hledger.Data.Types: [mtvalueexpr] :: ModifierTransaction -> String
+ Hledger.Data.Types: [open_timelog_entries] :: Journal -> [TimeLogEntry]
+ Hledger.Data.Types: [paccount] :: Posting -> AccountName
+ Hledger.Data.Types: [pamount] :: Posting -> MixedAmount
+ Hledger.Data.Types: [pbalanceassertion] :: Posting -> Maybe MixedAmount
+ Hledger.Data.Types: [pcomment] :: Posting -> String
+ Hledger.Data.Types: [pdate2] :: Posting -> Maybe Day
+ Hledger.Data.Types: [pdate] :: Posting -> Maybe Day
+ Hledger.Data.Types: [pstatus] :: Posting -> ClearedStatus
+ Hledger.Data.Types: [ptags] :: Posting -> [Tag]
+ Hledger.Data.Types: [ptperiodicexpr] :: PeriodicTransaction -> String
+ Hledger.Data.Types: [ptpostings] :: PeriodicTransaction -> [Posting]
+ Hledger.Data.Types: [ptransaction] :: Posting -> Maybe Transaction
+ Hledger.Data.Types: [ptype] :: Posting -> PostingType
+ Hledger.Data.Types: [rDetector] :: Reader -> FilePath -> String -> Bool
+ Hledger.Data.Types: [rFormat] :: Reader -> StorageFormat
+ Hledger.Data.Types: [rParser] :: Reader -> Maybe FilePath -> Bool -> FilePath -> String -> ExceptT String IO Journal
+ Hledger.Data.Types: [tcode] :: Transaction -> String
+ Hledger.Data.Types: [tcomment] :: Transaction -> String
+ Hledger.Data.Types: [tdate2] :: Transaction -> Maybe Day
+ Hledger.Data.Types: [tdate] :: Transaction -> Day
+ Hledger.Data.Types: [tdescription] :: Transaction -> String
+ Hledger.Data.Types: [tindex] :: Transaction -> Integer
+ Hledger.Data.Types: [tlaccount] :: TimeLogEntry -> String
+ Hledger.Data.Types: [tlcode] :: TimeLogEntry -> TimeLogCode
+ Hledger.Data.Types: [tldatetime] :: TimeLogEntry -> LocalTime
+ Hledger.Data.Types: [tldescription] :: TimeLogEntry -> String
+ Hledger.Data.Types: [tlsourcepos] :: TimeLogEntry -> GenericSourcePos
+ Hledger.Data.Types: [tpostings] :: Transaction -> [Posting]
+ Hledger.Data.Types: [tpreceding_comment_lines] :: Transaction -> String
+ Hledger.Data.Types: [tsourcepos] :: Transaction -> GenericSourcePos
+ Hledger.Data.Types: [tstatus] :: Transaction -> ClearedStatus
+ Hledger.Data.Types: [ttags] :: Transaction -> [Tag]
+ Hledger.Data.Types: data GenericSourcePos
+ Hledger.Data.Types: data MarketPrice
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.AccountAlias
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.Amount
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.AmountStyle
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.ClearedStatus
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.DateSpan
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.DigitGroupStyle
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.GenericSourcePos
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.Interval
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.Journal
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.JournalContext
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.MarketPrice
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.MixedAmount
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.ModifierTransaction
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.PeriodicTransaction
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.Posting
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.PostingType
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.Price
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.Side
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.TimeLogCode
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.TimeLogEntry
+ Hledger.Data.Types: instance Control.DeepSeq.NFData Hledger.Data.Types.Transaction
+ Hledger.Data.Types: instance Control.DeepSeq.NFData System.Time.ClockTime
+ Hledger.Data.Types: instance Data.Data.Data (Data.Decimal.DecimalRaw GHC.Integer.Type.Integer)
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.AccountAlias
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.Amount
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.AmountStyle
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.ClearedStatus
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.DateSpan
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.DigitGroupStyle
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.GenericSourcePos
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.Interval
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.Journal
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.JournalContext
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.MarketPrice
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.MixedAmount
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.ModifierTransaction
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.PeriodicTransaction
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.Posting
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.PostingType
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.Price
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.Side
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.TimeLogCode
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.TimeLogEntry
+ Hledger.Data.Types: instance Data.Data.Data Hledger.Data.Types.Transaction
+ Hledger.Data.Types: instance Data.Data.Data System.Time.ClockTime
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.AccountAlias
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.Amount
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.AmountStyle
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.ClearedStatus
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.DateSpan
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.DigitGroupStyle
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.GenericSourcePos
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.Interval
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.Journal
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.JournalContext
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.MarketPrice
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.MixedAmount
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.ModifierTransaction
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.PeriodicTransaction
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.Posting
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.PostingType
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.Price
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.Side
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.TimeLogCode
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.TimeLogEntry
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.Transaction
+ Hledger.Data.Types: instance GHC.Classes.Eq Hledger.Data.Types.WhichDate
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.AccountAlias
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.Amount
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.AmountStyle
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.ClearedStatus
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.DateSpan
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.DigitGroupStyle
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.GenericSourcePos
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.Interval
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.MarketPrice
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.MixedAmount
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.Price
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.Side
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.TimeLogCode
+ Hledger.Data.Types: instance GHC.Classes.Ord Hledger.Data.Types.TimeLogEntry
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0AccountAlias
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0Amount
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0AmountStyle
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0ClearedStatus
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0ClockTime
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0DateSpan
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0DigitGroupStyle
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0GenericSourcePos
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0Interval
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0Journal
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0JournalContext
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0MarketPrice
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0MixedAmount
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0ModifierTransaction
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0PeriodicTransaction
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0Posting
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0PostingType
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0Price
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0Side
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0TimeLogCode
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0TimeLogEntry
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_0Transaction
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_1AccountAlias
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_1ClearedStatus
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_1Interval
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_1PostingType
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_1Price
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_1Side
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_1TimeLogCode
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_2ClearedStatus
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_2Interval
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_2PostingType
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_2Price
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_2TimeLogCode
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_3Interval
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_3TimeLogCode
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_4Interval
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_4TimeLogCode
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_5Interval
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_6Interval
+ Hledger.Data.Types: instance GHC.Generics.Constructor Hledger.Data.Types.C1_7Interval
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1AccountAlias
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1Amount
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1AmountStyle
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1ClearedStatus
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1ClockTime
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1DateSpan
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1DigitGroupStyle
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1GenericSourcePos
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1Interval
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1Journal
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1JournalContext
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1MarketPrice
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1MixedAmount
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1ModifierTransaction
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1PeriodicTransaction
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1Posting
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1PostingType
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1Price
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1Side
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1TimeLogCode
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1TimeLogEntry
+ Hledger.Data.Types: instance GHC.Generics.Datatype Hledger.Data.Types.D1Transaction
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.AccountAlias
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.Amount
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.AmountStyle
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.ClearedStatus
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.DateSpan
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.DigitGroupStyle
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.GenericSourcePos
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.Interval
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.Journal
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.JournalContext
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.MarketPrice
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.MixedAmount
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.ModifierTransaction
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.PeriodicTransaction
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.Posting
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.PostingType
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.Price
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.Side
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.TimeLogCode
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.TimeLogEntry
+ Hledger.Data.Types: instance GHC.Generics.Generic Hledger.Data.Types.Transaction
+ Hledger.Data.Types: instance GHC.Generics.Generic System.Time.ClockTime
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_0Amount
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_0AmountStyle
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_0Journal
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_0JournalContext
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_0MarketPrice
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_0ModifierTransaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_0PeriodicTransaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_0Posting
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_0TimeLogEntry
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_0Transaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_10Transaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_1Amount
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_1AmountStyle
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_1Journal
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_1JournalContext
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_1MarketPrice
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_1ModifierTransaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_1PeriodicTransaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_1Posting
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_1TimeLogEntry
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_1Transaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_2Amount
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_2AmountStyle
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_2Journal
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_2JournalContext
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_2MarketPrice
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_2Posting
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_2TimeLogEntry
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_2Transaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_3Amount
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_3AmountStyle
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_3Journal
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_3JournalContext
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_3Posting
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_3TimeLogEntry
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_3Transaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_4AmountStyle
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_4Journal
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_4JournalContext
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_4Posting
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_4TimeLogEntry
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_4Transaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_5Journal
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_5Posting
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_5Transaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_6Journal
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_6Posting
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_6Transaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_7Journal
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_7Posting
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_7Transaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_8Journal
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_8Posting
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_8Transaction
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_9Journal
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_9Posting
+ Hledger.Data.Types: instance GHC.Generics.Selector Hledger.Data.Types.S1_0_9Transaction
+ Hledger.Data.Types: instance GHC.Read.Read Hledger.Data.Types.AccountAlias
+ Hledger.Data.Types: instance GHC.Read.Read Hledger.Data.Types.AmountStyle
+ Hledger.Data.Types: instance GHC.Read.Read Hledger.Data.Types.DigitGroupStyle
+ Hledger.Data.Types: instance GHC.Read.Read Hledger.Data.Types.GenericSourcePos
+ Hledger.Data.Types: instance GHC.Read.Read Hledger.Data.Types.JournalContext
+ Hledger.Data.Types: instance GHC.Read.Read Hledger.Data.Types.Side
+ Hledger.Data.Types: instance GHC.Show.Show Hledger.Data.Types.AccountAlias
+ Hledger.Data.Types: instance GHC.Show.Show Hledger.Data.Types.AmountStyle
+ Hledger.Data.Types: instance GHC.Show.Show Hledger.Data.Types.ClearedStatus
+ Hledger.Data.Types: instance GHC.Show.Show Hledger.Data.Types.DigitGroupStyle
+ Hledger.Data.Types: instance GHC.Show.Show Hledger.Data.Types.GenericSourcePos
+ Hledger.Data.Types: instance GHC.Show.Show Hledger.Data.Types.Interval
+ Hledger.Data.Types: instance GHC.Show.Show Hledger.Data.Types.JournalContext
+ Hledger.Data.Types: instance GHC.Show.Show Hledger.Data.Types.PostingType
+ Hledger.Data.Types: instance GHC.Show.Show Hledger.Data.Types.Reader
+ Hledger.Data.Types: instance GHC.Show.Show Hledger.Data.Types.Side
+ Hledger.Data.Types: instance GHC.Show.Show Hledger.Data.Types.WhichDate
+ Hledger.Data.Types: instance Text.Blaze.ToMarkup Hledger.Data.Types.Quantity
+ Hledger.Query: instance Data.Data.Data Hledger.Query.OrdPlus
+ Hledger.Query: instance Data.Data.Data Hledger.Query.Query
+ Hledger.Query: instance Data.Data.Data Hledger.Query.QueryOpt
+ Hledger.Query: instance GHC.Classes.Eq Hledger.Query.OrdPlus
+ Hledger.Query: instance GHC.Classes.Eq Hledger.Query.Query
+ Hledger.Query: instance GHC.Classes.Eq Hledger.Query.QueryOpt
+ Hledger.Query: instance GHC.Show.Show Hledger.Query.OrdPlus
+ Hledger.Query: instance GHC.Show.Show Hledger.Query.Query
+ Hledger.Query: instance GHC.Show.Show Hledger.Query.QueryOpt
+ Hledger.Read.CsvReader: instance GHC.Classes.Eq Hledger.Read.CsvReader.CsvRules
+ Hledger.Read.CsvReader: instance GHC.Show.Show Hledger.Read.CsvReader.CsvRules
+ Hledger.Read.JournalReader: defaultyeardirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate
+ Hledger.Read.JournalReader: directivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate
+ Hledger.Read.JournalReader: genericSourcePos :: SourcePos -> GenericSourcePos
+ Hledger.Read.JournalReader: journalp :: ParsecT [Char] JournalContext (ExceptT String IO) (JournalUpdate, JournalContext)
+ Hledger.Read.JournalReader: marketpricedirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) MarketPrice
+ Hledger.Read.JournalReader: modifiedaccountnamep :: Stream [Char] m Char => ParsecT [Char] JournalContext m AccountName
+ Hledger.Read.JournalReader: parseAndFinaliseJournal :: (ParsecT [Char] JournalContext (ExceptT String IO) (JournalUpdate, JournalContext)) -> Bool -> FilePath -> String -> ExceptT String IO Journal
+ Hledger.Reports.BalanceReport: balanceReportValue :: Journal -> Day -> BalanceReport -> BalanceReport
+ Hledger.Reports.BalanceReport: mixedAmountValue :: Journal -> Day -> MixedAmount -> MixedAmount
+ Hledger.Reports.MultiBalanceReports: instance GHC.Show.Show Hledger.Reports.MultiBalanceReports.MultiBalanceReport
+ Hledger.Reports.MultiBalanceReports: multiBalanceReportValue :: Journal -> Day -> MultiBalanceReport -> MultiBalanceReport
+ Hledger.Reports.ReportOptions: [accountlistmode_] :: ReportOpts -> AccountListMode
+ Hledger.Reports.ReportOptions: [average_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [balancetype_] :: ReportOpts -> BalanceType
+ Hledger.Reports.ReportOptions: [begin_] :: ReportOpts -> Maybe Day
+ Hledger.Reports.ReportOptions: [cleared_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [cost_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [daily_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [date2_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [depth_] :: ReportOpts -> Maybe Int
+ Hledger.Reports.ReportOptions: [display_] :: ReportOpts -> Maybe DisplayExp
+ Hledger.Reports.ReportOptions: [drop_] :: ReportOpts -> Int
+ Hledger.Reports.ReportOptions: [empty_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [end_] :: ReportOpts -> Maybe Day
+ Hledger.Reports.ReportOptions: [format_] :: ReportOpts -> Maybe FormatStr
+ Hledger.Reports.ReportOptions: [monthly_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [no_elide_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [no_total_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [pending_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [period_] :: ReportOpts -> Maybe (Interval, DateSpan)
+ Hledger.Reports.ReportOptions: [quarterly_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [query_] :: ReportOpts -> String
+ Hledger.Reports.ReportOptions: [real_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [related_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [row_total_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [uncleared_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [value_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [weekly_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: [yearly_] :: ReportOpts -> Bool
+ Hledger.Reports.ReportOptions: checkReportOpts :: ReportOpts -> ReportOpts
+ Hledger.Reports.ReportOptions: instance Data.Data.Data Hledger.Reports.ReportOptions.AccountListMode
+ Hledger.Reports.ReportOptions: instance Data.Data.Data Hledger.Reports.ReportOptions.BalanceType
+ Hledger.Reports.ReportOptions: instance Data.Data.Data Hledger.Reports.ReportOptions.ReportOpts
+ Hledger.Reports.ReportOptions: instance GHC.Classes.Eq Hledger.Reports.ReportOptions.AccountListMode
+ Hledger.Reports.ReportOptions: instance GHC.Classes.Eq Hledger.Reports.ReportOptions.BalanceType
+ Hledger.Reports.ReportOptions: instance GHC.Show.Show Hledger.Reports.ReportOptions.AccountListMode
+ Hledger.Reports.ReportOptions: instance GHC.Show.Show Hledger.Reports.ReportOptions.BalanceType
+ Hledger.Reports.ReportOptions: instance GHC.Show.Show Hledger.Reports.ReportOptions.ReportOpts
+ Hledger.Reports.ReportOptions: instance System.Console.CmdArgs.Default.Default Hledger.Reports.ReportOptions.AccountListMode
+ Hledger.Reports.ReportOptions: instance System.Console.CmdArgs.Default.Default Hledger.Reports.ReportOptions.BalanceType
+ Hledger.Reports.ReportOptions: instance System.Console.CmdArgs.Default.Default Hledger.Reports.ReportOptions.ReportOpts
+ Hledger.Reports.TransactionsReports: type AccountTransactionsReport = (String, [AccountTransactionsReportItem])
+ Hledger.Reports.TransactionsReports: type AccountTransactionsReportItem = (Transaction, Transaction, Bool, String, MixedAmount, MixedAmount)
+ Hledger.Utils: fifth6 :: (t, t1, t2, t3, t4, t5) -> t4
+ Hledger.Utils: first6 :: (t, t1, t2, t3, t4, t5) -> t
+ Hledger.Utils: fourth6 :: (t, t1, t2, t3, t4, t5) -> t3
+ Hledger.Utils: maximum' :: Integral a => [a] -> a
+ Hledger.Utils: second6 :: (t, t1, t2, t3, t4, t5) -> t1
+ Hledger.Utils: sixth6 :: (t, t1, t2, t3, t4, t5) -> t5
+ Hledger.Utils: third6 :: (t, t1, t2, t3, t4, t5) -> t2
+ Hledger.Utils.Parse: choice' :: Stream s m t => [ParsecT s u m a] -> ParsecT s u m a
+ Hledger.Utils.Parse: eolof :: (Stream [Char] m Char) => ParsecT [Char] st m ()
+ Hledger.Utils.Parse: fromparse :: Either ParseError a -> a
+ Hledger.Utils.Parse: nonspace :: (Stream [Char] m Char) => ParsecT [Char] st m Char
+ Hledger.Utils.Parse: parseWithCtx :: Stream s m t => u -> ParsecT s u m a -> s -> m (Either ParseError a)
+ Hledger.Utils.Parse: parseerror :: ParseError -> a
+ Hledger.Utils.Parse: parsewith :: Parsec [Char] () a -> String -> Either ParseError a
+ Hledger.Utils.Parse: restofline :: (Stream [Char] m Char) => ParsecT [Char] st m String
+ Hledger.Utils.Parse: showDateParseError :: ParseError -> String
+ Hledger.Utils.Parse: showParseError :: ParseError -> String
+ Hledger.Utils.Parse: spacenonewline :: (Stream [Char] m Char) => ParsecT [Char] st m Char
+ Hledger.Utils.Regex: regexReplaceCIMemo :: Regexp -> Replacement -> String -> String
+ Hledger.Utils.Regex: regexReplaceMemo :: Regexp -> Replacement -> String -> String
+ Hledger.Utils.String: chomp :: String -> String
+ Hledger.Utils.String: cliptopleft :: Int -> Int -> String -> String
+ Hledger.Utils.String: concatBottomPadded :: [String] -> String
+ Hledger.Utils.String: concatOneLine :: [String] -> String
+ Hledger.Utils.String: concatTopPadded :: [String] -> String
+ Hledger.Utils.String: elideLeft :: Int -> String -> String
+ Hledger.Utils.String: elideRight :: Int -> String -> String
+ Hledger.Utils.String: escapeDoubleQuotes :: String -> String
+ Hledger.Utils.String: escapeQuotes :: String -> String
+ Hledger.Utils.String: escapeSingleQuotes :: String -> String
+ Hledger.Utils.String: fitString :: Maybe Int -> Maybe Int -> Bool -> Bool -> String -> String
+ Hledger.Utils.String: fitStringMulti :: Maybe Int -> Maybe Int -> Bool -> Bool -> String -> String
+ Hledger.Utils.String: fitto :: Int -> Int -> String -> String
+ Hledger.Utils.String: formatString :: Bool -> Maybe Int -> Maybe Int -> String -> String
+ Hledger.Utils.String: isDoubleQuoted :: [Char] -> Bool
+ Hledger.Utils.String: isSingleQuoted :: [Char] -> Bool
+ Hledger.Utils.String: lowercase :: String -> String
+ Hledger.Utils.String: lstrip :: String -> String
+ Hledger.Utils.String: padLeftWide :: Int -> String -> String
+ Hledger.Utils.String: padRightWide :: Int -> String -> String
+ Hledger.Utils.String: padbottom :: Int -> String -> String
+ Hledger.Utils.String: padleft :: Int -> String -> String
+ Hledger.Utils.String: padright :: Int -> String -> String
+ Hledger.Utils.String: padtop :: Int -> String -> String
+ Hledger.Utils.String: quoteIfNeeded :: String -> String
+ Hledger.Utils.String: quoteIfSpaced :: String -> String
+ Hledger.Utils.String: rstrip :: String -> String
+ Hledger.Utils.String: singleQuoteIfNeeded :: String -> String
+ Hledger.Utils.String: strWidth :: String -> Int
+ Hledger.Utils.String: strip :: String -> String
+ Hledger.Utils.String: stripbrackets :: String -> String
+ Hledger.Utils.String: stripquotes :: String -> String
+ Hledger.Utils.String: takeWidth :: Int -> String -> String
+ Hledger.Utils.String: unbracket :: String -> String
+ Hledger.Utils.String: underline :: String -> String
+ Hledger.Utils.String: unwords' :: [String] -> String
+ Hledger.Utils.String: uppercase :: String -> String
+ Hledger.Utils.String: vConcatLeftAligned :: [String] -> String
+ Hledger.Utils.String: vConcatRightAligned :: [String] -> String
+ Hledger.Utils.String: words' :: String -> [String]
+ Hledger.Utils.Test: assertParse :: (Either ParseError a) -> Assertion
+ Hledger.Utils.Test: assertParseEqual :: (Show a, Eq a) => (Either ParseError a) -> a -> Assertion
+ Hledger.Utils.Test: assertParseFailure :: (Either ParseError a) -> Assertion
+ Hledger.Utils.Test: filterTests :: (Test -> Bool) -> Test -> Test
+ Hledger.Utils.Test: flattenTests :: Test -> [Test]
+ Hledger.Utils.Test: is :: (Eq a, Show a) => a -> a -> Assertion
+ Hledger.Utils.Test: printParseError :: (Show a) => a -> IO ()
+ Hledger.Utils.Test: testName :: Test -> String
+ Hledger.Utils.Tree: T :: (Map a (FastTree a)) -> FastTree a
+ Hledger.Utils.Tree: branches :: Tree a -> Forest a
+ Hledger.Utils.Tree: emptyTree :: FastTree a
+ Hledger.Utils.Tree: instance GHC.Classes.Eq a => GHC.Classes.Eq (Hledger.Utils.Tree.FastTree a)
+ Hledger.Utils.Tree: instance GHC.Classes.Ord a => GHC.Classes.Ord (Hledger.Utils.Tree.FastTree a)
+ Hledger.Utils.Tree: instance GHC.Show.Show a => GHC.Show.Show (Hledger.Utils.Tree.FastTree a)
+ Hledger.Utils.Tree: leaves :: Tree a -> [a]
+ Hledger.Utils.Tree: mergeTrees :: (Ord a) => FastTree a -> FastTree a -> FastTree a
+ Hledger.Utils.Tree: newtype FastTree a
+ Hledger.Utils.Tree: root :: Tree a -> a
+ Hledger.Utils.Tree: showforest :: Show a => Forest a -> String
+ Hledger.Utils.Tree: showtree :: Show a => Tree a -> String
+ Hledger.Utils.Tree: subs :: Tree a -> Forest a
+ Hledger.Utils.Tree: subtreeat :: Eq a => a -> Tree a -> Maybe (Tree a)
+ Hledger.Utils.Tree: subtreeinforest :: Eq a => a -> [Tree a] -> Maybe (Tree a)
+ Hledger.Utils.Tree: treeFromPath :: [a] -> FastTree a
+ Hledger.Utils.Tree: treeFromPaths :: (Ord a) => [[a]] -> FastTree a
+ Hledger.Utils.Tree: treeany :: (a -> Bool) -> Tree a -> Bool
+ Hledger.Utils.Tree: treefilter :: (a -> Bool) -> Tree a -> Tree a
+ Hledger.Utils.Tree: treemap :: (a -> b) -> Tree a -> Tree b
+ Hledger.Utils.Tree: treeprune :: Int -> Tree a -> Tree a
- Hledger.Data.Dates: failIfInvalidDay :: Monad m => String -> m ()
+ Hledger.Data.Dates: failIfInvalidDay :: (Monad m) => String -> m ()
- Hledger.Data.Dates: failIfInvalidMonth :: Monad m => String -> m ()
+ Hledger.Data.Dates: failIfInvalidMonth :: (Monad m) => String -> m ()
- Hledger.Data.Dates: failIfInvalidYear :: Monad m => String -> m ()
+ Hledger.Data.Dates: failIfInvalidYear :: (Monad m) => String -> m ()
- Hledger.Data.Transaction: nullsourcepos :: SourcePos
+ Hledger.Data.Transaction: nullsourcepos :: GenericSourcePos
- Hledger.Data.Types: Ctx :: !(Maybe Year) -> !(Maybe (Commodity, AmountStyle)) -> ![AccountName] -> ![AccountAlias] -> JournalContext
+ Hledger.Data.Types: Ctx :: !(Maybe Year) -> !(Maybe (Commodity, AmountStyle)) -> ![AccountName] -> ![AccountAlias] -> !Integer -> JournalContext
- Hledger.Data.Types: Journal :: [ModifierTransaction] -> [PeriodicTransaction] -> [Transaction] -> [TimeLogEntry] -> [HistoricalPrice] -> String -> JournalContext -> [(FilePath, String)] -> ClockTime -> Map Commodity AmountStyle -> Journal
+ Hledger.Data.Types: Journal :: [ModifierTransaction] -> [PeriodicTransaction] -> [Transaction] -> [TimeLogEntry] -> [MarketPrice] -> String -> JournalContext -> [(FilePath, String)] -> ClockTime -> Map Commodity AmountStyle -> Journal
- Hledger.Data.Types: TimeLogEntry :: SourcePos -> TimeLogCode -> LocalTime -> String -> String -> TimeLogEntry
+ Hledger.Data.Types: TimeLogEntry :: GenericSourcePos -> TimeLogCode -> LocalTime -> String -> String -> TimeLogEntry
- Hledger.Data.Types: Transaction :: SourcePos -> Day -> Maybe Day -> ClearedStatus -> String -> String -> String -> [Tag] -> [Posting] -> String -> Transaction
+ Hledger.Data.Types: Transaction :: Integer -> GenericSourcePos -> Day -> Maybe Day -> ClearedStatus -> String -> String -> String -> [Tag] -> [Posting] -> String -> Transaction
- Hledger.Data.Types: type Tag = (String, String)
+ Hledger.Data.Types: type Tag = (String, String) A tag name and (possibly empty) value.
- Hledger.Reports.ReportOptions: ReportOpts :: Maybe Day -> Maybe Day -> Maybe (Interval, DateSpan) -> Bool -> Bool -> Bool -> Bool -> Maybe Int -> Maybe DisplayExp -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Maybe FormatStr -> String -> Bool -> Bool -> BalanceType -> AccountListMode -> Int -> Bool -> Bool -> ReportOpts
+ Hledger.Reports.ReportOptions: ReportOpts :: Maybe Day -> Maybe Day -> Maybe (Interval, DateSpan) -> Bool -> Bool -> Bool -> Bool -> Maybe Int -> Maybe DisplayExp -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Maybe FormatStr -> String -> Bool -> Bool -> BalanceType -> AccountListMode -> Int -> Bool -> Bool -> Bool -> ReportOpts

Files

CHANGES view
@@ -1,6 +1,28 @@ API-ish changes in hledger-lib.-User-visible changes appear in hledger's change log.+For user-visible changes, see hledger's change log. +0.27 (unreleased)++- The main hledger types now derive NFData, which makes it easier to+  time things with criterion.++- Utils has been split up more.++- Utils.Regex: regular expression compilation has been memoized, and+  memoizing versions of regexReplace[CI] have been added, since+  compiling regular expressions every time seems to be quite+  expensive (#244).+ +- Utils.String: strWidth is now aware of multi-line strings (#242).++- Read: parsers now use a consistent p suffix.++- New dependencies: deepseq, uglymemo.++- All the hledger packages' cabal files are now generated from+  simpler, less redundant yaml files by hpack, in principle. In+  practice, manual fixups are still needed until hpack gets better,+  but it's still a win.  0.26 (2015/7/12) 
Hledger/Data.hs view
@@ -17,6 +17,7 @@                module Hledger.Data.Ledger,                module Hledger.Data.Posting,                module Hledger.Data.RawOptions,+               module Hledger.Data.StringFormat,                module Hledger.Data.TimeLog,                module Hledger.Data.Transaction,                module Hledger.Data.Types,@@ -34,6 +35,7 @@ import Hledger.Data.Ledger import Hledger.Data.Posting import Hledger.Data.RawOptions+import Hledger.Data.StringFormat import Hledger.Data.TimeLog import Hledger.Data.Transaction import Hledger.Data.Types@@ -49,6 +51,8 @@     ,tests_Hledger_Data_Journal     ,tests_Hledger_Data_Ledger     ,tests_Hledger_Data_Posting+    -- ,tests_Hledger_Data_RawOptions+    -- ,tests_Hledger_Data_StringFormat     ,tests_Hledger_Data_TimeLog     ,tests_Hledger_Data_Transaction     -- ,tests_Hledger_Data_Types
Hledger/Data/Account.hs view
@@ -169,10 +169,10 @@  -- | Flatten an account tree into a list, which is sometimes -- convenient. Note since accounts link to their parents/subs, the--- account tree remains intact and can still be used. It's a tree/list!+-- tree's structure remains intact and can still be used. It's a tree/list! flattenAccounts :: Account -> [Account] flattenAccounts a = squish a []-  where squish a as = a:Prelude.foldr squish as (asubs a)+  where squish a as = a : Prelude.foldr squish as (asubs a)  -- | Filter an account tree (to a list). filterAccounts :: (Account -> Bool) -> Account -> [Account]
Hledger/Data/AccountName.hs view
@@ -10,6 +10,7 @@ module Hledger.Data.AccountName where import Data.List+import Data.List.Split (splitOn) import Data.Tree import Test.HUnit import Text.Printf@@ -31,6 +32,16 @@ accountLeafName :: AccountName -> String accountLeafName = last . accountNameComponents +-- | Truncate all account name components but the last to two characters.+accountSummarisedName :: AccountName -> String+accountSummarisedName a+  --   length cs > 1 = take 2 (head cs) ++ ":" ++ a'+  | length cs > 1 = intercalate ":" (map (take 2) $ init cs) ++ ":" ++ a'+  | otherwise     = a'+    where+      cs = accountNameComponents a+      a' = accountLeafName a+ accountNameLevel :: AccountName -> Int accountNameLevel "" = 0 accountNameLevel a = length (filter (==acctsepchar) a) + 1@@ -100,13 +111,24 @@ --     ..:Af:Lu:Sn:Ca:Ch:Cash  ; Abbreviated and elided! -- @ elideAccountName :: Int -> AccountName -> AccountName-elideAccountName width s =-    elideLeft width $ accountNameFromComponents $ elideparts width [] $ accountNameComponents s+elideAccountName width s+  -- XXX special case for transactions register's multi-account pseudo-names+  | " (split)" `isSuffixOf` s =+    let+      names = splitOn ", " $ take (length s - 8) s+      widthpername = (max 0 (width - 8 - 2 * (max 1 (length names) - 1))) `div` length names+    in+     fitString Nothing (Just width) True False $+     (++" (split)") $+     intercalate ", " $+     [accountNameFromComponents $ elideparts widthpername [] $ accountNameComponents s' | s' <- names]+  | otherwise =+    fitString Nothing (Just width) True False $ accountNameFromComponents $ elideparts width [] $ accountNameComponents s       where         elideparts :: Int -> [String] -> [String] -> [String]         elideparts width done ss-          | length (accountNameFromComponents $ done++ss) <= width = done++ss-          | length ss > 1 = elideparts width (done++[take 2 $ head ss]) (tail ss)+          | strWidth (accountNameFromComponents $ done++ss) <= width = done++ss+          | length ss > 1 = elideparts width (done++[takeWidth 2 $ head ss]) (tail ss)           | otherwise = done++ss  -- | Keep only the first n components of an account name, where n
Hledger/Data/Amount.hs view
@@ -87,6 +87,7 @@   isReallyZeroMixedAmountCost,   -- ** rendering   showMixedAmount,+  showMixedAmountOneLine,   showMixedAmountDebug,   showMixedAmountWithoutPrice,   showMixedAmountOneLineWithoutPrice,@@ -118,7 +119,7 @@ import Hledger.Utils  -deriving instance Show HistoricalPrice+deriving instance Show MarketPrice  amountstyle = AmountStyle L False 0 (Just '.') Nothing @@ -377,10 +378,12 @@ -- -- * amounts in the same commodity are combined unless they have different prices or total prices ----- * multiple zero amounts are replaced by just one. If they had the same commodity, it is preserved.+-- * multiple zero amounts, all with the same non-null commodity, are replaced by just the last of them, preserving the commodity and amount style (all but the last zero amount are discarded) ----- * an empty amount list is replaced with a single commodityless zero+-- * multiple zero amounts with multiple commodities, or no commodities, are replaced by one commodity-less zero amount --+-- * an empty amount list is replaced by one commodity-less zero amount+-- -- * the special "missing" mixed amount remains unchanged -- normaliseMixedAmount :: MixedAmount -> MixedAmount@@ -393,7 +396,7 @@   | otherwise            = Mixed nonzeros   where     newzero = case filter (/= "") (map acommodity zeros) of-               [c] -> nullamt{acommodity=c}+               _:_ -> last zeros                _   -> nullamt     (zeros, nonzeros) = partition isReallyZeroAmount $                         map sumSimilarAmountsUsingFirstPrice $@@ -517,19 +520,25 @@ -- normalising it to one amount per commodity. Assumes amounts have -- no or similar prices, otherwise this can show misleading prices. showMixedAmount :: MixedAmount -> String-showMixedAmount = showMixedAmountHelper False+showMixedAmount = showMixedAmountHelper False False  -- | Like showMixedAmount, but zero amounts are shown with their -- commodity if they have one. showMixedAmountWithZeroCommodity :: MixedAmount -> String-showMixedAmountWithZeroCommodity = showMixedAmountHelper True+showMixedAmountWithZeroCommodity = showMixedAmountHelper True False -showMixedAmountHelper :: Bool -> MixedAmount -> String-showMixedAmountHelper showzerocommodity m =-  vConcatRightAligned $ map showw $ amounts $ normaliseMixedAmountSquashPricesForDisplay m+-- | Get the one-line string representation of a mixed amount.+showMixedAmountOneLine :: MixedAmount -> String+showMixedAmountOneLine = showMixedAmountHelper False True++showMixedAmountHelper :: Bool -> Bool -> MixedAmount -> String+showMixedAmountHelper showzerocommodity useoneline m =+  join $ map showamt $ amounts $ normaliseMixedAmountSquashPricesForDisplay m   where-    showw | showzerocommodity = showAmountWithZeroCommodity-          | otherwise         = showAmount+    join | useoneline = intercalate ", "+         | otherwise  = vConcatRightAligned+    showamt | showzerocommodity = showAmountWithZeroCommodity+            | otherwise         = showAmount  -- | Compact labelled trace of a mixed amount, for debugging. ltraceamount :: String -> MixedAmount -> MixedAmount@@ -622,13 +631,13 @@    -- MixedAmount -  ,"adding mixed amounts, preserving minimum precision and a single commodity on zero" ~: do+  ,"adding mixed amounts to zero, the commodity and amount style are preserved" ~: do     (sum $ map (Mixed . (:[]))              [usd 1.25-             ,usd (-1) `withPrecision` 0+             ,usd (-1) `withPrecision` 3              ,usd (-0.25)              ])-      `is` Mixed [usd 0 `withPrecision` 0]+      `is` Mixed [usd 0 `withPrecision` 3]    ,"adding mixed amounts with total prices" ~: do     (sum $ map (Mixed . (:[]))
Hledger/Data/Journal.hs view
@@ -9,7 +9,7 @@  module Hledger.Data.Journal (   -- * Parsing helpers-  addHistoricalPrice,+  addMarketPrice,   addModifierTransaction,   addPeriodicTransaction,   addTimeLogEntry,@@ -35,6 +35,9 @@   journalDescriptions,   journalFilePath,   journalFilePaths,+  journalTransactionAt,+  journalNextTransaction,+  journalPrevTransaction,   journalPostings,   -- * Standard account types   journalBalanceSheetAccountQuery,@@ -114,7 +117,7 @@ --                      ,show (jmodifiertxns j) --                      ,show (jperiodictxns j) --                      ,show $ open_timelog_entries j---                      ,show $ historical_prices j+--                      ,show $ jmarketprices j --                      ,show $ final_comment_lines j --                      ,show $ jContext j --                      ,show $ map fst $ files j@@ -125,7 +128,7 @@                       , jperiodictxns = []                       , jtxns = []                       , open_timelog_entries = []-                      , historical_prices = []+                      , jmarketprices = []                       , final_comment_lines = []                       , jContext = nullctx                       , files = []@@ -134,7 +137,7 @@                       }  nullctx :: JournalContext-nullctx = Ctx { ctxYear = Nothing, ctxDefaultCommodityAndStyle = Nothing, ctxAccount = [], ctxAliases = [] }+nullctx = Ctx{ctxYear=Nothing, ctxDefaultCommodityAndStyle=Nothing, ctxAccount=[], ctxAliases=[], ctxTransactionIndex=0}  journalFilePath :: Journal -> FilePath journalFilePath = fst . mainfile@@ -154,12 +157,26 @@ addPeriodicTransaction :: PeriodicTransaction -> Journal -> Journal addPeriodicTransaction pt j = j { jperiodictxns = pt : jperiodictxns j } -addHistoricalPrice :: HistoricalPrice -> Journal -> Journal-addHistoricalPrice h j = j { historical_prices = h : historical_prices j }+addMarketPrice :: MarketPrice -> Journal -> Journal+addMarketPrice h j = j { jmarketprices = h : jmarketprices j }  addTimeLogEntry :: TimeLogEntry -> Journal -> Journal addTimeLogEntry tle j = j { open_timelog_entries = tle : open_timelog_entries j } +-- | Get the transaction with this index (its 1-based position in the input stream), if any.+journalTransactionAt :: Journal -> Integer -> Maybe Transaction+journalTransactionAt Journal{jtxns=ts} i =+  -- it's probably ts !! (i+1), but we won't assume+  headMay [t | t <- ts, tindex t == i]++-- | Get the transaction that appeared immediately after this one in the input stream, if any.+journalNextTransaction :: Journal -> Transaction -> Maybe Transaction+journalNextTransaction j t = journalTransactionAt j (tindex t + 1)+  +-- | Get the transaction that appeared immediately before this one in the input stream, if any.+journalPrevTransaction :: Journal -> Transaction -> Maybe Transaction+journalPrevTransaction j t = journalTransactionAt j (tindex t - 1)+   -- | Unique transaction descriptions used in this journal. journalDescriptions :: Journal -> [String] journalDescriptions = nub . sort . map tdescription . jtxns@@ -411,7 +428,7 @@      , jtxns=reverse $ jtxns j -- NOTE: see addTransaction      , jmodifiertxns=reverse $ jmodifiertxns j -- NOTE: see addModifierTransaction      , jperiodictxns=reverse $ jperiodictxns j -- NOTE: see addPeriodicTransaction-     , historical_prices=reverse $ historical_prices j -- NOTE: see addHistoricalPrice+     , jmarketprices=reverse $ jmarketprices j -- NOTE: see addMarketPrice      , open_timelog_entries=reverse $ open_timelog_entries j -- NOTE: see addTimeLogEntry      })   >>= if assrt then journalCheckBalanceAssertions else return@@ -487,17 +504,19 @@                                     Left e    -> Left e       where balance = balanceTransaction (Just ss) --- | Convert all the journal's posting amounts (not price amounts) to--- their canonical display settings. Ie, all amounts in a given--- commodity will use (a) the display settings of the first, and (b)--- the greatest precision, of the posting amounts in that commodity.+-- | Convert all the journal's posting amounts (and historical price+-- amounts, but currently not transaction price amounts) to their+-- canonical display settings. Ie, all amounts in a given commodity+-- will use (a) the display settings of the first, and (b) the+-- greatest precision, of the posting amounts in that commodity. journalCanonicaliseAmounts :: Journal -> Journal-journalCanonicaliseAmounts j@Journal{jtxns=ts} = j''+journalCanonicaliseAmounts j@Journal{jtxns=ts, jmarketprices=mps} = j''     where-      j'' = j'{jtxns=map fixtransaction ts}+      j'' = j'{jtxns=map fixtransaction ts, jmarketprices=map fixmarketprice mps}       j' = j{jcommoditystyles = canonicalStyles $ dbg8 "journalAmounts" $ journalAmounts j}       fixtransaction t@Transaction{tpostings=ps} = t{tpostings=map fixposting ps}       fixposting p@Posting{pamount=a} = p{pamount=fixmixedamount a}+      fixmarketprice mp@MarketPrice{mpamount=a} = mp{mpamount=fixamount a}       fixmixedamount (Mixed as) = Mixed $ map fixamount as       fixamount a@Amount{acommodity=c} = a{astyle=journalCommodityStyle j' c} @@ -528,8 +547,8 @@ journalCommodityStyle j c = M.findWithDefault amountstyle c $ jcommoditystyles j  -- -- | Apply this journal's historical price records to unpriced amounts where possible.--- journalApplyHistoricalPrices :: Journal -> Journal--- journalApplyHistoricalPrices j@Journal{jtxns=ts} = j{jtxns=map fixtransaction ts}+-- journalApplyMarketPrices :: Journal -> Journal+-- journalApplyMarketPrices j@Journal{jtxns=ts} = j{jtxns=map fixtransaction ts} --     where --       fixtransaction t@Transaction{tdate=d, tpostings=ps} = t{tpostings=map fixposting ps} --        where@@ -537,14 +556,14 @@ --         fixmixedamount (Mixed as) = Mixed $ map fixamount as --         fixamount = fixprice --         fixprice a@Amount{price=Just _} = a---         fixprice a@Amount{commodity=c} = a{price=maybe Nothing (Just . UnitPrice) $ journalHistoricalPriceFor j d c}+--         fixprice a@Amount{commodity=c} = a{price=maybe Nothing (Just . UnitPrice) $ journalMarketPriceFor j d c}  -- -- | Get the price for a commodity on the specified day from the price database, if known. -- -- Does only one lookup step, ie will not look up the price of a price.--- journalHistoricalPriceFor :: Journal -> Day -> Commodity -> Maybe MixedAmount--- journalHistoricalPriceFor j d Commodity{symbol=s} = do---   let ps = reverse $ filter ((<= d).hdate) $ filter ((s==).hsymbol) $ sortBy (comparing hdate) $ historical_prices j---   case ps of (HistoricalPrice{hamount=a}:_) -> Just a+-- journalMarketPriceFor :: Journal -> Day -> Commodity -> Maybe MixedAmount+-- journalMarketPriceFor j d Commodity{symbol=s} = do+--   let ps = reverse $ filter ((<= d).mpdate) $ filter ((s==).hsymbol) $ sortBy (comparing mpdate) $ jmarketprices j+--   case ps of (MarketPrice{mpamount=a}:_) -> Just a --              _ -> Nothing  -- | Close any open timelog sessions in this journal using the provided current time.@@ -581,13 +600,23 @@ --               Just (UnitPrice ma)  -> c:(concatMap amountCommodities $ amounts ma) --               Just (TotalPrice ma) -> c:(concatMap amountCommodities $ amounts ma) --- | Get all this journal's (mixed) amounts, in the order parsed.-journalMixedAmounts :: Journal -> [MixedAmount]-journalMixedAmounts = map pamount . journalPostings---- | Get all this journal's component amounts, roughly in the order parsed.+-- | Get an ordered list of the amounts in this journal which will+-- influence amount style canonicalisation. These are:+--+-- * amounts in market price directives (in parse order)+-- * amounts in postings (in parse order)+--+-- Amounts in default commodity directives also influence+-- canonicalisation, but earlier, as amounts are parsed.+-- Amounts in posting prices are not used for canonicalisation.+-- journalAmounts :: Journal -> [Amount]-journalAmounts = concatMap flatten . journalMixedAmounts where flatten (Mixed as) = as+journalAmounts j =+  concat+   [map mpamount $ jmarketprices j+   ,concatMap flatten $ map pamount $ journalPostings j+   ]+  where flatten (Mixed as) = as  -- | The fully specified date span enclosing the dates (primary or secondary) -- of all this journal's transactions and postings, or DateSpan Nothing Nothing@@ -671,6 +700,7 @@          nulljournal          {jtxns = [            txnTieKnot $ Transaction {+             tindex=0,              tsourcepos=nullsourcepos,              tdate=parsedate "2008/01/01",              tdate2=Nothing,@@ -687,6 +717,7 @@            }           ,            txnTieKnot $ Transaction {+             tindex=0,              tsourcepos=nullsourcepos,              tdate=parsedate "2008/06/01",              tdate2=Nothing,@@ -703,6 +734,7 @@            }           ,            txnTieKnot $ Transaction {+             tindex=0,              tsourcepos=nullsourcepos,              tdate=parsedate "2008/06/02",              tdate2=Nothing,@@ -719,6 +751,7 @@            }           ,            txnTieKnot $ Transaction {+             tindex=0,              tsourcepos=nullsourcepos,              tdate=parsedate "2008/06/03",              tdate2=Nothing,@@ -735,6 +768,7 @@            }           ,            txnTieKnot $ Transaction {+             tindex=0,              tsourcepos=nullsourcepos,              tdate=parsedate "2008/12/31",              tdate2=Nothing,
− Hledger/Data/OutputFormat.hs
@@ -1,121 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-module Hledger.Data.OutputFormat (-          parseStringFormat-        , formatsp-        , formatValue-        , OutputFormat(..)-        , HledgerFormatField(..)-        , tests-        ) where--import Prelude ()-import Prelude.Compat-import Numeric-import Data.Char (isPrint)-import Data.Maybe-import Test.HUnit-import Text.Parsec-import Text.Printf--import Hledger.Data.Types---formatValue :: Bool -> Maybe Int -> Maybe Int -> String -> String-formatValue leftJustified min max value = printf formatS value-    where-      l = if leftJustified then "-" else ""-      min' = maybe "" show min-      max' = maybe "" (\i -> "." ++ (show i)) max-      formatS = "%" ++ l ++ min' ++ max' ++ "s"--parseStringFormat :: String -> Either String [OutputFormat]-parseStringFormat input = case (runParser (formatsp <* eof) () "(unknown)") input of-    Left y -> Left $ show y-    Right x -> Right x--{--Parsers--}--field :: Stream [Char] m Char => ParsecT [Char] st m HledgerFormatField-field = do-        try (string "account" >> return AccountField)-    <|> try (string "depth_spacer" >> return DepthSpacerField)-    <|> try (string "date" >> return DescriptionField)-    <|> try (string "description" >> return DescriptionField)-    <|> try (string "total" >> return TotalField)-    <|> try (many1 digit >>= (\s -> return $ FieldNo $ read s))--formatField :: Stream [Char] m Char => ParsecT [Char] st m OutputFormat-formatField = do-    char '%'-    leftJustified <- optionMaybe (char '-')-    minWidth <- optionMaybe (many1 $ digit)-    maxWidth <- optionMaybe (do char '.'; many1 $ digit) -- TODO: Can this be (char '1') *> (many1 digit)-    char '('-    f <- field-    char ')'-    return $ FormatField (isJust leftJustified) (parseDec minWidth) (parseDec maxWidth) f-    where-      parseDec s = case s of-        Just text -> Just m where ((m,_):_) = readDec text-        _ -> Nothing--formatLiteral :: Stream [Char] m Char => ParsecT [Char] st m OutputFormat-formatLiteral = do-    s <- many1 c-    return $ FormatLiteral s-    where-      isPrintableButNotPercentage x = isPrint x && (not $ x == '%')-      c =     (satisfy isPrintableButNotPercentage <?> "printable character")-          <|> try (string "%%" >> return '%')--formatp :: Stream [Char] m Char => ParsecT [Char] st m OutputFormat-formatp =-        formatField-    <|> formatLiteral--formatsp :: Stream [Char] m Char => ParsecT [Char] st m [OutputFormat]-formatsp = many formatp--testFormat :: OutputFormat -> String -> String -> Assertion-testFormat fs value expected = assertEqual name expected actual-    where-        (name, actual) = case fs of-            FormatLiteral l -> ("literal", formatValue False Nothing Nothing l)-            FormatField leftJustify min max _ -> ("field", formatValue leftJustify min max value)--testParser :: String -> [OutputFormat] -> Assertion-testParser s expected = case (parseStringFormat s) of-    Left  error -> assertFailure $ show error-    Right actual -> assertEqual ("Input: " ++ s) expected actual--tests = test [ formattingTests ++ parserTests ]--formattingTests = [-      testFormat (FormatLiteral " ")                                ""            " "-    , testFormat (FormatField False Nothing Nothing DescriptionField)    "description" "description"-    , testFormat (FormatField False (Just 20) Nothing DescriptionField)  "description" "         description"-    , testFormat (FormatField False Nothing (Just 20) DescriptionField)  "description" "description"-    , testFormat (FormatField True Nothing (Just 20) DescriptionField)   "description" "description"-    , testFormat (FormatField True (Just 20) Nothing DescriptionField)   "description" "description         "-    , testFormat (FormatField True (Just 20) (Just 20) DescriptionField) "description" "description         "-    , testFormat (FormatField True Nothing (Just 3) DescriptionField)    "description" "des"-    ]--parserTests = [-      testParser ""                             []-    , testParser "D"                            [FormatLiteral "D"]-    , testParser "%(date)"                      [FormatField False Nothing Nothing DescriptionField]-    , testParser "%(total)"                     [FormatField False Nothing Nothing TotalField]-    , testParser "Hello %(date)!"               [FormatLiteral "Hello ", FormatField False Nothing Nothing DescriptionField, FormatLiteral "!"]-    , testParser "%-(date)"                     [FormatField True Nothing Nothing DescriptionField]-    , testParser "%20(date)"                    [FormatField False (Just 20) Nothing DescriptionField]-    , testParser "%.10(date)"                   [FormatField False Nothing (Just 10) DescriptionField]-    , testParser "%20.10(date)"                 [FormatField False (Just 20) (Just 10) DescriptionField]-    , testParser "%20(account) %.10(total)\n"   [ FormatField False (Just 20) Nothing AccountField-                                                , FormatLiteral " "-                                                , FormatField False Nothing (Just 10) TotalField-                                                , FormatLiteral "\n"-                                                ]-  ]
Hledger/Data/Posting.hs view
@@ -37,6 +37,7 @@   joinAccountNames,   concatAccountNames,   accountNameApplyAliases,+  accountNameApplyAliasesMemo,   -- * arithmetic   sumPostings,   -- * rendering@@ -48,11 +49,11 @@ where import Data.List import Data.Maybe+import Data.MemoUgly (memo) import Data.Ord import Data.Time.Calendar import Safe import Test.HUnit-import Text.Printf  import Hledger.Utils import Hledger.Data.Types@@ -88,12 +89,12 @@     where       ledger3ishlayout = False       acctnamewidth = if ledger3ishlayout then 25 else 22-      showaccountname = printf ("%-"++(show acctnamewidth)++"s") . bracket . elideAccountName width+      showaccountname = fitString (Just acctnamewidth) Nothing False False . bracket . elideAccountName width       (bracket,width) = case t of                           BalancedVirtualPosting -> (\s -> "["++s++"]", acctnamewidth-2)                           VirtualPosting -> (\s -> "("++s++")", acctnamewidth-2)                           _ -> (id,acctnamewidth)-      showamount = padleft 12 . showMixedAmount+      showamount = padLeftWide 12 . showMixedAmount   showComment :: String -> String@@ -230,6 +231,10 @@              aname              aliases +-- | Memoising version of accountNameApplyAliases, maybe overkill.+accountNameApplyAliasesMemo :: [AccountAlias] -> AccountName -> AccountName+accountNameApplyAliasesMemo aliases = memo (accountNameApplyAliases aliases)+ -- aliasMatches :: AccountAlias -> AccountName -> Bool -- aliasMatches (BasicAlias old _) a = old `isAccountNamePrefixOf` a -- aliasMatches (RegexAlias re  _) a = regexMatchesCI re a@@ -238,7 +243,7 @@ aliasReplace (BasicAlias old new) a   | old `isAccountNamePrefixOf` a || old == a = new ++ drop (length old) a   | otherwise = a-aliasReplace (RegexAlias re repl) a = regexReplaceCI re repl a+aliasReplace (RegexAlias re repl) a = regexReplaceCIMemo re repl a   tests_Hledger_Data_Posting = TestList [
+ Hledger/Data/StringFormat.hs view
@@ -0,0 +1,180 @@+-- | Parse format strings provided by --format, with awareness of+-- hledger's report item fields. The formats are used by+-- report-specific renderers like renderBalanceReportItem.++{-# LANGUAGE FlexibleContexts #-}++module Hledger.Data.StringFormat (+          parseStringFormat+        , defaultStringFormatStyle+        , StringFormat(..)+        , StringFormatComponent(..)+        , ReportItemField(..)+        , tests+        ) where++import Prelude ()+import Prelude.Compat+import Numeric+import Data.Char (isPrint)+import Data.Maybe+import Test.HUnit+import Text.Parsec++import Hledger.Utils.String (formatString)++-- | A format specification/template to use when rendering a report line item as text.+--+-- A format is a sequence of components; each is either a literal+-- string, or a hledger report item field with specified width and+-- justification whose value will be interpolated at render time.+--+-- A component's value may be a multi-line string (or a+-- multi-commodity amount), in which case the final string will be+-- either single-line or a top or bottom-aligned multi-line string+-- depending on the StringFormat variant used.+--+-- Currently this is only used in the balance command's single-column+-- mode, which provides a limited StringFormat renderer.+--+data StringFormat =+    OneLine [StringFormatComponent]       -- ^ multi-line values will be rendered on one line, comma-separated+  | TopAligned [StringFormatComponent]    -- ^ values will be top-aligned (and bottom-padded to the same height)+  | BottomAligned [StringFormatComponent] -- ^ values will be bottom-aligned (and top-padded)+  deriving (Show, Eq)++data StringFormatComponent =+    FormatLiteral String        -- ^ Literal text to be rendered as-is+  | FormatField Bool+                (Maybe Int)+                (Maybe Int)+                ReportItemField -- ^ A data field to be formatted and interpolated. Parameters:+                                --+                                -- - Left justify ? Right justified if false+                                -- - Minimum width ? Will be space-padded if narrower than this+                                -- - Maximum width ? Will be clipped if wider than this+                                -- - Which of the standard hledger report item fields to interpolate+  deriving (Show, Eq)++-- | An id identifying which report item field to interpolate.  These+-- are drawn from several hledger report types, so are not all+-- applicable for a given report.+data ReportItemField =+    AccountField      -- ^ A posting or balance report item's account name+  | DefaultDateField  -- ^ A posting or register or entry report item's date+  | DescriptionField  -- ^ A posting or register or entry report item's description+  | TotalField        -- ^ A balance or posting report item's balance or running total.+                      --   Always rendered right-justified.+  | DepthSpacerField  -- ^ A balance report item's indent level (which may be different from the account name depth).+                      --   Rendered as this number of spaces, multiplied by the minimum width spec if any.+  | FieldNo Int       -- ^ A report item's nth field. May be unimplemented.+    deriving (Show, Eq)++----------------------------------------------------------------------++-- renderStringFormat :: StringFormat -> Map String String -> String+-- renderStringFormat fmt params =++----------------------------------------------------------------------++-- | Parse a string format specification, or return a parse error.+parseStringFormat :: String -> Either String StringFormat+parseStringFormat input = case (runParser (stringformatp <* eof) () "(unknown)") input of+    Left y -> Left $ show y+    Right x -> Right x++defaultStringFormatStyle = BottomAligned++stringformatp :: Stream [Char] m Char => ParsecT [Char] st m StringFormat+stringformatp = do+  alignspec <- optionMaybe (try $ char '%' >> oneOf "^_,")+  let constructor =+        case alignspec of+          Just '^' -> TopAligned+          Just '_' -> BottomAligned+          Just ',' -> OneLine+          _        -> defaultStringFormatStyle+  constructor <$> many componentp++componentp :: Stream [Char] m Char => ParsecT [Char] st m StringFormatComponent+componentp = formatliteralp <|> formatfieldp++formatliteralp :: Stream [Char] m Char => ParsecT [Char] st m StringFormatComponent+formatliteralp = do+    s <- many1 c+    return $ FormatLiteral s+    where+      isPrintableButNotPercentage x = isPrint x && (not $ x == '%')+      c =     (satisfy isPrintableButNotPercentage <?> "printable character")+          <|> try (string "%%" >> return '%')++formatfieldp :: Stream [Char] m Char => ParsecT [Char] st m StringFormatComponent+formatfieldp = do+    char '%'+    leftJustified <- optionMaybe (char '-')+    minWidth <- optionMaybe (many1 $ digit)+    maxWidth <- optionMaybe (do char '.'; many1 $ digit) -- TODO: Can this be (char '1') *> (many1 digit)+    char '('+    f <- fieldp+    char ')'+    return $ FormatField (isJust leftJustified) (parseDec minWidth) (parseDec maxWidth) f+    where+      parseDec s = case s of+        Just text -> Just m where ((m,_):_) = readDec text+        _ -> Nothing++fieldp :: Stream [Char] m Char => ParsecT [Char] st m ReportItemField+fieldp = do+        try (string "account" >> return AccountField)+    <|> try (string "depth_spacer" >> return DepthSpacerField)+    <|> try (string "date" >> return DescriptionField)+    <|> try (string "description" >> return DescriptionField)+    <|> try (string "total" >> return TotalField)+    <|> try (many1 digit >>= (\s -> return $ FieldNo $ read s))++----------------------------------------------------------------------++testFormat :: StringFormatComponent -> String -> String -> Assertion+testFormat fs value expected = assertEqual name expected actual+    where+        (name, actual) = case fs of+            FormatLiteral l -> ("literal", formatString False Nothing Nothing l)+            FormatField leftJustify min max _ -> ("field", formatString leftJustify min max value)++testParser :: String -> StringFormat -> Assertion+testParser s expected = case (parseStringFormat s) of+    Left  error -> assertFailure $ show error+    Right actual -> assertEqual ("Input: " ++ s) expected actual++tests = test [ formattingTests ++ parserTests ]++formattingTests = [+      testFormat (FormatLiteral " ")                                ""            " "+    , testFormat (FormatField False Nothing Nothing DescriptionField)    "description" "description"+    , testFormat (FormatField False (Just 20) Nothing DescriptionField)  "description" "         description"+    , testFormat (FormatField False Nothing (Just 20) DescriptionField)  "description" "description"+    , testFormat (FormatField True Nothing (Just 20) DescriptionField)   "description" "description"+    , testFormat (FormatField True (Just 20) Nothing DescriptionField)   "description" "description         "+    , testFormat (FormatField True (Just 20) (Just 20) DescriptionField) "description" "description         "+    , testFormat (FormatField True Nothing (Just 3) DescriptionField)    "description" "des"+    ]++parserTests = [+      testParser ""                             (defaultStringFormatStyle [])+    , testParser "D"                            (defaultStringFormatStyle [FormatLiteral "D"])+    , testParser "%(date)"                      (defaultStringFormatStyle [FormatField False Nothing Nothing DescriptionField])+    , testParser "%(total)"                     (defaultStringFormatStyle [FormatField False Nothing Nothing TotalField])+    , testParser "^%(total)"                    (TopAligned [FormatField False Nothing Nothing TotalField])+    , testParser "_%(total)"                    (BottomAligned [FormatField False Nothing Nothing TotalField])+    , testParser ",%(total)"                    (OneLine [FormatField False Nothing Nothing TotalField])+    , testParser "Hello %(date)!"               (defaultStringFormatStyle [FormatLiteral "Hello ", FormatField False Nothing Nothing DescriptionField, FormatLiteral "!"])+    , testParser "%-(date)"                     (defaultStringFormatStyle [FormatField True Nothing Nothing DescriptionField])+    , testParser "%20(date)"                    (defaultStringFormatStyle [FormatField False (Just 20) Nothing DescriptionField])+    , testParser "%.10(date)"                   (defaultStringFormatStyle [FormatField False Nothing (Just 10) DescriptionField])+    , testParser "%20.10(date)"                 (defaultStringFormatStyle [FormatField False (Just 20) (Just 10) DescriptionField])+    , testParser "%20(account) %.10(total)\n"   (defaultStringFormatStyle [FormatField False (Just 20) Nothing AccountField+                                                , FormatLiteral " "+                                                , FormatField False Nothing (Just 10) TotalField+                                                , FormatLiteral "\n"+                                                ])+  ]
Hledger/Data/TimeLog.hs view
@@ -79,6 +79,7 @@         error' $ "clock-out time less than clock-in time in:\n" ++ showTransaction t     where       t = Transaction {+            tindex       = 0,             tsourcepos   = tlsourcepos i,             tdate        = idate,             tdate2       = Nothing,
Hledger/Data/Transaction.hs view
@@ -30,6 +30,7 @@   -- * rendering   showTransaction,   showTransactionUnelided,+  showTransactionUnelidedOneLineAmounts,   -- * misc.   tests_Hledger_Data_Transaction )@@ -40,7 +41,6 @@ import Test.HUnit import Text.Printf import qualified Data.Map as Map-import Text.Parsec.Pos  import Hledger.Utils import Hledger.Data.Types@@ -56,11 +56,12 @@ instance Show PeriodicTransaction where     show t = "~ " ++ ptperiodicexpr t ++ "\n" ++ unlines (map show (ptpostings t)) -nullsourcepos :: SourcePos-nullsourcepos = initialPos ""+nullsourcepos :: GenericSourcePos+nullsourcepos = GenericSourcePos "" 1 1  nulltransaction :: Transaction nulltransaction = Transaction {+                    tindex=0,                     tsourcepos=nullsourcepos,                     tdate=nulldate,                     tdate2=Nothing,@@ -90,10 +91,10 @@ @ -} showTransaction :: Transaction -> String-showTransaction = showTransaction' True+showTransaction = showTransactionHelper True False  showTransactionUnelided :: Transaction -> String-showTransactionUnelided = showTransaction' False+showTransactionUnelided = showTransactionHelper False False  tests_showTransactionUnelided = [    "showTransactionUnelided" ~: do@@ -128,18 +129,19 @@       ]  ] +showTransactionUnelidedOneLineAmounts :: Transaction -> String+showTransactionUnelidedOneLineAmounts = showTransactionHelper False True+ -- cf showPosting-showTransaction' :: Bool -> Transaction -> String-showTransaction' elide t =+showTransactionHelper :: Bool -> Bool -> Transaction -> String+showTransactionHelper elide onelineamounts t =     unlines $ [descriptionline]               ++ newlinecomments-              ++ (postingsAsLines elide t (tpostings t))+              ++ (postingsAsLines elide onelineamounts t (tpostings t))               ++ [""]     where       descriptionline = rstrip $ concat [date, status, code, desc, samelinecomment]-      date = showdate (tdate t) ++ maybe "" showedate (tdate2 t)-      showdate = printf "%-10s" . showDate-      showedate = printf "=%s" . showdate+      date = showDate (tdate t) ++ maybe "" (("="++) . showDate) (tdate2 t)       status | tstatus t == Cleared = " *"              | tstatus t == Pending = " !"              | otherwise            = ""@@ -167,33 +169,41 @@ --       ls = lines s --       prefix = indent . (";"++) -postingsAsLines :: Bool -> Transaction -> [Posting] -> [String]-postingsAsLines elide t ps+postingsAsLines :: Bool -> Bool -> Transaction -> [Posting] -> [String]+postingsAsLines elide onelineamounts t ps     | elide && length ps > 1 && isTransactionBalanced Nothing t -- imprecise balanced check-       = (concatMap (postingAsLines False ps) $ init ps) ++ postingAsLines True ps (last ps)-    | otherwise = concatMap (postingAsLines False ps) ps+       = (concatMap (postingAsLines False onelineamounts ps) $ init ps) ++ postingAsLines True onelineamounts ps (last ps)+    | otherwise = concatMap (postingAsLines False onelineamounts ps) ps -postingAsLines :: Bool -> [Posting] -> Posting -> [String]-postingAsLines elideamount ps p =+postingAsLines :: Bool -> Bool -> [Posting] -> Posting -> [String]+postingAsLines elideamount onelineamounts ps p =     postinglines     ++ newlinecomments   where-    postinglines = map rstrip $ lines $ concatTopPadded [showacct p, "  ", amount, samelinecomment]-    amount = if elideamount then "" else showamt (pamount p)+    postinglines = map rstrip $ lines $ concatTopPadded [account, "  ", amount, samelinecomment]++    account =+      indent $+        showstatus p ++ fitString (Just acctwidth) Nothing False True (showAccountName Nothing (ptype p) (paccount p))+        where+          showstatus p = if pstatus p == Cleared then "* " else ""+          acctwidth = maximum $ map (strWidth . paccount) ps++    -- currently prices are considered part of the amount string when right-aligning amounts+    amount+      | elideamount    = ""+      | onelineamounts = fitString (Just amtwidth) Nothing False False $ showMixedAmountOneLine $ pamount p+      | otherwise      = fitStringMulti (Just amtwidth) Nothing False False $ showMixedAmount $ pamount p+      where+        amtwidth = maximum $ 12 : map (strWidth . showMixedAmount . pamount) ps  -- min. 12 for backwards compatibility+     (samelinecomment, newlinecomments) =       case renderCommentLines (pcomment p) of []   -> ("",[])                                               c:cs -> (c,cs)-    showacct p =-      indent $ showstatus p ++ printf (printf "%%-%ds" w) (showAccountName Nothing (ptype p) (paccount p))-        where-          showstatus p = if pstatus p == Cleared then "* " else ""-          w = maximum $ map (length . paccount) ps-    showamt =-        padleft 12 . showMixedAmount  tests_postingAsLines = [    "postingAsLines" ~: do-    let p `gives` ls = assertEqual "" ls (postingAsLines False [p] p)+    let p `gives` ls = assertEqual "" ls (postingAsLines False False [p] p)     posting `gives` ["                 0"]     posting{       pstatus=Cleared,@@ -410,7 +420,7 @@         ,"    assets:checking"         ,""         ])-       (let t = Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []+       (let t = Transaction 0 nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []                 [posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18], ptransaction=Just t}                 ,posting{paccount="assets:checking", pamount=Mixed [usd (-47.18)], ptransaction=Just t}                 ] ""@@ -424,7 +434,7 @@         ,"    assets:checking               $-47.18"         ,""         ])-       (let t = Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []+       (let t = Transaction 0 nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []                 [posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18], ptransaction=Just t}                 ,posting{paccount="assets:checking", pamount=Mixed [usd (-47.18)], ptransaction=Just t}                 ] ""@@ -440,7 +450,7 @@         ,""         ])        (showTransaction-        (txnTieKnot $ Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []+        (txnTieKnot $ Transaction 0 nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []          [posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18]}          ,posting{paccount="assets:checking", pamount=Mixed [usd (-47.19)]}          ] ""))@@ -453,7 +463,7 @@         ,""         ])        (showTransaction-        (txnTieKnot $ Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []+        (txnTieKnot $ Transaction 0 nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []          [posting{paccount="expenses:food:groceries", pamount=Mixed [usd 47.18]}          ] "")) @@ -465,7 +475,7 @@         ,""         ])        (showTransaction-        (txnTieKnot $ Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []+        (txnTieKnot $ Transaction 0 nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "coopportunity" "" []          [posting{paccount="expenses:food:groceries", pamount=missingmixedamt}          ] "")) @@ -478,7 +488,7 @@         ,""         ])        (showTransaction-        (txnTieKnot $ Transaction nullsourcepos (parsedate "2010/01/01") Nothing Uncleared "" "x" "" []+        (txnTieKnot $ Transaction 0 nullsourcepos (parsedate "2010/01/01") Nothing Uncleared "" "x" "" []          [posting{paccount="a", pamount=Mixed [num 1 `at` (usd 2 `withPrecision` 0)]}          ,posting{paccount="b", pamount= missingmixedamt}          ] ""))@@ -486,19 +496,19 @@   ,"balanceTransaction" ~: do      assertBool "detect unbalanced entry, sign error"                     (isLeft $ balanceTransaction Nothing-                           (Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "test" "" []+                           (Transaction 0 nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "test" "" []                             [posting{paccount="a", pamount=Mixed [usd 1]}                             ,posting{paccount="b", pamount=Mixed [usd 1]}                             ] ""))       assertBool "detect unbalanced entry, multiple missing amounts"                     (isLeft $ balanceTransaction Nothing-                           (Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "test" "" []+                           (Transaction 0 nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "test" "" []                             [posting{paccount="a", pamount=missingmixedamt}                             ,posting{paccount="b", pamount=missingmixedamt}                             ] "")) -     let e = balanceTransaction Nothing (Transaction nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "" "" []+     let e = balanceTransaction Nothing (Transaction 0 nullsourcepos (parsedate "2007/01/28") Nothing Uncleared "" "" "" []                            [posting{paccount="a", pamount=Mixed [usd 1]}                            ,posting{paccount="b", pamount=missingmixedamt}                            ] "")@@ -509,7 +519,7 @@                         Right e' -> (pamount $ last $ tpostings e')                         Left _ -> error' "should not happen") -     let e = balanceTransaction Nothing (Transaction nullsourcepos (parsedate "2011/01/01") Nothing Uncleared "" "" "" []+     let e = balanceTransaction Nothing (Transaction 0 nullsourcepos (parsedate "2011/01/01") Nothing Uncleared "" "" "" []                            [posting{paccount="a", pamount=Mixed [usd 1.35]}                            ,posting{paccount="b", pamount=Mixed [eur (-1)]}                            ] "")@@ -521,49 +531,49 @@                         Left _ -> error' "should not happen")       assertBool "balanceTransaction balances based on cost if there are unit prices" (isRight $-       balanceTransaction Nothing (Transaction nullsourcepos (parsedate "2011/01/01") Nothing Uncleared "" "" "" []+       balanceTransaction Nothing (Transaction 0 nullsourcepos (parsedate "2011/01/01") Nothing Uncleared "" "" "" []                            [posting{paccount="a", pamount=Mixed [usd 1 `at` eur 2]}                            ,posting{paccount="a", pamount=Mixed [usd (-2) `at` eur 1]}                            ] ""))       assertBool "balanceTransaction balances based on cost if there are total prices" (isRight $-       balanceTransaction Nothing (Transaction nullsourcepos (parsedate "2011/01/01") Nothing Uncleared "" "" "" []+       balanceTransaction Nothing (Transaction 0 nullsourcepos (parsedate "2011/01/01") Nothing Uncleared "" "" "" []                            [posting{paccount="a", pamount=Mixed [usd 1    @@ eur 1]}                            ,posting{paccount="a", pamount=Mixed [usd (-2) @@ eur 1]}                            ] ""))    ,"isTransactionBalanced" ~: do-     let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []+     let t = Transaction 0 nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []              [posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}              ,posting{paccount="c", pamount=Mixed [usd (-1.00)], ptransaction=Just t}              ] ""      assertBool "detect balanced" (isTransactionBalanced Nothing t)-     let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []+     let t = Transaction 0 nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []              [posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}              ,posting{paccount="c", pamount=Mixed [usd (-1.01)], ptransaction=Just t}              ] ""      assertBool "detect unbalanced" (not $ isTransactionBalanced Nothing t)-     let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []+     let t = Transaction 0 nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []              [posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}              ] ""      assertBool "detect unbalanced, one posting" (not $ isTransactionBalanced Nothing t)-     let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []+     let t = Transaction 0 nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []              [posting{paccount="b", pamount=Mixed [usd 0], ptransaction=Just t}              ] ""      assertBool "one zero posting is considered balanced for now" (isTransactionBalanced Nothing t)-     let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []+     let t = Transaction 0 nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []              [posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}              ,posting{paccount="c", pamount=Mixed [usd (-1.00)], ptransaction=Just t}              ,posting{paccount="d", pamount=Mixed [usd 100], ptype=VirtualPosting, ptransaction=Just t}              ] ""      assertBool "virtual postings don't need to balance" (isTransactionBalanced Nothing t)-     let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []+     let t = Transaction 0 nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []              [posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}              ,posting{paccount="c", pamount=Mixed [usd (-1.00)], ptransaction=Just t}              ,posting{paccount="d", pamount=Mixed [usd 100], ptype=BalancedVirtualPosting, ptransaction=Just t}              ] ""      assertBool "balanced virtual postings need to balance among themselves" (not $ isTransactionBalanced Nothing t)-     let t = Transaction nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []+     let t = Transaction 0 nullsourcepos (parsedate "2009/01/01") Nothing Uncleared "" "a" "" []              [posting{paccount="b", pamount=Mixed [usd 1.00], ptransaction=Just t}              ,posting{paccount="c", pamount=Mixed [usd (-1.00)], ptransaction=Just t}              ,posting{paccount="d", pamount=Mixed [usd 100], ptype=BalancedVirtualPosting, ptransaction=Just t}
Hledger/Data/Types.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, DeriveDataTypeable, StandaloneDeriving, TypeSynonymInstances, FlexibleInstances #-}+{-# LANGUAGE CPP, DeriveDataTypeable, StandaloneDeriving, DeriveGeneric, TypeSynonymInstances, FlexibleInstances #-} {-|  Most data types are defined here to avoid import cycles.@@ -7,7 +7,7 @@ > Journal                  -- a journal is read from one or more data files. It contains.. >  [Transaction]           -- journal transactions (aka entries), which have date, cleared status, code, description and.. >   [Posting]              -- multiple account postings, which have account name and amount->  [HistoricalPrice]       -- historical commodity prices+>  [MarketPrice]           -- historical market prices for commodities > > Ledger                   -- a ledger is derived from a journal, by applying a filter specification and doing some further processing. It contains.. >  Journal                 -- a filtered copy of the original journal, containing only the transactions and postings we are interested in@@ -19,6 +19,9 @@  module Hledger.Data.Types where++import GHC.Generics (Generic)+import Control.DeepSeq (NFData) import Control.Monad.Except (ExceptT) import Data.Data #ifndef DOUBLE@@ -29,7 +32,6 @@ import Data.Time.Calendar import Data.Time.LocalTime import System.Time (ClockTime(..))-import Text.Parsec.Pos  import Hledger.Utils.Regex @@ -38,29 +40,30 @@  data WhichDate = PrimaryDate | SecondaryDate deriving (Eq,Show) -data DateSpan = DateSpan (Maybe Day) (Maybe Day) deriving (Eq,Ord,Data,Typeable)+data DateSpan = DateSpan (Maybe Day) (Maybe Day) deriving (Eq,Ord,Data,Generic,Typeable) +instance NFData DateSpan+ data Interval = NoInterval               | Days Int | Weeks Int | Months Int | Quarters Int | Years Int               | DayOfMonth Int | DayOfWeek Int               -- WeekOfYear Int | MonthOfYear Int | QuarterOfYear Int-                deriving (Eq,Show,Ord,Data,Typeable)+                deriving (Eq,Show,Ord,Data,Generic,Typeable) +instance NFData Interval+ type AccountName = String  data AccountAlias = BasicAlias AccountName AccountName                   | RegexAlias Regexp Replacement-  deriving (-  Eq-  ,Read-  ,Show-  ,Ord-  ,Data-  ,Typeable-  )+  deriving (Eq, Read, Show, Ord, Data, Generic, Typeable) -data Side = L | R deriving (Eq,Show,Read,Ord,Typeable,Data)+instance NFData AccountAlias +data Side = L | R deriving (Eq,Show,Read,Ord,Typeable,Data,Generic)++instance NFData Side+ type Commodity = String  -- | The basic numeric type used in amounts. Different implementations@@ -74,7 +77,7 @@ deriving instance Data (Quantity) -- The following is for hledger-web, and requires blaze-markup. -- Doing it here avoids needing a matching flag on the hledger-web package.-instance ToMarkup (Quantity) +instance ToMarkup (Quantity)  where    toMarkup = toMarkup . show numberRepresentation = "Decimal"@@ -82,8 +85,10 @@  -- | An amount's price (none, per unit, or total) in another commodity. -- Note the price should be a positive number, although this is not enforced.-data Price = NoPrice | UnitPrice Amount | TotalPrice Amount deriving (Eq,Ord,Typeable,Data)+data Price = NoPrice | UnitPrice Amount | TotalPrice Amount deriving (Eq,Ord,Typeable,Data,Generic) +instance NFData Price+ -- | Display style for an amount. data AmountStyle = AmountStyle {       ascommodityside :: Side,       -- ^ does the symbol appear on the left or the right ?@@ -91,8 +96,10 @@       asprecision :: Int,            -- ^ number of digits displayed after the decimal point       asdecimalpoint :: Maybe Char,  -- ^ character used as decimal point: period or comma. Nothing means "unspecified, use default"       asdigitgroups :: Maybe DigitGroupStyle -- ^ style for displaying digit groups, if any-} deriving (Eq,Ord,Read,Show,Typeable,Data)+} deriving (Eq,Ord,Read,Show,Typeable,Data,Generic) +instance NFData AmountStyle+ -- | A style for displaying digit groups in the integer part of a -- floating point number. It consists of the character used to -- separate groups (comma or period, whichever is not used as decimal@@ -100,25 +107,35 @@ -- the decimal point. The last group size is assumed to repeat. Eg, -- comma between thousands is DigitGroups ',' [3]. data DigitGroupStyle = DigitGroups Char [Int]-  deriving (Eq,Ord,Read,Show,Typeable,Data)+  deriving (Eq,Ord,Read,Show,Typeable,Data,Generic) +instance NFData DigitGroupStyle+ data Amount = Amount {       acommodity :: Commodity,       aquantity :: Quantity,       aprice :: Price,                -- ^ the (fixed) price for this amount, if any       astyle :: AmountStyle-    } deriving (Eq,Ord,Typeable,Data)+    } deriving (Eq,Ord,Typeable,Data,Generic) -newtype MixedAmount = Mixed [Amount] deriving (Eq,Ord,Typeable,Data)+instance NFData Amount +newtype MixedAmount = Mixed [Amount] deriving (Eq,Ord,Typeable,Data,Generic)++instance NFData MixedAmount+ data PostingType = RegularPosting | VirtualPosting | BalancedVirtualPosting-                   deriving (Eq,Show,Typeable,Data)+                   deriving (Eq,Show,Typeable,Data,Generic) +instance NFData PostingType+ type Tag = (String, String)  -- ^ A tag name and (possibly empty) value.  data ClearedStatus = Uncleared | Pending | Cleared-                   deriving (Eq,Ord,Typeable,Data)+                   deriving (Eq,Ord,Typeable,Data,Generic) +instance NFData ClearedStatus+ instance Show ClearedStatus where -- custom show   show Uncleared = ""             -- a bad idea   show Pending   = "!"            -- don't do it@@ -136,15 +153,25 @@       pbalanceassertion :: Maybe MixedAmount,  -- ^ optional: the expected balance in the account after this posting       ptransaction :: Maybe Transaction    -- ^ this posting's parent transaction (co-recursive types).                                            -- Tying this knot gets tedious, Maybe makes it easier/optional.-    } deriving (Typeable,Data)+    } deriving (Typeable,Data,Generic) +instance NFData Posting+ -- The equality test for postings ignores the parent transaction's -- identity, to avoid infinite loops. instance Eq Posting where     (==) (Posting a1 b1 c1 d1 e1 f1 g1 h1 i1 _) (Posting a2 b2 c2 d2 e2 f2 g2 h2 i2 _) =  a1==a2 && b1==b2 && c1==c2 && d1==d2 && e1==e2 && f1==f2 && g1==g2 && h1==h2 && i1==i2 +-- | The position of parse errors (eg), like parsec's SourcePos but generic.+-- File name, 1-based line number and 1-based column number.+data GenericSourcePos = GenericSourcePos FilePath Int Int+  deriving (Eq, Read, Show, Ord, Data, Generic, Typeable)++instance NFData GenericSourcePos+ data Transaction = Transaction {-      tsourcepos :: SourcePos,+      tindex :: Integer, -- ^ this transaction's 1-based position in the input stream, or 0 when not available+      tsourcepos :: GenericSourcePos,       tdate :: Day,       tdate2 :: Maybe Day,       tstatus :: ClearedStatus,@@ -154,34 +181,46 @@       ttags :: [Tag], -- ^ tag names and values, extracted from the comment       tpostings :: [Posting],            -- ^ this transaction's postings       tpreceding_comment_lines :: String -- ^ any comment lines immediately preceding this transaction-    } deriving (Eq,Typeable,Data)+    } deriving (Eq,Typeable,Data,Generic) +instance NFData Transaction+ data ModifierTransaction = ModifierTransaction {       mtvalueexpr :: String,       mtpostings :: [Posting]-    } deriving (Eq,Typeable,Data)+    } deriving (Eq,Typeable,Data,Generic) +instance NFData ModifierTransaction+ data PeriodicTransaction = PeriodicTransaction {       ptperiodicexpr :: String,       ptpostings :: [Posting]-    } deriving (Eq,Typeable,Data)+    } deriving (Eq,Typeable,Data,Generic) -data TimeLogCode = SetBalance | SetRequiredHours | In | Out | FinalOut deriving (Eq,Ord,Typeable,Data)+instance NFData PeriodicTransaction +data TimeLogCode = SetBalance | SetRequiredHours | In | Out | FinalOut deriving (Eq,Ord,Typeable,Data,Generic)++instance NFData TimeLogCode+ data TimeLogEntry = TimeLogEntry {-      tlsourcepos :: SourcePos,+      tlsourcepos :: GenericSourcePos,       tlcode :: TimeLogCode,       tldatetime :: LocalTime,       tlaccount :: String,       tldescription :: String-    } deriving (Eq,Ord,Typeable,Data)+    } deriving (Eq,Ord,Typeable,Data,Generic) -data HistoricalPrice = HistoricalPrice {-      hdate :: Day,-      hcommodity :: Commodity,-      hamount :: Amount-    } deriving (Eq,Typeable,Data) -- & Show (in Amount.hs)+instance NFData TimeLogEntry +data MarketPrice = MarketPrice {+      mpdate :: Day,+      mpcommodity :: Commodity,+      mpamount :: Amount+    } deriving (Eq,Ord,Typeable,Data,Generic) -- & Show (in Amount.hs)++instance NFData MarketPrice+ type Year = Integer  -- | A journal "context" is some data which can change in the course of@@ -195,17 +234,23 @@                                         --   specified with "account" directive(s). Concatenated, these                                         --   are the account prefix prepended to parsed account names.     , ctxAliases   :: ![AccountAlias]   -- ^ the current list of account name aliases in effect-    } deriving (Read, Show, Eq, Data, Typeable)+    , ctxTransactionIndex   :: !Integer -- ^ the number of transactions read so far+    } deriving (Read, Show, Eq, Data, Typeable, Generic) +instance NFData JournalContext+ deriving instance Data (ClockTime) deriving instance Typeable (ClockTime)+deriving instance Generic (ClockTime) +instance NFData ClockTime+ data Journal = Journal {       jmodifiertxns :: [ModifierTransaction],       jperiodictxns :: [PeriodicTransaction],       jtxns :: [Transaction],       open_timelog_entries :: [TimeLogEntry],-      historical_prices :: [HistoricalPrice],+      jmarketprices :: [MarketPrice],       final_comment_lines :: String,        -- ^ any trailing comments from the journal file       jContext :: JournalContext,           -- ^ the context (parse state) at the end of parsing       files :: [(FilePath, String)],        -- ^ the file path and raw text of the main and@@ -214,17 +259,20 @@                                             -- order encountered.       filereadtime :: ClockTime,            -- ^ when this journal was last read from its file(s)       jcommoditystyles :: M.Map Commodity AmountStyle  -- ^ how to display amounts in each commodity-    } deriving (Eq, Typeable, Data)+    } deriving (Eq, Typeable, Data, Generic) +instance NFData Journal+ -- | A JournalUpdate is some transformation of a Journal. It can do I/O or--- raise an error.+-- raise an exception. type JournalUpdate = ExceptT String IO (Journal -> Journal)  -- | The id of a data format understood by hledger, eg @journal@ or @csv@.+-- The --output-format option selects one of these for output. type StorageFormat = String --- | A hledger journal reader is a triple of format name, format-detecting--- predicate, and a parser to Journal.+-- | A hledger journal reader is a triple of storage format name, a+-- detector of that format, and a parser from that format to Journal. data Reader = Reader {      -- name of the format this reader handles      rFormat   :: StorageFormat@@ -236,26 +284,6 @@  instance Show Reader where show r = rFormat r ++ " reader" --- format strings--data HledgerFormatField =-    AccountField-  | DefaultDateField-  | DescriptionField-  | TotalField-  | DepthSpacerField-  | FieldNo Int-    deriving (Show, Eq)--data OutputFormat =-    FormatLiteral String-  | FormatField Bool        -- Left justified ?-                (Maybe Int) -- Min width-                (Maybe Int) -- Max width-                HledgerFormatField       -- Field-  deriving (Show, Eq)-- -- | An account, with name, balances and links to parent/subaccounts -- which let you walk up or down the account tree. data Account = Account {@@ -269,8 +297,6 @@   aboring :: Bool           -- ^ used in the accounts report to label elidable parents   } -- -- | A Ledger has the journal it derives from, and the accounts -- derived from that. Accounts are accessible both list-wise and -- tree-wise, since each one knows its parent and subs; the first@@ -279,3 +305,4 @@   ljournal :: Journal,   laccounts :: [Account] }+
Hledger/Query.hs view
@@ -78,7 +78,9 @@            | Sym Regexp       -- ^ match if the entire commodity symbol is matched by this regexp            | Empty Bool       -- ^ if true, show zero-amount postings/accounts which are usually not shown                               --   more of a query option than a query criteria ?-           | Depth Int        -- ^ match if account depth is less than or equal to this value+           | Depth Int        -- ^ match if account depth is less than or equal to this value.+                              --   Depth is sometimes used like a query (for filtering report data)+                              --   and sometimes like a query option (for controlling display)            | Tag Regexp (Maybe Regexp)  -- ^ match if a tag's name, and optionally its value, is matched by these respective regexps                                         -- matching the regexp if provided, exists     deriving (Eq,Data,Typeable)@@ -254,7 +256,11 @@ parseQueryTerm _ ('r':'e':'a':'l':':':s) = Left $ Real $ parseBool s || null s parseQueryTerm _ ('a':'m':'t':':':s) = Left $ Amt ord q where (ord, q) = parseAmountQueryTerm s parseQueryTerm _ ('e':'m':'p':'t':'y':':':s) = Left $ Empty $ parseBool s-parseQueryTerm _ ('d':'e':'p':'t':'h':':':s) = Left $ Depth $ readDef 0 s+parseQueryTerm _ ('d':'e':'p':'t':'h':':':s)+  | n >= 0    = Left $ Depth n+  | otherwise = error' "depth: should have a positive number"+  where n = readDef 0 s+ parseQueryTerm _ ('c':'u':'r':':':s) = Left $ Sym s -- support cur: as an alias parseQueryTerm _ ('t':'a':'g':':':s) = Left $ Tag n v where (n,v) = parseTag s parseQueryTerm _ "" = Left $ Any
Hledger/Read.hs view
@@ -244,9 +244,9 @@    tests_Hledger_Read_CsvReader,     "journal" ~: do-    r <- runExceptT $ parseWithCtx nullctx JournalReader.journal ""-    assertBool "journal should parse an empty file" (isRight $ r)+    r <- runExceptT $ parseWithCtx nullctx JournalReader.journalp ""+    assertBool "journalp should parse an empty file" (isRight $ r)     jE <- readJournal Nothing Nothing True Nothing "" -- don't know how to get it from journal-    either error' (assertBool "journal parsing an empty file should give an empty journal" . null . jtxns) jE+    either error' (assertBool "journalp parsing an empty file should give an empty journal" . null . jtxns) jE    ]
Hledger/Read/CsvReader.hs view
@@ -51,7 +51,7 @@ import Hledger.Data import Hledger.Utils.UTF8IOCompat (getContents) import Hledger.Utils-import Hledger.Read.JournalReader (amountp, statusp)+import Hledger.Read.JournalReader (amountp, statusp, genericSourcePos)   reader :: Reader@@ -392,11 +392,11 @@ rulesp :: Stream [Char] m t => ParsecT [Char] CsvRules m CsvRules rulesp = do   many $ choice'-    [blankorcommentline                                                    <?> "blank or comment line"-    ,(directive        >>= modifyState . addDirective)                     <?> "directive"-    ,(fieldnamelist    >>= modifyState . setIndexesAndAssignmentsFromList) <?> "field name list"-    ,(fieldassignment  >>= modifyState . addAssignment)                    <?> "field assignment"-    ,(conditionalblock >>= modifyState . addConditionalBlock)              <?> "conditional block"+    [blankorcommentlinep                                                    <?> "blank or comment line"+    ,(directivep        >>= modifyState . addDirective)                     <?> "directive"+    ,(fieldnamelistp    >>= modifyState . setIndexesAndAssignmentsFromList) <?> "field name list"+    ,(fieldassignmentp  >>= modifyState . addAssignment)                    <?> "field assignment"+    ,(conditionalblockp >>= modifyState . addConditionalBlock)              <?> "conditional block"     ]   eof   r <- getState@@ -405,23 +405,23 @@           ,rconditionalblocks=reverse $ rconditionalblocks r           } -blankorcommentline :: Stream [Char] m t => ParsecT [Char] CsvRules m ()-blankorcommentline = pdbg 3 "trying blankorcommentline" >> choice' [blankline, commentline]+blankorcommentlinep :: Stream [Char] m t => ParsecT [Char] CsvRules m ()+blankorcommentlinep = pdbg 3 "trying blankorcommentlinep" >> choice' [blanklinep, commentlinep] -blankline :: Stream [Char] m t => ParsecT [Char] CsvRules m ()-blankline = many spacenonewline >> newline >> return () <?> "blank line"+blanklinep :: Stream [Char] m t => ParsecT [Char] CsvRules m ()+blanklinep = many spacenonewline >> newline >> return () <?> "blank line" -commentline :: Stream [Char] m t => ParsecT [Char] CsvRules m ()-commentline = many spacenonewline >> commentchar >> restofline >> return () <?> "comment line"+commentlinep :: Stream [Char] m t => ParsecT [Char] CsvRules m ()+commentlinep = many spacenonewline >> commentcharp >> restofline >> return () <?> "comment line" -commentchar :: Stream [Char] m t => ParsecT [Char] CsvRules m Char-commentchar = oneOf ";#*"+commentcharp :: Stream [Char] m t => ParsecT [Char] CsvRules m Char+commentcharp = oneOf ";#*" -directive :: Stream [Char] m t => ParsecT [Char] CsvRules m (DirectiveName, String)-directive = do+directivep :: Stream [Char] m t => ParsecT [Char] CsvRules m (DirectiveName, String)+directivep = do   pdbg 3 "trying directive"   d <- choice' $ map string directives-  v <- (((char ':' >> many spacenonewline) <|> many1 spacenonewline) >> directiveval)+  v <- (((char ':' >> many spacenonewline) <|> many1 spacenonewline) >> directivevalp)        <|> (optional (char ':') >> many spacenonewline >> eolof >> return "")   return (d,v)   <?> "directive"@@ -436,46 +436,46 @@    -- ,"base-currency"   ] -directiveval :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]-directiveval = anyChar `manyTill` eolof+directivevalp :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]+directivevalp = anyChar `manyTill` eolof -fieldnamelist :: Stream [Char] m t => ParsecT [Char] CsvRules m [CsvFieldName]-fieldnamelist = (do+fieldnamelistp :: Stream [Char] m t => ParsecT [Char] CsvRules m [CsvFieldName]+fieldnamelistp = (do   pdbg 3 "trying fieldnamelist"   string "fields"   optional $ char ':'   many1 spacenonewline   let separator = many spacenonewline >> char ',' >> many spacenonewline-  f <- fromMaybe "" <$> optionMaybe fieldname-  fs <- many1 $ (separator >> fromMaybe "" <$> optionMaybe fieldname)+  f <- fromMaybe "" <$> optionMaybe fieldnamep+  fs <- many1 $ (separator >> fromMaybe "" <$> optionMaybe fieldnamep)   restofline   return $ map (map toLower) $ f:fs   ) <?> "field name list" -fieldname :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]-fieldname = quotedfieldname <|> barefieldname+fieldnamep :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]+fieldnamep = quotedfieldnamep <|> barefieldnamep -quotedfieldname :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]-quotedfieldname = do+quotedfieldnamep :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]+quotedfieldnamep = do   char '"'   f <- many1 $ noneOf "\"\n:;#~"   char '"'   return f -barefieldname :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]-barefieldname = many1 $ noneOf " \t\n,;#~"+barefieldnamep :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]+barefieldnamep = many1 $ noneOf " \t\n,;#~" -fieldassignment :: Stream [Char] m t => ParsecT [Char] CsvRules m (JournalFieldName, FieldTemplate)-fieldassignment = do+fieldassignmentp :: Stream [Char] m t => ParsecT [Char] CsvRules m (JournalFieldName, FieldTemplate)+fieldassignmentp = do   pdbg 3 "trying fieldassignment"-  f <- journalfieldname-  assignmentseparator-  v <- fieldval+  f <- journalfieldnamep+  assignmentseparatorp+  v <- fieldvalp   return (f,v)   <?> "field assignment" -journalfieldname :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]-journalfieldname = pdbg 2 "trying journalfieldname" >> choice' (map string journalfieldnames)+journalfieldnamep :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]+journalfieldnamep = pdbg 2 "trying journalfieldnamep" >> choice' (map string journalfieldnames)  journalfieldnames =   [-- pseudo fields:@@ -494,9 +494,9 @@   ,"comment"   ] -assignmentseparator :: Stream [Char] m t => ParsecT [Char] CsvRules m ()-assignmentseparator = do-  pdbg 3 "trying assignmentseparator"+assignmentseparatorp :: Stream [Char] m t => ParsecT [Char] CsvRules m ()+assignmentseparatorp = do+  pdbg 3 "trying assignmentseparatorp"   choice [     -- try (many spacenonewline >> oneOf ":="),     try (many spacenonewline >> char ':'),@@ -505,51 +505,51 @@   _ <- many spacenonewline   return () -fieldval :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]-fieldval = do+fieldvalp :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]+fieldvalp = do   pdbg 2 "trying fieldval"   anyChar `manyTill` eolof -conditionalblock :: Stream [Char] m t => ParsecT [Char] CsvRules m ConditionalBlock-conditionalblock = do-  pdbg 3 "trying conditionalblock"+conditionalblockp :: Stream [Char] m t => ParsecT [Char] CsvRules m ConditionalBlock+conditionalblockp = do+  pdbg 3 "trying conditionalblockp"   string "if" >> many spacenonewline >> optional newline-  ms <- many1 recordmatcher-  as <- many (many1 spacenonewline >> fieldassignment)+  ms <- many1 recordmatcherp+  as <- many (many1 spacenonewline >> fieldassignmentp)   when (null as) $     fail "start of conditional block found, but no assignment rules afterward\n(assignment rules in a conditional block should be indented)\n"   return (ms, as)   <?> "conditional block" -recordmatcher :: Stream [Char] m t => ParsecT [Char] CsvRules m [[Char]]-recordmatcher = do-  pdbg 2 "trying recordmatcher"+recordmatcherp :: Stream [Char] m t => ParsecT [Char] CsvRules m [[Char]]+recordmatcherp = do+  pdbg 2 "trying recordmatcherp"   -- pos <- currentPos-  _  <- optional (matchoperator >> many spacenonewline >> optional newline)-  ps <- patterns+  _  <- optional (matchoperatorp >> many spacenonewline >> optional newline)+  ps <- patternsp   when (null ps) $     fail "start of record matcher found, but no patterns afterward\n(patterns should not be indented)\n"   return ps   <?> "record matcher" -matchoperator :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]-matchoperator = choice' $ map string+matchoperatorp :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char]+matchoperatorp = choice' $ map string   ["~"   -- ,"!~"   -- ,"="   -- ,"!="   ] -patterns :: Stream [Char] m t => ParsecT [Char] CsvRules m [[Char]]-patterns = do-  pdbg 3 "trying patterns"+patternsp :: Stream [Char] m t => ParsecT [Char] CsvRules m [[Char]]+patternsp = do+  pdbg 3 "trying patternsp"   ps <- many regexp   return ps  regexp :: Stream [Char] m t => ParsecT [Char] CsvRules m [Char] regexp = do   pdbg 3 "trying regexp"-  notFollowedBy matchoperator+  notFollowedBy matchoperatorp   c <- nonspace   cs <- anyChar `manyTill` eolof   return $ strip $ c:cs@@ -643,7 +643,7 @@      -- build the transaction     t = nulltransaction{-      tsourcepos               = sourcepos,+      tsourcepos               = genericSourcePos sourcepos,       tdate                    = date',       tdate2                   = mdate2',       tstatus                  = status,
Hledger/Read/JournalReader.hs view
@@ -21,16 +21,17 @@   -- * Reader   reader,   -- * Parsers used elsewhere-  parseJournalWith,+  parseAndFinaliseJournal,+  genericSourcePos,   getParentAccount,-  journal,-  directive,-  defaultyeardirective,-  historicalpricedirective,+  journalp,+  directivep,+  defaultyeardirectivep,+  marketpricedirectivep,   datetimep,   codep,   accountnamep,-  modifiedaccountname,+  modifiedaccountnamep,   postingp,   amountp,   amountp',@@ -93,25 +94,85 @@ -- | Parse and post-process a "Journal" from hledger's journal file -- format, or give an error. parse :: Maybe FilePath -> Bool -> FilePath -> String -> ExceptT String IO Journal-parse _ = parseJournalWith journal+parse _ = parseAndFinaliseJournal journalp  -- parsing utils --- | Flatten a list of JournalUpdate's into a single equivalent one.+genericSourcePos :: SourcePos -> GenericSourcePos+genericSourcePos p = GenericSourcePos (sourceName p) (sourceLine p) (sourceColumn p)++-- | Flatten a list of JournalUpdate's (journal-transforming+-- monadic actions which can do IO or raise an exception) into a+-- single equivalent action. combineJournalUpdates :: [JournalUpdate] -> JournalUpdate-combineJournalUpdates us = liftM (foldl' (\acc new x -> new (acc x)) id) $ sequence us+combineJournalUpdates us = foldl' (flip (.)) id <$> sequence us+-- XXX may be contributing to excessive stack use +-- cf http://neilmitchell.blogspot.co.uk/2015/09/detecting-space-leaks.html+-- $ ./devprof +RTS -K576K -xc+-- *** Exception (reporting due to +RTS -xc): (THUNK_STATIC), stack trace: +--   Hledger.Read.JournalReader.combineJournalUpdates.\,+--   called from Hledger.Read.JournalReader.combineJournalUpdates,+--   called from Hledger.Read.JournalReader.fixedlotprice,+--   called from Hledger.Read.JournalReader.partialbalanceassertion,+--   called from Hledger.Read.JournalReader.getDefaultCommodityAndStyle,+--   called from Hledger.Read.JournalReader.priceamount,+--   called from Hledger.Read.JournalReader.nosymbolamount,+--   called from Hledger.Read.JournalReader.numberp,+--   called from Hledger.Read.JournalReader.rightsymbolamount,+--   called from Hledger.Read.JournalReader.simplecommoditysymbol,+--   called from Hledger.Read.JournalReader.quotedcommoditysymbol,+--   called from Hledger.Read.JournalReader.commoditysymbol,+--   called from Hledger.Read.JournalReader.signp,+--   called from Hledger.Read.JournalReader.leftsymbolamount,+--   called from Hledger.Read.JournalReader.amountp,+--   called from Hledger.Read.JournalReader.spaceandamountormissing,+--   called from Hledger.Read.JournalReader.accountnamep.singlespace,+--   called from Hledger.Utils.Parse.nonspace,+--   called from Hledger.Read.JournalReader.accountnamep,+--   called from Hledger.Read.JournalReader.getAccountAliases,+--   called from Hledger.Read.JournalReader.getParentAccount,+--   called from Hledger.Read.JournalReader.modifiedaccountnamep,+--   called from Hledger.Read.JournalReader.postingp,+--   called from Hledger.Read.JournalReader.postings,+--   called from Hledger.Read.JournalReader.commentStartingWith,+--   called from Hledger.Read.JournalReader.semicoloncomment,+--   called from Hledger.Read.JournalReader.followingcommentp,+--   called from Hledger.Read.JournalReader.descriptionp,+--   called from Hledger.Read.JournalReader.codep,+--   called from Hledger.Read.JournalReader.statusp,+--   called from Hledger.Utils.Parse.spacenonewline,+--   called from Hledger.Read.JournalReader.secondarydatep,+--   called from Hledger.Data.Dates.datesepchar,+--   called from Hledger.Read.JournalReader.datep,+--   called from Hledger.Read.JournalReader.transaction,+--   called from Hledger.Utils.Parse.choice',+--   called from Hledger.Read.JournalReader.directive,+--   called from Hledger.Read.JournalReader.emptyorcommentlinep,+--   called from Hledger.Read.JournalReader.multilinecommentp,+--   called from Hledger.Read.JournalReader.journal.journalItem,+--   called from Hledger.Read.JournalReader.journal,+--   called from Hledger.Read.JournalReader.parseJournalWith,+--   called from Hledger.Read.readJournal.tryReaders.firstSuccessOrBestError,+--   called from Hledger.Read.readJournal.tryReaders,+--   called from Hledger.Read.readJournal,+--   called from Main.main,+--   called from Main.CAF+-- Stack space overflow: current size 33568 bytes.+ -- | Given a JournalUpdate-generating parsec parser, file path and data string, -- parse and post-process a Journal so that it's ready to use, or give an error.-parseJournalWith :: (ParsecT [Char] JournalContext (ExceptT String IO) (JournalUpdate,JournalContext)) -> Bool -> FilePath -> String -> ExceptT String IO Journal-parseJournalWith p assrt f s = do+parseAndFinaliseJournal ::+  (ParsecT [Char] JournalContext (ExceptT String IO) (JournalUpdate,JournalContext))+  -> Bool -> FilePath -> String -> ExceptT String IO Journal+parseAndFinaliseJournal parser assrt f s = do   tc <- liftIO getClockTime   tl <- liftIO getCurrentLocalTime   y <- liftIO getCurrentYear-  r <- runParserT p nullctx{ctxYear=Just y} f s+  r <- runParserT parser nullctx{ctxYear=Just y} f s   case r of     Right (updates,ctx) -> do-                           j <- updates `ap` return nulljournal+                           j <- ap updates (return nulljournal)                            case journalFinalise tc tl f s ctx assrt j of                              Right j'  -> return j'                              Left estr -> throwError estr@@ -151,13 +212,19 @@ clearAccountAliases :: Stream [Char] m Char => ParsecT [Char] JournalContext m () clearAccountAliases = modifyState (\(ctx@Ctx{..}) -> ctx{ctxAliases=[]}) +getIndex :: Stream [Char] m Char => ParsecT s JournalContext m Integer+getIndex = liftM ctxTransactionIndex getState++setIndex :: Stream [Char] m Char => Integer -> ParsecT [Char] JournalContext m ()+setIndex i = modifyState (\ctx -> ctx{ctxTransactionIndex=i})+ -- parsers  -- | Top-level journal parser. Returns a single composite, I/O performing, -- error-raising "JournalUpdate" (and final "JournalContext") which can be -- applied to an empty journal to get the final result.-journal :: ParsecT [Char] JournalContext (ExceptT String IO) (JournalUpdate,JournalContext)-journal = do+journalp :: ParsecT [Char] JournalContext (ExceptT String IO) (JournalUpdate,JournalContext)+journalp = do   journalupdates <- many journalItem   eof   finalctx <- getState@@ -166,36 +233,36 @@       -- As all journal line types can be distinguished by the first       -- character, excepting transactions versus empty (blank or       -- comment-only) lines, can use choice w/o try-      journalItem = choice [ directive-                           , liftM (return . addTransaction) transaction-                           , liftM (return . addModifierTransaction) modifiertransaction-                           , liftM (return . addPeriodicTransaction) periodictransaction-                           , liftM (return . addHistoricalPrice) historicalpricedirective+      journalItem = choice [ directivep+                           , liftM (return . addTransaction) transactionp+                           , liftM (return . addModifierTransaction) modifiertransactionp+                           , liftM (return . addPeriodicTransaction) periodictransactionp+                           , liftM (return . addMarketPrice) marketpricedirectivep                            , emptyorcommentlinep >> return (return id)                            , multilinecommentp >> return (return id)                            ] <?> "journal transaction or directive"  -- cf http://ledger-cli.org/3.0/doc/ledger3.html#Command-Directives-directive :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate-directive = do+directivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate+directivep = do   optional $ char '!'   choice' [-    includedirective-   ,aliasdirective-   ,endaliasesdirective-   ,accountdirective-   ,enddirective-   ,tagdirective-   ,endtagdirective-   ,defaultyeardirective-   ,defaultcommoditydirective-   ,commodityconversiondirective-   ,ignoredpricecommoditydirective+    includedirectivep+   ,aliasdirectivep+   ,endaliasesdirectivep+   ,accountdirectivep+   ,enddirectivep+   ,tagdirectivep+   ,endtagdirectivep+   ,defaultyeardirectivep+   ,defaultcommoditydirectivep+   ,commodityconversiondirectivep+   ,ignoredpricecommoditydirectivep    ]   <?> "directive" -includedirective :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate-includedirective = do+includedirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate+includedirectivep = do   string "include"   many1 spacenonewline   filename <- restofline@@ -206,7 +273,7 @@        filepath <- expandPath curdir filename        txt <- readFileOrError outerPos filepath        let inIncluded = show outerPos ++ " in included file " ++ show filename ++ ":\n"-       r <- runParserT journal outerState filepath txt+       r <- runParserT journalp outerState filepath txt        case r of          Right (ju, ctx) -> do                             u <- combineJournalUpdates [ return $ journalAddFile (filepath,txt)@@ -226,8 +293,8 @@ journalAddFile f j@Journal{files=fs} = j{files=fs++[f]}  -- NOTE: first encountered file to left, to avoid a reverse -accountdirective :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate-accountdirective = do+accountdirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate+accountdirectivep = do   string "account"   many1 spacenonewline   parent <- accountnamep@@ -236,15 +303,15 @@   -- return $ return id   return $ ExceptT $ return $ Right id -enddirective :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate-enddirective = do+enddirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate+enddirectivep = do   string "end"   popParentAccount   -- return (return id)   return $ ExceptT $ return $ Right id -aliasdirective :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate-aliasdirective = do+aliasdirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate+aliasdirectivep = do   string "alias"   many1 spacenonewline   alias <- accountaliasp@@ -275,28 +342,28 @@   repl <- rstrip <$> anyChar `manyTill` eolof   return $ RegexAlias re repl -endaliasesdirective :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate-endaliasesdirective = do+endaliasesdirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate+endaliasesdirectivep = do   string "end aliases"   clearAccountAliases   return (return id) -tagdirective :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate-tagdirective = do+tagdirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate+tagdirectivep = do   string "tag" <?> "tag directive"   many1 spacenonewline   _ <- many1 nonspace   restofline   return $ return id -endtagdirective :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate-endtagdirective = do+endtagdirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate+endtagdirectivep = do   (string "end tag" <|> string "pop") <?> "end tag or pop directive"   restofline   return $ return id -defaultyeardirective :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate-defaultyeardirective = do+defaultyeardirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate+defaultyeardirectivep = do   char 'Y' <?> "default year"   many spacenonewline   y <- many1 digit@@ -305,8 +372,8 @@   setYear y'   return $ return id -defaultcommoditydirective :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate-defaultcommoditydirective = do+defaultcommoditydirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate+defaultcommoditydirectivep = do   char 'D' <?> "default commodity"   many1 spacenonewline   Amount{..} <- amountp@@ -314,28 +381,28 @@   restofline   return $ return id -historicalpricedirective :: ParsecT [Char] JournalContext (ExceptT String IO) HistoricalPrice-historicalpricedirective = do-  char 'P' <?> "historical price"+marketpricedirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) MarketPrice+marketpricedirectivep = do+  char 'P' <?> "market price"   many spacenonewline   date <- try (do {LocalTime d _ <- datetimep; return d}) <|> datep -- a time is ignored   many1 spacenonewline-  symbol <- commoditysymbol+  symbol <- commoditysymbolp   many spacenonewline   price <- amountp   restofline-  return $ HistoricalPrice date symbol price+  return $ MarketPrice date symbol price -ignoredpricecommoditydirective :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate-ignoredpricecommoditydirective = do+ignoredpricecommoditydirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate+ignoredpricecommoditydirectivep = do   char 'N' <?> "ignored-price commodity"   many1 spacenonewline-  commoditysymbol+  commoditysymbolp   restofline   return $ return id -commodityconversiondirective :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate-commodityconversiondirective = do+commodityconversiondirectivep :: ParsecT [Char] JournalContext (ExceptT String IO) JournalUpdate+commodityconversiondirectivep = do   char 'C' <?> "commodity conversion"   many1 spacenonewline   amountp@@ -346,27 +413,27 @@   restofline   return $ return id -modifiertransaction :: ParsecT [Char] JournalContext (ExceptT String IO) ModifierTransaction-modifiertransaction = do+modifiertransactionp :: ParsecT [Char] JournalContext (ExceptT String IO) ModifierTransaction+modifiertransactionp = do   char '=' <?> "modifier transaction"   many spacenonewline   valueexpr <- restofline-  postings <- postings+  postings <- postingsp   return $ ModifierTransaction valueexpr postings -periodictransaction :: ParsecT [Char] JournalContext (ExceptT String IO) PeriodicTransaction-periodictransaction = do+periodictransactionp :: ParsecT [Char] JournalContext (ExceptT String IO) PeriodicTransaction+periodictransactionp = do   char '~' <?> "periodic transaction"   many spacenonewline   periodexpr <- restofline-  postings <- postings+  postings <- postingsp   return $ PeriodicTransaction periodexpr postings  -- | Parse a (possibly unbalanced) transaction.-transaction :: ParsecT [Char] JournalContext (ExceptT String IO) Transaction-transaction = do-  -- ptrace "transaction"-  sourcepos <- getPosition+transactionp :: ParsecT [Char] JournalContext (ExceptT String IO) Transaction+transactionp = do+  -- ptrace "transactionp"+  sourcepos <- genericSourcePos <$> getPosition   date <- datep <?> "transaction"   edate <- optionMaybe (secondarydatep date) <?> "secondary date"   lookAhead (spacenonewline <|> newline) <?> "whitespace or newline"@@ -375,15 +442,17 @@   description <- descriptionp >>= return . strip   comment <- try followingcommentp <|> (newline >> return "")   let tags = tagsInComment comment-  postings <- postings-  return $ txnTieKnot $ Transaction sourcepos date edate status code description comment tags postings ""+  postings <- postingsp+  i' <- (+1) <$> getIndex+  setIndex i'+  return $ txnTieKnot $ Transaction i' sourcepos date edate status code description comment tags postings ""  descriptionp = many (noneOf ";\n")  #ifdef TESTS-test_transaction = do+test_transactionp = do     let s `gives` t = do-                        let p = parseWithCtx nullctx transaction s+                        let p = parseWithCtx nullctx transactionp s                         assertBool $ isRight p                         let Right t2 = p                             -- same f = assertEqual (f t) (f t2)@@ -436,33 +505,33 @@       tdate=parsedate "2015/01/01",       } -    assertRight $ parseWithCtx nullctx transaction $ unlines+    assertRight $ parseWithCtx nullctx transactionp $ unlines       ["2007/01/28 coopportunity"       ,"    expenses:food:groceries                   $47.18"       ,"    assets:checking                          $-47.18"       ,""       ] -    -- transaction should not parse just a date-    assertLeft $ parseWithCtx nullctx transaction "2009/1/1\n"+    -- transactionp should not parse just a date+    assertLeft $ parseWithCtx nullctx transactionp "2009/1/1\n" -    -- transaction should not parse just a date and description-    assertLeft $ parseWithCtx nullctx transaction "2009/1/1 a\n"+    -- transactionp should not parse just a date and description+    assertLeft $ parseWithCtx nullctx transactionp "2009/1/1 a\n" -    -- transaction should not parse a following comment as part of the description-    let p = parseWithCtx nullctx transaction "2009/1/1 a ;comment\n b 1\n"+    -- transactionp should not parse a following comment as part of the description+    let p = parseWithCtx nullctx transactionp "2009/1/1 a ;comment\n b 1\n"     assertRight p     assertEqual "a" (let Right p' = p in tdescription p')      -- parse transaction with following whitespace line-    assertRight $ parseWithCtx nullctx transaction $ unlines+    assertRight $ parseWithCtx nullctx transactionp $ unlines         ["2012/1/1"         ,"  a  1"         ,"  b"         ," "         ] -    let p = parseWithCtx nullctx transaction $ unlines+    let p = parseWithCtx nullctx transactionp $ unlines              ["2009/1/1 x  ; transaction comment"              ," a  1  ; posting 1 comment"              ," ; posting 1 comment 2"@@ -481,8 +550,11 @@ datep = do   -- hacky: try to ensure precise errors for invalid dates   -- XXX reported error position is not too good-  -- pos <- getPosition-  datestr <- many1 $ choice' [digit, datesepchar]+  -- pos <- genericSourcePos <$> getPosition+  datestr <- do+    c <- digit+    cs <- many $ choice' [digit, datesepchar]+    return $ c:cs   let sepchars = nub $ sort $ filter (`elem` datesepchars) datestr   when (length sepchars /= 1) $ fail $ "bad date, different separators used: " ++ datestr   let dateparts = wordsBy (`elem` datesepchars) datestr@@ -558,8 +630,8 @@ codep = try (do { many1 spacenonewline; char '(' <?> "codep"; code <- anyChar `manyTill` char ')'; return code } ) <|> return ""  -- Parse the following whitespace-beginning lines as postings, posting tags, and/or comments.-postings :: Stream [Char] m Char => ParsecT [Char] JournalContext m [Posting]-postings = many (try postingp) <?> "postings"+postingsp :: Stream [Char] m Char => ParsecT [Char] JournalContext m [Posting]+postingsp = many (try postingp) <?> "postings"  -- linebeginningwithspaces :: Stream [Char] m Char => ParsecT [Char] JournalContext m String -- linebeginningwithspaces = do@@ -573,11 +645,11 @@   many1 spacenonewline   status <- statusp   many spacenonewline-  account <- modifiedaccountname+  account <- modifiedaccountnamep   let (ptype, account') = (accountNamePostingType account, unbracket account)-  amount <- spaceandamountormissing-  massertion <- partialbalanceassertion-  _ <- fixedlotprice+  amount <- spaceandamountormissingp+  massertion <- partialbalanceassertionp+  _ <- fixedlotpricep   many spacenonewline   ctx <- getState   comment <- try followingcommentp <|> (newline >> return "")@@ -653,13 +725,16 @@ #endif  -- | Parse an account name, then apply any parent account prefix and/or account aliases currently in effect.-modifiedaccountname :: Stream [Char] m Char => ParsecT [Char] JournalContext m AccountName-modifiedaccountname = do-  a <- accountnamep-  prefix <- getParentAccount-  let prefixed = prefix `joinAccountNames` a+modifiedaccountnamep :: Stream [Char] m Char => ParsecT [Char] JournalContext m AccountName+modifiedaccountnamep = do+  parent <- getParentAccount   aliases <- getAccountAliases-  return $ accountNameApplyAliases aliases prefixed+  a <- accountnamep+  return $+    accountNameApplyAliases aliases $+     -- XXX accountNameApplyAliasesMemo ? doesn't seem to make a difference+    joinAccountNames parent+    a  -- | Parse an account name. Account names start with a non-space, may -- have single spaces inside them, and are terminated by two or more@@ -686,8 +761,8 @@ -- | Parse whitespace then an amount, with an optional left or right -- currency symbol and optional price, or return the special -- "missing" marker amount.-spaceandamountormissing :: Stream [Char] m Char => ParsecT [Char] JournalContext m MixedAmount-spaceandamountormissing =+spaceandamountormissingp :: Stream [Char] m Char => ParsecT [Char] JournalContext m MixedAmount+spaceandamountormissingp =   try (do         many1 spacenonewline         (Mixed . (:[])) `fmap` amountp <|> return missingmixedamt@@ -700,18 +775,18 @@ is' :: (Eq a, Show a) => a -> a -> Assertion a `is'` e = assertEqual e a -test_spaceandamountormissing = do-    assertParseEqual' (parseWithCtx nullctx spaceandamountormissing " $47.18") (Mixed [usd 47.18])-    assertParseEqual' (parseWithCtx nullctx spaceandamountormissing "$47.18") missingmixedamt-    assertParseEqual' (parseWithCtx nullctx spaceandamountormissing " ") missingmixedamt-    assertParseEqual' (parseWithCtx nullctx spaceandamountormissing "") missingmixedamt+test_spaceandamountormissingp = do+    assertParseEqual' (parseWithCtx nullctx spaceandamountormissingp " $47.18") (Mixed [usd 47.18])+    assertParseEqual' (parseWithCtx nullctx spaceandamountormissingp "$47.18") missingmixedamt+    assertParseEqual' (parseWithCtx nullctx spaceandamountormissingp " ") missingmixedamt+    assertParseEqual' (parseWithCtx nullctx spaceandamountormissingp "") missingmixedamt #endif  -- | Parse a single-commodity amount, with optional symbol on the left or -- right, optional unit or total price, and optional (ignored) -- ledger-style balance assertion or fixed lot price declaration. amountp :: Stream [Char] m t => ParsecT [Char] JournalContext m Amount-amountp = try leftsymbolamount <|> try rightsymbolamount <|> nosymbolamount+amountp = try leftsymbolamountp <|> try rightsymbolamountp <|> nosymbolamountp  #ifdef TESTS test_amountp = do@@ -744,32 +819,32 @@   return $ case sign of Just '-' -> "-"                         _        -> "" -leftsymbolamount :: Stream [Char] m t => ParsecT [Char] JournalContext m Amount-leftsymbolamount = do+leftsymbolamountp :: Stream [Char] m t => ParsecT [Char] JournalContext m Amount+leftsymbolamountp = do   sign <- signp-  c <- commoditysymbol+  c <- commoditysymbolp   sp <- many spacenonewline   (q,prec,mdec,mgrps) <- numberp   let s = amountstyle{ascommodityside=L, ascommodityspaced=not $ null sp, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}-  p <- priceamount+  p <- priceamountp   let applysign = if sign=="-" then negate else id   return $ applysign $ Amount c q p s   <?> "left-symbol amount" -rightsymbolamount :: Stream [Char] m t => ParsecT [Char] JournalContext m Amount-rightsymbolamount = do+rightsymbolamountp :: Stream [Char] m t => ParsecT [Char] JournalContext m Amount+rightsymbolamountp = do   (q,prec,mdec,mgrps) <- numberp   sp <- many spacenonewline-  c <- commoditysymbol-  p <- priceamount+  c <- commoditysymbolp+  p <- priceamountp   let s = amountstyle{ascommodityside=R, ascommodityspaced=not $ null sp, asprecision=prec, asdecimalpoint=mdec, asdigitgroups=mgrps}   return $ Amount c q p s   <?> "right-symbol amount" -nosymbolamount :: Stream [Char] m t => ParsecT [Char] JournalContext m Amount-nosymbolamount = do+nosymbolamountp :: Stream [Char] m t => ParsecT [Char] JournalContext m Amount+nosymbolamountp = do   (q,prec,mdec,mgrps) <- numberp-  p <- priceamount+  p <- priceamountp   -- apply the most recently seen default commodity and style to this commodityless amount   defcs <- getDefaultCommodityAndStyle   let (c,s) = case defcs of@@ -778,21 +853,21 @@   return $ Amount c q p s   <?> "no-symbol amount" -commoditysymbol :: Stream [Char] m t => ParsecT [Char] JournalContext m String-commoditysymbol = (quotedcommoditysymbol <|> simplecommoditysymbol) <?> "commodity symbol"+commoditysymbolp :: Stream [Char] m t => ParsecT [Char] JournalContext m String+commoditysymbolp = (quotedcommoditysymbolp <|> simplecommoditysymbolp) <?> "commodity symbol" -quotedcommoditysymbol :: Stream [Char] m t => ParsecT [Char] JournalContext m String-quotedcommoditysymbol = do+quotedcommoditysymbolp :: Stream [Char] m t => ParsecT [Char] JournalContext m String+quotedcommoditysymbolp = do   char '"'   s <- many1 $ noneOf ";\n\""   char '"'   return s -simplecommoditysymbol :: Stream [Char] m t => ParsecT [Char] JournalContext m String-simplecommoditysymbol = many1 (noneOf nonsimplecommoditychars)+simplecommoditysymbolp :: Stream [Char] m t => ParsecT [Char] JournalContext m String+simplecommoditysymbolp = many1 (noneOf nonsimplecommoditychars) -priceamount :: Stream [Char] m t => ParsecT [Char] JournalContext m Price-priceamount =+priceamountp :: Stream [Char] m t => ParsecT [Char] JournalContext m Price+priceamountp =     try (do           many spacenonewline           char '@'@@ -807,8 +882,8 @@             return $ UnitPrice a))          <|> return NoPrice -partialbalanceassertion :: Stream [Char] m t => ParsecT [Char] JournalContext m (Maybe MixedAmount)-partialbalanceassertion =+partialbalanceassertionp :: Stream [Char] m t => ParsecT [Char] JournalContext m (Maybe MixedAmount)+partialbalanceassertionp =     try (do           many spacenonewline           char '='@@ -828,8 +903,8 @@ --          <|> return Nothing  -- http://ledger-cli.org/3.0/doc/ledger3.html#Fixing-Lot-Prices-fixedlotprice :: Stream [Char] m Char => ParsecT [Char] JournalContext m (Maybe Amount)-fixedlotprice =+fixedlotpricep :: Stream [Char] m Char => ParsecT [Char] JournalContext m (Maybe Amount)+fixedlotpricep =     try (do           many spacenonewline           char '{'@@ -933,33 +1008,33 @@   string "comment" >> many spacenonewline >> newline   go   where-    go = try (string "end comment" >> newline >> return ())+    go = try (eof <|> (string "end comment" >> newline >> return ()))          <|> (anyLine >> go)     anyLine = anyChar `manyTill` newline  emptyorcommentlinep :: Stream [Char] m Char => ParsecT [Char] JournalContext m () emptyorcommentlinep = do-  many spacenonewline >> (comment <|> (many spacenonewline >> newline >> return ""))+  many spacenonewline >> (commentp <|> (many spacenonewline >> newline >> return ""))   return ()  followingcommentp :: Stream [Char] m Char => ParsecT [Char] JournalContext m String followingcommentp =   -- ptrace "followingcommentp"-  do samelinecomment <- many spacenonewline >> (try semicoloncomment <|> (newline >> return ""))-     newlinecomments <- many (try (many1 spacenonewline >> semicoloncomment))+  do samelinecomment <- many spacenonewline >> (try semicoloncommentp <|> (newline >> return ""))+     newlinecomments <- many (try (many1 spacenonewline >> semicoloncommentp))      return $ unlines $ samelinecomment:newlinecomments -comment :: Stream [Char] m Char => ParsecT [Char] JournalContext m String-comment = commentStartingWith commentchars+commentp :: Stream [Char] m Char => ParsecT [Char] JournalContext m String+commentp = commentStartingWithp commentchars  commentchars :: [Char] commentchars = "#;*" -semicoloncomment :: Stream [Char] m Char => ParsecT [Char] JournalContext m String-semicoloncomment = commentStartingWith ";"+semicoloncommentp :: Stream [Char] m Char => ParsecT [Char] JournalContext m String+semicoloncommentp = commentStartingWithp ";" -commentStartingWith :: Stream [Char] m Char => String -> ParsecT [Char] JournalContext m String-commentStartingWith cs = do+commentStartingWithp :: Stream [Char] m Char => String -> ParsecT [Char] JournalContext m String+commentStartingWithp cs = do   -- ptrace "commentStartingWith"   oneOf cs   many spacenonewline@@ -975,23 +1050,23 @@ tagsInCommentLine :: String -> [Tag] tagsInCommentLine = catMaybes . map maybetag . map strip . splitAtElement ','   where-    maybetag s = case runParser (tag <* eof) nullctx "" s of+    maybetag s = case runParser (tagp <* eof) nullctx "" s of                   Right t -> Just t                   Left _ -> Nothing -tag = do+tagp = do   -- ptrace "tag"-  n <- tagname-  v <- tagvalue+  n <- tagnamep+  v <- tagvaluep   return (n,v) -tagname = do+tagnamep = do   -- ptrace "tagname"   n <- many1 $ noneOf ": \t"   char ':'   return n -tagvalue = do+tagvaluep = do   -- ptrace "tagvalue"   v <- anyChar `manyTill` ((char ',' >> return ()) <|> eolof)   return $ strip $ reverse $ dropWhile (==',') $ reverse $ strip v@@ -1035,24 +1110,24 @@ tests_Hledger_Read_JournalReader = TestList $ concat [     test_numberp,     test_amountp,-    test_spaceandamountormissing,+    test_spaceandamountormissingp,     test_tagcomment,     test_inlinecomment,     test_comments,     test_ledgerDateSyntaxToTags,     test_postingp,-    test_transaction,+    test_transactionp,     [-   "modifiertransaction" ~: do-     assertParse (parseWithCtx nullctx modifiertransaction "= (some value expr)\n some:postings  1\n")+   "modifiertransactionp" ~: do+     assertParse (parseWithCtx nullctx modifiertransactionp "= (some value expr)\n some:postings  1\n") -  ,"periodictransaction" ~: do-     assertParse (parseWithCtx nullctx periodictransaction "~ (some period expr)\n some:postings  1\n")+  ,"periodictransactionp" ~: do+     assertParse (parseWithCtx nullctx periodictransactionp "~ (some period expr)\n some:postings  1\n") -  ,"directive" ~: do-     assertParse (parseWithCtx nullctx directive "!include /some/file.x\n")-     assertParse (parseWithCtx nullctx directive "account some:account\n")-     assertParse (parseWithCtx nullctx (directive >> directive) "!account a\nend\n")+  ,"directivep" ~: do+     assertParse (parseWithCtx nullctx directivep "!include /some/file.x\n")+     assertParse (parseWithCtx nullctx directivep "account some:account\n")+     assertParse (parseWithCtx nullctx (directivep >> directivep) "!account a\nend\n")    ,"comment" ~: do      assertParse (parseWithCtx nullctx comment "; some comment \n")@@ -1080,28 +1155,28 @@       assertParseEqual (parseWithCtx nullctx p "2011/1/1 00:00-0800") startofday       assertParseEqual (parseWithCtx nullctx p "2011/1/1 00:00+1234") startofday -  ,"defaultyeardirective" ~: do-     assertParse (parseWithCtx nullctx defaultyeardirective "Y 2010\n")-     assertParse (parseWithCtx nullctx defaultyeardirective "Y 10001\n")+  ,"defaultyeardirectivep" ~: do+     assertParse (parseWithCtx nullctx defaultyeardirectivep "Y 2010\n")+     assertParse (parseWithCtx nullctx defaultyeardirectivep "Y 10001\n") -  ,"historicalpricedirective" ~:-    assertParseEqual (parseWithCtx nullctx historicalpricedirective "P 2004/05/01 XYZ $55.00\n") (HistoricalPrice (parsedate "2004/05/01") "XYZ" $ usd 55)+  ,"marketpricedirectivep" ~:+    assertParseEqual (parseWithCtx nullctx marketpricedirectivep "P 2004/05/01 XYZ $55.00\n") (MarketPrice (parsedate "2004/05/01") "XYZ" $ usd 55) -  ,"ignoredpricecommoditydirective" ~: do-     assertParse (parseWithCtx nullctx ignoredpricecommoditydirective "N $\n")+  ,"ignoredpricecommoditydirectivep" ~: do+     assertParse (parseWithCtx nullctx ignoredpricecommoditydirectivep "N $\n") -  ,"defaultcommoditydirective" ~: do-     assertParse (parseWithCtx nullctx defaultcommoditydirective "D $1,000.0\n")+  ,"defaultcommoditydirectivep" ~: do+     assertParse (parseWithCtx nullctx defaultcommoditydirectivep "D $1,000.0\n") -  ,"commodityconversiondirective" ~: do-     assertParse (parseWithCtx nullctx commodityconversiondirective "C 1h = $50.00\n")+  ,"commodityconversiondirectivep" ~: do+     assertParse (parseWithCtx nullctx commodityconversiondirectivep "C 1h = $50.00\n") -  ,"tagdirective" ~: do-     assertParse (parseWithCtx nullctx tagdirective "tag foo \n")+  ,"tagdirectivep" ~: do+     assertParse (parseWithCtx nullctx tagdirectivep "tag foo \n") -  ,"endtagdirective" ~: do-     assertParse (parseWithCtx nullctx endtagdirective "end tag \n")-     assertParse (parseWithCtx nullctx endtagdirective "pop \n")+  ,"endtagdirectivep" ~: do+     assertParse (parseWithCtx nullctx endtagdirectivep "end tag \n")+     assertParse (parseWithCtx nullctx endtagdirectivep "pop \n")    ,"accountnamep" ~: do     assertBool "accountnamep parses a normal account name" (isRight $ parsewith accountnamep "a:b:c")@@ -1109,10 +1184,10 @@     assertBool "accountnamep rejects an empty leading component" (isLeft $ parsewith accountnamep ":b:c")     assertBool "accountnamep rejects an empty trailing component" (isLeft $ parsewith accountnamep "a:b:") -  ,"leftsymbolamount" ~: do-    assertParseEqual (parseWithCtx nullctx leftsymbolamount "$1")  (usd 1 `withPrecision` 0)-    assertParseEqual (parseWithCtx nullctx leftsymbolamount "$-1") (usd (-1) `withPrecision` 0)-    assertParseEqual (parseWithCtx nullctx leftsymbolamount "-$1") (usd (-1) `withPrecision` 0)+  ,"leftsymbolamountp" ~: do+    assertParseEqual (parseWithCtx nullctx leftsymbolamountp "$1")  (usd 1 `withPrecision` 0)+    assertParseEqual (parseWithCtx nullctx leftsymbolamountp "$-1") (usd (-1) `withPrecision` 0)+    assertParseEqual (parseWithCtx nullctx leftsymbolamountp "-$1") (usd (-1) `withPrecision` 0)    ,"amount" ~: do      let -- | compare a parse result with an expected amount, showing the debug representation for clarity
Hledger/Read/TimelogReader.hs view
@@ -60,8 +60,8 @@ import Hledger.Data -- XXX too much reuse ? import Hledger.Read.JournalReader (-  directive, historicalpricedirective, defaultyeardirective, emptyorcommentlinep, datetimep,-  parseJournalWith, modifiedaccountname+  directivep, marketpricedirectivep, defaultyeardirectivep, emptyorcommentlinep, datetimep,+  parseAndFinaliseJournal, modifiedaccountnamep, genericSourcePos   ) import Hledger.Utils @@ -82,32 +82,32 @@ -- format, saving the provided file path and the current time, or give an -- error. parse :: Maybe FilePath -> Bool -> FilePath -> String -> ExceptT String IO Journal-parse _ = parseJournalWith timelogFile+parse _ = parseAndFinaliseJournal timelogfilep -timelogFile :: ParsecT [Char] JournalContext (ExceptT String IO) (JournalUpdate, JournalContext)-timelogFile = do items <- many timelogItem-                 eof-                 ctx <- getState-                 return (liftM (foldl' (\acc new x -> new (acc x)) id) $ sequence items, ctx)+timelogfilep :: ParsecT [Char] JournalContext (ExceptT String IO) (JournalUpdate, JournalContext)+timelogfilep = do items <- many timelogitemp+                  eof+                  ctx <- getState+                  return (liftM (foldl' (\acc new x -> new (acc x)) id) $ sequence items, ctx)     where       -- As all ledger line types can be distinguished by the first       -- character, excepting transactions versus empty (blank or       -- comment-only) lines, can use choice w/o try-      timelogItem = choice [ directive-                          , liftM (return . addHistoricalPrice) historicalpricedirective-                          , defaultyeardirective+      timelogitemp = choice [ directivep+                          , liftM (return . addMarketPrice) marketpricedirectivep+                          , defaultyeardirectivep                           , emptyorcommentlinep >> return (return id)-                          , liftM (return . addTimeLogEntry)  timelogentry+                          , liftM (return . addTimeLogEntry)  timelogentryp                           ] <?> "timelog entry, or default year or historical price directive"  -- | Parse a timelog entry.-timelogentry :: ParsecT [Char] JournalContext (ExceptT String IO) TimeLogEntry-timelogentry = do-  sourcepos <- getPosition+timelogentryp :: ParsecT [Char] JournalContext (ExceptT String IO) TimeLogEntry+timelogentryp = do+  sourcepos <- genericSourcePos <$> getPosition   code <- oneOf "bhioO"   many1 spacenonewline   datetime <- datetimep-  account <- fromMaybe "" <$> optionMaybe (many1 spacenonewline >> modifiedaccountname)+  account <- fromMaybe "" <$> optionMaybe (many1 spacenonewline >> modifiedaccountnamep)   description <- fromMaybe "" <$> optionMaybe (many1 spacenonewline >> restofline)   return $ TimeLogEntry sourcepos (read [code]) datetime account description 
Hledger/Reports/BalanceReport.hs view
@@ -10,6 +10,8 @@   BalanceReportItem,   RenderableAccountName,   balanceReport,+  balanceReportValue,+  mixedAmountValue,   flatShowsExclusiveBalance,    -- * Tests@@ -17,7 +19,9 @@ ) where +import Data.List (sort) import Data.Maybe+import Data.Time.Calendar import Test.HUnit  import Hledger.Data@@ -123,6 +127,41 @@ --     items = [(a,a',n, headDef 0 bs) | ((a,a',n), bs) <- mbrrows] --     total = headDef 0 mbrtotals +-- | Convert all the amounts in a single-column balance report to+-- their value on the given date in their default valuation+-- commodities.+balanceReportValue :: Journal -> Day -> BalanceReport -> BalanceReport+balanceReportValue j d r = r'+  where+    (items,total) = r+    r' = ([(n, mixedAmountValue j d a) |(n,a) <- items], mixedAmountValue j d total)++mixedAmountValue :: Journal -> Day -> MixedAmount -> MixedAmount+mixedAmountValue j d (Mixed as) = Mixed $ map (amountValue j d) as++-- | Find the market value of this amount on the given date, in it's+-- default valuation commodity, based on historical prices. If no+-- default valuation commodity can be found, the amount is left+-- unchanged.+amountValue :: Journal -> Day -> Amount -> Amount+amountValue j d a =+  case commodityValue j d (acommodity a) of+    Just v  -> v{aquantity=aquantity v * aquantity a+                ,aprice=aprice a+                }+    Nothing -> a++-- | Find the market value, if known, of one unit of this commodity on+-- the given date, in the commodity in which it has most recently been+-- market-priced (ie the commodity mentioned in the most recent+-- applicable historical price directive before this date).+commodityValue :: Journal -> Day -> Commodity -> Maybe Amount+commodityValue j d c+    | null applicableprices = Nothing+    | otherwise             = Just $ mpamount $ last applicableprices+  where+    applicableprices = [p | p <- sort $ jmarketprices j, mpcommodity p == c, mpdate p <= d]+ tests_balanceReport =   let     (opts,journal) `gives` r = do@@ -131,7 +170,7 @@           showw (acct,amt) = (acct, showMixedAmountDebug amt)       assertEqual "items" (map showw eitems) (map showw aitems)       assertEqual "total" (showMixedAmountDebug etotal) (showMixedAmountDebug atotal)-    usd0 = nullamt{acommodity="$"}+    usd0 = usd 0   in [     "balanceReport with no args on null journal" ~: do@@ -329,6 +368,7 @@          nulljournal          {jtxns = [            txnTieKnot $ Transaction {+             tindex=0,              tsourcepos=nullsourcepos,              tdate=parsedate "2008/01/01",              tdate2=Just $ parsedate "2009/01/01",
Hledger/Reports/MultiBalanceReports.hs view
@@ -8,7 +8,8 @@ module Hledger.Reports.MultiBalanceReports (   MultiBalanceReport(..),   MultiBalanceReportRow,-  multiBalanceReport+  multiBalanceReport,+  multiBalanceReportValue    -- -- * Tests   -- tests_Hledger_Reports_MultiBalanceReport@@ -18,6 +19,7 @@ import Data.List import Data.Maybe import Data.Ord+import Data.Time.Calendar import Safe -- import Test.HUnit @@ -176,4 +178,17 @@        dbg1 s = let p = "multiBalanceReport" in Hledger.Utils.dbg1 (p++" "++s)  -- add prefix in this function's debug output       -- dbg1 = const id  -- exclude this function from debug output++-- | Convert all the amounts in a multi-column balance report to their+-- value on the given date in their default valuation commodities+-- (which are determined as of that date, not the report interval dates).+multiBalanceReportValue :: Journal -> Day -> MultiBalanceReport -> MultiBalanceReport+multiBalanceReportValue j d r = r'+  where+    MultiBalanceReport (spans, rows, (coltotals, rowtotaltotal, rowavgtotal)) = r+    r' = MultiBalanceReport+         (spans,+          [(n, map convert rowamts, convert rowtotal, convert rowavg) | (n, rowamts, rowtotal, rowavg) <- rows],+          (map convert coltotals, convert rowtotaltotal, convert rowavgtotal))+    convert = mixedAmountValue j d 
Hledger/Reports/ReportOptions.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE RecordWildCards, DeriveDataTypeable #-}+{-# LANGUAGE CPP, RecordWildCards, DeriveDataTypeable #-} {-|  Options common to most hledger reports.@@ -12,6 +12,7 @@   FormatStr,   defreportopts,   rawOptsToReportOpts,+  checkReportOpts,   flat_,   tree_,   dateSpanFromOpts,@@ -30,6 +31,9 @@ where  import Data.Data (Data)+#if !MIN_VERSION_base(4,8,0)+import Data.Functor.Compat ((<$>))+#endif import Data.Typeable (Typeable) import Data.Time.Calendar import System.Console.CmdArgs.Default  -- some additional default stuff@@ -88,6 +92,7 @@     ,drop_           :: Int     ,row_total_      :: Bool     ,no_total_       :: Bool+    ,value_          :: Bool  } deriving (Show, Data, Typeable)  instance Default ReportOpts where def = defreportopts@@ -121,9 +126,10 @@     def     def     def+    def  rawOptsToReportOpts :: RawOpts -> IO ReportOpts-rawOptsToReportOpts rawopts = do+rawOptsToReportOpts rawopts = checkReportOpts <$> do   d <- getCurrentDay   return defreportopts{      begin_       = maybesmartdateopt d "begin" rawopts@@ -144,7 +150,7 @@     ,monthly_     = boolopt "monthly" rawopts     ,quarterly_   = boolopt "quarterly" rawopts     ,yearly_      = boolopt "yearly" rawopts-    ,format_      = maybestringopt "format" rawopts+    ,format_      = maybestringopt "format" rawopts -- XXX move to CliOpts or move validation from Cli.CliOptions to here     ,query_       = unwords $ listofstringopt "args" rawopts -- doesn't handle an arg like "" right     ,average_     = boolopt "average" rawopts     ,related_     = boolopt "related" rawopts@@ -153,7 +159,16 @@     ,drop_        = intopt "drop" rawopts     ,row_total_   = boolopt "row-total" rawopts     ,no_total_    = boolopt "no-total" rawopts+    ,value_       = boolopt "value" rawopts     }++-- | Do extra validation of opts, raising an error if there is trouble.+checkReportOpts :: ReportOpts -> ReportOpts+checkReportOpts ropts@ReportOpts{..} =+  either optserror (const ropts) $ do+    case depth_ of+      Just d | d < 0 -> Left "--depth should have a positive number"+      _              -> Right ()  accountlistmodeopt :: RawOpts -> AccountListMode accountlistmodeopt rawopts =
Hledger/Reports/TransactionsReports.hs view
@@ -11,6 +11,8 @@ module Hledger.Reports.TransactionsReports (   TransactionsReport,   TransactionsReportItem,+  AccountTransactionsReport,+  AccountTransactionsReportItem,   triOrigTransaction,   triDate,   triAmount,@@ -78,15 +80,16 @@  -- | An account transactions report represents transactions affecting -- a particular account (or possibly several accounts, but we don't--- use that). It is used by hledger-web's account register view, where--- we want to show one row per journal transaction, with:+-- use that). It is used by hledger-web's (and hledger-ui's) account+-- register view, where we want to show one row per journal+-- transaction, with: -- -- - the total increase/decrease to the current account -- -- - the names of the other account(s) posted to/from ----- - transaction dates adjusted to the date of the earliest posting to---   the current account, if those postings have their own dates+-- - transaction dates, adjusted to the date of the earliest posting to+--   the current account if those postings have their own dates -- -- Currently, reporting intervals are not supported, and report items -- are most recent first.@@ -198,8 +201,14 @@ --       (froms,tos) = partition (fromMaybe False . isNegativeMixedAmount . pamount) ps  -- | Generate a simplified summary of some postings' accounts.+-- To reduce noise, if there are both real and virtual postings, show only the real ones. summarisePostingAccounts :: [Posting] -> String-summarisePostingAccounts = intercalate ", " . map accountLeafName . nub . map paccount+summarisePostingAccounts ps =+  (intercalate ", " . map accountSummarisedName . nub . map paccount) displayps+  where+    realps = filter isReal ps+    displayps | null realps = ps+              | otherwise   = realps  filterTransactionPostings :: Query -> Transaction -> Transaction filterTransactionPostings m t@Transaction{tpostings=ps} = t{tpostings=filter (m `matchesPosting`) ps}
Hledger/Utils.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE FlexibleContexts #-} {-|  Standard imports and utilities which are useful everywhere, or needed low@@ -20,7 +19,11 @@                           ---- all of this one:                           module Hledger.Utils,                           module Hledger.Utils.Debug,+                          module Hledger.Utils.Parse,                           module Hledger.Utils.Regex,+                          module Hledger.Utils.String,+                          module Hledger.Utils.Test,+                          module Hledger.Utils.Tree,                           -- Debug.Trace.trace,                           -- module Data.PPrint,                           -- module Hledger.Utils.UTF8IOCompat@@ -30,205 +33,29 @@ where import Control.Monad (liftM) import Control.Monad.IO.Class (MonadIO, liftIO)-import Data.Char-import Data.List-import qualified Data.Map as M+-- import Data.Char+-- import Data.List -- import Data.Maybe -- import Data.PPrint import Data.Time.Clock import Data.Time.LocalTime-import Data.Tree import System.Directory (getHomeDirectory) import System.FilePath((</>), isRelative) import System.IO-import Test.HUnit-import Text.Parsec-import Text.Printf+-- import Text.Printf -- import qualified Data.Map as Map  import Hledger.Utils.Debug+import Hledger.Utils.Parse import Hledger.Utils.Regex+import Hledger.Utils.String+import Hledger.Utils.Test+import Hledger.Utils.Tree -- import Prelude hiding (readFile,writeFile,appendFile,getContents,putStr,putStrLn) -- import Hledger.Utils.UTF8IOCompat   (readFile,writeFile,appendFile,getContents,putStr,putStrLn) import Hledger.Utils.UTF8IOCompat (SystemString,fromSystemString,toSystemString,error',userError') --- strings -lowercase, uppercase :: String -> String-lowercase = map toLower-uppercase = map toUpper---- | Remove leading and trailing whitespace.-strip :: String -> String-strip = lstrip . rstrip---- | Remove leading whitespace.-lstrip :: String -> String-lstrip = dropWhile (`elem` " \t") :: String -> String -- XXX isSpace ?---- | Remove trailing whitespace.-rstrip :: String -> String-rstrip = reverse . lstrip . reverse---- | Remove trailing newlines/carriage returns.-chomp :: String -> String-chomp = reverse . dropWhile (`elem` "\r\n") . reverse--stripbrackets :: String -> String-stripbrackets = dropWhile (`elem` "([") . reverse . dropWhile (`elem` "])") . reverse :: String -> String--elideLeft :: Int -> String -> String-elideLeft width s =-    if length s > width then ".." ++ reverse (take (width - 2) $ reverse s) else s--elideRight :: Int -> String -> String-elideRight width s =-    if length s > width then take (width - 2) s ++ ".." else s--underline :: String -> String-underline s = s' ++ replicate (length s) '-' ++ "\n"-    where s'-            | last s == '\n' = s-            | otherwise = s ++ "\n"---- | Wrap a string in double quotes, and \-prefix any embedded single--- quotes, if it contains whitespace and is not already single- or--- double-quoted.-quoteIfSpaced :: String -> String-quoteIfSpaced s | isSingleQuoted s || isDoubleQuoted s = s-                | not $ any (`elem` s) whitespacechars = s-                | otherwise = "'"++escapeSingleQuotes s++"'"---- | Double-quote this string if it contains whitespace, single quotes--- or double-quotes, escaping the quotes as needed.-quoteIfNeeded :: String -> String-quoteIfNeeded s | any (`elem` s) (quotechars++whitespacechars) = "\"" ++ escapeDoubleQuotes s ++ "\""-                | otherwise = s---- | Single-quote this string if it contains whitespace or double-quotes.--- No good for strings containing single quotes.-singleQuoteIfNeeded :: String -> String-singleQuoteIfNeeded s | any (`elem` s) whitespacechars = "'"++s++"'"-                      | otherwise = s--quotechars, whitespacechars :: [Char]-quotechars      = "'\""-whitespacechars = " \t\n\r"--escapeDoubleQuotes :: String -> String-escapeDoubleQuotes = regexReplace "\"" "\""--escapeSingleQuotes :: String -> String-escapeSingleQuotes = regexReplace "'" "\'"--escapeQuotes :: String -> String-escapeQuotes = regexReplace "([\"'])" "\\1"---- | Quote-aware version of words - don't split on spaces which are inside quotes.--- NB correctly handles "a'b" but not "''a''". Can raise an error if parsing fails.-words' :: String -> [String]-words' "" = []-words' s  = map stripquotes $ fromparse $ parsewith p s-    where-      p = do ss <- (singleQuotedPattern <|> doubleQuotedPattern <|> pattern) `sepBy` many1 spacenonewline-             -- eof-             return ss-      pattern = many (noneOf whitespacechars)-      singleQuotedPattern = between (char '\'') (char '\'') (many $ noneOf "'")-      doubleQuotedPattern = between (char '"') (char '"') (many $ noneOf "\"")---- | Quote-aware version of unwords - single-quote strings which contain whitespace-unwords' :: [String] -> String-unwords' = unwords . map quoteIfNeeded---- | Strip one matching pair of single or double quotes on the ends of a string.-stripquotes :: String -> String-stripquotes s = if isSingleQuoted s || isDoubleQuoted s then init $ tail s else s--isSingleQuoted s@(_:_:_) = head s == '\'' && last s == '\''-isSingleQuoted _ = False--isDoubleQuoted s@(_:_:_) = head s == '"' && last s == '"'-isDoubleQuoted _ = False--unbracket :: String -> String-unbracket s-    | (head s == '[' && last s == ']') || (head s == '(' && last s == ')') = init $ tail s-    | otherwise = s---- | Join multi-line strings as side-by-side rectangular strings of the same height, top-padded.-concatTopPadded :: [String] -> String-concatTopPadded strs = intercalate "\n" $ map concat $ transpose padded-    where-      lss = map lines strs-      h = maximum $ map length lss-      ypad ls = replicate (difforzero h (length ls)) "" ++ ls-      xpad ls = map (padleft w) ls where w | null ls = 0-                                           | otherwise = maximum $ map length ls-      padded = map (xpad . ypad) lss---- | Join multi-line strings as side-by-side rectangular strings of the same height, bottom-padded.-concatBottomPadded :: [String] -> String-concatBottomPadded strs = intercalate "\n" $ map concat $ transpose padded-    where-      lss = map lines strs-      h = maximum $ map length lss-      ypad ls = ls ++ replicate (difforzero h (length ls)) ""-      xpad ls = map (padright w) ls where w | null ls = 0-                                            | otherwise = maximum $ map length ls-      padded = map (xpad . ypad) lss---- | Compose strings vertically and right-aligned.-vConcatRightAligned :: [String] -> String-vConcatRightAligned ss = intercalate "\n" $ map showfixedwidth ss-    where-      showfixedwidth = printf (printf "%%%ds" width)-      width = maximum $ map length ss---- | Convert a multi-line string to a rectangular string top-padded to the specified height.-padtop :: Int -> String -> String-padtop h s = intercalate "\n" xpadded-    where-      ls = lines s-      sh = length ls-      sw | null ls = 0-         | otherwise = maximum $ map length ls-      ypadded = replicate (difforzero h sh) "" ++ ls-      xpadded = map (padleft sw) ypadded---- | Convert a multi-line string to a rectangular string bottom-padded to the specified height.-padbottom :: Int -> String -> String-padbottom h s = intercalate "\n" xpadded-    where-      ls = lines s-      sh = length ls-      sw | null ls = 0-         | otherwise = maximum $ map length ls-      ypadded = ls ++ replicate (difforzero h sh) ""-      xpadded = map (padleft sw) ypadded---- | Convert a multi-line string to a rectangular string left-padded to the specified width.-padleft :: Int -> String -> String-padleft w "" = concat $ replicate w " "-padleft w s = intercalate "\n" $ map (printf (printf "%%%ds" w)) $ lines s---- | Convert a multi-line string to a rectangular string right-padded to the specified width.-padright :: Int -> String -> String-padright w "" = concat $ replicate w " "-padright w s = intercalate "\n" $ map (printf (printf "%%-%ds" w)) $ lines s---- | Clip a multi-line string to the specified width and height from the top left.-cliptopleft :: Int -> Int -> String -> String-cliptopleft w h = intercalate "\n" . take h . map (take w) . lines---- | Clip and pad a multi-line string to fill the specified width and height.-fitto :: Int -> Int -> String -> String-fitto w h s = intercalate "\n" $ take h $ rows ++ repeat blankline-    where-      rows = map (fit w) $ lines s-      fit w = take w . (++ repeat ' ')-      blankline = replicate w ' '- -- tuples  first3  (x,_,_) = x@@ -246,10 +73,12 @@ fourth5 (_,_,_,x,_) = x fifth5  (_,_,_,_,x) = x --- math--difforzero :: (Num a, Ord a) => a -> a -> a-difforzero a b = maximum [(a - b), 0]+first6  (x,_,_,_,_,_) = x+second6 (_,x,_,_,_,_) = x+third6  (_,_,x,_,_,_) = x+fourth6 (_,_,_,x,_,_) = x+fifth6  (_,_,_,_,x,_) = x+sixth6  (_,_,_,_,_,x) = x  -- lists @@ -263,120 +92,6 @@     split es = let (first,rest) = break (x==) es                in first : splitAtElement x rest --- trees---- standard tree helpers--root = rootLabel-subs = subForest-branches = subForest---- | List just the leaf nodes of a tree-leaves :: Tree a -> [a]-leaves (Node v []) = [v]-leaves (Node _ branches) = concatMap leaves branches---- | get the sub-tree rooted at the first (left-most, depth-first) occurrence--- of the specified node value-subtreeat :: Eq a => a -> Tree a -> Maybe (Tree a)-subtreeat v t-    | root t == v = Just t-    | otherwise = subtreeinforest v $ subs t---- | get the sub-tree for the specified node value in the first tree in--- forest in which it occurs.-subtreeinforest :: Eq a => a -> [Tree a] -> Maybe (Tree a)-subtreeinforest _ [] = Nothing-subtreeinforest v (t:ts) = case (subtreeat v t) of-                             Just t' -> Just t'-                             Nothing -> subtreeinforest v ts---- | remove all nodes past a certain depth-treeprune :: Int -> Tree a -> Tree a-treeprune 0 t = Node (root t) []-treeprune d t = Node (root t) (map (treeprune $ d-1) $ branches t)---- | apply f to all tree nodes-treemap :: (a -> b) -> Tree a -> Tree b-treemap f t = Node (f $ root t) (map (treemap f) $ branches t)---- | remove all subtrees whose nodes do not fulfill predicate-treefilter :: (a -> Bool) -> Tree a -> Tree a-treefilter f t = Node-                 (root t)-                 (map (treefilter f) $ filter (treeany f) $ branches t)---- | is predicate true in any node of tree ?-treeany :: (a -> Bool) -> Tree a -> Bool-treeany f t = f (root t) || any (treeany f) (branches t)---- treedrop -- remove the leaves which do fulfill predicate.--- treedropall -- do this repeatedly.---- | show a compact ascii representation of a tree-showtree :: Show a => Tree a -> String-showtree = unlines . filter (regexMatches "[^ \\|]") . lines . drawTree . treemap show---- | show a compact ascii representation of a forest-showforest :: Show a => Forest a -> String-showforest = concatMap showtree----- | An efficient-to-build tree suggested by Cale Gibbard, probably--- better than accountNameTreeFrom.-newtype FastTree a = T (M.Map a (FastTree a))-  deriving (Show, Eq, Ord)--emptyTree = T M.empty--mergeTrees :: (Ord a) => FastTree a -> FastTree a -> FastTree a-mergeTrees (T m) (T m') = T (M.unionWith mergeTrees m m')--treeFromPath :: [a] -> FastTree a-treeFromPath []     = T M.empty-treeFromPath (x:xs) = T (M.singleton x (treeFromPath xs))--treeFromPaths :: (Ord a) => [[a]] -> FastTree a-treeFromPaths = foldl' mergeTrees emptyTree . map treeFromPath----- parsing---- | Backtracking choice, use this when alternatives share a prefix.--- Consumes no input if all choices fail.-choice' :: Stream s m t => [ParsecT s u m a] -> ParsecT s u m a-choice' = choice . map Text.Parsec.try--parsewith :: Parsec [Char] () a -> String -> Either ParseError a-parsewith p = runParser p () ""--parseWithCtx :: Stream s m t => u -> ParsecT s u m a -> s -> m (Either ParseError a)-parseWithCtx ctx p = runParserT p ctx ""--fromparse :: Either ParseError a -> a-fromparse = either parseerror id--parseerror :: ParseError -> a-parseerror e = error' $ showParseError e--showParseError :: ParseError -> String-showParseError e = "parse error at " ++ show e--showDateParseError :: ParseError -> String-showDateParseError e = printf "date parse error (%s)" (intercalate ", " $ tail $ lines $ show e)--nonspace :: (Stream [Char] m Char) => ParsecT [Char] st m Char-nonspace = satisfy (not . isSpace)--spacenonewline :: (Stream [Char] m Char) => ParsecT [Char] st m Char-spacenonewline = satisfy (`elem` " \v\f\t")--restofline :: (Stream [Char] m Char) => ParsecT [Char] st m String-restofline = anyChar `manyTill` newline--eolof :: (Stream [Char] m Char) => ParsecT [Char] st m ()-eolof = (newline >> return ()) <|> eof- -- time  getCurrentLocalTime :: IO LocalTime@@ -385,44 +100,6 @@   tz <- getCurrentTimeZone   return $ utcToLocalTime tz t --- testing---- | Get a Test's label, or the empty string.-testName :: Test -> String-testName (TestLabel n _) = n-testName _ = ""---- | Flatten a Test containing TestLists into a list of single tests.-flattenTests :: Test -> [Test]-flattenTests (TestLabel _ t@(TestList _)) = flattenTests t-flattenTests (TestList ts) = concatMap flattenTests ts-flattenTests t = [t]---- | Filter TestLists in a Test, recursively, preserving the structure.-filterTests :: (Test -> Bool) -> Test -> Test-filterTests p (TestLabel l ts) = TestLabel l (filterTests p ts)-filterTests p (TestList ts) = TestList $ filter (any p . flattenTests) $ map (filterTests p) ts-filterTests _ t = t---- | Simple way to assert something is some expected value, with no label.-is :: (Eq a, Show a) => a -> a -> Assertion-a `is` e = assertEqual "" e a---- | Assert a parse result is successful, printing the parse error on failure.-assertParse :: (Either ParseError a) -> Assertion-assertParse parse = either (assertFailure.show) (const (return ())) parse---- | Assert a parse result is successful, printing the parse error on failure.-assertParseFailure :: (Either ParseError a) -> Assertion-assertParseFailure parse = either (const $ return ()) (const $ assertFailure "parse should not have succeeded") parse---- | Assert a parse result is some expected value, printing the parse error on failure.-assertParseEqual :: (Show a, Eq a) => (Either ParseError a) -> a -> Assertion-assertParseEqual parse expected = either (assertFailure.show) (`is` expected) parse--printParseError :: (Show a) => a -> IO ()-printParseError e = do putStr "parse error at "; print e- -- misc  isLeft :: Either a b -> Bool@@ -457,3 +134,8 @@   h <- openFile name ReadMode   hSetNewlineMode h universalNewlineMode   hGetContents h++-- | Total version of maximum, for integral types, giving 0 for an empty list.+maximum' :: Integral a => [a] -> a+maximum' [] = 0+maximum' xs = maximum xs
Hledger/Utils/Debug.hs view
@@ -71,7 +71,7 @@ -- a higher value (note: not @--debug N@ for some reason).  This uses -- unsafePerformIO and can be accessed from anywhere and before normal -- command-line processing. After command-line processing, it is also--- available as the @debug_@ field of 'Hledger.Cli.Options.CliOpts'.+-- available as the @debug_@ field of 'Hledger.Cli.CliOptions.CliOpts'. -- {-# OPTIONS_GHC -fno-cse #-}  -- {-# NOINLINE debugLevel #-} debugLevel :: Int
+ Hledger/Utils/Parse.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE FlexibleContexts #-}+module Hledger.Utils.Parse where++import Data.Char+import Data.List+import Text.Parsec+import Text.Printf++import Hledger.Utils.UTF8IOCompat (error')++-- | Backtracking choice, use this when alternatives share a prefix.+-- Consumes no input if all choices fail.+choice' :: Stream s m t => [ParsecT s u m a] -> ParsecT s u m a+choice' = choice . map Text.Parsec.try++parsewith :: Parsec [Char] () a -> String -> Either ParseError a+parsewith p = runParser p () ""++parseWithCtx :: Stream s m t => u -> ParsecT s u m a -> s -> m (Either ParseError a)+parseWithCtx ctx p = runParserT p ctx ""++fromparse :: Either ParseError a -> a+fromparse = either parseerror id++parseerror :: ParseError -> a+parseerror e = error' $ showParseError e++showParseError :: ParseError -> String+showParseError e = "parse error at " ++ show e++showDateParseError :: ParseError -> String+showDateParseError e = printf "date parse error (%s)" (intercalate ", " $ tail $ lines $ show e)++nonspace :: (Stream [Char] m Char) => ParsecT [Char] st m Char+nonspace = satisfy (not . isSpace)++spacenonewline :: (Stream [Char] m Char) => ParsecT [Char] st m Char+spacenonewline = satisfy (`elem` " \v\f\t")++restofline :: (Stream [Char] m Char) => ParsecT [Char] st m String+restofline = anyChar `manyTill` newline++eolof :: (Stream [Char] m Char) => ParsecT [Char] st m ()+eolof = (newline >> return ()) <|> eof+
Hledger/Utils/Regex.hs view
@@ -17,8 +17,14 @@  - have simple monomorphic types -- work with strings+- work with simple strings +Regex strings are automatically compiled into regular expressions the+first time they are seen, and these are cached. If you use a huge+number of unique regular expressions this might lead to increased+memory usage. Several functions have memoised variants (*Memo), which+also trade space for time.+ Current limitations:  - (?i) and similar are not supported@@ -34,6 +40,8 @@   ,regexMatchesCI   ,regexReplace   ,regexReplaceCI+  ,regexReplaceMemo+  ,regexReplaceCIMemo   ,regexReplaceBy   ,regexReplaceByCI   )@@ -42,6 +50,7 @@ import Data.Array import Data.Char import Data.List (foldl')+import Data.MemoUgly (memo) import Text.Regex.TDFA (   Regex, CompOption(..), ExecOption(..), defaultCompOpt, defaultExecOpt,   makeRegexOpts, AllMatches(getAllMatches), match, (=~), MatchText@@ -60,10 +69,10 @@ -- | Convert our string-based regexps to real ones. Can fail if the -- string regexp is malformed. toRegex :: Regexp -> Regex-toRegex = makeRegexOpts compOpt execOpt+toRegex = memo (makeRegexOpts compOpt execOpt)  toRegexCI :: Regexp -> Regex-toRegexCI = makeRegexOpts compOpt{caseSensitive=False} execOpt+toRegexCI = memo (makeRegexOpts compOpt{caseSensitive=False} execOpt)  compOpt :: CompOption compOpt = defaultCompOpt@@ -95,6 +104,14 @@  regexReplaceCI :: Regexp -> Replacement -> String -> String regexReplaceCI re = replaceRegex (toRegexCI re)++-- | A memoising version of regexReplace. Caches the result for each+-- search pattern, replacement pattern, target string tuple.+regexReplaceMemo :: Regexp -> Replacement -> String -> String+regexReplaceMemo re repl = memo (regexReplace re repl)++regexReplaceCIMemo :: Regexp -> Replacement -> String -> String+regexReplaceCIMemo re repl = memo (regexReplaceCI re repl)  -- 
+ Hledger/Utils/String.hs view
@@ -0,0 +1,392 @@+-- | String formatting helpers, starting to get a bit out of control.++module Hledger.Utils.String (+ -- * misc+ lowercase,+ uppercase,+ underline,+ stripbrackets,+ unbracket,+ -- quoting+ quoteIfSpaced,+ quoteIfNeeded,+ singleQuoteIfNeeded,+ -- quotechars,+ -- whitespacechars,+ escapeDoubleQuotes,+ escapeSingleQuotes,+ escapeQuotes,+ words',+ unwords',+ stripquotes,+ isSingleQuoted,+ isDoubleQuoted,+ -- * single-line layout+ strip,+ lstrip,+ rstrip,+ chomp,+ elideLeft,+ elideRight,+ formatString,+ -- * multi-line layout+ concatTopPadded,+ concatBottomPadded,+ concatOneLine,+ vConcatLeftAligned,+ vConcatRightAligned,+ padtop,+ padbottom,+ padleft,+ padright,+ cliptopleft,+ fitto,+ -- * wide-character-aware layout+ strWidth,+ takeWidth,+ fitString,+ fitStringMulti,+ padLeftWide,+ padRightWide+ ) where+++import Data.Char+import Data.List+import Text.Parsec+import Text.Printf (printf)++import Hledger.Utils.Parse+import Hledger.Utils.Regex++lowercase, uppercase :: String -> String+lowercase = map toLower+uppercase = map toUpper++-- | Remove leading and trailing whitespace.+strip :: String -> String+strip = lstrip . rstrip++-- | Remove leading whitespace.+lstrip :: String -> String+lstrip = dropWhile (`elem` " \t") :: String -> String -- XXX isSpace ?++-- | Remove trailing whitespace.+rstrip :: String -> String+rstrip = reverse . lstrip . reverse++-- | Remove trailing newlines/carriage returns.+chomp :: String -> String+chomp = reverse . dropWhile (`elem` "\r\n") . reverse++stripbrackets :: String -> String+stripbrackets = dropWhile (`elem` "([") . reverse . dropWhile (`elem` "])") . reverse :: String -> String++elideLeft :: Int -> String -> String+elideLeft width s =+    if length s > width then ".." ++ reverse (take (width - 2) $ reverse s) else s++elideRight :: Int -> String -> String+elideRight width s =+    if length s > width then take (width - 2) s ++ ".." else s++-- | Clip and pad a string to a minimum & maximum width, and/or left/right justify it.+-- Works on multi-line strings too (but will rewrite non-unix line endings).+formatString :: Bool -> Maybe Int -> Maybe Int -> String -> String+formatString leftJustified minwidth maxwidth s = intercalate "\n" $ map (printf fmt) $ lines s+    where+      justify = if leftJustified then "-" else ""+      minwidth' = maybe "" show minwidth+      maxwidth' = maybe "" (("."++).show) maxwidth+      fmt = "%" ++ justify ++ minwidth' ++ maxwidth' ++ "s"++underline :: String -> String+underline s = s' ++ replicate (length s) '-' ++ "\n"+    where s'+            | last s == '\n' = s+            | otherwise = s ++ "\n"++-- | Wrap a string in double quotes, and \-prefix any embedded single+-- quotes, if it contains whitespace and is not already single- or+-- double-quoted.+quoteIfSpaced :: String -> String+quoteIfSpaced s | isSingleQuoted s || isDoubleQuoted s = s+                | not $ any (`elem` s) whitespacechars = s+                | otherwise = "'"++escapeSingleQuotes s++"'"++-- | Double-quote this string if it contains whitespace, single quotes+-- or double-quotes, escaping the quotes as needed.+quoteIfNeeded :: String -> String+quoteIfNeeded s | any (`elem` s) (quotechars++whitespacechars) = "\"" ++ escapeDoubleQuotes s ++ "\""+                | otherwise = s++-- | Single-quote this string if it contains whitespace or double-quotes.+-- No good for strings containing single quotes.+singleQuoteIfNeeded :: String -> String+singleQuoteIfNeeded s | any (`elem` s) whitespacechars = "'"++s++"'"+                      | otherwise = s++quotechars, whitespacechars :: [Char]+quotechars      = "'\""+whitespacechars = " \t\n\r"++escapeDoubleQuotes :: String -> String+escapeDoubleQuotes = regexReplace "\"" "\""++escapeSingleQuotes :: String -> String+escapeSingleQuotes = regexReplace "'" "\'"++escapeQuotes :: String -> String+escapeQuotes = regexReplace "([\"'])" "\\1"++-- | Quote-aware version of words - don't split on spaces which are inside quotes.+-- NB correctly handles "a'b" but not "''a''". Can raise an error if parsing fails.+words' :: String -> [String]+words' "" = []+words' s  = map stripquotes $ fromparse $ parsewith p s+    where+      p = do ss <- (singleQuotedPattern <|> doubleQuotedPattern <|> pattern) `sepBy` many1 spacenonewline+             -- eof+             return ss+      pattern = many (noneOf whitespacechars)+      singleQuotedPattern = between (char '\'') (char '\'') (many $ noneOf "'")+      doubleQuotedPattern = between (char '"') (char '"') (many $ noneOf "\"")++-- | Quote-aware version of unwords - single-quote strings which contain whitespace+unwords' :: [String] -> String+unwords' = unwords . map quoteIfNeeded++-- | Strip one matching pair of single or double quotes on the ends of a string.+stripquotes :: String -> String+stripquotes s = if isSingleQuoted s || isDoubleQuoted s then init $ tail s else s++isSingleQuoted s@(_:_:_) = head s == '\'' && last s == '\''+isSingleQuoted _ = False++isDoubleQuoted s@(_:_:_) = head s == '"' && last s == '"'+isDoubleQuoted _ = False++unbracket :: String -> String+unbracket s+    | (head s == '[' && last s == ']') || (head s == '(' && last s == ')') = init $ tail s+    | otherwise = s++-- | Join several multi-line strings as side-by-side rectangular strings of the same height, top-padded.+-- Treats wide characters as double width.+concatTopPadded :: [String] -> String+concatTopPadded strs = intercalate "\n" $ map concat $ transpose padded+    where+      lss = map lines strs+      h = maximum $ map length lss+      ypad ls = replicate (difforzero h (length ls)) "" ++ ls+      xpad ls = map (padLeftWide w) ls where w | null ls = 0+                                               | otherwise = maximum $ map strWidth ls+      padded = map (xpad . ypad) lss++-- | Join several multi-line strings as side-by-side rectangular strings of the same height, bottom-padded.+-- Treats wide characters as double width.+concatBottomPadded :: [String] -> String+concatBottomPadded strs = intercalate "\n" $ map concat $ transpose padded+    where+      lss = map lines strs+      h = maximum $ map length lss+      ypad ls = ls ++ replicate (difforzero h (length ls)) ""+      xpad ls = map (padRightWide w) ls where w | null ls = 0+                                                | otherwise = maximum $ map strWidth ls+      padded = map (xpad . ypad) lss+++-- | Join multi-line strings horizontally, after compressing each of+-- them to a single line with a comma and space between each original line.+concatOneLine :: [String] -> String+concatOneLine strs = concat $ map ((intercalate ", ").lines) strs++-- | Join strings vertically, left-aligned and right-padded.+vConcatLeftAligned :: [String] -> String+vConcatLeftAligned ss = intercalate "\n" $ map showfixedwidth ss+    where+      showfixedwidth = printf (printf "%%-%ds" width)+      width = maximum $ map length ss++-- | Join strings vertically, right-aligned and left-padded.+vConcatRightAligned :: [String] -> String+vConcatRightAligned ss = intercalate "\n" $ map showfixedwidth ss+    where+      showfixedwidth = printf (printf "%%%ds" width)+      width = maximum $ map length ss++-- | Convert a multi-line string to a rectangular string top-padded to the specified height.+padtop :: Int -> String -> String+padtop h s = intercalate "\n" xpadded+    where+      ls = lines s+      sh = length ls+      sw | null ls = 0+         | otherwise = maximum $ map length ls+      ypadded = replicate (difforzero h sh) "" ++ ls+      xpadded = map (padleft sw) ypadded++-- | Convert a multi-line string to a rectangular string bottom-padded to the specified height.+padbottom :: Int -> String -> String+padbottom h s = intercalate "\n" xpadded+    where+      ls = lines s+      sh = length ls+      sw | null ls = 0+         | otherwise = maximum $ map length ls+      ypadded = ls ++ replicate (difforzero h sh) ""+      xpadded = map (padleft sw) ypadded++difforzero :: (Num a, Ord a) => a -> a -> a+difforzero a b = maximum [(a - b), 0]++-- | Convert a multi-line string to a rectangular string left-padded to the specified width.+-- Treats wide characters as double width.+padleft :: Int -> String -> String+padleft w "" = concat $ replicate w " "+padleft w s = intercalate "\n" $ map (printf (printf "%%%ds" w)) $ lines s++-- | Convert a multi-line string to a rectangular string right-padded to the specified width.+-- Treats wide characters as double width.+padright :: Int -> String -> String+padright w "" = concat $ replicate w " "+padright w s = intercalate "\n" $ map (printf (printf "%%-%ds" w)) $ lines s++-- | Clip a multi-line string to the specified width and height from the top left.+cliptopleft :: Int -> Int -> String -> String+cliptopleft w h = intercalate "\n" . take h . map (take w) . lines++-- | Clip and pad a multi-line string to fill the specified width and height.+fitto :: Int -> Int -> String -> String+fitto w h s = intercalate "\n" $ take h $ rows ++ repeat blankline+    where+      rows = map (fit w) $ lines s+      fit w = take w . (++ repeat ' ')+      blankline = replicate w ' '++-- Functions below treat wide (eg CJK) characters as double-width.++-- | General-purpose wide-char-aware single-line string layout function.+-- It can left- or right-pad a short string to a minimum width.+-- It can left- or right-clip a long string to a maximum width, optionally inserting an ellipsis (the third argument).+-- It clips and pads on the right when the fourth argument is true, otherwise on the left.+-- It treats wide characters as double width.+fitString :: Maybe Int -> Maybe Int -> Bool -> Bool -> String -> String+fitString mminwidth mmaxwidth ellipsify rightside s = (clip . pad) s+  where+    clip :: String -> String+    clip s =+      case mmaxwidth of+        Just w+          | strWidth s > w ->+            case rightside of+              True  -> takeWidth (w - length ellipsis) s ++ ellipsis+              False -> ellipsis ++ reverse (takeWidth (w - length ellipsis) $ reverse s)+          | otherwise -> s+          where+            ellipsis = if ellipsify then ".." else ""+        Nothing -> s+    pad :: String -> String+    pad s =+      case mminwidth of+        Just w+          | sw < w ->+            case rightside of+              True  -> s ++ replicate (w - sw) ' '+              False -> replicate (w - sw) ' ' ++ s+          | otherwise -> s+        Nothing -> s+      where sw = strWidth s++-- | A version of fitString that works on multi-line strings,+-- separate for now to avoid breakage.+-- This will rewrite any line endings to unix newlines.+fitStringMulti :: Maybe Int -> Maybe Int -> Bool -> Bool -> String -> String+fitStringMulti mminwidth mmaxwidth ellipsify rightside s =+  (intercalate "\n" . map (fitString mminwidth mmaxwidth ellipsify rightside) . lines) s++-- | Left-pad a string to the specified width.+-- Treats wide characters as double width.+-- Works on multi-line strings too (but will rewrite non-unix line endings).+padLeftWide :: Int -> String -> String+padLeftWide w "" = replicate w ' '+padLeftWide w s  = intercalate "\n" $ map (fitString (Just w) Nothing False False) $ lines s+-- XXX not yet replaceable by+-- padLeftWide w = fitStringMulti (Just w) Nothing False False++-- | Right-pad a string to the specified width.+-- Treats wide characters as double width.+-- Works on multi-line strings too (but will rewrite non-unix line endings).+padRightWide :: Int -> String -> String+padRightWide w "" = replicate w ' '+padRightWide w s  = intercalate "\n" $ map (fitString (Just w) Nothing False True) $ lines s+-- XXX not yet replaceable by+-- padRightWide w = fitStringMulti (Just w) Nothing False True++-- | Double-width-character-aware string truncation. Take as many+-- characters as possible from a string without exceeding the+-- specified width. Eg takeWidth 3 "りんご" = "り".+takeWidth :: Int -> String -> String+takeWidth _ ""     = ""+takeWidth 0 _      = ""+takeWidth w (c:cs) | cw <= w   = c:takeWidth (w-cw) cs+                   | otherwise = ""+  where cw = charWidth c++-- from Pandoc (copyright John MacFarlane, GPL)+-- see also http://unicode.org/reports/tr11/#Description++-- | Calculate the designated render width of a string, taking into+-- account wide characters and line breaks (the longest line within a+-- multi-line string determines the width ).+strWidth :: String -> Int+strWidth "" = 0+strWidth s = maximum $ map (foldr (\a b -> charWidth a + b) 0) $ lines s++-- | Get the designated render width of a character: 0 for a combining+-- character, 1 for a regular character, 2 for a wide character.+-- (Wide characters are rendered as exactly double width in apps and+-- fonts that support it.) (From Pandoc.)+charWidth :: Char -> Int+charWidth c =+  case c of+      _ | c <  '\x0300'                    -> 1+        | c >= '\x0300' && c <= '\x036F'   -> 0  -- combining+        | c >= '\x0370' && c <= '\x10FC'   -> 1+        | c >= '\x1100' && c <= '\x115F'   -> 2+        | c >= '\x1160' && c <= '\x11A2'   -> 1+        | c >= '\x11A3' && c <= '\x11A7'   -> 2+        | c >= '\x11A8' && c <= '\x11F9'   -> 1+        | c >= '\x11FA' && c <= '\x11FF'   -> 2+        | c >= '\x1200' && c <= '\x2328'   -> 1+        | c >= '\x2329' && c <= '\x232A'   -> 2+        | c >= '\x232B' && c <= '\x2E31'   -> 1+        | c >= '\x2E80' && c <= '\x303E'   -> 2+        | c == '\x303F'                    -> 1+        | c >= '\x3041' && c <= '\x3247'   -> 2+        | c >= '\x3248' && c <= '\x324F'   -> 1 -- ambiguous+        | c >= '\x3250' && c <= '\x4DBF'   -> 2+        | c >= '\x4DC0' && c <= '\x4DFF'   -> 1+        | c >= '\x4E00' && c <= '\xA4C6'   -> 2+        | c >= '\xA4D0' && c <= '\xA95F'   -> 1+        | c >= '\xA960' && c <= '\xA97C'   -> 2+        | c >= '\xA980' && c <= '\xABF9'   -> 1+        | c >= '\xAC00' && c <= '\xD7FB'   -> 2+        | c >= '\xD800' && c <= '\xDFFF'   -> 1+        | c >= '\xE000' && c <= '\xF8FF'   -> 1 -- ambiguous+        | c >= '\xF900' && c <= '\xFAFF'   -> 2+        | c >= '\xFB00' && c <= '\xFDFD'   -> 1+        | c >= '\xFE00' && c <= '\xFE0F'   -> 1 -- ambiguous+        | c >= '\xFE10' && c <= '\xFE19'   -> 2+        | c >= '\xFE20' && c <= '\xFE26'   -> 1+        | c >= '\xFE30' && c <= '\xFE6B'   -> 2+        | c >= '\xFE70' && c <= '\xFEFF'   -> 1+        | c >= '\xFF01' && c <= '\xFF60'   -> 2+        | c >= '\xFF61' && c <= '\x16A38'  -> 1+        | c >= '\x1B000' && c <= '\x1B001' -> 2+        | c >= '\x1D000' && c <= '\x1F1FF' -> 1+        | c >= '\x1F200' && c <= '\x1F251' -> 2+        | c >= '\x1F300' && c <= '\x1F773' -> 1+        | c >= '\x20000' && c <= '\x3FFFD' -> 2+        | otherwise                        -> 1+
+ Hledger/Utils/Test.hs view
@@ -0,0 +1,41 @@+module Hledger.Utils.Test where++import Test.HUnit+import Text.Parsec++-- | Get a Test's label, or the empty string.+testName :: Test -> String+testName (TestLabel n _) = n+testName _ = ""++-- | Flatten a Test containing TestLists into a list of single tests.+flattenTests :: Test -> [Test]+flattenTests (TestLabel _ t@(TestList _)) = flattenTests t+flattenTests (TestList ts) = concatMap flattenTests ts+flattenTests t = [t]++-- | Filter TestLists in a Test, recursively, preserving the structure.+filterTests :: (Test -> Bool) -> Test -> Test+filterTests p (TestLabel l ts) = TestLabel l (filterTests p ts)+filterTests p (TestList ts) = TestList $ filter (any p . flattenTests) $ map (filterTests p) ts+filterTests _ t = t++-- | Simple way to assert something is some expected value, with no label.+is :: (Eq a, Show a) => a -> a -> Assertion+a `is` e = assertEqual "" e a++-- | Assert a parse result is successful, printing the parse error on failure.+assertParse :: (Either ParseError a) -> Assertion+assertParse parse = either (assertFailure.show) (const (return ())) parse++-- | Assert a parse result is successful, printing the parse error on failure.+assertParseFailure :: (Either ParseError a) -> Assertion+assertParseFailure parse = either (const $ return ()) (const $ assertFailure "parse should not have succeeded") parse++-- | Assert a parse result is some expected value, printing the parse error on failure.+assertParseEqual :: (Show a, Eq a) => (Either ParseError a) -> a -> Assertion+assertParseEqual parse expected = either (assertFailure.show) (`is` expected) parse++printParseError :: (Show a) => a -> IO ()+printParseError e = do putStr "parse error at "; print e+
+ Hledger/Utils/Tree.hs view
@@ -0,0 +1,87 @@+module Hledger.Utils.Tree where++-- import Data.Char+import Data.List (foldl')+import qualified Data.Map as M+import Data.Tree+-- import Text.Parsec+-- import Text.Printf++import Hledger.Utils.Regex+-- import Hledger.Utils.UTF8IOCompat (error')++-- standard tree helpers++root = rootLabel+subs = subForest+branches = subForest++-- | List just the leaf nodes of a tree+leaves :: Tree a -> [a]+leaves (Node v []) = [v]+leaves (Node _ branches) = concatMap leaves branches++-- | get the sub-tree rooted at the first (left-most, depth-first) occurrence+-- of the specified node value+subtreeat :: Eq a => a -> Tree a -> Maybe (Tree a)+subtreeat v t+    | root t == v = Just t+    | otherwise = subtreeinforest v $ subs t++-- | get the sub-tree for the specified node value in the first tree in+-- forest in which it occurs.+subtreeinforest :: Eq a => a -> [Tree a] -> Maybe (Tree a)+subtreeinforest _ [] = Nothing+subtreeinforest v (t:ts) = case (subtreeat v t) of+                             Just t' -> Just t'+                             Nothing -> subtreeinforest v ts++-- | remove all nodes past a certain depth+treeprune :: Int -> Tree a -> Tree a+treeprune 0 t = Node (root t) []+treeprune d t = Node (root t) (map (treeprune $ d-1) $ branches t)++-- | apply f to all tree nodes+treemap :: (a -> b) -> Tree a -> Tree b+treemap f t = Node (f $ root t) (map (treemap f) $ branches t)++-- | remove all subtrees whose nodes do not fulfill predicate+treefilter :: (a -> Bool) -> Tree a -> Tree a+treefilter f t = Node+                 (root t)+                 (map (treefilter f) $ filter (treeany f) $ branches t)++-- | is predicate true in any node of tree ?+treeany :: (a -> Bool) -> Tree a -> Bool+treeany f t = f (root t) || any (treeany f) (branches t)++-- treedrop -- remove the leaves which do fulfill predicate.+-- treedropall -- do this repeatedly.++-- | show a compact ascii representation of a tree+showtree :: Show a => Tree a -> String+showtree = unlines . filter (regexMatches "[^ \\|]") . lines . drawTree . treemap show++-- | show a compact ascii representation of a forest+showforest :: Show a => Forest a -> String+showforest = concatMap showtree+++-- | An efficient-to-build tree suggested by Cale Gibbard, probably+-- better than accountNameTreeFrom.+newtype FastTree a = T (M.Map a (FastTree a))+  deriving (Show, Eq, Ord)++emptyTree = T M.empty++mergeTrees :: (Ord a) => FastTree a -> FastTree a -> FastTree a+mergeTrees (T m) (T m') = T (M.unionWith mergeTrees m m')++treeFromPath :: [a] -> FastTree a+treeFromPath []     = T M.empty+treeFromPath (x:xs) = T (M.singleton x (treeFromPath xs))++treeFromPaths :: (Ord a) => [[a]] -> FastTree a+treeFromPaths = foldl' mergeTrees emptyTree . map treeFromPath++
hledger-lib.cabal view
@@ -1,162 +1,166 @@+-- This file has been generated from package.yaml by hpack version 0.5.4.+--+-- see: https://github.com/sol/hpack+ name:           hledger-lib-version: 0.26+version:        0.27 stability:      stable-category:       Finance, Console-synopsis:       Core data types, parsers and utilities for the hledger accounting tool.-description:-                hledger is a library and set of user tools for working-                with financial data (or anything that can be tracked in a-                double-entry accounting ledger.) It is a haskell port and-                friendly fork of John Wiegley's Ledger. hledger provides-                command-line, curses and web interfaces, and aims to be a-                reliable, practical tool for daily use.-+category:       Finance+synopsis:       Core data types, parsers and functionality for the hledger accounting tools+description:    +    This is a reusable library containing hledger's core functionality.+    hledger is a cross-platform program for tracking money, time, or+    any other commodity, using double-entry accounting and a simple,+    editable file format. It is inspired by and largely compatible+    with ledger(1).  hledger provides command-line, curses and web+    interfaces, and aims to be a reliable, practical tool for daily+    use. license:        GPL license-file:   LICENSE author:         Simon Michael <simon@joyful.com> maintainer:     Simon Michael <simon@joyful.com> homepage:       http://hledger.org-bug-reports:    http://hledger.org/bugs-tested-with:    GHC==7.8.4, GHC==7.10.1+bug-reports:    http://bugs.hledger.org cabal-version:  >= 1.10 build-type:     Simple--- data-dir:       data--- data-files:--- extra-tmp-files:-extra-source-files: -                    tests/suite.hs-                    CHANGES---   README---   sample.ledger---   sample.timelog+tested-with:    GHC==7.4.2, GHC==7.6.3, GHC==7.8.4, GHC==7.10.2 +extra-source-files:+    CHANGES + source-repository head-  type:     git+  type: git   location: https://github.com/simonmichael/hledger  flag double-    description:   Use old Double number representation (instead of Decimal), for testing/benchmarking.-    default:       False-    manual:        True+  manual: True+  default: False+  description:+    Use old Double number representation (instead of Decimal), for testing/benchmarking.  flag old-locale-  description: A compatibility flag, set automatically by cabal.-               If false then depend on time >= 1.5, -               if true then depend on time < 1.5 together with old-locale.   default: False-+  description: +    A compatibility flag, set automatically by cabal.+    If false then depend on time >= 1.5, +    if true then depend on time < 1.5 together with old-locale.  library-  -- should set patchlevel here as in Makefile-  cpp-options: -DPATCHLEVEL=0-  ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures-  ghc-options: -fno-warn-type-defaults -fno-warn-orphans   if flag(double)     cpp-options: -DDOUBLE-  default-language: Haskell2010-  exposed-modules:-                  Hledger-                  Hledger.Data-                  Hledger.Data.Account-                  Hledger.Data.AccountName-                  Hledger.Data.Amount-                  Hledger.Data.Commodity-                  Hledger.Data.Dates-                  Hledger.Data.OutputFormat-                  Hledger.Data.Journal-                  Hledger.Data.Ledger-                  Hledger.Data.Posting-                  Hledger.Data.RawOptions-                  Hledger.Data.TimeLog-                  Hledger.Data.Transaction-                  Hledger.Data.Types-                  Hledger.Query-                  Hledger.Read-                  Hledger.Read.CsvReader-                  Hledger.Read.JournalReader-                  Hledger.Read.TimelogReader-                  Hledger.Reports-                  Hledger.Reports.ReportOptions-                  Hledger.Reports.BalanceHistoryReport-                  Hledger.Reports.BalanceReport-                  Hledger.Reports.EntriesReport-                  Hledger.Reports.MultiBalanceReports-                  Hledger.Reports.PostingsReport-                  Hledger.Reports.TransactionsReports-                  Hledger.Utils-                  Hledger.Utils.Debug-                  Hledger.Utils.Regex-                  Hledger.Utils.UTF8IOCompat+  ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans   build-depends:-                  base >= 4.3 && < 5-                 ,base-compat >= 0.8.1-                 ,array-                 ,blaze-markup >= 0.5.1-                 ,bytestring-                 ,cmdargs >= 0.10 && < 0.11-                 ,containers-                 ,csv-                 -- ,data-pprint >= 0.2.3 && < 0.3-                 ,Decimal-                 ,directory-                 ,filepath-                 ,mtl-                 ,mtl-compat-                 ,old-time-                 ,parsec >= 3-                 ,regex-tdfa-                 ,safe >= 0.2-                 ,split >= 0.1 && < 0.3-                 ,transformers >= 0.2 && < 0.5-                 ,utf8-string >= 0.3.5 && < 1.1-                 ,HUnit+      base >= 4.3 && < 5+    , base-compat >= 0.8.1+    , array+    , blaze-markup >= 0.5.1+    , bytestring+    , cmdargs >= 0.10 && < 0.11+    , containers+    , csv+    , Decimal+    , deepseq+    , directory+    , filepath+    , mtl+    , mtl-compat+    , old-time+    , parsec >= 3+    , regex-tdfa+    , safe >= 0.2+    , split >= 0.1 && < 0.3+    , transformers >= 0.2 && < 0.5+    , uglymemo+    , utf8-string >= 0.3.5 && < 1.1+    , HUnit+   if impl(ghc >= 7.4)     build-depends: pretty-show >= 1.6.4+   if flag(old-locale)     build-depends: time < 1.5, old-locale   else     build-depends: time >= 1.5 +  exposed-modules:+      Hledger+      Hledger.Data+      Hledger.Data.Account+      Hledger.Data.AccountName+      Hledger.Data.Amount+      Hledger.Data.Commodity+      Hledger.Data.Dates+      Hledger.Data.Journal+      Hledger.Data.Ledger+      Hledger.Data.StringFormat+      Hledger.Data.Posting+      Hledger.Data.RawOptions+      Hledger.Data.TimeLog+      Hledger.Data.Transaction+      Hledger.Data.Types+      Hledger.Query+      Hledger.Read+      Hledger.Read.CsvReader+      Hledger.Read.JournalReader+      Hledger.Read.TimelogReader+      Hledger.Reports+      Hledger.Reports.ReportOptions+      Hledger.Reports.BalanceHistoryReport+      Hledger.Reports.BalanceReport+      Hledger.Reports.EntriesReport+      Hledger.Reports.MultiBalanceReports+      Hledger.Reports.PostingsReport+      Hledger.Reports.TransactionsReports+      Hledger.Utils+      Hledger.Utils.Debug+      Hledger.Utils.Parse+      Hledger.Utils.Regex+      Hledger.Utils.String+      Hledger.Utils.Test+      Hledger.Utils.Tree+      Hledger.Utils.UTF8IOCompat+  default-language: Haskell2010  test-suite tests-  type:     exitcode-stdio-1.0-  main-is:  suite.hs-  hs-source-dirs: tests-  ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures-  ghc-options: -fno-warn-type-defaults -fno-warn-orphans-  default-language: Haskell2010-  build-depends: hledger-lib-               , base >= 4.3 && < 5-               , base-compat >= 0.8.1-               , array-               , blaze-markup >= 0.5.1-               , cmdargs-               , containers-               , csv-               -- , data-pprint >= 0.2.3 && < 0.3-               , Decimal-               , directory-               , filepath-               , HUnit-               , mtl-               , mtl-compat-               , old-time-               , parsec >= 3-               , regex-tdfa-               , safe-               , split-               , test-framework-               , test-framework-hunit-               , transformers+  type: exitcode-stdio-1.0+  main-is: suite.hs+  hs-source-dirs:+      tests+  ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans+  build-depends:+      base >= 4.3 && < 5+    , base-compat >= 0.8.1+    , array+    , blaze-markup >= 0.5.1+    , bytestring+    , cmdargs >= 0.10 && < 0.11+    , containers+    , csv+    , Decimal+    , deepseq+    , directory+    , filepath+    , mtl+    , mtl-compat+    , old-time+    , parsec >= 3+    , regex-tdfa+    , safe >= 0.2+    , split >= 0.1 && < 0.3+    , transformers >= 0.2 && < 0.5+    , uglymemo+    , utf8-string >= 0.3.5 && < 1.1+    , HUnit+    , hledger-lib+    , test-framework+    , test-framework-hunit+   if impl(ghc >= 7.4)     build-depends: pretty-show >= 1.6.4+   if flag(old-locale)     build-depends: time < 1.5, old-locale   else     build-depends: time >= 1.5 --- cf http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html---- Additional dependencies:--- required for make test: test-framework, test-framework-hunit--- required for make bench: tabular-0.1+  default-language: Haskell2010