Compare commits

..

7 Commits

Author SHA1 Message Date
Junegunn Choi
e8a39eeb0f Ignore invalid byte sequence in UTF-8 2024-07-29 20:24:35 +09:00
Junegunn Choi
c790ab2024 [bash] CTRL-R: Show timestamp and syntax highlighted command 2024-07-29 20:24:35 +09:00
Junegunn Choi
d9404fcce4 Remove stale comment 2024-07-28 10:13:41 +09:00
junegunn
5c01fee5a9 Deploying to master from @ junegunn/fzf@9b27d68a37 🚀 2024-07-28 00:02:00 +00:00
Junegunn Choi
9b27d68a37 Fix build error 2024-07-27 19:13:24 +09:00
Junegunn Choi
b99d884e57 Minor refactoring 2024-07-27 18:59:50 +09:00
Junegunn Choi
587df594b8 Fix incompatibility of adaptive height and 'start:reload'
This command would cause a deadlock and make fzf crash:

  fzf --bind 'start:reload:ls' --height ~100%

Because,

1. 'start' event is handled by Terminal
2. When 'reload' is bound to 'start', fzf avoids starting the initial reader
3. Terminal waits for the initial input to find the right height when
   adaptive height is used
4. Because the initial reader is not started, Terminal never gets the
   initial list
5. No chance to trigger 'start:reload', hence deadlock

This commit fixes the above problem by extracting the reload command
bound to 'start' event and starting the initial reader with that command
instead of letting Terminal start it.

This commit also makes the environment variables available to
$FZF_DEFAULT_COMMAND.

  FZF_DEFAULT_COMMAND='echo $FZF_QUERY' fzf --query foo

Fix #3944
2024-07-27 11:30:25 +09:00
8 changed files with 87 additions and 32 deletions

View File

@@ -1,6 +1,14 @@
CHANGELOG
=========
0.54.3
------
- Fixed incompatibility of adaptive height and 'start:reload'
- Environment variables are now available to `$FZF_DEFAULT_COMMAND`
```sh
FZF_DEFAULT_COMMAND='echo $FZF_QUERY' fzf --query foo
```
0.54.2
------
- Fixed incorrect syntax highlighting of truncated multi-line entries

File diff suppressed because one or more lines are too long

View File

