detached c8fa13e

chore(go): satisfy lint hints

commit
c8fa13edef90df2d5cf9c8029effbdb8f0ac613c
author
brice · · 2026-07-02 14:29 UTC
parent
22dc71c
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
16 files · +58 -82
M internal/adapters/ipc/transport_test.go +8 -16
@@ -25,13 +25,11 @@ func TestTransportSendRecvBothDirections(t *testing.T) {
2525 serverToClient := ports.Frame{Type: ports.MsgWelcome, Payload: ports.MarshalWelcome(ports.Welcome{SessionID: "s1", SessionName: "main"})}
2626
2727 var wg sync.WaitGroup
28- wg.Add(1)
29- go func() {
30- defer wg.Done()
28+ wg.Go(func() {
3129 if err := client.Send(clientToServer); err != nil {
3230 t.Errorf("client.Send() error = %v", err)
3331 }
34- }()
32+ })
3533
3634 got, err := server.Recv()
3735 wg.Wait() // ensure the sender goroutine has fully returned before any Fatalf
@@ -42,13 +40,11 @@ func TestTransportSendRecvBothDirections(t *testing.T) {
4240 t.Fatalf("server.Recv() = %#v, want %#v", got, clientToServer)
4341 }
4442
45- wg.Add(1)
46- go func() {
47- defer wg.Done()
43+ wg.Go(func() {
4844 if err := server.Send(serverToClient); err != nil {
4945 t.Errorf("server.Send() error = %v", err)
5046 }
51- }()
47+ })
5248
5349 got, err = client.Recv()
5450 wg.Wait()
@@ -106,13 +102,11 @@ func TestTransportLargeFrameNearCap(t *testing.T) {
106102 want := ports.Frame{Type: ports.MsgOutput, Payload: payload}
107103
108104 var wg sync.WaitGroup
109- wg.Add(1)
110- go func() {
111- defer wg.Done()
105+ wg.Go(func() {
112106 if err := client.Send(want); err != nil {
113107 t.Errorf("client.Send() error = %v", err)
114108 }
115- }()
109+ })
116110
117111 got, err := server.Recv()
118112 wg.Wait() // ensure the sender goroutine has fully returned before any Fatalf
@@ -147,16 +141,14 @@ func TestTransportManySmallFramesBackToBack(t *testing.T) {
147141 frames[150] = ports.Frame{Type: ports.MsgResize, Payload: ports.MarshalResize(ports.Resize{Size: domain.Size{Cols: 5, Rows: 6}})}
148142
149143 var wg sync.WaitGroup
150- wg.Add(1)
151- go func() {
152- defer wg.Done()
144+ wg.Go(func() {
153145 for _, f := range frames {
154146 if err := client.Send(f); err != nil {
155147 t.Errorf("client.Send() error = %v", err)
156148 return
157149 }
158150 }
159- }()
151+ })
160152
161153 // Report mismatches with Errorf (not Fatalf) and keep draining: the
162154 // sender goroutine's net.Pipe writes block until read, so bailing out
@@ -25,13 +25,11 @@ func TestTransportSendRecvBothDirections(t *testing.T) {
25 serverToClient := ports.Frame{Type: ports.MsgWelcome, Payload: ports.MarshalWelcome(ports.Welcome{SessionID: "s1", SessionName: "main"})}25 serverToClient := ports.Frame{Type: ports.MsgWelcome, Payload: ports.MarshalWelcome(ports.Welcome{SessionID: "s1", SessionName: "main"})}
26 26
27 var wg sync.WaitGroup27 var wg sync.WaitGroup
28- wg.Add(1)28+ wg.Go(func() {
29- go func() {
30- defer wg.Done()
31 if err := client.Send(clientToServer); err != nil {29 if err := client.Send(clientToServer); err != nil {
32 t.Errorf("client.Send() error = %v", err)30 t.Errorf("client.Send() error = %v", err)
33 }31 }
34- }()32+ })
35 33
36 got, err := server.Recv()34 got, err := server.Recv()
37 wg.Wait() // ensure the sender goroutine has fully returned before any Fatalf35 wg.Wait() // ensure the sender goroutine has fully returned before any Fatalf
@@ -42,13 +40,11 @@ func TestTransportSendRecvBothDirections(t *testing.T) {
42 t.Fatalf("server.Recv() = %#v, want %#v", got, clientToServer)40 t.Fatalf("server.Recv() = %#v, want %#v", got, clientToServer)
43 }41 }
44 42
45- wg.Add(1)43+ wg.Go(func() {
46- go func() {
47- defer wg.Done()
48 if err := server.Send(serverToClient); err != nil {44 if err := server.Send(serverToClient); err != nil {
49 t.Errorf("server.Send() error = %v", err)45 t.Errorf("server.Send() error = %v", err)
50 }46 }
51- }()47+ })
52 48
53 got, err = client.Recv()49 got, err = client.Recv()
54 wg.Wait()50 wg.Wait()
@@ -106,13 +102,11 @@ func TestTransportLargeFrameNearCap(t *testing.T) {
106 want := ports.Frame{Type: ports.MsgOutput, Payload: payload}102 want := ports.Frame{Type: ports.MsgOutput, Payload: payload}
107 103
108 var wg sync.WaitGroup104 var wg sync.WaitGroup
109- wg.Add(1)105+ wg.Go(func() {
110- go func() {
111- defer wg.Done()
112 if err := client.Send(want); err != nil {106 if err := client.Send(want); err != nil {
113 t.Errorf("client.Send() error = %v", err)107 t.Errorf("client.Send() error = %v", err)
114 }108 }
115- }()109+ })
116 110
117 got, err := server.Recv()111 got, err := server.Recv()
118 wg.Wait() // ensure the sender goroutine has fully returned before any Fatalf112 wg.Wait() // ensure the sender goroutine has fully returned before any Fatalf
@@ -147,16 +141,14 @@ func TestTransportManySmallFramesBackToBack(t *testing.T) {
147 frames[150] = ports.Frame{Type: ports.MsgResize, Payload: ports.MarshalResize(ports.Resize{Size: domain.Size{Cols: 5, Rows: 6}})}141 frames[150] = ports.Frame{Type: ports.MsgResize, Payload: ports.MarshalResize(ports.Resize{Size: domain.Size{Cols: 5, Rows: 6}})}
148 142
149 var wg sync.WaitGroup143 var wg sync.WaitGroup
150- wg.Add(1)144+ wg.Go(func() {
151- go func() {
152- defer wg.Done()
153 for _, f := range frames {145 for _, f := range frames {
154 if err := client.Send(f); err != nil {146 if err := client.Send(f); err != nil {
155 t.Errorf("client.Send() error = %v", err)147 t.Errorf("client.Send() error = %v", err)
156 return148 return
157 }149 }
158 }150 }
159- }()151+ })
160 152
161 // Report mismatches with Errorf (not Fatalf) and keep draining: the153 // Report mismatches with Errorf (not Fatalf) and keep draining: the
162 // sender goroutine's net.Pipe writes block until read, so bailing out154 // sender goroutine's net.Pipe writes block until read, so bailing out
M internal/adapters/term/resize_test.go +1 -1
@@ -15,7 +15,7 @@ func TestResizeLoop_CoalescesBurstToOneEmission(t *testing.T) {
1515 // consuming: draining is synchronous and deterministic here since
1616 // nothing else sends concurrently.
1717 sig := make(chan os.Signal, 8)
18- for i := 0; i < 5; i++ {
18+ for range 5 {
1919 sig <- syscall.SIGWINCH
2020 }
2121
@@ -15,7 +15,7 @@ func TestResizeLoop_CoalescesBurstToOneEmission(t *testing.T) {
15 // consuming: draining is synchronous and deterministic here since15 // consuming: draining is synchronous and deterministic here since
16 // nothing else sends concurrently.16 // nothing else sends concurrently.
17 sig := make(chan os.Signal, 8)17 sig := make(chan os.Signal, 8)
18- for i := 0; i < 5; i++ {18+ for range 5 {
19 sig <- syscall.SIGWINCH19 sig <- syscall.SIGWINCH
20 }20 }
21 21
M internal/adapters/term/terminal.go +2 -4
@@ -204,12 +204,10 @@ func (t *Terminal) ResizeEvents() <-chan domain.Size {
204204 t.sigCh = make(chan os.Signal, 1)
205205 signal.Notify(t.sigCh, syscall.SIGWINCH)
206206
207- t.resizeWG.Add(1)
208- go func() {
209- defer t.resizeWG.Done()
207+ t.resizeWG.Go(func() {
210208 defer signal.Stop(t.sigCh)
211209 resizeLoop(t.sigCh, t.resizeCh, t.resizeQuit, t.Size)
212- }()
210+ })
213211
214212 return t.resizeCh
215213 }
@@ -204,12 +204,10 @@ func (t *Terminal) ResizeEvents() <-chan domain.Size {
204 t.sigCh = make(chan os.Signal, 1)204 t.sigCh = make(chan os.Signal, 1)
205 signal.Notify(t.sigCh, syscall.SIGWINCH)205 signal.Notify(t.sigCh, syscall.SIGWINCH)
206 206
207- t.resizeWG.Add(1)207+ t.resizeWG.Go(func() {
208- go func() {
209- defer t.resizeWG.Done()
210 defer signal.Stop(t.sigCh)208 defer signal.Stop(t.sigCh)
211 resizeLoop(t.sigCh, t.resizeCh, t.resizeQuit, t.Size)209 resizeLoop(t.sigCh, t.resizeCh, t.resizeQuit, t.Size)
212- }()210+ })
213 211
214 return t.resizeCh212 return t.resizeCh
215 }213 }
M internal/adapters/term/terminal_test.go +1 -1
@@ -240,7 +240,7 @@ func TestTerminal_ResizeEvents_ConcurrentWithRestore(t *testing.T) {
240240 // locks in that watcher start/stop are ordered by the mutex; in all
241241 // interleavings the returned channel must eventually be closed and
242242 // no watcher goroutine may leak.
243- for i := 0; i < 50; i++ {
243+ for i := range 50 {
244244 inR, inW, err := os.Pipe()
245245 if err != nil {
246246 t.Fatalf("os.Pipe(in): %v", err)
@@ -240,7 +240,7 @@ func TestTerminal_ResizeEvents_ConcurrentWithRestore(t *testing.T) {
240 // locks in that watcher start/stop are ordered by the mutex; in all240 // locks in that watcher start/stop are ordered by the mutex; in all
241 // interleavings the returned channel must eventually be closed and241 // interleavings the returned channel must eventually be closed and
242 // no watcher goroutine may leak.242 // no watcher goroutine may leak.
243- for i := 0; i < 50; i++ {243+ for i := range 50 {
244 inR, inW, err := os.Pipe()244 inR, inW, err := os.Pipe()
245 if err != nil {245 if err != nil {
246 t.Fatalf("os.Pipe(in): %v", err)246 t.Fatalf("os.Pipe(in): %v", err)
M internal/app/daemon_integration_test.go +2 -2
@@ -111,8 +111,8 @@ func awaitText(t *testing.T, p *pump, sz domain.Size, want string) {
111111
112112 func screenText(s *vt.Screen) string {
113113 var b strings.Builder
114- for y := 0; y < s.Frame.Height; y++ {
115- for x := 0; x < s.Frame.Width; x++ {
114+ for y := range s.Frame.Height {
115+ for x := range s.Frame.Width {
116116 r := s.Frame.At(x, y).Rune
117117 if r == 0 {
118118 r = ' '
@@ -111,8 +111,8 @@ func awaitText(t *testing.T, p *pump, sz domain.Size, want string) {
111 111
112 func screenText(s *vt.Screen) string {112 func screenText(s *vt.Screen) string {
113 var b strings.Builder113 var b strings.Builder
114- for y := 0; y < s.Frame.Height; y++ {114+ for y := range s.Frame.Height {
115- for x := 0; x < s.Frame.Width; x++ {115+ for x := range s.Frame.Width {
116 r := s.Frame.At(x, y).Rune116 r := s.Frame.At(x, y).Rune
117 if r == 0 {117 if r == 0 {
118 r = ' '118 r = ' '
M internal/app/spawn_test.go +3 -5
@@ -28,10 +28,8 @@ func TestAcquireSpawnLockSingleWinnerUnderRace(t *testing.T) {
2828 start := make(chan struct{})
2929 releases := make(chan func(), racers)
3030
31- for i := 0; i < racers; i++ {
32- wg.Add(1)
33- go func() {
34- defer wg.Done()
31+ for range racers {
32+ wg.Go(func() {
3533 <-start
3634 release, acquired, err := acquireSpawnLock(dir)
3735 if err != nil {
@@ -42,7 +40,7 @@ func TestAcquireSpawnLockSingleWinnerUnderRace(t *testing.T) {
4240 wins.Add(1)
4341 releases <- release
4442 }
45- }()
43+ })
4644 }
4745
4846 close(start)
@@ -28,10 +28,8 @@ func TestAcquireSpawnLockSingleWinnerUnderRace(t *testing.T) {
28 start := make(chan struct{})28 start := make(chan struct{})
29 releases := make(chan func(), racers)29 releases := make(chan func(), racers)
30 30
31- for i := 0; i < racers; i++ {31+ for range racers {
32- wg.Add(1)32+ wg.Go(func() {
33- go func() {
34- defer wg.Done()
35 <-start33 <-start
36 release, acquired, err := acquireSpawnLock(dir)34 release, acquired, err := acquireSpawnLock(dir)
37 if err != nil {35 if err != nil {
@@ -42,7 +40,7 @@ func TestAcquireSpawnLockSingleWinnerUnderRace(t *testing.T) {
42 wins.Add(1)40 wins.Add(1)
43 releases <- release41 releases <- release
44 }42 }
45- }()43+ })
46 }44 }
47 45
48 close(start)46 close(start)
M internal/ports/wire.go +1 -1
@@ -474,7 +474,7 @@ func UnmarshalSessions(b []byte) (Sessions, error) {
474474 return Sessions{}, err
475475 }
476476 sessions := make([]SessionInfo, 0, count)
477- for i := 0; i < int(count); i++ {
477+ for range int(count) {
478478 var s SessionInfo
479479 if s.SessionID, err = r.getString(); err != nil {
480480 return Sessions{}, err
@@ -474,7 +474,7 @@ func UnmarshalSessions(b []byte) (Sessions, error) {
474 return Sessions{}, err474 return Sessions{}, err
475 }475 }
476 sessions := make([]SessionInfo, 0, count)476 sessions := make([]SessionInfo, 0, count)
477- for i := 0; i < int(count); i++ {477+ for range int(count) {
478 var s SessionInfo478 var s SessionInfo
479 if s.SessionID, err = r.getString(); err != nil {479 if s.SessionID, err = r.getString(); err != nil {
480 return Sessions{}, err480 return Sessions{}, err
M internal/ports/wire_test.go +1 -2
@@ -27,8 +27,7 @@ func mustNotPanic(t *testing.T, f func()) {
2727 // mid-string-len, mid-string — in one sweep.
2828 func assertAllPrefixesFail[T any](t *testing.T, full []byte, unmarshal func([]byte) (T, error)) {
2929 t.Helper()
30- for n := 0; n < len(full); n++ {
31- n := n
30+ for n := range len(full) {
3231 t.Run(fmt.Sprintf("truncated_at_%d", n), func(t *testing.T) {
3332 mustNotPanic(t, func() {
3433 if _, err := unmarshal(full[:n]); err == nil {
@@ -27,8 +27,7 @@ func mustNotPanic(t *testing.T, f func()) {
27 // mid-string-len, mid-string — in one sweep.27 // mid-string-len, mid-string — in one sweep.
28 func assertAllPrefixesFail[T any](t *testing.T, full []byte, unmarshal func([]byte) (T, error)) {28 func assertAllPrefixesFail[T any](t *testing.T, full []byte, unmarshal func([]byte) (T, error)) {
29 t.Helper()29 t.Helper()
30- for n := 0; n < len(full); n++ {30+ for n := range len(full) {
31- n := n
32 t.Run(fmt.Sprintf("truncated_at_%d", n), func(t *testing.T) {31 t.Run(fmt.Sprintf("truncated_at_%d", n), func(t *testing.T) {
33 mustNotPanic(t, func() {32 mustNotPanic(t, func() {
34 if _, err := unmarshal(full[:n]); err == nil {33 if _, err := unmarshal(full[:n]); err == nil {
M internal/usecase/daemon/daemon.go +3 -6
@@ -270,11 +270,9 @@ func (d *Daemon) Serve(ctx context.Context, l ports.Listener) error {
270270 if err != nil {
271271 break
272272 }
273- d.connWg.Add(1)
274- go func() {
275- defer d.connWg.Done()
273+ d.connWg.Go(func() {
276274 d.handleConn(tr)
277- }()
275+ })
278276 }
279277
280278 // Tear down. shutdownAll marks the daemon closing (route now rejects any
@@ -403,8 +401,7 @@ func (d *Daemon) handleHello(tr ports.Transport, f ports.Frame) {
403401
404402 sess, ac, rerr := d.route(h, tr)
405403 if rerr != nil {
406- var pe *protoErr
407- if errors.As(rerr, &pe) {
404+ if pe, ok := errors.AsType[*protoErr](rerr); ok {
408405 _ = tr.Send(frameError(pe.code, pe.text))
409406 } else {
410407 _ = tr.Send(frameError(ports.ErrInternal, rerr.Error()))
@@ -270,11 +270,9 @@ func (d *Daemon) Serve(ctx context.Context, l ports.Listener) error {
270 if err != nil {270 if err != nil {
271 break271 break
272 }272 }
273- d.connWg.Add(1)273+ d.connWg.Go(func() {
274- go func() {
275- defer d.connWg.Done()
276 d.handleConn(tr)274 d.handleConn(tr)
277- }()275+ })
278 }276 }
279 277
280 // Tear down. shutdownAll marks the daemon closing (route now rejects any278 // Tear down. shutdownAll marks the daemon closing (route now rejects any
@@ -403,8 +401,7 @@ func (d *Daemon) handleHello(tr ports.Transport, f ports.Frame) {
403 401
404 sess, ac, rerr := d.route(h, tr)402 sess, ac, rerr := d.route(h, tr)
405 if rerr != nil {403 if rerr != nil {
406- var pe *protoErr404+ if pe, ok := errors.AsType[*protoErr](rerr); ok {
407- if errors.As(rerr, &pe) {
408 _ = tr.Send(frameError(pe.code, pe.text))405 _ = tr.Send(frameError(pe.code, pe.text))
409 } else {406 } else {
410 _ = tr.Send(frameError(ports.ErrInternal, rerr.Error()))407 _ = tr.Send(frameError(ports.ErrInternal, rerr.Error()))
M internal/usecase/daemon/daemon_test.go +9 -17
@@ -150,8 +150,7 @@ func TestHandshakeEphemeralHappy(t *testing.T) {
150150 tr, sends, releaseConn := newConn(t, mustHello(ports.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}))
151151
152152 var hg sync.WaitGroup
153- hg.Add(1)
154- go func() { defer hg.Done(); d.handleConn(tr) }()
153+ hg.Go(func() { d.handleConn(tr) })
155154
156155 w := awaitFrame(t, sends, ports.MsgWelcome)
157156 welcome, err := ports.UnmarshalWelcome(w.Payload)
@@ -173,8 +172,7 @@ func TestHandshakeNewHappy(t *testing.T) {
173172 tr, sends, releaseConn := newConn(t, mustHello(ports.IntentNew, "work", domain.Size{Cols: 80, Rows: 24}))
174173
175174 var hg sync.WaitGroup
176- hg.Add(1)
177- go func() { defer hg.Done(); d.handleConn(tr) }()
175+ hg.Go(func() { d.handleConn(tr) })
178176
179177 w := awaitFrame(t, sends, ports.MsgWelcome)
180178 welcome, err := ports.UnmarshalWelcome(w.Payload)
@@ -256,8 +254,7 @@ func TestAttachReplaceDetachesOld(t *testing.T) {
256254 // Client A creates and attaches to ephemeral "0".
257255 trA, sendsA, releaseA := newConn(t, mustHello(ports.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}))
258256 var hg sync.WaitGroup
259- hg.Add(1)
260- go func() { defer hg.Done(); d.handleConn(trA) }()
257+ hg.Go(func() { d.handleConn(trA) })
261258 awaitFrame(t, sendsA, ports.MsgWelcome)
262259 awaitFrame(t, sendsA, ports.MsgOutput)
263260 sess := firstSession(d)
@@ -265,8 +262,7 @@ func TestAttachReplaceDetachesOld(t *testing.T) {
265262
266263 // Client B attaches to the same session, displacing A.
267264 trB, sendsB, releaseB := newConn(t, mustHello(ports.IntentAttach, "0", domain.Size{Cols: 80, Rows: 24}))
268- hg.Add(1)
269- go func() { defer hg.Done(); d.handleConn(trB) }()
265+ hg.Go(func() { d.handleConn(trB) })
270266 awaitFrame(t, sendsB, ports.MsgWelcome)
271267
272268 // A is notified it was detached.
@@ -299,8 +295,7 @@ func TestDetachKillsEphemeral(t *testing.T) {
299295 )
300296
301297 var hg sync.WaitGroup
302- hg.Add(1)
303- go func() { defer hg.Done(); d.handleConn(tr) }()
298+ hg.Go(func() { d.handleConn(tr) })
304299 awaitFrame(t, sends, ports.MsgWelcome)
305300
306301 require.Eventually(t, func() bool { return sessionCount(d) == 0 }, 2*time.Second, 5*time.Millisecond,
@@ -320,8 +315,7 @@ func TestDetachKeepsNamed(t *testing.T) {
320315 )
321316
322317 var hg sync.WaitGroup
323- hg.Add(1)
324- go func() { defer hg.Done(); d.handleConn(tr) }()
318+ hg.Go(func() { d.handleConn(tr) })
325319 awaitFrame(t, sends, ports.MsgWelcome)
326320 awaitFrame(t, sends, ports.MsgDetached) // ack for explicit detach
327321 hg.Wait() // handler returns after detach
@@ -388,7 +382,7 @@ func TestSchedulerDebounceCoalesces(t *testing.T) {
388382 <-newTimerCalled
389383
390384 // A burst of further dirties inside the window must be absorbed.
391- for i := 0; i < 5; i++ {
385+ for range 5 {
392386 select {
393387 case win.dirty <- struct{}{}:
394388 default:
@@ -452,8 +446,7 @@ func TestReaderEOFRemovesSessionAndSignalsShutdown(t *testing.T) {
452446 tr, sends, _ := newConn(t, mustHello(ports.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}))
453447
454448 var hg sync.WaitGroup
455- hg.Add(1)
456- go func() { defer hg.Done(); d.handleConn(tr) }()
449+ hg.Go(func() { d.handleConn(tr) })
457450
458451 // The client is detached with ReasonSessionKilled when the child exits.
459452 det := awaitFrame(t, sends, ports.MsgDetached)
@@ -576,8 +569,7 @@ func TestHelloRacingShutdownIsRejected(t *testing.T) {
576569
577570 tr1, sends1, release1 := newConn(t, mustHello(ports.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}))
578571 var hg sync.WaitGroup
579- hg.Add(1)
580- go func() { defer hg.Done(); d.handleConn(tr1) }()
572+ hg.Go(func() { d.handleConn(tr1) })
581573 awaitFrame(t, sends1, ports.MsgWelcome)
582574 sess := firstSession(d)
583575 require.NotNil(t, sess)
@@ -150,8 +150,7 @@ func TestHandshakeEphemeralHappy(t *testing.T) {
150 tr, sends, releaseConn := newConn(t, mustHello(ports.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}))150 tr, sends, releaseConn := newConn(t, mustHello(ports.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}))
151 151
152 var hg sync.WaitGroup152 var hg sync.WaitGroup
153- hg.Add(1)153+ hg.Go(func() { d.handleConn(tr) })
154- go func() { defer hg.Done(); d.handleConn(tr) }()
155 154
156 w := awaitFrame(t, sends, ports.MsgWelcome)155 w := awaitFrame(t, sends, ports.MsgWelcome)
157 welcome, err := ports.UnmarshalWelcome(w.Payload)156 welcome, err := ports.UnmarshalWelcome(w.Payload)
@@ -173,8 +172,7 @@ func TestHandshakeNewHappy(t *testing.T) {
173 tr, sends, releaseConn := newConn(t, mustHello(ports.IntentNew, "work", domain.Size{Cols: 80, Rows: 24}))172 tr, sends, releaseConn := newConn(t, mustHello(ports.IntentNew, "work", domain.Size{Cols: 80, Rows: 24}))
174 173
175 var hg sync.WaitGroup174 var hg sync.WaitGroup
176- hg.Add(1)175+ hg.Go(func() { d.handleConn(tr) })
177- go func() { defer hg.Done(); d.handleConn(tr) }()
178 176
179 w := awaitFrame(t, sends, ports.MsgWelcome)177 w := awaitFrame(t, sends, ports.MsgWelcome)
180 welcome, err := ports.UnmarshalWelcome(w.Payload)178 welcome, err := ports.UnmarshalWelcome(w.Payload)
@@ -256,8 +254,7 @@ func TestAttachReplaceDetachesOld(t *testing.T) {
256 // Client A creates and attaches to ephemeral "0".254 // Client A creates and attaches to ephemeral "0".
257 trA, sendsA, releaseA := newConn(t, mustHello(ports.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}))255 trA, sendsA, releaseA := newConn(t, mustHello(ports.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}))
258 var hg sync.WaitGroup256 var hg sync.WaitGroup
259- hg.Add(1)257+ hg.Go(func() { d.handleConn(trA) })
260- go func() { defer hg.Done(); d.handleConn(trA) }()
261 awaitFrame(t, sendsA, ports.MsgWelcome)258 awaitFrame(t, sendsA, ports.MsgWelcome)
262 awaitFrame(t, sendsA, ports.MsgOutput)259 awaitFrame(t, sendsA, ports.MsgOutput)
263 sess := firstSession(d)260 sess := firstSession(d)
@@ -265,8 +262,7 @@ func TestAttachReplaceDetachesOld(t *testing.T) {
265 262
266 // Client B attaches to the same session, displacing A.263 // Client B attaches to the same session, displacing A.
267 trB, sendsB, releaseB := newConn(t, mustHello(ports.IntentAttach, "0", domain.Size{Cols: 80, Rows: 24}))264 trB, sendsB, releaseB := newConn(t, mustHello(ports.IntentAttach, "0", domain.Size{Cols: 80, Rows: 24}))
268- hg.Add(1)265+ hg.Go(func() { d.handleConn(trB) })
269- go func() { defer hg.Done(); d.handleConn(trB) }()
270 awaitFrame(t, sendsB, ports.MsgWelcome)266 awaitFrame(t, sendsB, ports.MsgWelcome)
271 267
272 // A is notified it was detached.268 // A is notified it was detached.
@@ -299,8 +295,7 @@ func TestDetachKillsEphemeral(t *testing.T) {
299 )295 )
300 296
301 var hg sync.WaitGroup297 var hg sync.WaitGroup
302- hg.Add(1)298+ hg.Go(func() { d.handleConn(tr) })
303- go func() { defer hg.Done(); d.handleConn(tr) }()
304 awaitFrame(t, sends, ports.MsgWelcome)299 awaitFrame(t, sends, ports.MsgWelcome)
305 300
306 require.Eventually(t, func() bool { return sessionCount(d) == 0 }, 2*time.Second, 5*time.Millisecond,301 require.Eventually(t, func() bool { return sessionCount(d) == 0 }, 2*time.Second, 5*time.Millisecond,
@@ -320,8 +315,7 @@ func TestDetachKeepsNamed(t *testing.T) {
320 )315 )
321 316
322 var hg sync.WaitGroup317 var hg sync.WaitGroup
323- hg.Add(1)318+ hg.Go(func() { d.handleConn(tr) })
324- go func() { defer hg.Done(); d.handleConn(tr) }()
325 awaitFrame(t, sends, ports.MsgWelcome)319 awaitFrame(t, sends, ports.MsgWelcome)
326 awaitFrame(t, sends, ports.MsgDetached) // ack for explicit detach320 awaitFrame(t, sends, ports.MsgDetached) // ack for explicit detach
327 hg.Wait() // handler returns after detach321 hg.Wait() // handler returns after detach
@@ -388,7 +382,7 @@ func TestSchedulerDebounceCoalesces(t *testing.T) {
388 <-newTimerCalled382 <-newTimerCalled
389 383
390 // A burst of further dirties inside the window must be absorbed.384 // A burst of further dirties inside the window must be absorbed.
391- for i := 0; i < 5; i++ {385+ for range 5 {
392 select {386 select {
393 case win.dirty <- struct{}{}:387 case win.dirty <- struct{}{}:
394 default:388 default:
@@ -452,8 +446,7 @@ func TestReaderEOFRemovesSessionAndSignalsShutdown(t *testing.T) {
452 tr, sends, _ := newConn(t, mustHello(ports.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}))446 tr, sends, _ := newConn(t, mustHello(ports.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}))
453 447
454 var hg sync.WaitGroup448 var hg sync.WaitGroup
455- hg.Add(1)449+ hg.Go(func() { d.handleConn(tr) })
456- go func() { defer hg.Done(); d.handleConn(tr) }()
457 450
458 // The client is detached with ReasonSessionKilled when the child exits.451 // The client is detached with ReasonSessionKilled when the child exits.
459 det := awaitFrame(t, sends, ports.MsgDetached)452 det := awaitFrame(t, sends, ports.MsgDetached)
@@ -576,8 +569,7 @@ func TestHelloRacingShutdownIsRejected(t *testing.T) {
576 569
577 tr1, sends1, release1 := newConn(t, mustHello(ports.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}))570 tr1, sends1, release1 := newConn(t, mustHello(ports.IntentEphemeral, "", domain.Size{Cols: 80, Rows: 24}))
578 var hg sync.WaitGroup571 var hg sync.WaitGroup
579- hg.Add(1)572+ hg.Go(func() { d.handleConn(tr1) })
580- go func() { defer hg.Done(); d.handleConn(tr1) }()
581 awaitFrame(t, sends1, ports.MsgWelcome)573 awaitFrame(t, sends1, ports.MsgWelcome)
582 sess := firstSession(d)574 sess := firstSession(d)
583 require.NotNil(t, sess)575 require.NotNil(t, sess)
M pkg/renderer/frame_test.go +3 -3
@@ -22,7 +22,7 @@ func fillFrame(f Frame, rows []string) {
2222
2323 func TestNewFrameCanonicalOffsets(t *testing.T) {
2424 f := NewFrame(4, 3)
25- for y := 0; y < f.Height; y++ {
25+ for y := range f.Height {
2626 if f.lineOffset[y] != y*f.Width {
2727 t.Fatalf("lineOffset[%d] = %d, want %d", y, f.lineOffset[y], y*f.Width)
2828 }
@@ -88,7 +88,7 @@ func TestScrollUpEquivalence(t *testing.T) {
8888 f := NewFrame(tt.width, tt.height)
8989 fillFrame(f, tt.rows)
9090 f.ScrollUp(tt.top, tt.bottom, tt.n)
91- for y := 0; y < f.Height; y++ {
91+ for y := range f.Height {
9292 if got := rowRunes(f, y); got != tt.want[y] {
9393 t.Errorf("row %d = %q, want %q", y, got, tt.want[y])
9494 }
@@ -127,7 +127,7 @@ func TestScrollDownEquivalence(t *testing.T) {
127127 f := NewFrame(tt.width, tt.height)
128128 fillFrame(f, tt.rows)
129129 f.ScrollDown(tt.top, tt.bottom, tt.n)
130- for y := 0; y < f.Height; y++ {
130+ for y := range f.Height {
131131 if got := rowRunes(f, y); got != tt.want[y] {
132132 t.Errorf("row %d = %q, want %q", y, got, tt.want[y])
133133 }
@@ -22,7 +22,7 @@ func fillFrame(f Frame, rows []string) {
22 22
23 func TestNewFrameCanonicalOffsets(t *testing.T) {23 func TestNewFrameCanonicalOffsets(t *testing.T) {
24 f := NewFrame(4, 3)24 f := NewFrame(4, 3)
25- for y := 0; y < f.Height; y++ {25+ for y := range f.Height {
26 if f.lineOffset[y] != y*f.Width {26 if f.lineOffset[y] != y*f.Width {
27 t.Fatalf("lineOffset[%d] = %d, want %d", y, f.lineOffset[y], y*f.Width)27 t.Fatalf("lineOffset[%d] = %d, want %d", y, f.lineOffset[y], y*f.Width)
28 }28 }
@@ -88,7 +88,7 @@ func TestScrollUpEquivalence(t *testing.T) {
88 f := NewFrame(tt.width, tt.height)88 f := NewFrame(tt.width, tt.height)
89 fillFrame(f, tt.rows)89 fillFrame(f, tt.rows)
90 f.ScrollUp(tt.top, tt.bottom, tt.n)90 f.ScrollUp(tt.top, tt.bottom, tt.n)
91- for y := 0; y < f.Height; y++ {91+ for y := range f.Height {
92 if got := rowRunes(f, y); got != tt.want[y] {92 if got := rowRunes(f, y); got != tt.want[y] {
93 t.Errorf("row %d = %q, want %q", y, got, tt.want[y])93 t.Errorf("row %d = %q, want %q", y, got, tt.want[y])
94 }94 }
@@ -127,7 +127,7 @@ func TestScrollDownEquivalence(t *testing.T) {
127 f := NewFrame(tt.width, tt.height)127 f := NewFrame(tt.width, tt.height)
128 fillFrame(f, tt.rows)128 fillFrame(f, tt.rows)
129 f.ScrollDown(tt.top, tt.bottom, tt.n)129 f.ScrollDown(tt.top, tt.bottom, tt.n)
130- for y := 0; y < f.Height; y++ {130+ for y := range f.Height {
131 if got := rowRunes(f, y); got != tt.want[y] {131 if got := rowRunes(f, y); got != tt.want[y] {
132 t.Errorf("row %d = %q, want %q", y, got, tt.want[y])132 t.Errorf("row %d = %q, want %q", y, got, tt.want[y])
133 }133 }
M pkg/renderer/renderer.go +5 -5
@@ -115,7 +115,7 @@ func putBuffer(buf *bytes.Buffer) {
115115 }
116116
117117 func (r *Renderer) hasDirtyLines(frame Frame) bool {
118- for y := 0; y < frame.Height; y++ {
118+ for y := range frame.Height {
119119 if r.lineDirty(frame, y) {
120120 return true
121121 }
@@ -145,7 +145,7 @@ func hasScrollDamage(damage []Damage) bool {
145145 }
146146
147147 func (r *Renderer) writeFull(out *bytes.Buffer, frame Frame, st *drawState) {
148- for y := 0; y < frame.Height; y++ {
148+ for y := range frame.Height {
149149 r.emitSpan(out, frame, y, 0, frame.Width, st)
150150 }
151151 out.WriteString("\x1b[0m")
@@ -153,7 +153,7 @@ func (r *Renderer) writeFull(out *bytes.Buffer, frame Frame, st *drawState) {
153153
154154 func (r *Renderer) writeDamage(out *bytes.Buffer, frame Frame, damage []Damage, skip *Damage, st *drawState) {
155155 if len(damage) == 0 {
156- for y := 0; y < frame.Height; y++ {
156+ for y := range frame.Height {
157157 if r.lineDirty(frame, y) {
158158 r.emitSpan(out, frame, y, 0, frame.Width, st)
159159 }
@@ -184,7 +184,7 @@ func (r *Renderer) lineDirty(frame Frame, y int) bool {
184184 // through its logical accessor so the frame's physical layout is invisible.
185185 start := y * frame.Width
186186 row := frame.Row(y)
187- for i := 0; i < frame.Width; i++ {
187+ for i := range frame.Width {
188188 if !r.shadow[start+i].Equal(row[i]) {
189189 return true
190190 }
@@ -203,7 +203,7 @@ func (r *Renderer) replaceShadow(frame Frame) {
203203 }
204204 // Copy logical rows into canonical shadow order so the frame's line-offset
205205 // rotation never leaks into the renderer's own buffer.
206- for y := 0; y < frame.Height; y++ {
206+ for y := range frame.Height {
207207 copy(r.shadow[y*frame.Width:], frame.Row(y))
208208 }
209209 }
@@ -115,7 +115,7 @@ func putBuffer(buf *bytes.Buffer) {
115 }115 }
116 116
117 func (r *Renderer) hasDirtyLines(frame Frame) bool {117 func (r *Renderer) hasDirtyLines(frame Frame) bool {
118- for y := 0; y < frame.Height; y++ {118+ for y := range frame.Height {
119 if r.lineDirty(frame, y) {119 if r.lineDirty(frame, y) {
120 return true120 return true
121 }121 }
@@ -145,7 +145,7 @@ func hasScrollDamage(damage []Damage) bool {
145 }145 }
146 146
147 func (r *Renderer) writeFull(out *bytes.Buffer, frame Frame, st *drawState) {147 func (r *Renderer) writeFull(out *bytes.Buffer, frame Frame, st *drawState) {
148- for y := 0; y < frame.Height; y++ {148+ for y := range frame.Height {
149 r.emitSpan(out, frame, y, 0, frame.Width, st)149 r.emitSpan(out, frame, y, 0, frame.Width, st)
150 }150 }
151 out.WriteString("\x1b[0m")151 out.WriteString("\x1b[0m")
@@ -153,7 +153,7 @@ func (r *Renderer) writeFull(out *bytes.Buffer, frame Frame, st *drawState) {
153 153
154 func (r *Renderer) writeDamage(out *bytes.Buffer, frame Frame, damage []Damage, skip *Damage, st *drawState) {154 func (r *Renderer) writeDamage(out *bytes.Buffer, frame Frame, damage []Damage, skip *Damage, st *drawState) {
155 if len(damage) == 0 {155 if len(damage) == 0 {
156- for y := 0; y < frame.Height; y++ {156+ for y := range frame.Height {
157 if r.lineDirty(frame, y) {157 if r.lineDirty(frame, y) {
158 r.emitSpan(out, frame, y, 0, frame.Width, st)158 r.emitSpan(out, frame, y, 0, frame.Width, st)
159 }159 }
@@ -184,7 +184,7 @@ func (r *Renderer) lineDirty(frame Frame, y int) bool {
184 // through its logical accessor so the frame's physical layout is invisible.184 // through its logical accessor so the frame's physical layout is invisible.
185 start := y * frame.Width185 start := y * frame.Width
186 row := frame.Row(y)186 row := frame.Row(y)
187- for i := 0; i < frame.Width; i++ {187+ for i := range frame.Width {
188 if !r.shadow[start+i].Equal(row[i]) {188 if !r.shadow[start+i].Equal(row[i]) {
189 return true189 return true
190 }190 }
@@ -203,7 +203,7 @@ func (r *Renderer) replaceShadow(frame Frame) {
203 }203 }
204 // Copy logical rows into canonical shadow order so the frame's line-offset204 // Copy logical rows into canonical shadow order so the frame's line-offset
205 // rotation never leaks into the renderer's own buffer.205 // rotation never leaks into the renderer's own buffer.
206- for y := 0; y < frame.Height; y++ {206+ for y := range frame.Height {
207 copy(r.shadow[y*frame.Width:], frame.Row(y))207 copy(r.shadow[y*frame.Width:], frame.Row(y))
208 }208 }
209 }209 }
M pkg/renderer/renderer_test.go +2 -2
@@ -10,8 +10,8 @@ import (
1010
1111 // markFrame writes uniform printable content so each row is distinguishable.
1212 func markFrame(f *Frame) {
13- for y := 0; y < f.Height; y++ {
14- for x := 0; x < f.Width; x++ {
13+ for y := range f.Height {
14+ for x := range f.Width {
1515 f.Set(x, y, Cell{Rune: rune('A' + (y*f.Width+x)%26), Style: DefaultStyle()})
1616 }
1717 }
@@ -10,8 +10,8 @@ import (
10 10
11 // markFrame writes uniform printable content so each row is distinguishable.11 // markFrame writes uniform printable content so each row is distinguishable.
12 func markFrame(f *Frame) {12 func markFrame(f *Frame) {
13- for y := 0; y < f.Height; y++ {13+ for y := range f.Height {
14- for x := 0; x < f.Width; x++ {14+ for x := range f.Width {
15 f.Set(x, y, Cell{Rune: rune('A' + (y*f.Width+x)%26), Style: DefaultStyle()})15 f.Set(x, y, Cell{Rune: rune('A' + (y*f.Width+x)%26), Style: DefaultStyle()})
16 }16 }
17 }17 }
M pkg/renderer/scroll.go +1 -1
@@ -46,7 +46,7 @@ func (r *Renderer) canApplyScroll(frame Frame, scroll Damage, damage []Damage) b
4646 for y := scroll.Y; y < scroll.Y+scroll.Height-scroll.Count; y++ {
4747 frameRow := frame.Row(y)
4848 shadowStart := (y+scroll.Count)*r.width + scroll.X
49- for x := 0; x < scroll.Width; x++ {
49+ for x := range scroll.Width {
5050 if r.shadow[shadowStart+x].Equal(frameRow[scroll.X+x]) {
5151 continue
5252 }
@@ -46,7 +46,7 @@ func (r *Renderer) canApplyScroll(frame Frame, scroll Damage, damage []Damage) b
46 for y := scroll.Y; y < scroll.Y+scroll.Height-scroll.Count; y++ {46 for y := scroll.Y; y < scroll.Y+scroll.Height-scroll.Count; y++ {
47 frameRow := frame.Row(y)47 frameRow := frame.Row(y)
48 shadowStart := (y+scroll.Count)*r.width + scroll.X48 shadowStart := (y+scroll.Count)*r.width + scroll.X
49- for x := 0; x < scroll.Width; x++ {49+ for x := range scroll.Width {
50 if r.shadow[shadowStart+x].Equal(frameRow[scroll.X+x]) {50 if r.shadow[shadowStart+x].Equal(frameRow[scroll.X+x]) {
51 continue51 continue
52 }52 }
M pkg/vt/screen.go +2 -2
@@ -377,7 +377,7 @@ func (s *Screen) repairRow(y int) {
377377 return
378378 }
379379 w := s.Frame.Width
380- for x := 0; x < w; x++ {
380+ for x := range w {
381381 c := s.Frame.At(x, y)
382382 if c.Continuation {
383383 if x == 0 {
@@ -876,7 +876,7 @@ func (s *Screen) exitAlternateScreen() {
876876 // canonical, so a rotated source is normalized in the clone.
877877 func cloneFrame(frame renderer.Frame) renderer.Frame {
878878 out := renderer.NewFrame(frame.Width, frame.Height)
879- for y := 0; y < frame.Height; y++ {
879+ for y := range frame.Height {
880880 copy(out.Row(y), frame.Row(y))
881881 }
882882 return out
@@ -377,7 +377,7 @@ func (s *Screen) repairRow(y int) {
377 return377 return
378 }378 }
379 w := s.Frame.Width379 w := s.Frame.Width
380- for x := 0; x < w; x++ {380+ for x := range w {
381 c := s.Frame.At(x, y)381 c := s.Frame.At(x, y)
382 if c.Continuation {382 if c.Continuation {
383 if x == 0 {383 if x == 0 {
@@ -876,7 +876,7 @@ func (s *Screen) exitAlternateScreen() {
876 // canonical, so a rotated source is normalized in the clone.876 // canonical, so a rotated source is normalized in the clone.
877 func cloneFrame(frame renderer.Frame) renderer.Frame {877 func cloneFrame(frame renderer.Frame) renderer.Frame {
878 out := renderer.NewFrame(frame.Width, frame.Height)878 out := renderer.NewFrame(frame.Width, frame.Height)
879- for y := 0; y < frame.Height; y++ {879+ for y := range frame.Height {
880 copy(out.Row(y), frame.Row(y))880 copy(out.Row(y), frame.Row(y))
881 }881 }
882 return out882 return out
M pkg/vt/screen_test.go +14 -14
@@ -567,8 +567,8 @@ func TestClearAndErase(t *testing.T) {
567567 s.Write([]byte("\x1b[2J"))
568568
569569 // All cells should be blank.
570- for y := 0; y < 3; y++ {
571- for x := 0; x < 5; x++ {
570+ for y := range 3 {
571+ for x := range 5 {
572572 if c := cellAt(s, x, y); c.Rune != ' ' {
573573 t.Errorf("cell(%d,%d) = %q, want space after clear", x, y, c.Rune)
574574 }
@@ -646,7 +646,7 @@ func TestClearAndErase(t *testing.T) {
646646 s.Write([]byte("abcde"))
647647 s.Write([]byte("\x1b[1;3H"))
648648 s.Write([]byte("\x1b[2K"))
649- for x := 0; x < 5; x++ {
649+ for x := range 5 {
650650 if c := cellAt(s, x, 0); c.Rune != ' ' {
651651 t.Fatalf("line clear left %q at %d", c.Rune, x)
652652 }
@@ -1097,7 +1097,7 @@ func TestResize(t *testing.T) {
10971097 t.Errorf("frame size = %dx%d, want 10x5", s.Frame.Width, s.Frame.Height)
10981098 }
10991099 // Should be blank (new frame).
1100- for x := 0; x < 10; x++ {
1100+ for x := range 10 {
11011101 assertCell(t, s, x, 0, ' ')
11021102 }
11031103 // Cursor reset.
@@ -1182,8 +1182,8 @@ func TestInvalidInputIgnored(t *testing.T) {
11821182 s := NewScreen(10, 3)
11831183 s.Write(tt.input)
11841184 // Should not panic, and no cells should be modified.
1185- for y := 0; y < 3; y++ {
1186- for x := 0; x < 10; x++ {
1185+ for y := range 3 {
1186+ for x := range 10 {
11871187 if c := cellAt(s, x, y); c.Rune != ' ' {
11881188 t.Errorf("cell(%d,%d) = %q, want space", x, y, c.Rune)
11891189 }
@@ -1626,8 +1626,8 @@ func TestWideAndZeroWidthRunes(t *testing.T) {
16261626 s.Write([]byte("\xcc\x81")) // combining acute accent alone (no base char)
16271627
16281628 // No cells should be modified.
1629- for y := 0; y < 3; y++ {
1630- for x := 0; x < 10; x++ {
1629+ for y := range 3 {
1630+ for x := range 10 {
16311631 if c := cellAt(s, x, y); c.Rune != ' ' {
16321632 t.Errorf("cell(%d,%d) = %q, want space", x, y, c.Rune)
16331633 }
@@ -1795,8 +1795,8 @@ func TestCSIScrollUp(t *testing.T) {
17951795 s.Write([]byte("\x1b[100S"))
17961796
17971797 // All rows should be blank.
1798- for y := 0; y < 3; y++ {
1799- for x := 0; x < 5; x++ {
1798+ for y := range 3 {
1799+ for x := range 5 {
18001800 assertCell(t, s, x, y, ' ')
18011801 }
18021802 }
@@ -2002,8 +2002,8 @@ func TestC1Controls(t *testing.T) {
20022002 s.Write([]byte{0xC2, 0x9F}) // U+009F APC
20032003
20042004 // No cells should be modified.
2005- for y := 0; y < 3; y++ {
2006- for x := 0; x < 10; x++ {
2005+ for y := range 3 {
2006+ for x := range 10 {
20072007 if c := cellAt(s, x, y); c.Rune != ' ' {
20082008 t.Errorf("cell(%d,%d) = %q, want space after C1 controls", x, y, c.Rune)
20092009 }
@@ -2025,8 +2025,8 @@ func TestC1Controls(t *testing.T) {
20252025 }
20262026
20272027 // No cells should be modified.
2028- for y := 0; y < 3; y++ {
2029- for x := 0; x < 10; x++ {
2028+ for y := range 3 {
2029+ for x := range 10 {
20302030 if c := cellAt(s, x, y); c.Rune != ' ' {
20312031 t.Errorf("cell(%d,%d) = %q, want space after C1 range", x, y, c.Rune)
20322032 }
@@ -567,8 +567,8 @@ func TestClearAndErase(t *testing.T) {
567 s.Write([]byte("\x1b[2J"))567 s.Write([]byte("\x1b[2J"))
568 568
569 // All cells should be blank.569 // All cells should be blank.
570- for y := 0; y < 3; y++ {570+ for y := range 3 {
571- for x := 0; x < 5; x++ {571+ for x := range 5 {
572 if c := cellAt(s, x, y); c.Rune != ' ' {572 if c := cellAt(s, x, y); c.Rune != ' ' {
573 t.Errorf("cell(%d,%d) = %q, want space after clear", x, y, c.Rune)573 t.Errorf("cell(%d,%d) = %q, want space after clear", x, y, c.Rune)
574 }574 }
@@ -646,7 +646,7 @@ func TestClearAndErase(t *testing.T) {
646 s.Write([]byte("abcde"))646 s.Write([]byte("abcde"))
647 s.Write([]byte("\x1b[1;3H"))647 s.Write([]byte("\x1b[1;3H"))
648 s.Write([]byte("\x1b[2K"))648 s.Write([]byte("\x1b[2K"))
649- for x := 0; x < 5; x++ {649+ for x := range 5 {
650 if c := cellAt(s, x, 0); c.Rune != ' ' {650 if c := cellAt(s, x, 0); c.Rune != ' ' {
651 t.Fatalf("line clear left %q at %d", c.Rune, x)651 t.Fatalf("line clear left %q at %d", c.Rune, x)
652 }652 }
@@ -1097,7 +1097,7 @@ func TestResize(t *testing.T) {
1097 t.Errorf("frame size = %dx%d, want 10x5", s.Frame.Width, s.Frame.Height)1097 t.Errorf("frame size = %dx%d, want 10x5", s.Frame.Width, s.Frame.Height)
1098 }1098 }
1099 // Should be blank (new frame).1099 // Should be blank (new frame).
1100- for x := 0; x < 10; x++ {1100+ for x := range 10 {
1101 assertCell(t, s, x, 0, ' ')1101 assertCell(t, s, x, 0, ' ')
1102 }1102 }
1103 // Cursor reset.1103 // Cursor reset.
@@ -1182,8 +1182,8 @@ func TestInvalidInputIgnored(t *testing.T) {
1182 s := NewScreen(10, 3)1182 s := NewScreen(10, 3)
1183 s.Write(tt.input)1183 s.Write(tt.input)
1184 // Should not panic, and no cells should be modified.1184 // Should not panic, and no cells should be modified.
1185- for y := 0; y < 3; y++ {1185+ for y := range 3 {
1186- for x := 0; x < 10; x++ {1186+ for x := range 10 {
1187 if c := cellAt(s, x, y); c.Rune != ' ' {1187 if c := cellAt(s, x, y); c.Rune != ' ' {
1188 t.Errorf("cell(%d,%d) = %q, want space", x, y, c.Rune)1188 t.Errorf("cell(%d,%d) = %q, want space", x, y, c.Rune)
1189 }1189 }
@@ -1626,8 +1626,8 @@ func TestWideAndZeroWidthRunes(t *testing.T) {
1626 s.Write([]byte("\xcc\x81")) // combining acute accent alone (no base char)1626 s.Write([]byte("\xcc\x81")) // combining acute accent alone (no base char)
1627 1627
1628 // No cells should be modified.1628 // No cells should be modified.
1629- for y := 0; y < 3; y++ {1629+ for y := range 3 {
1630- for x := 0; x < 10; x++ {1630+ for x := range 10 {
1631 if c := cellAt(s, x, y); c.Rune != ' ' {1631 if c := cellAt(s, x, y); c.Rune != ' ' {
1632 t.Errorf("cell(%d,%d) = %q, want space", x, y, c.Rune)1632 t.Errorf("cell(%d,%d) = %q, want space", x, y, c.Rune)
1633 }1633 }
@@ -1795,8 +1795,8 @@ func TestCSIScrollUp(t *testing.T) {
1795 s.Write([]byte("\x1b[100S"))1795 s.Write([]byte("\x1b[100S"))
1796 1796
1797 // All rows should be blank.1797 // All rows should be blank.
1798- for y := 0; y < 3; y++ {1798+ for y := range 3 {
1799- for x := 0; x < 5; x++ {1799+ for x := range 5 {
1800 assertCell(t, s, x, y, ' ')1800 assertCell(t, s, x, y, ' ')
1801 }1801 }
1802 }1802 }
@@ -2002,8 +2002,8 @@ func TestC1Controls(t *testing.T) {
2002 s.Write([]byte{0xC2, 0x9F}) // U+009F APC2002 s.Write([]byte{0xC2, 0x9F}) // U+009F APC
2003 2003
2004 // No cells should be modified.2004 // No cells should be modified.
2005- for y := 0; y < 3; y++ {2005+ for y := range 3 {
2006- for x := 0; x < 10; x++ {2006+ for x := range 10 {
2007 if c := cellAt(s, x, y); c.Rune != ' ' {2007 if c := cellAt(s, x, y); c.Rune != ' ' {
2008 t.Errorf("cell(%d,%d) = %q, want space after C1 controls", x, y, c.Rune)2008 t.Errorf("cell(%d,%d) = %q, want space after C1 controls", x, y, c.Rune)
2009 }2009 }
@@ -2025,8 +2025,8 @@ func TestC1Controls(t *testing.T) {
2025 }2025 }
2026 2026
2027 // No cells should be modified.2027 // No cells should be modified.
2028- for y := 0; y < 3; y++ {2028+ for y := range 3 {
2029- for x := 0; x < 10; x++ {2029+ for x := range 10 {
2030 if c := cellAt(s, x, y); c.Rune != ' ' {2030 if c := cellAt(s, x, y); c.Rune != ' ' {
2031 t.Errorf("cell(%d,%d) = %q, want space after C1 range", x, y, c.Rune)2031 t.Errorf("cell(%d,%d) = %q, want space after C1 range", x, y, c.Rune)
2032 }2032 }

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