1//! Contains the [`VimModeSetting`] and [`HelixModeSetting`] used to enable/disable Vim and Helix modes.
2//!
3//! This is in its own crate as we want other crates to be able to enable or
4//! disable Vim/Helix modes without having to depend on the `vim` crate in its
5//! entirety.
6
7use anyhow::Result;
8use gpui::App;
9use schemars::JsonSchema;
10use serde::de::Error;
11use serde::{Deserialize, Deserializer, Serialize, Serializer};
12use settings::{Settings, SettingsSources};
13use std::fmt::Display;
14
15/// Initializes the `vim_mode_setting` crate.
16pub fn init(cx: &mut App) {
17 EditorModeSetting::register(cx);
18}
19
20/// Whether or not to enable Vim mode.
21///
22/// Default: `EditMode::Default`
23pub struct EditorModeSetting(pub EditorMode);
24
25#[derive(Copy, Clone, Debug, PartialEq, Eq, JsonSchema, Default)]
26pub enum EditorMode {
27 #[default]
28 Default,
29 Vim(ModalMode),
30 Helix(ModalMode),
31}
32
33impl<'de> Deserialize<'de> for EditorMode {
34 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
35 where
36 D: Deserializer<'de>,
37 {
38 let s = String::deserialize(deserializer)?;
39 match s.as_str() {
40 "default" => Ok(EditorMode::Default),
41 "vim" => Ok(EditorMode::Vim(ModalMode::Normal)),
42 "vim_normal" => Ok(EditorMode::Vim(ModalMode::Normal)),
43 "vim_insert" => Ok(EditorMode::Vim(ModalMode::Insert)),
44 "vim_replace" => Ok(EditorMode::Vim(ModalMode::Replace)),
45 "vim_visual" => Ok(EditorMode::Vim(ModalMode::Visual)),
46 "vim_visual_line" => Ok(EditorMode::Vim(ModalMode::VisualLine)),
47 "vim_visual_block" => Ok(EditorMode::Vim(ModalMode::VisualBlock)),
48 "helix_experimental" => Ok(EditorMode::Helix(ModalMode::HelixNormal)),
49 _ => Err(D::Error::custom(format!("Unknown editor mode: {}", s))),
50 }
51 }
52}
53
54impl Serialize for EditorMode {
55 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
56 where
57 S: Serializer,
58 {
59 let s = match self {
60 EditorMode::Default => "default",
61 EditorMode::Vim(ModalMode::Normal) => "vim",
62 EditorMode::Vim(ModalMode::Insert) => "vim_insert",
63 EditorMode::Vim(ModalMode::Replace) => "vim_replace",
64 EditorMode::Vim(ModalMode::Visual) => "vim_visual",
65 EditorMode::Vim(ModalMode::VisualLine) => "vim_visual_line",
66 EditorMode::Vim(ModalMode::VisualBlock) => "vim_visual_block",
67 EditorMode::Helix(ModalMode::Normal) => "helix_experimental",
68 _ => return Err(serde::ser::Error::custom("unsupported editor mode variant")),
69 };
70 serializer.serialize_str(s)
71 }
72}
73
74#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
75pub enum ModalMode {
76 Normal,
77 Insert,
78 Replace,
79 Visual,
80 VisualLine,
81 VisualBlock,
82 HelixNormal,
83}
84
85impl Display for ModalMode {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 match self {
88 ModalMode::Normal => write!(f, "NORMAL"),
89 ModalMode::Insert => write!(f, "INSERT"),
90 ModalMode::Replace => write!(f, "REPLACE"),
91 ModalMode::Visual => write!(f, "VISUAL"),
92 ModalMode::VisualLine => write!(f, "VISUAL LINE"),
93 ModalMode::VisualBlock => write!(f, "VISUAL BLOCK"),
94 ModalMode::HelixNormal => write!(f, "HELIX NORMAL"),
95 }
96 }
97}
98
99impl ModalMode {
100 pub fn is_visual(&self) -> bool {
101 match self {
102 Self::Visual | Self::VisualLine | Self::VisualBlock => true,
103 Self::Normal | Self::Insert | Self::Replace | Self::HelixNormal => false,
104 }
105 }
106}
107
108impl Default for ModalMode {
109 fn default() -> Self {
110 Self::Normal
111 }
112}
113
114impl Settings for EditorModeSetting {
115 const KEY: Option<&'static str> = Some("editor_mode");
116
117 type FileContent = Option<EditorMode>;
118
119 fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
120 Ok(Self(
121 sources
122 .user
123 .or(sources.server)
124 .copied()
125 .flatten()
126 .unwrap_or(sources.default.ok_or_else(Self::missing_default)?),
127 ))
128 }
129
130 fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {
131 // TODO: could possibly check if any of the `vim.<foo>` keys are set?
132 }
133}
134
135impl EditorMode {
136 pub fn is_modal(&self) -> bool {
137 matches!(self, EditorMode::Vim(_) | EditorMode::Helix(_))
138 }
139}