@@ -54,7 +54,45 @@ __fzf_cd__() {
) && printf 'builtin cd -- %q' "$(builtin unset CDPATH && builtin cd -- "$dir" && builtin pwd)"
}
if command -v perl > /dev/null; then
if command -v ruby > /dev/null; then
__fzf_history__() {
local n output
builtin history -w /tmp/fzf-bash-history
output=$(
ruby -e '
fmt = begin
require "rouge"
formatter = Rouge::Formatters::Terminal256.new(Rouge::Themes::Monokai.new)
lexer = Rouge::Lexers::Shell.new
lambda { |c| formatter.format(lexer.lex(c)) }
rescue LoadError
lambda { |c| c }
end
h = {}
i = 0
File.read("/tmp/fzf-bash-history").encode!("UTF-8", "UTF-8", :invalid => :replace).scan(/^#([0-9]+)$\n(.*?)\n(?=^#[0-9]+$|\z)/m) do |t, c|
next if c.empty?
h.delete(c)
h[c] = [i += 1, t]
end
h.to_a.reverse.each do |c, it|
i, t = it
print "\x1b[33m#{i}\t\x1b[32m#{Time.at(t.to_i).strftime(%[%F %T])}\x1b[m "
print fmt[c.gsub("\n", "\n\t")]
print "\0"
end
' | FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n1,4.. --ansi --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '"$'\t'"↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} +m --read0") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) --query "$READLINE_LINE" --bind 'enter:become:echo {4..}'
) || return
READLINE_LINE=$(command perl -pe 's/^\d*\t//' <<< "$output")
if [[ -z "$READLINE_POINT" ]]; then
echo "$READLINE_LINE"
else
READLINE_POINT=0x7fffffff
fi
}
elif command -v perl > /dev/null; then
__fzf_history__() {
local output script
script='BEGIN { getc; $/ = "\n\t"; $HISTCOUNT = $ENV{last_hist} + 1 } s/^[ *]//; s/\n/\n\t/gm; print $HISTCOUNT - $. . "\t$_" if !$seen{$_}++'

View File

@@ -58,7 +58,6 @@ const (
const (
EvtReadNew util.EventType = iota
EvtReadFin
EvtReadNone
EvtSearchNew
EvtSearchProgress
EvtSearchFin

View File

@@ -146,8 +146,25 @@ func Run(opts *Options) (int, error) {
// Process executor
executor := util.NewExecutor(opts.WithShell)
// Terminal I/O
var terminal *Terminal
var err error
var initialEnv []string
initialReload := opts.extractReloadOnStart()
if opts.Filter == nil {
terminal, err = NewTerminal(opts, eventBox, executor)
if err != nil {
return ExitError, err
}
if len(initialReload) > 0 {
var temps []string
initialReload, temps = terminal.replacePlaceholderInInitialCommand(initialReload)
initialEnv = terminal.environ()
defer removeFiles(temps)
}
}
// Reader
reloadOnStart := opts.reloadOnStart()
streamingFilter := opts.Filter != nil && !sort && !opts.Tac && !opts.Sync
var reader *Reader
if !streamingFilter {
@@ -155,12 +172,7 @@ func Run(opts *Options) (int, error) {
return chunkList.Push(data)
}, eventBox, executor, opts.ReadZero, opts.Filter == nil)
if reloadOnStart {
// reload or reload-sync action is bound to 'start' event, no need to start the reader
eventBox.Set(EvtReadNone, nil)
} else {
go reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip)
}
go reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip, initialReload, initialEnv)
}
// Matcher
@@ -212,7 +224,7 @@ func Run(opts *Options) (int, error) {
}
return false
}, eventBox, executor, opts.ReadZero, false)
reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip)
reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip, initialReload, initialEnv)
} else {
eventBox.Unwatch(EvtReadNew)
eventBox.WaitFor(EvtReadFin)
@@ -234,8 +246,7 @@ func Run(opts *Options) (int, error) {
}
// Synchronous search
sync := opts.Sync && !reloadOnStart
if sync {
if opts.Sync {
eventBox.Unwatch(EvtReadNew)
eventBox.WaitFor(EvtReadFin)
}
@@ -244,18 +255,14 @@ func Run(opts *Options) (int, error) {
go matcher.Loop()
defer matcher.Stop()
// Terminal I/O
terminal, err := NewTerminal(opts, eventBox, executor)
if err != nil {
return ExitError, err
}
// Handling adaptive height
maxFit := 0 // Maximum number of items that can fit on screen
padHeight := 0
heightUnknown := opts.Height.auto
if heightUnknown {
maxFit, padHeight = terminal.MaxFitAndPad()
}
deferred := opts.Select1 || opts.Exit0 || sync
deferred := opts.Select1 || opts.Exit0 || opts.Sync
go terminal.Loop()
if !deferred && !heightUnknown {
// Start right away
@@ -322,9 +329,6 @@ func Run(opts *Options) (int, error) {
err = quitSignal.err
stop = true
return
case EvtReadNone:
reading = false
terminal.UpdateCount(0, false, nil)
case EvtReadNew, EvtReadFin:
if evt == EvtReadFin && nextCommand != nil {
restart(*nextCommand, nextEnviron)

View File

@@ -2964,17 +2964,18 @@ func ParseOptions(useDefaults bool, args []string) (*Options, error) {
return opts, nil
}
func (opts *Options) reloadOnStart() bool {
// Not compatible with --filter
if opts.Filter != nil {
return false
}
func (opts *Options) extractReloadOnStart() string {
cmd := ""
if actions, prs := opts.Keymap[tui.Start.AsEvent()]; prs {
filtered := []*action{}
for _, action := range actions {
if action.t == actReload || action.t == actReloadSync {
return true
cmd = action.a
} else {
filtered = append(filtered, action)
}
}
opts.Keymap[tui.Start.AsEvent()] = filtered
}
return false
return cmd
}

View File

@@ -111,18 +111,19 @@ func (r *Reader) readChannel(inputChan chan string) bool {
}
// ReadSource reads data from the default command or from standard input
func (r *Reader) ReadSource(inputChan chan string, root string, opts walkerOpts, ignores []string) {
func (r *Reader) ReadSource(inputChan chan string, root string, opts walkerOpts, ignores []string, initCmd string, initEnv []string) {
r.startEventPoller()
var success bool
if inputChan != nil {
success = r.readChannel(inputChan)
} else if len(initCmd) > 0 {
success = r.readFromCommand(initCmd, initEnv)
} else if util.IsTty(os.Stdin) {
cmd := os.Getenv("FZF_DEFAULT_COMMAND")
if len(cmd) == 0 {
success = r.readFiles(root, opts, ignores)
} else {
// We can't export FZF_* environment variables to the default command
success = r.readFromCommand(cmd, nil)
success = r.readFromCommand(cmd, initEnv)
}
} else {
success = r.readFromStdin()

View File

@@ -2946,6 +2946,10 @@ type replacePlaceholderParams struct {
executor *util.Executor
}
func (t *Terminal) replacePlaceholderInInitialCommand(template string) (string, []string) {
return t.replacePlaceholder(template, false, string(t.input), []*Item{nil, nil})
}
func (t *Terminal) replacePlaceholder(template string, forcePlus bool, input string, list []*Item) (string, []string) {
return replacePlaceholder(replacePlaceholderParams{
template: template,