Define editor settings in editor crate

Max Brunsfeld created

Change summary

Cargo.lock                                   |  1 
crates/client/src/client.rs                  |  8 ++
crates/collab/src/tests.rs                   | 10 +--
crates/collab/src/tests/integration_tests.rs | 45 ++++++++++++---------
crates/editor/src/blink_manager.rs           |  6 +-
crates/editor/src/editor.rs                  |  9 +++
crates/editor/src/editor_settings.rs         | 34 ++++++++++++++++
crates/editor/src/editor_tests.rs            |  6 +
crates/editor/src/hover_popover.rs           |  6 +-
crates/project_panel/Cargo.toml              |  1 
crates/project_panel/src/project_panel.rs    |  1 
crates/search/src/project_search.rs          |  2 
crates/settings/src/settings.rs              | 18 --------
13 files changed, 93 insertions(+), 54 deletions(-)

Detailed changes

Cargo.lock πŸ”—

@@ -4873,6 +4873,7 @@ dependencies = [
 name = "project_panel"
 version = "0.1.0"
 dependencies = [
+ "client",
  "context_menu",
  "drag_and_drop",
  "editor",

crates/client/src/client.rs πŸ”—

@@ -70,10 +70,14 @@ pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5);
 
 actions!(client, [SignIn, SignOut]);
 
-pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
-    let client = Arc::downgrade(client);
+pub fn init_settings(cx: &mut AppContext) {
     settings::register_setting::<TelemetrySettings>(cx);
+}
 
+pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
+    init_settings(cx);
+
+    let client = Arc::downgrade(client);
     cx.add_global_action({
         let client = client.clone();
         move |_: &SignIn, cx| {

crates/collab/src/tests.rs πŸ”—

@@ -186,11 +186,6 @@ impl TestServer {
                 })
             });
 
-        cx.update(|cx| {
-            client::init(&client, cx);
-            language::init(cx);
-        });
-
         let fs = FakeFs::new(cx.background());
         let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
         let app_state = Arc::new(workspace::AppState {
@@ -205,8 +200,11 @@ impl TestServer {
             background_actions: || &[],
         });
 
-        Project::init(&client);
         cx.update(|cx| {
+            Project::init(&client);
+            client::init(&client, cx);
+            language::init(cx);
+            editor::init_settings(cx);
             workspace::init(app_state.clone(), cx);
             call::init(client.clone(), user_store.clone(), cx);
         });

crates/collab/src/tests/integration_tests.rs πŸ”—

@@ -27,7 +27,7 @@ use lsp::LanguageServerId;
 use project::{search::SearchQuery, DiagnosticSummary, HoverBlockKind, Project, ProjectPath};
 use rand::prelude::*;
 use serde_json::json;
-use settings::{SettingsStore};
+use settings::SettingsStore;
 use std::{
     cell::{Cell, RefCell},
     env, future, mem,
@@ -1439,7 +1439,6 @@ async fn test_host_disconnect(
     cx_b: &mut TestAppContext,
     cx_c: &mut TestAppContext,
 ) {
-    cx_b.update(editor::init);
     deterministic.forbid_parking();
     let mut server = TestServer::start(&deterministic).await;
     let client_a = server.create_client(cx_a, "user_a").await;
@@ -1449,6 +1448,8 @@ async fn test_host_disconnect(
         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
         .await;
 
+    cx_b.update(editor::init);
+
     client_a
         .fs
         .insert_tree(
@@ -1546,7 +1547,6 @@ async fn test_project_reconnect(
     cx_a: &mut TestAppContext,
     cx_b: &mut TestAppContext,
 ) {
-    cx_b.update(editor::init);
     deterministic.forbid_parking();
     let mut server = TestServer::start(&deterministic).await;
     let client_a = server.create_client(cx_a, "user_a").await;
@@ -1555,6 +1555,8 @@ async fn test_project_reconnect(
         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
         .await;
 
+    cx_b.update(editor::init);
+
     client_a
         .fs
         .insert_tree(
@@ -4992,7 +4994,6 @@ async fn test_collaborating_with_code_actions(
     cx_b: &mut TestAppContext,
 ) {
     deterministic.forbid_parking();
-    cx_b.update(editor::init);
     let mut server = TestServer::start(&deterministic).await;
     let client_a = server.create_client(cx_a, "user_a").await;
     let client_b = server.create_client(cx_b, "user_b").await;
@@ -5001,6 +5002,8 @@ async fn test_collaborating_with_code_actions(
         .await;
     let active_call_a = cx_a.read(ActiveCall::global);
 
+    cx_b.update(editor::init);
+
     // Set up a fake language server.
     let mut language = Language::new(
         LanguageConfig {
@@ -5205,7 +5208,6 @@ async fn test_collaborating_with_renames(
     cx_b: &mut TestAppContext,
 ) {
     deterministic.forbid_parking();
-    cx_b.update(editor::init);
     let mut server = TestServer::start(&deterministic).await;
     let client_a = server.create_client(cx_a, "user_a").await;
     let client_b = server.create_client(cx_b, "user_b").await;
@@ -5214,6 +5216,8 @@ async fn test_collaborating_with_renames(
         .await;
     let active_call_a = cx_a.read(ActiveCall::global);
 
+    cx_b.update(editor::init);
+
     // Set up a fake language server.
     let mut language = Language::new(
         LanguageConfig {
@@ -5395,8 +5399,6 @@ async fn test_language_server_statuses(
     cx_b: &mut TestAppContext,
 ) {
     deterministic.forbid_parking();
-
-    cx_b.update(editor::init);
     let mut server = TestServer::start(&deterministic).await;
     let client_a = server.create_client(cx_a, "user_a").await;
     let client_b = server.create_client(cx_b, "user_b").await;
@@ -5405,6 +5407,8 @@ async fn test_language_server_statuses(
         .await;
     let active_call_a = cx_a.read(ActiveCall::global);
 
+    cx_b.update(editor::init);
+
     // Set up a fake language server.
     let mut language = Language::new(
         LanguageConfig {
@@ -6112,8 +6116,6 @@ async fn test_basic_following(
     cx_d: &mut TestAppContext,
 ) {
     deterministic.forbid_parking();
-    cx_a.update(editor::init);
-    cx_b.update(editor::init);
 
     let mut server = TestServer::start(&deterministic).await;
     let client_a = server.create_client(cx_a, "user_a").await;
@@ -6131,6 +6133,9 @@ async fn test_basic_following(
     let active_call_a = cx_a.read(ActiveCall::global);
     let active_call_b = cx_b.read(ActiveCall::global);
 
+    cx_a.update(editor::init);
+    cx_b.update(editor::init);
+
     client_a
         .fs
         .insert_tree(
@@ -6709,9 +6714,6 @@ async fn test_following_tab_order(
     cx_a: &mut TestAppContext,
     cx_b: &mut TestAppContext,
 ) {
-    cx_a.update(editor::init);
-    cx_b.update(editor::init);
-
     let mut server = TestServer::start(&deterministic).await;
     let client_a = server.create_client(cx_a, "user_a").await;
     let client_b = server.create_client(cx_b, "user_b").await;
@@ -6721,6 +6723,9 @@ async fn test_following_tab_order(
     let active_call_a = cx_a.read(ActiveCall::global);
     let active_call_b = cx_b.read(ActiveCall::global);
 
+    cx_a.update(editor::init);
+    cx_b.update(editor::init);
+
     client_a
         .fs
         .insert_tree(
@@ -6831,9 +6836,6 @@ async fn test_peers_following_each_other(
     cx_b: &mut TestAppContext,
 ) {
     deterministic.forbid_parking();
-    cx_a.update(editor::init);
-    cx_b.update(editor::init);
-
     let mut server = TestServer::start(&deterministic).await;
     let client_a = server.create_client(cx_a, "user_a").await;
     let client_b = server.create_client(cx_b, "user_b").await;
@@ -6843,6 +6845,9 @@ async fn test_peers_following_each_other(
     let active_call_a = cx_a.read(ActiveCall::global);
     let active_call_b = cx_b.read(ActiveCall::global);
 
+    cx_a.update(editor::init);
+    cx_b.update(editor::init);
+
     // Client A shares a project.
     client_a
         .fs
@@ -7002,8 +7007,6 @@ async fn test_auto_unfollowing(
     cx_b: &mut TestAppContext,
 ) {
     deterministic.forbid_parking();
-    cx_a.update(editor::init);
-    cx_b.update(editor::init);
 
     // 2 clients connect to a server.
     let mut server = TestServer::start(&deterministic).await;
@@ -7015,6 +7018,9 @@ async fn test_auto_unfollowing(
     let active_call_a = cx_a.read(ActiveCall::global);
     let active_call_b = cx_b.read(ActiveCall::global);
 
+    cx_a.update(editor::init);
+    cx_b.update(editor::init);
+
     // Client A shares a project.
     client_a
         .fs
@@ -7169,8 +7175,6 @@ async fn test_peers_simultaneously_following_each_other(
     cx_b: &mut TestAppContext,
 ) {
     deterministic.forbid_parking();
-    cx_a.update(editor::init);
-    cx_b.update(editor::init);
 
     let mut server = TestServer::start(&deterministic).await;
     let client_a = server.create_client(cx_a, "user_a").await;
@@ -7180,6 +7184,9 @@ async fn test_peers_simultaneously_following_each_other(
         .await;
     let active_call_a = cx_a.read(ActiveCall::global);
 
+    cx_a.update(editor::init);
+    cx_b.update(editor::init);
+
     client_a.fs.insert_tree("/a", json!({})).await;
     let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
     let workspace_a = client_a.build_workspace(&project_a, cx_a);
@@ -1,8 +1,8 @@
-use std::time::Duration;
-
+use crate::EditorSettings;
 use gpui::{Entity, ModelContext};
 use settings::Settings;
 use smol::Timer;
+use std::time::Duration;
 
 pub struct BlinkManager {
     blink_interval: Duration,
@@ -64,7 +64,7 @@ impl BlinkManager {
     }
 
     fn blink_cursors(&mut self, epoch: usize, cx: &mut ModelContext<Self>) {
-        if cx.global::<Settings>().cursor_blink {
+        if settings::get_setting::<EditorSettings>(None, cx).cursor_blink {
             if epoch == self.blink_epoch && self.enabled && !self.blinking_paused {
                 self.visible = !self.visible;
                 cx.notify();

crates/editor/src/editor.rs πŸ”—

@@ -1,5 +1,6 @@
 mod blink_manager;
 pub mod display_map;
+mod editor_settings;
 mod element;
 
 mod git;
@@ -28,6 +29,7 @@ use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
 use copilot::Copilot;
 pub use display_map::DisplayPoint;
 use display_map::*;
+pub use editor_settings::EditorSettings;
 pub use element::*;
 use futures::FutureExt;
 use fuzzy::{StringMatch, StringMatchCandidate};
@@ -287,7 +289,12 @@ pub enum Direction {
     Next,
 }
 
+pub fn init_settings(cx: &mut AppContext) {
+    settings::register_setting::<EditorSettings>(cx);
+}
+
 pub fn init(cx: &mut AppContext) {
+    init_settings(cx);
     cx.add_action(Editor::new_file);
     cx.add_action(Editor::cancel);
     cx.add_action(Editor::newline);
@@ -2354,7 +2361,7 @@ impl Editor {
     }
 
     fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
-        if !cx.global::<Settings>().show_completions_on_input {
+        if !settings::get_setting::<EditorSettings>(None, cx).show_completions_on_input {
             return;
         }
 

crates/editor/src/editor_settings.rs πŸ”—

@@ -0,0 +1,34 @@
+use schemars::JsonSchema;
+use serde::{Deserialize, Serialize};
+use settings::Setting;
+
+#[derive(Deserialize)]
+pub struct EditorSettings {
+    pub cursor_blink: bool,
+    pub hover_popover_enabled: bool,
+    pub show_completions_on_input: bool,
+}
+
+#[derive(Clone, Serialize, Deserialize, JsonSchema)]
+pub struct EditorSettingsContent {
+    pub cursor_blink: Option<bool>,
+    pub hover_popover_enabled: Option<bool>,
+    pub show_completions_on_input: Option<bool>,
+}
+
+impl Setting for EditorSettings {
+    const KEY: Option<&'static str> = None;
+
+    type FileContent = EditorSettingsContent;
+
+    fn load(
+        default_value: &Self::FileContent,
+        user_values: &[&Self::FileContent],
+        _: &gpui::AppContext,
+    ) -> anyhow::Result<Self>
+    where
+        Self: Sized,
+    {
+        Self::load_via_json_merge(default_value, user_values)
+    }
+}

crates/editor/src/editor_tests.rs πŸ”—

@@ -4697,8 +4697,10 @@ async fn test_completion(cx: &mut gpui::TestAppContext) {
     apply_additional_edits.await.unwrap();
 
     cx.update(|cx| {
-        cx.update_global::<Settings, _, _>(|settings, _| {
-            settings.show_completions_on_input = false;
+        cx.update_global::<SettingsStore, _, _>(|settings, cx| {
+            settings.update_user_settings::<EditorSettings>(cx, |settings| {
+                settings.show_completions_on_input = Some(false);
+            });
         })
     });
     cx.set_state("editorˇ");

crates/editor/src/hover_popover.rs πŸ”—

@@ -1,6 +1,6 @@
 use crate::{
-    display_map::ToDisplayPoint, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSnapshot,
-    EditorStyle, RangeToAnchorExt,
+    display_map::ToDisplayPoint, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings,
+    EditorSnapshot, EditorStyle, RangeToAnchorExt,
 };
 use futures::FutureExt;
 use gpui::{
@@ -38,7 +38,7 @@ pub fn hover(editor: &mut Editor, _: &Hover, cx: &mut ViewContext<Editor>) {
 /// The internal hover action dispatches between `show_hover` or `hide_hover`
 /// depending on whether a point to hover over is provided.
 pub fn hover_at(editor: &mut Editor, point: Option<DisplayPoint>, cx: &mut ViewContext<Editor>) {
-    if cx.global::<Settings>().hover_popover_enabled {
+    if settings::get_setting::<EditorSettings>(None, cx).hover_popover_enabled {
         if let Some(point) = point {
             show_hover(editor, point, false, cx);
         } else {

crates/project_panel/Cargo.toml πŸ”—

@@ -24,6 +24,7 @@ futures.workspace = true
 unicase = "2.6"
 
 [dev-dependencies]
+client = { path = "../client", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 editor = { path = "../editor", features = ["test-support"] }
 gpui = { path = "../gpui", features = ["test-support"] }

crates/search/src/project_search.rs πŸ”—

@@ -1281,11 +1281,13 @@ pub mod tests {
         cx.update(|cx| {
             cx.set_global(SettingsStore::test(cx));
             cx.set_global(ActiveSearches::default());
+
             let mut settings = Settings::test(cx);
             settings.theme = Arc::new(theme);
             cx.set_global(settings);
 
             language::init(cx);
+            editor::init_settings(cx);
         });
     }
 }

crates/settings/src/settings.rs πŸ”—

@@ -38,10 +38,7 @@ pub struct Settings {
     pub buffer_font_family: FamilyId,
     pub buffer_font_size: f32,
     pub active_pane_magnification: f32,
-    pub cursor_blink: bool,
     pub confirm_quit: bool,
-    pub hover_popover_enabled: bool,
-    pub show_completions_on_input: bool,
     pub show_call_status_icon: bool,
     pub autosave: Autosave,
     pub default_dock_anchor: DockAnchor,
@@ -78,9 +75,6 @@ impl Setting for Settings {
             buffer_font_size: defaults.buffer_font_size.unwrap(),
             active_pane_magnification: defaults.active_pane_magnification.unwrap(),
             confirm_quit: defaults.confirm_quit.unwrap(),
-            cursor_blink: defaults.cursor_blink.unwrap(),
-            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(),
             autosave: defaults.autosave.unwrap(),
             default_dock_anchor: defaults.default_dock_anchor.unwrap(),
@@ -341,9 +335,6 @@ impl Settings {
             buffer_font_size: defaults.buffer_font_size.unwrap(),
             active_pane_magnification: defaults.active_pane_magnification.unwrap(),
             confirm_quit: defaults.confirm_quit.unwrap(),
-            cursor_blink: defaults.cursor_blink.unwrap(),
-            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(),
             autosave: defaults.autosave.unwrap(),
             default_dock_anchor: defaults.default_dock_anchor.unwrap(),
@@ -391,13 +382,7 @@ impl Settings {
             &mut self.active_pane_magnification,
             data.active_pane_magnification,
         );
-        merge(&mut self.cursor_blink, data.cursor_blink);
         merge(&mut self.confirm_quit, data.confirm_quit);
-        merge(&mut self.hover_popover_enabled, data.hover_popover_enabled);
-        merge(
-            &mut self.show_completions_on_input,
-            data.show_completions_on_input,
-        );
         merge(&mut self.autosave, data.autosave);
         merge(&mut self.default_dock_anchor, data.default_dock_anchor);
         merge(&mut self.base_keymap, data.base_keymap);
@@ -426,9 +411,6 @@ impl Settings {
             buffer_font_size: 14.,
             active_pane_magnification: 1.,
             confirm_quit: false,
-            cursor_blink: true,
-            hover_popover_enabled: true,
-            show_completions_on_input: true,
             show_call_status_icon: true,
             autosave: Autosave::Off,
             default_dock_anchor: DockAnchor::Bottom,