Skip to main content

par_term_config/config/
keybindings_methods.rs

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