Skip to content

Commit 169198f

Browse files
authored
Merge branch 'master' into fix-matchgroups
2 parents 3b416a7 + 861a57e commit 169198f

File tree

28 files changed

+1189
-1092
lines changed

28 files changed

+1189
-1092
lines changed

Justfile

Lines changed: 237 additions & 249 deletions
Large diffs are not rendered by default.

Shake.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env stack
2-
{- stack script --resolver nightly-2023-10-13 --compile
2+
{- stack script --resolver nightly-2024-02-28 --compile
33
--extra-include-dirs /Library/Developer/CommandLineTools/SDKs/MacOSX12.1.sdk/usr/include/ffi
44
--package base-prelude
55
--package directory

hledger-lib/Hledger/Data/Amount.hs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ module Hledger.Data.Amount (
9797
amountSetPrecisionMax,
9898
withPrecision,
9999
amountSetFullPrecision,
100-
amountSetFullPrecisionOr,
100+
amountSetFullPrecisionUpTo,
101101
amountInternalPrecision,
102102
amountDisplayPrecision,
103103
defaultMaxPrecision,
@@ -159,6 +159,7 @@ module Hledger.Data.Amount (
159159
wbUnpack,
160160
mixedAmountSetPrecision,
161161
mixedAmountSetFullPrecision,
162+
mixedAmountSetFullPrecisionUpTo,
162163
mixedAmountSetPrecisionMin,
163164
mixedAmountSetPrecisionMax,
164165

@@ -453,24 +454,26 @@ amountSetFullPrecision a = amountSetPrecision p a
453454

454455

455456
-- | We often want to display "infinite decimal" amounts rounded to some readable
456-
-- number of digits, while still displaying amounts with a large "non infinite" number
457-
-- of decimal digits (eg, 100 or 200 digits) in full.
457+
-- number of digits, while still displaying amounts with a large but "non infinite"
458+
-- number of decimal digits (eg 10 or 100 or 200 digits) in full.
458459
-- This helper is like amountSetFullPrecision, but with some refinements:
459-
-- 1. If the internal precision is the maximum (255), indicating an infinite decimal,
460-
-- the display precision is set to a smaller hard-coded default (8).
461-
-- 2. A maximum display precision can be specified, setting a hard upper limit.
460+
--
461+
-- 1. A maximum display precision can be specified, setting a hard upper limit.
462+
--
463+
-- 2. If no limit is specified, and the internal precision is the maximum (255),
464+
-- indicating an infinite decimal, display precision is set to a smaller default (8).
465+
--
462466
-- This function always sets an explicit display precision (ie, Precision n).
463-
amountSetFullPrecisionOr :: Maybe Word8 -> Amount -> Amount
464-
amountSetFullPrecisionOr mmaxp a = amountSetPrecision (Precision p2) a
467+
--
468+
amountSetFullPrecisionUpTo :: Maybe Word8 -> Amount -> Amount
469+
amountSetFullPrecisionUpTo mmaxp a = amountSetPrecision (Precision p) a
465470
where
466-
p1 = if -- dbg0 "maxdigits" $
467-
amountHasMaxDigits a then defaultMaxPrecision else max disp intp
468-
-- & dbg0 "p1"
471+
p = case mmaxp of
472+
Just maxp -> min maxp $ max disp intp
473+
Nothing -> if amountHasMaxDigits a then defaultMaxPrecision else max disp intp
469474
where
470-
intp = amountInternalPrecision a
471475
disp = amountDisplayPrecision a
472-
p2 = maybe p1 (min p1) mmaxp
473-
-- & dbg0 "p2"
476+
intp = amountInternalPrecision a
474477

475478
-- | The fallback display precision used when showing amounts
476479
-- representing an infinite decimal.
@@ -1228,6 +1231,14 @@ mixedAmountSetPrecision p = mapMixedAmountUnsafe (amountSetPrecision p)
12281231
mixedAmountSetFullPrecision :: MixedAmount -> MixedAmount
12291232
mixedAmountSetFullPrecision = mapMixedAmountUnsafe amountSetFullPrecision
12301233

1234+
-- | In each component amount, increase the display precision sufficiently
1235+
-- to render it exactly if possible, but not more than the given max precision,
1236+
-- and if no max precision is given and the amount has infinite decimals,
1237+
-- limit display precision to a hard-coded smaller number (8).
1238+
-- See amountSetFullPrecisionUpTo.
1239+
mixedAmountSetFullPrecisionUpTo :: Maybe Word8 -> MixedAmount -> MixedAmount
1240+
mixedAmountSetFullPrecisionUpTo mmaxp = mapMixedAmountUnsafe (amountSetFullPrecisionUpTo mmaxp)
1241+
12311242
-- | In each component amount, ensure the display precision is at least the given value.
12321243
-- Makes all amounts have an explicit Precision.
12331244
mixedAmountSetPrecisionMin :: Word8 -> MixedAmount -> MixedAmount

hledger-lib/Hledger/Data/Balancing.hs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,17 @@ transactionCheckBalanced BalancingOpts{commodity_styles_} t = errs
131131
rmsg
132132
| rsumok = ""
133133
| not rsignsok = "The real postings all have the same sign. Consider negating some of them."
134-
| otherwise = "The real postings' sum should be 0 but is: " ++ showMixedAmountOneLineWithoutCost False rsumcost
134+
| otherwise = "The real postings' sum should be 0 but is: " ++
135+
(showMixedAmountWith oneLineNoCostFmt{displayCost=True, displayZeroCommodity=True} $
136+
mixedAmountSetFullPrecisionUpTo Nothing $ mixedAmountSetFullPrecision
137+
rsumcost)
135138
bvmsg
136139
| bvsumok = ""
137140
| not bvsignsok = "The balanced virtual postings all have the same sign. Consider negating some of them."
138-
| otherwise = "The balanced virtual postings' sum should be 0 but is: " ++ showMixedAmountOneLineWithoutCost False bvsumcost
141+
| otherwise = "The balanced virtual postings' sum should be 0 but is: " ++
142+
(showMixedAmountWith oneLineNoCostFmt{displayCost=True, displayZeroCommodity=True} $
143+
mixedAmountSetFullPrecisionUpTo Nothing $ mixedAmountSetFullPrecision
144+
bvsumcost)
139145

140146
-- | Legacy form of transactionCheckBalanced.
141147
isTransactionBalanced :: BalancingOpts -> Transaction -> Bool

hledger-lib/Hledger/Data/Dates.hs

Lines changed: 67 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ module Hledger.Data.Dates (
6363
spanIntersect,
6464
spansIntersect,
6565
spanDefaultsFrom,
66+
spanExtend,
6667
spanUnion,
6768
spansUnion,
6869
daysSpan,
@@ -314,8 +315,8 @@ groupByDateSpan showempty date colspans =
314315
where
315316
groupByCols [] _ = []
316317
groupByCols (c:cs) [] = if showempty then (c, []) : groupByCols cs [] else []
317-
groupByCols (c:cs) ps = (c, map snd matches) : groupByCols cs later
318-
where (matches, later) = span ((spanEnd c >) . Just . fst) ps
318+
groupByCols (c:cs) ps = (c, map snd colps) : groupByCols cs laterps
319+
where (colps, laterps) = span ((spanEnd c >) . Just . fst) ps
319320

320321
beforeStart = maybe (const False) (>) $ spanStart =<< headMay colspans
321322

@@ -324,40 +325,82 @@ spansIntersect [] = nulldatespan
324325
spansIntersect [d] = d
325326
spansIntersect (d:ds) = d `spanIntersect` (spansIntersect ds)
326327

328+
-- | Calculate the union of a number of datespans.
329+
spansUnion [] = nulldatespan
330+
spansUnion [d] = d
331+
spansUnion (d:ds) = d `spanUnion` (spansUnion ds)
332+
327333
-- | Calculate the intersection of two datespans.
328334
--
329335
-- For non-intersecting spans, gives an empty span beginning on the second's start date:
330336
-- >>> DateSpan (Just $ Flex $ fromGregorian 2018 01 01) (Just $ Flex $ fromGregorian 2018 01 03) `spanIntersect` DateSpan (Just $ Flex $ fromGregorian 2018 01 03) (Just $ Flex $ fromGregorian 2018 01 05)
331337
-- DateSpan 2018-01-03..2018-01-02
332-
spanIntersect (DateSpan b1 e1) (DateSpan b2 e2) = DateSpan b e
333-
where
334-
b = latest b1 b2
335-
e = earliest e1 e2
338+
spanIntersect (DateSpan b1 e1) (DateSpan b2 e2) = DateSpan (laterDefinite b1 b2) (earlierDefinite e1 e2)
336339

337340
-- | Fill any unspecified dates in the first span with the dates from
338-
-- the second one. Sort of a one-way spanIntersect.
341+
-- the second one (if specified there). Sort of a one-way spanIntersect.
339342
spanDefaultsFrom (DateSpan a1 b1) (DateSpan a2 b2) = DateSpan a b
340343
where a = if isJust a1 then a1 else a2
341344
b = if isJust b1 then b1 else b2
342345

343-
-- | Calculate the union of a number of datespans.
344-
spansUnion [] = nulldatespan
345-
spansUnion [d] = d
346-
spansUnion (d:ds) = d `spanUnion` (spansUnion ds)
347-
348346
-- | Calculate the union of two datespans.
349-
spanUnion (DateSpan b1 e1) (DateSpan b2 e2) = DateSpan b e
350-
where
351-
b = earliest b1 b2
352-
e = latest e1 e2
353-
354-
latest d Nothing = d
355-
latest Nothing d = d
356-
latest (Just d1) (Just d2) = Just $ max d1 d2
357-
358-
earliest d Nothing = d
359-
earliest Nothing d = d
360-
earliest (Just d1) (Just d2) = Just $ min d1 d2
347+
-- If either span is open-ended, the union will be too.
348+
--
349+
-- >>> ys2024 = fromGregorian 2024 01 01
350+
-- >>> ys2025 = fromGregorian 2025 01 01
351+
-- >>> to2024 = DateSpan Nothing (Just $ Exact ys2024)
352+
-- >>> in2024 = DateSpan (Just $ Exact ys2024) (Just $ Exact ys2025)
353+
-- >>> spanUnion to2024 in2024
354+
-- DateSpan ..2024-12-31
355+
-- >>> spanUnion in2024 to2024
356+
-- DateSpan ..2024-12-31
357+
spanUnion (DateSpan b1 e1) (DateSpan b2 e2) = DateSpan (earlier b1 b2) (later e1 e2)
358+
359+
-- | Extend the first span to include any definite end dates of the second.
360+
-- Unlike spanUnion, open ends in the second are ignored.
361+
-- If the first span was open-ended, it still will be after being extended.
362+
--
363+
-- >>> ys2024 = fromGregorian 2024 01 01
364+
-- >>> ys2025 = fromGregorian 2025 01 01
365+
-- >>> to2024 = DateSpan Nothing (Just $ Exact ys2024)
366+
-- >>> all2024 = DateSpan (Just $ Exact ys2024) (Just $ Exact ys2025)
367+
-- >>> partof2024 = DateSpan (Just $ Exact $ fromGregorian 2024 03 01) (Just $ Exact $ fromGregorian 2024 09 01)
368+
-- >>> spanExtend to2024 all2024
369+
-- DateSpan 2024
370+
-- >>> spanExtend all2024 to2024
371+
-- DateSpan 2024
372+
-- >>> spanExtend partof2024 all2024
373+
-- DateSpan 2024
374+
-- >>> spanExtend all2024 partof2024
375+
-- DateSpan 2024
376+
--
377+
spanExtend (DateSpan b1 e1) (DateSpan b2 e2) = DateSpan (earlierDefinite b1 b2) (laterDefinite e1 e2)
378+
379+
-- | Pick the earlier of two DateSpan starts, treating Nothing as infinitely early.
380+
-- An Exact and Flex with the same date are considered equal; the first argument wins.
381+
earlier :: Maybe EFDay -> Maybe EFDay -> Maybe EFDay
382+
earlier = min
383+
384+
-- | Pick the later of two DateSpan starts, treating Nothing as infinitely late.
385+
-- An Exact and Flex with the same date are considered equal; the second argument wins.
386+
later :: Maybe EFDay -> Maybe EFDay -> Maybe EFDay
387+
later _ Nothing = Nothing
388+
later Nothing _ = Nothing
389+
later d1 d2 = max d1 d2
390+
391+
-- | Pick the earlier of two DateSpan ends that is a definite date (if any).
392+
-- An Exact and Flex with the same date are considered equal; the first argument wins.
393+
earlierDefinite :: Maybe EFDay -> Maybe EFDay -> Maybe EFDay
394+
earlierDefinite d1 Nothing = d1
395+
earlierDefinite Nothing d2 = d2
396+
earlierDefinite d1 d2 = min d1 d2
397+
398+
-- | Pick the later of two DateSpan ends that is a definite date (if any).
399+
-- An Exact and Flex with the same date are considered equal; the second argument wins.
400+
laterDefinite :: Maybe EFDay -> Maybe EFDay -> Maybe EFDay
401+
laterDefinite d1 Nothing = d1
402+
laterDefinite Nothing d2 = d2
403+
laterDefinite d1 d2 = max d1 d2
361404

362405
-- | Calculate the minimal DateSpan containing all of the given Days (in the
363406
-- usual exclusive-end-date sense: beginning on the earliest, and ending on

hledger-lib/Hledger/Data/Valuation.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ amountPriceDirectiveFromCost :: Day -> Amount -> Maybe PriceDirective
115115
amountPriceDirectiveFromCost d amt@Amount{acommodity=fromcomm, aquantity=n} = case acost amt of
116116
Just (UnitCost u) -> Just $ pd{pdamount=u}
117117
Just (TotalCost t) | n /= 0 -> Just $ pd{pdamount=u}
118-
where u = amountSetFullPrecisionOr Nothing $ divideAmount n t
118+
where u = amountSetFullPrecisionUpTo Nothing $ divideAmount n t
119119
_ -> Nothing
120120
where
121121
pd = PriceDirective{pddate = d, pdcommodity = fromcomm, pdamount = nullamt}
@@ -209,7 +209,7 @@ amountValueAtDate priceoracle styles mto d a =
209209
-- set the display precision to match the internal precision (showing all digits),
210210
-- unnormalised (don't strip trailing zeros);
211211
-- but if it looks like an infinite decimal, limit the precision to 8.
212-
& amountSetFullPrecisionOr Nothing
212+
& amountSetFullPrecisionUpTo Nothing
213213
& dbg9With (lbl "calculated value".showAmount)
214214

215215
-- | Calculate the gain of each component amount, that is the difference

hledger-lib/Hledger/Reports/ReportOptions.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,7 @@ reportSpanHelper bothdates j ReportSpec{_rsQuery=query, _rsReportOpts=ropts} =
696696
_ -> Nothing
697697
-- If the requested span is open-ended, close it using the journal's start and end dates.
698698
-- This can still be the null (open) span if the journal is empty.
699-
requestedspan' = dbg3 "requestedspan'" $ requestedspan `spanDefaultsFrom` (journalspan `spanUnion` pricespan)
699+
requestedspan' = dbg3 "requestedspan'" $ requestedspan `spanDefaultsFrom` (journalspan `spanExtend` pricespan)
700700
-- The list of interval spans enclosing the requested span.
701701
-- This list can be empty if the journal was empty,
702702
-- or if hledger-ui has added its special date:-tomorrow to the query

hledger-ui/hledger-ui.m4.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,9 +341,11 @@ This is very useful when reconciling. A good workflow is to have
341341
your bank's online register open in a browser window, for reference;
342342
the journal file open in an editor window;
343343
and hledger-ui in watch mode in a terminal window, eg:
344+
344345
```cli
345346
$ hledger-ui --watch --register checking -C
346347
```
348+
347349
As you mark things cleared in the editor,
348350
you can see the effect immediately without having to context switch.
349351
This leaves more mental bandwidth for your accounting.

hledger-web/Hledger/Web/Widget/Common.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ accountOnlyQuery = ("inacctonly:" <>) . quoteIfSpaced
9595

9696
mixedAmountAsHtml :: MixedAmount -> HtmlUrl a
9797
mixedAmountAsHtml b _ =
98-
for_ (lines (showMixedAmountWithoutCost False b)) $ \t -> do
98+
for_ (lines (showMixedAmountWith noCostFmt{displayZeroCommodity=True} b)) $ \t -> do
9999
H.span ! A.class_ c $ toHtml t
100100
H.br
101101
where

hledger-web/hledger-web.m4.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,12 @@ Query options and arguments may be used to set an initial filter,
129129
which although not shown in the UI, will restrict the data shown,
130130
in addition to any search query entered in the UI.
131131

132+
Note that hledger-web shows accounts with zero balances by default, like `hledger-ui` (and unlike `hledger`).
133+
Using the `-E/--empty` flag at startup will hide them.
134+
135+
If you see accounts which appear to have a zero balance, but cannot be hidden with `-E`:
136+
these have a mixed-cost balance which looks like zero when costs are hidden.
137+
Currently hledger-web does not show costs at all.
132138

133139
## General help options
134140

0 commit comments

Comments
 (0)