render_timer.go RawHistoryFolder

1package daemon
2
3import (
4	"sync"
5	"time"
6
7	"github.com/bnema/vev/internal/ports"
8)
9
10// timerToken is the complete, immutable identity of one timer worker. A lane
11// owns either this token or nothing; cancellation and completion are never
12// retained separately where a stale callback could reacquire them.
13type timerToken struct {
14	generation uint64
15	timer      ports.Timer
16	cancel     chan struct{}
17	done       chan struct{}
18}
19
20// timerLane is coordinator-owned and must be accessed while renderCoordinator.mu
21// is held. Detach removes the complete token before any external Timer call.
22type timerLane struct {
23	generation uint64
24	token      *timerToken
25}
26
27func (l *timerLane) replaceLocked() (uint64, *timerToken) {
28	l.generation++
29	return l.generation, l.detachLocked()
30}
31
32func (l *timerLane) detachLocked() *timerToken {
33	token := l.token
34	l.token = nil
35	if token != nil {
36		close(token.cancel)
37	}
38	return token
39}
40
41func (l *timerLane) publishLocked(generation uint64, timer ports.Timer) *timerToken {
42	if l.generation != generation || l.token != nil {
43		return nil
44	}
45	token := &timerToken{
46		generation: generation,
47		timer:      timer,
48		cancel:     make(chan struct{}),
49		done:       make(chan struct{}),
50	}
51	l.token = token
52	return token
53}
54
55func (l *timerLane) clearLocked(token *timerToken) bool {
56	if token == nil || l.generation != token.generation || l.token != token {
57		return false
58	}
59	l.token = nil
60	return true
61}
62
63// timerSupervisor owns worker accounting. Workers are registered only while
64// coordinator ownership is held and terminal teardown rejects registrations
65// before Wait begins, so Add cannot race the terminal Wait.
66type timerSupervisor struct{ workers sync.WaitGroup }
67
68func (s *timerSupervisor) startLocked(token *timerToken, timerC <-chan time.Time, fire func()) {
69	s.startTaskLocked(func() {
70		defer close(token.done)
71		select {
72		case <-timerC:
73			fire()
74		case <-token.cancel:
75		}
76	})
77}
78
79// startTaskLocked accounts for coordinator work that is not backed by a timer
80// token. The caller must hold the coordinator lock so terminal teardown cannot
81// begin waiting between registration and launch.
82func (s *timerSupervisor) startTaskLocked(task func()) {
83	s.workers.Add(1)
84	go func() {
85		defer s.workers.Done()
86		task()
87	}()
88}
89
90func (s *timerSupervisor) wait() { s.workers.Wait() }
91
92// stopDetachedTimer is intentionally non-blocking. Ordinary lifecycle and
93// attachment transitions may cancel/stop stale workers but callback stacks
94// never wait for a worker to finish.
95func stopDetachedTimer(token *timerToken) {
96	if token != nil && token.timer != nil {
97		token.timer.Stop()
98	}
99}

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