use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize, JsonSchema)]
pub enum Mode {
#[default]
Normal,
Insert,
Visual,
VisualLine,
Command,
}
impl Mode {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Normal => "NORMAL",
Self::Insert => "INSERT",
Self::Visual => "VISUAL",
Self::VisualLine => "V-LINE",
Self::Command => "COMMAND",
}
}
#[must_use]
pub const fn is_insertish(self) -> bool {
matches!(self, Self::Insert | Self::Command)
}
#[must_use]
pub const fn is_visualish(self) -> bool {
matches!(self, Self::Visual | Self::VisualLine)
}
#[must_use]
pub const fn cursor_shape(self) -> CursorShape {
match self {
Self::Normal | Self::Command => CursorShape::Block,
Self::Insert => CursorShape::Bar,
Self::Visual | Self::VisualLine => CursorShape::Underline,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize, JsonSchema)]
pub enum CursorShape {
#[default]
Block,
Bar,
Underline,
}
impl CursorShape {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Block => "block",
Self::Bar => "bar",
Self::Underline => "underline",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ModeTransition {
pub from: Mode,
pub to: Mode,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_normal() {
assert_eq!(Mode::default(), Mode::Normal);
}
#[test]
fn classifiers() {
assert!(Mode::Insert.is_insertish());
assert!(Mode::Command.is_insertish());
assert!(!Mode::Normal.is_insertish());
assert!(Mode::Visual.is_visualish());
assert!(Mode::VisualLine.is_visualish());
}
#[test]
fn display_labels() {
assert_eq!(Mode::Normal.as_str(), "NORMAL");
assert_eq!(Mode::Insert.as_str(), "INSERT");
}
#[test]
fn cursor_shape_per_mode() {
assert_eq!(Mode::Normal.cursor_shape(), CursorShape::Block);
assert_eq!(Mode::Command.cursor_shape(), CursorShape::Block);
assert_eq!(Mode::Insert.cursor_shape(), CursorShape::Bar);
assert_eq!(Mode::Visual.cursor_shape(), CursorShape::Underline);
assert_eq!(Mode::VisualLine.cursor_shape(), CursorShape::Underline);
}
#[test]
fn cursor_shape_labels() {
assert_eq!(CursorShape::Block.as_str(), "block");
assert_eq!(CursorShape::Bar.as_str(), "bar");
assert_eq!(CursorShape::Underline.as_str(), "underline");
}
#[test]
fn insert_is_the_only_bar_mode() {
let modes = [
Mode::Normal,
Mode::Insert,
Mode::Visual,
Mode::VisualLine,
Mode::Command,
];
let bars = modes
.into_iter()
.filter(|m| m.cursor_shape() == CursorShape::Bar)
.count();
assert_eq!(bars, 1);
}
}