rope-utf16-splay 0.2.0.0 → 0.3.1.0
raw patch · 8 files changed
+215/−50 lines, 8 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Data.Rope.UTF16.Internal: instance Data.Semigroup.Semigroup Data.Rope.UTF16.Internal.Chunk
- Data.Rope.UTF16.Internal: instance Data.Semigroup.Semigroup Data.Rope.UTF16.Internal.Rope
- Data.Rope.UTF16.Internal: instance Data.SplayTree.Measured Data.Rope.UTF16.Position.Position Data.Rope.UTF16.Internal.Chunk
- Data.Rope.UTF16.Internal: instance Data.SplayTree.Measured Data.Rope.UTF16.Position.Position Data.Rope.UTF16.Internal.Rope
- Data.Rope.UTF16.Position: Position :: !Int -> !RowColumn -> Position
- Data.Rope.UTF16.Position: RowColumn :: !Int -> !Int -> RowColumn
- Data.Rope.UTF16.Position: [codeUnits] :: Position -> !Int
- Data.Rope.UTF16.Position: [column] :: RowColumn -> !Int
- Data.Rope.UTF16.Position: [rowColumn] :: Position -> !RowColumn
- Data.Rope.UTF16.Position: [row] :: RowColumn -> !Int
- Data.Rope.UTF16.Position: data Position
- Data.Rope.UTF16.Position: data RowColumn
- Data.Rope.UTF16.Position: instance Data.Semigroup.Semigroup Data.Rope.UTF16.Position.Position
- Data.Rope.UTF16.Position: instance Data.Semigroup.Semigroup Data.Rope.UTF16.Position.RowColumn
- Data.Rope.UTF16.Position: instance GHC.Base.Monoid Data.Rope.UTF16.Position.Position
- Data.Rope.UTF16.Position: instance GHC.Base.Monoid Data.Rope.UTF16.Position.RowColumn
- Data.Rope.UTF16.Position: instance GHC.Classes.Eq Data.Rope.UTF16.Position.Position
- Data.Rope.UTF16.Position: instance GHC.Classes.Eq Data.Rope.UTF16.Position.RowColumn
- Data.Rope.UTF16.Position: instance GHC.Classes.Ord Data.Rope.UTF16.Position.Position
- Data.Rope.UTF16.Position: instance GHC.Classes.Ord Data.Rope.UTF16.Position.RowColumn
- Data.Rope.UTF16.Position: instance GHC.Show.Show Data.Rope.UTF16.Position.Position
- Data.Rope.UTF16.Position: instance GHC.Show.Show Data.Rope.UTF16.Position.RowColumn
- Data.Rope.UTF16.Position: lineStart :: Position -> Position
- Data.SplayTree: instance Data.SplayTree.Measured v a => Data.Semigroup.Semigroup (Data.SplayTree.SplayTree v a)
+ Data.Rope.UTF16: RowColumn :: !Int -> !Int -> RowColumn
+ Data.Rope.UTF16: [column] :: RowColumn -> !Int
+ Data.Rope.UTF16: [row] :: RowColumn -> !Int
+ Data.Rope.UTF16: all :: (Char -> Bool) -> Rope -> Bool
+ Data.Rope.UTF16: any :: (Char -> Bool) -> Rope -> Bool
+ Data.Rope.UTF16: columns :: Rope -> Int
+ Data.Rope.UTF16: data RowColumn
+ Data.Rope.UTF16: foldl :: (a -> Char -> a) -> a -> Rope -> a
+ Data.Rope.UTF16: foldl' :: (a -> Char -> a) -> a -> Rope -> a
+ Data.Rope.UTF16: foldr :: (Char -> a -> a) -> a -> Rope -> a
+ Data.Rope.UTF16: intercalate :: Rope -> [Rope] -> Rope
+ Data.Rope.UTF16: map :: (Char -> Char) -> Rope -> Rope
+ Data.Rope.UTF16: rows :: Rope -> Int
+ Data.Rope.UTF16: splitAtLine :: Int -> Rope -> (Rope, Rope)
+ Data.Rope.UTF16.Internal: all :: (Char -> Bool) -> Rope -> Bool
+ Data.Rope.UTF16.Internal: any :: (Char -> Bool) -> Rope -> Bool
+ Data.Rope.UTF16.Internal: columns :: Rope -> Int
+ Data.Rope.UTF16.Internal: foldl :: (a -> Char -> a) -> a -> Rope -> a
+ Data.Rope.UTF16.Internal: foldl' :: (a -> Char -> a) -> a -> Rope -> a
+ Data.Rope.UTF16.Internal: foldr :: (Char -> a -> a) -> a -> Rope -> a
+ Data.Rope.UTF16.Internal: instance Data.SplayTree.Measured Data.Rope.UTF16.Internal.Position.Position Data.Rope.UTF16.Internal.Chunk
+ Data.Rope.UTF16.Internal: instance Data.SplayTree.Measured Data.Rope.UTF16.Internal.Position.Position Data.Rope.UTF16.Internal.Rope
+ Data.Rope.UTF16.Internal: instance GHC.Base.Semigroup Data.Rope.UTF16.Internal.Chunk
+ Data.Rope.UTF16.Internal: instance GHC.Base.Semigroup Data.Rope.UTF16.Internal.Rope
+ Data.Rope.UTF16.Internal: intercalate :: Rope -> [Rope] -> Rope
+ Data.Rope.UTF16.Internal: map :: (Char -> Char) -> Rope -> Rope
+ Data.Rope.UTF16.Internal: rows :: Rope -> Int
+ Data.Rope.UTF16.Internal: splitAtLine :: Int -> Rope -> (Rope, Rope)
+ Data.Rope.UTF16.Internal.Position: Position :: !Int -> !RowColumn -> Position
+ Data.Rope.UTF16.Internal.Position: RowColumn :: !Int -> !Int -> RowColumn
+ Data.Rope.UTF16.Internal.Position: [codeUnits] :: Position -> !Int
+ Data.Rope.UTF16.Internal.Position: [column] :: RowColumn -> !Int
+ Data.Rope.UTF16.Internal.Position: [rowColumn] :: Position -> !RowColumn
+ Data.Rope.UTF16.Internal.Position: [row] :: RowColumn -> !Int
+ Data.Rope.UTF16.Internal.Position: data Position
+ Data.Rope.UTF16.Internal.Position: data RowColumn
+ Data.Rope.UTF16.Internal.Position: instance GHC.Base.Monoid Data.Rope.UTF16.Internal.Position.Position
+ Data.Rope.UTF16.Internal.Position: instance GHC.Base.Monoid Data.Rope.UTF16.Internal.Position.RowColumn
+ Data.Rope.UTF16.Internal.Position: instance GHC.Base.Semigroup Data.Rope.UTF16.Internal.Position.Position
+ Data.Rope.UTF16.Internal.Position: instance GHC.Base.Semigroup Data.Rope.UTF16.Internal.Position.RowColumn
+ Data.Rope.UTF16.Internal.Position: instance GHC.Classes.Eq Data.Rope.UTF16.Internal.Position.Position
+ Data.Rope.UTF16.Internal.Position: instance GHC.Classes.Eq Data.Rope.UTF16.Internal.Position.RowColumn
+ Data.Rope.UTF16.Internal.Position: instance GHC.Classes.Ord Data.Rope.UTF16.Internal.Position.Position
+ Data.Rope.UTF16.Internal.Position: instance GHC.Classes.Ord Data.Rope.UTF16.Internal.Position.RowColumn
+ Data.Rope.UTF16.Internal.Position: instance GHC.Show.Show Data.Rope.UTF16.Internal.Position.Position
+ Data.Rope.UTF16.Internal.Position: instance GHC.Show.Show Data.Rope.UTF16.Internal.Position.RowColumn
+ Data.SplayTree: instance Data.SplayTree.Measured v a => GHC.Base.Semigroup (Data.SplayTree.SplayTree v a)
- Data.SplayTree: Fork :: (SplayTree v a) -> a -> (SplayTree v a) -> !v -> SplayTree v a
+ Data.SplayTree: Fork :: SplayTree v a -> a -> SplayTree v a -> !v -> SplayTree v a
- Data.SplayTree: Inside :: (SplayTree v a) -> a -> (SplayTree v a) -> SplitResult v a
+ Data.SplayTree: Inside :: SplayTree v a -> a -> SplayTree v a -> SplitResult v a
Files
- CHANGELOG.md +11/−0
- README.md +2/−1
- rope-utf16-splay.cabal +2/−2
- src/Data/Rope/UTF16.hs +21/−5
- src/Data/Rope/UTF16/Internal.hs +88/−8
- src/Data/Rope/UTF16/Internal/Position.hs +31/−0
- src/Data/Rope/UTF16/Position.hs +0/−34
- tests/Main.hs +60/−0
CHANGELOG.md view
@@ -1,3 +1,14 @@+# 0.3.1.0++- Add `splitAtLine` function++# 0.3.0.0++- Add `rows` and `columns` functions+- Make `Position` module internal+- Add `map` and `intersperse` functions+- Add `foldl`, `foldl'`, `foldr`, `any`, and `all` functions+ # 0.2.0.0 - Rename rowColumnCodePoints to rowColumnCodeUnits
README.md view
@@ -1,4 +1,5 @@-# rope-utf16-splay [](https://travis-ci.org/ollef/rope-utf16-splay)+# rope-utf16-splay [](https://travis-ci.org/ollef/rope-utf16-splay) [](https://hackage.haskell.org/package/rope-utf16-splay)+ Thick strings optimised for indexing and updating using UTF-16 code units and row/column pairs.
rope-utf16-splay.cabal view
@@ -1,5 +1,5 @@ name: rope-utf16-splay-version: 0.2.0.0+version: 0.3.1.0 synopsis: Ropes optimised for updating using UTF-16 code units and row/column pairs. description: Ropes optimised for updating using UTF-16 code units and@@ -29,8 +29,8 @@ exposed-modules: Data.Rope.UTF16 Data.Rope.UTF16.Internal+ Data.Rope.UTF16.Internal.Position Data.Rope.UTF16.Internal.Text- Data.Rope.UTF16.Position Data.SplayTree build-depends: base >= 4.9 && < 5,
src/Data/Rope/UTF16.hs view
@@ -1,34 +1,50 @@ module Data.Rope.UTF16- ( module Position- , Rope.Rope+ ( Rope.Rope -- * Queries , Rope.null+ , Rope.length+ , Rope.rows+ , Rope.columns - -- * Conversions to and from 'Text' and 'String'+ -- * Conversions , Rope.toText , Rope.toLazyText , Rope.fromText , Rope.toString + -- * Transformations+ , Rope.map+ , Rope.intercalate+ -- * Chunking , Rope.toChunks , Rope.unconsChunk , Rope.unsnocChunk -- * UTF-16 code unit indexing- , Rope.length , Rope.splitAt , Rope.take , Rope.drop+ , Position.RowColumn(..) , Rope.rowColumnCodeUnits+ , Rope.splitAtLine -- * Breaking by predicate , Rope.span , Rope.break , Rope.takeWhile , Rope.dropWhile++ -- * Folds+ , Rope.foldl+ , Rope.foldl'+ , Rope.foldr++ -- * Special folds+ , Rope.any+ , Rope.all ) where -import Data.Rope.UTF16.Position as Position import Data.Rope.UTF16.Internal as Rope+import Data.Rope.UTF16.Internal.Position as Position
src/Data/Rope/UTF16/Internal.hs view
@@ -3,8 +3,9 @@ {-# language MultiParamTypeClasses #-} module Data.Rope.UTF16.Internal where -import Data.Foldable+import Data.Foldable as Foldable import Data.Function+import Data.List import Data.Semigroup import Data.String import Data.Text(Text)@@ -12,8 +13,8 @@ import qualified Data.Text.Lazy as Lazy import qualified Data.Text.Unsafe as Unsafe +import Data.Rope.UTF16.Internal.Position import Data.Rope.UTF16.Internal.Text-import Data.Rope.UTF16.Position import Data.SplayTree(SplayTree) import qualified Data.SplayTree as SplayTree @@ -83,8 +84,25 @@ null :: Rope -> Bool null (Rope r) = SplayTree.null r +-- | Length in code units (not characters)+length :: Rope -> Int+length = codeUnits . SplayTree.measure++-- | The number of newlines in the rope+--+-- @since 0.3.0.0+rows :: Rope -> Int+rows (Rope r) = row $ rowColumn $ SplayTree.measure r++-- | The number of code units (not characters) since the last newline or the+-- start of the rope+--+-- @since 0.3.0.0+columns :: Rope -> Int+columns (Rope r) = column $ rowColumn $ SplayTree.measure r+ ---------------------------------------------------------------------------------- * Conversions to and from 'Text' and 'String'+-- * Conversions toText :: Rope -> Text toText = Text.concat . toChunks@@ -111,9 +129,24 @@ | otherwise = Rope $ SplayTree.singleton $ chunk t toString :: Rope -> String-toString = concatMap Text.unpack . toChunks+toString = Foldable.concatMap Text.unpack . toChunks -------------------------------------------------------------------------------+-- * Transformations++-- | Map over the characters of a rope+--+-- @since 0.3.0.0+map :: (Char -> Char) -> Rope -> Rope+map f (Rope r) = Rope $ SplayTree.map (chunk . Text.map f . chunkText) r++-- | Concatenate the interspersion of a rope between the elements of a list of ropes+--+-- @since 0.3.0.0+intercalate :: Rope -> [Rope] -> Rope+intercalate r rs = mconcat $ intersperse r rs++------------------------------------------------------------------------------- -- * Chunking -- | The raw 'Text' data that the 'Rope' is built from@@ -135,10 +168,6 @@ ------------------------------------------------------------------------------- -- * UTF-16 code unit indexing --- | Length in code units (not characters)-length :: Rope -> Int-length = codeUnits . SplayTree.measure- -- | Split the rope at the nth code unit (not character) splitAt :: Int -> Rope -> (Rope, Rope) splitAt n (Rope r) = case SplayTree.split ((> n) . codeUnits) r of@@ -178,8 +207,20 @@ Unsafe.Iter _ delta -> go (i + delta) (v' <> RowColumn 0 delta) -------------------------------------------------------------------------------+-- * Lines++-- | Split the rope immediately after the i:th newline+--+-- @since 0.3.1.0+splitAtLine :: Int -> Rope -> (Rope, Rope)+splitAtLine r rope = Data.Rope.UTF16.Internal.splitAt i rope+ where+ i = rowColumnCodeUnits (RowColumn r 0) rope++------------------------------------------------------------------------------- -- * Breaking by predicate +-- | @span f r = (takeWhile f r, dropWhile f r)@ span :: (Char -> Bool) -> Rope -> (Rope, Rope) span f (Rope r) = case SplayTree.uncons r of Nothing -> (mempty, mempty)@@ -190,11 +231,50 @@ (pret, postt) = Text.span f $ chunkText t (pre', post') = Data.Rope.UTF16.Internal.span f $ Rope r' +-- | @break f = span (not . f)@ break :: (Char -> Bool) -> Rope -> (Rope, Rope) break f = Data.Rope.UTF16.Internal.span (not . f) +-- | @takeWhile f = fst . span f@ takeWhile :: (Char -> Bool) -> Rope -> Rope takeWhile f = fst . Data.Rope.UTF16.Internal.span f +-- | @dropWhile f = snd . span f@ dropWhile :: (Char -> Bool) -> Rope -> Rope dropWhile f = snd . Data.Rope.UTF16.Internal.span f++-------------------------------------------------------------------------------+-- * Folds++-- | Fold left+--+-- @since 0.3.0.0+foldl :: (a -> Char -> a) -> a -> Rope -> a+foldl f a (Rope r) = Foldable.foldl (\a' c -> Text.foldl f a' $ chunkText c) a r++-- | A strict version of 'foldl'+--+-- @since 0.3.0.0+foldl' :: (a -> Char -> a) -> a -> Rope -> a+foldl' f a (Rope r) = Foldable.foldl' (\a' c -> Text.foldl' f a' $ chunkText c) a r++-- | Fold right+--+-- @since 0.3.0.0+foldr :: (Char -> a -> a) -> a -> Rope -> a+foldr f a (Rope r) = Foldable.foldr (\c a' -> Text.foldr f a' $ chunkText c) a r++-------------------------------------------------------------------------------+-- * Special folds++-- | Do any characters in the rope satisfy the predicate?+--+-- @since 0.3.0.0+any :: (Char -> Bool) -> Rope -> Bool+any p (Rope r) = getAny $ foldMap (Any . Text.any p . chunkText) r++-- | Do all characters in the rope satisfy the predicate?+--+-- @since 0.3.0.0+all :: (Char -> Bool) -> Rope -> Bool+all p (Rope r) = getAll $ foldMap (All . Text.all p . chunkText) r
+ src/Data/Rope/UTF16/Internal/Position.hs view
@@ -0,0 +1,31 @@+module Data.Rope.UTF16.Internal.Position where++import Data.Semigroup++data RowColumn = RowColumn+ { row :: !Int -- ^ Number of newlines before this position+ , column :: !Int -- ^ Number of code units since last newline or start of string+ } deriving (Eq, Ord, Show)++instance Semigroup RowColumn where+ RowColumn 0 c1 <> RowColumn 0 c2 = RowColumn 0 $ c1 + c2+ RowColumn 0 _ <> v2 = v2+ RowColumn r1 c1 <> RowColumn 0 c2 = RowColumn r1 $ c1 + c2+ RowColumn r1 _ <> RowColumn r2 c2 = RowColumn (r1 + r2) c2++instance Monoid RowColumn where+ mempty = RowColumn 0 0+ mappend = (<>)++data Position = Position+ { codeUnits :: !Int+ , rowColumn :: !RowColumn+ } deriving (Eq, Ord, Show)++instance Semigroup Position where+ Position cp1 v1 <> Position cp2 v2+ = Position (cp1 + cp2) (v1 <> v2)++instance Monoid Position where+ mempty = Position 0 mempty+ mappend = (<>)
− src/Data/Rope/UTF16/Position.hs
@@ -1,34 +0,0 @@-module Data.Rope.UTF16.Position where--import Data.Semigroup--data RowColumn = RowColumn- { row :: !Int -- ^ Number of newlines before this position- , column :: !Int -- ^ Number of code units since last newline or start of string- } deriving (Eq, Ord, Show)--instance Semigroup RowColumn where- RowColumn 0 c1 <> RowColumn 0 c2 = RowColumn 0 $ c1 + c2- RowColumn 0 _ <> v2 = v2- RowColumn r1 c1 <> RowColumn 0 c2 = RowColumn r1 $ c1 + c2- RowColumn r1 _ <> RowColumn r2 c2 = RowColumn (r1 + r2) c2--instance Monoid RowColumn where- mempty = RowColumn 0 0- mappend = (<>)--data Position = Position- { codeUnits :: !Int- , rowColumn :: !RowColumn- } deriving (Eq, Ord, Show)--lineStart :: Position -> Position-lineStart (Position cp (RowColumn r c)) = Position (cp - c) $ RowColumn r 0--instance Semigroup Position where- Position cp1 v1 <> Position cp2 v2- = Position (cp1 + cp2) (v1 <> v2)--instance Monoid Position where- mempty = Position 0 mempty- mappend = (<>)
tests/Main.hs view
@@ -1,6 +1,8 @@+{-# LANGUAGE OverloadedStrings #-} module Main where import Data.Semigroup+import Data.Text (Text) import qualified Data.Text as Text import qualified Data.Text.Unsafe as Unsafe import Test.QuickCheck.Function as QC@@ -46,6 +48,11 @@ let t = Text.pack $ replicate newlines '\n' ++ takeWhile (/= '\n') s Rope.clamp16 (newlines + i) t == Rope.rowColumnCodeUnits (Rope.RowColumn newlines i) (Rope.fromText t) + , testProperty "splitAtLine" $ \s i -> do+ let t = Text.pack s+ (rows1', rows2') = Rope.splitAtLine i (Rope.fromText t)+ splitAtLineSpec i t == (Rope.toText rows1', Rope.toText rows2')+ , testProperty "span matches Text" $ \s p -> do let t = Text.pack s f = QC.applyFun p@@ -71,4 +78,57 @@ , testProperty "null matches Text" $ \s -> do let t = Text.pack s Rope.null (Rope.fromText t) == Text.null t++ , testProperty "rows is number of newlines" $ \s -> do+ let t = Text.pack s+ newlines = length $ filter (== '\n') s+ Rope.rows (Rope.fromText t) == newlines++ , testProperty "columns is number of code units of last line" $ \s -> do+ let t = Text.pack s+ lastLine = Text.takeWhileEnd (/= '\n') t+ cols = Unsafe.lengthWord16 lastLine+ Rope.columns (Rope.fromText t) == cols++ , testProperty "foldl matches Text" $ \s p a -> do+ let t = Text.pack s+ f = QC.applyFun2 p+ Rope.foldl f (a :: Int) (Rope.fromText t) == Text.foldl f a t++ , testProperty "foldl' matches Text" $ \s p a -> do+ let t = Text.pack s+ f = QC.applyFun2 p+ Rope.foldl' f (a :: Int) (Rope.fromText t) == Text.foldl' f a t++ , testProperty "foldr matches Text" $ \s p a -> do+ let t = Text.pack s+ f = QC.applyFun2 p+ Rope.foldr f (a :: Int) (Rope.fromText t) == Text.foldr f a t++ , testProperty "any matches Text" $ \s p -> do+ let t = Text.pack s+ f = QC.applyFun p+ Rope.any f (Rope.fromText t) == Text.any f t++ , testProperty "all matches Text" $ \s p -> do+ let t = Text.pack s+ f = QC.applyFun p+ Rope.all f (Rope.fromText t) == Text.all f t++ , testProperty "map matches Text" $ \s p -> do+ let t = Text.pack s+ f = QC.applyFun p+ Rope.toText (Rope.map f (Rope.fromText t)) == Text.map f t++ , testProperty "intercalate matches Text" $ \s ss -> do+ let t = Text.pack s+ ts = Text.pack <$> ss+ Rope.toText (Rope.intercalate (Rope.fromText t) (Rope.fromText <$> ts)) == Text.intercalate t ts ]++splitAtLineSpec :: Int -> Text -> (Text, Text)+splitAtLineSpec i t = case Text.splitOn "\n" t of+ [] -> ("", "")+ ts -> do+ let (pres, posts) = splitAt i (fmap (<> "\n") (init ts) <> [last ts])+ (Text.concat pres, Text.concat posts)