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  }