From 4d26f83d23165d841ebe95f3c1123616b30709ce Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 12 Sep 2024 14:46:08 -0400 Subject: [PATCH] Revert "settings: Remove auxiliary Content types where possible (#16744)" (#17768) This breaks setting `{"scrollbar": {"show":"never"}}` Release Notes: - N/A --- crates/auto_update/src/auto_update.rs | 23 +- crates/call/src/call_settings.rs | 22 +- crates/client/src/client.rs | 50 +-- crates/collab/src/tests/editor_tests.rs | 8 +- crates/collab/src/tests/following_tests.rs | 2 +- crates/collab_ui/src/chat_panel.rs | 2 +- .../src/chat_panel/message_editor.rs | 8 +- crates/collab_ui/src/collab_panel.rs | 2 +- crates/collab_ui/src/notification_panel.rs | 2 +- crates/collab_ui/src/panel_settings.rs | 78 ++--- .../src/project_diagnostics_settings.rs | 20 +- crates/editor/src/editor.rs | 4 +- crates/editor/src/editor_settings.rs | 318 +++++++++++------- crates/editor/src/editor_settings_controls.rs | 22 +- crates/editor/src/editor_tests.rs | 18 +- crates/editor/src/element.rs | 17 +- crates/extension/src/extension_settings.rs | 13 +- crates/extensions_ui/src/extensions_ui.rs | 2 +- crates/go_to_line/src/cursor_position.rs | 16 +- crates/gpui/src/geometry.rs | 2 - crates/language/src/language_settings.rs | 8 +- crates/languages/src/json.rs | 29 +- crates/outline_panel/src/outline_panel.rs | 16 +- .../src/outline_panel_settings.rs | 76 +++-- crates/performance/src/performance.rs | 184 ---------- crates/project/src/project_settings.rs | 41 ++- crates/project_panel/src/project_panel.rs | 28 +- .../src/project_panel_settings.rs | 92 +++-- crates/recent_projects/src/dev_servers.rs | 3 +- crates/recent_projects/src/ssh_connections.rs | 25 +- crates/repl/src/jupyter_settings.rs | 28 +- crates/tasks_ui/src/settings.rs | 18 +- crates/vim/src/digraph.rs | 2 +- crates/vim/src/normal.rs | 6 +- crates/vim/src/normal/paste.rs | 12 +- crates/vim/src/normal/scroll.rs | 2 +- crates/vim/src/normal/search.rs | 4 +- crates/vim/src/test.rs | 2 +- crates/vim/src/test/vim_test_context.rs | 6 +- crates/vim/src/vim.rs | 32 +- crates/welcome/src/base_keymap_picker.rs | 2 +- crates/welcome/src/base_keymap_setting.rs | 6 +- crates/welcome/src/welcome.rs | 2 +- crates/workspace/src/item.rs | 70 ++-- crates/workspace/src/workspace.rs | 8 +- crates/workspace/src/workspace_settings.rs | 132 ++++---- crates/worktree/src/worktree_settings.rs | 43 +-- crates/worktree/src/worktree_tests.rs | 11 +- crates/zed/src/zed.rs | 2 +- 49 files changed, 686 insertions(+), 833 deletions(-) delete mode 100644 crates/performance/src/performance.rs diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 499df7fc29..8063ff4c40 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -116,30 +116,27 @@ impl Drop for MacOsUnmounter { } } -/// Whether or not to automatically check for updates. -#[derive(Clone, Copy, JsonSchema, Deserialize, Serialize)] -#[serde(default)] -#[serde(transparent)] struct AutoUpdateSetting(bool); -impl Default for AutoUpdateSetting { - fn default() -> Self { - Self(true) - } -} +/// Whether or not to automatically check for updates. +/// +/// Default: true +#[derive(Clone, Copy, Default, JsonSchema, Deserialize, Serialize)] +#[serde(transparent)] +struct AutoUpdateSettingContent(bool); impl Settings for AutoUpdateSetting { const KEY: Option<&'static str> = Some("auto_update"); - type FileContent = Self; + type FileContent = Option; fn load(sources: SettingsSources, _: &mut AppContext) -> Result { let auto_update = [sources.release_channel, sources.user] .into_iter() - .find_map(|value| value.copied()) - .unwrap_or(*sources.default); + .find_map(|value| value.copied().flatten()) + .unwrap_or(sources.default.ok_or_else(Self::missing_default)?); - Ok(auto_update) + Ok(Self(auto_update.0)) } } diff --git a/crates/call/src/call_settings.rs b/crates/call/src/call_settings.rs index e10b711734..446178ffb9 100644 --- a/crates/call/src/call_settings.rs +++ b/crates/call/src/call_settings.rs @@ -4,20 +4,30 @@ use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; -/// Configuration of voice calls in Zed. -#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema)] -#[serde(default)] +#[derive(Deserialize, Debug)] pub struct CallSettings { - /// Whether the microphone should be muted when joining a channel or a call. pub mute_on_join: bool, - /// Whether your current project should be shared when joining an empty channel. pub share_on_join: bool, } +/// Configuration of voice calls in Zed. +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] +pub struct CallSettingsContent { + /// Whether the microphone should be muted when joining a channel or a call. + /// + /// Default: false + pub mute_on_join: Option, + + /// Whether your current project should be shared when joining an empty channel. + /// + /// Default: true + pub share_on_join: Option, +} + impl Settings for CallSettings { const KEY: Option<&'static str> = Some("calls"); - type FileContent = Self; + type FileContent = CallSettingsContent; fn load(sources: SettingsSources, _: &mut AppContext) -> Result { sources.json_merge() diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 83eef45be8..8787e2ed96 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -99,26 +99,20 @@ pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(20); actions!(client, [SignIn, SignOut, Reconnect]); -#[derive(Clone, Serialize, Deserialize, JsonSchema)] -#[serde(default)] -pub struct ClientSettings { - /// The server to connect to. If the environment variable - /// ZED_SERVER_URL is set, it will override this setting. - pub server_url: String, +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +pub struct ClientSettingsContent { + server_url: Option, } -impl Default for ClientSettings { - fn default() -> Self { - Self { - server_url: "https://zed.dev".to_owned(), - } - } +#[derive(Deserialize)] +pub struct ClientSettings { + pub server_url: String, } impl Settings for ClientSettings { const KEY: Option<&'static str> = None; - type FileContent = Self; + type FileContent = ClientSettingsContent; fn load(sources: SettingsSources, _: &mut AppContext) -> Result { let mut result = sources.json_merge::()?; @@ -130,37 +124,19 @@ impl Settings for ClientSettings { } #[derive(Default, Clone, Serialize, Deserialize, JsonSchema)] -#[serde(default)] -pub struct ProxySettings { - /// Set a proxy to use. The proxy protocol is specified by the URI scheme. - /// - /// Supported URI scheme: `http`, `https`, `socks4`, `socks4a`, `socks5`, - /// `socks5h`. `http` will be used when no scheme is specified. - /// - /// By default no proxy will be used, or Zed will try get proxy settings from - /// environment variables. - /// - /// Examples: - /// - "proxy": "socks5://localhost:10808" - /// - "proxy": "http://127.0.0.1:10809" - #[schemars(example = "Self::example_1")] - #[schemars(example = "Self::example_2")] - pub proxy: Option, +pub struct ProxySettingsContent { + proxy: Option, } -impl ProxySettings { - fn example_1() -> String { - "http://127.0.0.1:10809".to_owned() - } - fn example_2() -> String { - "socks5://localhost:10808".to_owned() - } +#[derive(Deserialize, Default)] +pub struct ProxySettings { + pub proxy: Option, } impl Settings for ProxySettings { const KEY: Option<&'static str> = None; - type FileContent = Self; + type FileContent = ProxySettingsContent; fn load(sources: SettingsSources, _: &mut AppContext) -> Result { Ok(Self { diff --git a/crates/collab/src/tests/editor_tests.rs b/crates/collab/src/tests/editor_tests.rs index a214291752..3f205b7f93 100644 --- a/crates/collab/src/tests/editor_tests.rs +++ b/crates/collab/src/tests/editor_tests.rs @@ -2261,11 +2261,11 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA cx_a.update(editor::init); cx_b.update(editor::init); // Turn inline-blame-off by default so no state is transferred without us explicitly doing so - let inline_blame_off_settings = InlineBlameSettings { + let inline_blame_off_settings = Some(InlineBlameSettings { enabled: false, - delay_ms: 0, - min_column: 0, - }; + delay_ms: None, + min_column: None, + }); cx_a.update(|cx| { SettingsStore::update_global(cx, |store, cx| { store.update_user_settings::(cx, |settings| { diff --git a/crates/collab/src/tests/following_tests.rs b/crates/collab/src/tests/following_tests.rs index 1bc3cd6917..e66b66a1b4 100644 --- a/crates/collab/src/tests/following_tests.rs +++ b/crates/collab/src/tests/following_tests.rs @@ -1649,7 +1649,7 @@ async fn test_following_into_excluded_file( cx.update(|cx| { cx.update_global::(|store, cx| { store.update_user_settings::(cx, |settings| { - settings.file_scan_exclusions = vec!["**/.git".to_string()]; + settings.file_scan_exclusions = Some(vec!["**/.git".to_string()]); }); }); }); diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index f6e6c7321f..5a79f364ff 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -1108,7 +1108,7 @@ impl Panel for ChatPanel { settings::update_settings_file::( self.fs.clone(), cx, - move |settings, _| settings.dock = position, + move |settings, _| settings.dock = Some(position), ); } diff --git a/crates/collab_ui/src/chat_panel/message_editor.rs b/crates/collab_ui/src/chat_panel/message_editor.rs index 0b1a2dbe69..028e148cba 100644 --- a/crates/collab_ui/src/chat_panel/message_editor.rs +++ b/crates/collab_ui/src/chat_panel/message_editor.rs @@ -113,7 +113,9 @@ impl MessageEditor { editor.set_show_indent_guides(false, cx); editor.set_completion_provider(Box::new(MessageEditorCompletionProvider(this))); editor.set_auto_replace_emoji_shortcode( - MessageEditorSettings::get_global(cx).auto_replace_emoji_shortcode, + MessageEditorSettings::get_global(cx) + .auto_replace_emoji_shortcode + .unwrap_or_default(), ); }); @@ -128,7 +130,9 @@ impl MessageEditor { cx.observe_global::(|view, cx| { view.editor.update(cx, |editor, cx| { editor.set_auto_replace_emoji_shortcode( - MessageEditorSettings::get_global(cx).auto_replace_emoji_shortcode, + MessageEditorSettings::get_global(cx) + .auto_replace_emoji_shortcode + .unwrap_or_default(), ) }) }) diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 3e6483c42d..7270110181 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -2813,7 +2813,7 @@ impl Panel for CollabPanel { settings::update_settings_file::( self.fs.clone(), cx, - move |settings, _| settings.dock = position, + move |settings, _| settings.dock = Some(position), ); } diff --git a/crates/collab_ui/src/notification_panel.rs b/crates/collab_ui/src/notification_panel.rs index 326e1f0f5b..33ca5a2952 100644 --- a/crates/collab_ui/src/notification_panel.rs +++ b/crates/collab_ui/src/notification_panel.rs @@ -672,7 +672,7 @@ impl Panel for NotificationPanel { settings::update_settings_file::( self.fs.clone(), cx, - move |settings, _| settings.dock = position, + move |settings, _| settings.dock = Some(position), ); } diff --git a/crates/collab_ui/src/panel_settings.rs b/crates/collab_ui/src/panel_settings.rs index a594f023bb..f9851d5797 100644 --- a/crates/collab_ui/src/panel_settings.rs +++ b/crates/collab_ui/src/panel_settings.rs @@ -2,84 +2,58 @@ use gpui::Pixels; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; -use ui::px; use workspace::dock::DockPosition; -#[derive(Clone, Deserialize, Debug, JsonSchema, Serialize)] -#[serde(default)] +#[derive(Deserialize, Debug)] pub struct CollaborationPanelSettings { - /// Whether to show the panel button in the status bar. pub button: bool, - /// Where to dock the panel. pub dock: DockPosition, - /// Default width of the panel in pixels. pub default_width: Pixels, } -impl Default for CollaborationPanelSettings { - fn default() -> Self { - Self { - button: true, - dock: DockPosition::Left, - default_width: px(240.), - } - } -} - -#[derive(Clone, Deserialize, Debug, JsonSchema, Serialize)] -#[serde(default)] +#[derive(Deserialize, Debug)] pub struct ChatPanelSettings { - /// Whether to show the panel button in the status bar. pub button: bool, - /// Where to dock the panel. pub dock: DockPosition, - /// Default width of the panel in pixels. pub default_width: Pixels, } -impl Default for ChatPanelSettings { - fn default() -> Self { - Self { - button: true, - dock: DockPosition::Right, - default_width: px(240.), - } - } -} - -#[derive(Clone, Deserialize, Debug, JsonSchema, Serialize)] -#[serde(default)] +#[derive(Deserialize, Debug)] pub struct NotificationPanelSettings { - /// Whether to show the panel button in the status bar. pub button: bool, - /// Where to dock the panel. pub dock: DockPosition, - /// Default width of the panel in pixels. pub default_width: Pixels, } -impl Default for NotificationPanelSettings { - fn default() -> Self { - Self { - button: true, - dock: DockPosition::Right, - default_width: px(380.), - } - } -} - #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] -#[serde(default)] +pub struct PanelSettingsContent { + /// Whether to show the panel button in the status bar. + /// + /// Default: true + pub button: Option, + /// Where to dock the panel. + /// + /// Default: left + pub dock: Option, + /// Default width of the panel in pixels. + /// + /// Default: 240 + pub default_width: Option, +} + +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] pub struct MessageEditorSettings { /// Whether to automatically replace emoji shortcodes with emoji characters. /// For example: typing `:wave:` gets replaced with `👋`. - pub auto_replace_emoji_shortcode: bool, + /// + /// Default: false + pub auto_replace_emoji_shortcode: Option, } impl Settings for CollaborationPanelSettings { const KEY: Option<&'static str> = Some("collaboration_panel"); - type FileContent = Self; + type FileContent = PanelSettingsContent; fn load( sources: SettingsSources, @@ -92,7 +66,7 @@ impl Settings for CollaborationPanelSettings { impl Settings for ChatPanelSettings { const KEY: Option<&'static str> = Some("chat_panel"); - type FileContent = Self; + type FileContent = PanelSettingsContent; fn load( sources: SettingsSources, @@ -105,7 +79,7 @@ impl Settings for ChatPanelSettings { impl Settings for NotificationPanelSettings { const KEY: Option<&'static str> = Some("notification_panel"); - type FileContent = Self; + type FileContent = PanelSettingsContent; fn load( sources: SettingsSources, @@ -118,7 +92,7 @@ impl Settings for NotificationPanelSettings { impl Settings for MessageEditorSettings { const KEY: Option<&'static str> = Some("message_editor"); - type FileContent = Self; + type FileContent = MessageEditorSettings; fn load( sources: SettingsSources, diff --git a/crates/diagnostics/src/project_diagnostics_settings.rs b/crates/diagnostics/src/project_diagnostics_settings.rs index 34739bcd17..55879d0c42 100644 --- a/crates/diagnostics/src/project_diagnostics_settings.rs +++ b/crates/diagnostics/src/project_diagnostics_settings.rs @@ -4,25 +4,23 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; -#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)] -#[serde(default)] -/// Diagnostics configuration. +#[derive(Deserialize, Debug)] pub struct ProjectDiagnosticsSettings { - /// Whether to show warnings or not by default. pub include_warnings: bool, } -impl Default for ProjectDiagnosticsSettings { - fn default() -> Self { - Self { - include_warnings: true, - } - } +/// Diagnostics configuration. +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] +pub struct ProjectDiagnosticsSettingsContent { + /// Whether to show warnings or not by default. + /// + /// Default: true + include_warnings: Option, } impl Settings for ProjectDiagnosticsSettings { const KEY: Option<&'static str> = Some("diagnostics"); - type FileContent = Self; + type FileContent = ProjectDiagnosticsSettingsContent; fn load(sources: SettingsSources, _: &mut AppContext) -> Result { sources.json_merge() diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index e50bf67ab0..4792c6b2cb 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -10640,7 +10640,7 @@ impl Editor { let fs = workspace.read(cx).app_state().fs.clone(); let current_show = TabBarSettings::get_global(cx).show; update_settings_file::(fs, cx, move |setting, _| { - setting.show = !current_show; + setting.show = Some(!current_show); }); } @@ -12563,7 +12563,7 @@ impl EditorSnapshot { let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| { matches!( ProjectSettings::get_global(cx).git.git_gutter, - GitGutterSetting::TrackedFiles + Some(GitGutterSetting::TrackedFiles) ) }); let gutter_settings = EditorSettings::get_global(cx).gutter; diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index 0532fd7bdf..2614e4ea30 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -3,105 +3,38 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; -#[derive(Clone, Serialize, Deserialize, JsonSchema)] -#[serde(default)] +#[derive(Deserialize, Clone)] pub struct EditorSettings { - /// Whether the cursor blinks in the editor. pub cursor_blink: bool, - /// How to highlight the current line in the editor. pub current_line_highlight: CurrentLineHighlight, - /// Whether to show the informational hover box when moving the mouse - /// over symbols in the editor. pub hover_popover_enabled: bool, - /// Whether to pop the completions menu while typing in an editor without - /// explicitly requesting it. pub show_completions_on_input: bool, - /// Whether to display inline and alongside documentation for items in the - /// completions menu. pub show_completion_documentation: bool, - /// The debounce delay before re-querying the language server for completion - /// documentation when not included in original completion list. pub completion_documentation_secondary_query_debounce: u64, - /// Whether to use additional LSP queries to format (and amend) the code after - /// every "trigger" symbol input, defined by LSP server capabilities. pub use_on_type_format: bool, - /// Toolbar related settings pub toolbar: Toolbar, - /// Scrollbar related settings pub scrollbar: Scrollbar, - /// Gutter related settings pub gutter: Gutter, - /// Whether the editor will scroll beyond the last line. pub scroll_beyond_last_line: ScrollBeyondLastLine, - /// The number of lines to keep above/below the cursor when auto-scrolling. pub vertical_scroll_margin: f32, - /// Scroll sensitivity multiplier. This multiplier is applied - /// to both the horizontal and vertical delta values while scrolling. pub scroll_sensitivity: f32, - /// Whether the line numbers on editors gutter are relative or not. pub relative_line_numbers: bool, - /// When to populate a new search's query based on the text under the cursor. pub seed_search_query_from_cursor: SeedQuerySetting, pub use_smartcase_search: bool, - /// The key to use for adding multiple cursors pub multi_cursor_modifier: MultiCursorModifier, - /// Hide the values of variables in `private` files, as defined by the - /// private_files setting. This only changes the visual representation, - /// the values are still present in the file and can be selected / copied / pasted pub redact_private_values: bool, - - /// How many lines to expand the multibuffer excerpts by default pub expand_excerpt_lines: u32, pub middle_click_paste: bool, - /// What to do when multibuffer is double clicked in some of its excerpts - /// (parts of singleton buffers). #[serde(default)] pub double_click_in_multibuffer: DoubleClickInMultibuffer, - /// Whether the editor search results will loop pub search_wrap: bool, #[serde(default)] pub search: SearchSettings, - /// Show method signatures in the editor, when inside parentheses. pub auto_signature_help: bool, - /// Whether to show the signature help after completion or a bracket pair inserted. - /// If `auto_signature_help` is enabled, this setting will be treated as enabled also. pub show_signature_help_after_edits: bool, - /// Jupyter REPL settings. pub jupyter: Jupyter, } -impl Default for EditorSettings { - fn default() -> Self { - Self { - cursor_blink: true, - current_line_highlight: CurrentLineHighlight::All, - hover_popover_enabled: true, - show_completions_on_input: true, - show_completion_documentation: true, - completion_documentation_secondary_query_debounce: 300, - use_on_type_format: true, - toolbar: Default::default(), - scrollbar: Default::default(), - gutter: Default::default(), - scroll_beyond_last_line: ScrollBeyondLastLine::OnePage, - vertical_scroll_margin: 3., - scroll_sensitivity: 1.0, - relative_line_numbers: false, - seed_search_query_from_cursor: SeedQuerySetting::Always, - multi_cursor_modifier: MultiCursorModifier::Alt, - redact_private_values: false, - expand_excerpt_lines: 3, - double_click_in_multibuffer: DoubleClickInMultibuffer::Select, - search_wrap: true, - auto_signature_help: false, - show_signature_help_after_edits: true, - jupyter: Default::default(), - use_smartcase_search: false, - middle_click_paste: true, - search: SearchSettings::default(), - } - } -} #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum CurrentLineHighlight { @@ -139,93 +72,48 @@ pub enum DoubleClickInMultibuffer { Open, } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[derive(Debug, Clone, Deserialize)] pub struct Jupyter { /// Whether the Jupyter feature is enabled. + /// + /// Default: true pub enabled: bool, } -impl Default for Jupyter { - fn default() -> Self { - Self { enabled: true } - } +#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct JupyterContent { + /// Whether the Jupyter feature is enabled. + /// + /// Default: true + pub enabled: Option, } #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(default)] pub struct Toolbar { - /// Whether to display breadcrumbs in the editor toolbar. pub breadcrumbs: bool, - /// Whether to display quick action buttons in the editor toolbar. pub quick_actions: bool, - /// Whether to show the selections menu in the editor toolbar pub selections_menu: bool, } -impl Default for Toolbar { - fn default() -> Self { - Self { - breadcrumbs: true, - quick_actions: true, - selections_menu: true, - } - } -} - #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] pub struct Scrollbar { - /// When to show the scrollbar in the editor. pub show: ShowScrollbar, - /// Whether to show git diff indicators in the scrollbar. pub git_diff: bool, - /// Whether to show buffer search result indicators in the scrollbar. pub selected_symbol: bool, - /// Whether to show selected symbol occurrences in the scrollbar. pub search_results: bool, - /// Whether to show diagnostic indicators in the scrollbar. pub diagnostics: bool, - /// Whether to show cursor positions in the scrollbar. pub cursors: bool, } -impl Default for Scrollbar { - fn default() -> Self { - Self { - show: ShowScrollbar::Auto, - git_diff: true, - selected_symbol: true, - search_results: true, - diagnostics: true, - cursors: true, - } - } -} - -/// Gutter-related settings. #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(default)] pub struct Gutter { - /// Whether to show line numbers in the gutter. pub line_numbers: bool, - /// Whether to show code action buttons in the gutter. pub code_actions: bool, - /// Whether to show runnable buttons in the gutter. pub runnables: bool, - /// Whether to show fold buttons in the gutter. pub folds: bool, } -impl Default for Gutter { - fn default() -> Self { - Self { - line_numbers: true, - code_actions: true, - runnables: true, - folds: true, - } - } -} - /// When to show the scrollbar in the editor. /// /// Default: auto @@ -283,6 +171,188 @@ pub struct SearchSettings { pub regex: bool, } +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +pub struct EditorSettingsContent { + /// Whether the cursor blinks in the editor. + /// + /// Default: true + pub cursor_blink: Option, + /// How to highlight the current line in the editor. + /// + /// Default: all + pub current_line_highlight: Option, + /// Whether to show the informational hover box when moving the mouse + /// over symbols in the editor. + /// + /// Default: true + pub hover_popover_enabled: Option, + + /// Whether to pop the completions menu while typing in an editor without + /// explicitly requesting it. + /// + /// Default: true + pub show_completions_on_input: Option, + /// Whether to display inline and alongside documentation for items in the + /// completions menu. + /// + /// Default: true + pub show_completion_documentation: Option, + /// The debounce delay before re-querying the language server for completion + /// documentation when not included in original completion list. + /// + /// Default: 300 ms + pub completion_documentation_secondary_query_debounce: Option, + /// Whether to use additional LSP queries to format (and amend) the code after + /// every "trigger" symbol input, defined by LSP server capabilities. + /// + /// Default: true + pub use_on_type_format: Option, + /// Toolbar related settings + pub toolbar: Option, + /// Scrollbar related settings + pub scrollbar: Option, + /// Gutter related settings + pub gutter: Option, + /// Whether the editor will scroll beyond the last line. + /// + /// Default: one_page + pub scroll_beyond_last_line: Option, + /// The number of lines to keep above/below the cursor when auto-scrolling. + /// + /// Default: 3. + pub vertical_scroll_margin: Option, + /// Scroll sensitivity multiplier. This multiplier is applied + /// to both the horizontal and vertical delta values while scrolling. + /// + /// Default: 1.0 + pub scroll_sensitivity: Option, + /// Whether the line numbers on editors gutter are relative or not. + /// + /// Default: false + pub relative_line_numbers: Option, + /// When to populate a new search's query based on the text under the cursor. + /// + /// Default: always + pub seed_search_query_from_cursor: Option, + pub use_smartcase_search: Option, + /// The key to use for adding multiple cursors + /// + /// Default: alt + pub multi_cursor_modifier: Option, + /// Hide the values of variables in `private` files, as defined by the + /// private_files setting. This only changes the visual representation, + /// the values are still present in the file and can be selected / copied / pasted + /// + /// Default: false + pub redact_private_values: Option, + + /// How many lines to expand the multibuffer excerpts by default + /// + /// Default: 3 + pub expand_excerpt_lines: Option, + + /// Whether to enable middle-click paste on Linux + /// + /// Default: true + pub middle_click_paste: Option, + + /// What to do when multibuffer is double clicked in some of its excerpts + /// (parts of singleton buffers). + /// + /// Default: select + pub double_click_in_multibuffer: Option, + /// Whether the editor search results will loop + /// + /// Default: true + pub search_wrap: Option, + + /// Defaults to use when opening a new buffer and project search items. + /// + /// Default: nothing is enabled + pub search: Option, + + /// Whether to automatically show a signature help pop-up or not. + /// + /// Default: false + pub auto_signature_help: Option, + + /// Whether to show the signature help pop-up after completions or bracket pairs inserted. + /// + /// Default: true + pub show_signature_help_after_edits: Option, + + /// Jupyter REPL settings. + pub jupyter: Option, +} + +// Toolbar related settings +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct ToolbarContent { + /// Whether to display breadcrumbs in the editor toolbar. + /// + /// Default: true + pub breadcrumbs: Option, + /// Whether to display quick action buttons in the editor toolbar. + /// + /// Default: true + pub quick_actions: Option, + + /// Whether to show the selections menu in the editor toolbar + /// + /// Default: true + pub selections_menu: Option, +} + +/// Scrollbar related settings +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] +pub struct ScrollbarContent { + /// When to show the scrollbar in the editor. + /// + /// Default: auto + pub show: Option, + /// Whether to show git diff indicators in the scrollbar. + /// + /// Default: true + pub git_diff: Option, + /// Whether to show buffer search result indicators in the scrollbar. + /// + /// Default: true + pub search_results: Option, + /// Whether to show selected symbol occurrences in the scrollbar. + /// + /// Default: true + pub selected_symbol: Option, + /// Whether to show diagnostic indicators in the scrollbar. + /// + /// Default: true + pub diagnostics: Option, + /// Whether to show cursor positions in the scrollbar. + /// + /// Default: true + pub cursors: Option, +} + +/// Gutter related settings +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct GutterContent { + /// Whether to show line numbers in the gutter. + /// + /// Default: true + pub line_numbers: Option, + /// Whether to show code action buttons in the gutter. + /// + /// Default: true + pub code_actions: Option, + /// Whether to show runnable buttons in the gutter. + /// + /// Default: true + pub runnables: Option, + /// Whether to show fold buttons in the gutter. + /// + /// Default: true + pub folds: Option, +} + impl EditorSettings { pub fn jupyter_enabled(cx: &AppContext) -> bool { EditorSettings::get_global(cx).jupyter.enabled @@ -292,7 +362,7 @@ impl EditorSettings { impl Settings for EditorSettings { const KEY: Option<&'static str> = None; - type FileContent = Self; + type FileContent = EditorSettingsContent; fn load( sources: SettingsSources, diff --git a/crates/editor/src/editor_settings_controls.rs b/crates/editor/src/editor_settings_controls.rs index 36d471dfa2..bbe1b00324 100644 --- a/crates/editor/src/editor_settings_controls.rs +++ b/crates/editor/src/editor_settings_controls.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use gpui::{AppContext, FontFeatures, FontWeight}; -use project::project_settings::ProjectSettings; +use project::project_settings::{InlineBlameSettings, ProjectSettings}; use settings::{EditableSettingControl, Settings}; use theme::{FontFamilyCache, ThemeSettings}; use ui::{ @@ -296,7 +296,14 @@ impl EditableSettingControl for InlineGitBlameControl { value: Self::Value, _cx: &AppContext, ) { - settings.git.inline_blame.enabled = value; + if let Some(inline_blame) = settings.git.inline_blame.as_mut() { + inline_blame.enabled = value; + } else { + settings.git.inline_blame = Some(InlineBlameSettings { + enabled: false, + ..Default::default() + }); + } } } @@ -342,7 +349,14 @@ impl EditableSettingControl for LineNumbersControl { value: Self::Value, _cx: &AppContext, ) { - settings.gutter.line_numbers = value; + if let Some(gutter) = settings.gutter.as_mut() { + gutter.line_numbers = Some(value); + } else { + settings.gutter = Some(crate::editor_settings::GutterContent { + line_numbers: Some(value), + ..Default::default() + }); + } } } @@ -388,7 +402,7 @@ impl EditableSettingControl for RelativeLineNumbersControl { value: Self::Value, _cx: &AppContext, ) { - settings.relative_line_numbers = value; + settings.relative_line_numbers = Some(value); } } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 7d42dc7a85..0b1e0385de 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -6964,7 +6964,7 @@ async fn test_handle_input_for_show_signature_help_auto_signature_help_true( cx.update(|cx| { cx.update_global::(|settings, cx| { settings.update_user_settings::(cx, |settings| { - settings.auto_signature_help = true; + settings.auto_signature_help = Some(true); }); }); }); @@ -7105,8 +7105,8 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui: cx.update(|cx| { cx.update_global::(|settings, cx| { settings.update_user_settings::(cx, |settings| { - settings.auto_signature_help = false; - settings.show_signature_help_after_edits = false; + settings.auto_signature_help = Some(false); + settings.show_signature_help_after_edits = Some(false); }); }); }); @@ -7232,8 +7232,8 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui: cx.update(|cx| { cx.update_global::(|settings, cx| { settings.update_user_settings::(cx, |settings| { - settings.auto_signature_help = false; - settings.show_signature_help_after_edits = true; + settings.auto_signature_help = Some(false); + settings.show_signature_help_after_edits = Some(true); }); }); }); @@ -7274,8 +7274,8 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui: cx.update(|cx| { cx.update_global::(|settings, cx| { settings.update_user_settings::(cx, |settings| { - settings.auto_signature_help = true; - settings.show_signature_help_after_edits = false; + settings.auto_signature_help = Some(true); + settings.show_signature_help_after_edits = Some(false); }); }); }); @@ -7318,7 +7318,7 @@ async fn test_signature_help(cx: &mut gpui::TestAppContext) { cx.update(|cx| { cx.update_global::(|settings, cx| { settings.update_user_settings::(cx, |settings| { - settings.auto_signature_help = true; + settings.auto_signature_help = Some(true); }); }); }); @@ -7759,7 +7759,7 @@ async fn test_completion(cx: &mut gpui::TestAppContext) { cx.update(|cx| { cx.update_global::(|settings, cx| { settings.update_user_settings::(cx, |settings| { - settings.show_completions_on_input = false; + settings.show_completions_on_input = Some(false); }); }) }); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 1c0a325b76..d4f5c565c2 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1283,7 +1283,10 @@ impl EditorElement { .row, ); - let git_gutter_setting = ProjectSettings::get_global(cx).git.git_gutter; + let git_gutter_setting = ProjectSettings::get_global(cx) + .git + .git_gutter + .unwrap_or_default(); let display_hunks = buffer_snapshot .git_diff_hunks_in_range(buffer_start_row..buffer_end_row) .map(|hunk| diff_hunk_to_display(&hunk, snapshot)) @@ -1363,10 +1366,12 @@ impl EditorElement { }; let padded_line_end = line_end + em_width * INLINE_BLAME_PADDING_EM_WIDTHS; - let min_column_in_pixels = self.column_pixels( - ProjectSettings::get_global(cx).git.inline_blame.min_column as usize, - cx, - ); + let min_column_in_pixels = ProjectSettings::get_global(cx) + .git + .inline_blame + .and_then(|settings| settings.min_column) + .map(|col| self.column_pixels(col as usize, cx)) + .unwrap_or(px(0.)); let min_start = content_origin.x - scroll_pixel_position.x + min_column_in_pixels; cmp::max(padded_line_end, min_start) @@ -3326,7 +3331,7 @@ impl EditorElement { .unwrap_or_else(|| { matches!( ProjectSettings::get_global(cx).git.git_gutter, - GitGutterSetting::TrackedFiles + Some(GitGutterSetting::TrackedFiles) ) }); if show_git_gutter { diff --git a/crates/extension/src/extension_settings.rs b/crates/extension/src/extension_settings.rs index 715dc3ca82..a2ab7ac9cc 100644 --- a/crates/extension/src/extension_settings.rs +++ b/crates/extension/src/extension_settings.rs @@ -6,25 +6,18 @@ use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; use std::sync::Arc; -#[derive(Deserialize, Serialize, Debug, Clone, JsonSchema)] -#[serde(default)] +#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)] pub struct ExtensionSettings { /// The extensions that should be automatically installed by Zed. /// /// This is used to make functionality provided by extensions (e.g., language support) /// available out-of-the-box. + #[serde(default)] pub auto_install_extensions: HashMap, bool>, + #[serde(default)] pub auto_update_extensions: HashMap, bool>, } -impl Default for ExtensionSettings { - fn default() -> Self { - Self { - auto_install_extensions: HashMap::from_iter([("html".into(), true)]), - auto_update_extensions: Default::default(), - } - } -} impl ExtensionSettings { /// Returns whether the given extension should be auto-installed. pub fn should_auto_install(&self, extension_id: &str) -> bool { diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index b2d6d7f283..f246e3cf4f 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -1000,7 +1000,7 @@ impl ExtensionsPage { this.update_settings::( selection, cx, - |setting, value| *setting = VimModeSetting(value), + |setting, value| *setting = Some(value), ); }), )), diff --git a/crates/go_to_line/src/cursor_position.rs b/crates/go_to_line/src/cursor_position.rs index de3d1dc74d..63e0f2b079 100644 --- a/crates/go_to_line/src/cursor_position.rs +++ b/crates/go_to_line/src/cursor_position.rs @@ -180,10 +180,18 @@ pub(crate) enum LineIndicatorFormat { Long, } +/// Whether or not to automatically check for updates. +/// +/// Values: short, long +/// Default: short +#[derive(Clone, Copy, Default, JsonSchema, Deserialize, Serialize)] +#[serde(transparent)] +pub(crate) struct LineIndicatorFormatContent(LineIndicatorFormat); + impl Settings for LineIndicatorFormat { const KEY: Option<&'static str> = Some("line_indicator_format"); - type FileContent = Self; + type FileContent = Option; fn load( sources: SettingsSources, @@ -191,9 +199,9 @@ impl Settings for LineIndicatorFormat { ) -> anyhow::Result { let format = [sources.release_channel, sources.user] .into_iter() - .find_map(|value| value.copied()) - .unwrap_or(*sources.default); + .find_map(|value| value.copied().flatten()) + .unwrap_or(sources.default.ok_or_else(Self::missing_default)?); - Ok(format) + Ok(format.0) } } diff --git a/crates/gpui/src/geometry.rs b/crates/gpui/src/geometry.rs index b203592360..8de9e6f009 100644 --- a/crates/gpui/src/geometry.rs +++ b/crates/gpui/src/geometry.rs @@ -5,7 +5,6 @@ use core::fmt::Debug; use derive_more::{Add, AddAssign, Div, DivAssign, Mul, Neg, Sub, SubAssign}; use refineable::Refineable; -use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; use std::{ cmp::{self, PartialOrd}, @@ -2202,7 +2201,6 @@ impl From for Radians { PartialEq, Serialize, Deserialize, - JsonSchema, )] #[repr(transparent)] pub struct Pixels(pub f32); diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 7a6b758a25..e1fcaaba28 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -70,10 +70,10 @@ pub struct LanguageSettings { /// The column at which to soft-wrap lines, for buffers where soft-wrap /// is enabled. pub preferred_line_length: u32, - /// Whether to show wrap guides (vertical rulers) in the editor. - /// Setting this to true will show a guide at the 'preferred_line_length' value - /// if softwrap is set to 'preferred_line_length', and will show any - /// additional guides as specified by the 'wrap_guides' setting. + // Whether to show wrap guides (vertical rulers) in the editor. + // Setting this to true will show a guide at the 'preferred_line_length' value + // if softwrap is set to 'preferred_line_length', and will show any + // additional guides as specified by the 'wrap_guides' setting. pub show_wrap_guides: bool, /// Character counts at which to show wrap guides (vertical rulers) in the editor. pub wrap_guides: Vec, diff --git a/crates/languages/src/json.rs b/crates/languages/src/json.rs index 102eb1ef2f..6b5f74c263 100644 --- a/crates/languages/src/json.rs +++ b/crates/languages/src/json.rs @@ -7,13 +7,10 @@ use feature_flags::FeatureFlagAppExt; use futures::StreamExt; use gpui::{AppContext, AsyncAppContext}; use http_client::github::{latest_github_release, GitHubLspBinaryVersion}; -use language::{ - CodeLabel, Language, LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate, -}; +use language::{LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate}; use lsp::LanguageServerBinary; use node_runtime::NodeRuntime; use project::ContextProviderWithTasks; -use rope::Rope; use serde_json::{json, Value}; use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore}; use smol::{ @@ -205,30 +202,6 @@ impl LspAdapter for JsonLspAdapter { }))) } - async fn label_for_completion( - &self, - item: &lsp::CompletionItem, - language: &Arc, - ) -> Option { - let text = if let Some(description) = item - .label_details - .as_ref() - .and_then(|label_details| label_details.description.as_ref()) - { - format!("{} {}", item.label, description) - } else if let Some(detail) = &item.detail { - format!("{} {}", item.label, detail) - } else { - item.label.clone() - }; - let rope = Rope::from(item.label.as_str()); - let runs = language.highlight_text(&rope, 0..item.label.len()); - Some(language::CodeLabel { - text, - runs, - filter_range: 0..item.label.len(), - }) - } async fn workspace_configuration( self: Arc, _: &Arc, diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index 361607533b..c5f0187c22 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -24,12 +24,12 @@ use editor::{ use file_icons::FileIcons; use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ - actions, anchored, deferred, div, impl_actions, uniform_list, Action, AnyElement, AppContext, - AssetSource, AsyncWindowContext, ClipboardItem, DismissEvent, Div, ElementId, EventEmitter, - FocusHandle, FocusableView, HighlightStyle, InteractiveElement, IntoElement, KeyContext, Model, - MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Render, SharedString, Stateful, - Styled, Subscription, Task, UniformListScrollHandle, View, ViewContext, VisualContext, - WeakView, WindowContext, + actions, anchored, deferred, div, impl_actions, px, uniform_list, Action, AnyElement, + AppContext, AssetSource, AsyncWindowContext, ClipboardItem, DismissEvent, Div, ElementId, + EventEmitter, FocusHandle, FocusableView, HighlightStyle, InteractiveElement, IntoElement, + KeyContext, Model, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Render, + SharedString, Stateful, Styled, Subscription, Task, UniformListScrollHandle, View, ViewContext, + VisualContext, WeakView, WindowContext, }; use itertools::Itertools; use language::{BufferId, BufferSnapshot, OffsetRangeExt, OutlineItem}; @@ -1938,7 +1938,7 @@ impl OutlinePanel { .child( ListItem::new(item_id) .indent_level(depth) - .indent_step_size(settings.indent_size) + .indent_step_size(px(settings.indent_size)) .selected(is_active) .when_some(icon_element, |list_item, icon_element| { list_item.child(h_flex().child(icon_element)) @@ -3801,7 +3801,7 @@ impl Panel for OutlinePanel { DockPosition::Left | DockPosition::Bottom => OutlinePanelDockPosition::Left, DockPosition::Right => OutlinePanelDockPosition::Right, }; - settings.dock = dock; + settings.dock = Some(dock); }, ); } diff --git a/crates/outline_panel/src/outline_panel_settings.rs b/crates/outline_panel/src/outline_panel_settings.rs index a8e51b96c5..e19fc3c008 100644 --- a/crates/outline_panel/src/outline_panel_settings.rs +++ b/crates/outline_panel/src/outline_panel_settings.rs @@ -1,5 +1,4 @@ -use anyhow; -use gpui::{px, Pixels}; +use gpui::Pixels; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; @@ -11,51 +10,66 @@ pub enum OutlinePanelDockPosition { Right, } -#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, JsonSchema)] +#[derive(Deserialize, Debug, Clone, Copy, PartialEq)] pub struct OutlinePanelSettings { - /// Whether to show the outline panel button in the status bar. pub button: bool, - /// Customize default width (in pixels) taken by outline panel pub default_width: Pixels, - /// The position of outline panel pub dock: OutlinePanelDockPosition, - /// Whether to show file icons in the outline panel. pub file_icons: bool, - /// Whether to show folder icons or chevrons for directories in the outline panel. pub folder_icons: bool, - /// Whether to show the git status in the outline panel. pub git_status: bool, - /// Amount of indentation (in pixels) for nested items. - pub indent_size: Pixels, - /// Whether to reveal it in the outline panel automatically, - /// when a corresponding project entry becomes active. - /// Gitignored entries are never auto revealed. + pub indent_size: f32, pub auto_reveal_entries: bool, - /// Whether to fold directories automatically - /// when directory has only one directory inside. pub auto_fold_dirs: bool, } -impl Default for OutlinePanelSettings { - fn default() -> Self { - Self { - button: true, - default_width: px(240.), - dock: OutlinePanelDockPosition::Left, - file_icons: true, - folder_icons: true, - auto_fold_dirs: true, - auto_reveal_entries: true, - indent_size: px(20.), - git_status: true, - } - } +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] +pub struct OutlinePanelSettingsContent { + /// Whether to show the outline panel button in the status bar. + /// + /// Default: true + pub button: Option, + /// Customize default width (in pixels) taken by outline panel + /// + /// Default: 240 + pub default_width: Option, + /// The position of outline panel + /// + /// Default: left + pub dock: Option, + /// Whether to show file icons in the outline panel. + /// + /// Default: true + pub file_icons: Option, + /// Whether to show folder icons or chevrons for directories in the outline panel. + /// + /// Default: true + pub folder_icons: Option, + /// Whether to show the git status in the outline panel. + /// + /// Default: true + pub git_status: Option, + /// Amount of indentation (in pixels) for nested items. + /// + /// Default: 20 + pub indent_size: Option, + /// Whether to reveal it in the outline panel automatically, + /// when a corresponding project entry becomes active. + /// Gitignored entries are never auto revealed. + /// + /// Default: true + pub auto_reveal_entries: Option, + /// Whether to fold directories automatically + /// when directory has only one directory inside. + /// + /// Default: true + pub auto_fold_dirs: Option, } impl Settings for OutlinePanelSettings { const KEY: Option<&'static str> = Some("outline_panel"); - type FileContent = Self; + type FileContent = OutlinePanelSettingsContent; fn load( sources: SettingsSources, diff --git a/crates/performance/src/performance.rs b/crates/performance/src/performance.rs deleted file mode 100644 index db2388c59a..0000000000 --- a/crates/performance/src/performance.rs +++ /dev/null @@ -1,184 +0,0 @@ -use std::time::Instant; - -use anyhow::Result; -use gpui::{ - div, AppContext, InteractiveElement as _, Render, StatefulInteractiveElement as _, - Subscription, ViewContext, VisualContext, -}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources, SettingsStore}; -use workspace::{ - ui::{Label, LabelCommon, LabelSize, Tooltip}, - ItemHandle, StatusItemView, Workspace, -}; - -const SHOW_STARTUP_TIME_DURATION: std::time::Duration = std::time::Duration::from_secs(5); - -pub fn init(cx: &mut AppContext) { - PerformanceSettings::register(cx); - - let mut enabled = PerformanceSettings::get_global(cx).show_in_status_bar; - let start_time = Instant::now(); - let mut _observe_workspaces = toggle_status_bar_items(enabled, start_time, cx); - - cx.observe_global::(move |cx| { - let new_value = PerformanceSettings::get_global(cx).show_in_status_bar; - if new_value != enabled { - enabled = new_value; - _observe_workspaces = toggle_status_bar_items(enabled, start_time, cx); - } - }) - .detach(); -} - -fn toggle_status_bar_items( - enabled: bool, - start_time: Instant, - cx: &mut AppContext, -) -> Option { - for window in cx.windows() { - if let Some(workspace) = window.downcast::() { - workspace - .update(cx, |workspace, cx| { - toggle_status_bar_item(workspace, enabled, start_time, cx); - }) - .ok(); - } - } - - if enabled { - log::info!("performance metrics display enabled"); - Some(cx.observe_new_views::(move |workspace, cx| { - toggle_status_bar_item(workspace, true, start_time, cx); - })) - } else { - log::info!("performance metrics display disabled"); - None - } -} - -struct PerformanceStatusBarItem { - display_mode: DisplayMode, -} - -#[derive(Copy, Clone, Debug)] -enum DisplayMode { - StartupTime, - Fps, -} - -impl PerformanceStatusBarItem { - fn new(start_time: Instant, cx: &mut ViewContext) -> Self { - let now = Instant::now(); - let display_mode = if now < start_time + SHOW_STARTUP_TIME_DURATION { - DisplayMode::StartupTime - } else { - DisplayMode::Fps - }; - - let this = Self { display_mode }; - - if let DisplayMode::StartupTime = display_mode { - cx.spawn(|this, mut cx| async move { - let now = Instant::now(); - let remaining_duration = - (start_time + SHOW_STARTUP_TIME_DURATION).saturating_duration_since(now); - cx.background_executor().timer(remaining_duration).await; - this.update(&mut cx, |this, cx| { - this.display_mode = DisplayMode::Fps; - cx.notify(); - }) - .ok(); - }) - .detach(); - } - - this - } -} - -impl Render for PerformanceStatusBarItem { - fn render(&mut self, cx: &mut gpui::ViewContext) -> impl gpui::IntoElement { - let text = match self.display_mode { - DisplayMode::StartupTime => cx - .time_to_first_window_draw() - .map_or("Pending".to_string(), |duration| { - format!("{}ms", duration.as_millis()) - }), - DisplayMode::Fps => cx.fps().map_or("".to_string(), |fps| { - format!("{:3} FPS", fps.round() as u32) - }), - }; - - use gpui::ParentElement; - let display_mode = self.display_mode; - div() - .id("performance status") - .child(Label::new(text).size(LabelSize::Small)) - .tooltip(move |cx| match display_mode { - DisplayMode::StartupTime => Tooltip::text("Time to first window draw", cx), - DisplayMode::Fps => cx - .new_view(|cx| { - let tooltip = Tooltip::new("Current FPS"); - if let Some(time_to_first) = cx.time_to_first_window_draw() { - tooltip.meta(format!( - "Time to first window draw: {}ms", - time_to_first.as_millis() - )) - } else { - tooltip - } - }) - .into(), - }) - } -} - -impl StatusItemView for PerformanceStatusBarItem { - fn set_active_pane_item( - &mut self, - _active_pane_item: Option<&dyn ItemHandle>, - _cx: &mut gpui::ViewContext, - ) { - // This is not currently used. - } -} - -fn toggle_status_bar_item( - workspace: &mut Workspace, - enabled: bool, - start_time: Instant, - cx: &mut ViewContext, -) { - if enabled { - workspace.status_bar().update(cx, |bar, cx| { - bar.add_right_item( - cx.new_view(|cx| PerformanceStatusBarItem::new(start_time, cx)), - cx, - ) - }); - } else { - workspace.status_bar().update(cx, |bar, cx| { - bar.remove_items_of_type::(cx); - }); - } -} - -/// Configuration of the display of performance details. -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] -#[serde(default)] -pub struct PerformanceSettings { - /// Display the time to first window draw and frame rate in the status bar. - pub show_in_status_bar: bool, -} - -impl Settings for PerformanceSettings { - const KEY: Option<&'static str> = Some("performance"); - - type FileContent = Self; - - fn load(sources: SettingsSources, _: &mut AppContext) -> Result { - sources.json_merge() - } -} diff --git a/crates/project/src/project_settings.rs b/crates/project/src/project_settings.rs index 3c21b1c5e8..70b2eccf23 100644 --- a/crates/project/src/project_settings.rs +++ b/crates/project/src/project_settings.rs @@ -20,7 +20,6 @@ use worktree::{PathChange, UpdatedEntriesSet, Worktree, WorktreeId}; use crate::worktree_store::{WorktreeStore, WorktreeStoreEvent}; #[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)] -#[serde(default)] pub struct ProjectSettings { /// Configuration for language servers. /// @@ -42,6 +41,7 @@ pub struct ProjectSettings { pub load_direnv: DirenvSettings, /// Configuration for session-related features + #[serde(default)] pub session: SessionSettings, } @@ -59,31 +59,36 @@ pub enum DirenvSettings { } #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] -#[serde(default)] pub struct GitSettings { /// Whether or not to show the git gutter. /// /// Default: tracked_files - pub git_gutter: GitGutterSetting, + pub git_gutter: Option, pub gutter_debounce: Option, /// Whether or not to show git blame data inline in /// the currently focused line. /// /// Default: on - pub inline_blame: InlineBlameSettings, + pub inline_blame: Option, } impl GitSettings { pub fn inline_blame_enabled(&self) -> bool { #[allow(unknown_lints, clippy::manual_unwrap_or_default)] - self.inline_blame.enabled + match self.inline_blame { + Some(InlineBlameSettings { enabled, .. }) => enabled, + _ => false, + } } pub fn inline_blame_delay(&self) -> Option { - self.inline_blame - .delay_ms - .gt(&0) - .then(|| Duration::from_millis(self.inline_blame.delay_ms)) + match self.inline_blame { + Some(InlineBlameSettings { + delay_ms: Some(delay_ms), + .. + }) if delay_ms > 0 => Some(Duration::from_millis(delay_ms)), + _ => None, + } } } @@ -97,34 +102,28 @@ pub enum GitGutterSetting { Hide, } -#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] -#[serde(default)] pub struct InlineBlameSettings { /// Whether or not to show git blame data inline in /// the currently focused line. /// /// Default: true + #[serde(default = "true_value")] pub enabled: bool, /// Whether to only show the inline blame information /// after a delay once the cursor stops moving. /// /// Default: 0 - pub delay_ms: u64, + pub delay_ms: Option, /// The minimum column number to show the inline blame information at /// /// Default: 0 - pub min_column: u32, + pub min_column: Option, } -impl Default for InlineBlameSettings { - fn default() -> Self { - Self { - enabled: true, - delay_ms: 0, - min_column: 0, - } - } +const fn true_value() -> bool { + true } #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 6ca843875b..c77a2170dd 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -2289,7 +2289,7 @@ impl ProjectPanel { .child( ListItem::new(entry_id.to_proto() as usize) .indent_level(depth) - .indent_step_size(settings.indent_size) + .indent_step_size(px(settings.indent_size)) .selected(is_marked || is_active) .when_some(canonical_path, |this, path| { this.end_slot::( @@ -2817,7 +2817,7 @@ impl Render for DraggedProjectEntryView { this.bg(cx.theme().colors().background).w(self.width).child( ListItem::new(self.selection.entry_id.to_proto() as usize) .indent_level(self.details.depth) - .indent_step_size(settings.indent_size) + .indent_step_size(px(settings.indent_size)) .child(if let Some(icon) = &self.details.icon { div().child(Icon::from_path(icon.clone())) } else { @@ -2855,7 +2855,7 @@ impl Panel for ProjectPanel { DockPosition::Left | DockPosition::Bottom => ProjectPanelDockPosition::Left, DockPosition::Right => ProjectPanelDockPosition::Right, }; - settings.dock = dock; + settings.dock = Some(dock); }, ); } @@ -3029,7 +3029,7 @@ mod tests { cx.update_global::(|store, cx| { store.update_user_settings::(cx, |worktree_settings| { worktree_settings.file_scan_exclusions = - vec!["**/.git".to_string(), "**/4/**".to_string()]; + Some(vec!["**/.git".to_string(), "**/4/**".to_string()]); }); }); }); @@ -4818,10 +4818,10 @@ mod tests { cx.update(|cx| { cx.update_global::(|store, cx| { store.update_user_settings::(cx, |worktree_settings| { - worktree_settings.file_scan_exclusions = Vec::new(); + worktree_settings.file_scan_exclusions = Some(Vec::new()); }); store.update_user_settings::(cx, |project_panel_settings| { - project_panel_settings.auto_reveal_entries = false + project_panel_settings.auto_reveal_entries = Some(false) }); }) }); @@ -4940,7 +4940,7 @@ mod tests { cx.update(|cx| { cx.update_global::(|store, cx| { store.update_user_settings::(cx, |project_panel_settings| { - project_panel_settings.auto_reveal_entries = true + project_panel_settings.auto_reveal_entries = Some(true) }); }) }); @@ -5054,10 +5054,10 @@ mod tests { cx.update(|cx| { cx.update_global::(|store, cx| { store.update_user_settings::(cx, |worktree_settings| { - worktree_settings.file_scan_exclusions = Vec::new(); + worktree_settings.file_scan_exclusions = Some(Vec::new()); }); store.update_user_settings::(cx, |project_panel_settings| { - project_panel_settings.auto_reveal_entries = false + project_panel_settings.auto_reveal_entries = Some(false) }); }) }); @@ -5256,7 +5256,7 @@ mod tests { cx.update_global::(|store, cx| { store.update_user_settings::(cx, |project_settings| { project_settings.file_scan_exclusions = - vec!["excluded_dir".to_string(), "**/.git".to_string()]; + Some(vec!["excluded_dir".to_string(), "**/.git".to_string()]); }); }); }); @@ -5569,10 +5569,10 @@ mod tests { cx.update_global::(|store, cx| { store.update_user_settings::(cx, |project_panel_settings| { - project_panel_settings.auto_fold_dirs = false; + project_panel_settings.auto_fold_dirs = Some(false); }); store.update_user_settings::(cx, |worktree_settings| { - worktree_settings.file_scan_exclusions = Vec::new(); + worktree_settings.file_scan_exclusions = Some(Vec::new()); }); }); }); @@ -5591,10 +5591,10 @@ mod tests { cx.update_global::(|store, cx| { store.update_user_settings::(cx, |project_panel_settings| { - project_panel_settings.auto_fold_dirs = false; + project_panel_settings.auto_fold_dirs = Some(false); }); store.update_user_settings::(cx, |worktree_settings| { - worktree_settings.file_scan_exclusions = Vec::new(); + worktree_settings.file_scan_exclusions = Some(Vec::new()); }); }); }); diff --git a/crates/project_panel/src/project_panel_settings.rs b/crates/project_panel/src/project_panel_settings.rs index 6910b4627a..4d73ae9245 100644 --- a/crates/project_panel/src/project_panel_settings.rs +++ b/crates/project_panel/src/project_panel_settings.rs @@ -2,7 +2,6 @@ use gpui::Pixels; use schemars::JsonSchema; use serde_derive::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; -use ui::px; #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Copy, PartialEq)] #[serde(rename_all = "snake_case")] @@ -11,50 +10,20 @@ pub enum ProjectPanelDockPosition { Right, } -#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, JsonSchema)] -#[serde(default)] +#[derive(Deserialize, Debug, Clone, Copy, PartialEq)] pub struct ProjectPanelSettings { - /// Whether to show the project panel button in the status bar. pub button: bool, - /// Customize default width (in pixels) taken by project panel pub default_width: Pixels, - /// The position of project panel pub dock: ProjectPanelDockPosition, - /// Whether to show file icons in the project panel. pub file_icons: bool, - /// Whether to show folder icons or chevrons for directories in the project panel. pub folder_icons: bool, - /// Whether to show the git status in the project panel. pub git_status: bool, - /// Amount of indentation (in pixels) for nested items. - pub indent_size: Pixels, - /// Whether to reveal it in the project panel automatically, - /// when a corresponding project entry becomes active. - /// Gitignored entries are never auto revealed. + pub indent_size: f32, pub auto_reveal_entries: bool, - /// Whether to fold directories automatically - /// when directory has only one directory inside. pub auto_fold_dirs: bool, - /// Scrollbar-related settings pub scrollbar: ScrollbarSettings, } -impl Default for ProjectPanelSettings { - fn default() -> Self { - Self { - button: true, - default_width: px(240.), - dock: ProjectPanelDockPosition::Left, - file_icons: true, - folder_icons: true, - git_status: true, - indent_size: px(20.), - auto_reveal_entries: true, - auto_fold_dirs: true, - scrollbar: Default::default(), - } - } -} /// When to show the scrollbar in the project panel. /// /// Default: always @@ -68,7 +37,7 @@ pub enum ShowScrollbar { Never, } -#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] pub struct ScrollbarSettings { /// When to show the scrollbar in the project panel. /// @@ -76,10 +45,63 @@ pub struct ScrollbarSettings { pub show: ShowScrollbar, } +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct ScrollbarSettingsContent { + /// When to show the scrollbar in the project panel. + /// + /// Default: always + pub show: Option, +} + +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] +pub struct ProjectPanelSettingsContent { + /// Whether to show the project panel button in the status bar. + /// + /// Default: true + pub button: Option, + /// Customize default width (in pixels) taken by project panel + /// + /// Default: 240 + pub default_width: Option, + /// The position of project panel + /// + /// Default: left + pub dock: Option, + /// Whether to show file icons in the project panel. + /// + /// Default: true + pub file_icons: Option, + /// Whether to show folder icons or chevrons for directories in the project panel. + /// + /// Default: true + pub folder_icons: Option, + /// Whether to show the git status in the project panel. + /// + /// Default: true + pub git_status: Option, + /// Amount of indentation (in pixels) for nested items. + /// + /// Default: 20 + pub indent_size: Option, + /// Whether to reveal it in the project panel automatically, + /// when a corresponding project entry becomes active. + /// Gitignored entries are never auto revealed. + /// + /// Default: true + pub auto_reveal_entries: Option, + /// Whether to fold directories automatically + /// when directory has only one directory inside. + /// + /// Default: false + pub auto_fold_dirs: Option, + /// Scrollbar-related settings + pub scrollbar: Option, +} + impl Settings for ProjectPanelSettings { const KEY: Option<&'static str> = Some("project_panel"); - type FileContent = Self; + type FileContent = ProjectPanelSettingsContent; fn load( sources: SettingsSources, diff --git a/crates/recent_projects/src/dev_servers.rs b/crates/recent_projects/src/dev_servers.rs index b7fa635945..d8b10f31f9 100644 --- a/crates/recent_projects/src/dev_servers.rs +++ b/crates/recent_projects/src/dev_servers.rs @@ -48,6 +48,7 @@ use workspace::{notifications::DetachAndPromptErr, AppState, ModalView, Workspac use crate::open_dev_server_project; use crate::ssh_connections::connect_over_ssh; use crate::ssh_connections::open_ssh_project; +use crate::ssh_connections::RemoteSettingsContent; use crate::ssh_connections::SshConnection; use crate::ssh_connections::SshConnectionModal; use crate::ssh_connections::SshProject; @@ -1023,7 +1024,7 @@ impl DevServerProjects { fn update_settings_file( &mut self, cx: &mut ViewContext, - f: impl FnOnce(&mut SshSettings) + Send + Sync + 'static, + f: impl FnOnce(&mut RemoteSettingsContent) + Send + Sync + 'static, ) { let Some(fs) = self .workspace diff --git a/crates/recent_projects/src/ssh_connections.rs b/crates/recent_projects/src/ssh_connections.rs index b54196022d..8da4284b7f 100644 --- a/crates/recent_projects/src/ssh_connections.rs +++ b/crates/recent_projects/src/ssh_connections.rs @@ -22,24 +22,8 @@ use ui::{ use util::paths::PathWithPosition; use workspace::{AppState, ModalView, Workspace}; -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] -#[serde(default)] +#[derive(Deserialize)] pub struct SshSettings { - /// ssh_connections is an array of ssh connections. - /// By default this setting is null, which disables the direct ssh connection support. - /// You can configure these from `project: Open Remote` in the command palette. - /// Zed's ssh support will pull configuration from your ~/.ssh too. - /// Examples: - /// [ - /// { - /// "host": "example-box", - /// "projects": [ - /// { - /// "paths": ["/home/user/code/zed"] - /// } - /// ] - /// } - /// ] pub ssh_connections: Option>, } @@ -78,10 +62,15 @@ pub struct SshProject { pub paths: Vec, } +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +pub struct RemoteSettingsContent { + pub ssh_connections: Option>, +} + impl Settings for SshSettings { const KEY: Option<&'static str> = None; - type FileContent = Self; + type FileContent = RemoteSettingsContent; fn load(sources: SettingsSources, _: &mut AppContext) -> Result { sources.json_merge() diff --git a/crates/repl/src/jupyter_settings.rs b/crates/repl/src/jupyter_settings.rs index f441da4790..aefef6cec5 100644 --- a/crates/repl/src/jupyter_settings.rs +++ b/crates/repl/src/jupyter_settings.rs @@ -6,10 +6,8 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; -#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] -#[serde(default)] +#[derive(Debug, Default)] pub struct JupyterSettings { - /// Default kernels to select for each language. pub kernel_selections: HashMap, } @@ -22,10 +20,26 @@ impl JupyterSettings { } } +#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)] +pub struct JupyterSettingsContent { + /// Default kernels to select for each language. + /// + /// Default: `{}` + pub kernel_selections: Option>, +} + +impl Default for JupyterSettingsContent { + fn default() -> Self { + JupyterSettingsContent { + kernel_selections: Some(HashMap::new()), + } + } +} + impl Settings for JupyterSettings { const KEY: Option<&'static str> = Some("jupyter"); - type FileContent = Self; + type FileContent = JupyterSettingsContent; fn load( sources: SettingsSources, @@ -37,8 +51,10 @@ impl Settings for JupyterSettings { let mut settings = JupyterSettings::default(); for value in sources.defaults_and_customizations() { - for (k, v) in &value.kernel_selections { - settings.kernel_selections.insert(k.clone(), v.clone()); + if let Some(source) = &value.kernel_selections { + for (k, v) in source { + settings.kernel_selections.insert(k.clone(), v.clone()); + } } } diff --git a/crates/tasks_ui/src/settings.rs b/crates/tasks_ui/src/settings.rs index 4ad6f607b7..1bcd496264 100644 --- a/crates/tasks_ui/src/settings.rs +++ b/crates/tasks_ui/src/settings.rs @@ -2,26 +2,22 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; -#[derive(Clone, Serialize, Deserialize, PartialEq, JsonSchema)] -#[serde(default)] -/// Task-related settings. +#[derive(Serialize, Deserialize, PartialEq, Default)] pub(crate) struct TaskSettings { - /// Whether to show task status indicator in the status bar. Default: true pub(crate) show_status_indicator: bool, } -impl Default for TaskSettings { - fn default() -> Self { - Self { - show_status_indicator: true, - } - } +/// Task-related settings. +#[derive(Serialize, Deserialize, PartialEq, Default, Clone, JsonSchema)] +pub(crate) struct TaskSettingsContent { + /// Whether to show task status indicator in the status bar. Default: true + show_status_indicator: Option, } impl Settings for TaskSettings { const KEY: Option<&'static str> = Some("task"); - type FileContent = Self; + type FileContent = TaskSettingsContent; fn load( sources: SettingsSources, diff --git a/crates/vim/src/digraph.rs b/crates/vim/src/digraph.rs index 282016cfda..443b7ff378 100644 --- a/crates/vim/src/digraph.rs +++ b/crates/vim/src/digraph.rs @@ -132,7 +132,7 @@ mod test { let mut custom_digraphs = HashMap::default(); custom_digraphs.insert("|-".into(), "⊢".into()); custom_digraphs.insert(":)".into(), "👨‍💻".into()); - s.custom_digraphs = custom_digraphs; + s.custom_digraphs = Some(custom_digraphs); }); }); diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 815086d0be..8198c0da53 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -1184,7 +1184,7 @@ mod test { let mut cx = VimTestContext::new(cx, true).await; cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |s| { - s.use_multiline_find = true; + s.use_multiline_find = Some(true); }); }); @@ -1226,7 +1226,7 @@ mod test { let mut cx = VimTestContext::new(cx, true).await; cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |s| { - s.use_multiline_find = true; + s.use_multiline_find = Some(true); }); }); @@ -1268,7 +1268,7 @@ mod test { let mut cx = VimTestContext::new(cx, true).await; cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |s| { - s.use_smartcase_find = true; + s.use_smartcase_find = Some(true); }); }); diff --git a/crates/vim/src/normal/paste.rs b/crates/vim/src/normal/paste.rs index 6465e33e0f..05469dbf9f 100644 --- a/crates/vim/src/normal/paste.rs +++ b/crates/vim/src/normal/paste.rs @@ -291,7 +291,7 @@ mod test { cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |s| { - s.use_system_clipboard = UseSystemClipboard::Never + s.use_system_clipboard = Some(UseSystemClipboard::Never) }); }); @@ -327,7 +327,7 @@ mod test { cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |s| { - s.use_system_clipboard = UseSystemClipboard::OnYank + s.use_system_clipboard = Some(UseSystemClipboard::OnYank) }); }); @@ -584,7 +584,7 @@ mod test { cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |s| { - s.use_system_clipboard = UseSystemClipboard::Never + s.use_system_clipboard = Some(UseSystemClipboard::Never) }); }); @@ -630,7 +630,7 @@ mod test { cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |s| { - s.use_system_clipboard = UseSystemClipboard::Never + s.use_system_clipboard = Some(UseSystemClipboard::Never) }); }); @@ -659,7 +659,7 @@ mod test { cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |s| { - s.use_system_clipboard = UseSystemClipboard::Never + s.use_system_clipboard = Some(UseSystemClipboard::Never) }); }); @@ -707,7 +707,7 @@ mod test { cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |s| { - s.use_system_clipboard = UseSystemClipboard::Never + s.use_system_clipboard = Some(UseSystemClipboard::Never) }); }); diff --git a/crates/vim/src/normal/scroll.rs b/crates/vim/src/normal/scroll.rs index 6a20ea4eb3..f89faa3748 100644 --- a/crates/vim/src/normal/scroll.rs +++ b/crates/vim/src/normal/scroll.rs @@ -294,7 +294,7 @@ mod test { cx.update_global(|store: &mut SettingsStore, cx| { store.update_user_settings::(cx, |s| { - s.scroll_beyond_last_line = ScrollBeyondLastLine::Off + s.scroll_beyond_last_line = Some(ScrollBeyondLastLine::Off) }); }); diff --git a/crates/vim/src/normal/search.rs b/crates/vim/src/normal/search.rs index 6418475ad2..28f33d49d8 100644 --- a/crates/vim/src/normal/search.rs +++ b/crates/vim/src/normal/search.rs @@ -542,7 +542,7 @@ mod test { let mut cx = VimTestContext::new(cx, true).await; cx.update_global(|store: &mut SettingsStore, cx| { - store.update_user_settings::(cx, |s| s.search_wrap = false); + store.update_user_settings::(cx, |s| s.search_wrap = Some(false)); }); cx.set_state("ˇhi\nhigh\nhi\n", Mode::Normal); @@ -655,7 +655,7 @@ mod test { // check that searching with unable search wrap cx.update_global(|store: &mut SettingsStore, cx| { - store.update_user_settings::(cx, |s| s.search_wrap = false); + store.update_user_settings::(cx, |s| s.search_wrap = Some(false)); }); cx.set_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal); cx.simulate_keystrokes("/ c c enter"); diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index be7db47315..9c61e9cd93 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -1300,7 +1300,7 @@ async fn test_command_alias(cx: &mut gpui::TestAppContext) { store.update_user_settings::(cx, |s| { let mut aliases = HashMap::default(); aliases.insert("Q".to_string(), "upper".to_string()); - s.command_aliases = aliases + s.command_aliases = Some(aliases) }); }); diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index b68d2ede8b..c985f68e70 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -57,7 +57,7 @@ impl VimTestContext { pub fn new_with_lsp(mut cx: EditorLspTestContext, enabled: bool) -> VimTestContext { cx.update(|cx| { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, |s| *s = VimModeSetting(enabled)); + store.update_user_settings::(cx, |s| *s = Some(enabled)); }); settings::KeymapFile::load_asset("keymaps/default-macos.json", cx).unwrap(); if enabled { @@ -105,7 +105,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 = VimModeSetting(true)); + store.update_user_settings::(cx, |s| *s = Some(true)); }); }) } @@ -113,7 +113,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 = VimModeSetting(false)); + store.update_user_settings::(cx, |s| *s = Some(false)); }); }) } diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 6baca17948..6e03374c22 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -46,8 +46,6 @@ use crate::state::ReplayableAction; /// Whether or not to enable Vim mode. /// /// Default: false -#[derive(Copy, Clone, Default, Deserialize, Serialize, JsonSchema)] -#[serde(default, transparent)] pub struct VimModeSetting(pub bool); /// An Action to Switch between modes @@ -101,7 +99,7 @@ pub fn init(cx: &mut AppContext) { let fs = workspace.app_state().fs.clone(); let currently_enabled = Vim::enabled(cx); update_settings_file::(fs, cx, move |setting, _| { - *setting = VimModeSetting(!currently_enabled); + *setting = Some(!currently_enabled) }) }); @@ -1070,10 +1068,12 @@ impl Vim { impl Settings for VimModeSetting { const KEY: Option<&'static str> = Some("vim_mode"); - type FileContent = Self; + type FileContent = Option; fn load(sources: SettingsSources, _: &mut AppContext) -> Result { - Ok(sources.user.copied().unwrap_or(*sources.default)) + Ok(Self(sources.user.copied().flatten().unwrap_or( + sources.default.ok_or_else(Self::missing_default)?, + ))) } } @@ -1089,8 +1089,7 @@ pub enum UseSystemClipboard { OnYank, } -#[derive(Clone, Serialize, Deserialize, JsonSchema)] -#[serde(default)] +#[derive(Deserialize)] struct VimSettings { pub toggle_relative_line_numbers: bool, pub use_system_clipboard: UseSystemClipboard, @@ -1099,22 +1098,19 @@ struct VimSettings { pub custom_digraphs: HashMap>, } -impl Default for VimSettings { - fn default() -> Self { - Self { - toggle_relative_line_numbers: false, - use_system_clipboard: UseSystemClipboard::Always, - use_multiline_find: false, - use_smartcase_find: false, - custom_digraphs: Default::default(), - } - } +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +struct VimSettingsContent { + pub toggle_relative_line_numbers: Option, + pub use_system_clipboard: Option, + pub use_multiline_find: Option, + pub use_smartcase_find: Option, + pub custom_digraphs: Option>>, } impl Settings for VimSettings { const KEY: Option<&'static str> = Some("vim"); - type FileContent = Self; + type FileContent = VimSettingsContent; fn load(sources: SettingsSources, _: &mut AppContext) -> Result { sources.json_merge() diff --git a/crates/welcome/src/base_keymap_picker.rs b/crates/welcome/src/base_keymap_picker.rs index fd7361f9b3..96a9df9c3c 100644 --- a/crates/welcome/src/base_keymap_picker.rs +++ b/crates/welcome/src/base_keymap_picker.rs @@ -177,7 +177,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate { .report_setting_event("keymap", base_keymap.to_string()); update_settings_file::(self.fs.clone(), cx, move |setting, _| { - *setting = base_keymap; + *setting = Some(base_keymap) }); } diff --git a/crates/welcome/src/base_keymap_setting.rs b/crates/welcome/src/base_keymap_setting.rs index 0c1724627c..1b52bbc9f9 100644 --- a/crates/welcome/src/base_keymap_setting.rs +++ b/crates/welcome/src/base_keymap_setting.rs @@ -87,15 +87,15 @@ impl BaseKeymap { impl Settings for BaseKeymap { const KEY: Option<&'static str> = Some("base_keymap"); - type FileContent = Self; + type FileContent = Option; fn load( sources: SettingsSources, _: &mut gpui::AppContext, ) -> anyhow::Result { - if let Some(user_value) = sources.user.copied() { + if let Some(Some(user_value)) = sources.user.copied() { return Ok(user_value); } - Ok(*sources.default) + sources.default.ok_or_else(Self::missing_default) } } diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 787c2e589b..fc837c6867 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -188,7 +188,7 @@ impl Render for WelcomePage { this.update_settings::( selection, cx, - |setting, value| *setting = VimModeSetting(value), + |setting, value| *setting = Some(value), ); }), )) diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 46b8f3bf7f..935f0268b6 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -36,49 +36,20 @@ use util::ResultExt; pub const LEADER_UPDATE_THROTTLE: Duration = Duration::from_millis(200); -#[derive(Clone, Serialize, Deserialize, JsonSchema)] -#[serde(default)] +#[derive(Deserialize)] pub struct ItemSettings { - /// Whether to show the Git file status on a tab item. pub git_status: bool, - /// Position of the close button in a tab. pub close_position: ClosePosition, - /// Whether to show the file icon for a tab. pub file_icons: bool, } -impl Default for ItemSettings { - fn default() -> Self { - Self { - git_status: false, - close_position: ClosePosition::Right, - file_icons: false, - } - } -} - -#[derive(Clone, Serialize, Deserialize, JsonSchema)] -#[serde(default)] +#[derive(Deserialize)] pub struct PreviewTabsSettings { - /// Whether to show opened editors as preview tabs. - /// Preview tabs do not stay open, are reused until explicitly set to be kept open opened (via double-click or editing) and show file names in italic. pub enabled: bool, - /// Whether to open tabs in preview mode when selected from the file finder. pub enable_preview_from_file_finder: bool, - /// Whether a preview tab gets replaced when code navigation is used to navigate away from the tab. pub enable_preview_from_code_navigation: bool, } -impl Default for PreviewTabsSettings { - fn default() -> Self { - Self { - enabled: true, - enable_preview_from_file_finder: false, - enable_preview_from_code_navigation: false, - } - } -} - #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum ClosePosition { @@ -96,10 +67,43 @@ impl ClosePosition { } } +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +pub struct ItemSettingsContent { + /// Whether to show the Git file status on a tab item. + /// + /// Default: false + git_status: Option, + /// Position of the close button in a tab. + /// + /// Default: right + close_position: Option, + /// Whether to show the file icon for a tab. + /// + /// Default: false + file_icons: Option, +} + +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +pub struct PreviewTabsSettingsContent { + /// Whether to show opened editors as preview tabs. + /// Preview tabs do not stay open, are reused until explicitly set to be kept open opened (via double-click or editing) and show file names in italic. + /// + /// Default: true + enabled: Option, + /// Whether to open tabs in preview mode when selected from the file finder. + /// + /// Default: false + enable_preview_from_file_finder: Option, + /// Whether a preview tab gets replaced when code navigation is used to navigate away from the tab. + /// + /// Default: false + enable_preview_from_code_navigation: Option, +} + impl Settings for ItemSettings { const KEY: Option<&'static str> = Some("tabs"); - type FileContent = Self; + type FileContent = ItemSettingsContent; fn load(sources: SettingsSources, _: &mut AppContext) -> Result { sources.json_merge() @@ -109,7 +113,7 @@ impl Settings for ItemSettings { impl Settings for PreviewTabsSettings { const KEY: Option<&'static str> = Some("preview_tabs"); - type FileContent = Self; + type FileContent = PreviewTabsSettingsContent; fn load(sources: SettingsSources, _: &mut AppContext) -> Result { sources.json_merge() diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 0d77427794..a7c63c57f6 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -6465,7 +6465,7 @@ mod tests { item.update(cx, |item, cx| { SettingsStore::update_global(cx, |settings, cx| { settings.update_user_settings::(cx, |settings| { - settings.autosave = AutosaveSetting::OnWindowChange; + settings.autosave = Some(AutosaveSetting::OnWindowChange); }) }); item.is_dirty = true; @@ -6485,7 +6485,7 @@ mod tests { cx.focus_self(); SettingsStore::update_global(cx, |settings, cx| { settings.update_user_settings::(cx, |settings| { - settings.autosave = AutosaveSetting::OnFocusChange; + settings.autosave = Some(AutosaveSetting::OnFocusChange); }) }); item.is_dirty = true; @@ -6508,7 +6508,7 @@ mod tests { item.update(cx, |item, cx| { SettingsStore::update_global(cx, |settings, cx| { settings.update_user_settings::(cx, |settings| { - settings.autosave = AutosaveSetting::AfterDelay { milliseconds: 500 }; + settings.autosave = Some(AutosaveSetting::AfterDelay { milliseconds: 500 }); }) }); item.is_dirty = true; @@ -6527,7 +6527,7 @@ mod tests { item.update(cx, |item, cx| { SettingsStore::update_global(cx, |settings, cx| { settings.update_user_settings::(cx, |settings| { - settings.autosave = AutosaveSetting::OnFocusChange; + settings.autosave = Some(AutosaveSetting::OnFocusChange); }) }); item.is_dirty = true; diff --git a/crates/workspace/src/workspace_settings.rs b/crates/workspace/src/workspace_settings.rs index f87840eb30..52827c6941 100644 --- a/crates/workspace/src/workspace_settings.rs +++ b/crates/workspace/src/workspace_settings.rs @@ -5,58 +5,22 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources}; -#[derive(Clone, Serialize, Deserialize, JsonSchema)] -#[serde(default)] +#[derive(Deserialize)] pub struct WorkspaceSettings { - /// Scale by which to zoom the active pane. - /// When set to 1.0, the active pane has the same size as others, - /// but when set to a larger value, the active pane takes up more space. pub active_pane_magnification: f32, - /// Direction to split horizontally. pub pane_split_direction_horizontal: PaneSplitDirectionHorizontal, - /// Direction to split vertically. pub pane_split_direction_vertical: PaneSplitDirectionVertical, - /// Centered layout related settings. pub centered_layout: CenteredLayoutSettings, - /// Whether or not to prompt the user to confirm before closing the application. pub confirm_quit: bool, - /// Whether or not to show the call status icon in the status bar. pub show_call_status_icon: bool, - /// When to automatically save edited buffers. pub autosave: AutosaveSetting, - /// Controls previous session restoration in freshly launched Zed instance. pub restore_on_startup: RestoreOnStartupBehavior, - /// The size of the workspace split drop targets on the outer edges. - /// Given as a fraction that will be multiplied by the smaller dimension of the workspace. pub drop_target_size: f32, - /// Whether to close the window when using 'close active item' on a workspace with no tabs pub when_closing_with_no_tabs: CloseWindowWhenNoItems, - /// Whether to use the system provided dialogs for Open and Save As. - /// When set to false, Zed will use the built-in keyboard-first pickers. pub use_system_path_prompts: bool, - /// Aliases for the command palette. When you type a key in this map, - /// it will be assumed to equal the value. pub command_aliases: HashMap, } -impl Default for WorkspaceSettings { - fn default() -> Self { - Self { - active_pane_magnification: 1.0, - pane_split_direction_horizontal: PaneSplitDirectionHorizontal::Up, - pane_split_direction_vertical: PaneSplitDirectionVertical::Left, - centered_layout: CenteredLayoutSettings::default(), - confirm_quit: false, - show_call_status_icon: true, - autosave: AutosaveSetting::Off, - restore_on_startup: RestoreOnStartupBehavior::default(), - drop_target_size: 0.2, - when_closing_with_no_tabs: CloseWindowWhenNoItems::default(), - use_system_path_prompts: true, - command_aliases: HashMap::default(), - } - } -} #[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum CloseWindowWhenNoItems { @@ -91,22 +55,77 @@ pub enum RestoreOnStartupBehavior { LastSession, } -#[derive(Clone, Serialize, Deserialize, JsonSchema)] -#[serde(default)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +pub struct WorkspaceSettingsContent { + /// Scale by which to zoom the active pane. + /// When set to 1.0, the active pane has the same size as others, + /// but when set to a larger value, the active pane takes up more space. + /// + /// Default: `1.0` + pub active_pane_magnification: Option, + // Direction to split horizontally. + // + // Default: "up" + pub pane_split_direction_horizontal: Option, + // Direction to split vertically. + // + // Default: "left" + pub pane_split_direction_vertical: Option, + // Centered layout related settings. + pub centered_layout: Option, + /// Whether or not to prompt the user to confirm before closing the application. + /// + /// Default: false + pub confirm_quit: Option, + /// Whether or not to show the call status icon in the status bar. + /// + /// Default: true + pub show_call_status_icon: Option, + /// When to automatically save edited buffers. + /// + /// Default: off + pub autosave: Option, + /// Controls previous session restoration in freshly launched Zed instance. + /// Values: none, last_workspace, last_session + /// Default: last_session + pub restore_on_startup: Option, + /// The size of the workspace split drop targets on the outer edges. + /// Given as a fraction that will be multiplied by the smaller dimension of the workspace. + /// + /// Default: `0.2` (20% of the smaller dimension of the workspace) + pub drop_target_size: Option, + /// Whether to close the window when using 'close active item' on a workspace with no tabs + /// + /// Default: auto ("on" on macOS, "off" otherwise) + pub when_closing_with_no_tabs: Option, + /// Whether to use the system provided dialogs for Open and Save As. + /// When set to false, Zed will use the built-in keyboard-first pickers. + /// + /// Default: true + pub use_system_path_prompts: Option, + /// Aliases for the command palette. When you type a key in this map, + /// it will be assumed to equal the value. + /// + /// Default: true + pub command_aliases: Option>, +} + +#[derive(Deserialize)] pub struct TabBarSettings { - /// Whether or not to show the tab bar in the editor. pub show: bool, - /// Whether or not to show the navigation history buttons in the tab bar. pub show_nav_history_buttons: bool, } -impl Default for TabBarSettings { - fn default() -> Self { - Self { - show_nav_history_buttons: true, - show: true, - } - } +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +pub struct TabBarSettingsContent { + /// Whether or not to show the tab bar in the editor. + /// + /// Default: true + pub show: Option, + /// Whether or not to show the navigation history buttons in the tab bar. + /// + /// Default: true + pub show_nav_history_buttons: Option, } #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] @@ -144,26 +163,17 @@ pub struct CenteredLayoutSettings { /// /// Default: 0.2 pub left_padding: Option, - /// The relative width of the right padding of the central pane from the - /// workspace when the centered layout is used. + // The relative width of the right padding of the central pane from the + // workspace when the centered layout is used. /// /// Default: 0.2 pub right_padding: Option, } -impl Default for CenteredLayoutSettings { - fn default() -> Self { - Self { - left_padding: Some(0.2), - right_padding: Some(0.2), - } - } -} - impl Settings for WorkspaceSettings { const KEY: Option<&'static str> = None; - type FileContent = Self; + type FileContent = WorkspaceSettingsContent; fn load(sources: SettingsSources, _: &mut AppContext) -> Result { sources.json_merge() @@ -173,7 +183,7 @@ impl Settings for WorkspaceSettings { impl Settings for TabBarSettings { const KEY: Option<&'static str> = Some("tab_bar"); - type FileContent = Self; + type FileContent = TabBarSettingsContent; fn load(sources: SettingsSources, _: &mut AppContext) -> Result { sources.json_merge() diff --git a/crates/worktree/src/worktree_settings.rs b/crates/worktree/src/worktree_settings.rs index 82be3a8028..32851d963a 100644 --- a/crates/worktree/src/worktree_settings.rs +++ b/crates/worktree/src/worktree_settings.rs @@ -25,8 +25,7 @@ impl WorktreeSettings { } } -#[derive(Clone, Serialize, Deserialize, JsonSchema)] -#[serde(default)] +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] pub struct WorktreeSettingsContent { /// Completely ignore files matching globs from `file_scan_exclusions` /// @@ -40,42 +39,12 @@ pub struct WorktreeSettingsContent { /// "**/.classpath", /// "**/.settings" /// ] - pub file_scan_exclusions: Vec, + #[serde(default)] + pub file_scan_exclusions: Option>, /// Treat the files matching these globs as `.env` files. /// Default: [ "**/.env*" ] - pub private_files: Vec, -} - -impl Default for WorktreeSettingsContent { - fn default() -> Self { - Self { - private_files: [ - "**/.env*", - "**/*.pem", - "**/*.key", - "**/*.cert", - "**/*.crt", - "**/secrets.yml", - ] - .into_iter() - .map(str::to_owned) - .collect(), - file_scan_exclusions: [ - "**/.git", - "**/.svn", - "**/.hg", - "**/CVS", - "**/.DS_Store", - "**/Thumbs.db", - "**/.classpath", - "**/.settings", - ] - .into_iter() - .map(str::to_owned) - .collect(), - } - } + pub private_files: Option>, } impl Settings for WorktreeSettings { @@ -88,8 +57,8 @@ impl Settings for WorktreeSettings { _: &mut AppContext, ) -> anyhow::Result { let result: WorktreeSettingsContent = sources.json_merge()?; - let mut file_scan_exclusions = result.file_scan_exclusions; - let mut private_files = result.private_files; + let mut file_scan_exclusions = result.file_scan_exclusions.unwrap_or_default(); + let mut private_files = result.private_files.unwrap_or_default(); file_scan_exclusions.sort(); private_files.sort(); Ok(Self { diff --git a/crates/worktree/src/worktree_tests.rs b/crates/worktree/src/worktree_tests.rs index 455bc62a79..929dc01c6d 100644 --- a/crates/worktree/src/worktree_tests.rs +++ b/crates/worktree/src/worktree_tests.rs @@ -673,7 +673,7 @@ async fn test_rescan_with_gitignore(cx: &mut TestAppContext) { cx.update(|cx| { cx.update_global::(|store, cx| { store.update_user_settings::(cx, |project_settings| { - project_settings.file_scan_exclusions = Vec::new(); + project_settings.file_scan_exclusions = Some(Vec::new()); }); }); }); @@ -910,7 +910,7 @@ async fn test_file_scan_exclusions(cx: &mut TestAppContext) { cx.update_global::(|store, cx| { store.update_user_settings::(cx, |project_settings| { project_settings.file_scan_exclusions = - vec!["**/foo/**".to_string(), "**/.DS_Store".to_string()]; + Some(vec!["**/foo/**".to_string(), "**/.DS_Store".to_string()]); }); }); }); @@ -945,7 +945,8 @@ async fn test_file_scan_exclusions(cx: &mut TestAppContext) { cx.update(|cx| { cx.update_global::(|store, cx| { store.update_user_settings::(cx, |project_settings| { - project_settings.file_scan_exclusions = vec!["**/node_modules/**".to_string()]; + project_settings.file_scan_exclusions = + Some(vec!["**/node_modules/**".to_string()]); }); }); }); @@ -1008,11 +1009,11 @@ async fn test_fs_events_in_exclusions(cx: &mut TestAppContext) { cx.update(|cx| { cx.update_global::(|store, cx| { store.update_user_settings::(cx, |project_settings| { - project_settings.file_scan_exclusions = vec![ + project_settings.file_scan_exclusions = Some(vec![ "**/.git".to_string(), "node_modules/".to_string(), "build_output".to_string(), - ]; + ]); }); }); }); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 9f670efcd7..93fee57ecd 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1996,7 +1996,7 @@ mod tests { cx.update_global::(|store, cx| { store.update_user_settings::(cx, |project_settings| { project_settings.file_scan_exclusions = - vec!["excluded_dir".to_string(), "**/.git".to_string()]; + Some(vec!["excluded_dir".to_string(), "**/.git".to_string()]); }); }); });