Skip to main content

lattice_keymap/
mode_id.rs

1//! `ModeId` — canonical interned-string identity for a mode.
2//!
3//! Lives in `lattice-keymap` so that `KeymapLayer::MajorMode(ModeId)` and
4//! `KeymapLayer::MinorMode(ModeId)` can be defined here without creating a
5//! circular dependency back up to `lattice-mode`.
6
7use internment::Intern;
8
9/// Canonical identity of a mode. Interned-string for `Copy + Eq +
10/// Hash` at zero allocation cost on the hot path.
11///
12/// Two `ModeId`s are equal iff their names are equal; equality is
13/// pointer-cheap because `internment::Intern<String>` deduplicates
14/// at construction. Across crates, the identity is the *string*,
15/// not a Rust type -- this is intentional, since lifecycle events
16/// and registry lookups are uniform across built-in and plugin
17/// modes (mode-architecture.md §1, "modes are an interface, not
18/// a distribution unit").
19///
20/// Naming convention: mode names always end in `-mode`. Group
21/// names (M.2) never end in `-mode`. The disambiguation rule
22/// in mode-architecture.md §6.7.1 depends on this convention.
23///
24/// `Ord` compares the names (alphabetical), not intern addresses, so
25/// orderings built from it are stable across runs.
26///
27/// # Examples
28///
29/// ```
30/// use lattice_keymap::ModeId;
31///
32/// let a = ModeId::new("diff-mode");
33/// assert_eq!(a, ModeId::new("diff-mode"));
34/// assert_eq!(a.as_str(), "diff-mode");
35/// assert!(a < ModeId::new("magit-mode"));
36/// ```
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
38pub struct ModeId(Intern<String>);
39
40impl ModeId {
41    /// Intern `name`. Two calls with equal strings produce equal
42    /// `ModeId`s; the underlying allocation is shared.
43    pub fn new(name: &str) -> Self {
44        Self(Intern::new(name.to_string()))
45    }
46
47    /// Borrow the canonical name. Stable for the program's
48    /// lifetime.
49    pub fn as_str(&self) -> &str {
50        self.0.as_ref()
51    }
52}
53
54impl std::fmt::Display for ModeId {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        f.write_str(self.as_str())
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    #![allow(clippy::unwrap_used)]
63    use super::*;
64
65    #[test]
66    fn mode_id_interns_equal_names() {
67        let a = ModeId::new("rust-mode");
68        let b = ModeId::new("rust-mode");
69        assert_eq!(a, b);
70        // Pointer-equal because Intern dedups.
71        assert_eq!(a.as_str().as_ptr(), b.as_str().as_ptr());
72    }
73
74    #[test]
75    fn mode_id_distinguishes_different_names() {
76        let a = ModeId::new("rust-mode");
77        let b = ModeId::new("python-mode");
78        assert_ne!(a, b);
79    }
80
81    #[test]
82    fn mode_id_display_is_the_name() {
83        assert_eq!(format!("{}", ModeId::new("lsp-mode")), "lsp-mode");
84    }
85}