v0.23.3 fc8669a

watcher.go RawHistoryFolder

1package config
2
3import (
4	"fmt"
5	"os"
6
7	"github.com/fsnotify/fsnotify"
8)
9
10// Watch starts watching the config file for changes and reloads automatically.
11func (m *Manager) Watch() error {
12	m.mu.Lock()
13	defer m.mu.Unlock()
14
15	if m.watching {
16		return nil // Already watching
17	}
18
19	m.viper.WatchConfig()
20	m.viper.OnConfigChange(func(_ fsnotify.Event) {
21		// Reload config
22		if err := m.reload(); err != nil {
23			fmt.Fprintf(os.Stderr, "Warning: failed to reload config: %v\n", err)
24			return
25		}
26
27		// Notify callbacks
28		m.mu.RLock()
29		config := m.config
30		callbacks := make([]func(*Config), len(m.callbacks))
31		copy(callbacks, m.callbacks)
32		m.mu.RUnlock()
33
34		for _, callback := range callbacks {
35			callback(config)
36		}
37	})
38
39	m.watching = true
40	return nil
41}
42
43// OnConfigChange registers a callback function to be called when config changes.
44func (m *Manager) OnConfigChange(callback func(*Config)) {
45	m.mu.Lock()
46	defer m.mu.Unlock()
47
48	m.callbacks = append(m.callbacks, callback)
49}
50
51// reload reloads the configuration (internal method, must be called with lock held for write).
52func (m *Manager) reload() error {
53	if err := m.viper.ReadInConfig(); err != nil {
54		return err
55	}
56
57	config := &Config{}
58	if err := m.viper.Unmarshal(config); err != nil {
59		return err
60	}
61
62	// Set database path if not specified
63	if config.Database.Path == "" {
64		dbPath, err := GetDatabaseFile()
65		if err != nil {
66			return fmt.Errorf("failed to get database path: %w", err)
67		}
68		config.Database.Path = dbPath
69	}
70
71	normalizeConfig(config)
72
73	// Validate all config values
74	if err := validateConfig(config); err != nil {
75		return fmt.Errorf("configuration validation failed: %w", err)
76	}
77
78	m.config = config
79	return nil
80}
81
82// Watch starts watching the global configuration for changes.
83func Watch() error {
84	if globalManager == nil {
85		return fmt.Errorf("configuration not initialized")
86	}
87	return globalManager.Watch()
88}
89
90// OnConfigChange registers a callback for global configuration changes.
91func OnConfigChange(callback func(*Config)) {
92	if globalManager == nil {
93		return
94	}
95	globalManager.OnConfigChange(callback)
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 8

Tags 42

↑↓ moveenter openesc close