editor_mode_setting.rs

  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, Schema, json_schema};
 10
 11use serde::de::Error;
 12use serde::{Deserialize, Deserializer, Serialize, Serializer};
 13use settings::{Settings, SettingsSources};
 14use std::borrow::Cow;
 15use std::fmt::Display;
 16
 17/// Initializes the `editor_mode_setting` crate.
 18pub fn init(cx: &mut App) {
 19    EditorModeSetting::register(cx);
 20}
 21
 22/// Whether or not to enable Vim mode.
 23///
 24/// Default: `EditMode::Default`
 25pub struct EditorModeSetting(pub EditorMode);
 26
 27#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
 28pub enum EditorMode {
 29    #[default]
 30    Default,
 31    Vim(ModalMode),
 32    Helix(ModalMode),
 33}
 34
 35impl JsonSchema for EditorMode {
 36    fn schema_name() -> Cow<'static, str> {
 37        "EditorMode".into()
 38    }
 39
 40    fn json_schema(_gen: &mut schemars::SchemaGenerator) -> Schema {
 41        let options = Self::get_schema_options();
 42        json_schema!({
 43            "oneOf": options,
 44            "description":  "Editor mode"
 45        })
 46    }
 47}
 48
 49impl<'de> Deserialize<'de> for EditorMode {
 50    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
 51    where
 52        D: Deserializer<'de>,
 53    {
 54        let s = String::deserialize(deserializer)?;
 55        match s.as_str() {
 56            "default" => Ok(EditorMode::Default),
 57            "vim" => Ok(EditorMode::Vim(ModalMode::Normal)),
 58            "vim_normal" => Ok(EditorMode::Vim(ModalMode::Normal)),
 59            "vim_insert" => Ok(EditorMode::Vim(ModalMode::Insert)),
 60            "vim_replace" => Ok(EditorMode::Vim(ModalMode::Replace)),
 61            "vim_visual" => Ok(EditorMode::Vim(ModalMode::Visual)),
 62            "vim_visual_line" => Ok(EditorMode::Vim(ModalMode::VisualLine)),
 63            "vim_visual_block" => Ok(EditorMode::Vim(ModalMode::VisualBlock)),
 64            "helix_experimental" => Ok(EditorMode::Helix(ModalMode::HelixNormal)),
 65            _ => Err(D::Error::custom(format!("Unknown editor mode: {}", s))),
 66        }
 67    }
 68}
 69
 70impl Serialize for EditorMode {
 71    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 72    where
 73        S: Serializer,
 74    {
 75        let s = match self {
 76            EditorMode::Default => "default",
 77            EditorMode::Vim(ModalMode::Normal) => "vim",
 78            EditorMode::Vim(ModalMode::Insert) => "vim_insert",
 79            EditorMode::Vim(ModalMode::Replace) => "vim_replace",
 80            EditorMode::Vim(ModalMode::Visual) => "vim_visual",
 81            EditorMode::Vim(ModalMode::VisualLine) => "vim_visual_line",
 82            EditorMode::Vim(ModalMode::VisualBlock) => "vim_visual_block",
 83            EditorMode::Helix(ModalMode::HelixNormal) => "helix_experimental",
 84            _ => return Err(serde::ser::Error::custom("unsupported editor mode variant")),
 85        };
 86        serializer.serialize_str(s)
 87    }
 88}
 89
 90#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
 91pub enum ModalMode {
 92    Normal,
 93    Insert,
 94    Replace,
 95    Visual,
 96    VisualLine,
 97    VisualBlock,
 98    HelixNormal,
 99}
100
101impl Display for ModalMode {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        match self {
104            ModalMode::Normal => write!(f, "NORMAL"),
105            ModalMode::Insert => write!(f, "INSERT"),
106            ModalMode::Replace => write!(f, "REPLACE"),
107            ModalMode::Visual => write!(f, "VISUAL"),
108            ModalMode::VisualLine => write!(f, "VISUAL LINE"),
109            ModalMode::VisualBlock => write!(f, "VISUAL BLOCK"),
110            ModalMode::HelixNormal => write!(f, "HELIX NORMAL"),
111        }
112    }
113}
114
115impl ModalMode {
116    pub fn is_visual(&self) -> bool {
117        match self {
118            Self::Visual | Self::VisualLine | Self::VisualBlock => true,
119            Self::Normal | Self::Insert | Self::Replace | Self::HelixNormal => false,
120        }
121    }
122}
123
124impl Default for ModalMode {
125    fn default() -> Self {
126        Self::Normal
127    }
128}
129
130impl Settings for EditorModeSetting {
131    const KEY: Option<&'static str> = Some("editor_mode");
132
133    type FileContent = Option<EditorMode>;
134
135    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
136        Ok(Self(
137            sources
138                .user
139                .or(sources.server)
140                .copied()
141                .flatten()
142                .unwrap_or(sources.default.ok_or_else(Self::missing_default)?),
143        ))
144    }
145
146    fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {
147        // TODO: could possibly check if any of the `vim.<foo>` keys are set?
148    }
149}
150
151impl EditorMode {
152    pub fn is_modal(&self) -> bool {
153        matches!(self, EditorMode::Vim(_) | EditorMode::Helix(_))
154    }
155
156    pub fn vim() -> EditorMode {
157        EditorMode::Vim(ModalMode::default())
158    }
159
160    pub fn get_schema_options() -> Vec<serde_json::Value> {
161        vec![
162            serde_json::json!({
163                "const": "default",
164                "description": "Standard editing mode"
165            }),
166            serde_json::json!({
167                "const": "vim",
168                "description": "Vim normal mode"
169            }),
170            serde_json::json!({
171                "const": "vim_normal",
172                "description": "Vim normal mode"
173            }),
174            serde_json::json!({
175                "const": "vim_insert",
176                "description": "Vim insert mode"
177            }),
178            serde_json::json!({
179                "const": "vim_replace",
180                "description": "Vim replace mode"
181            }),
182            serde_json::json!({
183                "const": "vim_visual",
184                "description": "Vim visual mode"
185            }),
186            serde_json::json!({
187                "const": "vim_visual_line",
188                "description": "Vim visual line mode"
189            }),
190            serde_json::json!({
191                "const": "vim_visual_block",
192                "description": "Vim visual block mode"
193            }),
194            serde_json::json!({
195                "const": "helix_experimental",
196                "description": "Helix mode (experimental)"
197            }),
198        ]
199    }
200}