diff --git a/edit.go b/edit.go index 1ced5ab..b60815d 100644 --- a/edit.go +++ b/edit.go @@ -7,6 +7,7 @@ package backend import ( "fmt" + "github.com/limetext/backend/render" "github.com/limetext/text" ) @@ -17,14 +18,16 @@ type ( // // Think of it a bit like an SQL transaction. Edit struct { - invalid bool - composite text.CompositeAction - savedSel text.RegionSet - savedCount int - command string - args Args - v *View - bypassUndo bool + invalid bool + composite text.CompositeAction + savedSel text.RegionSet + savedRegs render.ViewRegionMap + appliedRegs render.ViewRegionMap + savedCount int + command string + args Args + v *View + bypassUndo bool } ) @@ -32,6 +35,7 @@ func newEdit(v *View) *Edit { ret := &Edit{ v: v, savedCount: v.ChangeCount(), + savedRegs: v.cloneRegions(), } for _, r := range v.Sel().Regions() { ret.savedSel.Add(r) @@ -47,13 +51,41 @@ func (e *Edit) String() string { // Applies the action of this Edit object. Should typically not be manually called. func (e *Edit) Apply() { e.composite.Apply() + if e.appliedRegs != nil { + e.v.restoreRegions(e.appliedRegs) + } } // Reverses the application of this Edit object. Should typically not be manually called. func (e *Edit) Undo() { e.composite.Undo() + if e.savedRegs != nil { + e.v.restoreRegions(e.savedRegs) + } e.v.Sel().Clear() for _, r := range e.savedSel.Regions() { e.v.Sel().Add(r) } } + +func cloneViewRegions(regions render.ViewRegionMap) render.ViewRegionMap { + ret := make(render.ViewRegionMap, len(regions)) + for key, value := range regions { + ret[key] = *value.Clone() + } + return ret +} + +func (v *View) cloneRegions() render.ViewRegionMap { + v.lock.Lock() + defer v.lock.Unlock() + + return cloneViewRegions(v.regions) +} + +func (v *View) restoreRegions(regions render.ViewRegionMap) { + v.lock.Lock() + defer v.lock.Unlock() + + v.regions = cloneViewRegions(regions) +} diff --git a/undo.go b/undo.go index d8085e8..a68a721 100644 --- a/undo.go +++ b/undo.go @@ -121,6 +121,7 @@ func (us *UndoStack) GlueFrom(mark int) { } e.v = us.actions[mark].v e.savedSel.AddAll(us.actions[mark].savedSel.Regions()) + e.savedRegs = cloneViewRegions(us.actions[mark].savedRegs) entries := make([]entry, us.position-mark) for i := range entries { @@ -128,6 +129,9 @@ func (us *UndoStack) GlueFrom(mark int) { entries[i].name = a.command entries[i].args = a.args e.composite.Add(a) + if a.appliedRegs != nil { + e.appliedRegs = cloneViewRegions(a.appliedRegs) + } } us.position = mark us.actions = us.actions[:mark+1] diff --git a/view.go b/view.go index bfa4298..5ac58ad 100644 --- a/view.go +++ b/view.go @@ -431,6 +431,7 @@ func (v *View) EndEdit(edit *Edit) { for j := len(v.editstack) - 1; j >= i; j-- { current_edit := v.editstack[j] current_edit.invalid = true + current_edit.appliedRegs = v.cloneRegions() sel_same := reflect.DeepEqual(*v.Sel(), current_edit.savedSel) buf_same := v.ChangeCount() == current_edit.savedCount eq := (sel_same && buf_same && current_edit.composite.Len() == 0) diff --git a/view_test.go b/view_test.go index ffb926c..50c1d79 100644 --- a/view_test.go +++ b/view_test.go @@ -13,6 +13,7 @@ import ( "testing" "time" + "github.com/limetext/backend/render" "github.com/limetext/text" "github.com/limetext/util" ) @@ -146,6 +147,34 @@ func TestErase(t *testing.T) { } } +func TestUndoRestoresRegions(t *testing.T) { + w := GetEditor().NewWindow() + defer w.Close() + + v := w.NewFile() + defer func() { + v.SetScratch(true) + v.Close() + }() + + edit := v.BeginEdit() + v.Insert(edit, 0, "abcdefghij") + v.EndEdit(edit) + + expected := []text.Region{{A: 1, B: 3}, {A: 6, B: 9}} + v.AddRegions("test.regions", expected, "comment", "", render.DEFAULT) + + edit = v.BeginEdit() + v.Erase(edit, text.Region{A: 0, B: v.Size()}) + v.EndEdit(edit) + + v.undoStack.Undo(true) + + if regions := v.GetRegions("test.regions"); !reflect.DeepEqual(regions, expected) { + t.Errorf("expected regions %v after undo, got %v", expected, regions) + } +} + func TestSaveAsNewFile(t *testing.T) { tests := []struct { text string