From 6b767df2fc47a77a4f856e040346ce49c0e7bf00 Mon Sep 17 00:00:00 2001 From: tv Date: Fri, 25 Jan 2019 04:15:45 +0100 Subject: Add support for Text --- blessings.cabal | 16 ++++-- src/Blessings.hs | 130 +++++++++++++++++++++++++--------------------- src/Blessings/Internal.hs | 11 ++++ src/Blessings/String.hs | 18 +++++++ src/Blessings/Text.hs | 18 +++++++ 5 files changed, 129 insertions(+), 64 deletions(-) create mode 100644 src/Blessings/Internal.hs create mode 100644 src/Blessings/String.hs create mode 100644 src/Blessings/Text.hs diff --git a/blessings.cabal b/blessings.cabal index 1b13a57..e8be029 100644 --- a/blessings.cabal +++ b/blessings.cabal @@ -3,14 +3,20 @@ build-type: Simple cabal-version: >= 1.2 license: MIT name: blessings -version: 1.4.0 +version: 2.0.0 library - build-depends: base - exposed-modules: Blessings - ghc-options: -Wall + build-depends: + base, + text + exposed-modules: + Blessings, + Blessings.Internal, + Blessings.String, + Blessings.Text + ghc-options: -O2 -Wall hs-source-dirs: src source-repository head - location: http://cgit.cd.retiolum/blessings + location: https://cgit.krebsco.de/blessings type: git diff --git a/src/Blessings.hs b/src/Blessings.hs index 56c1af0..21cbabd 100644 --- a/src/Blessings.hs +++ b/src/Blessings.hs @@ -1,10 +1,17 @@ {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} -module Blessings where +module Blessings + ( module Export + , module Blessings + ) where +import qualified Prelude +import Blessings.Internal as Export (Blessable) +import qualified Blessings.Internal as Bless +import Prelude hiding (length,drop,take) import Control.Applicative -import Data.List import Data.String import Data.Ix (inRange) @@ -181,7 +188,7 @@ filterPm :: (Pm -> Maybe Int) -> Pm -> Pm filterPm f = rec [] where rec ys xs@(xhead:xtail) = maybe (rec (ys ++ [xhead]) xtail) - (rec ys . flip drop xs) + (rec ys . flip Prelude.drop xs) (f xs) rec ys _ = ys @@ -204,13 +211,14 @@ type RenderState = [(FColor, BColor, Blink, Bold, Underline)] emptyRenderState :: RenderState emptyRenderState = [(ECMA48FColor 39, ECMA48BColor 49, NoBlink, NoBold, NoUnderline)] -renderString :: RenderState -> Blessings String -> String -> String -renderString _ (Plain s) y = s ++ y +render :: (Blessable a) => RenderState -> Blessings a -> a -> a + +render _ (Plain s) y = s <> y -- TODO merge successive sequences: \ESC[32m\ESC[1m -> \ESC[31;1m -renderString rs@((fc, bc, bl, b, u):_) (SGR c t) y = - renderSGR bra ++ renderString rs' t (renderSGR ket ++ y) +render rs@((fc, bc, bl, b, u):_) (SGR c t) y = + renderSGR bra <> render rs' t (renderSGR ket <> y) where fc' = maybe fc id $ fromPm c bc' = maybe bc id $ fromPm c @@ -227,57 +235,61 @@ renderString rs@((fc, bc, bl, b, u):_) (SGR c t) y = (if b' /= b then (toPm b', toPm b) else ([],[])) : (if u' /= u then (toPm u', toPm u) else ([],[])) : [] -renderString _ (SGR _ _) _ = - error "renderString called w/o proper initial state" +render _ (SGR _ _) _ = + error "render called w/o proper initial state" -- where a proper initial state is s.th. like emptyRenderState -renderString r (Append t1 t2) y = - renderString r t1 $ renderString r t2 y - -renderString _ Empty y = y - - -len :: Blessings String -> Int -len (Plain x) = length x -len (SGR _ x) = len x -len (Append t1 t2) = len t1 + len t2 -len Empty = 0 - - -pp :: Blessings String -> String -pp t = renderString emptyRenderState t "" - - -renderSGR :: Pm -> String -renderSGR [] = [] -renderSGR xs = ("\ESC["++) . (++"m") . intercalate ";" $ map show xs - - -blessingsDrop :: Int -> Blessings String -> Blessings String -blessingsDrop n = \case - Append t1 t2 -> - case compare n (len t1) of - LT -> Append (blessingsDrop n t1) t2 - EQ -> t2 - GT -> blessingsDrop (n - len t1) t2 - Plain s -> - Plain (drop n s) - SGR pm t -> - SGR pm (blessingsDrop n t) - Empty -> - Empty - - -blessingsTake :: Int -> Blessings String -> Blessings String -blessingsTake n = \case - Append t1 t2 -> - case compare n (len t1) of - LT -> blessingsTake n t1 - EQ -> t1 - GT -> Append t1 (blessingsTake (n - len t1) t2) - Plain s -> - Plain (take n s) - SGR pm t -> - SGR pm (blessingsTake n t) - Empty -> - Empty +render r (Append t1 t2) y = + render r t1 $ render r t2 y + +render _ Empty y = y + + +renderSGR :: (Blessable a) => Pm -> a +renderSGR [] = mempty +renderSGR pm = + ("\ESC["<>) . (<>"m") . Bless.intercalate ";" . map Bless.fromInt $ pm + + +pp :: (Blessable a) => Blessings a -> a +pp t = render emptyRenderState t "" + + +instance Blessable a => Blessable (Blessings a) where + length (Plain x) = Bless.length x + length (SGR _ t) = Bless.length t + length (Append t1 t2) = Bless.length t1 + Bless.length t2 + length Empty = 0 + + drop n = \case + Append t1 t2 -> + case compare n (Bless.length t1) of + LT -> Bless.drop n t1 <> t2 + EQ -> t2 + GT -> Bless.drop (n - Bless.length t1) t2 + Plain s -> + Plain (Bless.drop n s) + SGR pm t -> + SGR pm (Bless.drop n t) + Empty -> + Empty + + take n = \case + Append t1 t2 -> + case compare n (Bless.length t1) of + LT -> Bless.take n t1 + EQ -> t1 + GT -> t1 <> Bless.take (n - Bless.length t1) t2 + Plain s -> + Plain (Bless.take n s) + SGR pm t -> + SGR pm (Bless.take n t) + Empty -> + Empty + + intercalate i = \case + [] -> mempty + [t] -> t + (t:ts) -> t <> i <> Bless.intercalate i ts + + fromInt = Plain . Bless.fromInt diff --git a/src/Blessings/Internal.hs b/src/Blessings/Internal.hs new file mode 100644 index 0000000..c96a587 --- /dev/null +++ b/src/Blessings/Internal.hs @@ -0,0 +1,11 @@ +module Blessings.Internal where + +import Data.String (IsString) + + +class (IsString a, Monoid a) => Blessable a where + length :: a -> Int + drop :: Int -> a -> a + take :: Int -> a -> a + intercalate :: a -> [a] -> a + fromInt :: Int -> a diff --git a/src/Blessings/String.hs b/src/Blessings/String.hs new file mode 100644 index 0000000..c2c7273 --- /dev/null +++ b/src/Blessings/String.hs @@ -0,0 +1,18 @@ +{-# LANGUAGE FlexibleInstances #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Blessings.String + ( module Blessings + ) where + +import Blessings +import Blessings.Internal +import qualified Data.List as L + + +instance Blessable String where + length = L.length + drop = L.drop + take = L.take + intercalate = L.intercalate + fromInt = show diff --git a/src/Blessings/Text.hs b/src/Blessings/Text.hs new file mode 100644 index 0000000..64d261b --- /dev/null +++ b/src/Blessings/Text.hs @@ -0,0 +1,18 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Blessings.Text + ( module Blessings + ) where + +import Blessings +import Blessings.Internal +import Data.Text (Text) +import qualified Data.Text as T + + +instance Blessable Text where + length = T.length + drop = T.drop + take = T.take + intercalate = T.intercalate + fromInt = T.pack . show -- cgit v1.2.3