From 7e6339979fc83570d4f7d340a17245eee1f2995a Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sun, 6 Sep 2026 19:36:40 +0900 Subject: [PATCH] Fix stale progress in --listen status payload (#4907) - progress was set to 100 by every UpdateList, partial results included, and never reset when a search started - So a GET could report a new query, the previous result set, and progress 100, and a client had no way to tell it was stale - Now cleared where the search is requested and set to 100 only on the final result. Query and the reset happen in the same locked section that dumpStatus locks, so query == X with progress 100 means the matches belong to X Fix #4903 --- CHANGELOG.md | 2 ++ src/terminal.go | 5 ++++- test/test_server.rb | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fbe7661..a8bb7003 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ CHANGELOG - Fixed an escape sequence split across reads being parsed as a fragment, which leaked the rest into the query (#4899) - e.g. A terminal answering the startup `DECRQM` query late left `?2004;2$y`, CTRL-UP left `5A`, and SGR mouse input left `0;1;1M` - Fixed `--tiebreak=pathname` not detecting the last path separator when the line contains a non-ASCII character before it (#4902) +- Fixed `progress` in the `--listen` status payload staying at 100 while a new search was running, which made a snapshot with a new query and the previous result set look complete (#4903) + - It is now reset when a search starts and reaches 100 on the final result, so `progress` of 100 means the matches belong to the query reported next to them - Fixed adaptive height not reserving a line for the divider of an inline header or footer border, so the list came up one line short for each of them (#4904) - e.g. `seq 10 | fzf --height=~100% --list-border --header-lines=1 --header-lines-border=inline` - Vim plugin diff --git a/src/terminal.go b/src/terminal.go index 59016476..ec0e7609 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -2059,7 +2059,9 @@ func (t *Terminal) UpdateList(result MatchResult) { prevIndex = t.targetIndex t.targetIndex = minItem.Index() } - t.progress = 100 + if result.final() { + t.progress = 100 + } t.merger = merger t.resultMerger = merger t.passMerger = result.passMerger @@ -8597,6 +8599,7 @@ func (t *Terminal) Loop() error { reload := changed || newCommand != nil if reload { t.wait.searching = true + t.progress = 0 } var reloadRequest *searchRequest if reload { diff --git a/test/test_server.rb b/test/test_server.rb index cbfbab04..d35140db 100644 --- a/test/test_server.rb +++ b/test/test_server.rb @@ -55,6 +55,29 @@ class TestServer < TestInteractive end end + def test_listen_progress + tmux.send_keys "seq 10 | #{FZF} --listen 6266", :Enter + tmux.until { |lines| assert_equal 10, lines.match_count } + uri = URI('http://localhost:6266') + state = -> { JSON.parse(Net::HTTP.get(uri), symbolize_names: true) } + + # Idle: the last search is complete + assert_equal 100, state.call[:progress] + + # While a search is running, progress is not left at 100 from the + # previous one, so it can tell a settled snapshot from a stale one + Net::HTTP.post(uri, 'reload(sleep 1; seq 100)') + tmux.until { assert_equal 0, state.call[:progress] } + + # Settled: matches belong to the snapshot that reports 100 + tmux.until { |lines| assert_equal 100, lines.match_count } + tmux.until do + st = state.call + assert_equal 100, st[:progress] + assert_equal 100, st[:matchCount] + end + end + def test_listen_with_api_key uri = URI('http://localhost:6266') tmux.send_keys 'seq 10 | FZF_API_KEY=123abc fzf --listen 6266', :Enter