Skip to main content

fret_runtime/keymap/
conflicts.rs

1use crate::CommandId;
2use std::collections::{HashMap, HashSet};
3
4use super::{
5    Keymap, KeymapBindingSignature, KeymapConflict, KeymapConflictEntry, KeymapConflictKind,
6};
7
8impl Keymap {
9    /// Returns keymap conflicts, defined as multiple bindings sharing the same
10    /// `(platform, when, sequence)` tuple (ADR 0021 section 7).
11    ///
12    /// This is intended for diagnostics and future UI reporting.
13    pub fn conflicts(&self) -> Vec<KeymapConflict> {
14        let mut by_sig: HashMap<KeymapBindingSignature, Vec<KeymapConflictEntry>> = HashMap::new();
15
16        for (index, b) in self.bindings.iter().enumerate() {
17            let sig = KeymapBindingSignature {
18                platform: b.platform,
19                sequence: b.sequence.clone(),
20                when: b.when.clone(),
21            };
22            by_sig.entry(sig).or_default().push(KeymapConflictEntry {
23                index,
24                command: b.command.clone(),
25            });
26        }
27
28        let mut out: Vec<KeymapConflict> = Vec::new();
29        for (signature, mut entries) in by_sig {
30            if entries.len() <= 1 {
31                continue;
32            }
33
34            entries.sort_by_key(|e| e.index);
35
36            let mut commands: HashSet<Option<&CommandId>> = HashSet::new();
37            let mut any_unbind = false;
38            for e in &entries {
39                any_unbind |= e.command.is_none();
40                commands.insert(e.command.as_ref());
41            }
42
43            let kind = if commands.len() <= 1 {
44                KeymapConflictKind::Redundant
45            } else if any_unbind {
46                KeymapConflictKind::Unbind
47            } else {
48                KeymapConflictKind::Override
49            };
50
51            out.push(KeymapConflict {
52                signature,
53                kind,
54                entries,
55            });
56        }
57
58        out.sort_by(|a, b| {
59            let ka = a.entries.last().map(|e| e.index).unwrap_or_default();
60            let kb = b.entries.last().map(|e| e.index).unwrap_or_default();
61            ka.cmp(&kb)
62        });
63        out
64    }
65}