package internal import "time" // History stores a stack of items, always keeping the bottom-most one. It must // be created using the NewHistory constructor, otherwise it will be invalid. type History[T comparable] struct { max int stack []T topIndex int topTime time.Time } // NewHistory creates a new History. The initial item will be on the bottom, and // it will remain there until the History overflows and chooses the item after // it to be the initial item. func NewHistory[T comparable] (initial T, max int) *History[T] { return &History[T] { max: max, stack: []T { initial }, } } // Top returns the most recent item. func (this *History[T]) Top () T { return this.stack[this.topIndex] } // Swap replaces the most recent item with another. func (this *History[T]) Swap (item T) { this.topTime = time.Now() this.SwapSilently(item) } // SwapSilently replaces the most recent item with another without updating the // time. func (this *History[T]) SwapSilently (item T) { this.stack[this.topIndex] = item } // Push pushes a new item onto the stack. If the stack overflows (becomes bigger // than the specified max value), the initial item is removed and the one on top // of it takes its place. func (this *History[T]) Push (item T) { this.topTime = time.Now() if this.Top() != item { this.topIndex ++ this.stack = append(this.stack[:this.topIndex], item) } if len(this.stack) > this.max { this.stack = this.stack[1:] } } // PushWeak replaces the most recent item if it was added recently (sooner than // specified by minAge), and will otherwise push the item normally. If the // history was popped or cleared beforehand, the item will always be pushed // normally. This is intended to be used for things such as keystrokes. func (this *History[T]) PushWeak (item T, minAge time.Duration) { if time.Since(this.topTime) > minAge { this.Push(item) } else { this.Swap(item) } } // Redo undoes an Undo operation and returns the resulting top of the stack. func (this *History[T]) Redo () T { if this.topIndex < len(this.stack) - 1 { this.topIndex ++ } return this.Top() } // Undo removes the most recent item and returns what was under it. If there is // only one item (the initial item), it will kept and returned. func (this *History[T]) Undo () T { this.topTime = time.Time { } if this.topIndex > 0 { this.topIndex -- } return this.Top() } // Clear removes all items except for the initial one. func (this *History[T]) Clear () { this.topTime = time.Time { } this.stack = this.stack[:1] this.topIndex = 0 }