m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-13 13:53:47 -05:00

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 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 0.54.2
------ ------
- Fixed incorrect syntax highlighting of truncated multi-line entries - 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)" ) && 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__() { __fzf_history__() {
local output script local output script
script='BEGIN { getc; $/ = "\n\t"; $HISTCOUNT = $ENV{last_hist} + 1 } s/^[ *]//; s/\n/\n\t/gm; print $HISTCOUNT - $. . "\t$_" if !$seen{$_}++' 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 ( const (
EvtReadNew util.EventType = iota EvtReadNew util.EventType = iota
EvtReadFin EvtReadFin
EvtReadNone
EvtSearchNew EvtSearchNew
EvtSearchProgress EvtSearchProgress
EvtSearchFin EvtSearchFin

View File

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

View File

@@ -2964,17 +2964,18 @@ func ParseOptions(useDefaults bool, args []string) (*Options, error) {
return opts, nil return opts, nil
} }
func (opts *Options) reloadOnStart() bool { func (opts *Options) extractReloadOnStart() string {
// Not compatible with --filter cmd := ""
if opts.Filter != nil {
return false
}
if actions, prs := opts.Keymap[tui.Start.AsEvent()]; prs { if actions, prs := opts.Keymap[tui.Start.AsEvent()]; prs {
filtered := []*action{}
for _, action := range actions { for _, action := range actions {
if action.t == actReload || action.t == actReloadSync { 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 // 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() r.startEventPoller()
var success bool var success bool
if inputChan != nil { if inputChan != nil {
success = r.readChannel(inputChan) success = r.readChannel(inputChan)
} else if len(initCmd) > 0 {
success = r.readFromCommand(initCmd, initEnv)
} else if util.IsTty(os.Stdin) { } else if util.IsTty(os.Stdin) {
cmd := os.Getenv("FZF_DEFAULT_COMMAND") cmd := os.Getenv("FZF_DEFAULT_COMMAND")
if len(cmd) == 0 { if len(cmd) == 0 {
success = r.readFiles(root, opts, ignores) success = r.readFiles(root, opts, ignores)
} else { } else {
// We can't export FZF_* environment variables to the default command success = r.readFromCommand(cmd, initEnv)
success = r.readFromCommand(cmd, nil)
} }
} else { } else {
success = r.readFromStdin() success = r.readFromStdin()

View File

@@ -2946,6 +2946,10 @@ type replacePlaceholderParams struct {
executor *util.Executor 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) { func (t *Terminal) replacePlaceholder(template string, forcePlus bool, input string, list []*Item) (string, []string) {
return replacePlaceholder(replacePlaceholderParams{ return replacePlaceholder(replacePlaceholderParams{
template: template, template: template,