zk/adapter/fzf/finder.go

124 lines
3.1 KiB
Go
Raw Normal View History

2021-01-23 12:29:14 +00:00
package fzf
import (
"fmt"
2021-01-23 14:24:08 +00:00
"os"
"path/filepath"
2021-01-23 12:29:14 +00:00
"github.com/mickael-menu/zk/adapter/term"
2021-01-23 12:29:14 +00:00
"github.com/mickael-menu/zk/core/note"
"github.com/mickael-menu/zk/core/style"
"github.com/mickael-menu/zk/core/zk"
2021-01-23 14:24:08 +00:00
"github.com/mickael-menu/zk/util/opt"
2021-01-23 12:29:14 +00:00
stringsutil "github.com/mickael-menu/zk/util/strings"
)
// NoteFinder wraps a note.Finder and filters its result interactively using fzf.
type NoteFinder struct {
opts NoteFinderOpts
finder note.Finder
terminal *term.Terminal
2021-01-23 12:29:14 +00:00
}
// NoteFinderOpts holds the configuration for the fzf notes finder.
//
// The absolute path to the notebook (BasePath) and the working directory
// (CurrentPath) are used to make the path of each note relative to the working
// directory.
type NoteFinderOpts struct {
// Indicates whether fzf is opened for every query, even if empty.
AlwaysFilter bool
// Preview command to run when selecting a note.
PreviewCmd opt.String
// When non nil, a "create new note from query" binding will be added to
// fzf to create a note in this directory.
NewNoteDir *zk.Dir
// Absolute path to the notebook.
BasePath string
// Path to the working directory.
CurrentPath string
}
func NewNoteFinder(opts NoteFinderOpts, finder note.Finder, terminal *term.Terminal) *NoteFinder {
return &NoteFinder{
opts: opts,
finder: finder,
terminal: terminal,
}
2021-01-23 12:29:14 +00:00
}
func (f *NoteFinder) Find(opts note.FinderOpts) ([]note.Match, error) {
selectedMatches := make([]note.Match, 0)
2021-01-23 12:29:14 +00:00
matches, err := f.finder.Find(opts)
relPaths := []string{}
2021-01-23 12:29:14 +00:00
if !opts.Interactive || !f.terminal.IsInteractive() || err != nil || (!f.opts.AlwaysFilter && len(matches) == 0) {
2021-01-23 12:29:14 +00:00
return matches, err
}
for _, match := range matches {
path, err := filepath.Rel(f.opts.CurrentPath, filepath.Join(f.opts.BasePath, match.Path))
if err != nil {
return selectedMatches, err
}
relPaths = append(relPaths, path)
}
2021-01-23 12:29:14 +00:00
2021-01-23 14:24:08 +00:00
zkBin, err := os.Executable()
if err != nil {
return selectedMatches, err
}
bindings := []Binding{}
if dir := f.opts.NewNoteDir; dir != nil {
suffix := ""
if dir.Name != "" {
suffix = " in " + dir.Name + "/"
}
bindings = append(bindings, Binding{
Keys: "Ctrl-N",
Description: "create a note with the query as title" + suffix,
Action: fmt.Sprintf("abort+execute(%s new %s --title {q} < /dev/tty > /dev/tty)", zkBin, dir.Path),
})
}
2021-01-23 14:24:08 +00:00
fzf, err := New(Opts{
PreviewCmd: f.opts.PreviewCmd.OrString("cat {-1}").NonEmpty(),
2021-01-23 14:24:08 +00:00
Padding: 2,
Bindings: bindings,
2021-01-23 12:29:14 +00:00
})
if err != nil {
return selectedMatches, err
}
for i, match := range matches {
title := match.Title
if title == "" {
title = relPaths[i]
}
2021-01-23 14:24:08 +00:00
fzf.Add([]string{
f.terminal.MustStyle(title, style.RuleYellow),
f.terminal.MustStyle(stringsutil.JoinLines(match.Body), style.RuleUnderstate),
f.terminal.MustStyle(relPaths[i], style.RuleUnderstate),
2021-01-23 14:24:08 +00:00
})
}
selection, err := fzf.Selection()
if err != nil {
return selectedMatches, err
}
2021-01-23 12:29:14 +00:00
for _, s := range selection {
path := s[len(s)-1]
for i, m := range matches {
if relPaths[i] == path {
2021-01-23 12:29:14 +00:00
selectedMatches = append(selectedMatches, m)
}
}
}
return selectedMatches, nil
}