@@ -68,6 +68,7 @@ module Text.Pandoc.Pretty (
6868 , quotes
6969 , doubleQuotes
7070 , charWidth
71+ , realLength
7172 )
7273
7374where
@@ -195,7 +196,7 @@ outp off s | off <= 0 = do
195196 when (column st' == 0 && usePrefix st' && not (null rawpref)) $ do
196197 let pref = reverse $ dropWhile isSpace $ reverse rawpref
197198 modify $ \st -> st{ output = fromString pref : output st
198− , column = column st + length pref }
199+ , column = column st + realLength pref }
199200 when (off < 0) $ do
200201 modify $ \st -> st { output = fromString s : output st
201202 , column = 0
@@ -205,7 +206,7 @@ outp off s = do
205206 let pref = prefix st'
206207 when (column st' == 0 && usePrefix st' && not (null pref)) $ do
207208 modify $ \st -> st{ output = fromString pref : output st
208− , column = column st + length pref }
209+ , column = column st + realLength pref }
209210 modify $ \st -> st{ output = fromString s : output st
210211 , column = column st + off
211212 , newlines = 0 }
@@ -312,7 +313,7 @@ renderList (b1@Block{} : BreakingSpace : b2@Block{} : xs) =
312313renderList (Block width lns : xs) = do
313314 st <- get
314315 let oldPref = prefix st
315− case column st - length oldPref of
316+ case column st - realLength oldPref of
316317 n | n > 0 -> modify $ \s -> s{ prefix = oldPref ++ replicate n ' ' }
317318 _ -> return ()
318319 renderDoc $ blockToDoc width lns
@@ -324,7 +325,7 @@ mergeBlocks addSpace (Block w1 lns1) (Block w2 lns2) =
324325 Block (w1 + w2 + if addSpace then 1 else 0) $
325326 zipWith (\l1 l2 -> pad w1 l1 ++ l2) (lns1 ++ empties) (map sp lns2 ++ empties)
326327 where empties = replicate (abs $ length lns1 - length lns2) ""
327− pad n s = s ++ replicate (n - length s) ' '
328+ pad n s = s ++ replicate (n - realLength s) ' '
328329 sp "" = ""
329330 sp xs = if addSpace then (' ' : xs) else xs
330331mergeBlocks _ _ _ = error "mergeBlocks tried on non-Block!"
@@ -345,9 +346,9 @@ text = Doc . toChunks
345346 toChunks [] = mempty
346347 toChunks s = case break (=='\n') s of
347348 ([], _:ys) -> NewLine `cons` toChunks ys
348− (xs, _:ys) -> Text (length xs) xs `cons`
349+ (xs, _:ys) -> Text (realLength xs) xs `cons`
349350 NewLine `cons` toChunks ys
350− (xs, []) -> singleton $ Text (length xs) xs
351+ (xs, []) -> singleton $ Text (realLength xs) xs
351352
352353-- | A character.
353354char :: Char -> Doc
@@ -401,7 +402,7 @@ nowrap doc = Doc $ fromList $ map replaceSpace $ toList $ unDoc doc
401402
402403-- | Returns the width of a 'Doc'.
403404offset :: Doc -> Int
404−offset d = case map length . lines . render Nothing $ d of
405+offset d = case map realLength . lines . render Nothing $ d of
405406 [] -> 0
406407 os -> maximum os
407408
@@ -416,11 +417,11 @@ lblock = block id
416417
417418-- | Like 'lblock' but aligned to the right.
418419rblock :: Int -> Doc -> Doc
419−rblock w = block (\s -> replicate (w - length s) ' ' ++ s) w
420+rblock w = block (\s -> replicate (w - realLength s) ' ' ++ s) w
420421
421422-- | Like 'lblock' but aligned centered.
422423cblock :: Int -> Doc -> Doc
423−cblock w = block (\s -> replicate ((w - length s) `div` 2) ' ' ++ s) w
424+cblock w = block (\s -> replicate ((w - realLength s) `div` 2) ' ' ++ s) w
424425
425426-- | Returns the height of a block or other 'Doc'.
426427height :: Doc -> Int
@@ -435,7 +436,7 @@ chop n cs = case break (=='\n') cs of
435436 (_:[]) -> [xs, ""]
436437 (_:zs) -> xs : chop n zs
437438 else take n xs : chop n (drop n xs ++ ys)
438− where len = length xs
439+ where len = realLength xs
439440
440441-- | Encloses a 'Doc' inside a start and end 'Doc'.
441442inside :: Doc -> Doc -> Doc -> Doc
@@ -498,3 +499,8 @@ charWidth c =
498499 | c >= '\xFE68' && c <= '\xFE6B' -> 2
499500 | c >= '\x2F800' && c <= '\x2FA1D' -> 2
500501 | otherwise -> 1
502+
503+-- | Get real length of string, taking into account combining and double-wide
504+-- characters.
505+realLength :: String -> Int
506+realLength = sum . map charWidth