v0.3.0 cf370a0

window_actions_test.go RawHistoryFolder

1package core
2
3import (
4	"reflect"
5	"slices"
6	"testing"
7)
8
9func TestMoveWindowInColumn(t *testing.T) {
10	for _, o := range []Overflow{OverflowScroll, OverflowFixed} {
11		w := workspace()
12		w.Overflow = o
13		w.Columns = []Column{{Windows: []WindowID{1, 2, 3}, Focus: 1}}
14		w.Apply(ActionMoveWindowUp)
15		if !slices.Equal(w.Columns[0].Windows, []WindowID{2, 1, 3}) || w.Columns[0].Focus != 0 {
16			t.Fatal(o, w.Columns)
17		}
18		// Top end: nothing moves.
19		w.Apply(ActionMoveWindowUp)
20		if !slices.Equal(w.Columns[0].Windows, []WindowID{2, 1, 3}) || w.Columns[0].Focus != 0 {
21			t.Fatal(o, w.Columns)
22		}
23		w.Apply(ActionMoveWindowDown)
24		w.Apply(ActionMoveWindowDown)
25		if !slices.Equal(w.Columns[0].Windows, []WindowID{1, 3, 2}) || w.Columns[0].Focus != 2 {
26			t.Fatal(o, w.Columns)
27		}
28		w.Apply(ActionMoveWindowDown)
29		if id, _ := w.Focused(); id != 2 || w.Columns[0].Focus != 2 {
30			t.Fatal(o, w.Columns)
31		}
32	}
33}
34
35func TestMoveWindowReleasesSlot(t *testing.T) {
36	w := workspace()
37	w.Columns = []Column{{Windows: []WindowID{1, 2}, Slot: 7}}
38	w.Apply(ActionMoveWindowDown)
39	if w.Columns[0].Slot != 0 || !slices.Equal(w.Columns[0].Windows, []WindowID{2, 1}) {
40		t.Fatal(w.Columns)
41	}
42	// A window moving into row 0 takes the place of the slot window: the
43	// slot is released too.
44	w = workspace()
45	w.Columns = []Column{{Windows: []WindowID{1, 2}, Slot: 7, Focus: 1}}
46	w.Apply(ActionMoveWindowUp)
47	if w.Columns[0].Slot != 0 {
48		t.Fatal(w.Columns)
49	}
50	// Rows below keep it.
51	w = workspace()
52	w.Columns = []Column{{Windows: []WindowID{1, 2, 3}, Slot: 7, Focus: 1}}
53	w.Apply(ActionMoveWindowDown)
54	if w.Columns[0].Slot != 7 {
55		t.Fatal(w.Columns)
56	}
57}
58
59func TestMoveWindowOnFloat(t *testing.T) {
60	w := workspace()
61	w.Columns = []Column{{Windows: []WindowID{1, 2}}}
62	w.AddFloating(3, 20, 20)
63	w.Apply(ActionMoveWindowDown)
64	if !slices.Equal(w.Columns[0].Windows, []WindowID{1, 2}) {
65		t.Fatal(w.Columns)
66	}
67}
68
69func TestResizeColumn(t *testing.T) {
70	w := workspace()
71	w.SetOutput(1000, 800)
72	w.SetGaps(10)
73	w.AddWindow(1)
74	w.AddWindow(2)
75	before := w.columnWidth(w.Focus)
76	w.Apply("set-column-width +10%")
77	if w.Columns[w.Focus].Width.Den != 100 || w.columnWidth(w.Focus) <= before {
78		t.Fatal(w.Columns[w.Focus].Width, w.columnWidth(w.Focus), before)
79	}
80	w.Apply("set-column-width -10%")
81	if d := w.columnWidth(w.Focus) - before; d < -1 || d > 1 {
82		t.Fatal(w.columnWidth(w.Focus), before)
83	}
84	for range 20 {
85		w.Apply("set-column-width +10%")
86	}
87	if w.Columns[w.Focus].Width != (Width{Num: 100, Den: 100}) {
88		t.Fatal(w.Columns[w.Focus].Width)
89	}
90	for range 20 {
91		w.Apply("set-column-width -10%")
92	}
93	if w.Columns[w.Focus].Width != (Width{Num: 10, Den: 100}) {
94		t.Fatal(w.Columns[w.Focus].Width)
95	}
96	// A preset snaps to the nearest whole percent once; steps then
97	// round-trip exactly.
98	for _, p := range []Width{{Num: 1, Den: 3}, {Num: 1, Den: 2}, {Num: 2, Den: 3}} {
99		w.Columns[w.Focus].Width = p
100		px := w.columnWidth(w.Focus)
101		w.Apply("set-column-width +7%")
102		w.Apply("set-column-width -7%")
103		snapped := w.columnWidth(w.Focus)
104		if d := snapped - px; d < -w.Usable.W/100 || d > w.Usable.W/100 {
105			t.Fatal(p, snapped, px)
106		}
107		w.Apply("set-column-width +7%")
108		w.Apply("set-column-width -7%")
109		if w.columnWidth(w.Focus) != snapped {
110			t.Fatal(p, w.columnWidth(w.Focus), snapped)
111		}
112	}
113}
114
115func TestResizeColumnUnmaximizesAndCycles(t *testing.T) {
116	w := workspace()
117	w.SetOutput(1000, 800)
118	w.SetPresets([]Width{{Num: 1, Den: 2}, {Num: 1, Den: 1}})
119	w.AddWindow(1)
120	w.ToggleFullWidth()
121	w.Apply("set-column-width -10%")
122	if w.Columns[0].FullWidth {
123		t.Fatal("still maximized")
124	}
125	// A width outside the presets restarts the cycle at the first preset.
126	w.CycleWidth()
127	if w.Columns[0].Width != (Width{Num: 1, Den: 2}) {
128		t.Fatal(w.Columns[0].Width)
129	}
130}
131
132func TestResizeColumnSameValueAsPreset(t *testing.T) {
133	w := workspace()
134	w.SetOutput(1000, 800)
135	w.SetPresets([]Width{{Num: 1, Den: 3}, {Num: 1, Den: 2}, {Num: 1, Den: 1}})
136	w.AddWindow(1)
137	w.Columns[0].Width = Width{Num: 50, Den: 100}
138	w.CycleWidth()
139	if w.Columns[0].Width != (Width{Num: 1, Den: 1}) {
140		t.Fatal(w.Columns[0].Width)
141	}
142}
143
144func TestColumnEditsUnderFullscreen(t *testing.T) {
145	w := workspace()
146	w.SetOutput(1000, 800)
147	w.Columns = []Column{{Windows: []WindowID{1, 2}, Width: Width{Num: 1, Den: 2}}}
148	w.SetFullscreen(1, true)
149	w.Apply("set-column-width -10%")
150	w.Apply(ActionMoveWindowDown)
151	c := w.Columns[0]
152	if c.Width != (Width{Num: 1, Den: 2}) || !slices.Equal(c.Windows, []WindowID{1, 2}) {
153		t.Fatal(c)
154	}
155}
156
157func TestResizeColumnFixedNoop(t *testing.T) {
158	w := workspace()
159	w.Overflow = OverflowFixed
160	w.AddWindow(1)
161	w.Apply("set-column-width +10%")
162	if w.Columns[0].Width != (Width{}) {
163		t.Fatal(w.Columns[0].Width)
164	}
165}
166
167func TestMoveWorkspace(t *testing.T) {
168	m := monitor()
169	m.AddWindow(1)
170	m.FocusNumber(2)
171	m.AddWindow(2)
172	m.FocusNumber(3)
173	m.AddWindow(3)
174	// [1:{1}] [2:{2}] [3:{3}] [4:{}]
175	m.Apply(ActionMoveWorkspaceUp)
176	if !reflect.DeepEqual(windows(m), [][]WindowID{{1}, {3}, {2}, {}}) || m.Active != 1 {
177		t.Fatal(windows(m), m.Active)
178	}
179	m.Apply(ActionMoveWorkspaceUp)
180	m.Apply(ActionMoveWorkspaceUp)
181	if !reflect.DeepEqual(windows(m), [][]WindowID{{3}, {1}, {2}, {}}) || m.Active != 0 {
182		t.Fatal(windows(m), m.Active)
183	}
184	m.Apply(ActionMoveWorkspaceDown)
185	m.Apply(ActionMoveWorkspaceDown)
186	// Never past the trailing empty workspace.
187	m.Apply(ActionMoveWorkspaceDown)
188	if !reflect.DeepEqual(windows(m), [][]WindowID{{1}, {2}, {3}, {}}) || m.Active != 2 {
189		t.Fatal(windows(m), m.Active)
190	}
191	// The trailing empty workspace does not move.
192	m.FocusNumber(4)
193	m.Apply(ActionMoveWorkspaceUp)
194	if !reflect.DeepEqual(windows(m), [][]WindowID{{1}, {2}, {3}, {}}) || m.Active != 3 {
195		t.Fatal(windows(m), m.Active)
196	}
197}
198
199func TestMoveWorkspaceHidden(t *testing.T) {
200	m := named(monitor(), NamedWorkspace{Name: "dev"})
201	m.AddWindow(1)
202	m.FocusNumber(2)
203	m.AddWindow(2)
204	m.ToggleNamed("dev")
205	m.Apply(ActionMoveWorkspaceUp)
206	if !reflect.DeepEqual(windows(m), [][]WindowID{{1}, {2}, {}}) {
207		t.Fatal(windows(m))
208	}
209}

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 25

Tags 10

↑↓ moveenter openesc close