From 665763957838ef50cc11d61c99ec5eb94a2c6dde Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sat, 16 May 2026 18:22:21 +0900 Subject: [PATCH] Export both FZF_IDLE_TIME and FZF_IDLE_TIME_MS Seconds is more ergonomic for shell threshold checks; milliseconds covers sub-second every() bindings. Exporting both lets the same script pick whichever resolution matches its every() interval. --- CHANGELOG.md | 6 +++--- man/man1/fzf.1 | 14 ++++++++------ src/terminal.go | 4 +++- test/test_core.rb | 17 ++++++++--------- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56948163..4cc0700b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,15 +4,15 @@ CHANGELOG 0.73.0 ------ - Timer-driven `every(N)` event for `--bind`, where `N` is seconds (fractional, floored to `0.01`). Ticks that overlap an in-flight action are coalesced, so a slow `reload` cannot accumulate a backlog. -- New `FZF_IDLE_MS` environment variable (milliseconds since the last user activity) exported to child processes. Pair with `every(N)` to build idle-based behavior such as auto-accept or auto-quit (#1211). +- New `FZF_IDLE_TIME` (whole seconds) and `FZF_IDLE_TIME_MS` (milliseconds) environment variables exported to child processes, holding the elapsed time since the last user activity. Pair with `every(N)` to build idle-based behavior such as auto-accept or auto-quit (#1211). ```sh # Live process list; --track --id-nth 2 keeps the cursor on the same PID across reloads fzf --header-lines 1 --track --id-nth 2 --bind 'start,every(2):reload-sync:ps -ef' # Auto-accept after 10 seconds of inactivity, with a countdown in the footer after 5s fzf --bind 'every(1):bg-transform: - if [[ $FZF_IDLE_MS -lt 5000 ]]; then echo change-footer: - elif [[ $FZF_IDLE_MS -lt 10000 ]]; then echo "change-footer:auto-accept in $(((10000 - FZF_IDLE_MS) / 1000))s" + if [[ $FZF_IDLE_TIME -lt 5 ]]; then echo change-footer: + elif [[ $FZF_IDLE_TIME -lt 10 ]]; then echo "change-footer:auto-accept in $((10 - FZF_IDLE_TIME))s" else echo accept fi' ``` diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index a8ee427a..ad5c83c3 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -1500,7 +1500,9 @@ fzf exports the following environment variables to its child processes. .br .BR FZF_KEY " The name of the last key pressed" .br -.BR FZF_IDLE_MS " Milliseconds since the last user activity" +.BR FZF_IDLE_TIME " Whole seconds since the last user activity" +.br +.BR FZF_IDLE_TIME_MS " Milliseconds since the last user activity" .br .BR FZF_PORT " Port number when \-\-listen option is used" .br @@ -1947,9 +1949,9 @@ Triggered every \fIN\fR seconds (\fIN\fR can be a fractional number, e.g. \fB0.5\fR). The minimum interval is \fB0.01\fR seconds; values are floored to that. -Combine with the \fBFZF_IDLE_MS\fR environment variable (milliseconds -since the last user activity) to build idle\-based behavior without a -separate event. +Combine with the \fBFZF_IDLE_TIME\fR (whole seconds) and +\fBFZF_IDLE_TIME_MS\fR (milliseconds) environment variables to build +idle\-based behavior without a separate event. e.g. \fB# Live process list, refreshed every 2 seconds. @@ -1959,8 +1961,8 @@ e.g. # Auto\-accept after 10 seconds of inactivity, with a countdown in the footer after 5s. fzf \-\-bind 'every(1):bg\-transform: - if [[ $FZF_IDLE_MS \-lt 5000 ]]; then echo change\-footer: - elif [[ $FZF_IDLE_MS \-lt 10000 ]]; then echo "change\-footer:auto\-accept in $(((10000 \- FZF_IDLE_MS) / 1000))s" + if [[ $FZF_IDLE_TIME \-lt 5 ]]; then echo change\-footer: + elif [[ $FZF_IDLE_TIME \-lt 10 ]]; then echo "change\-footer:auto\-accept in $((10 \- FZF_IDLE_TIME))s" else echo accept fi'\fR .RE diff --git a/src/terminal.go b/src/terminal.go index a6e94e1f..8dae78ac 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -1389,7 +1389,9 @@ func (t *Terminal) environImpl(forPreview bool) []string { env = append(env, "FZF_QUERY="+string(t.input)) env = append(env, "FZF_ACTION="+t.lastAction.Name()) env = append(env, "FZF_KEY="+t.lastKey) - env = append(env, fmt.Sprintf("FZF_IDLE_MS=%d", time.Since(t.lastActivity).Milliseconds())) + idleMs := time.Since(t.lastActivity).Milliseconds() + env = append(env, fmt.Sprintf("FZF_IDLE_TIME=%d", idleMs/1000)) + env = append(env, fmt.Sprintf("FZF_IDLE_TIME_MS=%d", idleMs)) env = append(env, "FZF_PROMPT="+string(t.promptString)) env = append(env, "FZF_GHOST="+string(t.ghost)) env = append(env, "FZF_POINTER="+string(t.pointer)) diff --git a/test/test_core.rb b/test/test_core.rb index 3788c8c8..cad5f26b 100644 --- a/test/test_core.rb +++ b/test/test_core.rb @@ -1431,20 +1431,19 @@ class TestCore < TestInteractive assert_includes tmux.capture[-3], 'STOPPED' end - def test_fzf_idle_ms_env - # FZF_IDLE_MS, combined with every(), implements idle-based behavior. - # Print seconds derived from milliseconds for stable assertions. - tmux.send_keys %(seq 100 | fzf --bind 'every(0.2):transform-header(echo "idle=$((FZF_IDLE_MS / 1000))")'), :Enter + def test_fzf_idle_time_env + # FZF_IDLE_TIME + FZF_IDLE_TIME_MS combined with every() implement idle-based behavior. + tmux.send_keys %(seq 100 | fzf --bind 'every(0.2):transform-header(echo "s=$FZF_IDLE_TIME ms_ok=$((FZF_IDLE_TIME_MS / 1000 == FZF_IDLE_TIME))")'), :Enter tmux.until { |lines| assert_equal 100, lines.match_count } - # Idle counter advances without any input - tmux.until { |lines| assert_includes lines[-3], 'idle=1' } - tmux.until { |lines| assert_includes lines[-3], 'idle=2' } + # Idle counter advances without any input; ms/1000 stays consistent with seconds. + tmux.until { |lines| assert_includes lines[-3], 's=1 ms_ok=1' } + tmux.until { |lines| assert_includes lines[-3], 's=2 ms_ok=1' } # Any keystroke resets the counter tmux.send_keys 'x' - tmux.until { |lines| assert_includes lines[-3], 'idle=0' } + tmux.until { |lines| assert_includes lines[-3], 's=0 ms_ok=1' } tmux.send_keys :BSpace # And it advances again afterwards - tmux.until { |lines| assert_includes lines[-3], 'idle=1' } + tmux.until { |lines| assert_includes lines[-3], 's=1 ms_ok=1' } end def test_every_event_rejects_invalid_arg