@@ -45,7 +45,10 @@ import Data.Word
4545infixr 5 <|>
4646infixr 4 <->
4747
48−type StringSeq = Seq.Seq Char
48+-- | We pair each character with it's display length. This way we only compute the length once per
49+-- character.
50+-- * Though currently the width of some strings is still compute multiple times.
51+type DisplayString = Seq.Seq (Char, Word)
4952
5053-- | An image in VTY defines:
5154--
@@ -69,8 +72,7 @@ data Image =
6972 HorizText
7073 { attr :: !Attr
7174 -- All character data is stored as Char sequences with the ISO-10646 encoding.
72− , text :: StringSeq
73− -- in columns
75+ , text :: DisplayString
7476 , output_width :: !Word -- >= 0
7577 , char_width :: !Word -- >= 1
7678 }
@@ -113,7 +115,7 @@ data Image =
113115
114116instance Show Image where
115117 show ( HorizText { output_width = ow, text = txt } )
116− = "HorizText [" ++ show ow ++ "] (" ++ show txt ++ ")"
118+ = "HorizText [" ++ show ow ++ "] (" ++ show (fmap fst txt) ++ ")"
117119 show ( BGFill { output_width = c, output_height = r } )
118120 = "BGFill (" ++ show c ++ "," ++ show r ++ ")"
119121 show ( HorizJoin { part_left = l, part_right = r, output_width = c } )
@@ -128,14 +130,13 @@ instance Show Image where
128130 = "ImagePad " ++ show size ++ " ( " ++ show i ++ " )"
129131 show ( EmptyImage ) = "EmptyImage"
130132
131−-- | Currently append in the Monoid instance is equivalent to <->. Future versions will just stack
132−-- the images.
133+-- | Currently append in the Monoid instance is equivalent to <->.
133134instance Monoid Image where
134135 mempty = empty_image
135136 mappend = (<->)
136137
137138-- A horizontal text image of 0 characters in width simplifies to the EmptyImage
138−horiz_text :: Attr -> StringSeq -> Word -> Image
139+horiz_text :: Attr -> DisplayString -> Word -> Image
139140horiz_text a txt ow
140141 | ow == 0 = EmptyImage
141142 | otherwise = HorizText a txt ow (toEnum $ Seq.length txt)
@@ -288,7 +289,9 @@ vert_cat = foldr (<->) EmptyImage
288289-- | an image of a single character. This is a standard Haskell 31-bit character assumed to be in
289290-- the ISO-10646 encoding.
290291char :: Attr -> Char -> Image
291−char !a !c = HorizText a (Seq.singleton c) (safe_wcwidth c) 1
292+char !a !c =
293+ let display_width = safe_wcwidth c
294+ in HorizText a (Seq.singleton (c, display_width)) display_width 1
292295
293296-- | A string of characters layed out on a single row with the same display attribute. The string is
294297-- assumed to be a sequence of ISO-10646 characters.
@@ -300,7 +303,9 @@ char !a !c = HorizText a (Seq.singleton c) (safe_wcwidth c) 1
300303-- directly to iso_10646_string or string.
301304--
302305iso_10646_string :: Attr -> String -> Image
303−iso_10646_string !a !str = horiz_text a (Seq.fromList str) (safe_wcswidth str)
306+iso_10646_string !a !str =
307+ let display_text = Seq.fromList $ map (\c -> (c, safe_wcwidth c)) str
308+ in horiz_text a display_text (safe_wcswidth str)
304309
305310-- | Alias for iso_10646_string. Since the usual case is that a literal string like "foo" is
306311-- represented internally as a list of ISO 10646 31 bit characters.
@@ -316,14 +321,16 @@ string = iso_10646_string
316321utf8_string :: Attr -> [Word8] -> Image
317322utf8_string !a !str = string a ( decode str )
318323
324+-- | Returns the display width of a character. Assumes all characters with unknown widths are 0 width
319325safe_wcwidth :: Char -> Word
320326safe_wcwidth c = case wcwidth c of
321− i | i < 0 -> 0 -- error "negative wcwidth"
327+ i | i < 0 -> 0
322328 | otherwise -> toEnum i
323329
330+-- | Returns the display width of a string. Assumes all characters with unknown widths are 0 width
324331safe_wcswidth :: String -> Word
325332safe_wcswidth str = case wcswidth str of
326− i | i < 0 -> 0 -- error "negative wcswidth"
333+ i | i < 0 -> 0
327334 | otherwise -> toEnum i
328335
329336-- | Renders a UTF-8 encoded bytestring.