func (l *AsyncList) Index() int {
	if len(l.scope) <= 0 {
		return 0
	}
	selected := l.scope[l.cursor]
	for i, item := range l.items {
		if item == selected {
			return i
		}
	}
	return NotFound
}
Index returns the index of the item currently selected inside the searched list.
package prompt
import (
	"context"
	"fmt"
	"reflect"
	"sort"
	"strings"
	"sync"
	"sync/atomic"
	"github.com/isacikgoz/fuzzy"
)
type AsyncList struct {
	itemsChan chan interface{}
	items     []interface{}
	scope     []interface{}
	buffer    []interface{}
	matches   sync.Map
	cursor    int  
	size      int  
	start     int
	find      string
	mx        sync.Mutex
	update    chan struct{}
	ctx       *searchContext
}
type searchContext struct {
	ctx      context.Context
	cancel   func()
	buffer   []fuzzy.Match
	progress int32
}
func newSearchContext(c context.Context) *searchContext {
	ctx, cancel := context.WithCancel(c)
	return &searchContext{
		ctx:    ctx,
		cancel: cancel,
		buffer: make([]fuzzy.Match, 0),
	}
}
func (c *searchContext) addBuffer(items ...fuzzy.Match) {
	c.buffer = append(c.buffer, items...)
}
func (c *searchContext) getBuffer() []fuzzy.Match {
	return c.buffer
}
func (c *searchContext) clearBuffer() {
	c.buffer = make([]fuzzy.Match, 0)
	return
}
func (c *searchContext) searchInProgress() bool {
	return atomic.LoadInt32(&c.progress) != 0
}
func (c *searchContext) stopSearch() {
	if atomic.LoadInt32(&c.progress) == 0 {
		return
	}
	c.cancel()
	c.clearBuffer()
	return
}
func (c *searchContext) startSearch() bool {
	c.ctx, c.cancel = context.WithCancel(context.Background())
	return atomic.CompareAndSwapInt32(&c.progress, 0, 1)
}
func NewAsyncList(items chan interface{}, size int) (*AsyncList, error) {
	if size < 1 {
		return nil, fmt.Errorf("list size %d must be greater than 0", size)
	}
	if items == nil || reflect.TypeOf(items).Kind() != reflect.Chan {
		return nil, fmt.Errorf("items %v is not a chan", items)
	}
	is := make([]interface{}, 0)
	list := &AsyncList{
		size:      size,
		items:     is,
		itemsChan: items,
		scope:     is,
		mx:        sync.Mutex{},
		update:    make(chan struct{}),
		buffer:    make([]interface{}, 0),
		ctx:       newSearchContext(context.Background()),
	}
	go func() {
		var flush int
		for val := range items {
			list.addBuffer(val)
			if flush < 4096 {
				flush++
				continue
			}
			list.flushBuffer()
			flush = 0
		}
		list.flushBuffer()
	}()
	return list, nil
}
func (l *AsyncList) flushBuffer() {
	l.mx.Lock()
	defer l.mx.Unlock()
	if len(l.buffer) == 0 {
		return
	}
	l.items = append(l.items, l.buffer...)
	l.scope = append(l.scope, l.buffer...)
	l.update <- struct{}{}
	l.buffer = make([]interface{}, 0)
}
func (l *AsyncList) addBuffer(items ...interface{}) {
	l.buffer = append(l.buffer, items...)
}
func (l *AsyncList) Prev() {
	if l.cursor > 0 {
		l.cursor--
	}
	if l.start > l.cursor {
		l.start = l.cursor
	}
}
func (l *AsyncList) Search(term string) {
	l.mx.Lock()
	defer l.mx.Unlock()
	term = strings.Trim(term, " ")
	l.cursor = 0
	l.start = 0
	l.find = term
	l.search(term)
}
func (l *AsyncList) CancelSearch() {
	l.cursor = 0
	l.start = 0
	l.scope = l.items
}
func (l *AsyncList) flushToScope(ctx *searchContext, fireUpdate bool) {
	defer ctx.clearBuffer()
	sort.Stable(fuzzy.Sortable(ctx.buffer))
	for _, match := range ctx.buffer {
		item := l.items[match.Index]
		l.scope = append(l.scope, item)
		l.matches.Store(item, match.MatchedIndexes)
	}
	if fireUpdate && l.update != nil {
		l.update <- struct{}{}
	}
}
func (l *AsyncList) search(term string) {
	if len(term) == 0 {
		l.scope = l.items
		return
	}
	l.ctx.stopSearch()
	l.matches = sync.Map{}
	l.scope = make([]interface{}, 0)
	l.ctx.startSearch()
	results := fuzzy.FindFrom(context.Background(), term, interfaceSource(l.items))
	go func() {
		var flush int
		var done bool
		for result := range results {
			if !l.ctx.searchInProgress() {
				break
			}
			l.ctx.addBuffer(result)
			if !done && flush == l.size {
				l.flushToScope(l.ctx, true)
				done = true
				continue
			}
			if flush < 16384 {
				flush++
				continue
			}
			l.flushToScope(l.ctx, false)
			flush = 0
		}
		l.flushToScope(l.ctx, true)
		l.ctx.clearBuffer()
	}()
}
func (l *AsyncList) Start() int {
	return l.start
}
func (l *AsyncList) SetStart(i int) {
	if i < 0 {
		i = 0
	}
	if i > l.cursor {
		l.start = l.cursor
	} else {
		l.start = i
	}
}
func (l *AsyncList) SetCursor(i int) {
	max := len(l.scope) - 1
	if i >= max {
		i = max
	}
	if i < 0 {
		i = 0
	}
	l.cursor = i
	if l.start > l.cursor {
		l.start = l.cursor
	} else if l.start+l.size <= l.cursor {
		l.start = l.cursor - l.size + 1
	}
}
func (l *AsyncList) Next() {
	max := len(l.scope) - 1
	if l.cursor < max {
		l.cursor++
	}
	if l.start+l.size <= l.cursor {
		l.start = l.cursor - l.size + 1
	}
}
func (l *AsyncList) PageUp() {
	start := l.start - l.size
	if start < 0 {
		l.start = 0
	} else {
		l.start = start
	}
	cursor := l.start
	if cursor < l.cursor {
		l.cursor = cursor
	}
}
func (l *AsyncList) PageDown() {
	start := l.start + l.size
	max := len(l.scope) - l.size
	switch {
	case len(l.scope) < l.size:
		l.start = 0
	case start > max:
		l.start = max
	default:
		l.start = start
	}
	cursor := l.start
	if cursor == l.cursor {
		l.cursor = len(l.scope) - 1
	} else if cursor > l.cursor {
		l.cursor = cursor
	}
}
func (l *AsyncList) CanPageDown() bool {
	max := len(l.scope)
	return l.start+l.size < max
}
func (l *AsyncList) CanPageUp() bool {
	return l.start > 0
}