spans_test.go RawHistoryFolder

1package core
2
3import "testing"
4
5func spanWorkspace(mode Overflow, n int) *Workspace {
6	w := workspace()
7	w.Overflow = mode
8	w.MaxColumns = 3
9	w.SetOutput(1000, 800)
10	w.SetGaps(10)
11	for i := 0; i < n; i++ {
12		w.AddWindow(WindowID(i + 1))
13	}
14	w.View = 0
15	w.view = slide{}
16	return w
17}
18
19func stampSpans(w *Workspace, first, last int) {
20	w.spanSeq++
21	for i := first; i <= last; i++ {
22		w.Columns[i].Span = 10000 / (last - first + 1)
23		w.Columns[i].spanStamp = w.spanSeq
24	}
25	w.Columns[last].Span += 10000 % (last - first + 1)
26}
27
28func TestSpansMutations(t *testing.T) {
29	for _, mode := range []Overflow{OverflowFixed, OverflowCascade, OverflowScroll} {
30		for _, mutation := range []string{"add", "remove", "take", "receive", "swap", "grow", "shrink", "action"} {
31			t.Run(string(mode)+"/"+mutation, func(t *testing.T) {
32				w := spanWorkspace(mode, 2)
33				stampSpans(w, 0, 1)
34				switch mutation {
35				case "add":
36					w.AddWindow(3)
37				case "remove":
38					w.RemoveWindow(1)
39				case "take":
40					col, ok := w.takeColumn()
41					if !ok {
42						t.Fatal("take")
43					}
44					other := spanWorkspace(mode, 1)
45					other.receive(col, -1)
46					for _, c := range other.Columns {
47						if c.Span != 0 {
48							t.Fatal("destination kept stale span")
49						}
50					}
51				case "receive":
52					w.receive(Column{Windows: []WindowID{3}}, 1)
53				case "swap":
54					w.Focus = 0
55					w.MoveColumn(1)
56				case "grow":
57					w.GrowColumn(false)
58				case "shrink":
59					w.ShrinkColumn()
60				case "action":
61					w.Apply("set-column-width +1")
62				}
63				for _, c := range w.Columns {
64					if mutation == "swap" {
65						if c.Span == 0 {
66							t.Fatal("swap discarded valid spans")
67						}
68					} else if c.Span != 0 || c.spanStamp != 0 {
69						t.Fatal("stale span", c)
70					}
71				}
72			})
73		}
74	}
75}
76
77func TestSpansCascadeIsolation(t *testing.T) {
78	w := spanWorkspace(OverflowCascade, 5)
79	stampSpans(w, 0, 2)
80	stampSpans(w, 3, 4)
81	w.RemoveWindow(w.Columns[0].Windows[0])
82	if w.Columns[0].Span != 0 || w.Columns[1].Span != 0 {
83		t.Fatal("changed band retained spans")
84	}
85	if w.Columns[2].Span != 5000 || w.Columns[3].Span != 5000 {
86		t.Fatal("unrelated band reset")
87	}
88}
89
90func TestSpanWidths(t *testing.T) {
91	for _, mode := range []Overflow{OverflowFixed, OverflowCascade, OverflowScroll} {
92		w := spanWorkspace(mode, 2)
93		stampSpans(w, 0, 1)
94		w.Columns[0].Span = 3000
95		w.Columns[1].Span = 7000
96		rs := w.columnRects()
97		avail := 970
98		if mode == OverflowScroll {
99			avail = 980
100		}
101		if rs[0].W != avail*3/10 {
102			t.Fatal(mode, rs)
103		}
104		w.SetOutput(1400, 800)
105		if w.columnRects()[0].W != (avail+400)*3/10 {
106			t.Fatal("ratio lost", mode, w.columnRects())
107		}
108		w.Columns[0].FullWidth = true
109		if w.columnWidth(0) != 1380 {
110			t.Fatal("special width lost")
111		}
112	}
113	w := spanWorkspace(OverflowFixed, 4)
114	stampSpans(w, 0, 3)
115	w.scroll()
116	for _, c := range w.Columns {
117		if c.Span != 0 {
118			t.Fatal("spiral spans")
119		}
120	}
121}
122
123func TestSpansDropIntoLane(t *testing.T) {
124	for _, mode := range []Overflow{OverflowFixed, OverflowCascade, OverflowScroll} {
125		w := spanWorkspace(mode, 2)
126		stampSpans(w, 0, 1)
127		src := spanWorkspace(mode, 1)
128		src.Columns[0].Windows[0] = 9
129		m := monitor()
130		m.Workspaces = []*Workspace{w, src}
131		sc := &screen{mon: m}
132		c := &Core{screens: []*screen{sc}}
133		c.drop(9, dropTarget{kind: dropInsert, screen: sc, ws: w, anchor: w.Columns[1].Windows[0]})
134		if len(w.Columns) != 3 {
135			t.Fatal("not dropped", mode, w.Columns)
136		}
137		for _, col := range w.Columns {
138			if col.Span != 0 || col.spanStamp != 0 {
139				t.Fatal("drop retained stamp", mode, col)
140			}
141		}
142	}
143}
144
145func TestSpanLaneIdentity(t *testing.T) {
146	for _, action := range []string{"cross-drop", "within-drop", "within-keyboard"} {
147		t.Run(action, func(t *testing.T) {
148			w := spanWorkspace(OverflowCascade, 4)
149			w.SetMaxColumns(2)
150			for i := range w.Columns {
151				w.Columns[i].BandStart = i%2 == 0
152			}
153			for _, i := range []int{0, 2} {
154				rs := w.columnRects()
155				w.setBorder(border{kind: borderColumn, col: i}, rs[i].X+rs[i].W+5)
156			}
157			for _, col := range w.Columns {
158				if col.Span != 5000 {
159					t.Fatal("not stamped equally", w.Columns)
160				}
161			}
162			cross := action == "cross-drop"
163			if action == "cross-drop" || action == "within-drop" {
164				m := monitor()
165				m.Workspaces = []*Workspace{w}
166				sc := &screen{mon: m}
167				c := &Core{screens: []*screen{sc}}
168				to := 1
169				if cross {
170					to = 2
171				}
172				c.drop(w.Columns[0].Windows[0], dropTarget{kind: dropSwap, screen: sc, ws: w, anchor: w.Columns[to].Windows[0]})
173			} else {
174				w.Focus = 0
175				w.MoveColumn(1)
176			}
177			w.dropStaleSpans()
178			for _, col := range w.Columns {
179				if cross && col.Span != 0 {
180					t.Fatal("cross-band swap retained stamp", w.Columns)
181				}
182				if !cross && col.Span != 5000 {
183					t.Fatal("within-band swap discarded stamp", w.Columns)
184				}
185			}
186		})
187	}
188}
189
190// A column arriving from another workspace carries that workspace's stamp,
191// which may collide with a stamp issued here.
192func TestSpansForeignStampCleared(t *testing.T) {
193	w := spanWorkspace(OverflowScroll, 2)
194	stampSpans(w, 0, 1)
195	stamp := w.spanSeq
196	w.receive(Column{Windows: []WindowID{9}, Span: 3000, spanStamp: stamp}, -1)
197	w.dropStaleSpans()
198	for _, col := range w.Columns {
199		if col.Span != 0 || col.spanStamp != 0 {
200			t.Fatal("foreign stamp joined the lane", w.Columns)
201		}
202	}
203}

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 26

Tags 10

↑↓ moveenter openesc close