packages feed

text-region 0.1.0.1 → 0.2.0.0

raw patch · 4 files changed

+78/−209 lines, 4 filesdep −containersdep −mtlPVP ok

version bump matches the API change (PVP)

Dependencies removed: containers, mtl

API changes (from Hackage documentation)

- Data.Text.Region: applyEdit :: EditAction e s => e s -> Contents s -> Contents s
- Data.Text.Region: class ApplyMap a
- Data.Text.Region: edit :: EditAction Replace s => s -> r -> EditM s r a -> (a, s)
- Data.Text.Region: edit_ :: EditAction Replace s => s -> r -> EditM s r a -> s
- Data.Text.Region: instance Data.Text.Region.ApplyMap ()
- Data.Text.Region: instance Data.Text.Region.ApplyMap (Data.Text.Region.Types.Replace s)
- Data.Text.Region: instance Data.Text.Region.ApplyMap (e s) => Data.Text.Region.ApplyMap (Data.Text.Region.Types.Chain e s)
- Data.Text.Region: instance Data.Text.Region.ApplyMap Data.Text.Region.Types.Map
- Data.Text.Region: instance Data.Text.Region.ApplyMap Data.Text.Region.Types.Point
- Data.Text.Region: instance Data.Text.Region.ApplyMap Data.Text.Region.Types.Region
- Data.Text.Region: instance Data.Text.Region.ApplyMap a => Data.Text.Region.ApplyMap [a]
- Data.Text.Region: instance Data.Text.Region.EditAction e s => Data.Text.Region.EditAction (Data.Text.Region.Types.Chain e) s
- Data.Text.Region: inverse :: EditAction e s => Contents s -> e s -> e s
- Data.Text.Region: push :: ActionIso (Edit s) -> EditM s r ()
- Data.Text.Region: redo :: (EditAction Replace s, ApplyMap r) => EditM s r ()
- Data.Text.Region: run :: (EditAction Replace s, ApplyMap r) => Edit s -> EditM s r ()
- Data.Text.Region: runGroup :: (EditAction Replace s, ApplyMap r) => [Edit s] -> EditM s r ()
- Data.Text.Region: run_ :: (EditAction Replace s, ApplyMap r) => Edit s -> EditM s r (ActionIso (Edit s))
- Data.Text.Region: undo :: (EditAction Replace s, ApplyMap r) => EditM s r ()
- Data.Text.Region: updateMap :: (EditAction e s, ApplyMap a) => e s -> a -> a
- Data.Text.Region.Types: ActionIso :: e -> e -> ActionIso e
- Data.Text.Region.Types: ActionStack :: [ActionIso e] -> [ActionIso e] -> ActionStack e
- Data.Text.Region.Types: Chain :: [e s] -> Chain e s
- Data.Text.Region.Types: EditM :: State (EditState s r) a -> EditM s r a
- Data.Text.Region.Types: EditState :: ActionStack (Edit s) -> Contents s -> r -> EditState s r
- Data.Text.Region.Types: [_actionBack] :: ActionIso e -> e
- Data.Text.Region.Types: [_action] :: ActionIso e -> e
- Data.Text.Region.Types: [_chain] :: Chain e s -> [e s]
- Data.Text.Region.Types: [_edited] :: EditState s r -> Contents s
- Data.Text.Region.Types: [_history] :: EditState s r -> ActionStack (Edit s)
- Data.Text.Region.Types: [_redoStack] :: ActionStack e -> [ActionIso e]
- Data.Text.Region.Types: [_regions] :: EditState s r -> r
- Data.Text.Region.Types: [_undoStack] :: ActionStack e -> [ActionIso e]
- Data.Text.Region.Types: [runEditM] :: EditM s r a -> State (EditState s r) a
- Data.Text.Region.Types: action :: forall e_aiNF. Lens' (ActionIso e_aiNF) e_aiNF
- Data.Text.Region.Types: actionBack :: forall e_aiNF. Lens' (ActionIso e_aiNF) e_aiNF
- Data.Text.Region.Types: chain :: forall e_ai6C s_ai6D e_aiNw s_aiNx. Iso (Chain e_ai6C s_ai6D) (Chain e_aiNw s_aiNx) [e_ai6C s_ai6D] [e_aiNw s_aiNx]
- Data.Text.Region.Types: data ActionIso e
- Data.Text.Region.Types: data ActionStack e
- Data.Text.Region.Types: data EditState s r
- Data.Text.Region.Types: editState :: Editable s => s -> r -> EditState s r
- Data.Text.Region.Types: edited :: forall s_aiUn r_aiUo. Lens' (EditState s_aiUn r_aiUo) (Contents s_aiUn)
- Data.Text.Region.Types: emptyStack :: ActionStack e
- Data.Text.Region.Types: history :: forall s_aiUn r_aiUo. Lens' (EditState s_aiUn r_aiUo) (ActionStack (Edit s_aiUn))
- Data.Text.Region.Types: instance (Data.Text.Region.Types.Editable s, Data.Aeson.Types.FromJSON.FromJSON s, Data.Aeson.Types.FromJSON.FromJSON r) => Data.Aeson.Types.FromJSON.FromJSON (Data.Text.Region.Types.EditState s r)
- Data.Text.Region.Types: instance (Data.Text.Region.Types.Editable s, Data.Aeson.Types.ToJSON.ToJSON s, Data.Aeson.Types.ToJSON.ToJSON r) => Data.Aeson.Types.ToJSON.ToJSON (Data.Text.Region.Types.EditState s r)
- Data.Text.Region.Types: instance Control.Monad.State.Class.MonadState (Data.Text.Region.Types.EditState s r) (Data.Text.Region.Types.EditM s r)
- Data.Text.Region.Types: instance Data.Aeson.Types.FromJSON.FromJSON (e s) => Data.Aeson.Types.FromJSON.FromJSON (Data.Text.Region.Types.Chain e s)
- Data.Text.Region.Types: instance Data.Aeson.Types.FromJSON.FromJSON e => Data.Aeson.Types.FromJSON.FromJSON (Data.Text.Region.Types.ActionIso e)
- Data.Text.Region.Types: instance Data.Aeson.Types.FromJSON.FromJSON e => Data.Aeson.Types.FromJSON.FromJSON (Data.Text.Region.Types.ActionStack e)
- Data.Text.Region.Types: instance Data.Aeson.Types.ToJSON.ToJSON (e s) => Data.Aeson.Types.ToJSON.ToJSON (Data.Text.Region.Types.Chain e s)
- Data.Text.Region.Types: instance Data.Aeson.Types.ToJSON.ToJSON e => Data.Aeson.Types.ToJSON.ToJSON (Data.Text.Region.Types.ActionIso e)
- Data.Text.Region.Types: instance Data.Aeson.Types.ToJSON.ToJSON e => Data.Aeson.Types.ToJSON.ToJSON (Data.Text.Region.Types.ActionStack e)
- Data.Text.Region.Types: instance GHC.Base.Applicative (Data.Text.Region.Types.EditM s r)
- Data.Text.Region.Types: instance GHC.Base.Functor (Data.Text.Region.Types.EditM s r)
- Data.Text.Region.Types: instance GHC.Base.Monad (Data.Text.Region.Types.EditM s r)
- Data.Text.Region.Types: instance GHC.Base.Monoid (Data.Text.Region.Types.Chain e s)
- Data.Text.Region.Types: instance GHC.Base.Monoid e => Data.Group.Group (Data.Text.Region.Types.ActionIso e)
- Data.Text.Region.Types: instance GHC.Base.Monoid e => GHC.Base.Monoid (Data.Text.Region.Types.ActionIso e)
- Data.Text.Region.Types: instance GHC.Classes.Eq (e s) => GHC.Classes.Eq (Data.Text.Region.Types.Chain e s)
- Data.Text.Region.Types: instance GHC.Show.Show (e s) => GHC.Show.Show (Data.Text.Region.Types.Chain e s)
- Data.Text.Region.Types: newtype Chain e s
- Data.Text.Region.Types: newtype EditM s r a
- Data.Text.Region.Types: redoStack :: forall e_aiQs. Lens' (ActionStack e_aiQs) [ActionIso e_aiQs]
- Data.Text.Region.Types: regions :: forall s_aiUn r_aiUo r_aiXi. Lens (EditState s_aiUn r_aiUo) (EditState s_aiUn r_aiXi) r_aiUo r_aiXi
- Data.Text.Region.Types: type Edit s = Chain Replace s
- Data.Text.Region.Types: undoStack :: forall e_aiQs. Lens' (ActionStack e_aiQs) [ActionIso e_aiQs]
+ Data.Text.Region: instance Data.Text.Region.Types.Editable s => Data.Text.Region.EditAction Data.Text.Region.Types.Edit s
+ Data.Text.Region: inversed :: EditAction e s => e s -> Contents s -> e s
+ Data.Text.Region: overlaps :: Region -> Region -> Bool
+ Data.Text.Region: update :: Editable s => Edit s -> Region -> Region
+ Data.Text.Region.Types: Edit :: [Replace s] -> Edit s
+ Data.Text.Region.Types: [_replaces] :: Edit s -> [Replace s]
+ Data.Text.Region.Types: instance (Data.Aeson.Types.ToJSON.ToJSON s, Data.Text.Region.Types.Editable s) => GHC.Show.Show (Data.Text.Region.Types.Edit s)
+ Data.Text.Region.Types: instance (Data.Text.Region.Types.Editable s, Data.Aeson.Types.FromJSON.FromJSON s) => Data.Aeson.Types.FromJSON.FromJSON (Data.Text.Region.Types.Edit s)
+ Data.Text.Region.Types: instance (Data.Text.Region.Types.Editable s, Data.Aeson.Types.ToJSON.ToJSON s) => Data.Aeson.Types.ToJSON.ToJSON (Data.Text.Region.Types.Edit s)
+ Data.Text.Region.Types: instance GHC.Base.Monoid (Data.Text.Region.Types.Edit s)
+ Data.Text.Region.Types: instance GHC.Classes.Eq s => GHC.Classes.Eq (Data.Text.Region.Types.Edit s)
+ Data.Text.Region.Types: newtype Edit s
+ Data.Text.Region.Types: pointRegion :: Iso' Point Region
+ Data.Text.Region.Types: replaces :: forall s_alyS s_amdN. Iso (Edit s_alyS) (Edit s_amdN) [Replace s_alyS] [Replace s_amdN]
- Data.Text.Region: apply :: EditAction Replace s => Edit s -> Contents s -> Contents s
+ Data.Text.Region: apply :: Editable s => Edit s -> Contents s -> Contents s
- Data.Text.Region: applyMap :: ApplyMap a => Map -> a -> a
+ Data.Text.Region: applyMap :: Map -> Region -> Region
- Data.Text.Region: class (Editable s, ApplyMap (e s)) => EditAction e s
+ Data.Text.Region: class Editable s => EditAction e s
- Data.Text.Region: perform :: EditAction e s => e s -> State (Contents s) (e s)
+ Data.Text.Region: perform :: EditAction e s => e s -> Contents s -> Contents s
- Data.Text.Region.Types: replaceRegion :: forall s_ag2w. Lens' (Replace s_ag2w) Region
+ Data.Text.Region.Types: replaceRegion :: forall s_ak05. Lens' (Replace s_ak05) Region
- Data.Text.Region.Types: replaceWith :: forall s_ag2w s_ai6o. Lens (Replace s_ag2w) (Replace s_ai6o) (Contents s_ag2w) (Contents s_ai6o)
+ Data.Text.Region.Types: replaceWith :: forall s_ak05 s_alyE. Lens (Replace s_ak05) (Replace s_alyE) (Contents s_ak05) (Contents s_alyE)

