global editor mode setting

Co-authored-by: Oleksiy Syvokon <oleksiy.syvokon@gmail.com>
This commit is contained in:
Smit Barmase 2025-08-21 21:28:04 +05:30
parent 27a26d53b1
commit 0d5becfadf
No known key found for this signature in database
11 changed files with 93 additions and 78 deletions

2
Cargo.lock generated
View file

@ -17993,6 +17993,8 @@ version = "0.1.0"
dependencies = [
"anyhow",
"gpui",
"schemars",
"serde",
"settings",
"workspace-hack",
]

View file

@ -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::<VimModeSetting>(
this.update_settings::<EditorModeSetting>(
selection,
cx,
|setting, value| *setting = Some(value),
|setting, value| {
*setting = Some(if value {
EditorMode::Vim
} else {
EditorMode::Default
});
},
);
}),
)),

View file

@ -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 = <dyn Fs>::global(cx);
move |&selection, _, cx| {
update_settings_file::<VimModeSetting>(fs.clone(), cx, move |setting, _| {
update_settings_file::<EditorModeSetting>(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,
}
});

View file

@ -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);

View file

@ -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);

View file

@ -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::<VimModeSetting>(cx, |s| *s = Some(enabled));
store.update_user_settings::<EditorModeSetting>(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::<VimModeSetting>(cx, |s| *s = Some(true));
store.update_user_settings::<EditorModeSetting>(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::<VimModeSetting>(cx, |s| *s = Some(false));
store.update_user_settings::<EditorModeSetting>(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::<vim_mode_setting::HelixModeSetting>(cx, |s| {
*s = Some(true)
store.update_user_settings::<vim_mode_setting::EditorModeSetting>(cx, |s| {
*s = Some(EditorMode::Helix)
});
});
})

View file

@ -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::<VimModeSetting>(fs, cx, move |setting, _| {
*setting = Some(!currently_enabled)
update_settings_file::<EditorModeSetting>(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

View file

@ -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

View file

@ -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<bool>;
impl Settings for EditorModeSetting {
const KEY: Option<&'static str> = Some("editor_mode");
type FileContent = Option<EditorMode>;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
Ok(Self(
@ -39,29 +48,3 @@ impl Settings for VimModeSetting {
// TODO: could possibly check if any of the `vim.<foo>` 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<bool>;
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
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.<foo>` keys are set?
}
}

View file

@ -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::<SettingsStore>(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(),
);

View file

@ -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();
}
},