v0.7.0 36c429d

recovery_transcript.go RawHistoryFolder

1package vt
2
3import renderer "github.com/bnema/vev-vt/core"
4
5// RecoveryTranscriptSnapshot owns compact viewport pages to replay after
6// retained history. No semantic row slices are retained by the snapshot.
7type RecoveryTranscriptSnapshot struct {
8	segments  []recoveryTranscriptSegment
9	nextRowID RowID
10}
11
12type recoveryTranscriptSegment struct {
13	frame  renderer.Frame
14	bounds []LineBound
15	rowIDs []RowID
16}
17
18// RecoveryTranscriptSnapshot captures the primary viewport, followed by the
19// alternate viewport when active. Untouched trailing rows are omitted and each
20// viewport ends with a hard line, independently of physical row rotation.
21func (s *Screen) RecoveryTranscriptSnapshot() RecoveryTranscriptSnapshot {
22	if s == nil {
23		return RecoveryTranscriptSnapshot{}
24	}
25	buffers := []*buffer{s.buffer}
26	if s.alternate != nil {
27		buffers = []*buffer{s.alternate.buffer, s.buffer}
28	}
29	nextID := s.nextRowID
30	if nextID < ^RowID(0) {
31		nextID++
32	}
33	snapshot := RecoveryTranscriptSnapshot{nextRowID: nextID, segments: make([]recoveryTranscriptSegment, 0, len(buffers))}
34	for _, b := range buffers {
35		if b == nil {
36			continue
37		}
38		rows := b.frame.Height
39		for rows > 0 && recoveryTranscriptFrameRowUntouched(b.frame, rows-1, b.bound(rows-1)) {
40			rows--
41		}
42		if rows == 0 {
43			continue
44		}
45		frame := renderer.NewFrame(b.frame.Width, rows)
46		for y := range rows {
47			for x := range frame.Width {
48				frame.Set(x, y, b.frame.Cell(x, y))
49			}
50		}
51		bounds := append([]LineBound(nil), b.boundaries[:rows]...)
52		bounds[rows-1].Soft = false
53		snapshot.segments = append(snapshot.segments, recoveryTranscriptSegment{
54			frame: frame, bounds: bounds, rowIDs: append([]RowID(nil), b.rowIDs[:rows]...),
55		})
56	}
57	return snapshot
58}
59
60func recoveryTranscriptFrameRowUntouched(frame renderer.Frame, y int, bound LineBound) bool {
61	if bound.End != 0 || bound.Soft {
62		return false
63	}
64	blank := renderer.BlankCell()
65	for x := range frame.Width {
66		if !frame.Cell(x, y).Equal(blank) {
67			return false
68		}
69	}
70	return true
71}
72
73// Marshal encodes the compact capture without consulting or mutating live state.
74func (snapshot RecoveryTranscriptSnapshot) Marshal() ([]byte, error) {
75	view := HistoryView{nextRowID: snapshot.nextRowID}
76	for _, segment := range snapshot.segments {
77		for start := 0; start < segment.frame.Height; {
78			count := min(segment.frame.Height-start, maxHistoryChunkRows, maxHistorySlabRows(segment.frame.Width))
79			frame := renderer.NewFrame(segment.frame.Width, count)
80			lastStyleRow := make(map[renderer.Style]int)
81			for y := range count {
82				for x := range frame.Width {
83					cell := segment.frame.Cell(x, start+y)
84					frame.Set(x, y, cell)
85					style := cell.Style.Canonical()
86					if style != renderer.DefaultStyle() {
87						lastStyleRow[style] = y
88					}
89				}
90			}
91			drops := make([]uint64, count)
92			for _, y := range lastStyleRow {
93				drops[y]++
94			}
95			chunk := &HistoryChunk{
96				page: newSealedPage(frame), count: count, width: frame.Width,
97				bounds: segment.bounds[start : start+count], rowIDs: segment.rowIDs[start : start+count],
98				styleDrops: drops, styleCount: uint64(len(lastStyleRow)) + 1,
99			}
100			chunk.recordPayloads()
101			view.chunks = append(view.chunks, chunk)
102			view.rows += count
103			view.cells += count * frame.Width
104			view.logicalBytes += historyChunkLogicalBytes(chunk)
105			start += count
106		}
107	}
108	return MarshalHistory(view)
109}

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