summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2025-07-02 21:28:11 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2025-07-02 21:28:11 +0900
commitd34675d3c9c0f11e42d29c705e261eb4df794f25 (patch)
tree68630d44e04b331c1de9b2e466e2d4221ab95761
parentce95adc66c27d97b8f1bb56f139b7efd3f53e5c4 (diff)
downloadfzf-d34675d3c9c0f11e42d29c705e261eb4df794f25.tar.gz
Fix panic caused by incorrect update ordering
Fix #4442 Make sure to prepare windows before rendering elements. Thanks to @nugged for the report.
-rw-r--r--src/terminal.go74
-rw-r--r--test/test_core.rb7
2 files changed, 48 insertions, 33 deletions
diff --git a/src/terminal.go b/src/terminal.go
index 9c627e12..7f688d87 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -450,31 +450,35 @@ func (a byTimeOrder) Less(i, j int) bool {
return a[i].at.Before(a[j].at)
}
+// EventTypes are listed in the order of their priority.
const (
- reqPrompt util.EventType = iota
+ reqResize util.EventType = iota
+ reqReinit
+ reqFullRedraw
+ reqRedraw
+
+ reqJump
+ reqPrompt
reqInfo
reqHeader
reqFooter
reqList
- reqJump
- reqActivate
- reqReinit
- reqFullRedraw
- reqResize
- reqRedraw
reqRedrawInputLabel
reqRedrawHeaderLabel
reqRedrawFooterLabel
reqRedrawListLabel
reqRedrawBorderLabel
reqRedrawPreviewLabel
- reqClose
- reqPrintQuery
+
reqPreviewReady
reqPreviewEnqueue
reqPreviewDisplay
reqPreviewRefresh
reqPreviewDelayed
+
+ reqActivate
+ reqClose
+ reqPrintQuery
reqBecome
reqQuit
reqFatal
@@ -1624,14 +1628,12 @@ func (t *Terminal) changeHeader(header string) bool {
return needFullRedraw
}
-func (t *Terminal) changeFooter(footer string) bool {
+func (t *Terminal) changeFooter(footer string) {
var lines []string
if len(footer) > 0 {
lines = strings.Split(strings.TrimSuffix(footer, "\n"), "\n")
}
- needFullRedraw := len(t.footer) != len(lines)
t.footer = lines
- return needFullRedraw
}
// UpdateHeader updates the header
@@ -2889,6 +2891,13 @@ func (t *Terminal) resizeIfNeeded() bool {
return true
}
+ // Check footer window
+ if len(t.footer) > 0 && (t.footerWindow == nil || t.footerWindow.Height() != len(t.footer)) ||
+ len(t.footer) == 0 && t.footerWindow != nil {
+ t.printAll()
+ return true
+ }
+
// Check if the header borders are used and header has changed
allHeaderLines := t.visibleHeaderLines()
primaryHeaderLines := allHeaderLines
@@ -5116,6 +5125,8 @@ func (t *Terminal) Loop() error {
t.uiMutex.Lock()
t.mutex.Lock()
info := false
+ header := false
+ footer := false
for _, key := range keys {
req := util.EventType(key)
value := (*events)[req]
@@ -5153,13 +5164,9 @@ func (t *Terminal) Loop() error {
}
t.printList()
case reqHeader:
- if !t.resizeIfNeeded() {
- t.printHeader()
- }
+ header = true
case reqFooter:
- if !t.resizeIfNeeded() {
- t.printFooter()
- }
+ footer = true
case reqActivate:
t.suppress = false
if t.hasPreviewer() {
@@ -5243,8 +5250,16 @@ func (t *Terminal) Loop() error {
return
}
}
- if info && !t.resizeIfNeeded() {
- t.printInfo()
+ if (info || header || footer) && !t.resizeIfNeeded() {
+ if info {
+ t.printInfo()
+ }
+ if header {
+ t.printHeader()
+ }
+ if footer {
+ t.printFooter()
+ }
}
t.flush()
t.mutex.Unlock()
@@ -5670,24 +5685,17 @@ func (t *Terminal) Loop() error {
t.cx = len(t.input)
case actChangeHeader, actTransformHeader, actBgTransformHeader:
capture(false, func(header string) {
+ // When a dedicated header window is not used, we may need to
+ // update other elements as well.
if t.changeHeader(header) {
- if t.headerWindow != nil {
- // Need to resize header window
- req(reqRedraw)
- } else {
- req(reqHeader, reqList, reqPrompt, reqInfo)
- }
- } else {
- req(reqHeader)
+ req(reqList, reqPrompt, reqInfo)
}
+ req(reqHeader)
})
case actChangeFooter, actTransformFooter, actBgTransformFooter:
capture(false, func(footer string) {
- if t.changeFooter(footer) {
- req(reqRedraw)
- } else {
- req(reqFooter)
- }
+ t.changeFooter(footer)
+ req(reqFooter)
})
case actChangeHeaderLabel, actTransformHeaderLabel, actBgTransformHeaderLabel:
capture(true, func(label string) {
diff --git a/test/test_core.rb b/test/test_core.rb
index b7eba741..b19b1dc5 100644
--- a/test/test_core.rb
+++ b/test/test_core.rb
@@ -1981,4 +1981,11 @@ class TestCore < TestInteractive
refute lines.any_include?('[1]')
end
end
+
+ def test_render_order
+ tmux.send_keys %(seq 100 | #{FZF} --bind='focus:preview(echo boom)+change-footer(bam)'), :Enter
+ tmux.until { assert_equal 100, it.match_count }
+ tmux.until { assert it.any_include?('boom') }
+ tmux.until { assert it.any_include?('bam') }
+ end
end