1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
//! Keybinding management methods for `Config`.
//!
//! Covers:
//! - Merging default keybindings and status-bar widgets into user config
//! - Generating / synchronising snippet and action keybindings
use super::config_struct::Config;
impl Config {
/// Merge default keybindings into the user's config.
/// Only adds keybindings for actions that don't already exist in the user's config.
/// This ensures new features with default keybindings are available to existing users.
pub(crate) fn merge_default_keybindings(&mut self) {
let default_keybindings = crate::defaults::keybindings();
// Get the set of actions already configured by the user (owned strings to avoid borrow issues)
let existing_actions: std::collections::HashSet<String> = self
.keybindings
.iter()
.map(|kb| kb.action.clone())
.collect();
// Add any default keybindings whose actions are not already configured
let mut added_count = 0;
for default_kb in default_keybindings {
if !existing_actions.contains(&default_kb.action) {
log::info!(
"Adding new default keybinding: {} -> {}",
default_kb.key,
default_kb.action
);
self.keybindings.push(default_kb);
added_count += 1;
}
}
if added_count > 0 {
log::info!(
"Merged {} new default keybinding(s) into user config",
added_count
);
}
}
/// Merge default status bar widgets into the user's config.
/// Only adds widgets whose `WidgetId` doesn't already exist in the user's widget list.
/// This ensures new built-in widgets are available to existing users.
pub(crate) fn merge_default_widgets(&mut self) {
let default_widgets = crate::status_bar::default_widgets();
let existing_ids: std::collections::HashSet<crate::status_bar::WidgetId> = self
.status_bar
.status_bar_widgets
.iter()
.map(|w| w.id.clone())
.collect();
let mut added_count = 0;
for default_widget in default_widgets {
if !existing_ids.contains(&default_widget.id) {
log::info!(
"Adding new default status bar widget: {:?}",
default_widget.id
);
self.status_bar.status_bar_widgets.push(default_widget);
added_count += 1;
}
}
if added_count > 0 {
log::info!(
"Merged {} new default status bar widget(s) into user config",
added_count
);
}
}
/// Generate keybindings for snippets and actions that have keybindings configured.
///
/// This method adds or updates keybindings for snippets and actions in the keybindings list,
/// using the format "snippet:<id>" for snippets and "action:<id>" for actions.
/// If a keybinding for a snippet/action already exists, it will be updated with the new key.
pub fn generate_snippet_action_keybindings(&mut self) {
use crate::config::KeyBinding;
// Track actions we've seen to remove stale keybindings later
let mut seen_actions = std::collections::HashSet::new();
let mut added_count = 0;
let mut updated_count = 0;
// Generate keybindings for snippets
for snippet in &self.snippets {
if let Some(key) = &snippet.keybinding {
let action = format!("snippet:{}", snippet.id);
seen_actions.insert(action.clone());
if !key.is_empty() && snippet.enabled && snippet.keybinding_enabled {
// Check if this action already has a keybinding
if let Some(existing) =
self.keybindings.iter_mut().find(|kb| kb.action == action)
{
// Update existing keybinding if the key changed
if existing.key != *key {
log::info!(
"Updating keybinding for snippet '{}': {} -> {} (was: {})",
snippet.title,
key,
action,
existing.key
);
existing.key = key.clone();
updated_count += 1;
}
} else {
// Add new keybinding
log::info!(
"Adding keybinding for snippet '{}': {} -> {} (enabled={}, keybinding_enabled={})",
snippet.title,
key,
action,
snippet.enabled,
snippet.keybinding_enabled
);
self.keybindings.push(KeyBinding {
key: key.clone(),
action,
});
added_count += 1;
}
} else if !key.is_empty() {
log::info!(
"Skipping keybinding for snippet '{}': {} (enabled={}, keybinding_enabled={})",
snippet.title,
key,
snippet.enabled,
snippet.keybinding_enabled
);
}
}
}
// Generate keybindings for actions
for action_config in &self.actions {
if let Some(key) = action_config.keybinding() {
let action = format!("action:{}", action_config.id());
seen_actions.insert(action.clone());
if !key.is_empty() && action_config.keybinding_enabled() {
// Check if this action already has a keybinding
if let Some(existing) =
self.keybindings.iter_mut().find(|kb| kb.action == action)
{
// Update existing keybinding if the key changed
if existing.key != key {
log::info!(
"Updating keybinding for action '{}': {} -> {} (was: {})",
action_config.title(),
key,
action,
existing.key
);
existing.key = key.to_string();
updated_count += 1;
}
} else {
// Add new keybinding
log::info!(
"Adding keybinding for action '{}': {} -> {} (keybinding_enabled={})",
action_config.title(),
key,
action,
action_config.keybinding_enabled()
);
self.keybindings.push(KeyBinding {
key: key.to_string(),
action,
});
added_count += 1;
}
} else if !key.is_empty() {
log::info!(
"Skipping keybinding for action '{}': {} (keybinding_enabled={})",
action_config.title(),
key,
action_config.keybinding_enabled()
);
}
}
}
// Remove stale keybindings for snippets that no longer have keybindings or are disabled
let original_len = self.keybindings.len();
self.keybindings.retain(|kb| {
// Keep if it's not a snippet/action keybinding
if !kb.action.starts_with("snippet:") && !kb.action.starts_with("action:") {
return true;
}
// Keep if we saw it during our scan
seen_actions.contains(&kb.action)
});
let removed_count = original_len - self.keybindings.len();
if added_count > 0 || updated_count > 0 || removed_count > 0 {
log::info!(
"Snippet/Action keybindings: {} added, {} updated, {} removed",
added_count,
updated_count,
removed_count
);
}
}
}