summaryrefslogtreecommitdiffstats
path: root/src/Blessings/String/WCWidth.hs
blob: 16ab8535c1a29a6989940b0897dfd7e3f28011d5 (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
65
66
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE PatternGuards #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Blessings.String.WCWidth
    ( module Blessings
    ) where

import Blessings
import Blessings.Internal
import Data.Char.WCWidth qualified as WCWidth
import Data.List qualified as List


instance Blessable String where
  length      = length'
  drop        = drop'
  take        = take'
  splitAt     = splitAt'
  break       = List.break
  intercalate = List.intercalate
  fromWord8   = Prelude.show
  show        = Prelude.show


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

drop' :: Int -> String -> String
drop' k t =
    if k <= 0
      then t
      else
        case t of
          c : t' ->
            drop' (k - wcwidth' c) t'
          [] -> mempty

take' :: Int -> String -> String
take' k0 =
    rec k0
  where
    rec k t =
      if | (c : t') <- t, nc <- wcwidth' c, nc <= k ->
            c : rec (k - nc) t'

         | otherwise ->
            []

splitAt' :: Int -> String -> (String, String)
splitAt' k0 =
    rec k0 []
  where
    rec k a t =
      if | (c : t') <- t, nc <- wcwidth' c, nc <= k ->
            rec (k - nc) (c : a) t'

         | otherwise ->
            (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