Files

src/Data/Text/Region.hs view
@@ -2,10 +2,9 @@ 
 module Data.Text.Region (
 	pt, start, lineStart, regionLength, till, linesSize, regionLines, emptyRegion, line,
-	regionSize, expandLines, atRegion, ApplyMap(..), updateMap, cutMap, insertMap,
+	regionSize, expandLines, atRegion, overlaps, applyMap, cutMap, insertMap,
 	cutRegion, insertRegion,
-	EditAction(..), cut, paste, overwrite, inverse, applyEdit, apply,
-	edit, edit_, push, run_, run, runGroup, undo, redo,
+	EditAction(..), cut, paste, overwrite, apply, update,
 
 	module Data.Text.Region.Types
 	) where
@@ -13,10 +12,8 @@ import Prelude hiding (id, (.))
 import Prelude.Unicode
 
-import Control.Arrow
 import Control.Category
 import Control.Lens
-import Control.Monad.State
 
 import Data.Text.Region.Types
 
@@ -74,33 +71,15 @@ 	fromc cts = cts ^. splitted (r ^. regionTo) . _1 . splitted (r ^. regionFrom) . _2
 	toc cts cts' = (cts ^. splitted (r ^. regionFrom) . _1) `concatCts` cts' `concatCts` (cts ^. splitted (r ^. regionTo) . _2)
 
