Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make FzF options configurable #154

Merged
merged 10 commits into from
Apr 24, 2022
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ All notable changes to this project will be documented in this file.
* You can customize the default behavior with the [`use-additional-text-edits` configuration key](docs/config-lsp.md).
* [#163](https://github.com/mickael-menu/zk/issues/163) Use the `ZK_SHELL` environment variable to override the shell for `zk` only.
* [#173](https://github.com/mickael-menu/zk/issues/173) Support for double star globbing in `note.ignore` config option.
* [#137](https://github.com/mickael-menu/zk/issues/137) Customize the `fzf` options used by `zk`'s interactive modes with the [`fzf-options`](docs/tool-fzf.md) config option (contributed by [@Nelyah](https://github.com/mickael-menu/zk/pull/154)).

* [#168](https://github.com/mickael-menu/zk/discussions/168) Customize the `fzf` key binding to create new notes with the [`fzf-bind-new`](docs/tool-fzf.md) config option.

### Changed

* The default `fzf` key binding to create a new note with `zk edit --interactive` was changed to `Ctrl-E`, to avoid conflict with the default `Ctrl-N` binding.

### Fixed

Expand Down
4 changes: 2 additions & 2 deletions docs/assets/media/edit.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/assets/media/list.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/assets/media/new2.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/assets/media/screencast.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ You can customize your experience using [custom templates](template.md) to gener

If you are not sure whether a note already exists for a particular subject, the "search or create" mode might be more appropriate than `zk new`. It is inspired by [Notational Velocity](https://notational.net/) and enables searching for an existing note or creating a new one in a single action.

From `zk`'s interactive edit screen, press `Ctrl-N` to create a new note using the current search query as title.
From `zk`'s interactive edit screen, press `Ctrl-E` to create a new note using the current search query as title.

<div align="center"><img alt="Create a note" width="85%" src="assets/media/new2.svg"/></div>

Expand Down
2 changes: 1 addition & 1 deletion docs/note-creation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ By default, `zk new` will start [your editor](tool-editor.md) after creating the

If you are not sure whether a note already exists for a particular subject, the "search or create" mode might be more appropriate than `zk new`. It is inspired by [Notational Velocity](https://notational.net/) and enables searching for an existing note or creating a new one in a single action.

This option is available when running `zk edit --interactive`, which spawns [`fzf`](tool-fzf.md) to filter selected notes. From `fzf`, press `Ctrl-N` to create a new note using the current search query as title.
This option is available when running `zk edit --interactive`, which spawns [`fzf`](tool-fzf.md) to filter selected notes. From `fzf`, press `Ctrl-E` to create a new note using the current search query as title.

## Create a note with initial content

Expand Down
30 changes: 30 additions & 0 deletions docs/tool-fzf.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,33 @@ The following variables are available in the line template.
| `checksum` | string | SHA-256 checksum of the note file |

1. YAML keys are normalized to lower case.


## `fzf` options

You can override the default `fzf` options used by `zk` with `fzf-options`. Look at `man fzf` for the list of available options.

```toml
[tool]
fzf-options = "--height 40% --border"
```

Note that this overrides all the default options used by `zk`, you might want to keep some of them:

* `--tiebreak begin` Prefer matches located at the beginning of the line
* `--exact` Look for exact matches instead of fuzzy ones by default
* `--tabstop 4` Length of tab characters
* `--height 100%` Height of the list relative to the terminal window
* `--layout reverse` Display the input field at the top
* `--no-hscroll` Make sure the path and titles are always visible
* `--color hl:-1,hl+:-1` Don't highlight search terms
* `--preview-window wrap` Enable line wrapping in the preview window

## Key bindings

When running `fzf` with `zk edit --interactive`, you can [create a new note with the `Ctrl-E` key binding](note-creation.md#search-or-create-with-a-single-command). This binding is customizable with `fzf-bind-new`. You can also disable it by setting it to an empty string (`""`).

```toml
[tool]
fzf-bind-new = "Ctrl-C"
```
24 changes: 12 additions & 12 deletions internal/adapter/fzf/fzf.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"
"sync"

"github.com/kballard/go-shellquote"
"github.com/mickael-menu/zk/internal/util/errors"
"github.com/mickael-menu/zk/internal/util/opt"
stringsutil "github.com/mickael-menu/zk/internal/util/strings"
Expand All @@ -26,6 +27,8 @@ var (
type Opts struct {
// Preview command executed by fzf when hovering a line.
PreviewCmd opt.String
// Optionally provide additional arguments, taken from the config `fzf-options` property.
Options opt.String
// Amount of space between two non-empty fields.
Padding int
// Delimiter used by fzf between fields.
Expand Down Expand Up @@ -72,21 +75,18 @@ func New(opts Opts) (*Fzf, error) {
opts.Delimiter = "\x01"
}

// Hard-coded fzf options that are required by zk.
args := []string{
"--delimiter", opts.Delimiter,
"--tiebreak", "begin",
"--ansi",
"--exact",
"--tabstop", "4",
"--height", "100%",
"--layout", "reverse",
//"--info", "inline",
// Make sure the path and titles are always visible
"--no-hscroll",
// Don't highlight search terms
"--color", "hl:-1,hl+:-1",
"--preview-window", "wrap",
"--delimiter", opts.Delimiter,
}

// Additional options.
additionalArgs, err := shellquote.Split(opts.Options.String())
if err != nil {
return nil, errors.Wrapf(err, "can't split the fzf-options: %s", opts.Options.String())
}
args = append(args, additionalArgs...)

header := ""
binds := []string{}
Expand Down
31 changes: 26 additions & 5 deletions internal/adapter/fzf/note_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"time"

"github.com/mickael-menu/zk/internal/adapter/term"
Expand Down Expand Up @@ -32,6 +33,10 @@ type NoteFilterOpts struct {
AlwaysFilter bool
// Format for a single line, taken from the config `fzf-line` property.
LineTemplate opt.String
// Optionally provide additional arguments, taken from the config `fzf-options` property.
FzfOptions opt.String
// Key binding for the new action.
NewBinding opt.String
// Preview command to run when selecting a note.
PreviewCmd opt.String
// When non null, a "create new note from query" binding will be added to
Expand Down Expand Up @@ -88,16 +93,20 @@ func (f *NoteFilter) Apply(notes []core.ContextualNote) ([]core.ContextualNote,
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),
})
newBinding := f.opts.NewBinding.OrString("Ctrl-E").String()
if newBinding != "" {
bindings = append(bindings, Binding{
Keys: newBinding,
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),
})
}
}

previewCmd := f.opts.PreviewCmd.OrString("cat {-1}").Unwrap()

fzf, err := New(Opts{
Options: f.opts.FzfOptions.OrString(defaultOptions),
PreviewCmd: opt.NewNotEmptyString(previewCmd),
Padding: 2,
Bindings: bindings,
Expand Down Expand Up @@ -158,6 +167,18 @@ func (f *NoteFilter) Apply(notes []core.ContextualNote) ([]core.ContextualNote,

var defaultLineTemplate = `{{style "title" title-or-path}} {{style "understate" body}} {{style "understate" (json metadata)}}`

// defaultOptions are the default fzf options used when filtering notes.
var defaultOptions = strings.Join([]string{
"--tiebreak begin", // Prefer matches located at the beginning of the line
"--exact", // Look for exact matches instead of fuzzy ones by default
"--tabstop 4", // Length of tab characters
"--height 100%", // Height of the list relative to the terminal window
"--layout reverse", // Display the input field at the top
"--no-hscroll", // Make sure the path and titles are always visible
"--color hl:-1,hl+:-1", // Don't highlight search terms
"--preview-window wrap", // Enable line wrapping in the preview window
}, " ")

type lineRenderContext struct {
Filename string
FilenameStem string `handlebars:"filename-stem"`
Expand Down
2 changes: 2 additions & 0 deletions internal/cli/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ func (c *Container) CurrentNotebook() (*core.Notebook, error) {
func (c *Container) NewNoteFilter(opts fzf.NoteFilterOpts) *fzf.NoteFilter {
opts.PreviewCmd = c.Config.Tool.FzfPreview
opts.LineTemplate = c.Config.Tool.FzfLine
opts.FzfOptions = c.Config.Tool.FzfOptions
opts.NewBinding = c.Config.Tool.FzfBindNew
return fzf.NewNoteFilter(opts, c.FS, c.Terminal, c.TemplateLoader)
}

Expand Down
10 changes: 10 additions & 0 deletions internal/core/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ type ToolConfig struct {
Pager opt.String
FzfPreview opt.String
FzfLine opt.String
FzfOptions opt.String
FzfBindNew opt.String
}

// LSPConfig holds the Language Server Protocol configuration.
Expand Down Expand Up @@ -362,6 +364,12 @@ func ParseConfig(content []byte, path string, parentConfig Config) (Config, erro
if tool.FzfLine != nil {
config.Tool.FzfLine = opt.NewNotEmptyString(*tool.FzfLine)
}
if tool.FzfOptions != nil {
config.Tool.FzfOptions = opt.NewNotEmptyString(*tool.FzfOptions)
}
if tool.FzfBindNew != nil {
config.Tool.FzfBindNew = opt.NewStringWithPtr(tool.FzfBindNew)
}

// LSP completion
lspCompl := tomlConf.LSP.Completion
Expand Down Expand Up @@ -506,6 +514,8 @@ type tomlToolConfig struct {
Pager *string
FzfPreview *string `toml:"fzf-preview"`
FzfLine *string `toml:"fzf-line"`
FzfOptions *string `toml:"fzf-options"`
FzfBindNew *string `toml:"fzf-bind-new"`
}

type tomlLSPConfig struct {
Expand Down
4 changes: 4 additions & 0 deletions internal/core/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ func TestParseComplete(t *testing.T) {
pager = "less"
fzf-preview = "bat {1}"
fzf-line = "{{title}}"
fzf-options = "--border --height 40%"
fzf-bind-new = "Ctrl-C"

[extra]
hello = "world"
Expand Down Expand Up @@ -229,6 +231,8 @@ func TestParseComplete(t *testing.T) {
Pager: opt.NewString("less"),
FzfPreview: opt.NewString("bat {1}"),
FzfLine: opt.NewString("{{title}}"),
FzfOptions: opt.NewString("--border --height 40%"),
FzfBindNew: opt.NewString("Ctrl-C"),
},
LSP: LSPConfig{
Completion: LSPCompletionConfig{
Expand Down