{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TupleSections #-}
#ifdef DEBUG
#define DEFRAGMENTATION_THRESHOLD 4
#else
#define DEFRAGMENTATION_THRESHOLD 4096
#endif
module Data.Text.Rope
( Rope
, fromText
, fromTextLines
, toText
, toTextLines
, null
, lines
, lengthInLines
, splitAtLine
, length
, splitAt
, Position(..)
, lengthAsPosition
, splitAtPosition
) where
import Prelude ((-), (+), seq)
import Control.DeepSeq (NFData, rnf)
import Data.Bool (Bool(..), otherwise)
import Data.Char (Char)
import Data.Eq (Eq, (==))
import Data.Function ((.), ($), on)
import Data.Maybe (Maybe(..))
import Data.Monoid (Monoid(..))
import Data.Ord (Ord, compare, (<), (<=), Ordering(..))
import Data.Semigroup (Semigroup(..))
import Data.String (IsString(..))
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TextLazy
import qualified Data.Text.Lazy.Builder as Builder
import Data.Text.Lines (Position(..))
import qualified Data.Text.Lines as TL
import Data.Word (Word)
import Text.Show (Show)
#ifdef DEBUG
import Prelude (error)
import GHC.Stack (HasCallStack)
#else
#define HasCallStack ()
import Text.Show (show)
#endif
data Rope
= Empty
| Node
{ Rope -> Rope
_ropeLeft :: !Rope
, Rope -> TextLines
_ropeMiddle :: !TL.TextLines
, Rope -> Rope
_ropeRight :: !Rope
, Rope -> Word
_ropeCharLen :: !Word
, Rope -> Position
_ropeCharLenAsPos :: !Position
}
instance NFData Rope where
rnf :: Rope -> ()
rnf Rope
Empty = ()
rnf (Node Rope
l TextLines
_ Rope
r Word
_ Position
_) = Rope -> ()
forall a. NFData a => a -> ()
rnf Rope
l () -> () -> ()
`seq` Rope -> ()
forall a. NFData a => a -> ()
rnf Rope
r
instance Eq Rope where
== :: Rope -> Rope -> Bool
(==) = Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
(==) (Text -> Text -> Bool) -> (Rope -> Text) -> Rope -> Rope -> Bool
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` Rope -> Text
toLazyText
instance Ord Rope where
compare :: Rope -> Rope -> Ordering
compare = Text -> Text -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Text -> Text -> Ordering)
-> (Rope -> Text) -> Rope -> Rope -> Ordering
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` Rope -> Text
toLazyText
#ifdef DEBUG
deriving instance Show Rope
#else
instance Show Rope where
show :: Rope -> String
show = Text -> String
forall a. Show a => a -> String
show (Text -> String) -> (Rope -> Text) -> Rope -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Rope -> Text
toLazyText
#endif
instance IsString Rope where
fromString :: String -> Rope
fromString = TextLines -> Rope
fromTextLines (TextLines -> Rope) -> (String -> TextLines) -> String -> Rope
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> TextLines
forall a. IsString a => String -> a
fromString
null :: Rope -> Bool
null :: Rope -> Bool
null = \case
Rope
Empty -> Bool
True
Node{} -> Bool
False
length :: Rope -> Word
length :: Rope -> Word
length = \case
Rope
Empty -> Word
0
Node Rope
_ TextLines
_ Rope
_ Word
w Position
_ -> Word
w
lengthAsPosition :: Rope -> Position
lengthAsPosition :: Rope -> Position
lengthAsPosition = \case
Rope
Empty -> Position
forall a. Monoid a => a
mempty
Node Rope
_ TextLines
_ Rope
_ Word
_ Position
p -> Position
p
instance Semigroup Rope where
Rope
Empty <> :: Rope -> Rope -> Rope
<> Rope
t = Rope
t
Rope
t <> Rope
Empty = Rope
t
Node Rope
l1 TextLines
c1 Rope
r1 Word
u1 Position
p1 <> Node Rope
l2 TextLines
c2 Rope
r2 Word
u2 Position
p2 = Rope -> TextLines -> Rope -> Word -> Position -> Rope
defragment
Rope
l1
TextLines
c1
(Rope -> TextLines -> Rope -> Word -> Position -> Rope
Node (Rope
r1 Rope -> Rope -> Rope
forall a. Semigroup a => a -> a -> a
<> Rope
l2) TextLines
c2 Rope
r2 (Rope -> Word
length Rope
r1 Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Word
u2) (Rope -> Position
lengthAsPosition Rope
r1 Position -> Position -> Position
forall a. Semigroup a => a -> a -> a
<> Position
p2))
(Word
u1 Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Word
u2)
(Position
p1 Position -> Position -> Position
forall a. Semigroup a => a -> a -> a
<> Position
p2)
instance Monoid Rope where
mempty :: Rope
mempty = Rope
Empty
mappend :: Rope -> Rope -> Rope
mappend = Rope -> Rope -> Rope
forall a. Semigroup a => a -> a -> a
(<>)
defragment :: HasCallStack => Rope -> TL.TextLines -> Rope -> Word -> Position -> Rope
defragment :: Rope -> TextLines -> Rope -> Word -> Position -> Rope
defragment !Rope
l !TextLines
c !Rope
r !Word
u !Position
p
#ifdef DEBUG
| TL.null c = error "Data.Text.Lines: violated internal invariant"
#endif
| Word
u Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
< DEFRAGMENTATION_THRESHOLD
= Rope -> TextLines -> Rope -> Word -> Position -> Rope
Node Rope
Empty (Rope -> TextLines
toTextLines Rope
rp) Rope
Empty Word
u Position
p
| Bool
otherwise
= Rope
rp
where
rp :: Rope
rp = Rope -> TextLines -> Rope -> Word -> Position -> Rope
Node Rope
l TextLines
c Rope
r Word
u Position
p
fromTextLines :: TL.TextLines -> Rope
fromTextLines :: TextLines -> Rope
fromTextLines TextLines
tl
| TextLines -> Bool
TL.null TextLines
tl = Rope
Empty
| Bool
otherwise = Rope -> TextLines -> Rope -> Word -> Position -> Rope
Node Rope
Empty TextLines
tl Rope
Empty (TextLines -> Word
TL.length TextLines
tl) (TextLines -> Position
TL.lengthAsPosition TextLines
tl)
node :: HasCallStack => Rope -> TL.TextLines -> Rope -> Rope
node :: Rope -> TextLines -> Rope -> Rope
node Rope
l TextLines
c Rope
r = Rope -> TextLines -> Rope -> Word -> Position -> Rope
defragment Rope
l TextLines
c Rope
r Word
totalLength Position
totalLengthAsPosition
where
totalLength :: Word
totalLength = Rope -> Word
length Rope
l Word -> Word -> Word
forall a. Num a => a -> a -> a
+ TextLines -> Word
TL.length TextLines
c Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Rope -> Word
length Rope
r
totalLengthAsPosition :: Position
totalLengthAsPosition = Rope -> Position
lengthAsPosition Rope
l Position -> Position -> Position
forall a. Semigroup a => a -> a -> a
<> TextLines -> Position
TL.lengthAsPosition TextLines
c Position -> Position -> Position
forall a. Semigroup a => a -> a -> a
<> Rope -> Position
lengthAsPosition Rope
r
(|>) :: Rope -> TL.TextLines -> Rope
Rope
tr |> :: Rope -> TextLines -> Rope
|> TextLines
tl
| TextLines -> Bool
TL.null TextLines
tl = Rope
tr
| Bool
otherwise = Rope -> TextLines -> Rope -> Rope
node Rope
tr TextLines
tl Rope
Empty
(<|) :: TL.TextLines -> Rope -> Rope
TextLines
tl <| :: TextLines -> Rope -> Rope
<| Rope
tr
| TextLines -> Bool
TL.null TextLines
tl = Rope
tr
| Bool
otherwise = Rope -> TextLines -> Rope -> Rope
node Rope
Empty TextLines
tl Rope
tr
fromText :: Text -> Rope
fromText :: Text -> Rope
fromText = TextLines -> Rope
fromTextLines (TextLines -> Rope) -> (Text -> TextLines) -> Text -> Rope
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> TextLines
TL.fromText
foldMapRope :: Monoid a => (TL.TextLines -> a) -> Rope -> a
foldMapRope :: (TextLines -> a) -> Rope -> a
foldMapRope TextLines -> a
f = Rope -> a
go
where
go :: Rope -> a
go = \case
Rope
Empty -> a
forall a. Monoid a => a
mempty
Node Rope
l TextLines
c Rope
r Word
_ Position
_ -> Rope -> a
go Rope
l a -> a -> a
forall a. Monoid a => a -> a -> a
`mappend` TextLines -> a
f TextLines
c a -> a -> a
forall a. Monoid a => a -> a -> a
`mappend` Rope -> a
go Rope
r
data Lines = Lines ![Text] !Bool
instance Semigroup Lines where
Lines [] Bool
_ <> :: Lines -> Lines -> Lines
<> Lines
ls = Lines
ls
Lines
ls <> Lines [] Bool
_ = Lines
ls
Lines [Text]
xs Bool
x <> Lines [Text]
ys Bool
y = [Text] -> Bool -> Lines
Lines (if Bool
x then [Text]
xs [Text] -> [Text] -> [Text]
forall a. Semigroup a => a -> a -> a
<> [Text]
ys else [Text] -> [Text] -> [Text]
forall a. Semigroup a => [a] -> [a] -> [a]
go [Text]
xs [Text]
ys) Bool
y
where
go :: [a] -> [a] -> [a]
go [] [a]
vs = [a]
vs
go [a
u] (a
v : [a]
vs) = (a
u a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
v) a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a]
vs
go (a
u : [a]
us) [a]
vs = a
u a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a] -> [a] -> [a]
go [a]
us [a]
vs
instance Monoid Lines where
mempty :: Lines
mempty = [Text] -> Bool -> Lines
Lines [] Bool
False
mappend :: Lines -> Lines -> Lines
mappend = Lines -> Lines -> Lines
forall a. Semigroup a => a -> a -> a
(<>)
lines :: Rope -> [Text]
lines :: Rope -> [Text]
lines = (\(Lines [Text]
ls Bool
_) -> [Text]
ls) (Lines -> [Text]) -> (Rope -> Lines) -> Rope -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TextLines -> Lines) -> Rope -> Lines
forall a. Monoid a => (TextLines -> a) -> Rope -> a
foldMapRope
(\TextLines
tl -> [Text] -> Bool -> Lines
Lines (TextLines -> [Text]
TL.lines TextLines
tl) (Text -> Char
T.last (TextLines -> Text
TL.toText TextLines
tl) Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\n'))
lastChar :: Rope -> Maybe Char
lastChar :: Rope -> Maybe Char
lastChar = \case
Rope
Empty -> Maybe Char
forall a. Maybe a
Nothing
Node Rope
_ TextLines
c Rope
Empty Word
_ Position
_ -> Char -> Maybe Char
forall a. a -> Maybe a
Just (Char -> Maybe Char) -> Char -> Maybe Char
forall a b. (a -> b) -> a -> b
$ Text -> Char
T.last (Text -> Char) -> Text -> Char
forall a b. (a -> b) -> a -> b
$ TextLines -> Text
TL.toText TextLines
c
Node Rope
_ TextLines
_ Rope
r Word
_ Position
_ -> Rope -> Maybe Char
lastChar Rope
r
lengthInLines :: Rope -> Word
lengthInLines :: Rope -> Word
lengthInLines Rope
rp = case Rope -> Maybe Char
lastChar Rope
rp of
Maybe Char
Nothing -> Word
0
Just Char
ch -> Position -> Word
TL.posLine (Rope -> Position
lengthAsPosition Rope
rp) Word -> Word -> Word
forall a. Num a => a -> a -> a
+ (if Char
ch Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\n' then Word
0 else Word
1)
toTextLines :: Rope -> TL.TextLines
toTextLines :: Rope -> TextLines
toTextLines = [TextLines] -> TextLines
forall a. Monoid a => [a] -> a
mconcat ([TextLines] -> TextLines)
-> (Rope -> [TextLines]) -> Rope -> TextLines
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TextLines -> [TextLines]) -> Rope -> [TextLines]
forall a. Monoid a => (TextLines -> a) -> Rope -> a
foldMapRope (TextLines -> [TextLines] -> [TextLines]
forall a. a -> [a] -> [a]
:[])
toLazyText :: Rope -> TextLazy.Text
toLazyText :: Rope -> Text
toLazyText = (TextLines -> Text) -> Rope -> Text
forall a. Monoid a => (TextLines -> a) -> Rope -> a
foldMapRope (Text -> Text
TextLazy.fromStrict (Text -> Text) -> (TextLines -> Text) -> TextLines -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TextLines -> Text
TL.toText)
toText :: Rope -> Text
toText :: Rope -> Text
toText = Text -> Text
TextLazy.toStrict (Text -> Text) -> (Rope -> Text) -> Rope -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
Builder.toLazyText (Builder -> Text) -> (Rope -> Builder) -> Rope -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TextLines -> Builder) -> Rope -> Builder
forall a. Monoid a => (TextLines -> a) -> Rope -> a
foldMapRope (Text -> Builder
Builder.fromText (Text -> Builder) -> (TextLines -> Text) -> TextLines -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TextLines -> Text
TL.toText)
splitAt :: HasCallStack => Word -> Rope -> (Rope, Rope)
splitAt :: Word -> Rope -> (Rope, Rope)
splitAt !Word
len = \case
Rope
Empty -> (Rope
Empty, Rope
Empty)
Node Rope
l TextLines
c Rope
r Word
_ Position
_
| Word
len Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
<= Word
ll -> case Word -> Rope -> (Rope, Rope)
splitAt Word
len Rope
l of
(Rope
before, Rope
after) -> (Rope
before, Rope -> TextLines -> Rope -> Rope
node Rope
after TextLines
c Rope
r)
| Word
len Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
<= Word
llc -> case Word -> TextLines -> (TextLines, TextLines)
TL.splitAt (Word
len Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
ll) TextLines
c of
(TextLines
before, TextLines
after) -> (Rope
l Rope -> TextLines -> Rope
|> TextLines
before, TextLines
after TextLines -> Rope -> Rope
<| Rope
r)
| Bool
otherwise -> case Word -> Rope -> (Rope, Rope)
splitAt (Word
len Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
llc) Rope
r of
(Rope
before, Rope
after) -> (Rope -> TextLines -> Rope -> Rope
node Rope
l TextLines
c Rope
before, Rope
after)
where
ll :: Word
ll = Rope -> Word
length Rope
l
llc :: Word
llc = Word
ll Word -> Word -> Word
forall a. Num a => a -> a -> a
+ TextLines -> Word
TL.length TextLines
c
splitAtLine :: HasCallStack => Word -> Rope -> (Rope, Rope)
splitAtLine :: Word -> Rope -> (Rope, Rope)
splitAtLine !Word
len = \case
Rope
Empty -> (Rope
Empty, Rope
Empty)
Node Rope
l TextLines
c Rope
r Word
_ Position
_
| Word
len Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
<= Word
ll -> case Word -> Rope -> (Rope, Rope)
splitAtLine Word
len Rope
l of
(Rope
before, Rope
after) -> (Rope
before, Rope -> TextLines -> Rope -> Rope
node Rope
after TextLines
c Rope
r)
| Word
len Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
<= Word
llc -> case Word -> TextLines -> (TextLines, TextLines)
TL.splitAtLine (Word
len Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
ll) TextLines
c of
(TextLines
before, TextLines
after) -> (Rope
l Rope -> TextLines -> Rope
|> TextLines
before, TextLines
after TextLines -> Rope -> Rope
<| Rope
r)
| Bool
otherwise -> case Word -> Rope -> (Rope, Rope)
splitAtLine (Word
len Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
llc) Rope
r of
(Rope
before, Rope
after) -> (Rope -> TextLines -> Rope -> Rope
node Rope
l TextLines
c Rope
before, Rope
after)
where
ll :: Word
ll = Position -> Word
TL.posLine (Rope -> Position
lengthAsPosition Rope
l)
llc :: Word
llc = Word
ll Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Position -> Word
TL.posLine (TextLines -> Position
TL.lengthAsPosition TextLines
c)
subOnRope :: Rope -> Position -> Position -> Position
subOnRope :: Rope -> Position -> Position -> Position
subOnRope Rope
rp (Position Word
xl Word
xc) (Position Word
yl Word
yc) = case Word
xl Word -> Word -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` Word
yl of
Ordering
GT -> Word -> Word -> Position
Position (Word
xl Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
yl) Word
xc
Ordering
EQ -> Word -> Word -> Position
Position Word
0 (Word
xc Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
yc)
Ordering
LT -> Word -> Word -> Position
Position Word
0 (Word
xc Word -> Word -> Word
forall a. Num a => a -> a -> a
- Rope -> Word
length Rope
rp')
where
(Rope
_, Rope
rp') = Word -> Rope -> (Rope, Rope)
splitAtLine Word
xl Rope
rp
subOnLines :: TL.TextLines -> Position -> Position -> Position
subOnLines :: TextLines -> Position -> Position -> Position
subOnLines TextLines
tl (Position Word
xl Word
xc) (Position Word
yl Word
yc) = case Word
xl Word -> Word -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` Word
yl of
Ordering
GT -> Word -> Word -> Position
Position (Word
xl Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
yl) Word
xc
Ordering
EQ -> Word -> Word -> Position
Position Word
0 (Word
xc Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
yc)
Ordering
LT -> Word -> Word -> Position
Position Word
0 (Word
xc Word -> Word -> Word
forall a. Num a => a -> a -> a
- TextLines -> Word
TL.length TextLines
tl')
where
(TextLines
_, TextLines
tl') = Word -> TextLines -> (TextLines, TextLines)
TL.splitAtLine Word
xl TextLines
tl
splitAtPosition :: HasCallStack => Position -> Rope -> (Rope, Rope)
splitAtPosition :: Position -> Rope -> (Rope, Rope)
splitAtPosition (Position Word
0 Word
0) = (Rope
forall a. Monoid a => a
mempty,)
splitAtPosition !Position
len = \case
Rope
Empty -> (Rope
Empty, Rope
Empty)
Node Rope
l TextLines
c Rope
r Word
_ Position
_
| Position
len Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
<= Position
ll -> case Position -> Rope -> (Rope, Rope)
splitAtPosition Position
len Rope
l of
(Rope
before, Rope
after)
| Rope -> Bool
null Rope
after -> case Position -> Rope -> (Rope, Rope)
splitAtPosition Position
len' (TextLines
c TextLines -> Rope -> Rope
<| Rope
r) of
(Rope
r', Rope
r'') -> (Rope
l Rope -> Rope -> Rope
forall a. Semigroup a => a -> a -> a
<> Rope
r', Rope
r'')
| Bool
otherwise -> (Rope
before, Rope -> TextLines -> Rope -> Rope
node Rope
after TextLines
c Rope
r)
| Position
len Position -> Position -> Bool
forall a. Ord a => a -> a -> Bool
<= Position
llc -> case Position -> TextLines -> (TextLines, TextLines)
TL.splitAtPosition Position
len' TextLines
c of
(TextLines
before, TextLines
after)
| TextLines -> Bool
TL.null TextLines
after -> case Position -> Rope -> (Rope, Rope)
splitAtPosition Position
len'' Rope
r of
(Rope
r', Rope
r'') -> ((Rope
l Rope -> TextLines -> Rope
|> TextLines
c) Rope -> Rope -> Rope
forall a. Semigroup a => a -> a -> a
<> Rope
r', Rope
r'')
| Bool
otherwise -> (Rope
l Rope -> TextLines -> Rope
|> TextLines
before, TextLines
after TextLines -> Rope -> Rope
<| Rope
r)
| Bool
otherwise -> case Position -> Rope -> (Rope, Rope)
splitAtPosition Position
len'' Rope
r of
(Rope
before, Rope
after) -> (Rope -> TextLines -> Rope -> Rope
node Rope
l TextLines
c Rope
before, Rope
after)
where
ll :: Position
ll = Rope -> Position
lengthAsPosition Rope
l
lc :: Position
lc = TextLines -> Position
TL.lengthAsPosition TextLines
c
llc :: Position
llc = Position
ll Position -> Position -> Position
forall a. Semigroup a => a -> a -> a
<> Position
lc
len' :: Position
len' = Rope -> Position -> Position -> Position
subOnRope Rope
l Position
len Position
ll
len'' :: Position
len'' = TextLines -> Position -> Position -> Position
subOnLines TextLines
c Position
len' Position
lc