-class ApplyMap a where
-	applyMap ∷ Map → a → a
-
-instance ApplyMap () where
-	applyMap _ = id
-
-instance ApplyMap a ⇒ ApplyMap [a] where
-	applyMap m = map (applyMap m)
-
-instance ApplyMap Map where
-	applyMap = mappend
-
-instance ApplyMap Region where
-	applyMap = view ∘ mapIso
-
-instance ApplyMap Point where
-	applyMap m p = view regionFrom $ applyMap m (p `till` p)
-
-instance ApplyMap (Replace s) where
-	applyMap m (Replace r w) = Replace (applyMap m r) w
-
-instance ApplyMap (e s) ⇒ ApplyMap (Chain e s) where
-	applyMap m (Chain rs) = Chain (map (applyMap m) rs)
+-- | Does regions overlaps
+overlaps ∷ Region → Region → Bool
+overlaps l r
+	| r ^. regionFrom ≥ l ^. regionTo = False
+	| r ^. regionTo ≤ l ^. regionFrom = False
+	| otherwise = True
 
--- | Update 'Region' after some action
-updateMap ∷ (EditAction e s, ApplyMap a) ⇒ e s → a → a
-updateMap = applyMap ∘ actionMap
+applyMap ∷ Map → Region → Region
+applyMap = view ∘ mapIso
 
 -- | Cut 'Region' mapping
 cutMap ∷ Region → Map
