@@ -135,6 +135,7 @@ module Hledger.Data.Amount (
135
135
showMixedAmountElided ,
136
136
showMixedAmountWithZeroCommodity ,
137
137
showMixedAmountB ,
138
+ showMixedAmountOneLineB ,
138
139
showMixedAmountLinesB ,
139
140
buildCell ,
140
141
mixedAmountSetPrecision ,
@@ -151,7 +152,7 @@ import Data.Char (isDigit)
151
152
import Data.Decimal (DecimalRaw (.. ), decimalPlaces , normalizeDecimal , roundTo )
152
153
import Data.Default (Default (.. ))
153
154
import Data.Foldable (toList )
154
- import Data.List (find , foldl' , intercalate , intersperse , mapAccumL , partition )
155
+ import Data.List (find , foldl' , intercalate , intersperse )
155
156
import Data.List.NonEmpty (NonEmpty (.. ), nonEmpty )
156
157
import qualified Data.Map.Strict as M
157
158
import qualified Data.Set as S
@@ -160,16 +161,16 @@ import Data.Semigroup (Semigroup(..))
160
161
import Data.Text (Text )
161
162
import qualified Data.Text as T
162
163
import Data.Word (Word8 )
163
- import Safe (lastDef , lastMay )
164
164
import System.Console.ANSI (Color (.. ),ColorIntensity (.. ))
165
+ import Text.Layout.Table (right , singleCutMark )
166
+ import Text.Layout.Table.Cell.ElidableList (ElidableList , elidableListR )
165
167
166
168
import Test.Tasty (testGroup )
167
169
import Test.Tasty.HUnit ((@?=) , assertBool , testCase )
168
170
169
171
import Hledger.Data.Types
170
172
import Hledger.Utils
171
- (Cell (.. ), RenderText , numDigitsInt , textQuoteIfNeeded , trace , colorB ,
172
- renderText , visibleLength )
173
+ (Cell (.. ), RenderText , textQuoteIfNeeded , trace , colorB , renderText , trim )
173
174
174
175
175
176
-- A 'Commodity' is a symbol representing a currency or some other kind of
@@ -201,8 +202,6 @@ data AmountDisplayOpts = AmountDisplayOpts
201
202
, displayThousandsSep :: Bool -- ^ Whether to display thousands separators.
202
203
, displayColour :: Bool -- ^ Whether to colourise negative Amounts.
203
204
, displayOneLine :: Bool -- ^ Whether to display on one line.
204
- , displayMinWidth :: Maybe Int -- ^ Minimum width to pad to
205
- , displayMaxWidth :: Maybe Int -- ^ Maximum width to clip to
206
205
-- | Display amounts in this order (without the commodity symbol) and display
207
206
-- a 0 in case a corresponding commodity does not exist
208
207
, displayOrder :: Maybe [CommoditySymbol ]
@@ -218,8 +217,6 @@ noColour = AmountDisplayOpts { displayPrice = True
218
217
, displayZeroCommodity = False
219
218
, displayThousandsSep = True
220
219
, displayOneLine = False
221
- , displayMinWidth = Just 0
222
- , displayMaxWidth = Nothing
223
220
, displayOrder = Nothing
224
221
}
225
222
@@ -802,17 +799,17 @@ showMixedAmountWithoutPrice c = buildCell . showMixedAmountB noPrice{displayColo
802
799
-- any \@ prices.
803
800
-- With a True argument, adds ANSI codes to show negative amounts in red.
804
801
--
805
- -- > showMixedAmountOneLineWithoutPrice c = buildCell . showMixedAmountB oneLine {displayColour=c}
802
+ -- > showMixedAmountOneLineWithoutPrice c = buildCell . showMixedAmountOneLineB noPrice {displayColour=c}
806
803
showMixedAmountOneLineWithoutPrice :: Bool -> MixedAmount -> String
807
- showMixedAmountOneLineWithoutPrice c = buildCell . showMixedAmountB oneLine {displayColour= c}
804
+ showMixedAmountOneLineWithoutPrice c = buildCell . showMixedAmountB noPrice {displayColour= c}
808
805
809
806
-- | Like showMixedAmountOneLineWithoutPrice, but show at most the given width,
810
807
-- with an elision indicator if there are more.
811
808
-- With a True argument, adds ANSI codes to show negative amounts in red.
812
809
--
813
- -- > showMixedAmountElided w c = buildCell . showMixedAmountB oneLine {displayColour=c, displayMaxWidth=Just w }
810
+ -- > showMixedAmountElided w c = trim right w . showMixedAmountOneLineB noPrice {displayColour=c}
814
811
showMixedAmountElided :: Int -> Bool -> MixedAmount -> String
815
- showMixedAmountElided w c = buildCell . showMixedAmountB oneLine {displayColour= c, displayMaxWidth = Just w }
812
+ showMixedAmountElided w c = trim right (singleCutMark " .. " ) w . showMixedAmountOneLineB noPrice {displayColour= c}
816
813
817
814
-- | Get an unambiguous string representation of a mixed amount for debugging.
818
815
showMixedAmountDebug :: MixedAmount -> String
@@ -831,10 +828,10 @@ showMixedAmountDebug m | m == missingmixedamt = "(missing)"
831
828
-- exceed the requested maximum width.
832
829
-- - If displayed on multiple lines, any Amounts longer than the
833
830
-- maximum width will be elided.
834
- showMixedAmountB :: AmountDisplayOpts -> MixedAmount -> RenderText
831
+ showMixedAmountB :: AmountDisplayOpts -> MixedAmount -> Either ( ElidableList String RenderText ) RenderText
835
832
showMixedAmountB opts ma
836
- | displayOneLine opts = showMixedAmountOneLineB opts ma
837
- | otherwise = mconcat $ intersperse sep lines
833
+ | displayOneLine opts = Left $ showMixedAmountOneLineB opts ma
834
+ | otherwise = Right . mconcat $ intersperse sep lines
838
835
where
839
836
lines = showMixedAmountLinesB opts ma
840
837
sep = " \n "
@@ -844,96 +841,21 @@ showMixedAmountB opts ma
844
841
-- width. This does not honour displayOneLine: all amounts will be displayed as if
845
842
-- displayOneLine were False.
846
843
showMixedAmountLinesB :: AmountDisplayOpts -> MixedAmount -> [RenderText ]
847
- showMixedAmountLinesB opts@ AmountDisplayOpts {displayMaxWidth= mmax,displayMinWidth= mmin} ma =
848
- map (adBuilder . pad) elided
849
- where
850
- astrs = amtDisplayList 0 (showAmountB opts) . orderedAmounts opts $
851
- if displayPrice opts then ma else mixedAmountStripPrices ma
852
- width = maximum $ map (visibleLength . adBuilder) elided
853
-
854
- pad amt
855
- | Just mw <- mmin =
856
- let w = (max width mw) - visibleLength (adBuilder amt)
857
- in amt{ adBuilder = renderText (T. replicate w " " ) <> adBuilder amt }
858
- | otherwise = amt
859
-
860
- elided = maybe id elideTo mmax astrs
861
- elideTo m xs = maybeAppend elisionStr short
862
- where
863
- elisionStr = elisionDisplay (Just m) 0 (length long) $ lastDef nullAmountDisplay short
864
- (short, long) = partition ((m>= ) . visibleLength . adBuilder) xs
844
+ showMixedAmountLinesB opts =
845
+ map (showAmountB opts) . orderedAmounts opts
846
+ . if displayPrice opts then id else mixedAmountStripPrices
865
847
866
848
-- | Helper for showMixedAmountB to deal with single line displays. This does not
867
849
-- honour displayOneLine: all amounts will be displayed as if displayOneLine
868
850
-- were True.
869
- showMixedAmountOneLineB :: AmountDisplayOpts -> MixedAmount -> RenderText
870
- showMixedAmountOneLineB opts@ AmountDisplayOpts {displayMaxWidth= mmax,displayMinWidth= mmin} ma =
871
- pad . mconcat . intersperse sep $ map adBuilder elided
872
- where
873
- width = maybe 0 adTotal $ lastMay elided
874
- astrs = amtDisplayList (visibleLength sep) (showAmountB opts) . orderedAmounts opts $
875
- if displayPrice opts then ma else mixedAmountStripPrices ma
876
- sep = " , "
877
- n = length astrs
878
-
879
- pad = (renderText (T. replicate (fromMaybe 0 mmin - width) " " ) <> )
880
-
881
- elided = maybe id elideTo mmax astrs
882
- elideTo m = addElide . takeFitting m . withElided
883
- -- Add the last elision string to the end of the display list
884
- addElide [] = []
885
- addElide xs = maybeAppend (snd $ last xs) $ map fst xs
886
- -- Return the elements of the display list which fit within the maximum width
887
- -- (including their elision strings). Always display at least one amount,
888
- -- regardless of width.
889
- takeFitting _ [] = []
890
- takeFitting m (x: xs) = x : dropWhileRev (\ (a,e) -> m < adTotal (fromMaybe a e)) xs
891
- dropWhileRev p = foldr (\ x xs -> if null xs && p x then [] else x: xs) []
892
-
893
- -- Add the elision strings (if any) to each amount
894
- withElided = zipWith (\ num amt -> (amt, elisionDisplay Nothing (visibleLength sep) num amt)) [n- 1 ,n- 2 .. 0 ]
851
+ showMixedAmountOneLineB :: AmountDisplayOpts -> MixedAmount -> ElidableList String RenderText
852
+ showMixedAmountOneLineB opts = elidableListR (\ n -> show n ++ " more.." ) " , " . showMixedAmountLinesB opts
895
853
896
854
orderedAmounts :: AmountDisplayOpts -> MixedAmount -> [Amount ]
897
855
orderedAmounts dopts = maybe id (mapM pad) (displayOrder dopts) . amounts
898
856
where
899
857
pad c = fromMaybe (amountWithCommodity c nullamt) . find ((c== ) . acommodity)
900
858
901
-
902
- data AmountDisplay = AmountDisplay
903
- { adBuilder :: ! RenderText -- ^ String representation of the Amount
904
- , adTotal :: ! Int -- ^ Cumulative length of MixedAmount this Amount is part of, including separators
905
- } deriving (Show )
906
-
907
- nullAmountDisplay :: AmountDisplay
908
- nullAmountDisplay = AmountDisplay mempty 0
909
-
910
- amtDisplayList :: Int -> (Amount -> RenderText ) -> [Amount ] -> [AmountDisplay ]
911
- amtDisplayList sep showamt = snd . mapAccumL display (- sep)
912
- where
913
- display tot amt = (tot', AmountDisplay str tot')
914
- where
915
- str = showamt amt
916
- tot' = tot + (visibleLength str) + sep
917
-
918
- -- The string "m more", added to the previous running total
919
- elisionDisplay :: Maybe Int -> Int -> Int -> AmountDisplay -> Maybe AmountDisplay
920
- elisionDisplay mmax sep n lastAmt
921
- | n > 0 = Just $ AmountDisplay str (adTotal lastAmt + len)
922
- | otherwise = Nothing
923
- where
924
- fullString = T. pack $ show n ++ " more.."
925
- -- sep from the separator, 7 from " more..", numDigits n from number
926
- fullLength = sep + 7 + numDigitsInt n
927
-
928
- str | Just m <- mmax, fullLength > m = renderText $ T. take (m - 2 ) fullString <> " .."
929
- | otherwise = renderText fullString
930
- len = case mmax of Nothing -> fullLength
931
- Just m -> max 2 $ min m fullLength
932
-
933
- maybeAppend :: Maybe a -> [a ] -> [a ]
934
- maybeAppend Nothing = id
935
- maybeAppend (Just a) = (++ [a])
936
-
937
859
-- | Compact labelled trace of a mixed amount, for debugging.
938
860
ltraceamount :: String -> MixedAmount -> MixedAmount
939
861
ltraceamount s a = trace (s ++ " : " ++ showMixedAmount a) a
0 commit comments