1package main 2 3import ( 4 "errors" 5 "fmt" 6 "sync" 7 "unsafe" 8) 9 10// Undo is a struct that can store several states of the editor and position 11type Undo struct { 12 index int 13 size int 14 editorCopies []Editor 15 editorLineCopies []map[int][]rune 16 editorPositionCopies []Position 17 mut *sync.RWMutex 18 maxMemoryUse uint64 // can be <= 0 to not check for memory use 19} 20 21const ( 22 // number of undo actions possible to store in the circular buffer 23 defaultUndoCount = 4096 24 25 // maximum amount of memory the undo buffers can use before re-using buffers, 0 to disable 26 defaultUndoMemory = 0 // 32 * 1024 * 1024 27) 28 29var ( 30 // Circular undo buffer with room for N actions, change false to true to check for too limit memory use 31 undo = NewUndo(defaultUndoCount, defaultUndoMemory) 32 33 // Save the contents of one switch. 34 // Used when switching between a .c or .cpp file to the corresponding .h file. 35 switchBuffer = NewUndo(1, defaultUndoMemory) 36 37 // Save a copy of the undo stack when switching between files 38 switchUndoBackup = NewUndo(defaultUndoCount, defaultUndoMemory) 39) 40 41// NewUndo takes arguments that are only for initializing the undo buffers. 42// The *Position and *vt100.Canvas is used only as a default values for the elements in the undo buffers. 43func NewUndo(size int, maxMemoryUse uint64) *Undo { 44 return &Undo{0, size, make([]Editor, size), make([]map[int][]rune, size), make([]Position, size), &sync.RWMutex{}, maxMemoryUse} 45} 46 47func lineMapMemoryFootprint(m map[int][]rune) uint64 { 48 var sum uint64 49 for _, v := range m { 50 sum += uint64(cap(v)) 51 } 52 return sum 53} 54 55// MemoryFootprint returns how much memory one Undo struct is using 56// TODO: Check if the size of the slices that contains structs are correct 57func (u *Undo) MemoryFootprint() uint64 { 58 var sum uint64 59 for _, m := range u.editorLineCopies { 60 sum += lineMapMemoryFootprint(m) 61 } 62 sum += uint64(unsafe.Sizeof(u.index)) 63 sum += uint64(unsafe.Sizeof(u.size)) 64 sum += uint64(unsafe.Sizeof(u.editorCopies)) 65 sum += uint64(unsafe.Sizeof(u.editorPositionCopies)) 66 sum += uint64(unsafe.Sizeof(u.mut)) 67 sum += uint64(unsafe.Sizeof(u.maxMemoryUse)) 68 return sum 69} 70 71// Snapshot will store a snapshot, and move to the next position in the circular buffer 72func (u *Undo) Snapshot(e *Editor) { 73 u.mut.Lock() 74 defer u.mut.Unlock() 75 76 u.editorCopies[u.index] = *e 77 u.editorLineCopies[u.index] = e.CopyLines() 78 u.editorPositionCopies[u.index] = e.pos 79 80 // Go forward 1 step in the circular buffer 81 u.index++ 82 // Circular buffer wrap 83 if u.index >= u.size { 84 u.index = 0 85 } 86 87 // If the undo buffer uses too much memory, reduce the size to 10 88 if u.maxMemoryUse > 0 && u.MemoryFootprint() > u.maxMemoryUse { 89 newSize := 10 90 91 smallest := newSize 92 if u.size < smallest { 93 smallest = u.size 94 } 95 96 newUndo := NewUndo(newSize, u.maxMemoryUse) 97 newUndo.index = u.index 98 if newUndo.index >= newUndo.size { 99 newUndo.index = 0 100 } 101 newUndo.mut = u.mut 102 103 u.mut.Lock() 104 defer u.mut.Unlock() 105 106 // Copy over the contents to the new undo struct 107 offset := u.index 108 for i := 0; i < smallest; i++ { 109 copyFromPos := i + offset 110 if copyFromPos > u.size { 111 copyFromPos -= u.size 112 } 113 copyToPos := i 114 fmt.Println(copyFromPos, copyToPos) 115 116 newUndo.editorCopies[copyToPos] = u.editorCopies[copyFromPos] 117 newUndo.editorLineCopies[copyToPos] = u.editorLineCopies[copyFromPos] 118 newUndo.editorPositionCopies[copyToPos] = u.editorPositionCopies[copyFromPos] 119 } 120 121 // Replace the undo struct 122 *u = *newUndo 123 124 // Adjust the index after the size has been changed 125 if u.index >= u.size { 126 u.index = 0 127 } 128 } 129 130} 131 132// Restore will restore a previous snapshot, and move to the previous position in the circular buffer 133func (u *Undo) Restore(e *Editor) error { 134 u.mut.Lock() 135 defer u.mut.Unlock() 136 137 // Go back 1 step in the circular buffer 138 u.index-- 139 // Circular buffer wrap 140 if u.index < 0 { 141 u.index = u.size - 1 142 } 143 144 // Restore the state from this index, if there is something there 145 if lines := u.editorLineCopies[u.index]; len(lines) > 0 { 146 147 *e = u.editorCopies[u.index] 148 e.lines = lines 149 e.pos = u.editorPositionCopies[u.index] 150 151 return nil 152 } 153 return errors.New("no undo state at this index") 154} 155 156// Index will return the current undo index, in the undo buffers 157func (u *Undo) Index() int { 158 return u.index 159} 160 161// Len will return the current number of stored undo snapshots. 162// This is the same as the index int that points to the next free slot. 163func (u *Undo) Len() int { 164 return u.index 165} 166