v0.3.0 52147d7

screen_scroll_test.go RawHistoryFolder

1package vt
2
3import (
4	"testing"
5)
6
7// TestWideCharSurvivesRotatedScroll proves a wide-character pair travels intact
8// with its line when a full-width scroll rotates line offsets rather than
9// copying cells.
10func TestWideCharSurvivesRotatedScroll(t *testing.T) {
11	tests := []struct {
12		name string
13		run  func(t *testing.T)
14	}{
15		{
16			name: "wide pair moves up one row on newline scroll",
17			run: func(t *testing.T) {
18				s := NewScreen(4, 3)
19				// Place a wide rune (世, width 2) on the bottom row, then scroll up.
20				s.Write([]byte("\x1b[3;1H世"))
21				assertCell(t, s, 0, 2, '世')
22				assertContinuation(t, s, 1, 2)
23				// Newline at the bottom row scrolls the region up by one.
24				s.Write([]byte("\n"))
25				// The wide pair is now on row 1, intact; bottom row blank.
26				assertCell(t, s, 0, 1, '世')
27				assertContinuation(t, s, 1, 1)
28				assertBlank(t, s, 0, 2)
29				assertBlank(t, s, 1, 2)
30				if err := s.Frame.CheckInvariants(); err != nil {
31					t.Fatalf("invariants: %v", err)
32				}
33			},
34		},
35		{
36			name: "wide pair moves down on reverse index",
37			run: func(t *testing.T) {
38				s := NewScreen(4, 3)
39				s.Write([]byte("\x1b[1;1H世")) // top row
40				assertCell(t, s, 0, 0, '世')
41				assertContinuation(t, s, 1, 0)
42				s.Write([]byte("\x1b[1;1H\x1bM")) // cursor top, reverse index -> scroll down
43				assertCell(t, s, 0, 1, '世')
44				assertContinuation(t, s, 1, 1)
45				assertBlank(t, s, 0, 0)
46				if err := s.Frame.CheckInvariants(); err != nil {
47					t.Fatalf("invariants: %v", err)
48				}
49			},
50		},
51	}
52	for _, tt := range tests {
53		t.Run(tt.name, tt.run)
54	}
55}
56
57// TestLineOffsetInvariantAfterVTSequences drives interleaved writes and scrolls
58// through the VT layer and asserts the frame's line-offset stays a valid
59// permutation.
60func TestLineOffsetInvariantAfterVTSequences(t *testing.T) {
61	s := NewScreen(10, 6)
62	seqs := [][]byte{
63		[]byte("line0\r\nline1\r\nline2\r\nline3\r\nline4\r\nline5"),
64		[]byte("\x1b[2;5r"),       // scroll region rows 2..5
65		[]byte("\x1b[5;1Hmore\n"), // scroll within region
66		[]byte("\x1b[1;1H\x1bM"),  // reverse index at top
67		[]byte("\x1b[3S"),         // scroll up 3
68		[]byte("\x1b[2T"),         // scroll down 2
69		[]byte("\x1b[r"),          // reset region
70		[]byte("\x1b[6;1Hbottom\n"),
71	}
72	for i, seq := range seqs {
73		s.Write(seq)
74		s.ClearDamage()
75		if err := s.Frame.CheckInvariants(); err != nil {
76			t.Fatalf("after seq %d: invariants broken: %v", i, err)
77		}
78	}
79}

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 2

Tags 13

↑↓ moveenter openesc close