diff --git a/fzf.go b/fzf.go index 404b78a..add6c4d 100644 --- a/fzf.go +++ b/fzf.go @@ -13,7 +13,9 @@ var defaultFindOption = findOption{ // Fuzzy Finder. type FZF struct { - option *option + option *option + model *model + program *tea.Program } // New returns a new Fuzzy Finder. @@ -23,8 +25,12 @@ func New(opts ...Option) *FZF { opt(&o) } + m := newModel(&o) + return &FZF{ - option: &o, + option: &o, + model: m, + program: tea.NewProgram(m), } } @@ -47,22 +53,29 @@ func (fzf *FZF) Find(items interface{}, itemFunc func(i int) string, opts ...Fin if err != nil { return nil, err } - m := newModel(fzf, is, &findOption) + fzf.model.setItems(is) + fzf.model.setFindOption(&findOption) - p := tea.NewProgram(m) - if _, err := p.Run(); err != nil { + if _, err := fzf.program.Run(); err != nil { return nil, err } - if m.abort { + if fzf.model.abort { return nil, ErrAbort } - return m.choices, nil + return fzf.model.choices, nil +} + +// Quit quits the Fuzzy Finder. +func (fzf *FZF) Quit() { + fzf.program.Quit() } -func (fzf *FZF) multiple() bool { - return fzf.option.noLimit || fzf.option.limit > 1 +// Abort aborts the Fuzzy Finder. +func (fzf *FZF) Abort() { + fzf.model.abort = true + fzf.Quit() } // Option represents a option for the Find. diff --git a/model.go b/model.go index 82b2aed..43d50e7 100644 --- a/model.go +++ b/model.go @@ -14,8 +14,8 @@ var ( ) type model struct { - fzf *FZF items *items + option *option findOption *findOption // state @@ -46,45 +46,34 @@ type model struct { input textinput.Model } -func newModel(fzf *FZF, items *items, opt *findOption) *model { +func newModel(opt *option) *model { input := textinput.New() - input.Prompt = fzf.option.prompt - input.Placeholder = fzf.option.inputPlaceholder + input.Prompt = opt.prompt + input.Placeholder = opt.inputPlaceholder input.Focus() - if !fzf.multiple() { - fzf.option.keymap.Toggle.SetEnabled(false) - } - - var matches matches - for i := 0; i < items.Len(); i++ { - matches = append(matches, match{ - Str: items.String(i), - Index: i, - }) + if !opt.multiple() { + opt.keymap.Toggle.SetEnabled(false) } return &model{ - fzf: fzf, - items: items, - findOption: opt, + option: opt, // state abort: false, - cursor: fzf.option.styles.option.cursor.Render(fzf.option.cursor), - nocursor: strings.Repeat(" ", lipgloss.Width(fzf.option.cursor)), + cursor: opt.styles.option.cursor.Render(opt.cursor), + nocursor: strings.Repeat(" ", lipgloss.Width(opt.cursor)), cursorPosition: 0, - promptWidth: lipgloss.Width(fzf.option.prompt), + promptWidth: lipgloss.Width(opt.prompt), - selectedPrefix: fzf.option.styles.option.selectedPrefix.Render(fzf.option.selectedPrefix), - unselectedPrefix: fzf.option.styles.option.unselectedPrefix.Render(fzf.option.unselectedPrefix), + selectedPrefix: opt.styles.option.selectedPrefix.Render(opt.selectedPrefix), + unselectedPrefix: opt.styles.option.unselectedPrefix.Render(opt.unselectedPrefix), - matchesStyle: fzf.option.styles.option.matches, - cursorLineStyle: fzf.option.styles.option.cursorLine, - cursorLineMatchesStyle: lipgloss.NewStyle().Inherit(fzf.option.styles.option.matches).Inherit(fzf.option.styles.option.cursorLine), + matchesStyle: opt.styles.option.matches, + cursorLineStyle: opt.styles.option.cursorLine, + cursorLineMatchesStyle: lipgloss.NewStyle().Inherit(opt.styles.option.matches).Inherit(opt.styles.option.cursorLine), - matches: matches, choices: []int{}, // window windowWidth: 0, @@ -95,6 +84,23 @@ func newModel(fzf *FZF, items *items, opt *findOption) *model { } } +func (m *model) setItems(items *items) { + var matches matches + for i := 0; i < items.Len(); i++ { + matches = append(matches, match{ + Str: items.String(i), + Index: i, + }) + } + + m.items = items + m.matches = matches +} + +func (m *model) setFindOption(findOption *findOption) { + m.findOption = findOption +} + func (m *model) Init() tea.Cmd { return tea.Batch( textinput.Blink, @@ -122,9 +128,9 @@ func (m *model) headerView() string { // input _, _ = v.WriteString(m.input.View()) // count - if m.fzf.option.countViewEnabled { + if m.option.countViewEnabled { _, _ = v.WriteRune('\n') - _, _ = v.WriteString(m.fzf.option.countViewFunc(m.items.Len(), len(m.matches), m.windowWidth)) + _, _ = v.WriteString(m.option.countViewFunc(m.items.Len(), len(m.matches), m.windowWidth)) } return v.String() @@ -150,7 +156,7 @@ func (m *model) itemsView() string { } // write toggle - if m.fzf.multiple() { + if m.option.multiple() { if intContains(m.choices, match.Index) { _, _ = v.WriteString(m.selectedPrefix) } else { @@ -197,23 +203,23 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: // key switch { - case key.Matches(msg, m.fzf.option.keymap.Abort): + case key.Matches(msg, m.option.keymap.Abort): // abort m.abort = true return m, tea.Quit - case key.Matches(msg, m.fzf.option.keymap.Choose): + case key.Matches(msg, m.option.keymap.Choose): // choose m.choice() return m, tea.Quit - case key.Matches(msg, m.fzf.option.keymap.Toggle): + case key.Matches(msg, m.option.keymap.Toggle): // toggle m.toggle() - case key.Matches(msg, m.fzf.option.keymap.Up): + case key.Matches(msg, m.option.keymap.Up): // up m.cursorUp() m.fixYPosition() m.fixCursor() - case key.Matches(msg, m.fzf.option.keymap.Down): + case key.Matches(msg, m.option.keymap.Down): // down m.cursorDown() m.fixYPosition() @@ -265,7 +271,7 @@ func (m *model) toggle() { if intContains(m.choices, match.Index) { m.choices = intFilter(m.choices, func(i int) bool { return i != match.Index }) } else { - if m.fzf.option.noLimit || len(m.choices) < m.fzf.option.limit { + if m.option.noLimit || len(m.choices) < m.option.limit { m.choices = append(m.choices, match.Index) } } diff --git a/option.go b/option.go index 9f78862..5786041 100644 --- a/option.go +++ b/option.go @@ -55,6 +55,10 @@ type option struct { countViewFunc func(itemsCount, matchesCount, windowWidth int) string } +func (o *option) multiple() bool { + return o.noLimit || o.limit > 1 +} + // Option represents a option for the Fuzzy Finder. type Option func(o *option)