lib: Implement concat(Top|Bottom)Padded in terms of renderRow, allowing them to be width aware.
This commit is contained in:
		
							parent
							
								
									a620ab9666
								
							
						
					
					
						commit
						6d7bd9e475
					
				| @ -50,13 +50,18 @@ module Hledger.Utils.String ( | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| import Data.Char (isSpace, toLower, toUpper) | import Data.Char (isSpace, toLower, toUpper) | ||||||
| import Data.List (intercalate, transpose) | import Data.Default (def) | ||||||
|  | import Data.List (intercalate) | ||||||
| import Text.Megaparsec ((<|>), between, many, noneOf, sepBy) | import Text.Megaparsec ((<|>), between, many, noneOf, sepBy) | ||||||
| import Text.Megaparsec.Char (char) | import Text.Megaparsec.Char (char) | ||||||
| import Text.Printf (printf) | import Text.Printf (printf) | ||||||
| 
 | 
 | ||||||
| import Hledger.Utils.Parse | import Hledger.Utils.Parse | ||||||
| import Hledger.Utils.Regex (toRegex', regexReplace) | import Hledger.Utils.Regex (toRegex', regexReplace) | ||||||
|  | import Text.Tabular (Header(..), Properties(..)) | ||||||
|  | import Text.Tabular.AsciiWide (Align(..), Cell(..), TableOpts(..), renderRow) | ||||||
|  | import Text.WideString (strWidth, charWidth) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| -- | Take elements from the end of a list. | -- | Take elements from the end of a list. | ||||||
| takeEnd n l = go (drop n l) l | takeEnd n l = go (drop n l) l | ||||||
| @ -172,26 +177,16 @@ unbracket s | |||||||
| -- | Join several multi-line strings as side-by-side rectangular strings of the same height, top-padded. | -- | Join several multi-line strings as side-by-side rectangular strings of the same height, top-padded. | ||||||
| -- Treats wide characters as double width. | -- Treats wide characters as double width. | ||||||
| concatTopPadded :: [String] -> String | concatTopPadded :: [String] -> String | ||||||
| concatTopPadded strs = intercalate "\n" $ map concat $ transpose padded | concatTopPadded = renderRow def{tableBorders=False, borderSpaces=False} | ||||||
|     where |                 . Group NoLine . map (Header . cell) | ||||||
|       lss = map lines strs |   where cell = Cell BottomLeft . map (\x -> (x, strWidth x)) . lines | ||||||
|       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. | -- | Join several multi-line strings as side-by-side rectangular strings of the same height, bottom-padded. | ||||||
| -- Treats wide characters as double width. | -- Treats wide characters as double width. | ||||||
| concatBottomPadded :: [String] -> String | concatBottomPadded :: [String] -> String | ||||||
| concatBottomPadded strs = intercalate "\n" $ map concat $ transpose padded | concatBottomPadded = renderRow def{tableBorders=False, borderSpaces=False} | ||||||
|     where |                    . Group NoLine . map (Header . cell) | ||||||
|       lss = map lines strs |   where cell = Cell TopLeft . map (\x -> (x, strWidth x)) . lines | ||||||
|       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 | -- | Join multi-line strings horizontally, after compressing each of | ||||||
| @ -331,16 +326,6 @@ takeWidth w (c:cs) | cw <= w   = c:takeWidth (w-cw) cs | |||||||
|                    | otherwise = "" |                    | otherwise = "" | ||||||
|   where cw = charWidth c |   where cw = charWidth c | ||||||
| 
 | 
 | ||||||
| -- from Pandoc (copyright John MacFarlane, GPL) |  | ||||||
| -- see also http://unicode.org/reports/tr11/#Description |  | ||||||
| 
 |  | ||||||
| -- | Calculate the render width of a string, considering |  | ||||||
| -- wide characters (counted as double width), and line breaks |  | ||||||
| -- (in a multi-line string, the longest line determines the |  | ||||||
| -- width). |  | ||||||
| strWidth :: String -> Int |  | ||||||
| strWidth = maximum . (0:) . map (foldr (\a b -> charWidth a + b) 0) . lines |  | ||||||
| 
 |  | ||||||
| -- | Like strWidth, but also strips ANSI escape sequences before | -- | Like strWidth, but also strips ANSI escape sequences before | ||||||
| -- calculating the width. | -- calculating the width. | ||||||
| -- | -- | ||||||
| @ -358,51 +343,3 @@ stripAnsi s = either err id $ regexReplace ansire "" s | |||||||
|  where |  where | ||||||
|    err    = error "stripAnsi: invalid replacement pattern"      -- PARTIAL, shouldn't happen |    err    = error "stripAnsi: invalid replacement pattern"      -- PARTIAL, shouldn't happen | ||||||
|    ansire = toRegex' "\ESC\\[([0-9]+;)*([0-9]+)?[ABCDHJKfmsu]"  -- PARTIAL, should succeed |    ansire = toRegex' "\ESC\\[([0-9]+;)*([0-9]+)?[ABCDHJKfmsu]"  -- PARTIAL, should succeed | ||||||
| 
 |  | ||||||
| -- | 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 |  | ||||||
| 
 |  | ||||||
|  | |||||||
| @ -69,8 +69,8 @@ import qualified Data.Text as T | |||||||
| 
 | 
 | ||||||
| -- import Hledger.Utils.Parse | -- import Hledger.Utils.Parse | ||||||
| -- import Hledger.Utils.Regex | -- import Hledger.Utils.Regex | ||||||
| import Hledger.Utils.String (charWidth) |  | ||||||
| import Hledger.Utils.Test | import Hledger.Utils.Test | ||||||
|  | import Text.WideString (charWidth, textWidth) | ||||||
| 
 | 
 | ||||||
| -- lowercase, uppercase :: String -> String | -- lowercase, uppercase :: String -> String | ||||||
| -- lowercase = map toLower | -- lowercase = map toLower | ||||||
| @ -344,63 +344,6 @@ textTakeWidth w t | not (T.null t), | |||||||
|                 = T.cons c $ textTakeWidth (w-cw) (T.tail t) |                 = T.cons c $ textTakeWidth (w-cw) (T.tail t) | ||||||
|               | otherwise = "" |               | otherwise = "" | ||||||
| 
 | 
 | ||||||
| -- -- 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 ). |  | ||||||
| textWidth :: Text -> Int |  | ||||||
| textWidth "" = 0 |  | ||||||
| textWidth s = maximum $ map (T.foldr (\a b -> charWidth a + b) 0) $ T.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 |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| -- | Read a decimal number from a Text. Assumes the input consists only of digit | -- | Read a decimal number from a Text. Assumes the input consists only of digit | ||||||
| -- characters. | -- characters. | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								tests/balance/bcexample.hledger
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								tests/balance/bcexample.hledger
									
									
									
									
									
										Symbolic link
									
								
							| @ -0,0 +1 @@ | |||||||
