@@ -1,4 +1,5 @@
11{-# OPTIONS_GHC -fno-warn-orphans #-}
2+{-# LANGUAGE BangPatterns #-}
23-- |
34-- Module : Data.Text.Lazy
45-- Copyright : (c) Bryan O'Sullivan 2009
@@ -57,6 +58,7 @@ module Data.Text.Lazy
5758 , intersperse
5859 , transpose
5960 , reverse
61+ , replace
6062
6163 -- ** Case conversion
6264 -- $case
@@ -109,6 +111,11 @@ module Data.Text.Lazy
109111 , drop
110112 , takeWhile
111113 , dropWhile
114+ , dropWhileEnd
115+ , dropAround
116+ , strip
117+ , stripStart
118+ , stripEnd
112119 , splitAt
113120 , span
114121 , break
@@ -118,7 +125,10 @@ module Data.Text.Lazy
118125 , tails
119126
120127 -- ** Breaking into many substrings
128+ -- $split
121129 , split
130+ , splitTimes
131+ , splitTimesEnd
122132 , splitWith
123133 , chunksOf
124134 -- , breakSubstring
@@ -160,7 +170,7 @@ module Data.Text.Lazy
160170
161171import Prelude (Char, Bool(..), Int, Maybe(..), String,
162172 Eq(..), Ord(..), Read(..), Show(..),
163− (&&), (+), (-), (.), ($), (++),
173+ (&&), (||), (+), (-), (.), ($), (++),
164174 div, flip, fromIntegral, not, otherwise)
165175import qualified Prelude as P
166176import Data.Int (Int64)
@@ -430,6 +440,14 @@ reverse = rev Empty
430440 where rev a Empty = a
431441 rev a (Chunk t ts) = rev (Chunk (T.reverse t) a) ts
432442
443+-- | /O(m)*O(n)/ Replace every occurrence of one substring with another.
444+replace :: Text -- ^ Text to search for
445+ -> Text -- ^ Replacement text
446+ -> Text -- ^ Input text
447+ -> Text
448+replace s d = intercalate d . split s
449+{-# INLINE replace #-}
450+
433451-- ----------------------------------------------------------------------------
434452-- ** Case conversions (folds)
435453
@@ -662,7 +680,7 @@ take i t0 = take' i t0
662680-- | /O(n)/ 'drop' @n@, applied to a 'Text', returns the suffix of the
663681-- 'Text' of length @n@, or the empty 'Text' if @n@ is greater than the
664682-- length of the 'Text'. Subject to fusion.
665−drop :: Int -> Text -> Text
683+drop :: Int64 -> Text -> Text
666684drop i t0
667685 | i <= 0 = t0
668686 | otherwise = drop' i t0
@@ -718,6 +736,52 @@ dropWhile p t0 = dropWhile' t0
718736"LAZY TEXT dropWhile -> unfused" [1] forall p t.
719737 unstream (S.dropWhile p (stream t)) = dropWhile p t
720738 #-}
739+-- | /O(n)/ 'dropWhileEnd' @p@ @t@ returns the prefix remaining after
740+-- dropping characters that fail the predicate @p@ from the end of
741+-- @t@.
742+-- Examples:
743+--
744+-- > dropWhileEnd (=='.') "foo..." == "foo"
745+dropWhileEnd :: (Char -> Bool) -> Text -> Text
746+dropWhileEnd p = go
747+ where go Empty = Empty
748+ go (Chunk t Empty) = if T.null t'
749+ then Empty
750+ else Chunk t' Empty
751+ where t' = T.dropWhileEnd p t
752+ go (Chunk t ts) = case go ts of
753+ Empty -> go (Chunk t Empty)
754+ ts' -> Chunk t ts'
755+{-# INLINE dropWhileEnd #-}
756+
757+-- | /O(n)/ 'dropAround' @p@ @t@ returns the substring remaining after
758+-- dropping characters that fail the predicate @p@ from both the
759+-- beginning and end of @t@. Subject to fusion.
760+dropAround :: (Char -> Bool) -> Text -> Text
761+dropAround p = dropWhile p . dropWhileEnd p
762+{-# INLINE [1] dropAround #-}
763+
764+-- | /O(n)/ Remove leading white space from a string. Equivalent to:
765+--
766+-- > dropWhile isSpace
767+stripStart :: Text -> Text
768+stripStart = dropWhile isSpace
769+{-# INLINE [1] stripStart #-}
770+
771+-- | /O(n)/ Remove trailing white space from a string. Equivalent to:
772+--
773+-- > dropWhileEnd isSpace
774+stripEnd :: Text -> Text
775+stripEnd = dropWhileEnd isSpace
776+{-# INLINE [1] stripEnd #-}
777+
778+-- | /O(n)/ Remove leading and trailing white space from a string.
779+-- Equivalent to:
780+--
781+-- > dropAround isSpace
782+strip :: Text -> Text
783+strip = dropAround isSpace
784+{-# INLINE [1] strip #-}
721785
722786-- | /O(n)/ 'splitAt' @n t@ returns a pair whose first element is a
723787-- prefix of @t@ of length @n@, and whose second is the remainder of
@@ -791,24 +855,93 @@ tails ts@(Chunk t ts')
791855 | T.length t == 1 = ts : tails ts'
792856 | otherwise = ts : tails (Chunk (T.unsafeTail t) ts')
793857
794−-- | /O(n)/ Break a 'Text' into pieces separated by the byte
795−-- argument, consuming the delimiter. I.e.
858+-- $split
859+--
860+-- Splitting functions in this library do not perform character-wise
861+-- copies to create substrings; they just construct new 'Text's that
862+-- are slices of the original.
863+
864+-- | /O(m)*O(n)/ Break a 'Text' into pieces separated by the first
865+-- 'Text' argument, consuming the delimiter. Examples:
796866--
797−-- > split '\n' "a\nb\nd\ne" == ["a","b","d","e"]
798−-- > split 'a' "aXaXaXa" == ["","X","X","X",""]
799−-- > split 'x' "x" == ["",""]
867+-- > split "\r\n" "a\r\nb\r\nd\r\ne" == ["a","b","d","e"]
868+-- > split "aaa" "aaaXaaaXaaaXaaa" == ["","X","X","X",""]
869+-- > split "x" "x" == ["",""]
800870--
801871-- and
802872--
803−-- > intercalate [c] . split c == id
804−-- > split == splitWith . (==)
805−--
806−-- As for all splitting functions in this library, this function does
807−-- not copy the substrings, it just constructs new 'Text's that are
808−-- slices of the original.
809−split :: Char -> Text -> [Text]
810−split c = splitWith (==c)
811−{-# INLINE split #-}
873+-- > intercalate s . split s == id
874+-- > split (singleton c) == splitWith (==c)
875+split :: Text -- ^ Text to split on
876+ -> Text -- ^ Input text
877+ -> [Text]
878+split pat src0
879+ | l == 0 = [src0]
880+ | l == 1 = splitWith (== (head pat)) src0
881+ | otherwise = go src0
882+ where
883+ l = length pat
884+ go src = search 0 src
885+ where
886+ search !n !s
887+ | null s = [src] -- not found
888+ | pat `isPrefixOf` s = take n src : go (drop l s)
889+ | otherwise = search (n+1) (tail s)
890+{-# INLINE [1] split #-}
891+
892+{-# RULES
893+"LAZY TEXT split/singleton -> splitWith/==" [~1] forall c t.
894+ split (singleton c) t = splitWith (==c) t
895+ #-}
896+
897+-- | /O(m)*O(n)/ Break a 'Text' into pieces at most @k@ times,
898+-- treating the first 'Text' argument as the delimiter to break on,
899+-- and consuming the delimiter. The last element of the list contains
900+-- the remaining text after the number of times to split has been
901+-- reached. A value of zero or less for @k@ causes no splitting to
902+-- occur.
903+--
904+-- Examples:
905+--
906+-- > splitTimes 0 "//" "a//b//c" == ["a//b//c"]
907+-- > splitTimes 2 ":" "a:b:c:d:e" == ["a","b","c:d:e"]
908+-- > splitTimes 100 "???" "a????b" == ["a","?b"]
909+--
910+-- and
911+--
912+-- > intercalate s . splitTimes k s == id
913+splitTimes :: Int64 -- ^ Maximum number of times to split
914+ -> Text -- ^ Text to split on
915+ -> Text -- ^ Input text
916+ -> [Text]
917+splitTimes k pat src0
918+ | k <= 0 || l == 0 = [src0]
919+ | otherwise = go k src0
920+ where
921+ l = length pat
922+ go !i src = search 0 src
923+ where
924+ search !n !s
925+ | i == 0 || null s = [src] -- not found or limit reached
926+ | pat `isPrefixOf` s = take n src : go (i-1) (drop l s)
927+ | otherwise = search (n+1) (tail s)
928+{-# INLINE splitTimes #-}
929+
930+-- | /O(m)*O(n)/ Break a 'Text' into pieces at most @k@ times, like
931+-- 'splitTimes', but start from the end of the input and work towards
932+-- the start.
933+--
934+-- Examples:
935+--
936+-- > splitTimes 2 "::" "a::b::c::d::e" == ["a","b","c::d::e"]
937+-- > splitTimesEnd 2 "::" "a::b::c::d::e" == ["a::b::c","d","e"]
938+splitTimesEnd :: Int64 -- ^ Maximum number of times to split
939+ -> Text -- ^ Text to split on
940+ -> Text -- ^ Input text
941+ -> [Text]
942+splitTimesEnd k pat src =
943+ L.reverse . L.map reverse $ splitTimes k (reverse pat) (reverse src)
944+{-# INLINE splitTimesEnd #-}
812945
813946-- | /O(n)/ Splits a 'Text' into components delimited by separators,
814947-- where the predicate returns True for a separator element. The