v0.2.0 80c61e3

move_pane_commit.go RawHistoryFolder

1package daemon
2
3import (
4	"github.com/bnema/vev/internal/domain"
5	"github.com/bnema/vev/internal/usecase/layout"
6)
7
8// movePaneCommit owns the single architecture-locked publication section. Its
9// inputs are immutable admission/fence snapshots; outputs form the postcommit
10// plan. publishLocked performs all fallible checks before its first write.
11type movePaneCommit struct {
12	req                       movePaneRequest
13	source, destination       *session
14	sourceTab, destinationTab *tab
15	movedPane                 *pane
16	sourceAttachments         []*attachedClient
17	sourceTransports          map[*attachedClient]transportSnapshot
18	sourceGeneration          uint64
19	destinationGeneration     uint64
20	frozenEffects             frozenAttachmentEffectGates
21	err                       error
22
23	syncCleanup              syncTimerCleanup
24	sourceEmpty              bool
25	sourceTabRemoved         bool
26	retiredParked            []parkedAttachmentRetirement
27	retiredAttachments       []detachedAttachmentSnapshot
28	sourceMetadata           domain.CatalogueMetadataUpdate
29	sourceMetadataValid      bool
30	destinationMetadata      domain.CatalogueMetadataUpdate
31	destinationMetadataValid bool
32}
33
34func (c *movePaneCommit) publishLocked(d *Daemon) bool {
35	var candidate *movePaneCandidate
36	var err error
37	d.mu.Lock()
38	if d.closing || d.sessions[c.source.id] != c.source || d.sessions[c.destination.id] != c.destination {
39		d.mu.Unlock()
40		return false
41	}
42	d.notices.routingMu.Lock()
43	unlockSessions := lockAttachmentSessions(c.source, c.destination)
44	defer unlockSessions()
45	defer d.notices.routingMu.Unlock()
46	defer d.mu.Unlock()
47
48	if !moveSessionLocatorCurrentLocked(c.source, c.req.Source) || !moveSessionLocatorCurrentLocked(c.destination, c.req.Destination) {
49		return false
50	}
51	if c.req.Attachment != nil && !attachmentRegisteredLocked(c.source, c.req.Attachment) {
52		return false
53	}
54	if c.req.AttachmentToken.ac != nil && (c.req.AttachmentToken.ac != c.req.Attachment || !moveAttachmentTokenCurrentLocked(c.req.AttachmentToken, c.source)) {
55		return false
56	}
57
58	unlockTabs := lockMoveTabs(c.sourceTab, c.destinationTab)
59	c.movedPane.mu.Lock()
60	defer c.movedPane.mu.Unlock()
61	defer unlockTabs()
62
63	if !moveTabMemberLocked(c.source, c.sourceTab) || !moveTabMemberLocked(c.destination, c.destinationTab) || c.sourceTab == c.destinationTab ||
64		c.sourceTab.layoutGeneration != c.sourceGeneration || c.destinationTab.layoutGeneration != c.destinationGeneration ||
65		c.movedPane.stableID != string(c.req.SourcePaneID) || c.sourceTab.panes[c.movedPane.id] != c.movedPane ||
66		c.sourceTab.tree == nil || c.sourceTab.tree.Root == nil || !layout.ContainsLeaf(c.sourceTab.tree.Root, c.movedPane.id) ||
67		c.destinationTab.tree == nil || c.destinationTab.tree.Root == nil {
68		return false
69	}
70	owner := c.movedPane.ownerSnapshot()
71	if owner != nil && (owner.session != c.source || owner.tab != c.sourceTab) {
72		return false
73	}
74	candidate, err = prepareMovePaneCandidate(c.sourceTab, c.destinationTab, c.movedPane)
75	if err != nil {
76		c.err = err
77		return false
78	}
79	if candidate.sourceGeneration != c.sourceGeneration || candidate.destinationGeneration != c.destinationGeneration {
80		return false
81	}
82
83	sourceWillEmpty := candidate.removeSourceTab && len(c.source.tabs) == 1
84	if sourceWillEmpty && c.source != c.destination && !sameMoveAttachmentsLocked(c.source, c.sourceAttachments) {
85		return false
86	}
87
88	// Everything above is fallible. Shared topology and owner publication are
89	// committed once; attachment views are repaired after these locks release.
90	if candidate.removeSourceTab {
91		idx := indexMoveTabLocked(c.source, c.sourceTab)
92		if idx < 0 {
93			return false
94		}
95		if len(c.source.tabs) > 1 {
96			c.source.prepareAttachmentViewsForRemovedTabLocked(c.sourceTab, idx)
97		}
98		c.source.tabs = append(c.source.tabs[:idx], c.source.tabs[idx+1:]...)
99		c.sourceTabRemoved = true
100	} else {
101		c.sourceTab.tree = candidate.sourceTree
102		c.sourceTab.bumpLayoutGenerationLocked()
103	}
104	delete(c.sourceTab.panes, candidate.sourceID)
105	if d.beforeMovePaneCommit != nil {
106		d.beforeMovePaneCommit()
107	}
108	c.destinationTab.tree = candidate.destinationTree
109	c.destinationTab.nextPaneID = candidate.destinationNextPaneID
110	c.destinationTab.panes[candidate.destinationID] = c.movedPane
111	c.destinationTab.bumpLayoutGenerationLocked()
112	c.movedPane.id = candidate.destinationID
113	oldOwner, newOwner := c.movedPane.publishOwnerLocked(c.destination, c.destinationTab, 0)
114	c.syncCleanup.append(d.migratePaneSyncOwnerLocked(c.movedPane, oldOwner, newOwner))
115
116	if sourceWillEmpty {
117		d.unregisterSessionLocked(c.source)
118		c.retiredAttachments = detachMoveAttachmentsLocked(c.source, c.sourceTransports)
119		c.source.tabs = nil
120		d.purgeParkingForSessionLocked(c.source)
121		c.retiredParked = d.purgeParkedForSessionLocked(c.source)
122	}
123	c.sourceEmpty = len(c.source.tabs) == 0
124	if !c.source.ephemeral {
125		c.sourceMetadata = c.source.persistRecordLocked(max(d.nowUnixNano(), c.source.createdAt, int64(1))).MetadataUpdate()
126		c.sourceMetadataValid = true
127	}
128	if !c.destination.ephemeral && c.destination != c.source {
129		c.destinationMetadata = c.destination.persistRecordLocked(max(d.nowUnixNano(), c.destination.createdAt, int64(1))).MetadataUpdate()
130		c.destinationMetadataValid = true
131	}
132	return true
133}

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 12

Tags 11

↑↓ moveenter openesc close