@@ -116,19 +95,25 @@ 	(if is < s then (s .-. ie) .+. is else s)
 	(if is < e then (e .-. ie) .+. is else e)
 
--- | Update second region position as if it was data inserted at first region
+-- | Update second region position as if it was data inserted at first region (region sets insertion point and data size)
+-- Region tries not to extend if data inserted at region bound except when region is empty
+-- This allows define replace as cut and insert in special case when we replace region itself
 insertRegion ∷ Region → Region → Region
-insertRegion (Region is ie) (Region s e) = Region
-	(if is < s then (s .-. is) .+. ie else s)
-	(if is < e then (e .-. is) .+. ie else e)
+insertRegion (Region is ie) (Region s e)
+	| (s ≡ e) ∧ (is ≡ s) = Region is ie
+	| otherwise = Region
+		(if is ≤ s then (s .-. is) .+. ie else s)
+		(if is < e then (e .-. is) .+. ie else e)
 
-class (Editable s, ApplyMap (e s)) ⇒ EditAction e s where
+class Editable s ⇒ EditAction e s where
 	-- | Make replace action over 'Region' and 'Contents'
 	replace ∷ Region → Contents s → e s
 	-- | Make 'Map' from action
 	actionMap ∷ e s → Map
-	-- | Perform action, modifying 'Contents' and returning inverse (undo) action
-	perform ∷ e s → State (Contents s) (e s)
+	-- | Perform action, modifying 'Contents'
+	perform ∷ e s → Contents s → Contents s
+	-- | Get action undo
+	inversed ∷ e s → Contents s → e s
 
 -- | Cuts region
 cut ∷ EditAction e s ⇒ Region → e s
