main 5041353

floating_session_registry.go RawHistoryFolder

1package ui
2
3import (
4	"iter"
5	"maps"
6	"slices"
7
8	"github.com/bnema/dumber/internal/domain/entity"
9)
10
11// floatingSessionRegistry owns every floating pane session, keyed by tab and
12// profile session. All lookups over sessions go through it so the ownership
13// rules (one session per key, nil entries ignored, pane-to-tab mapping via
14// the key, never by parsing PaneID) live in one place.
15type floatingSessionRegistry map[floatingSessionKey]*floatingWorkspaceSession
16
17// all yields every non-nil session without allocating. Callers must not add
18// or remove sessions while iterating; use forTabSnapshot for that.
19func (r floatingSessionRegistry) all() iter.Seq2[floatingSessionKey, *floatingWorkspaceSession] {
20	return func(yield func(floatingSessionKey, *floatingWorkspaceSession) bool) {
21		for key, session := range r {
22			if session != nil && !yield(key, session) {
23				return
24			}
25		}
26	}
27}
28
29// forTab yields the non-nil sessions owned by tabID without allocating, so it
30// is safe on hot GTK layout callbacks. Same mutation rule as all.
31func (r floatingSessionRegistry) forTab(tabID entity.TabID) iter.Seq2[floatingSessionKey, *floatingWorkspaceSession] {
32	return func(yield func(floatingSessionKey, *floatingWorkspaceSession) bool) {
33		for key, session := range r.all() {
34			if key.tabID == tabID && !yield(key, session) {
35				return
36			}
37		}
38	}
39}
40
41// forTabSnapshot copies tabID's sessions so the caller may release them.
42func (r floatingSessionRegistry) forTabSnapshot(tabID entity.TabID) map[floatingSessionKey]*floatingWorkspaceSession {
43	return maps.Collect(r.forTab(tabID))
44}
45
46// byPaneID returns the session hosting paneID and its key.
47func (r floatingSessionRegistry) byPaneID(paneID entity.PaneID) (floatingSessionKey, *floatingWorkspaceSession, bool) {
48	if paneID == "" {
49		return floatingSessionKey{}, nil, false
50	}
51	for key, session := range r {
52		if session != nil && session.paneID == paneID {
53			return key, session, true
54		}
55	}
56	return floatingSessionKey{}, nil, false
57}
58
59// visibleForTab returns the visible session of tabID, if any.
60func (r floatingSessionRegistry) visibleForTab(tabID entity.TabID) (floatingSessionKey, *floatingWorkspaceSession, bool) {
61	for key, session := range r {
62		if key.tabID != tabID || session == nil || session.pane == nil {
63			continue
64		}
65		if session.pane.IsVisible() {
66			return key, session, true
67		}
68	}
69	return floatingSessionKey{tabID: tabID}, nil, false
70}
71
72// tabIDs returns the distinct tabs owning at least one session, sorted.
73func (r floatingSessionRegistry) tabIDs() []entity.TabID {
74	ids := make([]entity.TabID, 0, len(r))
75	for key := range r {
76		if !slices.Contains(ids, key.tabID) {
77			ids = append(ids, key.tabID)
78		}
79	}
80	slices.Sort(ids)
81	return ids
82}

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 8

Tags 42

↑↓ moveenter openesc close