detached 0309f6e

fix: address m3 review findings

commit
0309f6e49eafa3609e25ae08106348f95c64fc78
author
brice · · 2026-07-02 15:27 UTC
parent
4428ba6
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
12 files · +85 -73
M internal/adapters/ipc/listener.go +2 -2
@@ -64,7 +64,7 @@ func Listen(dir string) (ports.Listener, error) {
6464 }
6565
6666 if err := os.Chmod(sockPath, 0o600); err != nil {
67- ln.Close()
67+ _ = ln.Close()
6868 return nil, fmt.Errorf("ipc: chmod %s: %w", sockPath, err)
6969 }
7070
@@ -92,7 +92,7 @@ func probeLiveDaemon(sockPath string) error {
9292 }
9393 return fmt.Errorf("ipc: probing socket %s: %w", sockPath, err)
9494 }
95- conn.Close()
95+ _ = conn.Close()
9696 return ErrDaemonRunning
9797 }
9898
@@ -64,7 +64,7 @@ func Listen(dir string) (ports.Listener, error) {
64 }64 }
65 65
66 if err := os.Chmod(sockPath, 0o600); err != nil {66 if err := os.Chmod(sockPath, 0o600); err != nil {
67- ln.Close()67+ _ = ln.Close()
68 return nil, fmt.Errorf("ipc: chmod %s: %w", sockPath, err)68 return nil, fmt.Errorf("ipc: chmod %s: %w", sockPath, err)
69 }69 }
70 70
@@ -92,7 +92,7 @@ func probeLiveDaemon(sockPath string) error {
92 }92 }
93 return fmt.Errorf("ipc: probing socket %s: %w", sockPath, err)93 return fmt.Errorf("ipc: probing socket %s: %w", sockPath, err)
94 }94 }
95- conn.Close()95+ _ = conn.Close()
96 return ErrDaemonRunning96 return ErrDaemonRunning
97 }97 }
98 98
M internal/adapters/ipc/listener_test.go +6 -6
@@ -18,7 +18,7 @@ func TestListenAcceptRoundTrip(t *testing.T) {
1818 if err != nil {
1919 t.Fatalf("Listen() error = %v", err)
2020 }
21- defer ln.Close()
21+ defer func() { _ = ln.Close() }()
2222
2323 wantPath := filepath.Join(dir, "daemon.sock")
2424 if ln.Addr() != wantPath {
@@ -40,7 +40,7 @@ func TestListenAcceptRoundTrip(t *testing.T) {
4040 errCh <- err
4141 return
4242 }
43- defer conn.Close()
43+ defer func() { _ = conn.Close() }()
4444 client := NewTransport(conn)
4545 errCh <- client.Send(ports.Frame{Type: ports.MsgPing})
4646 }()
@@ -49,7 +49,7 @@ func TestListenAcceptRoundTrip(t *testing.T) {
4949 if err != nil {
5050 t.Fatalf("Accept() error = %v", err)
5151 }
52- defer transport.Close()
52+ defer func() { _ = transport.Close() }()
5353
5454 got, err := transport.Recv()
5555 if err != nil {
@@ -71,7 +71,7 @@ func TestListenMkdirsSocketDir(t *testing.T) {
7171 if err != nil {
7272 t.Fatalf("Listen() error = %v", err)
7373 }
74- defer ln.Close()
74+ defer func() { _ = ln.Close() }()
7575
7676 fi, err := os.Stat(dir)
7777 if err != nil {
@@ -113,7 +113,7 @@ func TestListenStaleSocketCleanedUp(t *testing.T) {
113113 if err != nil {
114114 t.Fatalf("Listen() over stale socket error = %v, want success", err)
115115 }
116- defer ln.Close()
116+ defer func() { _ = ln.Close() }()
117117 }
118118
119119 func TestListenLiveDaemonRejected(t *testing.T) {
@@ -123,7 +123,7 @@ func TestListenLiveDaemonRejected(t *testing.T) {
123123 if err != nil {
124124 t.Fatalf("first Listen() error = %v", err)
125125 }
126- defer first.Close()
126+ defer func() { _ = first.Close() }()
127127
128128 _, err = Listen(dir)
129129 if !errors.Is(err, ErrDaemonRunning) {
@@ -18,7 +18,7 @@ func TestListenAcceptRoundTrip(t *testing.T) {
18 if err != nil {18 if err != nil {
19 t.Fatalf("Listen() error = %v", err)19 t.Fatalf("Listen() error = %v", err)
20 }20 }
21- defer ln.Close()21+ defer func() { _ = ln.Close() }()
22 22
23 wantPath := filepath.Join(dir, "daemon.sock")23 wantPath := filepath.Join(dir, "daemon.sock")
24 if ln.Addr() != wantPath {24 if ln.Addr() != wantPath {
@@ -40,7 +40,7 @@ func TestListenAcceptRoundTrip(t *testing.T) {
40 errCh <- err40 errCh <- err
41 return41 return
42 }42 }
43- defer conn.Close()43+ defer func() { _ = conn.Close() }()
44 client := NewTransport(conn)44 client := NewTransport(conn)
45 errCh <- client.Send(ports.Frame{Type: ports.MsgPing})45 errCh <- client.Send(ports.Frame{Type: ports.MsgPing})
46 }()46 }()
@@ -49,7 +49,7 @@ func TestListenAcceptRoundTrip(t *testing.T) {
49 if err != nil {49 if err != nil {
50 t.Fatalf("Accept() error = %v", err)50 t.Fatalf("Accept() error = %v", err)
51 }51 }
52- defer transport.Close()52+ defer func() { _ = transport.Close() }()
53 53
54 got, err := transport.Recv()54 got, err := transport.Recv()
55 if err != nil {55 if err != nil {
@@ -71,7 +71,7 @@ func TestListenMkdirsSocketDir(t *testing.T) {
71 if err != nil {71 if err != nil {
72 t.Fatalf("Listen() error = %v", err)72 t.Fatalf("Listen() error = %v", err)
73 }73 }
74- defer ln.Close()74+ defer func() { _ = ln.Close() }()
75 75
76 fi, err := os.Stat(dir)76 fi, err := os.Stat(dir)
77 if err != nil {77 if err != nil {
@@ -113,7 +113,7 @@ func TestListenStaleSocketCleanedUp(t *testing.T) {
113 if err != nil {113 if err != nil {
114 t.Fatalf("Listen() over stale socket error = %v, want success", err)114 t.Fatalf("Listen() over stale socket error = %v, want success", err)
115 }115 }
116- defer ln.Close()116+ defer func() { _ = ln.Close() }()
117 }117 }
118 118
119 func TestListenLiveDaemonRejected(t *testing.T) {119 func TestListenLiveDaemonRejected(t *testing.T) {
@@ -123,7 +123,7 @@ func TestListenLiveDaemonRejected(t *testing.T) {
123 if err != nil {123 if err != nil {
124 t.Fatalf("first Listen() error = %v", err)124 t.Fatalf("first Listen() error = %v", err)
125 }125 }
126- defer first.Close()126+ defer func() { _ = first.Close() }()
127 127
128 _, err = Listen(dir)128 _, err = Listen(dir)
129 if !errors.Is(err, ErrDaemonRunning) {129 if !errors.Is(err, ErrDaemonRunning) {
M internal/adapters/ipc/transport_test.go +13 -13
@@ -15,8 +15,8 @@ import (
1515
1616 func TestTransportSendRecvBothDirections(t *testing.T) {
1717 c1, c2 := net.Pipe()
18- defer c1.Close()
19- defer c2.Close()
18+ defer func() { _ = c1.Close() }()
19+ defer func() { _ = c2.Close() }()
2020
2121 client := NewTransport(c1)
2222 server := NewTransport(c2)
@@ -73,7 +73,7 @@ func TestTransportEOFOnClose(t *testing.T) {
7373 func TestTransportCloseClosesUnderlyingConn(t *testing.T) {
7474 c1, c2 := net.Pipe()
7575 client := NewTransport(c1)
76- defer c2.Close()
76+ defer func() { _ = c2.Close() }()
7777
7878 if err := client.Close(); err != nil {
7979 t.Fatalf("Close() error = %v", err)
@@ -87,8 +87,8 @@ func TestTransportCloseClosesUnderlyingConn(t *testing.T) {
8787
8888 func TestTransportLargeFrameNearCap(t *testing.T) {
8989 c1, c2 := net.Pipe()
90- defer c1.Close()
91- defer c2.Close()
90+ defer func() { _ = c1.Close() }()
91+ defer func() { _ = c2.Close() }()
9292
9393 client := NewTransport(c1)
9494 server := NewTransport(c2)
@@ -125,8 +125,8 @@ func TestTransportLargeFrameNearCap(t *testing.T) {
125125
126126 func TestTransportManySmallFramesBackToBack(t *testing.T) {
127127 c1, c2 := net.Pipe()
128- defer c1.Close()
129- defer c2.Close()
128+ defer func() { _ = c1.Close() }()
129+ defer func() { _ = c2.Close() }()
130130
131131 client := NewTransport(c1)
132132 server := NewTransport(c2)
@@ -168,8 +168,8 @@ func TestTransportManySmallFramesBackToBack(t *testing.T) {
168168
169169 func TestTransportRecvOversizeFrame(t *testing.T) {
170170 c1, c2 := net.Pipe()
171- defer c1.Close()
172- defer c2.Close()
171+ defer func() { _ = c1.Close() }()
172+ defer func() { _ = c2.Close() }()
173173
174174 server := NewTransport(c2)
175175
@@ -187,8 +187,8 @@ func TestTransportRecvOversizeFrame(t *testing.T) {
187187
188188 func TestTransportRecvZeroLengthFrame(t *testing.T) {
189189 c1, c2 := net.Pipe()
190- defer c1.Close()
191- defer c2.Close()
190+ defer func() { _ = c1.Close() }()
191+ defer func() { _ = c2.Close() }()
192192
193193 server := NewTransport(c2)
194194
@@ -221,8 +221,8 @@ func TestTransportRecvTruncatedHeader(t *testing.T) {
221221
222222 func TestTransportSendOversizePayloadRejected(t *testing.T) {
223223 c1, c2 := net.Pipe()
224- defer c1.Close()
225- defer c2.Close()
224+ defer func() { _ = c1.Close() }()
225+ defer func() { _ = c2.Close() }()
226226
227227 client := NewTransport(c1)
228228 payload := make([]byte, maxFrameLen) // +1 byte type => exceeds max
@@ -15,8 +15,8 @@ import (
15 15
16 func TestTransportSendRecvBothDirections(t *testing.T) {16 func TestTransportSendRecvBothDirections(t *testing.T) {
17 c1, c2 := net.Pipe()17 c1, c2 := net.Pipe()
18- defer c1.Close()18+ defer func() { _ = c1.Close() }()
19- defer c2.Close()19+ defer func() { _ = c2.Close() }()
20 20
21 client := NewTransport(c1)21 client := NewTransport(c1)
22 server := NewTransport(c2)22 server := NewTransport(c2)
@@ -73,7 +73,7 @@ func TestTransportEOFOnClose(t *testing.T) {
73 func TestTransportCloseClosesUnderlyingConn(t *testing.T) {73 func TestTransportCloseClosesUnderlyingConn(t *testing.T) {
74 c1, c2 := net.Pipe()74 c1, c2 := net.Pipe()
75 client := NewTransport(c1)75 client := NewTransport(c1)
76- defer c2.Close()76+ defer func() { _ = c2.Close() }()
77 77
78 if err := client.Close(); err != nil {78 if err := client.Close(); err != nil {
79 t.Fatalf("Close() error = %v", err)79 t.Fatalf("Close() error = %v", err)
@@ -87,8 +87,8 @@ func TestTransportCloseClosesUnderlyingConn(t *testing.T) {
87 87
88 func TestTransportLargeFrameNearCap(t *testing.T) {88 func TestTransportLargeFrameNearCap(t *testing.T) {
89 c1, c2 := net.Pipe()89 c1, c2 := net.Pipe()
90- defer c1.Close()90+ defer func() { _ = c1.Close() }()
91- defer c2.Close()91+ defer func() { _ = c2.Close() }()
92 92
93 client := NewTransport(c1)93 client := NewTransport(c1)
94 server := NewTransport(c2)94 server := NewTransport(c2)
@@ -125,8 +125,8 @@ func TestTransportLargeFrameNearCap(t *testing.T) {
125 125
126 func TestTransportManySmallFramesBackToBack(t *testing.T) {126 func TestTransportManySmallFramesBackToBack(t *testing.T) {
127 c1, c2 := net.Pipe()127 c1, c2 := net.Pipe()
128- defer c1.Close()128+ defer func() { _ = c1.Close() }()
129- defer c2.Close()129+ defer func() { _ = c2.Close() }()
130 130
131 client := NewTransport(c1)131 client := NewTransport(c1)
132 server := NewTransport(c2)132 server := NewTransport(c2)
@@ -168,8 +168,8 @@ func TestTransportManySmallFramesBackToBack(t *testing.T) {
168 168
169 func TestTransportRecvOversizeFrame(t *testing.T) {169 func TestTransportRecvOversizeFrame(t *testing.T) {
170 c1, c2 := net.Pipe()170 c1, c2 := net.Pipe()
171- defer c1.Close()171+ defer func() { _ = c1.Close() }()
172- defer c2.Close()172+ defer func() { _ = c2.Close() }()
173 173
174 server := NewTransport(c2)174 server := NewTransport(c2)
175 175
@@ -187,8 +187,8 @@ func TestTransportRecvOversizeFrame(t *testing.T) {
187 187
188 func TestTransportRecvZeroLengthFrame(t *testing.T) {188 func TestTransportRecvZeroLengthFrame(t *testing.T) {
189 c1, c2 := net.Pipe()189 c1, c2 := net.Pipe()
190- defer c1.Close()190+ defer func() { _ = c1.Close() }()
191- defer c2.Close()191+ defer func() { _ = c2.Close() }()
192 192
193 server := NewTransport(c2)193 server := NewTransport(c2)
194 194
@@ -221,8 +221,8 @@ func TestTransportRecvTruncatedHeader(t *testing.T) {
221 221
222 func TestTransportSendOversizePayloadRejected(t *testing.T) {222 func TestTransportSendOversizePayloadRejected(t *testing.T) {
223 c1, c2 := net.Pipe()223 c1, c2 := net.Pipe()
224- defer c1.Close()224+ defer func() { _ = c1.Close() }()
225- defer c2.Close()225+ defer func() { _ = c2.Close() }()
226 226
227 client := NewTransport(c1)227 client := NewTransport(c1)
228 payload := make([]byte, maxFrameLen) // +1 byte type => exceeds max228 payload := make([]byte, maxFrameLen) // +1 byte type => exceeds max
M internal/adapters/term/pty_integration_test.go +5 -5
@@ -24,20 +24,20 @@ func openPTYPair(t *testing.T) (master, slave *os.File) {
2424 }
2525
2626 if err := unix.IoctlSetPointerInt(int(m.Fd()), unix.TIOCSPTLCK, 0); err != nil {
27- m.Close()
27+ _ = m.Close()
2828 t.Fatalf("unlock pty (TIOCSPTLCK): %v", err)
2929 }
3030
3131 n, err := unix.IoctlGetInt(int(m.Fd()), unix.TIOCGPTN)
3232 if err != nil {
33- m.Close()
33+ _ = m.Close()
3434 t.Fatalf("get pty number (TIOCGPTN): %v", err)
3535 }
3636
3737 slavePath := fmt.Sprintf("/dev/pts/%d", n)
3838 s, err := os.OpenFile(slavePath, os.O_RDWR|unix.O_NOCTTY, 0)
3939 if err != nil {
40- m.Close()
40+ _ = m.Close()
4141 t.Fatalf("open slave %s: %v", slavePath, err)
4242 }
4343
@@ -50,8 +50,8 @@ func openPTYPair(t *testing.T) (master, slave *os.File) {
5050 // pipes can't cover.
5151 func TestTerminal_RealPTY_RawModeSizeAndEscapes(t *testing.T) {
5252 master, slave := openPTYPair(t)
53- defer master.Close()
54- defer slave.Close()
53+ defer func() { _ = master.Close() }()
54+ defer func() { _ = slave.Close() }()
5555
5656 ws := &unix.Winsize{Row: 24, Col: 80}
5757 if err := unix.IoctlSetWinsize(int(slave.Fd()), unix.TIOCSWINSZ, ws); err != nil {
@@ -24,20 +24,20 @@ func openPTYPair(t *testing.T) (master, slave *os.File) {
24 }24 }
25 25
26 if err := unix.IoctlSetPointerInt(int(m.Fd()), unix.TIOCSPTLCK, 0); err != nil {26 if err := unix.IoctlSetPointerInt(int(m.Fd()), unix.TIOCSPTLCK, 0); err != nil {
27- m.Close()27+ _ = m.Close()
28 t.Fatalf("unlock pty (TIOCSPTLCK): %v", err)28 t.Fatalf("unlock pty (TIOCSPTLCK): %v", err)
29 }29 }
30 30
31 n, err := unix.IoctlGetInt(int(m.Fd()), unix.TIOCGPTN)31 n, err := unix.IoctlGetInt(int(m.Fd()), unix.TIOCGPTN)
32 if err != nil {32 if err != nil {
33- m.Close()33+ _ = m.Close()
34 t.Fatalf("get pty number (TIOCGPTN): %v", err)34 t.Fatalf("get pty number (TIOCGPTN): %v", err)
35 }35 }
36 36
37 slavePath := fmt.Sprintf("/dev/pts/%d", n)37 slavePath := fmt.Sprintf("/dev/pts/%d", n)
38 s, err := os.OpenFile(slavePath, os.O_RDWR|unix.O_NOCTTY, 0)38 s, err := os.OpenFile(slavePath, os.O_RDWR|unix.O_NOCTTY, 0)
39 if err != nil {39 if err != nil {
40- m.Close()40+ _ = m.Close()
41 t.Fatalf("open slave %s: %v", slavePath, err)41 t.Fatalf("open slave %s: %v", slavePath, err)
42 }42 }
43 43
@@ -50,8 +50,8 @@ func openPTYPair(t *testing.T) (master, slave *os.File) {
50 // pipes can't cover.50 // pipes can't cover.
51 func TestTerminal_RealPTY_RawModeSizeAndEscapes(t *testing.T) {51 func TestTerminal_RealPTY_RawModeSizeAndEscapes(t *testing.T) {
52 master, slave := openPTYPair(t)52 master, slave := openPTYPair(t)
53- defer master.Close()53+ defer func() { _ = master.Close() }()
54- defer slave.Close()54+ defer func() { _ = slave.Close() }()
55 55
56 ws := &unix.Winsize{Row: 24, Col: 80}56 ws := &unix.Winsize{Row: 24, Col: 80}
57 if err := unix.IoctlSetWinsize(int(slave.Fd()), unix.TIOCSWINSZ, ws); err != nil {57 if err := unix.IoctlSetWinsize(int(slave.Fd()), unix.TIOCSWINSZ, ws); err != nil {
M internal/adapters/term/terminal.go +2 -2
@@ -102,11 +102,11 @@ func (t *Terminal) EnterRaw() (func() error, error) {
102102 t.orig = old
103103
104104 if _, err := t.bw.WriteString(altScreenEnter + cursorHide); err != nil {
105- t.restoreRawLocked()
105+ _ = t.restoreRawLocked()
106106 return nil, fmt.Errorf("term: enter alt screen: %w", err)
107107 }
108108 if err := t.bw.Flush(); err != nil {
109- t.restoreRawLocked()
109+ _ = t.restoreRawLocked()
110110 return nil, fmt.Errorf("term: enter alt screen: %w", err)
111111 }
112112
@@ -102,11 +102,11 @@ func (t *Terminal) EnterRaw() (func() error, error) {
102 t.orig = old102 t.orig = old
103 103
104 if _, err := t.bw.WriteString(altScreenEnter + cursorHide); err != nil {104 if _, err := t.bw.WriteString(altScreenEnter + cursorHide); err != nil {
105- t.restoreRawLocked()105+ _ = t.restoreRawLocked()
106 return nil, fmt.Errorf("term: enter alt screen: %w", err)106 return nil, fmt.Errorf("term: enter alt screen: %w", err)
107 }107 }
108 if err := t.bw.Flush(); err != nil {108 if err := t.bw.Flush(); err != nil {
109- t.restoreRawLocked()109+ _ = t.restoreRawLocked()
110 return nil, fmt.Errorf("term: enter alt screen: %w", err)110 return nil, fmt.Errorf("term: enter alt screen: %w", err)
111 }111 }
112 112
M internal/adapters/term/terminal_test.go +28 -28
@@ -33,11 +33,11 @@ func TestTerminal_EnterRaw_NonTTY_EmitsAltScreenAndCursorEscapes(t *testing.T) {
3333 if err != nil {
3434 t.Fatalf("os.Pipe(in): %v", err)
3535 }
36- defer inW.Close()
37- defer inR.Close()
36+ defer func() { _ = inW.Close() }()
37+ defer func() { _ = inR.Close() }()
3838
3939 outR, outW, captured, done := pipeCapture(t)
40- defer outR.Close()
40+ defer func() { _ = outR.Close() }()
4141
4242 tm := NewWithFiles(inR, outW)
4343
@@ -57,7 +57,7 @@ func TestTerminal_EnterRaw_NonTTY_EmitsAltScreenAndCursorEscapes(t *testing.T) {
5757 t.Fatalf("second restore call: %v", err)
5858 }
5959
60- outW.Close()
60+ _ = outW.Close()
6161 <-done
6262
6363 want := altScreenEnter + cursorHide + cursorShow + altScreenExit
@@ -71,11 +71,11 @@ func TestTerminal_EnterRaw_IsIdempotentAcrossCalls(t *testing.T) {
7171 if err != nil {
7272 t.Fatalf("os.Pipe(in): %v", err)
7373 }
74- defer inW.Close()
75- defer inR.Close()
74+ defer func() { _ = inW.Close() }()
75+ defer func() { _ = inR.Close() }()
7676
7777 outR, outW, captured, done := pipeCapture(t)
78- defer outR.Close()
78+ defer func() { _ = outR.Close() }()
7979
8080 tm := NewWithFiles(inR, outW)
8181
@@ -95,7 +95,7 @@ func TestTerminal_EnterRaw_IsIdempotentAcrossCalls(t *testing.T) {
9595 t.Fatalf("restore2: %v", err)
9696 }
9797
98- outW.Close()
98+ _ = outW.Close()
9999 <-done
100100
101101 // Alt-screen/cursor escapes must appear exactly once for enter and
@@ -112,11 +112,11 @@ func TestTerminal_InOut(t *testing.T) {
112112 if err != nil {
113113 t.Fatalf("os.Pipe(in): %v", err)
114114 }
115- defer inW.Close()
116- defer inR.Close()
115+ defer func() { _ = inW.Close() }()
116+ defer func() { _ = inR.Close() }()
117117
118118 outR, outW, captured, done := pipeCapture(t)
119- defer outR.Close()
119+ defer func() { _ = outR.Close() }()
120120
121121 tm := NewWithFiles(inR, outW)
122122
@@ -131,7 +131,7 @@ func TestTerminal_InOut(t *testing.T) {
131131 t.Fatalf("Flush: %v", err)
132132 }
133133
134- outW.Close()
134+ _ = outW.Close()
135135 <-done
136136
137137 if got := captured.String(); got != "payload" {
@@ -144,11 +144,11 @@ func TestTerminal_ResizeEvents_ReturnsSameChannelAndClosesOnRestore(t *testing.T
144144 if err != nil {
145145 t.Fatalf("os.Pipe(in): %v", err)
146146 }
147- defer inW.Close()
148- defer inR.Close()
147+ defer func() { _ = inW.Close() }()
148+ defer func() { _ = inR.Close() }()
149149
150150 outR, outW, _, done := pipeCapture(t)
151- defer outR.Close()
151+ defer func() { _ = outR.Close() }()
152152
153153 tm := NewWithFiles(inR, outW)
154154
@@ -176,7 +176,7 @@ func TestTerminal_ResizeEvents_ReturnsSameChannelAndClosesOnRestore(t *testing.T
176176 t.Fatalf("resize channel was not closed after restore")
177177 }
178178
179- outW.Close()
179+ _ = outW.Close()
180180 <-done
181181 }
182182
@@ -185,11 +185,11 @@ func TestTerminal_ResizeEvents_AfterRestore_NoWatcherAndClosedChannel(t *testing
185185 if err != nil {
186186 t.Fatalf("os.Pipe(in): %v", err)
187187 }
188- defer inW.Close()
189- defer inR.Close()
188+ defer func() { _ = inW.Close() }()
189+ defer func() { _ = inR.Close() }()
190190
191191 outR, outW, _, done := pipeCapture(t)
192- defer outR.Close()
192+ defer func() { _ = outR.Close() }()
193193
194194 tm := NewWithFiles(inR, outW)
195195
@@ -230,7 +230,7 @@ func TestTerminal_ResizeEvents_AfterRestore_NoWatcherAndClosedChannel(t *testing
230230 // Returns immediately iff no watcher goroutine was leaked.
231231 tm.resizeWG.Wait()
232232
233- outW.Close()
233+ _ = outW.Close()
234234 <-done
235235 }
236236
@@ -285,11 +285,11 @@ func TestTerminal_ResizeEvents_ConcurrentWithRestore(t *testing.T) {
285285 }
286286 tm.resizeWG.Wait()
287287
288- outW.Close()
288+ _ = outW.Close()
289289 <-done
290- inR.Close()
291- inW.Close()
292- outR.Close()
290+ _ = inR.Close()
291+ _ = inW.Close()
292+ _ = outR.Close()
293293 }
294294 }
295295
@@ -298,12 +298,12 @@ func TestTerminal_Size_NonTTY_ReturnsError(t *testing.T) {
298298 if err != nil {
299299 t.Fatalf("os.Pipe(in): %v", err)
300300 }
301- defer inW.Close()
302- defer inR.Close()
301+ defer func() { _ = inW.Close() }()
302+ defer func() { _ = inR.Close() }()
303303
304304 outR, outW, _, _ := pipeCapture(t)
305- defer outR.Close()
306- defer outW.Close()
305+ defer func() { _ = outR.Close() }()
306+ defer func() { _ = outW.Close() }()
307307
308308 tm := NewWithFiles(inR, outW)
309309
@@ -33,11 +33,11 @@ func TestTerminal_EnterRaw_NonTTY_EmitsAltScreenAndCursorEscapes(t *testing.T) {
33 if err != nil {33 if err != nil {
34 t.Fatalf("os.Pipe(in): %v", err)34 t.Fatalf("os.Pipe(in): %v", err)
35 }35 }
36- defer inW.Close()36+ defer func() { _ = inW.Close() }()
37- defer inR.Close()37+ defer func() { _ = inR.Close() }()
38 38
39 outR, outW, captured, done := pipeCapture(t)39 outR, outW, captured, done := pipeCapture(t)
40- defer outR.Close()40+ defer func() { _ = outR.Close() }()
41 41
42 tm := NewWithFiles(inR, outW)42 tm := NewWithFiles(inR, outW)
43 43
@@ -57,7 +57,7 @@ func TestTerminal_EnterRaw_NonTTY_EmitsAltScreenAndCursorEscapes(t *testing.T) {
57 t.Fatalf("second restore call: %v", err)57 t.Fatalf("second restore call: %v", err)
58 }58 }
59 59
60- outW.Close()60+ _ = outW.Close()
61 <-done61 <-done
62 62
63 want := altScreenEnter + cursorHide + cursorShow + altScreenExit63 want := altScreenEnter + cursorHide + cursorShow + altScreenExit
@@ -71,11 +71,11 @@ func TestTerminal_EnterRaw_IsIdempotentAcrossCalls(t *testing.T) {
71 if err != nil {71 if err != nil {
72 t.Fatalf("os.Pipe(in): %v", err)72 t.Fatalf("os.Pipe(in): %v", err)
73 }73 }
74- defer inW.Close()74+ defer func() { _ = inW.Close() }()
75- defer inR.Close()75+ defer func() { _ = inR.Close() }()
76 76
77 outR, outW, captured, done := pipeCapture(t)77 outR, outW, captured, done := pipeCapture(t)
78- defer outR.Close()78+ defer func() { _ = outR.Close() }()
79 79
80 tm := NewWithFiles(inR, outW)80 tm := NewWithFiles(inR, outW)
81 81
@@ -95,7 +95,7 @@ func TestTerminal_EnterRaw_IsIdempotentAcrossCalls(t *testing.T) {
95 t.Fatalf("restore2: %v", err)95 t.Fatalf("restore2: %v", err)
96 }96 }
97 97
98- outW.Close()98+ _ = outW.Close()
99 <-done99 <-done
100 100
101 // Alt-screen/cursor escapes must appear exactly once for enter and101 // Alt-screen/cursor escapes must appear exactly once for enter and
@@ -112,11 +112,11 @@ func TestTerminal_InOut(t *testing.T) {
112 if err != nil {112 if err != nil {
113 t.Fatalf("os.Pipe(in): %v", err)113 t.Fatalf("os.Pipe(in): %v", err)
114 }114 }
115- defer inW.Close()115+ defer func() { _ = inW.Close() }()
116- defer inR.Close()116+ defer func() { _ = inR.Close() }()
117 117
118 outR, outW, captured, done := pipeCapture(t)118 outR, outW, captured, done := pipeCapture(t)
119- defer outR.Close()119+ defer func() { _ = outR.Close() }()
120 120
121 tm := NewWithFiles(inR, outW)121 tm := NewWithFiles(inR, outW)
122 122
@@ -131,7 +131,7 @@ func TestTerminal_InOut(t *testing.T) {
131 t.Fatalf("Flush: %v", err)131 t.Fatalf("Flush: %v", err)
132 }132 }
133 133
134- outW.Close()134+ _ = outW.Close()
135 <-done135 <-done
136 136
137 if got := captured.String(); got != "payload" {137 if got := captured.String(); got != "payload" {
@@ -144,11 +144,11 @@ func TestTerminal_ResizeEvents_ReturnsSameChannelAndClosesOnRestore(t *testing.T
144 if err != nil {144 if err != nil {
145 t.Fatalf("os.Pipe(in): %v", err)145 t.Fatalf("os.Pipe(in): %v", err)
146 }146 }
147- defer inW.Close()147+ defer func() { _ = inW.Close() }()
148- defer inR.Close()148+ defer func() { _ = inR.Close() }()
149 149
150 outR, outW, _, done := pipeCapture(t)150 outR, outW, _, done := pipeCapture(t)
151- defer outR.Close()151+ defer func() { _ = outR.Close() }()
152 152
153 tm := NewWithFiles(inR, outW)153 tm := NewWithFiles(inR, outW)
154 154
@@ -176,7 +176,7 @@ func TestTerminal_ResizeEvents_ReturnsSameChannelAndClosesOnRestore(t *testing.T
176 t.Fatalf("resize channel was not closed after restore")176 t.Fatalf("resize channel was not closed after restore")
177 }177 }
178 178
179- outW.Close()179+ _ = outW.Close()
180 <-done180 <-done
181 }181 }
182 182
@@ -185,11 +185,11 @@ func TestTerminal_ResizeEvents_AfterRestore_NoWatcherAndClosedChannel(t *testing
185 if err != nil {185 if err != nil {
186 t.Fatalf("os.Pipe(in): %v", err)186 t.Fatalf("os.Pipe(in): %v", err)
187 }187 }
188- defer inW.Close()188+ defer func() { _ = inW.Close() }()
189- defer inR.Close()189+ defer func() { _ = inR.Close() }()
190 190
191 outR, outW, _, done := pipeCapture(t)191 outR, outW, _, done := pipeCapture(t)
192- defer outR.Close()192+ defer func() { _ = outR.Close() }()
193 193
194 tm := NewWithFiles(inR, outW)194 tm := NewWithFiles(inR, outW)
195 195
@@ -230,7 +230,7 @@ func TestTerminal_ResizeEvents_AfterRestore_NoWatcherAndClosedChannel(t *testing
230 // Returns immediately iff no watcher goroutine was leaked.230 // Returns immediately iff no watcher goroutine was leaked.
231 tm.resizeWG.Wait()231 tm.resizeWG.Wait()
232 232
233- outW.Close()233+ _ = outW.Close()
234 <-done234 <-done
235 }235 }
236 236
@@ -285,11 +285,11 @@ func TestTerminal_ResizeEvents_ConcurrentWithRestore(t *testing.T) {
285 }285 }
286 tm.resizeWG.Wait()286 tm.resizeWG.Wait()
287 287
288- outW.Close()288+ _ = outW.Close()
289 <-done289 <-done
290- inR.Close()290+ _ = inR.Close()
291- inW.Close()291+ _ = inW.Close()
292- outR.Close()292+ _ = outR.Close()
293 }293 }
294 }294 }
295 295
@@ -298,12 +298,12 @@ func TestTerminal_Size_NonTTY_ReturnsError(t *testing.T) {
298 if err != nil {298 if err != nil {
299 t.Fatalf("os.Pipe(in): %v", err)299 t.Fatalf("os.Pipe(in): %v", err)
300 }300 }
301- defer inW.Close()301+ defer func() { _ = inW.Close() }()
302- defer inR.Close()302+ defer func() { _ = inR.Close() }()
303 303
304 outR, outW, _, _ := pipeCapture(t)304 outR, outW, _, _ := pipeCapture(t)
305- defer outR.Close()305+ defer func() { _ = outR.Close() }()
306- defer outW.Close()306+ defer func() { _ = outW.Close() }()
307 307
308 tm := NewWithFiles(inR, outW)308 tm := NewWithFiles(inR, outW)
309 309
M internal/app/daemon_integration_test.go +3 -3
@@ -128,7 +128,7 @@ func TestIntegration_AttachFirstOutput(t *testing.T) {
128128 dir, _ := startDaemon(t, daemon.WithShell("/bin/sh", []string{"-c", "printf HELLO; sleep 30"}))
129129
130130 tr, p := attach(t, dir, ports.IntentEphemeral, "", sz)
131- defer tr.Close()
131+ defer func() { _ = tr.Close() }()
132132
133133 awaitText(t, p, sz, "HELLO")
134134 }
@@ -138,7 +138,7 @@ func TestIntegration_InputRoundtrip(t *testing.T) {
138138 dir, _ := startDaemon(t, daemon.WithShell("/bin/cat", nil))
139139
140140 tr, p := attach(t, dir, ports.IntentEphemeral, "", sz)
141- defer tr.Close()
141+ defer func() { _ = tr.Close() }()
142142
143143 require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(ports.Input{Data: []byte("PINGPONG\n")})}))
144144 awaitText(t, p, sz, "PINGPONG")
@@ -182,6 +182,6 @@ func TestIntegration_NamedSurvivesReattach(t *testing.T) {
182182
183183 // Re-attach: the first paint must reproduce the retained screen state.
184184 tr2, p2 := attach(t, dir, ports.IntentAttach, "work", sz)
185- defer tr2.Close()
185+ defer func() { _ = tr2.Close() }()
186186 awaitText(t, p2, sz, "MARKER")
187187 }
@@ -128,7 +128,7 @@ func TestIntegration_AttachFirstOutput(t *testing.T) {
128 dir, _ := startDaemon(t, daemon.WithShell("/bin/sh", []string{"-c", "printf HELLO; sleep 30"}))128 dir, _ := startDaemon(t, daemon.WithShell("/bin/sh", []string{"-c", "printf HELLO; sleep 30"}))
129 129
130 tr, p := attach(t, dir, ports.IntentEphemeral, "", sz)130 tr, p := attach(t, dir, ports.IntentEphemeral, "", sz)
131- defer tr.Close()131+ defer func() { _ = tr.Close() }()
132 132
133 awaitText(t, p, sz, "HELLO")133 awaitText(t, p, sz, "HELLO")
134 }134 }
@@ -138,7 +138,7 @@ func TestIntegration_InputRoundtrip(t *testing.T) {
138 dir, _ := startDaemon(t, daemon.WithShell("/bin/cat", nil))138 dir, _ := startDaemon(t, daemon.WithShell("/bin/cat", nil))
139 139
140 tr, p := attach(t, dir, ports.IntentEphemeral, "", sz)140 tr, p := attach(t, dir, ports.IntentEphemeral, "", sz)
141- defer tr.Close()141+ defer func() { _ = tr.Close() }()
142 142
143 require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(ports.Input{Data: []byte("PINGPONG\n")})}))143 require.NoError(t, tr.Send(ports.Frame{Type: ports.MsgInput, Payload: ports.MarshalInput(ports.Input{Data: []byte("PINGPONG\n")})}))
144 awaitText(t, p, sz, "PINGPONG")144 awaitText(t, p, sz, "PINGPONG")
@@ -182,6 +182,6 @@ func TestIntegration_NamedSurvivesReattach(t *testing.T) {
182 182
183 // Re-attach: the first paint must reproduce the retained screen state.183 // Re-attach: the first paint must reproduce the retained screen state.
184 tr2, p2 := attach(t, dir, ports.IntentAttach, "work", sz)184 tr2, p2 := attach(t, dir, ports.IntentAttach, "work", sz)
185- defer tr2.Close()185+ defer func() { _ = tr2.Close() }()
186 awaitText(t, p2, sz, "MARKER")186 awaitText(t, p2, sz, "MARKER")
187 }187 }
M internal/app/run.go +8 -8
@@ -147,13 +147,13 @@ func runDaemon() error {
147147 if err != nil {
148148 return err
149149 }
150- defer logFile.Close()
150+ defer func() { _ = logFile.Close() }()
151151
152152 ln, err := ipc.Listen(ipc.SocketDir())
153153 if err != nil {
154154 return fmt.Errorf("vev: daemon listen: %w", err)
155155 }
156- defer ln.Close()
156+ defer func() { _ = ln.Close() }()
157157
158158 ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
159159 defer stop()
@@ -176,7 +176,7 @@ func runAttach(ctx context.Context, intent uint8, name string) error {
176176 if err != nil {
177177 return err
178178 }
179- defer logFile.Close()
179+ defer func() { _ = logFile.Close() }()
180180
181181 transport, err := ensureDaemon(ctx, ipc.SocketDir(), realDial, realSpawn, defaultBackoff)
182182 if err != nil {
@@ -194,7 +194,7 @@ func runList(_ context.Context) error {
194194 fmt.Println("no sessions")
195195 return nil
196196 }
197- defer transport.Close()
197+ defer func() { _ = transport.Close() }()
198198
199199 if err := transport.Send(ports.Frame{Type: ports.MsgList, Payload: ports.MarshalList(ports.List{})}); err != nil {
200200 return fmt.Errorf("vev: requesting session list: %w", err)
@@ -221,17 +221,17 @@ func runList(_ context.Context) error {
221221 // printSessions renders a session table (or a friendly note when empty).
222222 func printSessions(w io.Writer, sessions []ports.SessionInfo) {
223223 if len(sessions) == 0 {
224- fmt.Fprintln(w, "no sessions")
224+ _, _ = fmt.Fprintln(w, "no sessions")
225225 return
226226 }
227227 tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0)
228- fmt.Fprintln(tw, "NAME\tWINDOWS\tATTACHED")
228+ _, _ = fmt.Fprintln(tw, "NAME\tWINDOWS\tATTACHED")
229229 for _, s := range sessions {
230230 attached := "no"
231231 if s.Attached {
232232 attached = "yes"
233233 }
234- fmt.Fprintf(tw, "%s\t%d\t%s\n", s.Name, s.Windows, attached)
234+ _, _ = fmt.Fprintf(tw, "%s\t%d\t%s\n", s.Name, s.Windows, attached)
235235 }
236236 _ = tw.Flush()
237237 }
@@ -242,7 +242,7 @@ func runKill(_ context.Context, name string) error {
242242 if err != nil {
243243 return fmt.Errorf("vev: no daemon running")
244244 }
245- defer transport.Close()
245+ defer func() { _ = transport.Close() }()
246246
247247 if err := transport.Send(ports.Frame{Type: ports.MsgKill, Payload: ports.MarshalKill(ports.Kill{Name: name})}); err != nil {
248248 return fmt.Errorf("vev: requesting kill: %w", err)
@@ -147,13 +147,13 @@ func runDaemon() error {
147 if err != nil {147 if err != nil {
148 return err148 return err
149 }149 }
150- defer logFile.Close()150+ defer func() { _ = logFile.Close() }()
151 151
152 ln, err := ipc.Listen(ipc.SocketDir())152 ln, err := ipc.Listen(ipc.SocketDir())
153 if err != nil {153 if err != nil {
154 return fmt.Errorf("vev: daemon listen: %w", err)154 return fmt.Errorf("vev: daemon listen: %w", err)
155 }155 }
156- defer ln.Close()156+ defer func() { _ = ln.Close() }()
157 157
158 ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)158 ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
159 defer stop()159 defer stop()
@@ -176,7 +176,7 @@ func runAttach(ctx context.Context, intent uint8, name string) error {
176 if err != nil {176 if err != nil {
177 return err177 return err
178 }178 }
179- defer logFile.Close()179+ defer func() { _ = logFile.Close() }()
180 180
181 transport, err := ensureDaemon(ctx, ipc.SocketDir(), realDial, realSpawn, defaultBackoff)181 transport, err := ensureDaemon(ctx, ipc.SocketDir(), realDial, realSpawn, defaultBackoff)
182 if err != nil {182 if err != nil {
@@ -194,7 +194,7 @@ func runList(_ context.Context) error {
194 fmt.Println("no sessions")194 fmt.Println("no sessions")
195 return nil195 return nil
196 }196 }
197- defer transport.Close()197+ defer func() { _ = transport.Close() }()
198 198
199 if err := transport.Send(ports.Frame{Type: ports.MsgList, Payload: ports.MarshalList(ports.List{})}); err != nil {199 if err := transport.Send(ports.Frame{Type: ports.MsgList, Payload: ports.MarshalList(ports.List{})}); err != nil {
200 return fmt.Errorf("vev: requesting session list: %w", err)200 return fmt.Errorf("vev: requesting session list: %w", err)
@@ -221,17 +221,17 @@ func runList(_ context.Context) error {
221 // printSessions renders a session table (or a friendly note when empty).221 // printSessions renders a session table (or a friendly note when empty).
222 func printSessions(w io.Writer, sessions []ports.SessionInfo) {222 func printSessions(w io.Writer, sessions []ports.SessionInfo) {
223 if len(sessions) == 0 {223 if len(sessions) == 0 {
224- fmt.Fprintln(w, "no sessions")224+ _, _ = fmt.Fprintln(w, "no sessions")
225 return225 return
226 }226 }
227 tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0)227 tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0)
228- fmt.Fprintln(tw, "NAME\tWINDOWS\tATTACHED")228+ _, _ = fmt.Fprintln(tw, "NAME\tWINDOWS\tATTACHED")
229 for _, s := range sessions {229 for _, s := range sessions {
230 attached := "no"230 attached := "no"
231 if s.Attached {231 if s.Attached {
232 attached = "yes"232 attached = "yes"
233 }233 }
234- fmt.Fprintf(tw, "%s\t%d\t%s\n", s.Name, s.Windows, attached)234+ _, _ = fmt.Fprintf(tw, "%s\t%d\t%s\n", s.Name, s.Windows, attached)
235 }235 }
236 _ = tw.Flush()236 _ = tw.Flush()
237 }237 }
@@ -242,7 +242,7 @@ func runKill(_ context.Context, name string) error {
242 if err != nil {242 if err != nil {
243 return fmt.Errorf("vev: no daemon running")243 return fmt.Errorf("vev: no daemon running")
244 }244 }
245- defer transport.Close()245+ defer func() { _ = transport.Close() }()
246 246
247 if err := transport.Send(ports.Frame{Type: ports.MsgKill, Payload: ports.MarshalKill(ports.Kill{Name: name})}); err != nil {247 if err := transport.Send(ports.Frame{Type: ports.MsgKill, Payload: ports.MarshalKill(ports.Kill{Name: name})}); err != nil {
248 return fmt.Errorf("vev: requesting kill: %w", err)248 return fmt.Errorf("vev: requesting kill: %w", err)
M internal/app/spawn.go +1 -1
@@ -141,7 +141,7 @@ func realSpawn() error {
141141 if err != nil {
142142 return fmt.Errorf("opening %s: %w", os.DevNull, err)
143143 }
144- defer devNull.Close()
144+ defer func() { _ = devNull.Close() }()
145145
146146 cmd := exec.Command("/proc/self/exe", "--daemon")
147147 cmd.Stdin = devNull
@@ -141,7 +141,7 @@ func realSpawn() error {
141 if err != nil {141 if err != nil {
142 return fmt.Errorf("opening %s: %w", os.DevNull, err)142 return fmt.Errorf("opening %s: %w", os.DevNull, err)
143 }143 }
144- defer devNull.Close()144+ defer func() { _ = devNull.Close() }()
145 145
146 cmd := exec.Command("/proc/self/exe", "--daemon")146 cmd := exec.Command("/proc/self/exe", "--daemon")
147 cmd.Stdin = devNull147 cmd.Stdin = devNull
M internal/usecase/daemon/daemon.go +2 -2
@@ -343,7 +343,7 @@ func (d *Daemon) handleConn(tr ports.Transport) {
343343 // handleList replies with the current session listing and closes the (one-shot
344344 // control) connection.
345345 func (d *Daemon) handleList(tr ports.Transport) {
346- defer tr.Close()
346+ defer func() { _ = tr.Close() }()
347347
348348 d.mu.Lock()
349349 infos := make([]ports.SessionInfo, 0, len(d.sessions))
@@ -368,7 +368,7 @@ func (d *Daemon) handleList(tr ports.Transport) {
368368 // handleKill terminates the named session (if any) and closes the control
369369 // connection; the resulting EOF is the client's success signal.
370370 func (d *Daemon) handleKill(tr ports.Transport, f ports.Frame) {
371- defer tr.Close()
371+ defer func() { _ = tr.Close() }()
372372
373373 k, err := ports.UnmarshalKill(f.Payload)
374374 if err != nil {
@@ -343,7 +343,7 @@ func (d *Daemon) handleConn(tr ports.Transport) {
343 // handleList replies with the current session listing and closes the (one-shot343 // handleList replies with the current session listing and closes the (one-shot
344 // control) connection.344 // control) connection.
345 func (d *Daemon) handleList(tr ports.Transport) {345 func (d *Daemon) handleList(tr ports.Transport) {
346- defer tr.Close()346+ defer func() { _ = tr.Close() }()
347 347
348 d.mu.Lock()348 d.mu.Lock()
349 infos := make([]ports.SessionInfo, 0, len(d.sessions))349 infos := make([]ports.SessionInfo, 0, len(d.sessions))
@@ -368,7 +368,7 @@ func (d *Daemon) handleList(tr ports.Transport) {
368 // handleKill terminates the named session (if any) and closes the control368 // handleKill terminates the named session (if any) and closes the control
369 // connection; the resulting EOF is the client's success signal.369 // connection; the resulting EOF is the client's success signal.
370 func (d *Daemon) handleKill(tr ports.Transport, f ports.Frame) {370 func (d *Daemon) handleKill(tr ports.Transport, f ports.Frame) {
371- defer tr.Close()371+ defer func() { _ = tr.Close() }()
372 372
373 k, err := ports.UnmarshalKill(f.Payload)373 k, err := ports.UnmarshalKill(f.Payload)
374 if err != nil {374 if err != nil {
M internal/usecase/daemon/daemon_test.go +14 -2
@@ -339,19 +339,24 @@ func TestEphemeralNumberingReuse(t *testing.T) {
339339
340340 // --- attach replace ---------------------------------------------------------
341341
342-func TestAltDigitSwitchesActiveWindow(t *testing.T) {
342+func TestAltDigitSwitchesBetweenThreeWindows(t *testing.T) {
343343 writes1 := make(chan []byte, 1)
344344 writes2 := make(chan []byte, 1)
345+ writes3 := make(chan []byte, 1)
345346 p1, releasePTY1 := newBlockingPTYWithWrites(t, writes1)
346347 p2, releasePTY2 := newBlockingPTYWithWrites(t, writes2)
347- d := newTestDaemon(t, newFactorySeq(t, p1, p2), stubClock{})
348+ p3, releasePTY3 := newBlockingPTYWithWrites(t, writes3)
349+ d := newTestDaemon(t, newFactorySeq(t, p1, p2, p3), stubClock{})
348350 tr, sends, releaseConn := newConn(t,
349351 mustHello(ports.IntentNew, "work", domain.Size{Cols: 80, Rows: 24}),
350352 frameInput([]byte("\x1bc")),
353+ frameInput([]byte("\x1bc")),
351354 frameInput([]byte("\x1b1")),
352355 frameInput([]byte("A")),
353356 frameInput([]byte("\x1b2")),
354357 frameInput([]byte("B")),
358+ frameInput([]byte("\x1b3")),
359+ frameInput([]byte("C")),
355360 )
356361
357362 var hg sync.WaitGroup
@@ -359,14 +364,21 @@ func TestAltDigitSwitchesActiveWindow(t *testing.T) {
359364 awaitFrame(t, sends, ports.MsgWelcome)
360365 awaitFrame(t, sends, ports.MsgOutput)
361366
367+ require.Eventually(t, func() bool {
368+ sessions := listSessions(t, d)
369+ return len(sessions.Sessions) == 1 && sessions.Sessions[0].Windows == 3
370+ }, 2*time.Second, 5*time.Millisecond)
362371 require.Eventually(t, func() bool { return len(writes1) == 1 }, 2*time.Second, 5*time.Millisecond)
363372 require.Equal(t, []byte("A"), <-writes1)
364373 require.Eventually(t, func() bool { return len(writes2) == 1 }, 2*time.Second, 5*time.Millisecond)
365374 require.Equal(t, []byte("B"), <-writes2)
375+ require.Eventually(t, func() bool { return len(writes3) == 1 }, 2*time.Second, 5*time.Millisecond)
376+ require.Equal(t, []byte("C"), <-writes3)
366377
367378 releaseConn()
368379 releasePTY1()
369380 releasePTY2()
381+ releasePTY3()
370382 hg.Wait()
371383 d.sessWg.Wait()
372384 }
@@ -339,19 +339,24 @@ func TestEphemeralNumberingReuse(t *testing.T) {
339 339
340 // --- attach replace ---------------------------------------------------------340 // --- attach replace ---------------------------------------------------------
341 341
342-func TestAltDigitSwitchesActiveWindow(t *testing.T) {342+func TestAltDigitSwitchesBetweenThreeWindows(t *testing.T) {
343 writes1 := make(chan []byte, 1)343 writes1 := make(chan []byte, 1)
344 writes2 := make(chan []byte, 1)344 writes2 := make(chan []byte, 1)
345+ writes3 := make(chan []byte, 1)
345 p1, releasePTY1 := newBlockingPTYWithWrites(t, writes1)346 p1, releasePTY1 := newBlockingPTYWithWrites(t, writes1)
346 p2, releasePTY2 := newBlockingPTYWithWrites(t, writes2)347 p2, releasePTY2 := newBlockingPTYWithWrites(t, writes2)
347- d := newTestDaemon(t, newFactorySeq(t, p1, p2), stubClock{})348+ p3, releasePTY3 := newBlockingPTYWithWrites(t, writes3)
349+ d := newTestDaemon(t, newFactorySeq(t, p1, p2, p3), stubClock{})
348 tr, sends, releaseConn := newConn(t,350 tr, sends, releaseConn := newConn(t,
349 mustHello(ports.IntentNew, "work", domain.Size{Cols: 80, Rows: 24}),351 mustHello(ports.IntentNew, "work", domain.Size{Cols: 80, Rows: 24}),
350 frameInput([]byte("\x1bc")),352 frameInput([]byte("\x1bc")),
353+ frameInput([]byte("\x1bc")),
351 frameInput([]byte("\x1b1")),354 frameInput([]byte("\x1b1")),
352 frameInput([]byte("A")),355 frameInput([]byte("A")),
353 frameInput([]byte("\x1b2")),356 frameInput([]byte("\x1b2")),
354 frameInput([]byte("B")),357 frameInput([]byte("B")),
358+ frameInput([]byte("\x1b3")),
359+ frameInput([]byte("C")),
355 )360 )
356 361
357 var hg sync.WaitGroup362 var hg sync.WaitGroup
@@ -359,14 +364,21 @@ func TestAltDigitSwitchesActiveWindow(t *testing.T) {
359 awaitFrame(t, sends, ports.MsgWelcome)364 awaitFrame(t, sends, ports.MsgWelcome)
360 awaitFrame(t, sends, ports.MsgOutput)365 awaitFrame(t, sends, ports.MsgOutput)
361 366
367+ require.Eventually(t, func() bool {
368+ sessions := listSessions(t, d)
369+ return len(sessions.Sessions) == 1 && sessions.Sessions[0].Windows == 3
370+ }, 2*time.Second, 5*time.Millisecond)
362 require.Eventually(t, func() bool { return len(writes1) == 1 }, 2*time.Second, 5*time.Millisecond)371 require.Eventually(t, func() bool { return len(writes1) == 1 }, 2*time.Second, 5*time.Millisecond)
363 require.Equal(t, []byte("A"), <-writes1)372 require.Equal(t, []byte("A"), <-writes1)
364 require.Eventually(t, func() bool { return len(writes2) == 1 }, 2*time.Second, 5*time.Millisecond)373 require.Eventually(t, func() bool { return len(writes2) == 1 }, 2*time.Second, 5*time.Millisecond)
365 require.Equal(t, []byte("B"), <-writes2)374 require.Equal(t, []byte("B"), <-writes2)
375+ require.Eventually(t, func() bool { return len(writes3) == 1 }, 2*time.Second, 5*time.Millisecond)
376+ require.Equal(t, []byte("C"), <-writes3)
366 377
367 releaseConn()378 releaseConn()
368 releasePTY1()379 releasePTY1()
369 releasePTY2()380 releasePTY2()
381+ releasePTY3()
370 hg.Wait()382 hg.Wait()
371 d.sessWg.Wait()383 d.sessWg.Wait()
372 }384 }
M internal/usecase/keys/router.go +1 -1
@@ -11,7 +11,7 @@ import (
1111
1212 const (
1313 ESC byte = 0x1b
14- ESCDelay = 500 * time.Millisecond
14+ ESCDelay = 40 * time.Millisecond
1515 )
1616
1717 // Action is an intercepted vev key binding.
@@ -11,7 +11,7 @@ import (
11 11
12 const (12 const (
13 ESC byte = 0x1b13 ESC byte = 0x1b
14- ESCDelay = 500 * time.Millisecond14+ ESCDelay = 40 * time.Millisecond
15 )15 )
16 16
17 // Action is an intercepted vev key binding.17 // Action is an intercepted vev key binding.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close