Skip to content

Commit

Permalink
pass fzf query or cmd match to editor environment
Browse files Browse the repository at this point in the history
  • Loading branch information
khimaros committed Nov 23, 2023
1 parent be1249d commit 93ee42e
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 13 deletions.
34 changes: 34 additions & 0 deletions docs/tool-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,37 @@ You can customize which editor to use either from the [configuration file](confi
```
3. `VISUAL` environment variable
4. `EDITOR` environment variable

When invoking the editor, `zk` will provide the following environment to the editor:

`ZK_QUERY`: the query interactively provided to `fzf` or otherwise the value passed with the `-m` flag

## Advanced editor usage

It is also possible to create a custom script for your editor, for example to jump to a specific instance of a search term.

Consider the following script:

```bash
#!/bin/bash

set -eou pipefail

grep -nEv '^[[:space:]]*$' "$1" \
| fzf \
--tiebreak=begin \
--exact \
--tabstop=4 \
--height=100% \
--layout=reverse \
--no-hscroll \
--color=hl:-1,hl+:-1 \
--preview-window=wrap \
--delimiter=':' \
--with-nth=2.. \
--query="${ZK_QUERY}" \
| sed 's/:.*$//' \
| xargs -o -I {} vim +{} "$1"
```

This script could then be configured as the `editor` in `.zk/config.yaml` to open a second `fzf` instance prepopulated with the query which was previously entered into `zk`.
6 changes: 4 additions & 2 deletions internal/adapter/editor/editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import (
// Editor represents an external editor able to edit the notes.
type Editor struct {
editor string
query string
}

// NewEditor creates a new Editor from the given editor user setting or the
// matching environment variables.
func NewEditor(editor opt.String) (*Editor, error) {
func NewEditor(editor opt.String, query string) (*Editor, error) {
editor = osutil.GetOptEnv("ZK_EDITOR").
Or(editor).
Or(osutil.GetOptEnv("VISUAL")).
Expand All @@ -29,7 +30,7 @@ func NewEditor(editor opt.String) (*Editor, error) {
return nil, fmt.Errorf("no editor set in config")
}

return &Editor{editor.Unwrap()}, nil
return &Editor{editor.Unwrap(), query}, nil
}

// Open launches the editor with the notes at given paths.
Expand All @@ -38,6 +39,7 @@ func (e *Editor) Open(paths ...string) error {
// initial note content to `zk new`. Without this, Vim doesn't work
// properly in this case.
// See https://github.com/mickael-menu/zk/issues/4
os.Setenv("ZK_QUERY", e.query)
cmd := executil.CommandFromString(e.editor + " " + shellquote.Join(paths...) + " </dev/tty")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
Expand Down
10 changes: 5 additions & 5 deletions internal/adapter/editor/editor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestEditorUsesZkEditorFirst(t *testing.T) {
os.Setenv("VISUAL", "visual")
os.Setenv("EDITOR", "editor")

editor, err := NewEditor(opt.NewString("custom-editor"))
editor, err := NewEditor(opt.NewString("custom-editor"), "")
assert.Nil(t, err)
assert.Equal(t, editor.editor, "zk-editor")
}
Expand All @@ -23,7 +23,7 @@ func TestEditorFallsbackOnUserConfig(t *testing.T) {
os.Setenv("VISUAL", "visual")
os.Setenv("EDITOR", "editor")

editor, err := NewEditor(opt.NewString("custom-editor"))
editor, err := NewEditor(opt.NewString("custom-editor"), "")
assert.Nil(t, err)
assert.Equal(t, editor.editor, "custom-editor")
}
Expand All @@ -33,7 +33,7 @@ func TestEditorFallsbackOnVisual(t *testing.T) {
os.Setenv("VISUAL", "visual")
os.Setenv("EDITOR", "editor")

editor, err := NewEditor(opt.NullString)
editor, err := NewEditor(opt.NullString, "")
assert.Nil(t, err)
assert.Equal(t, editor.editor, "visual")
}
Expand All @@ -43,7 +43,7 @@ func TestEditorFallsbackOnEditor(t *testing.T) {
os.Unsetenv("VISUAL")
os.Setenv("EDITOR", "editor")

editor, err := NewEditor(opt.NullString)
editor, err := NewEditor(opt.NullString, "")
assert.Nil(t, err)
assert.Equal(t, editor.editor, "editor")
}
Expand All @@ -53,7 +53,7 @@ func TestEditorFailsWhenUnset(t *testing.T) {
os.Unsetenv("VISUAL")
os.Unsetenv("EDITOR")

editor, err := NewEditor(opt.NullString)
editor, err := NewEditor(opt.NullString, "")
assert.Err(t, err, "no editor set in config")
assert.Nil(t, editor)
}
5 changes: 4 additions & 1 deletion internal/adapter/fzf/fzf.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ type Fzf struct {
cmd *exec.Cmd
pipe io.WriteCloser
closeOnce sync.Once

Query string
}

// New runs a fzf instance.
Expand Down Expand Up @@ -162,7 +164,8 @@ func New(opts Opts) (*Fzf, error) {
func (f *Fzf) parseSelection(output []byte) {
f.selection = make([][]string, 0)
lines := stringsutil.SplitLines(string(output))
for _, line := range lines {
f.Query = lines[0]
for _, line := range lines[1:] {
fields := strings.Split(line, f.opts.Delimiter)
// Trim padding
for i, field := range fields {
Expand Down
4 changes: 4 additions & 0 deletions internal/adapter/fzf/note_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type NoteFilter struct {
fs core.FileStorage
terminal *term.Terminal
templateLoader core.TemplateLoader
Query string
}

// NoteFilterOpts holds the configuration for the fzf notes filtering.
Expand Down Expand Up @@ -153,6 +154,8 @@ func (f *NoteFilter) Apply(notes []core.ContextualNote) ([]core.ContextualNote,
return selectedNotes, err
}

f.Query = fzf.Query

for _, s := range selection {
path := s[len(s)-1]
for i, m := range notes {
Expand All @@ -175,6 +178,7 @@ var defaultOptions = strings.Join([]string{
"--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
"--print-query", // Print the query as the first line
"--color hl:-1,hl+:-1", // Don't highlight search terms
"--preview-window wrap", // Enable line wrapping in the preview window
}, " ")
Expand Down
3 changes: 2 additions & 1 deletion internal/adapter/handlebars/handlebars_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@ func TestFormatDateHelper(t *testing.T) {
testString(t, "{{format-date now 'timestamp'}}", context, "200911172034")
testString(t, "{{format-date now 'timestamp-unix'}}", context, "1258490098")
testString(t, "{{format-date now 'cust: %Y-%m'}}", context, "cust: 2009-11")
testString(t, "{{format-date now 'elapsed'}}", context, "14 years ago")
// FIXME: this test will break once per year
testString(t, "{{format-date now 'elapsed'}}", context, "15 years ago")
}

func TestDateHelper(t *testing.T) {
Expand Down
7 changes: 6 additions & 1 deletion internal/cli/cmd/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,12 @@ func (cmd *Edit) Run(container *cli.Container) error {
paths = append(paths, absPath)
}

editor, err := container.NewNoteEditor(notebook)
query := filter.Query
if query == "" && len(cmd.Match) > 0 {
query = cmd.Match[0]
}

editor, err := container.NewNoteEditor(notebook, query)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/cmd/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (cmd *New) Run(container *cli.Container) error {
fmt.Printf("%+v\n", path)
return nil
} else {
editor, err := container.NewNoteEditor(notebook)
editor, err := container.NewNoteEditor(notebook, "")
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions internal/cli/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,8 @@ func (c *Container) NewNoteFilter(opts fzf.NoteFilterOpts) *fzf.NoteFilter {
return fzf.NewNoteFilter(opts, c.FS, c.Terminal, c.TemplateLoader)
}

func (c *Container) NewNoteEditor(notebook *core.Notebook) (*editor.Editor, error) {
return editor.NewEditor(notebook.Config.Tool.Editor)
func (c *Container) NewNoteEditor(notebook *core.Notebook, query string) (*editor.Editor, error) {
return editor.NewEditor(notebook.Config.Tool.Editor, query)
}

// Paginate creates an auto-closing io.Writer which will be automatically
Expand Down

0 comments on commit 93ee42e

Please sign in to comment.