Files
2026-01-24 11:17:24 +01:00

163 lines
4.5 KiB
Go

package fuzzyfinder
import (
"context"
"sync"
)
type opt struct {
mode mode
previewFunc func(i, width, height int) string
multi bool
hotReload bool
hotReloadLock sync.Locker
promptString string
header string
beginAtTop bool
context context.Context
query string
selectOne bool
preselected func(i int) bool
}
type mode int
const (
// ModeSmart enables a smart matching. It is the default matching mode.
// At the beginning, matching mode is ModeCaseInsensitive, but it switches
// over to ModeCaseSensitive if an upper case character is inputted.
ModeSmart mode = iota
// ModeCaseSensitive enables a case-sensitive matching.
ModeCaseSensitive
// ModeCaseInsensitive enables a case-insensitive matching.
ModeCaseInsensitive
)
var defaultOption = opt{
promptString: "> ",
hotReloadLock: &sync.Mutex{}, // this won't resolve the race condition but avoid nil panic
preselected: func(i int) bool { return false },
}
// Option represents available fuzzy-finding options.
type Option func(*opt)
// WithMode specifies a matching mode. The default mode is ModeSmart.
func WithMode(m mode) Option {
return func(o *opt) {
o.mode = m
}
}
// WithPreviewWindow enables to display a preview for the selected item.
// The argument f receives i, width and height. i is the same as Find's one.
// width and height are the size of the terminal so that you can use these to adjust
// a preview content. Note that width and height are calculated as a rune-based length.
//
// If there is no selected item, previewFunc passes -1 to previewFunc.
//
// If f is nil, the preview feature is disabled.
func WithPreviewWindow(f func(i, width, height int) string) Option {
return func(o *opt) {
o.previewFunc = f
}
}
// WithHotReload reloads the passed slice automatically when some entries are appended.
// The caller must pass a pointer of the slice instead of the slice itself.
//
// Deprecated: use WithHotReloadLock instead.
func WithHotReload() Option {
return func(o *opt) {
o.hotReload = true
}
}
// WithHotReloadLock reloads the passed slice automatically when some entries are appended.
// The caller must pass a pointer of the slice instead of the slice itself.
// The caller must pass a RLock which is used to synchronize access to the slice.
// The caller MUST NOT lock in the itemFunc passed to Find / FindMulti because it will be locked by the fuzzyfinder.
// If used together with WithPreviewWindow, the caller MUST use the RLock only in the previewFunc passed to WithPreviewWindow.
func WithHotReloadLock(lock sync.Locker) Option {
return func(o *opt) {
o.hotReload = true
o.hotReloadLock = lock
}
}
type cursorPosition int
const (
CursorPositionBottom cursorPosition = iota
CursorPositionTop
)
// WithCursorPosition sets the initial position of the cursor
//
// If Find is called with WithCursorPosition and WithPreselected, the cursor will be positioned at the first preselected item.
func WithCursorPosition(position cursorPosition) Option {
return func(o *opt) {
switch position {
case CursorPositionTop:
o.beginAtTop = true
case CursorPositionBottom:
o.beginAtTop = false
}
}
}
// WithPromptString changes the prompt string. The default value is "> ".
func WithPromptString(s string) Option {
return func(o *opt) {
o.promptString = s
}
}
// withMulti enables to select multiple items by tab key.
func withMulti() Option {
return func(o *opt) {
o.multi = true
}
}
// WithHeader enables to set the header.
func WithHeader(s string) Option {
return func(o *opt) {
o.header = s
}
}
// WithContext enables closing the fuzzy finder from parent.
func WithContext(ctx context.Context) Option {
return func(o *opt) {
o.context = ctx
}
}
// WithQuery enables to set the initial query.
func WithQuery(s string) Option {
return func(o *opt) {
o.query = s
}
}
// WithQuery enables to set the initial query.
func WithSelectOne() Option {
return func(o *opt) {
o.selectOne = true
}
}
// WithPreselected enables to specify which items should be preselected.
// The argument f is a function that returns true for items that should be preselected.
// i is the same index value passed to itemFunc in Find or FindMulti.
// This option is effective in both Find and FindMulti, but in Find mode only
// the first preselected item will be considered.
//
// If Find is called with WithCursorPosition and WithPreselected, the cursor will be positioned at the first preselected item.
func WithPreselected(f func(i int) bool) Option {
return func(o *opt) {
o.preselected = f
}
}