summaryrefslogtreecommitdiffstats
path: root/src/Blessings/Text/WCWidth.hs
blob: 277e1a16ef633fe0b2f15d3d1a7213d3de8afdf4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE PatternGuards #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Blessings.Text.WCWidth
    ( module Blessings
    ) where

import Blessings
import Blessings.Internal
import Data.Char.WCWidth qualified as WCWidth
import Data.Text (Text)
import Data.Text qualified as Text


instance Blessable Text where
  length      = length'
  drop        = drop'
  take        = take'
  splitAt     = splitAt'
  intercalate = Text.intercalate
  fromWord8   = Text.pack . show


length' :: Text -> Int
length' = Text.foldr ((+) . wcwidth') 0

drop' :: Int -> Text -> Text
drop' k t =
    if k <= 0
      then t
      else
        case Text.uncons t of
          Just (c, t') ->
            drop' (k - wcwidth' c) t'
          Nothing -> mempty

take' :: Int -> Text -> Text
take' k0 =
    Text.pack . rec k0
  where
    rec k t =
      if | Just (c, t') <- Text.uncons t, nc <- wcwidth' c, nc <= k ->
            c : rec (k - nc) t'

         | otherwise ->
            []

splitAt' :: Int -> Text -> (Text, Text)
splitAt' k0 =
    rec k0 mempty
  where
    rec k a t =
      if | Just (c, t') <- Text.uncons t, nc <- wcwidth' c, nc <= k ->
            rec (k - nc) (c : a) t'

         | otherwise ->
            (Text.pack $ reverse a, t)

-- TODO this breaks when WCWidth.wcwidth returns -1, which happens for
-- non-printable characters like '\n'.
-- Following wcwidth' isn't entirely correct because WCWidth.wcwidth '\0' == 0
wcwidth' :: Char -> Int
wcwidth' = max 1 . WCWidth.wcwidth