From 1833670fb91dfa17047af320ce7f015e0d500ba6 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Thu, 29 Feb 2024 09:49:33 +0900 Subject: Add $FZF_DEFAULT_OPTS_FILE (#3618) For those who prefer to manage default options in a file. If the file is not found, fzf will exit with an error. We're not setting a default value for it because: 1. it's hard to find a default value that can be universally agreed upon 2. to avoid fzf having to check for the existence of the file even when it's not used --- CHANGELOG.md | 6 ++++++ README.md | 4 ++++ man/man1/fzf.1 | 9 ++++++++- src/options.go | 37 +++++++++++++++++++++++++++++++------ test/test_go.rb | 1 + 5 files changed, 50 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19594f4f..f2d9354c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ CHANGELOG - You would wonder why fzf implements directory traversal anyway when it's a filter program following the Unix philosophy. But fzf has had [the traversal code for years][walker] to tackle the performance problem on Windows. And I decided to use the same approach on different platforms as well for the benefits listed above. - Built-in traversal is now done using the excellent [charlievieth/fastwalk][fastwalk] library, which easily outperforms its competitors and supports safely following symlinks. +- Added `$FZF_DEFAULT_OPTS_FILE` to allow managing default options in a file + - See [#3618](https://github.com/junegunn/fzf/pull/3618) + - Option precedence from lower to higher + 1. Options read from `$FZF_DEFAULT_OPTS_FILE` + 1. Options from `$FZF_DEFAULT_OPTS` + 1. Options from command-line arguments [find]: https://github.com/junegunn/fzf/blob/0.46.1/src/constants.go#L60-L64 [walker]: https://github.com/junegunn/fzf/pull/1847 diff --git a/README.md b/README.md index 4c9b01fd..e0b367b6 100644 --- a/README.md +++ b/README.md @@ -329,6 +329,10 @@ or `py`. - `FZF_DEFAULT_OPTS` - Default options - e.g. `export FZF_DEFAULT_OPTS="--layout=reverse --inline-info"` +- `FZF_DEFAULT_OPTS_FILE` + - If you prefer to manage default options in a file, set this variable to + point to the location of the file + - e.g. `export FZF_DEFAULT_OPTS_FILE=~/.fzfrc` ### Options diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index c10f334e..76bfa648 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -865,7 +865,14 @@ with \fB$SHELL -c\fR if \fBSHELL\fR is set, otherwise with \fBsh -c\fR, so in this case make sure that the command is POSIX-compliant. .TP .B FZF_DEFAULT_OPTS -Default options. e.g. \fBexport FZF_DEFAULT_OPTS="--extended --cycle"\fR +Default options. +.br +e.g. \fBexport FZF_DEFAULT_OPTS="--layout=reverse --border --cycle"\fR +.TP +.B FZF_DEFAULT_OPTS_FILE +The location of the file that contains the default options. +.br +e.g. \fBexport FZF_DEFAULT_OPTS_FILE=~/.fzfrc\fR .TP .B FZF_API_KEY Can be used to require an API key when using \fB--listen\fR option. If not set, diff --git a/src/options.go b/src/options.go index dcb70dcb..09787a44 100644 --- a/src/options.go +++ b/src/options.go @@ -126,8 +126,8 @@ const usage = `usage: fzf [options] Environment variables FZF_DEFAULT_COMMAND Default command to use when input is tty - FZF_DEFAULT_OPTS Default options - (e.g. '--layout=reverse --inline-info') + FZF_DEFAULT_OPTS Default options (e.g. '--layout=reverse --info=inline') + FZF_DEFAULT_OPTS_FILE Location of the file to read default options from FZF_API_KEY X-API-Key header for HTTP server (--listen) ` @@ -421,8 +421,10 @@ func help(code int) { os.Exit(code) } +var errorContext = "" + func errorExit(msg string) { - os.Stderr.WriteString(msg + "\n") + os.Stderr.WriteString(errorContext + msg + "\n") os.Exit(exitError) } @@ -2167,13 +2169,36 @@ func ParseOptions() *Options { } } - // Options from Env var - words, _ := shellwords.Parse(os.Getenv("FZF_DEFAULT_OPTS")) + // 1. Options from $FZF_DEFAULT_OPTS_FILE + if path := os.Getenv("FZF_DEFAULT_OPTS_FILE"); path != "" { + bytes, err := os.ReadFile(path) + if err != nil { + errorContext = "$FZF_DEFAULT_OPTS_FILE: " + errorExit(err.Error()) + } + + words, parseErr := shellwords.Parse(string(bytes)) + if parseErr != nil { + errorContext = path + ": " + errorExit(parseErr.Error()) + } + if len(words) > 0 { + parseOptions(opts, words) + } + } + + // 2. Options from $FZF_DEFAULT_OPTS string + words, parseErr := shellwords.Parse(os.Getenv("FZF_DEFAULT_OPTS")) + errorContext = "$FZF_DEFAULT_OPTS: " + if parseErr != nil { + errorExit(parseErr.Error()) + } if len(words) > 0 { parseOptions(opts, words) } - // Options from command-line arguments + // 3. Options from command-line arguments + errorContext = "" parseOptions(opts, os.Args[1:]) postProcessOptions(opts) diff --git a/test/test_go.rb b/test/test_go.rb index ecc111db..599d5442 100755 --- a/test/test_go.rb +++ b/test/test_go.rb @@ -2582,6 +2582,7 @@ class TestGoFZF < TestBase def test_change_preview_window_rotate tmux.send_keys "seq 100 | #{FZF} --preview-window left,border-none --preview 'echo hello' --bind '" \ "a:change-preview-window(right|down|up|hidden|)'", :Enter + tmux.until { |lines| assert(lines.any? { _1.include?('100/100') }) } 3.times do tmux.until { |lines| lines[0].start_with?('hello') } tmux.send_keys 'a' -- cgit v1.2.3