move_picker.go RawHistoryFolder

1// Package daemon holds vev's server-side session multiplexer use case.
2package daemon
3
4import (
5	"github.com/bnema/vev/internal/domain"
6	"github.com/bnema/vev/internal/protocol"
7	"github.com/bnema/vev/internal/usecase/picker"
8)
9
10// moveSourceLocator captures the exact source of one move at command time. The
11// presenting client never supplies it: only the daemon resolves it, and only
12// for the interaction that captured it.
13type moveSessionLocator struct {
14	ID          domain.SessionID
15	Incarnation domain.IncarnationID
16	Name        string
17}
18
19type moveSourceLocator struct {
20	Session              moveSessionLocator
21	TabID                domain.TabStableID
22	PaneID               domain.PaneStableID
23	Attachment           *attachedClient
24	AttachmentCapability attachmentCapability
25}
26
27func (d *Daemon) movePickerSourceError(source moveSourceLocator) error {
28	if d == nil || source.Session.ID == "" || source.TabID == "" {
29		return domain.UserWarn(domain.NoticeSessionUnavailable, "Source session is no longer available.", errMovePaneInvalid)
30	}
31	sess := d.sessionByID(source.Session.ID)
32	if sess == nil || sess.incarnation != source.Session.Incarnation {
33		return domain.UserWarn(domain.NoticeSessionUnavailable, "Source session is no longer available.", errMovePaneInvalid)
34	}
35	sess.mu.Lock()
36	defer sess.mu.Unlock()
37	if source.AttachmentCapability.ac != nil && !source.AttachmentCapability.currentInSessionLocked(sess) {
38		return domain.UserWarn(domain.NoticeSessionUnavailable, "Source attachment is no longer active.", errMovePaneInvalid)
39	}
40	if source.Attachment != nil && !attachmentRegisteredLocked(sess, source.Attachment) {
41		return domain.UserWarn(domain.NoticeSessionUnavailable, "Source attachment is no longer active.", errMovePaneInvalid)
42	}
43	tb := findMoveTabLocked(sess, source.TabID)
44	if tb == nil {
45		return domain.UserWarn(domain.NoticeSessionUnavailable, "Tab no longer exists.", errMovePaneInvalid)
46	}
47	if source.PaneID == "" {
48		return nil
49	}
50	tb.mu.Lock()
51	defer tb.mu.Unlock()
52	if paneByStableIDLocked(tb, string(source.PaneID)) == nil {
53		return domain.UserWarn(domain.NoticeSessionUnavailable, "Pane no longer exists.", errMovePaneInvalid)
54	}
55	return nil
56}
57
58// commitMovePickerSelection performs the move the owning source authorised.
59// beforeFollow runs once admission proves the move will follow; afterFollow
60// receives the fresh effect on the published destination capability that the
61// move postcommit admits, so a composite follow can report its completion
62// after the committing effect ended.
63func (d *Daemon) commitMovePickerSelection(intent protocol.PickerIntent, source moveSourceLocator, target picker.Target, beforeFollow func() error, afterFollow func(*attachmentEffect)) error {
64	destination := moveSessionLocator{ID: target.Session, Incarnation: target.Incarnation, Name: target.Name}
65	switch intent {
66	case protocol.PickerIntentMovePane:
67		if target.TabID == "" {
68			return errMovePaneInvalid
69		}
70		return d.movePane(movePaneRequest{
71			Follow:               true,
72			BeforeFollow:         beforeFollow,
73			AfterFollow:          afterFollow,
74			Attachment:           source.Attachment,
75			AttachmentCapability: source.AttachmentCapability,
76			Source:               source.Session,
77			SourceTabID:          source.TabID,
78			SourcePaneID:         source.PaneID,
79			Destination:          destination,
80			DestinationTabID:     target.TabID,
81		})
82	case protocol.PickerIntentMoveTab:
83		return d.moveTab(moveTabRequest{
84			Follow:               true,
85			BeforeFollow:         beforeFollow,
86			AfterFollow:          afterFollow,
87			Attachment:           source.Attachment,
88			AttachmentCapability: source.AttachmentCapability,
89			Source:               source.Session,
90			SourceTabID:          source.TabID,
91			Destination:          destination,
92		})
93	default:
94		return errMovePaneInvalid
95	}
96}

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