@@ -142,80 +127,26 @@ overwrite ∷ EditAction e s ⇒ Point → Contents s → e s
 overwrite p c = replace (p `regionSize` measure c) c
 
--- | Get undo-action
-inverse ∷ EditAction e s ⇒ Contents s → e s → e s
-inverse cts act = evalState (perform act) cts
-
--- | Apply action to 'Contents'
-applyEdit ∷ EditAction e s ⇒ e s → Contents s → Contents s
-applyEdit act = snd ∘ runState (perform act)
+-- | 'perform' for 'Edit'
+apply ∷ Editable s ⇒ Edit s → Contents s → Contents s
+apply = perform
 
--- | 'applyEdit' for 'Edit'
-apply ∷ EditAction Replace s ⇒ Edit s → Contents s → Contents s
-apply = applyEdit
+-- | Update regions
+update ∷ Editable s ⇒ Edit s → Region → Region
+update = applyMap ∘ actionMap
 
 instance Editable s ⇒ EditAction Replace s where
 	replace = Replace
-	actionMap (Replace r w) = insertMap ((r ^. regionFrom) `regionSize` measure w) `mappend` cutMap r
-	perform (Replace r w) = state $ \cts → (Replace ((r ^. regionFrom) `regionSize` measure w) (cts ^. atRegion r), atRegion r .~ w $ cts)
-
-instance EditAction e s ⇒ EditAction (Chain e) s where
-	replace rgn txt = Chain [replace rgn txt]
-	actionMap (Chain []) = mempty
-	actionMap (Chain (r : rs)) = actionMap (applyMap (actionMap r) (Chain rs)) `mappend` actionMap r
-	perform (Chain rs) = (Chain ∘ reverse) <$> go mempty rs where
-		go _ [] = return []
-		go m (c : cs) = (:) <$> perform (applyMap m c) <*> go (actionMap (applyMap m c) `mappend` m) cs
-
--- | Run edit monad and return result with updated contents
-edit ∷ EditAction Replace s ⇒ s → r → EditM s r a → (a, s)
-edit txt rs act = second (view $ edited . from contents) $ runState (runEditM act) (editState txt rs)
-
--- | Run edit monad and return updated contents
-edit_ ∷ EditAction Replace s ⇒ s → r → EditM s r a → s
-edit_ txt rs = snd ∘ edit txt rs
-
--- | Push action into history, also drops redo stack
-push ∷ ActionIso (Edit s) → EditM s r ()
-push e = modify (over (history . undoStack) (e :)) >> modify (set (history . redoStack) [])
-
--- | Run edit action and returns corresponding redo-undo action
-run_ ∷ (EditAction Replace s, ApplyMap r) ⇒ Edit s → EditM s r (ActionIso (Edit s))
-run_ e = do
-	cts ← gets (view edited)
-	let
-		(undo', cts') = runState (perform e) cts
-	modify (set edited cts')
-	modify (over regions (applyMap $ actionMap e))
-	return $ ActionIso e undo'
-
--- | Run edit action with updating undo/redo stack
-run ∷ (EditAction Replace s, ApplyMap r) ⇒ Edit s → EditM s r ()
-run e = run_ e >>= push
-
--- | Run edit actions, updating undo/redo stack for each of them, but act like they was applied simultaneously
--- For example, cutting 1-st and then 3-rd letter:
--- @run (cut first) >> run (cut third) -- 1234 -> 234 -> 23@
--- @runGroup [cut first, cut third] -- 1234 -> 234 -> 24@
-runGroup ∷ (EditAction Replace s, ApplyMap r) ⇒ [Edit s] → EditM s r ()
-runGroup = go mempty where
-	go _ [] = return ()
-	go m (e:es) = run e' >> go (applyMap m $ actionMap e') es where
-		e' = applyMap m e
-
--- | Undo last action
-undo ∷ (EditAction Replace s, ApplyMap r) ⇒ EditM s r ()
-undo = do
-	us@(~(u:_)) ← gets (view $ history . undoStack)
-	unless (null us) $ do
-		_ ← run_ (u ^. actionBack)
-		modify (over (history . undoStack) tail)
-		modify (over (history . redoStack) (u :))
+	actionMap (Replace r w) = insertMap (r & regionLength .~ measure w) `mappend` cutMap r
+	perform (Replace r w) cts = cts & atRegion r .~ w
+	inversed (Replace r w) cts = Replace (r & regionLength .~ measure w) (cts ^. atRegion r)
 
