haskeline-0.6.3: System/Console/Haskeline/Backend/WCWidth.hs
module System.Console.Haskeline.Backend.WCWidth(
gsWidth,
splitAtWidth,
takeWidth,
) where
-- Certain characters are "wide", i.e. take up two spaces in the terminal.
-- This module wraps the necessary foreign routines, and also provides some convenience
-- functions for width-breaking code.
import System.Console.Haskeline.LineState
import Data.List
import Foreign.C.Types
foreign import ccall unsafe mk_wcwidth :: CWchar -> Int
wcwidth :: Char -> Int
wcwidth c = case mk_wcwidth $ toEnum $ fromEnum c of
-1 -> 0 -- Control characters have zero width. (Used by the
-- "\SOH...\STX" hack in LineState.stringToGraphemes.)
w -> w
gWidth :: Grapheme -> Int
gWidth g = wcwidth (baseChar g)
gsWidth :: [Grapheme] -> Int
gsWidth = foldl' (+) 0 . map gWidth
-- | Split off the maximal list which is no more than the given width.
-- returns the width of that list.
splitAtWidth :: Int -> [Grapheme] -> ([Grapheme],[Grapheme],Int)
splitAtWidth n xs = case splitAtWidth' n xs of
(this,rest,remaining) -> (this,rest,n-remaining)
-- Returns the amount of unused space in the line.
splitAtWidth' :: Int -> [Grapheme] -> ([Grapheme],[Grapheme],Int)
splitAtWidth' w [] = ([],[],w)
splitAtWidth' w (g:gs)
| gw > w = ([],g:gs,w)
| otherwise = (g:gs',gs'',r)
where
gw = gWidth g
(gs',gs'',r) = splitAtWidth' (w-gw) gs
-- Returns the longest prefix less than or equal to the given width
-- plus the width of that list.
takeWidth :: Int -> [Grapheme] -> ([Grapheme],Int)
takeWidth n gs = case splitAtWidth n gs of
(gs',_,len) -> (gs',len)