v0.2.0 13c6a90

expel.go RawHistoryFolder

1package core
2
3import "slices"
4
5// Consume or expel (ADR 016): Cmd+[ and Cmd+] move the focused tiled window
6// one column left or right. Alone in its column it joins the bottom of the
7// neighbor column (consume); in a stacked column it leaves for a new column
8// (expel). A full fixed workspace never gets a new column: the window stacks
9// into the neighbor column instead, past the edge on the neighbor monitor.
10//
11// A slot column's slot window is its first window: windows only join a
12// column at the bottom, so the slot follows Windows[0].
13
14// full reports whether a fixed-overflow workspace has no room for a column.
15func (w *Workspace) full() bool {
16	return w.Overflow == OverflowFixed && len(w.Columns) >= max(w.MaxColumns, 1)
17}
18
19// ConsumeOrExpel moves the focused window in direction dir (-1 or 1). It
20// returns false when the window must leave for the neighbor monitor: a full
21// workspace with the window stacked in the edge column.
22func (w *Workspace) ConsumeOrExpel(dir int) bool {
23	if len(w.Columns) == 0 || w.onFloat() || (dir != -1 && dir != 1) {
24		return true
25	}
26	// A fullscreen window hides the others (fixed) or its column (scroll):
27	// moving would break its way back.
28	if w.fullscreenHides() {
29		return true
30	}
31	src := w.Focus
32	col := w.Columns[src]
33	id := col.Windows[col.Focus]
34	next := src + dir
35	edge := next < 0 || next >= len(w.Columns)
36	switch {
37	case len(col.Windows) == 1 && edge:
38		// Consume at the edge: move-column already crosses monitors.
39	case len(col.Windows) == 1:
40		// The column goes away, and a slot with it.
41		w.stackWindow(id, w.Columns[next].Windows[0])
42	case !w.full():
43		c := Column{Windows: []WindowID{id}, Width: col.Width}
44		if col.Focus == 0 && col.Slot != 0 {
45			// The slot and its width follow the slot window.
46			c.Slot = col.Slot
47			w.Columns[src].Slot, w.Columns[src].Width = 0, Width{}
48		} else if col.Slot != 0 {
49			c.Width = Width{}
50		}
51		w.RemoveWindow(id)
52		w.insertColumn(src+max(dir, 0), c)
53	case edge:
54		return false
55	default:
56		w.stackWindow(id, w.Columns[next].Windows[0])
57	}
58	return true
59}
60
61// stack moves tiled window id to the bottom of the column holding target
62// and focuses it. Leaving a slot column's first row releases the slot.
63func (w *Workspace) stackWindow(id, target WindowID) {
64	w.dropSlotOf(id)
65	w.RemoveWindow(id)
66	i := slices.IndexFunc(w.Columns, func(c Column) bool { return slices.Contains(c.Windows, target) })
67	w.Columns[i].Windows = append(w.Columns[i].Windows, id)
68	// Activate leaves a fullscreen of the target column, which would hide it.
69	w.Activate(id)
70}
71
72// dropSlotOf clears the slot of the column whose slot window is id.
73func (w *Workspace) dropSlotOf(id WindowID) {
74	for i := range w.Columns {
75		if w.Columns[i].Slot != 0 && w.Columns[i].Windows[0] == id {
76			w.Columns[i].Slot = 0
77		}
78	}
79}
80
81// takeWindow removes the focused tiled window from its column for another
82// workspace; its slot, if any, is released.
83func (w *Workspace) takeWindow() (WindowID, bool) {
84	id, ok := w.Focused()
85	if !ok || w.onFloat() {
86		return 0, false
87	}
88	w.dropSlotOf(id)
89	w.RemoveWindow(id)
90	return id, true
91}
92
93// expelTo puts a window expelled from the neighbor monitor on the side
94// facing it (dir > 0: it came from the left): as a new edge column, or at
95// the bottom of the edge column when the workspace is full.
96func (w *Workspace) expelTo(id WindowID, dir int) {
97	at := 0
98	if dir < 0 {
99		at = len(w.Columns)
100	}
101	if !w.full() {
102		w.receive(Column{Windows: []WindowID{id}}, at)
103		return
104	}
105	i := min(at, len(w.Columns)-1)
106	w.Columns[i].Windows = append(w.Columns[i].Windows, id)
107	w.Activate(id)
108}

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