Skip to main content

lattice_mode/modes/
text.rs

1//! `text-mode` -- the default major mode for plain-text content.
2//!
3//! Catch-all major mode that any buffer falls back to when no
4//! more-specific major mode applies. Provides:
5//!
6//! - No tree-sitter parser.
7//! - No LSP attachment.
8//! - The default keymap layer (vim grammar).
9//! - No mode-scoped option overrides (all options take their
10//!   global / default values).
11//!
12//! Per `mode-architecture.md` ยง4.1, this is the foundation
13//! catch-all. Buffer content with no language detection lands
14//! here. Buffer kinds with their own behavior (Help, FileTree,
15//! Oil, language-specific Documents) declare their own majors;
16//! those modes can `implies` text-mode if they want the
17//! default keymap, or specify their own from scratch.
18
19use crate::{
20    CapabilitySet, LifecycleFuture, Mode, ModeContext, ModeId, ModeKind, OptionOverrideSet,
21};
22
23/// Catch-all major mode for plain-text content.
24pub struct TextMode;
25
26impl TextMode {
27    /// Canonical id for this mode. Used for `:enable text-mode`,
28    /// `:customize text-mode` (if it ever has options), etc.
29    pub fn mode_id() -> ModeId {
30        ModeId::new("text-mode")
31    }
32}
33
34impl Mode for TextMode {
35    type Guard = ();
36
37    fn id(&self) -> ModeId {
38        Self::mode_id()
39    }
40
41    fn kind(&self) -> ModeKind {
42        ModeKind::Major
43    }
44
45    fn required_capabilities(&self) -> CapabilitySet {
46        // text-mode imposes no requirements -- it activates on
47        // any buffer.
48        CapabilitySet::empty()
49    }
50
51    /// Plain text is prose, so `autowrap` covers every line (RF.4).
52    ///
53    /// The one override this mode carries, and it does not contradict
54    /// the module docs above ("no mode-scoped option overrides"): that
55    /// described a mode with nothing to say, and prose-versus-code IS
56    /// something a catch-all text major can say. A `.txt` buffer that
57    /// does not wrap while a `.md` one does would be an arbitrary split.
58    fn options(&self) -> OptionOverrideSet {
59        lattice_config::overrides! {
60            lattice_config::AutoWrapOption = lattice_core::AutoWrap::All,
61        }
62    }
63
64    fn on_activate(&self, _ctx: ModeContext) -> LifecycleFuture<'_, ()> {
65        // No setup work; text-mode is content-free.
66        Box::pin(async { Ok(()) })
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn id_and_kind() {
76        let m = TextMode;
77        assert_eq!(m.id(), TextMode::mode_id());
78        assert_eq!(m.id().as_str(), "text-mode");
79        assert_eq!(m.kind(), ModeKind::Major);
80    }
81
82    /// RF.4: plain text is prose. A `.txt` buffer that does not wrap
83    /// while a `.md` one does would be an arbitrary split.
84    #[test]
85    fn text_mode_wraps_prose() {
86        let opts = TextMode.options();
87        assert!(
88            opts.iter()
89                .any(|o| o.option_type_id
90                    == std::any::TypeId::of::<lattice_config::AutoWrapOption>()),
91            "text-mode must override autowrap to `all`"
92        );
93    }
94
95    #[test]
96    fn no_capability_requirements() {
97        let m = TextMode;
98        assert_eq!(m.required_capabilities(), CapabilitySet::empty());
99    }
100}