mirror of
https://github.com/junegunn/fzf
synced 2026-08-10 20:01:42 +00:00
Separate floating pane border label from pane title
--border-label of a tmux floating pane was stored in the pane title and
read back by pane-border-format as '#{pane_title}', so any program in the
pane could replace the border text by setting the title. On Zellij the
name was only set when the pane was created. Either way the built-in
label actions could not touch it, as fzf draws no border of its own
there.
- tmux: hold the label in pane-scoped @fzf-border-label. Set the option
even without a label, so a later change-border-label has somewhere to
write. Drop select-pane -T; the pane title is left to the user
- Zellij: run 'zellij action rename-pane' on label change. 'zellij run'
has no option to set the environment of the new pane, so the command
exports the target itself
- change-border-label and transform-border-label now update the native
border, through the setter nativeLabelSetter picks by multiplexer
- The pane is named in a __FZF_INTERNAL_ variable that runProxy withholds
from the environment replay, so the same name in the outer environment
cannot redirect the update to another pane
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
package fzf
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLabelUpdater(t *testing.T) {
|
||||
var mu sync.Mutex
|
||||
applied := []int{}
|
||||
// The goroutine can still be running after the test function returns, so
|
||||
// it records the label instead of reporting it, and -1 stands in for one
|
||||
// that could not be parsed
|
||||
updater := &labelUpdater{set: func(label string) {
|
||||
n, err := strconv.Atoi(label)
|
||||
if err != nil {
|
||||
n = -1
|
||||
}
|
||||
mu.Lock()
|
||||
applied = append(applied, n)
|
||||
mu.Unlock()
|
||||
// Long enough for the burst to pile up behind the first update
|
||||
time.Sleep(time.Millisecond)
|
||||
}}
|
||||
|
||||
const last = 99
|
||||
for i := 0; i <= last; i++ {
|
||||
updater.update(strconv.Itoa(i))
|
||||
}
|
||||
|
||||
// However many updates are dropped, the final one is always applied
|
||||
deadline := time.Now().Add(5 * time.Second)
|
||||
for {
|
||||
mu.Lock()
|
||||
done := len(applied) > 0 && applied[len(applied)-1] == last
|
||||
mu.Unlock()
|
||||
if done {
|
||||
break
|
||||
}
|
||||
if time.Now().After(deadline) {
|
||||
t.Fatalf("last update was not applied: %v", applied)
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
// Updates are never applied out of order, so the border cannot be left
|
||||
// showing a label older than the last one requested
|
||||
for i, n := range applied {
|
||||
if i > 0 && n <= applied[i-1] {
|
||||
t.Errorf("applied out of order: %v", applied)
|
||||
break
|
||||
}
|
||||
}
|
||||
// A burst collapses instead of running one command per update. The
|
||||
// updates are queued in a tight loop while each one takes a millisecond
|
||||
// to apply, so all of them landing would mean no coalescing at all
|
||||
if len(applied) >= last+1 {
|
||||
t.Errorf("expected updates to be coalesced, applied all %d", len(applied))
|
||||
}
|
||||
t.Logf("applied %d of %d updates", len(applied), last+1)
|
||||
}
|
||||
Reference in New Issue
Block a user