packages feed

table-layout-1.0.0.0: src/Text/Layout/Table/Cell/WideString.hs

{-# LANGUAGE GeneralisedNewtypeDeriving #-}
{-# LANGUAGE MultiWayIf                 #-}
{-# LANGUAGE OverloadedStrings          #-}
module Text.Layout.Table.Cell.WideString
    ( WideString(..)
    , WideText(..)
    ) where

import Data.String
import qualified Data.Text as T
import Text.DocLayout

import Text.Layout.Table.Cell
import Text.Layout.Table.Primitives.AlignInfo

-- | A newtype for String in which characters can be wider than one space.
newtype WideString = WideString String
    deriving (Eq, Ord, Show, Read, Semigroup, Monoid, IsString)

instance Cell WideString where
    visibleLength (WideString s) = realLength s
    measureAlignment p (WideString s) = measureAlignmentWide p s
    buildCell (WideString s) = buildCell s
    buildCellView = buildCellViewLRHelper
      (\(WideString s) -> buildCell s)
      (\i (WideString s) -> WideString $ dropWide True i s)
      (\i (WideString s) -> WideString . reverse . dropWide False i $ reverse s)

-- | Drop characters from the left side of a 'String' until at least the
-- provided width has been removed.
--
-- The provided `Bool` determines whether to continue dropping zero-width
-- characters after the requested width has been dropped.
dropWide :: Bool -> Int -> String -> String
dropWide _ _ [] = []
dropWide gobbleZeroWidth i l@(x : xs)
    | gobbleZeroWidth && i == 0 && charLen == 0 = dropWide gobbleZeroWidth i xs
    | i <= 0       = l
    | charLen <= i = dropWide gobbleZeroWidth (i - charLen) xs
    | otherwise    = replicate (charLen - i) ' ' ++ dropWide gobbleZeroWidth 0 xs
  where
    charLen = charWidth x

measureAlignmentWide :: (Char -> Bool) -> String -> AlignInfo
measureAlignmentWide p xs = case break p xs of
    (ls, rs) -> AlignInfo (realLength ls) $ case rs of
        []      -> Nothing
        _ : rs' -> Just $ realLength rs'

-- | A newtype for Text in which characters can be wider than one space.
newtype WideText = WideText T.Text
    deriving (Eq, Ord, Show, Read, Semigroup, Monoid, IsString)

instance Cell WideText where
    visibleLength (WideText s) = realLength s
    measureAlignment p (WideText s) = measureAlignmentWideT p s
    buildCell (WideText s) = buildCell s
    buildCellView = buildCellViewLRHelper
        (\(WideText s) -> buildCell s)
        (\i (WideText s) -> WideText $ dropLeftWideT i s)
        (\i (WideText s) -> WideText $ dropRightWideT i s)

dropLeftWideT :: Int -> T.Text -> T.Text
dropLeftWideT i txt = case T.uncons txt of
    Nothing -> txt
    Just (x, xs) -> let l = charWidth x in if
        | i == 0 && l == 0 -> dropLeftWideT i xs
        | i <= 0    -> txt
        | l <= i    -> dropLeftWideT (i - l) xs
        | otherwise -> T.replicate (l - i) " " <> dropLeftWideT 0 xs

dropRightWideT :: Int -> T.Text -> T.Text
dropRightWideT i txt = case T.unsnoc txt of
    Nothing -> txt
    Just (xs, x) -> let l = charWidth x in if
        | i <= 0    -> txt
        | l <= i    -> dropRightWideT (i - l) xs
        | otherwise -> xs <> T.replicate (l - i) " "

measureAlignmentWideT :: (Char -> Bool) -> T.Text -> AlignInfo
measureAlignmentWideT p xs = case T.break p xs of
    (ls, rs) -> AlignInfo (realLength ls) $ if T.null rs
        then Nothing
        else Just . realLength $ T.drop 1 rs