Fix stale progress in --listen status payload (#4907)
CodeQL / Analyze (go) (push) Canceled after 0s
build / build (push) Canceled after 0s
Test fzf on macOS / build (push) Canceled after 0s

- 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
This commit is contained in:
Junegunn Choi
2026-09-06 19:36:40 +09:00
committed by GitHub
parent 1372d04f79
commit 7e6339979f
3 changed files with 29 additions and 1 deletions
+2
View File
@@ -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
+4 -1
View File
@@ -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 {
+23
View File
@@ -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