mattermost-community-enterp.../vendor/github.com/isacikgoz/prompt/sync_list.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
Full Mattermost server source with integrated Community Enterprise features.
Includes vendor directory for offline/air-gapped builds.

Structure:
- enterprise-impl/: Enterprise feature implementations
- enterprise-community/: Init files that register implementations
- enterprise/: Bridge imports (community_imports.go)
- vendor/: All dependencies for offline builds

Build (online):
  go build ./cmd/mattermost

Build (offline/air-gapped):
  go build -mod=vendor ./cmd/mattermost

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:59:07 +09:00

268 lines
5.5 KiB
Go

// Package prompt is a slightly modified version of promptui's list. The original
// version can be found at https://github.com/manifoldco/promptui
// A little copying is better than a little dependency. - Go proverbs.
package prompt
import (
"context"
"fmt"
"reflect"
"sort"
"strings"
"github.com/isacikgoz/fuzzy"
)
type interfaceSource []interface{}
func (is interfaceSource) String(i int) string { return fmt.Sprint(is[i]) }
func (is interfaceSource) Len() int { return len(is) }
// NotFound is an index returned when no item was selected.
const NotFound = -1
// SyncList holds a collection of items that can be displayed with an N number of
// visible items. The list can be moved up, down by one item of time or an
// entire page (ie: visible size). It keeps track of the current selected item.
type SyncList struct {
items []interface{}
scope []interface{}
matches map[interface{}][]int
cursor int // cursor holds the index of the current selected item
size int // size is the number of visible options
start int
find string
}
// NewList creates and initializes a list of searchable items. The items attribute must be a slice type.
func NewList(items interface{}, size int) (*SyncList, 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.Slice {
return nil, fmt.Errorf("items %v is not a slice", items)
}
slice := reflect.ValueOf(items)
values := make([]interface{}, slice.Len())
for i := range values {
item := slice.Index(i)
values[i] = item.Interface()
}
return &SyncList{
size: size,
items: values,
scope: values,
}, nil
}
// Prev moves the visible list back one item.
func (l *SyncList) Prev() {
if l.cursor > 0 {
l.cursor--
}
if l.start > l.cursor {
l.start = l.cursor
}
}
// Search allows the list to be filtered by a given term.
func (l *SyncList) Search(term string) {
term = strings.Trim(term, " ")
l.cursor = 0
l.start = 0
l.find = term
l.search(term)
}
// CancelSearch stops the current search and returns the list to its original order.
func (l *SyncList) CancelSearch() {
l.cursor = 0
l.start = 0
l.scope = l.items
}
func (l *SyncList) search(term string) {
if len(term) == 0 {
l.scope = l.items
return
}
l.matches = make(map[interface{}][]int)
matches := fuzzy.FindFrom(context.Background(), term, interfaceSource(l.items))
results := make([]fuzzy.Match, 0)
for match := range matches {
results = append(results, match)
}
sort.Stable(fuzzy.Sortable(results))
l.scope = make([]interface{}, 0)
for _, r := range results {
item := l.items[r.Index]
l.scope = append(l.scope, item)
l.matches[item] = r.MatchedIndexes
}
}
// Start returns the current render start position of the list.
func (l *SyncList) Start() int {
return l.start
}
// SetStart sets the current scroll position. Values out of bounds will be clamped.
func (l *SyncList) SetStart(i int) {
if i < 0 {
i = 0
}
if i > l.cursor {
l.start = l.cursor
} else {
l.start = i
}
}
// SetCursor sets the position of the cursor in the list. Values out of bounds will
// be clamped.
func (l *SyncList) 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
}
}
// Next moves the visible list forward one item.
func (l *SyncList) 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
}
}
// PageUp moves the visible list backward by x items. Where x is the size of the
// visible items on the list.
func (l *SyncList) 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
}
}
// PageDown moves the visible list forward by x items. Where x is the size of
// the visible items on the list.
func (l *SyncList) 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
}
}
// CanPageDown returns whether a list can still PageDown().
func (l *SyncList) CanPageDown() bool {
max := len(l.scope)
return l.start+l.size < max
}
// CanPageUp returns whether a list can still PageUp().
func (l *SyncList) CanPageUp() bool {
return l.start > 0
}
// Index returns the index of the item currently selected inside the searched list.
func (l *SyncList) 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
}
// Items returns a slice equal to the size of the list with the current visible
// items and the index of the active item in this list.
func (l *SyncList) Items() ([]interface{}, int) {
var result []interface{}
max := len(l.scope)
end := l.start + l.size
if end > max {
end = max
}
active := NotFound
for i, j := l.start, 0; i < end; i, j = i+1, j+1 {
if l.cursor == i {
active = j
}
result = append(result, l.scope[i])
}
return result, active
}
func (l *SyncList) Size() int {
return l.size
}
func (l *SyncList) Cursor() int {
return l.cursor
}
func (l *SyncList) Matches(item interface{}) []int {
return l.matches[item]
}
func (l *SyncList) Update() chan struct{} {
return nil
}