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                seen_actions.insert(action.clone());
147
148                if !key.is_empty() && action_config.keybinding_enabled() {
149                    // Check if this action already has a keybinding
150                    if let Some(existing) =
151                        self.keybindings.iter_mut().find(|kb| kb.action == action)
152                    {
153                        // Update existing keybinding if the key changed
154                        if existing.key != key {
155                            log::info!(
156                                "Updating keybinding for action '{}': {} -> {} (was: {})",
157                                action_config.title(),
158                                key,
159                                action,
160                                existing.key
161                            );
162                            existing.key = key.to_string();
163                            updated_count += 1;
164                        }
165                    } else {
166                        // Add new keybinding
167                        log::info!(
168                            "Adding keybinding for action '{}': {} -> {} (keybinding_enabled={})",
169                            action_config.title(),
170                            key,
171                            action,
172                            action_config.keybinding_enabled()
173                        );
174                        self.keybindings.push(KeyBinding {
175                            key: key.to_string(),
176                            action,
177                        });
178                        added_count += 1;
179                    }
180                } else if !key.is_empty() {
181                    log::info!(
182                        "Skipping keybinding for action '{}': {} (keybinding_enabled={})",
183                        action_config.title(),
184                        key,
185                        action_config.keybinding_enabled()
186                    );
187                }
188            }
189        }
190
191        // Remove stale keybindings for snippets that no longer have keybindings or are disabled
192        let original_len = self.keybindings.len();
193        self.keybindings.retain(|kb| {
194            // Keep if it's not a snippet/action keybinding
195            if !kb.action.starts_with("snippet:") && !kb.action.starts_with("action:") {
196                return true;
197            }
198            // Keep if we saw it during our scan
199            seen_actions.contains(&kb.action)
200        });
201        let removed_count = original_len - self.keybindings.len();
202
203        if added_count > 0 || updated_count > 0 || removed_count > 0 {
204            log::info!(
205                "Snippet/Action keybindings: {} added, {} updated, {} removed",
206                added_count,
207                updated_count,
208                removed_count
209            );
210        }
211    }
212}