-redo ∷ (EditAction Replace s, ApplyMap r) ⇒ EditM s r ()
-redo = do
-	rs@(~(r:_)) ← gets (view $ history . redoStack)
-	unless (null rs) $ do
-		_ ← run_ (r ^. action)
-		modify (over (history . redoStack) tail)
-		modify (over (history . undoStack) (r :))
+instance Editable s ⇒ EditAction Edit s where
+	replace rgn txt = Edit [replace rgn txt]
+	actionMap = foldr go mempty ∘ view replaces where
+		go r m = actionMap (over replaceRegion (applyMap m) r) `mappend` m
+	perform = snd ∘ foldr go (mempty, id) ∘ view replaces where
+		go r (m, fn) = (actionMap r' `mappend` m, perform r' ∘ fn) where
+			r' = over replaceRegion (applyMap m) r
+	inversed e@(Edit rs) cts = Edit [Replace (applyMap m r) (cts ^. atRegion r) | Replace r _ ← rs] where
+		m = actionMap e
src/Data/Text/Region/Types.hs view
@@ -1,17 +1,13 @@ {-# LANGUAGE TemplateHaskell, RankNTypes, TypeSynonymInstances, FlexibleInstances, OverloadedStrings, GeneralizedNewtypeDeriving, FlexibleContexts #-}
 
 module Data.Text.Region.Types (
-	Point(..), pointLine, pointColumn, Size, (.-.), (.+.),
+	Point(..), pointLine, pointColumn, pointRegion, Size, (.-.), (.+.),
 	Region(..), regionFrom, regionTo,
 	Map(..),
 	Contents, emptyContents,
 	concatCts, splitCts, splitted,
 	Editable(..), contents, by, measure,
-	Replace(..), replaceRegion, replaceWith, Chain(..), chain, Edit,
-	ActionIso(..), action, actionBack,
-	ActionStack(..), undoStack, redoStack, emptyStack,
-	EditState(..), editState, history, edited, regions,
-	EditM(..),
+	Replace(..), replaceRegion, replaceWith, Edit(..), replaces,
 
 	module Data.Group
 	) where
@@ -21,7 +17,6 @@ 
 import Control.Category
 import Control.Lens hiding ((.=))
-import Control.Monad.State
 import Data.Aeson
 import qualified Data.ByteString.Lazy.Char8 as L
 import Data.Group
@@ -37,6 +32,10 @@ 
 makeLenses ''Point
 
+-- | As empty region
+pointRegion ∷ Iso' Point Region
+pointRegion = iso (\p → Region p p) _regionFrom
+
 instance ToJSON Point where
 	toJSON (Point l c) = object ["line" .= l, "column" .= c]
 
@@ -66,7 +65,9 @@ 	| bl ≡ l = Point 0 (max 0 (c - bc))
 	| otherwise = Point 0 0
 
--- | Opposite to ".-.", @(pt .-. base) .+. base = pt@
+-- | Opposite to '.-.'
+--
+-- prop> (pt .-. base) .+. base = pt
 (.+.) ∷ Point → Point → Point
 (Point l c) .+. (Point bl bc)
 	| l ≡ 0 = Point bl (c + bc)
@@ -94,6 +95,7 @@ -- Combining this functions while edit, we get function, that maps regions from source data to edited one
 -- To get back function, we must also combine opposite actions, or we can represent actions as 'Iso'
 -- Same idea goes for modifying contents, represent each action as isomorphism and combine them together
+-- This works if we don't use overlapped regions
 newtype Map = Map { mapIso :: Iso' Region Region }
 
 instance Monoid Map where
@@ -179,75 +181,15 @@ instance (Editable s, ToJSON s) ⇒ Show (Replace s) where
 	show = L.unpack ∘ encode
 
--- | Chain of edit actions
-newtype Chain e s = Chain {
-	_chain ∷ [e s] } deriving (Eq, Show, Monoid)
-
-makeLenses ''Chain
-
-instance ToJSON (e s) ⇒ ToJSON (Chain e s) where
-	toJSON = toJSON ∘ _chain
-
-instance FromJSON (e s) ⇒ FromJSON (Chain e s) where
-	parseJSON = fmap Chain ∘ parseJSON
-
-type Edit s = Chain Replace s
-
--- | Some action with its inverse
-data ActionIso e = ActionIso {
-	_action ∷ e,
-	_actionBack ∷ e }
-
-makeLenses ''ActionIso
-
-instance Monoid e ⇒ Monoid (ActionIso e) where
-	mempty = ActionIso mempty mempty
-	ActionIso l l' `mappend` ActionIso r r' = ActionIso (l `mappend` r) (r' `mappend` l')
-
-instance Monoid e ⇒ Group (ActionIso e) where
-	invert (ActionIso f b) = ActionIso b f
-
-instance ToJSON e ⇒ ToJSON (ActionIso e) where
-	toJSON (ActionIso f b) = object ["fore" .= f, "back" .= b]
-
-instance FromJSON e ⇒ FromJSON (ActionIso e) where
-	parseJSON = withObject "action-iso" $ \v → ActionIso <$> v .: "fore" <*> v .: "back"
-
--- | Stack of undo/redo actions
-data ActionStack e = ActionStack {
-	_undoStack ∷ [ActionIso e],
-	_redoStack ∷ [ActionIso e] }
-
-makeLenses ''ActionStack
-
-instance ToJSON e ⇒ ToJSON (ActionStack e) where
-	toJSON (ActionStack u r) = object ["undo" .= u, "redo" .= r]
-
-instance FromJSON e ⇒ FromJSON (ActionStack e) where
-	parseJSON = withObject "action-stack" $ \v → ActionStack <$> v .: "undo" <*> v .: "redo"
-
-emptyStack ∷ ActionStack e
-emptyStack = ActionStack [] []
-
--- | Edit state
-data EditState s r = EditState {
-	-- | Edit history is stack of edit actions
-	_history ∷ ActionStack (Edit s),
-	-- | Currently edited data
-	_edited ∷ Contents s,
-	-- | Some region-based state, that will be updated on each edit
-	_regions ∷ r }
-
-makeLenses ''EditState
-
-instance (Editable s, ToJSON s, ToJSON r) ⇒ ToJSON (EditState s r) where
-	toJSON (EditState h e rs) = object ["history" .= h, "contents" .= view (from contents) e, "regions" .= rs ]
+-- | Edit is several replace actions, applied simultaneously, must not overlap
+newtype Edit s = Edit {
+	_replaces ∷ [Replace s] }
+		deriving (Eq, Show, Monoid)
 
-instance (Editable s, FromJSON s, FromJSON r) ⇒ FromJSON (EditState s r) where
-	parseJSON = withObject "edit-state" $ \v → EditState <$> v .: "history" <*> fmap (view contents) (v .: "contents") <*> v .: "regions"
+makeLenses ''Edit
 
--- | Make edit state for contents
-editState ∷ Editable s ⇒ s → r → EditState s r
-editState x = EditState emptyStack (x ^. contents)
+instance (Editable s, ToJSON s) ⇒ ToJSON (Edit s) where
+	toJSON = toJSON ∘ _replaces
 
-newtype EditM s r a = EditM { runEditM ∷ State (EditState s r) a } deriving (Applicative, Functor, Monad, MonadState (EditState s r))
+instance (Editable s, FromJSON s) ⇒ FromJSON (Edit s) where
+	parseJSON = fmap Edit ∘ parseJSON
tests/Test.hs view
@@ -5,7 +5,6 @@ import Prelude.Unicode
 
 import Control.Lens
-import Control.Monad.State
 import Data.Text.Region
 import Test.Hspec
 
@@ -29,20 +28,17 @@ 	describe "regions are updated" $ do
 		it "should delete correctly" $
 			apply (cut quux `mappend` cut bar) (by text) ≡ by "foo baz"
-	describe "editor monad" $ do
-		it "should perform undo/redo" $
-			(≡ text) $ edit_ text () $ do
-				runGroup [
-					cut bar,
-					replace quux (by nums),
-					paste start (by xxx)]
-				undo >> redo >> undo >> undo >> undo
+		it "should perform undo" $
+			let
+				act' = mconcat [cut bar, replace quux (by nums), paste start (by xxx)]
+				undo' = inversed act' (by text)
+			in
+			(apply undo' ∘ apply act') (by text) ≡ by text
 		it "should reverse text" $
-			(≡ reverse text) $ edit_ text (pt 0 (length text)) $ replicateM_ (length text) $ do
-				-- cut first letter and insert at caret
-				let
-					l = pt 0 0 `till` pt 0 1
-				c ← gets (view regions)
-				cts ← gets (view edited)
-				run $ paste c (view (atRegion l) cts)
-				run $ cut l
+			let
+				go 0 _ txt = txt
+				go n c txt = go (n - 1) (over pointRegion (update act') c) (apply act' txt) where
+					act' = mconcat [cut first, paste c (txt ^. atRegion first)]
+					first = pt 0 0 `till` pt 0 1
+			in
+			go (length text) (pt 0 (length text)) (by text) ≡ by (reverse text)
text-region.cabal view
@@ -1,5 +1,5 @@ name:                text-region
-version:             0.1.0.1
+version:             0.2.0.0
 synopsis:            Marking text regions
 description: Provides functions to update text region positions according to text edit actions
 homepage:            https://github.com/mvoidex/text-region
@@ -28,10 +28,10 @@     base-unicode-symbols >= 0.2,
     aeson >= 0.9,
     bytestring >= 0.10,
-    containers >= 0.5,
+    -- containers >= 0.5,
     groups >= 0.4.0,
     lens >= 4.12,
-    mtl >= 2.2,
+    -- mtl >= 2.2,
     text >= 1.2.1
 
 test-suite test
@@ -46,7 +46,7 @@     base-unicode-symbols >= 0.2,
     text-region,
     hspec,
-    containers >= 0.5,
+    -- containers >= 0.5,
     lens >= 4.12,
-    mtl >= 2.2,
+    -- mtl >= 2.2,
     text >= 1.2.1