diff --git a/Cargo.lock b/Cargo.lock index 2b3d7b2691..30e76ca131 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17993,6 +17993,8 @@ version = "0.1.0" dependencies = [ "anyhow", "gpui", + "schemars", + "serde", "settings", "workspace-hack", ] diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index fd504764b6..afa896cdaf 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -27,7 +27,7 @@ use ui::{ CheckboxWithLabel, Chip, ContextMenu, PopoverMenu, ScrollableHandle, Scrollbar, ScrollbarState, ToggleButton, Tooltip, prelude::*, }; -use vim_mode_setting::VimModeSetting; +use vim_mode_setting::{EditorMode, EditorModeSetting}; use workspace::{ Workspace, WorkspaceId, item::{Item, ItemEvent}, @@ -1335,17 +1335,26 @@ impl ExtensionsPage { .child(CheckboxWithLabel::new( "enable-vim", Label::new("Enable vim mode"), - if VimModeSetting::get_global(cx).0 { - ui::ToggleState::Selected - } else { - ui::ToggleState::Unselected + { + let editor_mode = EditorModeSetting::get_global(cx).0; + if matches!(editor_mode, EditorMode::Vim | EditorMode::Helix) { + ui::ToggleState::Selected + } else { + ui::ToggleState::Unselected + } }, cx.listener(move |this, selection, _, cx| { telemetry::event!("Vim Mode Toggled", source = "Feature Upsell"); - this.update_settings::( + this.update_settings::( selection, cx, - |setting, value| *setting = Some(value), + |setting, value| { + *setting = Some(if value { + EditorMode::Vim + } else { + EditorMode::Default + }); + }, ); }), )), diff --git a/crates/onboarding/src/basics_page.rs b/crates/onboarding/src/basics_page.rs index 441d2ca4b7..30c1b5d3f7 100644 --- a/crates/onboarding/src/basics_page.rs +++ b/crates/onboarding/src/basics_page.rs @@ -12,7 +12,7 @@ use ui::{ ParentElement as _, StatefulInteractiveElement, SwitchField, ToggleButtonGroup, ToggleButtonSimple, ToggleButtonWithIcon, prelude::*, rems_from_px, }; -use vim_mode_setting::VimModeSetting; +use vim_mode_setting::{EditorMode, EditorModeSetting}; use crate::theme_preview::{ThemePreviewStyle, ThemePreviewTile}; @@ -331,11 +331,13 @@ fn render_base_keymap_section(tab_index: &mut isize, cx: &mut App) -> impl IntoE } fn render_vim_mode_switch(tab_index: &mut isize, cx: &mut App) -> impl IntoElement { - let toggle_state = if VimModeSetting::get_global(cx).0 { + let editor_mode = EditorModeSetting::get_global(cx).0; + let toggle_state = if matches!(editor_mode, EditorMode::Vim | EditorMode::Helix) { ui::ToggleState::Selected } else { ui::ToggleState::Unselected }; + SwitchField::new( "onboarding-vim-mode", "Vim Mode", @@ -344,10 +346,10 @@ fn render_vim_mode_switch(tab_index: &mut isize, cx: &mut App) -> impl IntoEleme { let fs = ::global(cx); move |&selection, _, cx| { - update_settings_file::(fs.clone(), cx, move |setting, _| { + update_settings_file::(fs.clone(), cx, move |setting, _| { *setting = match selection { - ToggleState::Selected => Some(true), - ToggleState::Unselected => Some(false), + ToggleState::Selected => Some(EditorMode::Vim), + ToggleState::Unselected => Some(EditorMode::Default), ToggleState::Indeterminate => None, } }); diff --git a/crates/vim/src/insert.rs b/crates/vim/src/insert.rs index 8ef1cd7811..3e9b9eb63f 100644 --- a/crates/vim/src/insert.rs +++ b/crates/vim/src/insert.rs @@ -4,7 +4,7 @@ use gpui::{Action, Context, Window, actions}; use language::SelectionGoal; use settings::Settings; use text::Point; -use vim_mode_setting::HelixModeSetting; +use vim_mode_setting::{EditorMode, EditorModeSetting}; use workspace::searchable::Direction; actions!( @@ -53,7 +53,7 @@ impl Vim { self.update_editor(cx, |_, editor, cx| { editor.dismiss_menus_and_popups(false, window, cx); - if !HelixModeSetting::get_global(cx).0 { + if EditorModeSetting::get_global(cx).0 != EditorMode::Helix { editor.change_selections(Default::default(), window, cx, |s| { s.move_cursors_with(|map, mut cursor, _| { *cursor.column_mut() = cursor.column().saturating_sub(1); @@ -63,7 +63,7 @@ impl Vim { } }); - if HelixModeSetting::get_global(cx).0 { + if EditorModeSetting::get_global(cx).0 == EditorMode::Helix { self.switch_mode(Mode::HelixNormal, false, window, cx); } else { self.switch_mode(Mode::Normal, false, window, cx); diff --git a/crates/vim/src/normal/paste.rs b/crates/vim/src/normal/paste.rs index 933b119d37..be74c209dc 100644 --- a/crates/vim/src/normal/paste.rs +++ b/crates/vim/src/normal/paste.rs @@ -5,7 +5,7 @@ use schemars::JsonSchema; use serde::Deserialize; use settings::Settings; use std::cmp; -use vim_mode_setting::HelixModeSetting; +use vim_mode_setting::{EditorMode, EditorModeSetting}; use crate::{ Vim, @@ -220,7 +220,7 @@ impl Vim { }); }); - if HelixModeSetting::get_global(cx).0 { + if EditorModeSetting::get_global(cx).0 == EditorMode::Helix { self.switch_mode(Mode::HelixNormal, true, window, cx); } else { self.switch_mode(Mode::Normal, true, window, cx); diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index e7ac692df1..b7adcccf55 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -64,7 +64,13 @@ impl VimTestContext { pub fn init_keybindings(enabled: bool, cx: &mut App) { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, |s| *s = Some(enabled)); + store.update_user_settings::(cx, |s| { + *s = Some(if enabled { + EditorMode::Vim + } else { + EditorMode::Default + }) + }); }); let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure( "keymaps/default-macos.json", @@ -130,7 +136,7 @@ impl VimTestContext { pub fn enable_vim(&mut self) { self.cx.update(|_, cx| { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, |s| *s = Some(true)); + store.update_user_settings::(cx, |s| *s = Some(EditorMode::Vim)); }); }) } @@ -138,7 +144,7 @@ impl VimTestContext { pub fn disable_vim(&mut self) { self.cx.update(|_, cx| { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, |s| *s = Some(false)); + store.update_user_settings::(cx, |s| *s = Some(EditorMode::Vim)); }); }) } @@ -146,8 +152,8 @@ impl VimTestContext { pub fn enable_helix(&mut self) { self.cx.update(|_, cx| { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, |s| { - *s = Some(true) + store.update_user_settings::(cx, |s| { + *s = Some(EditorMode::Helix) }); }); }) diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 9da01e6f44..46325dfa36 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -45,8 +45,7 @@ use std::{mem, ops::Range, sync::Arc}; use surrounds::SurroundsType; use theme::ThemeSettings; use ui::{IntoElement, SharedString, px}; -use vim_mode_setting::HelixModeSetting; -use vim_mode_setting::VimModeSetting; +use vim_mode_setting::{EditorMode, EditorModeSetting}; use workspace::{self, Pane, Workspace}; use crate::state::ReplayableAction; @@ -246,8 +245,12 @@ pub fn init(cx: &mut App) { workspace.register_action(|workspace, _: &ToggleVimMode, _, cx| { let fs = workspace.app_state().fs.clone(); let currently_enabled = Vim::enabled(cx); - update_settings_file::(fs, cx, move |setting, _| { - *setting = Some(!currently_enabled) + update_settings_file::(fs, cx, move |setting, _| { + *setting = Some(if currently_enabled { + EditorMode::Default + } else { + EditorMode::Vim + }); }) }); @@ -405,7 +408,9 @@ impl Vim { let editor = cx.entity(); let mut initial_mode = VimSettings::get_global(cx).default_mode; - if initial_mode == Mode::Normal && HelixModeSetting::get_global(cx).0 { + if initial_mode == Mode::Normal + && matches!(EditorModeSetting::get_global(cx).0, EditorMode::Helix) + { initial_mode = Mode::HelixNormal; } @@ -496,7 +501,7 @@ impl Vim { vim.update(cx, |_, cx| { Vim::action(editor, cx, |vim, _: &SwitchToNormalMode, window, cx| { - if HelixModeSetting::get_global(cx).0 { + if matches!(EditorModeSetting::get_global(cx).0, EditorMode::Helix) { vim.switch_mode(Mode::HelixNormal, false, window, cx) } else { vim.switch_mode(Mode::Normal, false, window, cx) @@ -819,7 +824,11 @@ impl Vim { } pub fn enabled(cx: &mut App) -> bool { - VimModeSetting::get_global(cx).0 || HelixModeSetting::get_global(cx).0 + if EditorModeSetting::get_global(cx).0 == EditorMode::Default { + return false; + } + return true; + // VimModeSetting::get_global(cx).0 || HelixModeSetting::get_global(cx).0 } /// Called whenever an keystroke is typed so vim can observe all actions diff --git a/crates/vim_mode_setting/Cargo.toml b/crates/vim_mode_setting/Cargo.toml index fbb7f30b4c..ef4fde738f 100644 --- a/crates/vim_mode_setting/Cargo.toml +++ b/crates/vim_mode_setting/Cargo.toml @@ -14,5 +14,7 @@ path = "src/vim_mode_setting.rs" [dependencies] anyhow.workspace = true gpui.workspace = true +schemars.workspace = true settings.workspace = true workspace-hack.workspace = true +serde.workspace = true diff --git a/crates/vim_mode_setting/src/vim_mode_setting.rs b/crates/vim_mode_setting/src/vim_mode_setting.rs index 6f60d3f21f..e9606419df 100644 --- a/crates/vim_mode_setting/src/vim_mode_setting.rs +++ b/crates/vim_mode_setting/src/vim_mode_setting.rs @@ -6,23 +6,32 @@ use anyhow::Result; use gpui::App; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; /// Initializes the `vim_mode_setting` crate. pub fn init(cx: &mut App) { - VimModeSetting::register(cx); - HelixModeSetting::register(cx); + EditorModeSetting::register(cx); } /// Whether or not to enable Vim mode. /// -/// Default: false -pub struct VimModeSetting(pub bool); +/// Default: `EditMode::Default` +pub struct EditorModeSetting(pub EditorMode); -impl Settings for VimModeSetting { - const KEY: Option<&'static str> = Some("vim_mode"); +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)] +pub enum EditorMode { + Vim, + Helix, + #[default] + Default, +} - type FileContent = Option; +impl Settings for EditorModeSetting { + const KEY: Option<&'static str> = Some("editor_mode"); + + type FileContent = Option; fn load(sources: SettingsSources, _: &mut App) -> Result { Ok(Self( @@ -39,29 +48,3 @@ impl Settings for VimModeSetting { // TODO: could possibly check if any of the `vim.` keys are set? } } - -/// Whether or not to enable Helix mode. -/// -/// Default: false -pub struct HelixModeSetting(pub bool); - -impl Settings for HelixModeSetting { - const KEY: Option<&'static str> = Some("helix_mode"); - - type FileContent = Option; - - fn load(sources: SettingsSources, _: &mut App) -> Result { - Ok(Self( - sources - .user - .or(sources.server) - .copied() - .flatten() - .unwrap_or(sources.default.ok_or_else(Self::missing_default)?), - )) - } - - fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) { - // TODO: could possibly check if any of the `helix.` keys are set? - } -} diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 638e1dca0e..d955fd11a8 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -70,7 +70,7 @@ use ui::{PopoverMenuHandle, prelude::*}; use util::markdown::MarkdownString; use util::{ResultExt, asset_str}; use uuid::Uuid; -use vim_mode_setting::VimModeSetting; +use vim_mode_setting::{EditorMode, EditorModeSetting}; use workspace::notifications::{ NotificationId, SuppressEvent, dismiss_app_notification, show_app_notification, }; @@ -1287,21 +1287,15 @@ pub fn handle_keymap_file_changes( let (base_keymap_tx, mut base_keymap_rx) = mpsc::unbounded(); let (keyboard_layout_tx, mut keyboard_layout_rx) = mpsc::unbounded(); let mut old_base_keymap = *BaseKeymap::get_global(cx); - let mut old_vim_enabled = VimModeSetting::get_global(cx).0; - let mut old_helix_enabled = vim_mode_setting::HelixModeSetting::get_global(cx).0; + let mut old_editor_mode = EditorModeSetting::get_global(cx).0; cx.observe_global::(move |cx| { let new_base_keymap = *BaseKeymap::get_global(cx); - let new_vim_enabled = VimModeSetting::get_global(cx).0; - let new_helix_enabled = vim_mode_setting::HelixModeSetting::get_global(cx).0; + let new_editor_mode = EditorModeSetting::get_global(cx).0; - if new_base_keymap != old_base_keymap - || new_vim_enabled != old_vim_enabled - || new_helix_enabled != old_helix_enabled - { + if new_base_keymap != old_base_keymap || new_editor_mode != old_editor_mode { old_base_keymap = new_base_keymap; - old_vim_enabled = new_vim_enabled; - old_helix_enabled = new_helix_enabled; + old_editor_mode = new_editor_mode; base_keymap_tx.unbounded_send(()).unwrap(); } @@ -1499,7 +1493,10 @@ pub fn load_default_keymap(cx: &mut App) { cx.bind_keys(KeymapFile::load_asset(asset_path, Some(KeybindSource::Base), cx).unwrap()); } - if VimModeSetting::get_global(cx).0 || vim_mode_setting::HelixModeSetting::get_global(cx).0 { + if matches!( + EditorModeSetting::get_global(cx).0, + EditorMode::Vim | EditorMode::Helix + ) { cx.bind_keys( KeymapFile::load_asset(VIM_KEYMAP_PATH, Some(KeybindSource::Vim), cx).unwrap(), ); diff --git a/crates/zed/src/zed/quick_action_bar.rs b/crates/zed/src/zed/quick_action_bar.rs index e57d5d3889..fee96727ea 100644 --- a/crates/zed/src/zed/quick_action_bar.rs +++ b/crates/zed/src/zed/quick_action_bar.rs @@ -23,7 +23,7 @@ use ui::{ ButtonStyle, ContextMenu, ContextMenuEntry, DocumentationSide, IconButton, IconName, IconSize, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*, }; -use vim_mode_setting::VimModeSetting; +use vim_mode_setting::{EditorMode, EditorModeSetting}; use workspace::{ ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, item::ItemHandle, }; @@ -301,7 +301,8 @@ impl Render for QuickActionBar { let editor_focus_handle = editor.focus_handle(cx); let editor = editor.downgrade(); let editor_settings_dropdown = { - let vim_mode_enabled = VimModeSetting::get_global(cx).0; + let editor_mode = EditorModeSetting::get_global(cx).0; + let vim_mode_enabled = matches!(editor_mode, EditorMode::Vim | EditorMode::Helix); PopoverMenu::new("editor-settings") .trigger_with_tooltip( @@ -576,8 +577,12 @@ impl Render for QuickActionBar { None, { move |window, cx| { - let new_value = !vim_mode_enabled; - VimModeSetting::override_global(VimModeSetting(new_value), cx); + let new_value = if vim_mode_enabled { + EditorMode::Default + } else { + EditorMode::Vim + }; + EditorModeSetting::override_global(EditorModeSetting(new_value), cx); window.refresh(); } },