v0.1.0 af61ae3

scroll.go RawHistoryFolder

1package ansi
2
3import (
4	"bytes"
5	"strconv"
6)
7
8func findSafeScroll(frame Frame, damage []Damage) (Damage, bool) {
9	for _, d := range damage {
10		if d.Kind == DamageScrollUp && isSafeScroll(frame, d) {
11			return d, true
12		}
13	}
14	return Damage{}, false
15}
16
17func isSafeScroll(frame Frame, d Damage) bool {
18	return d.X == 0 && d.Width == frame.Width && d.Y >= 0 && d.Y <= frame.Height && d.Height > 0 && d.Height <= frame.Height-d.Y && d.Count > 0 && d.Count < d.Height
19}
20
21func emitScrollUp(out *bytes.Buffer, d Damage) {
22	out.WriteString("\x1b[0m")
23	// \x1b[%d;%dr — set scroll region
24	out.WriteString("\x1b[")
25	var b [16]byte
26	n := strconv.AppendInt(b[:0], int64(d.Y+1), 10)
27	out.Write(n)
28	out.WriteByte(';')
29	n = strconv.AppendInt(b[:0], int64(d.Y+d.Height), 10)
30	out.Write(n)
31	out.WriteByte('r')
32	// Position cursor at bottom of scroll region.
33	writeCursor(out, d.Y+d.Height-1, 0)
34	if d.Count == 1 {
35		out.WriteByte('\n')
36	} else {
37		out.WriteString("\x1b[")
38		n = strconv.AppendInt(b[:0], int64(d.Count), 10)
39		out.Write(n)
40		out.WriteByte('S')
41	}
42	out.WriteString("\x1b[r")
43}
44
45func canApplyScrollAgainst(frame Frame, scroll Damage, damage []Damage, committed Frame) bool {
46	for y := scroll.Y; y < scroll.Y+scroll.Height-scroll.Count; y++ {
47		frameRow := frame.Row(y)
48		committedRow := committed.Row(y + scroll.Count)
49		for x := range scroll.Width {
50			column := scroll.X + x
51			committedCell, frameCell := committedRow[column], frameRow[column]
52			if committedCell == frameCell || committedCell.Equal(frameCell) {
53				continue
54			}
55			if !damageCoversCell(damage, column, y) {
56				return false
57			}
58		}
59	}
60	return true
61}
62
63func damageCoversCell(damage []Damage, x, y int) bool {
64	for _, d := range damage {
65		if (d.Kind == DamageText || d.Kind == DamageClear) && x >= d.X && x < d.X+d.Width && y >= d.Y && y < d.Y+d.Height {
66			return true
67		}
68	}
69	return false
70}

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