@@ -1,3 +1,4 @@
1+{-# LANGUAGE BangPatterns #-}
12{-# OPTIONS_GHC -fno-warn-unused-imports #-}
23-- |
34-- Module : Data.Text.Compat
@@ -144,8 +145,41 @@ module Data.Text.Compat
144145
145146import Data.ByteString (ByteString)
146147import Data.Text hiding (split)
147−import Prelude (Char)
148+import Data.Text.Unsafe (unsafeTail)
149+import Prelude (Char, (+), otherwise)
148150
149151split :: Char -> Text -> [Text]
150152split = splitChar
151153{-# INLINE split #-}
154+
155+-- | /O(n)/ Break a string on a substring, returning a pair of the
156+-- part of the string prior to the match, and the rest of the string.
157+--
158+-- The following relationship holds:
159+--
160+-- > break (==c) l == breakSubstring (singleton c) l
161+--
162+-- For example, to tokenise a string, dropping delimiters:
163+--
164+-- > tokenise x y = h : if null t then [] else tokenise x (drop (length x) t)
165+-- > where (h,t) = breakSubstring x y
166+--
167+-- To skip to the first occurence of a string:
168+--
169+-- > snd (breakSubstring x y)
170+--
171+-- To take the parts of a string before a delimiter:
172+--
173+-- > fst (breakSubstring x y)
174+--
175+breakSubstring :: Text -- ^ String to search for
176+ -> Text -- ^ String to search in
177+ -> (Text,Text) -- ^ Head and tail of string broken at substring
178+
179+breakSubstring pat src = search 0 src
180+ where
181+ search !n !s
182+ | null s = (src,empty) -- not found
183+ | pat `isPrefixOf` s = (take n src,s)
184+ | otherwise = search (n+1) (unsafeTail s)
185+{-# INLINE breakSubstring #-}