github.com/jmigpin/editor@v1.6.0/util/iout/iorw/rwundo/history.go (about) 1 package rwundo 2 3 import ( 4 "container/list" 5 "fmt" 6 "sync" 7 8 "github.com/jmigpin/editor/util/iout/iorw/rwedit" 9 ) 10 11 ////godebug:annotatefile 12 13 type History struct { 14 maxLen int // max elements in list // TODO: max data size 15 hlist *HList 16 ugroup struct { // undo group 17 sync.Mutex 18 ohlist *HList // original list 19 c rwedit.SimpleCursor 20 } 21 } 22 23 func NewHistory(maxLen int) *History { 24 h := &History{hlist: NewHList(), maxLen: maxLen} 25 return h 26 } 27 28 //---------- 29 30 func (h *History) Append(edits *Edits) { h.hlist.Append(edits, h.maxLen) } 31 func (h *History) Clear() { h.hlist.Clear() } 32 func (h *History) ClearUndones() { h.hlist.ClearUndones() } 33 34 //func (h *History) MergeNDoneBack(n int) { h.hlist.MergeNDoneBack(n) } 35 36 //---------- 37 38 func (h *History) UndoRedo(redo, peek bool) (*Edits, bool) { 39 // the call to undo could be inside an undogroup, use the original list; usually this is ok since the only operations should be undo/redo, but if other write operations are done while on this undogroup, there could be undefined behaviour (programmer responsability) 40 h.ugroup.Lock() 41 defer h.ugroup.Unlock() 42 hl := h.hlist 43 if h.ugroup.ohlist != nil { 44 hl = h.ugroup.ohlist 45 } 46 47 if redo { 48 return hl.Redo(peek) 49 } else { 50 return hl.Undo(peek) 51 } 52 } 53 54 //---------- 55 56 func (h *History) BeginUndoGroup(c rwedit.SimpleCursor) { 57 h.ugroup.Lock() 58 defer h.ugroup.Unlock() 59 if h.ugroup.ohlist != nil { 60 panic("history undo group already set") 61 } 62 63 // replace hlist 64 h.ugroup.ohlist = h.hlist 65 h.hlist = NewHList() 66 67 // keep cursordata 68 h.ugroup.c = c 69 } 70 71 func (h *History) EndUndoGroup(c rwedit.SimpleCursor) { 72 h.ugroup.Lock() 73 defer h.ugroup.Unlock() 74 if h.ugroup.ohlist == nil { 75 panic("history undo group is not set") 76 } 77 defer func() { h.ugroup.ohlist = nil }() 78 79 // merge all, should then have either 0 or 1 element 80 h.hlist.mergeToDoneBack(h.hlist.list.Front()) 81 if h.hlist.list.Len() > 1 { 82 panic(fmt.Sprintf("history undo group merge: %v", h.hlist.list.Len())) 83 } 84 85 if h.hlist.list.Len() == 1 { 86 // overwrite undogroup cursors - allows a setbytes to not end with the full content selected since it overwrites all 87 edits := h.hlist.list.Front().Value.(*Edits) 88 edits.preCursor = h.ugroup.c 89 edits.postCursor = c 90 // append undogroup elements to the original list 91 h.ugroup.ohlist.Append(edits, h.maxLen) 92 } 93 94 // restore original list 95 h.hlist = h.ugroup.ohlist 96 } 97 98 //---------- 99 100 type HList struct { 101 list *list.List 102 undone *list.Element 103 } 104 105 func NewHList() *HList { 106 return &HList{list: list.New()} 107 } 108 109 //---------- 110 111 func (hl *HList) DoneBack() *list.Element { 112 if hl.undone != nil { 113 return hl.undone.Prev() 114 } 115 return hl.list.Back() 116 } 117 118 //---------- 119 120 func (hl *HList) Append(edits *Edits, maxLen int) { 121 if edits.Empty() { 122 return 123 } 124 hl.ClearUndones() // make back clear 125 hl.list.PushBack(edits) // add to the back 126 hl.clearOlds(maxLen) 127 tryToMergeLastTwoEdits(hl) // simplify history 128 } 129 130 //---------- 131 132 func (hl *HList) Undo(peek bool) (*Edits, bool) { 133 u := hl.DoneBack() 134 if u == nil { 135 return nil, false 136 } 137 if !peek { 138 hl.undone = u 139 } 140 return u.Value.(*Edits), true 141 } 142 143 func (hl *HList) Redo(peek bool) (*Edits, bool) { 144 u := hl.undone 145 if u == nil { 146 return nil, false 147 } 148 if !peek { 149 hl.undone = hl.undone.Next() 150 } 151 return u.Value.(*Edits), true 152 } 153 154 //---------- 155 156 func (hl *HList) Clear() { 157 hl.list = list.New() 158 hl.undone = nil 159 } 160 161 func (hl *HList) ClearUndones() { 162 for e := hl.undone; e != nil; { 163 u := e.Next() 164 hl.list.Remove(e) 165 e = u 166 } 167 hl.undone = nil 168 } 169 170 func (hl *HList) clearOlds(maxLen int) { 171 for hl.list.Len() > maxLen { 172 e := hl.list.Front() 173 if e == hl.undone { 174 break 175 } 176 hl.list.Remove(e) 177 } 178 } 179 180 //---------- 181 182 func (hl *HList) mergeToDoneBack(elem *list.Element) { 183 for hl.mergeNextNotUndone(elem) { 184 } 185 } 186 func (hl *HList) mergeNextNotUndone(elem *list.Element) bool { 187 if elem == nil { 188 return false 189 } 190 if elem == hl.undone { 191 return false 192 } 193 edits := elem.Value.(*Edits) 194 next := elem.Next() 195 if next == nil { 196 return false 197 } 198 nextEdits := next.Value.(*Edits) 199 edits.MergeEdits(nextEdits) 200 hl.list.Remove(next) 201 return true 202 } 203 204 //func (hl *HList) MergeNDoneBack(n int) { 205 // e := hl.DoneBack() 206 // for ; n > 0 && e != nil; e = e.Prev() { 207 // n-- 208 // } 209 // hl.mergeToDoneBack(e) 210 //} 211 212 //---------- 213 214 func (hl *HList) NDoneBack(n int) ([]*Edits, []*list.Element) { 215 b := hl.DoneBack() 216 w := []*Edits{} 217 u := []*list.Element{} 218 for e := b; e != nil && n > 0; e = e.Prev() { 219 edits := e.Value.(*Edits) 220 w = append(w, edits) 221 u = append(u, e) 222 n-- 223 } 224 // reverse append order 225 l := len(w) 226 for i := 0; i < l/2; i++ { 227 k := l - 1 - i 228 w[i], w[k] = w[k], w[i] 229 u[i], u[k] = u[k], u[i] 230 } 231 return w, u 232 }