Detailed changes
@@ -70,7 +70,7 @@ use scroll::{
};
use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
use serde::{Deserialize, Serialize};
-use settings::Settings;
+use settings::{Settings, SettingsStore};
use smallvec::SmallVec;
use snippet::Snippet;
use std::{
@@ -6868,6 +6868,12 @@ impl Editor {
.as_singleton()
.and_then(|b| b.read(cx).file()),
) {
+ let vim_mode = cx
+ .global::<SettingsStore>()
+ .untyped_user_settings()
+ .get("vim_mode")
+ == Some(&serde_json::Value::Bool(true));
+
let settings = cx.global::<Settings>();
let extension = Path::new(file.file_name(cx))
@@ -6880,12 +6886,12 @@ impl Editor {
"save" => "save editor",
_ => name,
},
- json!({ "File Extension": extension, "Vim Mode": settings.vim_mode, "In Clickhouse": true }),
+ json!({ "File Extension": extension, "Vim Mode": vim_mode, "In Clickhouse": true }),
settings.telemetry(),
);
let event = ClickhouseEvent::Editor {
file_extension: extension.map(ToString::to_string),
- vim_mode: settings.vim_mode,
+ vim_mode,
operation: name,
copilot_enabled: settings.features.copilot,
copilot_enabled_for_language: settings.show_copilot_suggestions(
@@ -43,7 +43,6 @@ pub struct Settings {
pub hover_popover_enabled: bool,
pub show_completions_on_input: bool,
pub show_call_status_icon: bool,
- pub vim_mode: bool,
pub autosave: Autosave,
pub default_dock_anchor: DockAnchor,
pub editor_defaults: EditorSettings,
@@ -65,6 +64,8 @@ pub struct Settings {
}
impl Setting for Settings {
+ const KEY: Option<&'static str> = None;
+
type FileContent = SettingsFileContent;
fn load(
@@ -93,7 +94,6 @@ impl Setting for Settings {
hover_popover_enabled: defaults.hover_popover_enabled.unwrap(),
show_completions_on_input: defaults.show_completions_on_input.unwrap(),
show_call_status_icon: defaults.show_call_status_icon.unwrap(),
- vim_mode: defaults.vim_mode.unwrap(),
autosave: defaults.autosave.unwrap(),
default_dock_anchor: defaults.default_dock_anchor.unwrap(),
editor_defaults: EditorSettings {
@@ -550,8 +550,6 @@ pub struct SettingsFileContent {
#[serde(default)]
pub show_call_status_icon: Option<bool>,
#[serde(default)]
- pub vim_mode: Option<bool>,
- #[serde(default)]
pub autosave: Option<Autosave>,
#[serde(default)]
pub default_dock_anchor: Option<DockAnchor>,
@@ -647,7 +645,6 @@ impl Settings {
hover_popover_enabled: defaults.hover_popover_enabled.unwrap(),
show_completions_on_input: defaults.show_completions_on_input.unwrap(),
show_call_status_icon: defaults.show_call_status_icon.unwrap(),
- vim_mode: defaults.vim_mode.unwrap(),
autosave: defaults.autosave.unwrap(),
default_dock_anchor: defaults.default_dock_anchor.unwrap(),
editor_defaults: EditorSettings {
@@ -741,7 +738,6 @@ impl Settings {
&mut self.show_completions_on_input,
data.show_completions_on_input,
);
- merge(&mut self.vim_mode, data.vim_mode);
merge(&mut self.autosave, data.autosave);
merge(&mut self.default_dock_anchor, data.default_dock_anchor);
merge(&mut self.base_keymap, data.base_keymap);
@@ -940,7 +936,6 @@ impl Settings {
hover_popover_enabled: true,
show_completions_on_input: true,
show_call_status_icon: true,
- vim_mode: false,
autosave: Autosave::Off,
default_dock_anchor: DockAnchor::Bottom,
editor_defaults: EditorSettings {
@@ -23,7 +23,7 @@ pub trait Setting: 'static {
/// The name of a key within the JSON file from which this setting should
/// be deserialized. If this is `None`, then the setting will be deserialized
/// from the root object.
- const KEY: Option<&'static str> = None;
+ const KEY: Option<&'static str>;
/// The type that is stored in an individual JSON file.
type FileContent: Clone + Serialize + DeserializeOwned + JsonSchema;
@@ -165,6 +165,28 @@ impl SettingsStore {
.expect("no default value for setting type")
}
+ /// Get the user's settings as a raw JSON value.
+ ///
+ /// This is only for debugging and reporting. For user-facing functionality,
+ /// use the typed setting interface.
+ pub fn untyped_user_settings(&self) -> &serde_json::Value {
+ self.user_deserialized_settings
+ .as_ref()
+ .map_or(&serde_json::Value::Null, |s| &s.untyped)
+ }
+
+ /// Override the global value for a particular setting.
+ ///
+ /// This is only for tests. Normally, settings are only loaded from
+ /// JSON files.
+ #[cfg(any(test, feature = "test-support"))]
+ pub fn replace_value<T: Setting>(&mut self, value: T) {
+ self.setting_values
+ .get_mut(&TypeId::of::<T>())
+ .expect("unregistered setting type")
+ .set_global_value(Box::new(value))
+ }
+
/// Update the value of a setting.
///
/// Returns a list of edits to apply to the JSON file.
@@ -1164,6 +1186,8 @@ mod tests {
}
impl Setting for MultiKeySettings {
+ const KEY: Option<&'static str> = None;
+
type FileContent = MultiKeySettingsJson;
fn load(
@@ -18,8 +18,8 @@ impl<'a> VimTestContext<'a> {
pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> VimTestContext<'a> {
let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
cx.update(|cx| {
- cx.update_global(|settings: &mut Settings, _| {
- settings.vim_mode = enabled;
+ cx.update_global(|store: &mut SettingsStore, _| {
+ store.replace_value(VimModeSetting(enabled));
});
search::init(cx);
crate::init(cx);
@@ -52,16 +52,16 @@ impl<'a> VimTestContext<'a> {
pub fn enable_vim(&mut self) {
self.cx.update(|cx| {
- cx.update_global(|settings: &mut Settings, _| {
- settings.vim_mode = true;
+ cx.update_global(|store: &mut SettingsStore, _| {
+ store.replace_value(VimModeSetting(true))
});
})
}
pub fn disable_vim(&mut self) {
self.cx.update(|cx| {
- cx.update_global(|settings: &mut Settings, _| {
- settings.vim_mode = false;
+ cx.update_global(|store: &mut SettingsStore, _| {
+ store.replace_value(VimModeSetting(false))
});
})
}
@@ -22,11 +22,13 @@ use language::CursorShape;
use motion::Motion;
use normal::normal_replace;
use serde::Deserialize;
-use settings::Settings;
+use settings::{Setting, SettingsStore};
use state::{Mode, Operator, VimState};
use visual::visual_replace;
use workspace::{self, Workspace};
+struct VimModeSetting(bool);
+
#[derive(Clone, Deserialize, PartialEq)]
pub struct SwitchMode(pub Mode);
@@ -40,6 +42,8 @@ actions!(vim, [Tab, Enter]);
impl_actions!(vim, [Number, SwitchMode, PushOperator]);
pub fn init(cx: &mut AppContext) {
+ settings::register_setting::<VimModeSetting>(cx);
+
editor_events::init(cx);
normal::init(cx);
visual::init(cx);
@@ -91,11 +95,11 @@ pub fn init(cx: &mut AppContext) {
filter.filtered_namespaces.insert("vim");
});
cx.update_default_global(|vim: &mut Vim, cx: &mut AppContext| {
- vim.set_enabled(cx.global::<Settings>().vim_mode, cx)
+ vim.set_enabled(settings::get_setting::<VimModeSetting>(None, cx).0, cx)
});
- cx.observe_global::<Settings, _>(|cx| {
+ cx.observe_global::<SettingsStore, _>(|cx| {
cx.update_default_global(|vim: &mut Vim, cx: &mut AppContext| {
- vim.set_enabled(cx.global::<Settings>().vim_mode, cx)
+ vim.set_enabled(settings::get_setting::<VimModeSetting>(None, cx).0, cx)
});
})
.detach();
@@ -330,6 +334,26 @@ impl Vim {
}
}
+impl Setting for VimModeSetting {
+ const KEY: Option<&'static str> = Some("vim_mode");
+
+ type FileContent = Option<bool>;
+
+ fn load(
+ default_value: &Self::FileContent,
+ user_values: &[&Self::FileContent],
+ _: &AppContext,
+ ) -> Self {
+ Self(
+ user_values
+ .first()
+ .map(|e| **e)
+ .flatten()
+ .unwrap_or(default_value.unwrap()),
+ )
+ }
+}
+
fn local_selections_changed(newest_empty: bool, cx: &mut WindowContext) {
Vim::update(cx, |vim, cx| {
if vim.enabled && vim.state.mode == Mode::Normal && !newest_empty {