180 lines
3.9 KiB
Go
180 lines
3.9 KiB
Go
|
package marker
|
||
|
|
||
|
// TODO: fix headers for message that are nil
|
||
|
|
||
|
// Marker provides the interface for the marking behavior of messages
|
||
|
type Marker interface {
|
||
|
Mark(uint32)
|
||
|
Unmark(uint32)
|
||
|
ToggleMark(uint32)
|
||
|
Remark()
|
||
|
Marked() []uint32
|
||
|
IsMarked(uint32) bool
|
||
|
ToggleVisualMark()
|
||
|
UpdateVisualMark()
|
||
|
ClearVisualMark()
|
||
|
}
|
||
|
|
||
|
// UIDProvider provides the underlying uids and the selected message index
|
||
|
type UIDProvider interface {
|
||
|
Uids() []uint32
|
||
|
SelectedIndex() int
|
||
|
}
|
||
|
|
||
|
type controller struct {
|
||
|
uidProvider UIDProvider
|
||
|
marked map[uint32]struct{}
|
||
|
lastMarked map[uint32]struct{}
|
||
|
visualStartUID uint32
|
||
|
visualMarkMode bool
|
||
|
}
|
||
|
|
||
|
// New returns a new Marker
|
||
|
func New(up UIDProvider) Marker {
|
||
|
return &controller{
|
||
|
uidProvider: up,
|
||
|
marked: make(map[uint32]struct{}),
|
||
|
lastMarked: make(map[uint32]struct{}),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Mark markes the uid as marked
|
||
|
func (mc *controller) Mark(uid uint32) {
|
||
|
if mc.visualMarkMode {
|
||
|
// visual mode has override, bogus input from user
|
||
|
return
|
||
|
}
|
||
|
mc.marked[uid] = struct{}{}
|
||
|
}
|
||
|
|
||
|
// Unmark unmarks the uid
|
||
|
func (mc *controller) Unmark(uid uint32) {
|
||
|
if mc.visualMarkMode {
|
||
|
// user probably wanted to clear the visual marking
|
||
|
mc.ClearVisualMark()
|
||
|
return
|
||
|
}
|
||
|
delete(mc.marked, uid)
|
||
|
}
|
||
|
|
||
|
// Remark restores the previous marks
|
||
|
func (mc *controller) Remark() {
|
||
|
mc.marked = mc.lastMarked
|
||
|
}
|
||
|
|
||
|
// ToggleMark toggles the marked state for the given uid
|
||
|
func (mc *controller) ToggleMark(uid uint32) {
|
||
|
if mc.visualMarkMode {
|
||
|
// visual mode has override, bogus input from user
|
||
|
return
|
||
|
}
|
||
|
if mc.IsMarked(uid) {
|
||
|
mc.Unmark(uid)
|
||
|
} else {
|
||
|
mc.Mark(uid)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// resetMark removes the marking from all messages
|
||
|
func (mc *controller) resetMark() {
|
||
|
mc.lastMarked = mc.marked
|
||
|
mc.marked = make(map[uint32]struct{})
|
||
|
}
|
||
|
|
||
|
// removeStaleUID removes uids that are no longer presents in the UIDProvider
|
||
|
func (mc *controller) removeStaleUID() {
|
||
|
for mark := range mc.marked {
|
||
|
present := false
|
||
|
for _, uid := range mc.uidProvider.Uids() {
|
||
|
if mark == uid {
|
||
|
present = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if !present {
|
||
|
delete(mc.marked, mark)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// IsMarked checks whether the given uid has been marked
|
||
|
func (mc *controller) IsMarked(uid uint32) bool {
|
||
|
_, marked := mc.marked[uid]
|
||
|
return marked
|
||
|
}
|
||
|
|
||
|
// Marked returns the uids of all marked messages
|
||
|
func (mc *controller) Marked() []uint32 {
|
||
|
mc.removeStaleUID()
|
||
|
marked := make([]uint32, len(mc.marked))
|
||
|
i := 0
|
||
|
for uid := range mc.marked {
|
||
|
marked[i] = uid
|
||
|
i++
|
||
|
}
|
||
|
return marked
|
||
|
}
|
||
|
|
||
|
// ToggleVisualMark enters or leaves the visual marking mode
|
||
|
func (mc *controller) ToggleVisualMark() {
|
||
|
mc.visualMarkMode = !mc.visualMarkMode
|
||
|
if mc.visualMarkMode {
|
||
|
// just entered visual mode, reset whatever marking was already done
|
||
|
mc.resetMark()
|
||
|
uids := mc.uidProvider.Uids()
|
||
|
if idx := mc.uidProvider.SelectedIndex(); idx >= 0 && idx < len(uids) {
|
||
|
mc.visualStartUID = uids[idx]
|
||
|
mc.marked[mc.visualStartUID] = struct{}{}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ClearVisualMark leaves the visual marking mode and resets any marking
|
||
|
func (mc *controller) ClearVisualMark() {
|
||
|
mc.resetMark()
|
||
|
mc.visualMarkMode = false
|
||
|
mc.visualStartUID = 0
|
||
|
}
|
||
|
|
||
|
// UpdateVisualMark updates the index with the currently selected message
|
||
|
func (mc *controller) UpdateVisualMark() {
|
||
|
if !mc.visualMarkMode {
|
||
|
// nothing to do
|
||
|
return
|
||
|
}
|
||
|
startIdx := mc.visualStartIdx()
|
||
|
if startIdx < 0 {
|
||
|
// something deleted the startuid, abort the marking process
|
||
|
mc.ClearVisualMark()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
selectedIdx := mc.uidProvider.SelectedIndex()
|
||
|
if selectedIdx < 0 {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
uids := mc.uidProvider.Uids()
|
||
|
|
||
|
var visUids []uint32
|
||
|
if selectedIdx > startIdx {
|
||
|
visUids = uids[startIdx : selectedIdx+1]
|
||
|
} else {
|
||
|
visUids = uids[selectedIdx : startIdx+1]
|
||
|
}
|
||
|
mc.resetMark()
|
||
|
for _, uid := range visUids {
|
||
|
mc.marked[uid] = struct{}{}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// returns the index of needle in haystack or -1 if not found
|
||
|
func (mc *controller) visualStartIdx() int {
|
||
|
for idx, u := range mc.uidProvider.Uids() {
|
||
|
if u == mc.visualStartUID {
|
||
|
return idx
|
||
|
}
|
||
|
}
|
||
|
return -1
|
||
|
}
|