v0.7.2 ab6686c

screen_autowrap_test.go RawHistoryFolder

1package vt
2
3import (
4	"bytes"
5	"testing"
6
7	renderer "github.com/bnema/vev-vt/core"
8)
9
10func TestScreenAutoWrapModeRightMargin(t *testing.T) {
11	tests := []struct {
12		name        string
13		width       int
14		height      int
15		input       string
16		wantRows    []string
17		wantRow     int
18		wantCol     int
19		wantHistory int
20	}{
21		{
22			name:  "successive writes overwrite right margin",
23			width: 4, height: 2, input: "\x1b[?7labcde",
24			wantRows: []string{"abce", "    "}, wantRow: 0, wantCol: 3,
25		},
26		{
27			name:  "bottom row does not scroll",
28			width: 3, height: 2, input: "top\r\n\x1b[?7labcd",
29			wantRows: []string{"top", "abd"}, wantRow: 1, wantCol: 2,
30		},
31		{
32			name:  "width one overwrites",
33			width: 1, height: 2, input: "\x1b[?7lab",
34			wantRows: []string{"b", " "}, wantRow: 0, wantCol: 0,
35		},
36		{
37			name:  "vev raw-mode initialization disables wrapping",
38			width: 4, height: 2, input: "\x1b[?7lstatus",
39			wantRows: []string{"stas", "    "}, wantRow: 0, wantCol: 3,
40		},
41	}
42
43	for _, test := range tests {
44		t.Run(test.name, func(t *testing.T) {
45			s := NewScreenWithHistory(test.width, test.height, HistoryConfig{MaxRows: 10})
46			s.Write([]byte(test.input))
47			for row, want := range test.wantRows {
48				if got := screenRowText(s, row); got != want {
49					t.Fatalf("row %d = %q, want %q", row, got, want)
50				}
51			}
52			if s.Row != test.wantRow || s.Col != test.wantCol {
53				t.Fatalf("cursor = (%d,%d), want (%d,%d)", s.Row, s.Col, test.wantRow, test.wantCol)
54			}
55			if got := s.History().Len(); got != test.wantHistory {
56				t.Fatalf("history length = %d, want %d", got, test.wantHistory)
57			}
58			if s.buffer.boundaries[0].Soft {
59				t.Fatal("disabled autowrap created a soft wrap")
60			}
61		})
62	}
63}
64
65func TestScreenAutoWrapWideRuneAtRightMargin(t *testing.T) {
66	s := NewScreen(4, 2)
67	s.Write([]byte("abc\x1b[?7l界"))
68
69	if got := s.Cell(3, 0); got.Rune != '\uFFFD' || got.Continuation {
70		t.Fatalf("right-margin cell = %#v, want narrow replacement", got)
71	}
72	for x := 0; x < s.Columns(); x++ {
73		if s.Cell(x, 0).Continuation && (x == 0 || renderer.RuneWidth(s.Cell(x-1, 0).Rune) != 2) {
74			t.Fatalf("orphaned continuation at column %d", x)
75		}
76	}
77	if s.Row != 0 || s.Col != 3 {
78		t.Fatalf("cursor = (%d,%d), want (0,3)", s.Row, s.Col)
79	}
80}
81
82func TestScreenAutoWrapModeTransitionsAndResize(t *testing.T) {
83	s := NewScreen(4, 2)
84	s.Write([]byte("abcd")) // Leave a deferred wrap pending.
85	s.Write([]byte("\x1b[?7le"))
86	if got := screenRowText(s, 0); got != "abce" {
87		t.Fatalf("row after disabling pending wrap = %q, want %q", got, "abce")
88	}
89
90	s.Resize(3, 2)
91	if s.Row != 0 || s.Col != 2 {
92		t.Fatalf("cursor after resize = (%d,%d), want (0,2)", s.Row, s.Col)
93	}
94	s.Write([]byte("f\x1b[?7hgh"))
95	if s.Row != 1 || s.Col != 1 {
96		t.Fatalf("cursor after re-enabling wrap = (%d,%d), want (1,1)", s.Row, s.Col)
97	}
98	if got := screenRowText(s, 1); got != "h  " {
99		t.Fatalf("wrapped row = %q, want %q", got, "h  ")
100	}
101}
102
103func TestScreenAutoWrapSavedAndReset(t *testing.T) {
104	s := NewScreen(4, 2)
105	s.Write([]byte("\x1b[?7l\x1b7\x1b[?7h\x1b8"))
106	if s.AutoWrapMode() {
107		t.Fatal("ESC 8 did not restore disabled autowrap mode")
108	}
109
110	s.Write([]byte("\x1bc"))
111	if !s.AutoWrapMode() {
112		t.Fatal("RIS did not restore enabled autowrap default")
113	}
114}
115
116func TestScreenApplicationCursorMode(t *testing.T) {
117	s := NewScreen(4, 2)
118	if s.ApplicationCursorMode() {
119		t.Fatal("application cursor mode enabled by default")
120	}
121	s.Write([]byte("\x1b[?"))
122	s.Write([]byte("1h"))
123	if !s.ApplicationCursorMode() || !s.Snapshot().ApplicationCursorMode() {
124		t.Fatal("split DECCKM enable was not tracked in screen and snapshot")
125	}
126	s.Write([]byte("\x1b[?1l"))
127	if s.ApplicationCursorMode() {
128		t.Fatal("DECCKM disable was not tracked")
129	}
130	s.Write([]byte("\x1b[?1h\x1bc"))
131	if s.ApplicationCursorMode() {
132		t.Fatal("RIS did not reset DECCKM")
133	}
134}
135
136func TestScreenCursorAndAutoWrapModeQueries(t *testing.T) {
137	s := NewScreen(4, 2)
138	var got bytes.Buffer
139	s.OnResponse = func(response []byte) { got.Write(response) }
140	s.Write([]byte("\x1b[?1$p\x1b[?7$p\x1b[?1h\x1b[?7l\x1b[?1$p\x1b[?7$p"))
141	want := "\x1b[?1;2$y\x1b[?7;1$y\x1b[?1;1$y\x1b[?7;2$y"
142	if got.String() != want {
143		t.Fatalf("mode reports = %q, want %q", got.String(), want)
144	}
145}
146
147func screenRowText(s *Screen, row int) string {
148	runes := make([]rune, s.Columns())
149	for col := range s.Columns() {
150		r := s.Cell(col, row).Rune
151		if r == 0 {
152			r = ' '
153		}
154		runes[col] = r
155	}
156	return string(runes)
157}

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