v0.6.0 b250c5e

replay_property_test.go RawHistoryFolder

1package ansi_test
2
3import (
4	"fmt"
5	"math/rand/v2"
6	"strings"
7	"testing"
8
9	vt "github.com/bnema/vev-vt"
10	"github.com/bnema/vev-vt/ansi"
11	core "github.com/bnema/vev-vt/core"
12)
13
14type hiddenCellSource struct{ core.CellSource }
15
16// Random VT scroll workloads rendered with real screen damage must replay to
17// the exact source grid for every CellSource shape the renderer dispatches on.
18func TestRandomScrollWorkloadsReplayExactly(t *testing.T) {
19	seeds := 400
20	if testing.Short() {
21		seeds = 60
22	}
23	for _, kind := range []string{"screen", "frame", "pointer", "hidden"} {
24		t.Run(kind, func(t *testing.T) {
25			for seed := range seeds {
26				runReplayWorkload(t, kind, uint64(seed))
27			}
28		})
29	}
30}
31
32func runReplayWorkload(t *testing.T, kind string, seed uint64) {
33	t.Helper()
34	r := rand.New(rand.NewPCG(seed, 0x5c4011))
35	cols, rows := 4+r.IntN(14), 3+r.IntN(10)
36	s := vt.NewScreen(cols, rows)
37	renderer := ansi.New(ansi.Capabilities{})
38	replay := vt.NewScreen(cols, rows)
39	mirror := core.NewFrame(cols, rows)
40	for step := range 30 {
41		input := randomScrollInput(r, cols, rows)
42		s.Write(input)
43		var source core.CellSource = s
44		if kind != "screen" {
45			for y := range rows {
46				for x := range cols {
47					mirror.Set(x, y, s.Cell(x, y))
48				}
49			}
50			switch kind {
51			case "frame":
52				source = mirror
53			case "pointer":
54				source = &mirror
55			default:
56				source = hiddenCellSource{mirror}
57			}
58		}
59		prepared, err := renderer.Prepare(source, s.Damage(), false)
60		if err != nil {
61			t.Fatal(err)
62		}
63		prepared.Commit()
64		replay.Write(prepared.Bytes())
65		s.ClearDamage()
66		for y := range rows {
67			for x := range cols {
68				want, got := s.Cell(x, y), replay.Cell(x, y)
69				if want.Rune != got.Rune || want.Continuation != got.Continuation || !want.Style.Equal(got.Style) {
70					t.Fatalf("seed %d step %d input %q: cell (%d,%d) = %+v, want %+v", seed, step, input, x, y, got, want)
71				}
72			}
73		}
74	}
75}
76
77func randomScrollInput(r *rand.Rand, cols, rows int) []byte {
78	var b strings.Builder
79	for range 1 + r.IntN(12) {
80		switch r.IntN(14) {
81		case 0:
82			top := 1 + r.IntN(rows)
83			fmt.Fprintf(&b, "\x1b[%d;%dr", top, top+r.IntN(rows-top+1))
84		case 1:
85			b.WriteString("\x1b[r")
86		case 2:
87			b.WriteString("\x1bD")
88		case 3:
89			b.WriteString("\x1bM")
90		case 4:
91			fmt.Fprintf(&b, "\x1b[%dS", 1+r.IntN(3))
92		case 5:
93			fmt.Fprintf(&b, "\x1b[%dT", 1+r.IntN(3))
94		case 6:
95			fmt.Fprintf(&b, "\x1b[%dL", 1+r.IntN(3))
96		case 7:
97			fmt.Fprintf(&b, "\x1b[%dM", 1+r.IntN(3))
98		case 8:
99			b.WriteString("\r\n")
100		case 9:
101			b.WriteString([]string{"\x1b[K", "\x1b[1K", "\x1b[2K", "\x1b[J"}[r.IntN(4)])
102		case 10:
103			fmt.Fprintf(&b, "\x1b[%dm", []int{0, 1, 7, 31, 42}[r.IntN(5)])
104		case 11:
105			fmt.Fprintf(&b, "\x1b[%d;%dH", 1+r.IntN(rows), 1+r.IntN(cols))
106		case 12:
107			b.WriteString("界")
108		default:
109			for range 1 + r.IntN(cols) {
110				b.WriteByte(byte('a' + r.IntN(26)))
111			}
112		}
113	}
114	return []byte(b.String())
115}

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