A driver that runs Init's commands in turn, as cmd/012's fake terminal does, no longer waits out the 500 ms; that fake answers DA1 as a terminal would.
- commit
- 7d5076964b800717b002374261ded591d97bdc57
- parent
- ce72c55daf82eb2acc3928cfb9fc28f460df9c81
- author
- Dami <dami.etoile@gmail.com>
- kind
- fix risk score 1.6 of 3, Jev's estimate
| M | cmd/ | 2 | ++ |
| M | cmd/ | 10 | ++++++++- |
| M | internal/ | 5 | +++-- |
| M | internal/ | 2 | +- |
| M | internal/ | 3 | ++- |
| M | internal/ | 32 | ++++++++++++++++++++++-------- |
@@ -6,6 +6,7 @@ import (
66 "testing"77 88 tea "charm.land/bubbletea/v2"9+ uv "github.com/charmbracelet/ultraviolet"910 "github.com/charmbracelet/x/ansi"1011 )1112 @@ -15,6 +16,7 @@ func TestNuOpensNotebook(t *testing.T) {
1516 var screen string1617 e.runTUI = func(m tea.Model, _ ...tea.ProgramOption) error {1718 m.Init()19+ m.Update(uv.PrimaryDeviceAttributesEvent{62, 22}) // the terminal answers1820 m.Update(tea.WindowSizeMsg{Width: 80, Height: 20})1921 screen = ansi.Strip(m.View().Content)2022 return nil@@ -2,6 +2,7 @@ package main
22 33 import (44 "errors"5+ "fmt"56 "io"67 "os"78 "path/filepath"@@ -11,6 +12,8 @@ import (
1112 "time"1213 1314 tea "charm.land/bubbletea/v2"15+ uv "github.com/charmbracelet/ultraviolet"16+ "github.com/charmbracelet/x/ansi"1417 )1518 1619 var (@@ -23,7 +26,8 @@ var (
2326 // fakeTerminal stands in for the terminal and Bubble Tea: it checks the2427 // UI was given a terminal of its own (when tty is set), carries out the2528 // model's commands as the program would, feeding their messages back,26-// then presses keys until the model quits.29+// answers the device attributes as a terminal does, which the UI waits30+// for before it draws, then presses keys until the model quits.2731 type fakeTerminal struct {2832 t *testing.T2933 keys []tea.KeyPressMsg@@ -84,6 +88,10 @@ func (f *fakeTerminal) drive(m tea.Model, cmd tea.Cmd) bool {
8488 return false8589 }8690 _, next := m.Update(msg)91+ if raw, ok := msg.(tea.RawMsg); ok && strings.Contains(fmt.Sprint(raw.Msg), ansi.RequestPrimaryDeviceAttributes) {92+ _, answer := m.Update(uv.PrimaryDeviceAttributesEvent{62, 22})93+ next = tea.Batch(next, answer)94+ }8795 return f.drive(m, next)8896 }8997 @@ -42,6 +42,7 @@ type terminal struct {
4242 six sixelState // the sixel images drawn and to draw4343 blurred bool // the terminal window doesn't have focus4444 waitBG bool // the screen waits for the background (termbg.go)45+ bgKnown chan struct{} // closed when it's over4546 }4647 4748 // newTerminal starts with what getenv, the terminal's environment, says:@@ -100,7 +101,7 @@ func (t *terminal) handle(msg tea.Msg) tea.Cmd {
100101 return tea.Raw(ansi.WindowOp(16)) // 16: report the cell size in pixels101102 }102103 case uv.PrimaryDeviceAttributesEvent:103- t.waitBG = false // the background's answer, if any, came first104+ t.backgroundKnown() // its answer, if any, came first104105 if slices.Contains(msg, 4) && !t.kitty && !t.tmux && !t.sixel {105106 t.sixel = true106107 return tea.Raw(ansi.WindowOp(16)) // sixel images are drawn at the cell size@@ -122,7 +123,7 @@ func (t *terminal) handle(msg tea.Msg) tea.Cmd {
122123 // background decides the theme, so ask for it again.123124 return tea.RequestBackgroundColor124125 case bgWaitedMsg:125- t.waitBG = false126+ t.backgroundKnown()126127 case tea.FocusMsg:127128 t.blurred = false128129 case tea.BlurMsg:@@ -168,7 +168,7 @@ func (m *Model) TraceUnder(p telemetry.Parent) { m.spans.Enter(p) }
168168 // screen until it knows (termbg.go).169169 func (m *Model) Init() tea.Cmd {170170 // jev.send starts any questions queued while loading the file.171- return tea.Batch(m.term.awaitBackground(), tea.Raw(shiftEscapeOn), m.term.probes(), m.jev.send(m.spans.Parent()), m.startupCmd(), m.startStdin(), m.startOpenCmd(), m.startNotebook(), m.syncFollowers())171+ return tea.Batch(tea.Raw(shiftEscapeOn), m.term.probes(), m.term.awaitBackground(), m.jev.send(m.spans.Parent()), m.startupCmd(), m.startStdin(), m.startOpenCmd(), m.startNotebook(), m.syncFollowers())172172 }173173 174174 // Update implements tea.Model.@@ -225,7 +225,8 @@ func (m *Model) handlePrefs(msg tea.Msg) bool {
225225 switch msg := msg.(type) {226226 case tea.BackgroundColorMsg:227227 m.prefs.light = !msg.IsDark()228- m.term.bg, m.term.waitBG = msg.Color, false228+ m.term.bg = msg.Color229+ m.term.backgroundKnown()229230 if p := m.applyTheme(); p != "" && m.note == "" {230231 m.note = m.th.Warning.Render(p)231232 }@@ -27,19 +27,33 @@ type bgWaitedMsg struct{}
2727 // that holds notebooks' pauses for them to end (pauses_test.go).2828 var after func(d time.Duration, msg tea.Msg) tea.Cmd2929 30-// wait gives msg once d has passed, on the model's clock.31-func wait(d time.Duration, msg tea.Msg) tea.Cmd {30+// awaitBackground holds the screen until the terminal says its31+// background; probes asks. Its command gives bgWaitedMsg after32+// bgPatience, or nothing once the wait is over.33+func (t *terminal) awaitBackground() tea.Cmd {34+ t.waitBG, t.bgKnown = true, make(chan struct{})3235 if after != nil {33- return after(d, msg)36+ return after(bgPatience, bgWaitedMsg{})37+ }38+ known := t.bgKnown39+ return func() tea.Msg {40+ timer := time.NewTimer(bgPatience)41+ defer timer.Stop()42+ select {43+ case <-timer.C:44+ return bgWaitedMsg{}45+ case <-known:46+ return nil47+ }3448 }35- return tea.Tick(d, func(time.Time) tea.Msg { return msg })3649 }3750 38-// awaitBackground holds the screen until the terminal says its39-// background; probes asks.40-func (t *terminal) awaitBackground() tea.Cmd {41- t.waitBG = true42- return wait(bgPatience, bgWaitedMsg{})51+// backgroundKnown ends the wait for the background.52+func (t *terminal) backgroundKnown() {53+ if t.waitBG {54+ t.waitBG = false55+ close(t.bgKnown)56+ }4357 }4458 4559 // View implements tea.Model: the screen, once the theme is known.