|  | ../../examples/bcexample.hledger | ||||||
							
								
								
									
										29
									
								
								tests/balance/bcexample.test
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tests/balance/bcexample.test
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | # Record a complicated real-life example. Layout is not perfect, but any | ||||||
|  | # changes should be noted and evaluated whether they improve things. | ||||||
|  | 
 | ||||||
|  | $ hledger -f bcexample.hledger bal -t -1 --color=always | ||||||
|  | > | ||||||
|  |              70.00 GLD         | ||||||
|  |             17.00 ITOT         | ||||||
|  | 489.957000000000 RGAGX         | ||||||
|  |            5716.53 USD         | ||||||
|  |           337.26 VACHR         | ||||||
|  | 309.950000000000 VBMPX         | ||||||
|  |              36.00 VEA         | ||||||
|  |             294.00 VHT  Assets | ||||||
|  |         [31m-3077.70 USD[m  Equity | ||||||
|  |      52000.00 IRAUSD           | ||||||
|  |        260911.70 USD  Expenses | ||||||
|  |     [31m-52000.00 IRAUSD[m         | ||||||
|  |       [31m-365071.44 USD[m         | ||||||
|  |        [31m-337.26 VACHR[m  Income | ||||||
|  |         [31m-2891.85 USD[m  Liabilities | ||||||
|  | -------------------- | ||||||
|  |              70.00 GLD | ||||||
|  |             17.00 ITOT | ||||||
|  | 489.957000000000 RGAGX | ||||||
|  |         [31m-104412.76 USD[m | ||||||
|  | 309.950000000000 VBMPX | ||||||
|  |              36.00 VEA | ||||||
|  |             294.00 VHT | ||||||
|  | >=0 | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user