use std::collections::HashMap;
use reovim_kernel::api::v1::{CursorStyle, Mode, ModeId, ModuleId};
#[derive(Debug, Clone)]
pub struct ModeEntry {
id: ModeId,
pub display_name: &'static str,
pub cursor_style: CursorStyle,
pub accepts_char_input: bool,
pub has_selection: bool,
pub inherits_from: Option<ModeId>,
pub is_entry: bool,
owner: Option<ModuleId>,
}
impl ModeEntry {
#[must_use]
pub fn from_mode<M: Mode>(mode: M) -> Self {
Self {
id: mode.id(),
display_name: mode.display_name(),
cursor_style: mode.cursor_style(),
accepts_char_input: mode.accepts_char_input(),
has_selection: mode.has_selection(),
inherits_from: mode.inherits_from().map(|m| m.id()),
is_entry: mode.is_entry(),
owner: None,
}
}
#[must_use]
pub fn from_info(info: reovim_driver_input::ModeInfo) -> Self {
Self {
id: info.id,
display_name: info.display_name,
cursor_style: info.cursor_style,
accepts_char_input: info.accepts_char_input,
has_selection: info.has_selection,
inherits_from: info.inherits_from,
is_entry: info.is_entry,
owner: None,
}
}
#[must_use]
pub fn with_owner(mut self, owner: ModuleId) -> Self {
self.owner = Some(owner);
self
}
#[must_use]
pub const fn id(&self) -> &ModeId {
&self.id
}
#[must_use]
pub const fn owner(&self) -> Option<&ModuleId> {
self.owner.as_ref()
}
}
#[derive(Default, Debug)]
pub struct ModeRegistry {
modes: HashMap<ModeId, ModeEntry>,
entry_mode: Option<ModeId>,
}
impl ModeRegistry {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn register_mode<M: Mode>(&mut self, mode: M) {
self.register(ModeEntry::from_mode(mode));
}
pub fn register_all<M: Mode, I: IntoIterator<Item = M>>(&mut self, modes: I) {
for mode in modes {
self.register_mode(mode);
}
}
pub fn register(&mut self, entry: ModeEntry) {
if entry.is_entry && self.entry_mode.is_none() {
self.entry_mode = Some(entry.id.clone());
}
let id = entry.id.clone();
self.modes.insert(id, entry);
}
#[must_use]
pub const fn entry_mode(&self) -> Option<&ModeId> {
self.entry_mode.as_ref()
}
#[must_use]
pub fn get(&self, id: &ModeId) -> Option<&ModeEntry> {
self.modes.get(id)
}
#[must_use]
pub fn contains(&self, id: &ModeId) -> bool {
self.modes.contains_key(id)
}
#[must_use]
pub fn accepts_char_input(&self, id: &ModeId) -> bool {
self.modes.get(id).is_some_and(|e| e.accepts_char_input)
}
#[must_use]
pub fn has_selection(&self, id: &ModeId) -> bool {
self.modes.get(id).is_some_and(|e| e.has_selection)
}
#[must_use]
pub fn cursor_style(&self, id: &ModeId) -> CursorStyle {
self.modes
.get(id)
.map_or(CursorStyle::Block, |e| e.cursor_style)
}
#[must_use]
pub fn display_name(&self, id: &ModeId) -> &'static str {
self.modes.get(id).map_or("UNKNOWN", |e| e.display_name)
}
#[must_use]
pub fn inherits_from(&self, id: &ModeId) -> Option<&ModeId> {
self.modes.get(id).and_then(|e| e.inherits_from.as_ref())
}
pub fn ids(&self) -> impl Iterator<Item = &ModeId> {
self.modes.keys()
}
#[must_use]
pub fn find_by_name(&self, module: &str, name: &str) -> Option<&ModeId> {
self.modes.values().find_map(|entry| {
if entry.id.module().as_str() == module && entry.id.name() == name {
Some(&entry.id)
} else {
None
}
})
}
#[must_use]
pub fn len(&self) -> usize {
self.modes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.modes.is_empty()
}
pub fn unregister_for_module(&mut self, module: &ModuleId) -> usize {
let before = self.modes.len();
self.modes
.retain(|_, entry| entry.owner.as_ref() != Some(module));
before - self.modes.len()
}
}
#[cfg(test)]
#[path = "mode_tests.rs"]
mod tests;