v0.7.2 ab6686c

history_codec_records.go RawHistoryFolder

1package vt
2
3import (
4	"encoding/binary"
5	"math"
6	"unicode/utf8"
7
8	renderer "github.com/bnema/vev-vt/core"
9)
10
11func appendHistoryStyle(dst []byte, style renderer.Style) []byte {
12	style = style.Canonical()
13	flags := byte(0)
14	for i, set := range [...]bool{style.Bold, style.Italic, style.Inverse, style.HasForegroundRGB, style.HasBackgroundRGB, style.HasUnderlineColor, style.HasUnderlineColorRGB} {
15		if set {
16			flags |= 1 << i
17		}
18	}
19	dst = append(dst, flags)
20	dst = binary.BigEndian.AppendUint16(dst, uint16(style.Attrs))
21	dst = binary.BigEndian.AppendUint64(dst, uint64(int64(style.Foreground)))
22	dst = binary.BigEndian.AppendUint64(dst, uint64(int64(style.Background)))
23	dst = append(dst, style.ForegroundRGB.R, style.ForegroundRGB.G, style.ForegroundRGB.B)
24	dst = append(dst, style.BackgroundRGB.R, style.BackgroundRGB.G, style.BackgroundRGB.B)
25	dst = append(dst, byte(style.UnderlineStyle))
26	dst = binary.BigEndian.AppendUint64(dst, uint64(int64(style.UnderlineColor)))
27	return append(dst, style.UnderlineColorRGB.R, style.UnderlineColorRGB.G, style.UnderlineColorRGB.B)
28}
29
30func appendHistoryBound(dst []byte, bound LineBound) []byte {
31	dst = binary.BigEndian.AppendUint32(dst, uint32(bound.End))
32	if bound.Soft {
33		return append(dst, 1)
34	}
35	return append(dst, 0)
36}
37
38type historyParser struct{ data []byte }
39
40func (p *historyParser) uint32() (uint32, bool) {
41	if len(p.data) < 4 {
42		return 0, false
43	}
44	value := binary.BigEndian.Uint32(p.data)
45	p.data = p.data[4:]
46	return value, true
47}
48
49func (p *historyParser) uint64() (uint64, bool) {
50	if len(p.data) < 8 {
51		return 0, false
52	}
53	value := binary.BigEndian.Uint64(p.data)
54	p.data = p.data[8:]
55	return value, true
56}
57
58func (p *historyParser) style() (renderer.Style, bool) {
59	if len(p.data) < historyStyleBytes {
60		return renderer.Style{}, false
61	}
62	b := p.data[:historyStyleBytes]
63	p.data = p.data[historyStyleBytes:]
64	flags := b[0]
65	if flags&0x80 != 0 {
66		return renderer.Style{}, false
67	}
68	foreground, fok := historyInt(binary.BigEndian.Uint64(b[3:11]))
69	background, bok := historyInt(binary.BigEndian.Uint64(b[11:19]))
70	underline, uok := historyInt(binary.BigEndian.Uint64(b[26:34]))
71	if !fok || !bok || !uok {
72		return renderer.Style{}, false
73	}
74	return renderer.Style{
75		Bold:                 flags&1 != 0,
76		Italic:               flags&2 != 0,
77		Inverse:              flags&4 != 0,
78		Attrs:                renderer.StyleAttrs(binary.BigEndian.Uint16(b[1:3])),
79		Foreground:           foreground,
80		Background:           background,
81		HasForegroundRGB:     flags&8 != 0,
82		ForegroundRGB:        renderer.RGB{R: b[19], G: b[20], B: b[21]},
83		HasBackgroundRGB:     flags&16 != 0,
84		BackgroundRGB:        renderer.RGB{R: b[22], G: b[23], B: b[24]},
85		UnderlineStyle:       renderer.UnderlineStyle(b[25]),
86		HasUnderlineColor:    flags&32 != 0,
87		UnderlineColor:       underline,
88		HasUnderlineColorRGB: flags&64 != 0,
89		UnderlineColorRGB:    renderer.RGB{R: b[34], G: b[35], B: b[36]},
90	}, true
91}
92
93func (p *historyParser) bound() (LineBound, bool) {
94	if len(p.data) < historyBoundBytes {
95		return LineBound{}, false
96	}
97	end := binary.BigEndian.Uint32(p.data[:4])
98	flag := p.data[4]
99	p.data = p.data[historyBoundBytes:]
100	if flag > 1 || uint64(end) > uint64(math.MaxInt) {
101		return LineBound{}, false
102	}
103	return LineBound{End: int(end), Soft: flag == 1}, true
104}
105
106func (p *historyParser) storedCell(styles, payloads uint32) (rune, uint32, uint32, bool, bool) {
107	if len(p.data) < historyStoredCellBytes {
108		return 0, 0, 0, false, false
109	}
110	b := p.data[:historyStoredCellBytes]
111	p.data = p.data[historyStoredCellBytes:]
112	r := rune(binary.BigEndian.Uint32(b[:4]))
113	id := binary.BigEndian.Uint32(b[4:8])
114	if !utf8.ValidRune(r) || id >= styles || b[8] > 3 {
115		return 0, 0, 0, false, false
116	}
117	var payloadID uint32
118	if b[8]&2 != 0 {
119		var ok bool
120		payloadID, ok = p.uint32()
121		if !ok || payloadID == 0 || payloadID > payloads {
122			return 0, 0, 0, false, false
123		}
124	}
125	return r, id, payloadID, b[8]&1 != 0, true
126}
127
128func historyInt(raw uint64) (int, bool) {
129	value := int64(raw)
130	return int(value), int64(int(value)) == value
131}
132
133func validHistoryCell(cell renderer.Cell) bool {
134	return utf8.ValidRune(cell.Rune) && cell.Style.UnderlineStyle <= renderer.UnderlineDashed
135}

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