mirror of
https://github.com/junegunn/fzf.git
synced 2025-08-01 12:42:01 -07:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d24b58ef3f | ||
|
06ae9b0f3b | ||
|
2a9c1c06a4 | ||
|
90ad1b7f22 | ||
|
f22fbcd1af | ||
|
1d761684c5 | ||
|
e491770f1c | ||
|
a41be61506 | ||
|
1a8f633611 | ||
|
af8fe918d8 | ||
|
8ef9dfd9a2 | ||
|
66df24040f | ||
|
ed4442d9ea | ||
|
0edb5d5ebb | ||
|
9ffc2c7ca3 |
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,6 +1,18 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
0.61.3
|
||||
------
|
||||
- Reverted #4351 as it caused `tmux run-shell 'fzf --tmux'` to fail (#4559 #4560)
|
||||
- More environment variables for child processes (#4356)
|
||||
|
||||
0.61.2
|
||||
------
|
||||
- Fixed panic when using header border without pointer/marker (@phanen)
|
||||
- Fixed `--tmux` option when already inside a tmux popup (@peikk0)
|
||||
- Bug fixes and improvements in CTRL-T binding of fish (#4334) (@bitraid)
|
||||
- Added `--no-tty-default` option to make fzf search for the current TTY device instead of defaulting to `/dev/tty` (#4242)
|
||||
|
||||
0.61.1
|
||||
------
|
||||
- Disable bracketed-paste mode on exit. This fixes issue where pasting breaks after running fzf on old bash versions that don't support the mode.
|
||||
|
2
install
2
install
@@ -2,7 +2,7 @@
|
||||
|
||||
set -u
|
||||
|
||||
version=0.61.1
|
||||
version=0.61.3
|
||||
auto_completion=
|
||||
key_bindings=
|
||||
update_config=2
|
||||
|
@@ -1,4 +1,4 @@
|
||||
$version="0.61.1"
|
||||
$version="0.61.3"
|
||||
|
||||
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
|
||||
|
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
..
|
||||
.TH fzf\-tmux 1 "Apr 2025" "fzf 0.61.1" "fzf\-tmux - open fzf in tmux split pane"
|
||||
.TH fzf\-tmux 1 "Apr 2025" "fzf 0.61.3" "fzf\-tmux - open fzf in tmux split pane"
|
||||
|
||||
.SH NAME
|
||||
fzf\-tmux - open fzf in tmux split pane
|
||||
|
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
..
|
||||
.TH fzf 1 "Apr 2025" "fzf 0.61.1" "fzf - a command-line fuzzy finder"
|
||||
.TH fzf 1 "Apr 2025" "fzf 0.61.3" "fzf - a command-line fuzzy finder"
|
||||
|
||||
.SH NAME
|
||||
fzf - a command-line fuzzy finder
|
||||
@@ -228,6 +228,13 @@ e.g. \fB# Avoid rendering both fzf instances at the same time
|
||||
(sleep 1; seq 1000000; sleep 1) |
|
||||
fzf \-\-sync \-\-query 5 \-\-listen \-\-bind start:up,load:up,result:up,focus:change\-header:Ready\fR
|
||||
.RE
|
||||
.TP
|
||||
.B "\-\-no\-tty\-default"
|
||||
Make fzf search for the current TTY device via standard error instead of
|
||||
defaulting to \fB/dev/tty\fR. This option avoids issues when launching
|
||||
emacsclient from within fzf. Alternatively, you can change the default TTY
|
||||
device by setting \fB--tty-default=DEVICE_NAME\fR.
|
||||
|
||||
.SS GLOBAL STYLE
|
||||
.TP
|
||||
.BI "\-\-style=" "PRESET"
|
||||
@@ -1272,10 +1279,20 @@ fzf exports the following environment variables to its child processes.
|
||||
.br
|
||||
.BR FZF_PROMPT " Prompt string"
|
||||
.br
|
||||
.BR FZF_GHOST " Ghost string"
|
||||
.br
|
||||
.BR FZF_POINTER " Pointer string"
|
||||
.br
|
||||
.BR FZF_PREVIEW_LABEL " Preview label string"
|
||||
.br
|
||||
.BR FZF_BORDER_LABEL " Border label string"
|
||||
.br
|
||||
.BR FZF_LIST_LABEL " List label string"
|
||||
.br
|
||||
.BR FZF_INPUT_LABEL " Input label string"
|
||||
.br
|
||||
.BR FZF_HEADER_LABEL " Header label string"
|
||||
.br
|
||||
.BR FZF_ACTION " The name of the last action performed"
|
||||
.br
|
||||
.BR FZF_KEY " The name of the last key pressed"
|
||||
|
@@ -42,45 +42,79 @@ function fzf_key_bindings
|
||||
end
|
||||
|
||||
function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix'
|
||||
set -l fzf_query ''
|
||||
set -l prefix ''
|
||||
set -l dir '.'
|
||||
set -l query
|
||||
set -l commandline (commandline -t | string unescape -n)
|
||||
|
||||
# Strip -option= from token if present
|
||||
set -l prefix (string match -r -- '^-[^\s=]+=' $commandline)
|
||||
set commandline (string replace -- "$prefix" '' $commandline)
|
||||
# Set variables containing the major and minor fish version numbers, using
|
||||
# a method compatible with all supported fish versions.
|
||||
set -l -- fish_major (string match -r -- '^\d+' $version)
|
||||
set -l -- fish_minor (string match -r -- '^\d+\.(\d+)' $version)[2]
|
||||
|
||||
# Enable home directory expansion of leading ~/
|
||||
set commandline (string replace -r -- '^~/' '\$HOME/' $commandline)
|
||||
# fish v3.3.0 and newer: Don't use option prefix if " -- " is preceded.
|
||||
set -l -- match_regex '(?<fzf_query>[\s\S]*?(?=\n?$)$)'
|
||||
set -l -- prefix_regex '^-[^\s=]+=|^-(?!-)\S'
|
||||
if test "$fish_major" -eq 3 -a "$fish_minor" -lt 3
|
||||
or string match -q -v -- '* -- *' (string sub -l (commandline -Cp) -- (commandline -p))
|
||||
set -- match_regex "(?<prefix>$prefix_regex)?$match_regex"
|
||||
end
|
||||
|
||||
# Escape special characters, except for the $ sign of valid variable names,
|
||||
# so that the original string with expanded variables is returned after eval.
|
||||
set commandline (string escape -n -- $commandline)
|
||||
set commandline (string replace -r -a -- '\\\\\$(?=[\w])' '\$' $commandline)
|
||||
# Set $prefix and expanded $fzf_query with preserved trailing newlines.
|
||||
if test "$fish_major" -ge 4
|
||||
# fish v4.0.0 and newer
|
||||
string match -q -r -- $match_regex (commandline --current-token --tokens-expanded | string collect -N)
|
||||
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
||||
# fish v3.2.0 - v3.7.1 (last v3)
|
||||
string match -q -r -- $match_regex (commandline --current-token --tokenize | string collect -N)
|
||||
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)' '')
|
||||
else
|
||||
# fish older than v3.2.0 (v3.1b1 - v3.1.2)
|
||||
set -l -- cl_token (commandline --current-token --tokenize | string collect -N)
|
||||
set -- prefix (string match -r -- $prefix_regex $cl_token)
|
||||
set -- fzf_query (string replace -- "$prefix" '' $cl_token | string collect -N)
|
||||
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)|\\\n\\\n$' '')
|
||||
end
|
||||
|
||||
# eval is used to do shell expansion on paths
|
||||
eval set commandline $commandline
|
||||
|
||||
# Combine multiple consecutive slashes into one.
|
||||
set commandline (string replace -r -a -- '/+' '/' $commandline)
|
||||
|
||||
if test -n "$commandline"
|
||||
# Strip trailing slash, unless $dir is root dir (/)
|
||||
set dir (string replace -r -- '(?<!^)/$' '' $commandline)
|
||||
|
||||
# Set $dir to the longest existing filepath
|
||||
while not test -d "$dir"
|
||||
# If path is absolute, this can keep going until ends up at /
|
||||
# If path is relative, this can keep going until entire input is consumed, dirname returns "."
|
||||
set dir (dirname -- $dir)
|
||||
if test -n "$fzf_query"
|
||||
# Normalize path in $fzf_query, set $dir to the longest existing directory.
|
||||
if test \( "$fish_major" -ge 4 \) -o \( "$fish_major" -eq 3 -a "$fish_minor" -ge 5 \)
|
||||
# fish v3.5.0 and newer
|
||||
set -- fzf_query (path normalize -- $fzf_query)
|
||||
set -- dir $fzf_query
|
||||
while not path is -d $dir
|
||||
set -- dir (path dirname $dir)
|
||||
end
|
||||
else
|
||||
# fish older than v3.5.0 (v3.1b1 - v3.4.1)
|
||||
if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
||||
# fish v3.2.0 - v3.4.1
|
||||
string match -q -r -- '(?<fzf_query>^[\s\S]*?(?=\n?$)$)' \
|
||||
(string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
||||
else
|
||||
# fish v3.1b1 - v3.1.2
|
||||
set -- fzf_query (string replace -r -a -- '(?<=/)/|(?<!^)/+(?!\n)$' '' $fzf_query | string collect -N)
|
||||
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r '\\\n$' '')
|
||||
end
|
||||
set -- dir $fzf_query
|
||||
while not test -d "$dir"
|
||||
set -- dir (dirname -z -- "$dir" | string split0)
|
||||
end
|
||||
end
|
||||
|
||||
if test "$dir" = '.'; and test (string sub -l 2 -- $commandline) != './'
|
||||
# If $dir is "." but commandline is not a relative path, this means no file path found
|
||||
set fzf_query $commandline
|
||||
else
|
||||
# Also remove trailing slash after dir, to "split" input properly
|
||||
set fzf_query (string replace -r -- "^$dir/?" '' $commandline)
|
||||
if not string match -q -- '.' $dir; or string match -q -r -- '^\./|^\.$' $fzf_query
|
||||
# Strip $dir from $fzf_query - preserve trailing newlines.
|
||||
if test "$fish_major" -ge 4
|
||||
# fish v4.0.0 and newer
|
||||
string match -q -r -- '^'(string escape --style=regex -- $dir)'/?(?<fzf_query>[\s\S]*)' $fzf_query
|
||||
else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2
|
||||
# fish v3.2.0 - v3.7.1 (last v3)
|
||||
string match -q -r -- '^/?(?<fzf_query>[\s\S]*?(?=\n?$)$)' \
|
||||
(string replace -- "$dir" '' $fzf_query | string collect -N)
|
||||
else
|
||||
# fish older than v3.2.0 (v3.1b1 - v3.1.2)
|
||||
set -- fzf_query (string replace -- "$dir" '' $fzf_query | string collect -N)
|
||||
eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^/?|\\\n$' '')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -95,13 +129,13 @@ function fzf_key_bindings
|
||||
set -l prefix $commandline[3]
|
||||
|
||||
set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
|
||||
"--reverse --walker=file,dir,follow,hidden --scheme=path --walker-root=$dir" \
|
||||
"$FZF_CTRL_T_OPTS --multi")
|
||||
"--reverse --walker=file,dir,follow,hidden --scheme=path" \
|
||||
"$FZF_CTRL_T_OPTS --multi --print0")
|
||||
|
||||
set -lx FZF_DEFAULT_COMMAND "$FZF_CTRL_T_COMMAND"
|
||||
set -lx FZF_DEFAULT_OPTS_FILE
|
||||
|
||||
if set -l result (eval (__fzfcmd) --query=$fzf_query)
|
||||
if set -l result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | string split0)
|
||||
# Remove last token from commandline.
|
||||
commandline -t ''
|
||||
for i in $result
|
||||
@@ -154,13 +188,13 @@ function fzf_key_bindings
|
||||
set -l prefix $commandline[3]
|
||||
|
||||
set -lx FZF_DEFAULT_OPTS (__fzf_defaults \
|
||||
"--reverse --walker=dir,follow,hidden --scheme=path --walker-root=$dir" \
|
||||
"$FZF_ALT_C_OPTS --no-multi")
|
||||
"--reverse --walker=dir,follow,hidden --scheme=path" \
|
||||
"$FZF_ALT_C_OPTS --no-multi --print0")
|
||||
|
||||
set -lx FZF_DEFAULT_OPTS_FILE
|
||||
set -lx FZF_DEFAULT_COMMAND "$FZF_ALT_C_COMMAND"
|
||||
|
||||
if set -l result (eval (__fzfcmd) --query=$fzf_query)
|
||||
if set -l result (eval (__fzfcmd) --query=$fzf_query --walker-root=$dir | string split0)
|
||||
cd -- $result
|
||||
commandline -rt -- $prefix
|
||||
end
|
||||
|
@@ -631,6 +631,7 @@ type Options struct {
|
||||
MEMProfile string
|
||||
BlockProfile string
|
||||
MutexProfile string
|
||||
TtyDefault string
|
||||
}
|
||||
|
||||
func filterNonEmpty(input []string) []string {
|
||||
@@ -730,6 +731,7 @@ func defaultOptions() *Options {
|
||||
WalkerOpts: walkerOpts{file: true, hidden: true, follow: true},
|
||||
WalkerRoot: []string{"."},
|
||||
WalkerSkip: []string{".git", "node_modules"},
|
||||
TtyDefault: tui.DefaultTtyDevice,
|
||||
Help: false,
|
||||
Version: false}
|
||||
}
|
||||
@@ -2336,6 +2338,12 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
||||
}
|
||||
case "--no-tmux":
|
||||
opts.Tmux = nil
|
||||
case "--tty-default":
|
||||
if opts.TtyDefault, err = nextString("tty device name required"); err != nil {
|
||||
return err
|
||||
}
|
||||
case "--no-tty-default":
|
||||
opts.TtyDefault = ""
|
||||
case "--force-tty-in":
|
||||
// NOTE: We need this because `system('fzf --tmux < /dev/tty')` doesn't
|
||||
// work on Neovim. Same as '-' option of fzf-tmux.
|
||||
|
@@ -144,7 +144,7 @@ func runProxy(commandPrefix string, cmdBuilder func(temp string, needBash bool)
|
||||
env = elems[1:]
|
||||
}
|
||||
executor := util.NewExecutor(opts.WithShell)
|
||||
ttyin, err := tui.TtyIn()
|
||||
ttyin, err := tui.TtyIn(opts.TtyDefault)
|
||||
if err != nil {
|
||||
return ExitError, err
|
||||
}
|
||||
|
@@ -323,7 +323,9 @@ func (r *Reader) readFiles(roots []string, opts walkerOpts, ignores []string) bo
|
||||
return filepath.SkipDir
|
||||
}
|
||||
}
|
||||
path += sep
|
||||
if path != sep {
|
||||
path += sep
|
||||
}
|
||||
}
|
||||
if ((opts.file && !isDir) || (opts.dir && isDir)) && r.pusher(stringBytes(path)) {
|
||||
atomic.StoreInt32(&r.event, int32(EvtReadNew))
|
||||
|
@@ -381,6 +381,7 @@ type Terminal struct {
|
||||
slab *util.Slab
|
||||
theme *tui.ColorTheme
|
||||
tui tui.Renderer
|
||||
ttyDefault string
|
||||
ttyin *os.File
|
||||
executing *util.AtomicBool
|
||||
termSize tui.TermSize
|
||||
@@ -809,7 +810,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
||||
// when you run fzf multiple times in your Go program. Closing it is known to
|
||||
// cause problems with 'become' action and invalid terminal state after exit.
|
||||
if ttyin == nil {
|
||||
if ttyin, err = tui.TtyIn(); err != nil {
|
||||
if ttyin, err = tui.TtyIn(opts.TtyDefault); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -817,7 +818,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
||||
if tui.HasFullscreenRenderer() {
|
||||
renderer = tui.NewFullscreenRenderer(opts.Theme, opts.Black, opts.Mouse)
|
||||
} else {
|
||||
renderer, err = tui.NewLightRenderer(ttyin, opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, opts.ClearOnExit,
|
||||
renderer, err = tui.NewLightRenderer(opts.TtyDefault, ttyin, opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, opts.ClearOnExit,
|
||||
true, func(h int) int { return h })
|
||||
}
|
||||
} else {
|
||||
@@ -833,7 +834,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
||||
effectiveMinHeight += borderLines(opts.BorderShape)
|
||||
return util.Min(termHeight, util.Max(evaluateHeight(opts, termHeight), effectiveMinHeight))
|
||||
}
|
||||
renderer, err = tui.NewLightRenderer(ttyin, opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, opts.ClearOnExit, false, maxHeightFunc)
|
||||
renderer, err = tui.NewLightRenderer(opts.TtyDefault, ttyin, opts.Theme, opts.Black, opts.Mouse, opts.Tabstop, opts.ClearOnExit, false, maxHeightFunc)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -967,6 +968,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
|
||||
keyChan: make(chan tui.Event),
|
||||
eventChan: make(chan tui.Event, 6), // start | (load + result + zero|one) | (focus) | (resize)
|
||||
tui: renderer,
|
||||
ttyDefault: opts.TtyDefault,
|
||||
ttyin: ttyin,
|
||||
initFunc: func() error { return renderer.Init() },
|
||||
executing: util.NewAtomicBool(false),
|
||||
@@ -1089,9 +1091,13 @@ func (t *Terminal) environImpl(forPreview bool) []string {
|
||||
env = append(env, "FZF_ACTION="+t.lastAction.Name())
|
||||
env = append(env, "FZF_KEY="+t.lastKey)
|
||||
env = append(env, "FZF_PROMPT="+string(t.promptString))
|
||||
env = append(env, "FZF_GHOST="+string(t.ghost))
|
||||
env = append(env, "FZF_POINTER="+string(t.pointer))
|
||||
env = append(env, "FZF_PREVIEW_LABEL="+t.previewLabelOpts.label)
|
||||
env = append(env, "FZF_BORDER_LABEL="+t.borderLabelOpts.label)
|
||||
env = append(env, "FZF_LIST_LABEL="+t.listLabelOpts.label)
|
||||
env = append(env, "FZF_INPUT_LABEL="+t.inputLabelOpts.label)
|
||||
env = append(env, "FZF_HEADER_LABEL="+t.headerLabelOpts.label)
|
||||
if len(t.nthCurrent) > 0 {
|
||||
env = append(env, "FZF_NTH="+RangesToString(t.nthCurrent))
|
||||
}
|
||||
@@ -2647,6 +2653,9 @@ func (t *Terminal) headerIndent(borderShape tui.BorderShape) int {
|
||||
}
|
||||
if borderShape.HasLeft() {
|
||||
indentSize -= 1 + t.borderWidth
|
||||
if indentSize < 0 {
|
||||
indentSize = 0
|
||||
}
|
||||
}
|
||||
return indentSize
|
||||
}
|
||||
@@ -3144,7 +3153,7 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
|
||||
wasWrapped = true
|
||||
}
|
||||
|
||||
if len(line) > 0 && line[len(line)-1] == '\n' {
|
||||
if len(line) > 0 && line[len(line)-1] == '\n' && lineOffset < len(lines)-1 {
|
||||
line = line[:len(line)-1]
|
||||
} else {
|
||||
wrapped = true
|
||||
@@ -4039,7 +4048,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
|
||||
t.executing.Set(true)
|
||||
if !background {
|
||||
// Open a separate handle for tty input
|
||||
if in, _ := tui.TtyIn(); in != nil {
|
||||
if in, _ := tui.TtyIn(t.ttyDefault); in != nil {
|
||||
cmd.Stdin = in
|
||||
if in != os.Stdin {
|
||||
defer in.Close()
|
||||
@@ -4048,7 +4057,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
if !util.IsTty(os.Stdout) {
|
||||
if out, _ := tui.TtyOut(); out != nil {
|
||||
if out, _ := tui.TtyOut(t.ttyDefault); out != nil {
|
||||
cmd.Stdout = out
|
||||
defer out.Close()
|
||||
}
|
||||
@@ -4056,7 +4065,7 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
|
||||
|
||||
cmd.Stderr = os.Stderr
|
||||
if !util.IsTty(os.Stderr) {
|
||||
if out, _ := tui.TtyOut(); out != nil {
|
||||
if out, _ := tui.TtyOut(t.ttyDefault); out != nil {
|
||||
cmd.Stderr = out
|
||||
defer out.Close()
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ const (
|
||||
maxInputBuffer = 1024 * 1024
|
||||
)
|
||||
|
||||
const consoleDevice string = "/dev/tty"
|
||||
const DefaultTtyDevice string = "/dev/tty"
|
||||
|
||||
var offsetRegexp = regexp.MustCompile("(.*?)\x00?\x1b\\[([0-9]+);([0-9]+)R")
|
||||
var offsetRegexpBegin = regexp.MustCompile("^\x1b\\[[0-9]+;[0-9]+R")
|
||||
@@ -146,8 +146,8 @@ type LightWindow struct {
|
||||
wrapSignWidth int
|
||||
}
|
||||
|
||||
func NewLightRenderer(ttyin *os.File, theme *ColorTheme, forceBlack bool, mouse bool, tabstop int, clearOnExit bool, fullscreen bool, maxHeightFunc func(int) int) (Renderer, error) {
|
||||
out, err := openTtyOut()
|
||||
func NewLightRenderer(ttyDefault string, ttyin *os.File, theme *ColorTheme, forceBlack bool, mouse bool, tabstop int, clearOnExit bool, fullscreen bool, maxHeightFunc func(int) int) (Renderer, error) {
|
||||
out, err := openTtyOut(ttyDefault)
|
||||
if err != nil {
|
||||
out = os.Stderr
|
||||
}
|
||||
@@ -271,7 +271,7 @@ func (r *LightRenderer) getBytesInternal(buffer []byte, nonblock bool) ([]byte,
|
||||
c, ok := r.getch(nonblock)
|
||||
if !nonblock && !ok {
|
||||
r.Close()
|
||||
return nil, errors.New("failed to read " + consoleDevice)
|
||||
return nil, errors.New("failed to read " + DefaultTtyDevice)
|
||||
}
|
||||
|
||||
retries := 0
|
||||
|
@@ -42,26 +42,35 @@ func (r *LightRenderer) closePlatform() {
|
||||
r.ttyout.Close()
|
||||
}
|
||||
|
||||
func openTty(mode int) (*os.File, error) {
|
||||
in, err := os.OpenFile(consoleDevice, mode, 0)
|
||||
if err != nil {
|
||||
func openTty(ttyDefault string, mode int) (*os.File, error) {
|
||||
var in *os.File
|
||||
var err error
|
||||
if len(ttyDefault) > 0 {
|
||||
in, err = os.OpenFile(ttyDefault, mode, 0)
|
||||
}
|
||||
if in == nil || err != nil || ttyDefault != DefaultTtyDevice && !util.IsTty(in) {
|
||||
tty := ttyname()
|
||||
if len(tty) > 0 {
|
||||
if in, err := os.OpenFile(tty, mode, 0); err == nil {
|
||||
return in, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("failed to open " + consoleDevice)
|
||||
if ttyDefault != DefaultTtyDevice {
|
||||
if in, err = os.OpenFile(DefaultTtyDevice, mode, 0); err == nil {
|
||||
return in, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("failed to open " + DefaultTtyDevice)
|
||||
}
|
||||
return in, nil
|
||||
}
|
||||
|
||||
func openTtyIn() (*os.File, error) {
|
||||
return openTty(syscall.O_RDONLY)
|
||||
func openTtyIn(ttyDefault string) (*os.File, error) {
|
||||
return openTty(ttyDefault, syscall.O_RDONLY)
|
||||
}
|
||||
|
||||
func openTtyOut() (*os.File, error) {
|
||||
return openTty(syscall.O_WRONLY)
|
||||
func openTtyOut(ttyDefault string) (*os.File, error) {
|
||||
return openTty(ttyDefault, syscall.O_WRONLY)
|
||||
}
|
||||
|
||||
func (r *LightRenderer) setupTerminal() {
|
||||
|
@@ -76,12 +76,12 @@ func (r *LightRenderer) closePlatform() {
|
||||
windows.SetConsoleMode(windows.Handle(r.inHandle), r.origStateInput)
|
||||
}
|
||||
|
||||
func openTtyIn() (*os.File, error) {
|
||||
func openTtyIn(ttyDefault string) (*os.File, error) {
|
||||
// not used
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func openTtyOut() (*os.File, error) {
|
||||
func openTtyOut(ttyDefault string) (*os.File, error) {
|
||||
return os.Stderr, nil
|
||||
}
|
||||
|
||||
|
@@ -44,11 +44,11 @@ func ttyname() string {
|
||||
}
|
||||
|
||||
// TtyIn returns terminal device to read user input
|
||||
func TtyIn() (*os.File, error) {
|
||||
return openTtyIn()
|
||||
func TtyIn(ttyDefault string) (*os.File, error) {
|
||||
return openTtyIn(ttyDefault)
|
||||
}
|
||||
|
||||
// TtyIn returns terminal device to write to
|
||||
func TtyOut() (*os.File, error) {
|
||||
return openTtyOut()
|
||||
func TtyOut(ttyDefault string) (*os.File, error) {
|
||||
return openTtyOut(ttyDefault)
|
||||
}
|
||||
|
@@ -11,11 +11,11 @@ func ttyname() string {
|
||||
}
|
||||
|
||||
// TtyIn on Windows returns os.Stdin
|
||||
func TtyIn() (*os.File, error) {
|
||||
func TtyIn(ttyDefault string) (*os.File, error) {
|
||||
return os.Stdin, nil
|
||||
}
|
||||
|
||||
// TtyOut on Windows returns nil
|
||||
func TtyOut() (*os.File, error) {
|
||||
func TtyOut(ttyDefault string) (*os.File, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
@@ -1632,14 +1632,16 @@ class TestCore < TestInteractive
|
||||
end
|
||||
|
||||
def test_env_vars
|
||||
def to_vars(lines)
|
||||
lines.select { it.start_with?('FZF_') }.to_h do
|
||||
key, val = it.split('=', 2)
|
||||
def env_vars
|
||||
return {} unless File.exist?(tempname)
|
||||
|
||||
File.readlines(tempname).select { it.start_with?('FZF_') }.to_h do
|
||||
key, val = it.chomp.split('=', 2)
|
||||
[key.to_sym, val]
|
||||
end
|
||||
end
|
||||
|
||||
tmux.send_keys %(seq 100 | #{FZF} --multi --reverse --preview-window up,99%,noborder --preview 'env | grep ^FZF_ | sort' --no-input --bind enter:show-input+refresh-preview,space:disable-search+refresh-preview), :Enter
|
||||
tmux.send_keys %(seq 100 | #{FZF} --multi --reverse --preview-window 0 --preview 'env | grep ^FZF_ | sort > #{tempname}' --no-input --bind enter:show-input+refresh-preview,space:disable-search+refresh-preview), :Enter
|
||||
expected = {
|
||||
FZF_TOTAL_COUNT: '100',
|
||||
FZF_MATCH_COUNT: '100',
|
||||
@@ -1648,31 +1650,32 @@ class TestCore < TestInteractive
|
||||
FZF_KEY: '',
|
||||
FZF_POS: '1',
|
||||
FZF_QUERY: '',
|
||||
FZF_PROMPT: '>',
|
||||
FZF_POINTER: '>',
|
||||
FZF_PROMPT: '> ',
|
||||
FZF_INPUT_STATE: 'hidden'
|
||||
}
|
||||
tmux.until do |lines|
|
||||
assert_equal expected, to_vars(lines).slice(*expected.keys)
|
||||
tmux.until do
|
||||
assert_equal expected, env_vars.slice(*expected.keys)
|
||||
end
|
||||
tmux.send_keys :Enter
|
||||
tmux.until do |lines|
|
||||
tmux.until do
|
||||
expected.merge!(FZF_INPUT_STATE: 'enabled', FZF_ACTION: 'show-input', FZF_KEY: 'enter')
|
||||
assert_equal expected, to_vars(lines).slice(*expected.keys)
|
||||
assert_equal expected, env_vars.slice(*expected.keys)
|
||||
end
|
||||
tmux.send_keys :Tab, :Tab
|
||||
tmux.until do |lines|
|
||||
tmux.until do
|
||||
expected.merge!(FZF_ACTION: 'toggle-down', FZF_KEY: 'tab', FZF_POS: '3', FZF_SELECT_COUNT: '2')
|
||||
assert_equal expected, to_vars(lines).slice(*expected.keys)
|
||||
assert_equal expected, env_vars.slice(*expected.keys)
|
||||
end
|
||||
tmux.send_keys '99'
|
||||
tmux.until do |lines|
|
||||
tmux.until do
|
||||
expected.merge!(FZF_ACTION: 'char', FZF_KEY: '9', FZF_QUERY: '99', FZF_MATCH_COUNT: '1', FZF_POS: '1')
|
||||
assert_equal expected, to_vars(lines).slice(*expected.keys)
|
||||
assert_equal expected, env_vars.slice(*expected.keys)
|
||||
end
|
||||
tmux.send_keys :Space
|
||||
tmux.until do |lines|
|
||||
tmux.until do
|
||||
expected.merge!(FZF_INPUT_STATE: 'disabled', FZF_ACTION: 'disable-search', FZF_KEY: 'space')
|
||||
assert_equal expected, to_vars(lines).slice(*expected.keys)
|
||||
assert_equal expected, env_vars.slice(*expected.keys)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1931,4 +1934,9 @@ class TestCore < TestInteractive
|
||||
tmux.send_keys :Space
|
||||
tmux.until { |lines| assert lines.any_include?('bar') }
|
||||
end
|
||||
|
||||
def test_trailing_new_line
|
||||
tmux.send_keys %(echo -en "foo\n" | fzf --read0 --no-multi-line), :Enter
|
||||
tmux.until { |lines| assert_includes lines, '> foo␊' }
|
||||
end
|
||||
end
|
||||
|
@@ -991,4 +991,16 @@ class TestLayout < TestInteractive
|
||||
BLOCK
|
||||
tmux.until { assert_block(block, it) }
|
||||
end
|
||||
|
||||
def test_header_border_no_pointer_and_marker
|
||||
tmux.send_keys %(seq 10 | #{FZF} --header-lines 1 --header-border sharp --no-list-border --pointer '' --marker ''), :Enter
|
||||
block = <<~BLOCK
|
||||
┌──────
|
||||
│ 1
|
||||
└──────
|
||||
9/9 ─
|
||||
>
|
||||
BLOCK
|
||||
tmux.until { assert_block(block, it) }
|
||||
end
|
||||
end
|
||||
|
Reference in New Issue
Block a user