From 7de2084bcf9f9a7df1f91a191aaa7c2180ae9201 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Wed, 17 Sep 2025 00:26:50 -0600 Subject: [PATCH] EDITOR!! --- crates/editor/src/code_completion_tests.rs | 3 +- crates/editor/src/code_context_menus.rs | 2 +- crates/editor/src/display_map.rs | 22 +- crates/editor/src/editor.rs | 31 +- crates/editor/src/editor_settings.rs | 949 ++++++------------ crates/editor/src/editor_settings_controls.rs | 87 +- crates/editor/src/editor_tests.rs | 64 +- crates/editor/src/element.rs | 54 +- crates/editor/src/hover_links.rs | 2 +- crates/editor/src/hover_popover.rs | 2 +- crates/editor/src/inlay_hint_cache.rs | 6 +- crates/editor/src/jsx_tag_auto_close.rs | 13 +- crates/language/src/buffer.rs | 19 +- .../settings/src/editable_setting_control.rs | 5 +- crates/settings/src/settings_content.rs | 5 + .../settings/src/settings_content/editor.rs | 586 +++++++++++ .../settings/src/settings_content/language.rs | 2 +- .../settings/src/settings_content/terminal.rs | 2 +- crates/ui/src/components/scrollbar.rs | 11 + 19 files changed, 1052 insertions(+), 813 deletions(-) create mode 100644 crates/settings/src/settings_content/editor.rs diff --git a/crates/editor/src/code_completion_tests.rs b/crates/editor/src/code_completion_tests.rs index a1d9f04a9c590ef1f20779bf19c2fe0be8905709..ec97c0ebb31952da9ad8e9e6f4f75b4b0078c4a3 100644 --- a/crates/editor/src/code_completion_tests.rs +++ b/crates/editor/src/code_completion_tests.rs @@ -1,9 +1,10 @@ -use crate::{code_context_menus::CompletionsMenu, editor_settings::SnippetSortOrder}; +use crate::code_context_menus::CompletionsMenu; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::TestAppContext; use language::CodeLabel; use lsp::{CompletionItem, CompletionItemKind, LanguageServerId}; use project::{Completion, CompletionSource}; +use settings::SnippetSortOrder; use std::sync::Arc; use std::sync::atomic::AtomicBool; use text::Anchor; diff --git a/crates/editor/src/code_context_menus.rs b/crates/editor/src/code_context_menus.rs index 9384e2efed162e1a21b0b31fdc0e7f65ba25b130..a89125a3aa6aebe23665469f34962dbacddc52d6 100644 --- a/crates/editor/src/code_context_menus.rs +++ b/crates/editor/src/code_context_menus.rs @@ -32,7 +32,6 @@ use ui::{Color, IntoElement, ListItem, Pixels, Popover, Styled, prelude::*}; use util::ResultExt; use crate::CodeActionSource; -use crate::editor_settings::SnippetSortOrder; use crate::hover_popover::{hover_markdown_style, open_markdown_url}; use crate::{ CodeActionProvider, CompletionId, CompletionItemKind, CompletionProvider, DisplayRow, Editor, @@ -40,6 +39,7 @@ use crate::{ actions::{ConfirmCodeAction, ConfirmCompletion}, split_words, styled_runs_for_code_label, }; +use settings::SnippetSortOrder; pub const MENU_GAP: Pixels = px(4.); pub const MENU_ASIDE_X_PADDING: Pixels = px(16.); diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 1acbdab7a6646fe46b9ad9d9cb09c1549d64bb1a..cc6bb3571bc11c836fa4d13abb76f6cd4a554755 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -1529,12 +1529,11 @@ pub mod tests { use language::{ Buffer, Diagnostic, DiagnosticEntry, DiagnosticSet, Language, LanguageConfig, LanguageMatcher, - language_settings::{AllLanguageSettings, AllLanguageSettingsContent}, }; use lsp::LanguageServerId; use project::Project; use rand::{Rng, prelude::*}; - use settings::SettingsStore; + use settings::{SettingsContent, SettingsStore}; use smol::stream::StreamExt; use std::{env, sync::Arc}; use text::PointUtf16; @@ -1564,7 +1563,9 @@ pub mod tests { log::info!("wrap width: {:?}", wrap_width); cx.update(|cx| { - init_test(cx, |s| s.defaults.tab_size = NonZeroU32::new(tab_size)); + init_test(cx, |s| { + s.project.all_languages.defaults.tab_size = NonZeroU32::new(tab_size) + }); }); let buffer = cx.update(|cx| { @@ -1623,8 +1624,9 @@ pub mod tests { log::info!("setting tab size to {:?}", tab_size); cx.update(|cx| { cx.update_global::(|store, cx| { - store.update_user_settings::(cx, |s| { - s.defaults.tab_size = NonZeroU32::new(tab_size); + store.update_user_settings(cx, |s| { + s.project.all_languages.defaults.tab_size = + NonZeroU32::new(tab_size); }); }); }); @@ -2084,7 +2086,11 @@ pub mod tests { ); language.set_theme(&theme); - cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap()))); + cx.update(|cx| { + init_test(cx, |s| { + s.project.all_languages.defaults.tab_size = Some(2.try_into().unwrap()) + }) + }); let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx)); cx.condition(&buffer, |buf, _| !buf.is_parsing()).await; @@ -2910,7 +2916,7 @@ pub mod tests { chunks } - fn init_test(cx: &mut App, f: impl Fn(&mut AllLanguageSettingsContent)) { + fn init_test(cx: &mut App, f: impl Fn(&mut SettingsContent)) { let settings = SettingsStore::test(cx); cx.set_global(settings); workspace::init_settings(cx); @@ -2919,7 +2925,7 @@ pub mod tests { Project::init_settings(cx); theme::init(LoadThemes::JustBase, cx); cx.update_global::(|store, cx| { - store.update_user_settings::(cx, f); + store.update_user_settings(cx, f); }); } } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 00ae0bcbac1e49e193739ed8b8af77a8c02dc845..28dd71c94b341e42fe6a68de6efcf097c7536e6c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -126,8 +126,8 @@ use language::{ Language, OffsetRangeExt, Point, Runnable, RunnableRange, Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery, language_settings::{ - self, InlayHintSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode, - all_language_settings, language_settings, + self, LspInsertMode, RewrapBehavior, WordsCompletionMode, all_language_settings, + language_settings, }, point_from_lsp, point_to_lsp, text_diff_with_options, }; @@ -160,9 +160,7 @@ use project::{ }, git_store::{GitStoreEvent, RepositoryEvent}, lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle}, - project_settings::{ - DiagnosticSeverity, GitGutterSetting, GoToDiagnosticSeverityFilter, ProjectSettings, - }, + project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter, ProjectSettings}, }; use rand::seq::SliceRandom; use rpc::{ErrorCode, ErrorExt, proto::PeerId}; @@ -171,7 +169,10 @@ use selections_collection::{ MutableSelectionsCollection, SelectionsCollection, resolve_selections, }; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsLocation, SettingsStore, update_settings_file}; +use settings::{ + GitGutterSetting, InlayHintSettings, Settings, SettingsLocation, SettingsStore, + update_settings_file, +}; use smallvec::{SmallVec, smallvec}; use snippet::Snippet; use std::{ @@ -5587,8 +5588,9 @@ impl Editor { .language_at(buffer_position) .map(|language| language.name()); - let completion_settings = - language_settings(language.clone(), buffer_snapshot.file(), cx).completions; + let completion_settings = language_settings(language.clone(), buffer_snapshot.file(), cx) + .completions + .clone(); let show_completion_documentation = buffer_snapshot .settings_at(buffer_position, cx) @@ -18990,8 +18992,8 @@ 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 = Some(!current_show); + update_settings_file(fs, cx, move |setting, _| { + setting.tab_bar.get_or_insert_default().show = Some(!current_show); }); } @@ -21659,11 +21661,12 @@ impl Editor { } } +// todo(settings_refactor) this should not be! fn vim_enabled(cx: &App) -> bool { cx.global::() .raw_user_settings() - .get("vim_mode") - == Some(&serde_json::Value::Bool(true)) + .and_then(|settings| settings.content.vim_mode) + == Some(true) } fn process_completion_for_edit( @@ -22896,7 +22899,7 @@ fn inlay_hint_settings( ) -> InlayHintSettings { let file = snapshot.file_at(location); let language = snapshot.language_at(location).map(|l| l.name()); - language_settings(language, file, cx).inlay_hints + language_settings(language, file, cx).inlay_hints.clone() } fn consume_contiguous_rows( @@ -23103,7 +23106,7 @@ impl EditorSnapshot { let show_git_gutter = self.show_git_diff_gutter.unwrap_or_else(|| { matches!( ProjectSettings::get_global(cx).git.git_gutter, - Some(GitGutterSetting::TrackedFiles) + 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 09f496637498b2535f8836282ffed4ea30950a4f..f184aed90b85071418a006a36293d235d9a580a4 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -4,15 +4,19 @@ use std::num::NonZeroU32; use gpui::App; use language::CursorShape; use project::project_settings::DiagnosticSeverity; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsKey, SettingsSources, SettingsUi, VsCodeSettings}; +pub use settings::{ + CurrentLineHighlight, DisplayIn, DocumentColorsRenderMode, DoubleClickInMultibuffer, + GoToDefinitionFallback, HideMouseMode, MinimapThumb, MinimapThumbBorder, MultiCursorModifier, + ScrollBeyondLastLine, ScrollbarDiagnostics, SeedQuerySetting, ShowMinimap, SnippetSortOrder, + VsCodeSettings, +}; +use settings::{Settings, SettingsContent}; use ui::scrollbars::{ScrollbarVisibility, ShowScrollbar}; -use util::serde::default_true; +use util::MergeFrom; /// Imports from the VSCode settings at /// https://code.visualstudio.com/docs/reference/default-settings -#[derive(Deserialize, Clone)] +#[derive(Clone)] pub struct EditorSettings { pub cursor_blink: bool, pub cursor_shape: Option, @@ -41,83 +45,22 @@ pub struct EditorSettings { pub expand_excerpt_lines: u32, pub excerpt_context_lines: u32, pub middle_click_paste: bool, - #[serde(default)] pub double_click_in_multibuffer: DoubleClickInMultibuffer, pub search_wrap: bool, - #[serde(default)] pub search: SearchSettings, pub auto_signature_help: bool, pub show_signature_help_after_edits: bool, - #[serde(default)] pub go_to_definition_fallback: GoToDefinitionFallback, pub jupyter: Jupyter, pub hide_mouse: Option, pub snippet_sort_order: SnippetSortOrder, - #[serde(default)] pub diagnostics_max_severity: Option, pub inline_code_actions: bool, pub drag_and_drop_selection: DragAndDropSelection, pub lsp_document_colors: DocumentColorsRenderMode, pub minimum_contrast_for_highlights: f32, } - -/// How to render LSP `textDocument/documentColor` colors in the editor. -#[derive( - Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi, -)] -#[serde(rename_all = "snake_case")] -pub enum DocumentColorsRenderMode { - /// Do not query and render document colors. - None, - /// Render document colors as inlay hints near the color text. - #[default] - Inlay, - /// Draw a border around the color text. - Border, - /// Draw a background behind the color text. - Background, -} - -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi)] -#[serde(rename_all = "snake_case")] -pub enum CurrentLineHighlight { - // Don't highlight the current line. - None, - // Highlight the gutter area. - Gutter, - // Highlight the editor area. - Line, - // Highlight the full line. - All, -} - -/// When to populate a new search's query based on the text under the cursor. -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi)] -#[serde(rename_all = "snake_case")] -pub enum SeedQuerySetting { - /// Always populate the search query with the word under the cursor. - Always, - /// Only populate the search query when there is text selected. - Selection, - /// Never populate the search query - Never, -} - -/// What to do when multibuffer is double clicked in some of its excerpts (parts of singleton buffers). -#[derive( - Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi, -)] -#[serde(rename_all = "snake_case")] -pub enum DoubleClickInMultibuffer { - /// Behave as a regular buffer and select the whole word. - #[default] - Select, - /// Open the excerpt clicked as a new buffer in the new tab, if no `alt` modifier was pressed during double click. - /// Otherwise, behave as a regular buffer and select the whole word. - Open, -} - -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone)] pub struct Jupyter { /// Whether the Jupyter feature is enabled. /// @@ -125,18 +68,7 @@ pub struct Jupyter { pub enabled: bool, } -#[derive( - Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi, -)] -#[serde(rename_all = "snake_case")] -pub struct JupyterContent { - /// Whether the Jupyter feature is enabled. - /// - /// Default: true - pub enabled: Option, -} - -#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct StatusBar { /// Whether to display the active language button in the status bar. /// @@ -148,7 +80,7 @@ pub struct StatusBar { pub cursor_position_button: bool, } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Toolbar { pub breadcrumbs: bool, pub quick_actions: bool, @@ -157,7 +89,7 @@ pub struct Toolbar { pub code_actions: bool, } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Scrollbar { pub show: ShowScrollbar, pub git_diff: bool, @@ -169,7 +101,7 @@ pub struct Scrollbar { pub axes: ScrollbarAxes, } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct Minimap { pub show: ShowMinimap, pub display_in: DisplayIn, @@ -197,7 +129,7 @@ impl Minimap { } } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Gutter { pub min_line_number_digits: usize, pub line_numbers: bool, @@ -206,69 +138,8 @@ pub struct Gutter { pub folds: bool, } -/// When to show the minimap in the editor. -/// -/// Default: never -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum ShowMinimap { - /// Follow the visibility of the scrollbar. - Auto, - /// Always show the minimap. - Always, - /// Never show the minimap. - #[default] - Never, -} - -/// Where to show the minimap in the editor. -/// -/// Default: all_editors -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum DisplayIn { - /// Show on all open editors. - AllEditors, - /// Show the minimap on the active editor only. - #[default] - ActiveEditor, -} - -/// When to show the minimap thumb. -/// -/// Default: always -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum MinimapThumb { - /// Show the minimap thumb only when the mouse is hovering over the minimap. - Hover, - /// Always show the minimap thumb. - #[default] - Always, -} - -/// Defines the border style for the minimap's scrollbar thumb. -/// -/// Default: left_open -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum MinimapThumbBorder { - /// Displays a border on all sides of the thumb. - Full, - /// Displays a border on all sides except the left side of the thumb. - #[default] - LeftOpen, - /// Displays a border on all sides except the right side of the thumb. - RightOpen, - /// Displays a border only on the left side of the thumb. - LeftOnly, - /// Displays the thumb without any border. - None, -} - /// Forcefully enable or disable the scrollbar for each axis -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "lowercase")] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct ScrollbarAxes { /// When false, forcefully disables the horizontal scrollbar. Otherwise, obey other settings. /// @@ -282,480 +153,29 @@ pub struct ScrollbarAxes { } /// Whether to allow drag and drop text selection in buffer. -#[derive( - Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, SettingsUi, -)] +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] pub struct DragAndDropSelection { /// When true, enables drag and drop text selection in buffer. /// /// Default: true - #[serde(default = "default_true")] pub enabled: bool, /// The delay in milliseconds that must elapse before drag and drop is allowed. Otherwise, a new text selection is created. /// /// Default: 300 - #[serde(default = "default_drag_and_drop_selection_delay_ms")] pub delay: u64, } -fn default_drag_and_drop_selection_delay_ms() -> u64 { - 300 -} - -/// Which diagnostic indicators to show in the scrollbar. -/// -/// Default: all -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "lowercase")] -pub enum ScrollbarDiagnostics { - /// Show all diagnostic levels: hint, information, warnings, error. - All, - /// Show only the following diagnostic levels: information, warning, error. - Information, - /// Show only the following diagnostic levels: warning, error. - Warning, - /// Show only the following diagnostic level: error. - Error, - /// Do not show diagnostics. - None, -} - -/// The key to use for adding multiple cursors -/// -/// Default: alt -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, SettingsUi)] -#[serde(rename_all = "snake_case")] -pub enum MultiCursorModifier { - Alt, - #[serde(alias = "cmd", alias = "ctrl")] - CmdOrCtrl, -} - -/// Whether the editor will scroll beyond the last line. -/// -/// Default: one_page -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, SettingsUi)] -#[serde(rename_all = "snake_case")] -pub enum ScrollBeyondLastLine { - /// The editor will not scroll beyond the last line. - Off, - - /// The editor will scroll beyond the last line by one page. - OnePage, - - /// The editor will scroll beyond the last line by the same number of lines as vertical_scroll_margin. - VerticalScrollMargin, -} - /// Default options for buffer and project search items. -#[derive( - Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, SettingsUi, -)] +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] pub struct SearchSettings { /// Whether to show the project search button in the status bar. - #[serde(default = "default_true")] pub button: bool, - #[serde(default)] pub whole_word: bool, - #[serde(default)] pub case_sensitive: bool, - #[serde(default)] pub include_ignored: bool, - #[serde(default)] pub regex: bool, } - -/// What to do when go to definition yields no results. -#[derive( - Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi, -)] -#[serde(rename_all = "snake_case")] -pub enum GoToDefinitionFallback { - /// Disables the fallback. - None, - /// Looks up references of the same symbol instead. - #[default] - FindAllReferences, -} - -/// Determines when the mouse cursor should be hidden in an editor or input box. -/// -/// Default: on_typing_and_movement -#[derive( - Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi, -)] -#[serde(rename_all = "snake_case")] -pub enum HideMouseMode { - /// Never hide the mouse cursor - Never, - /// Hide only when typing - OnTyping, - /// Hide on both typing and cursor movement - #[default] - OnTypingAndMovement, -} - -/// Determines how snippets are sorted relative to other completion items. -/// -/// Default: inline -#[derive( - Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi, -)] -#[serde(rename_all = "snake_case")] -pub enum SnippetSortOrder { - /// Place snippets at the top of the completion list - Top, - /// Sort snippets normally using the default comparison logic - #[default] - Inline, - /// Place snippets at the bottom of the completion list - Bottom, - /// Do not show snippets in the completion list - None, -} - -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, SettingsUi, SettingsKey)] -#[settings_ui(group = "Editor")] -#[settings_key(None)] -pub struct EditorSettingsContent { - /// Whether the cursor blinks in the editor. - /// - /// Default: true - pub cursor_blink: Option, - /// Cursor shape for the default editor. - /// Can be "bar", "block", "underline", or "hollow". - /// - /// Default: bar - pub cursor_shape: Option, - /// Determines when the mouse cursor should be hidden in an editor or input box. - /// - /// Default: on_typing_and_movement - pub hide_mouse: Option, - /// Determines how snippets are sorted relative to other completion items. - /// - /// Default: inline - pub snippet_sort_order: Option, - /// How to highlight the current line in the editor. - /// - /// Default: all - pub current_line_highlight: Option, - /// Whether to highlight all occurrences of the selected text in an editor. - /// - /// Default: true - pub selection_highlight: Option, - /// Whether the text selection should have rounded corners. - /// - /// Default: true - pub rounded_selection: Option, - /// The debounce delay before querying highlights from the language - /// server based on the current cursor location. - /// - /// Default: 75 - pub lsp_highlight_debounce: Option, - /// Whether to show the informational hover box when moving the mouse - /// over symbols in the editor. - /// - /// Default: true - pub hover_popover_enabled: Option, - /// Time to wait in milliseconds before showing the informational hover box. - /// - /// Default: 300 - pub hover_popover_delay: Option, - /// Status bar related settings - pub status_bar: Option, - /// Toolbar related settings - pub toolbar: Option, - /// Scrollbar related settings - pub scrollbar: Option, - /// Minimap related settings - pub minimap: 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, - /// Whether to scroll when clicking near the edge of the visible text area. - /// - /// Default: false - pub autoscroll_on_clicks: Option, - /// The number of characters to keep on either side when scrolling with the mouse. - /// - /// Default: 5. - pub horizontal_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, - /// Scroll sensitivity multiplier for fast scrolling. This multiplier is applied - /// to both the horizontal and vertical delta values while scrolling. Fast scrolling - /// happens when a user holds the alt or option key while scrolling. - /// - /// Default: 4.0 - pub fast_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, - /// Determines the modifier to be used to add multiple cursors with the mouse. The open hover link mouse gestures will adapt such that it do not conflict with the multicursor modifier. - /// - /// 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, - - /// How many lines of context to provide in multibuffer excerpts by default - /// - /// Default: 2 - pub excerpt_context_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: false - pub show_signature_help_after_edits: Option, - /// The minimum APCA perceptual contrast to maintain when - /// rendering text over highlight backgrounds in the editor. - /// - /// Values range from 0 to 106. Set to 0 to disable adjustments. - /// Default: 45 - pub minimum_contrast_for_highlights: Option, - - /// Whether to follow-up empty go to definition responses from the language server or not. - /// `FindAllReferences` allows to look up references of the same symbol instead. - /// `None` disables the fallback. - /// - /// Default: FindAllReferences - pub go_to_definition_fallback: Option, - - /// Jupyter REPL settings. - pub jupyter: Option, - - /// Which level to use to filter out diagnostics displayed in the editor. - /// - /// Affects the editor rendering only, and does not interrupt - /// the functionality of diagnostics fetching and project diagnostics editor. - /// Which files containing diagnostic errors/warnings to mark in the tabs. - /// Diagnostics are only shown when file icons are also active. - /// - /// Shows all diagnostics if not specified. - /// - /// Default: warning - #[serde(default)] - pub diagnostics_max_severity: Option, - - /// Whether to show code action button at start of buffer line. - /// - /// Default: true - pub inline_code_actions: Option, - - /// Drag and drop related settings - pub drag_and_drop_selection: Option, - - /// How to render LSP `textDocument/documentColor` colors in the editor. - /// - /// Default: [`DocumentColorsRenderMode::Inlay`] - pub lsp_document_colors: Option, -} - -// Status bar related settings -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq, SettingsUi)] -pub struct StatusBarContent { - /// Whether to display the active language button in the status bar. - /// - /// Default: true - pub active_language_button: Option, - /// Whether to show the cursor position button in the status bar. - /// - /// Default: true - pub cursor_position_button: Option, -} - -// Toolbar related settings -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq, SettingsUi)] -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, - /// Whether to display Agent review buttons in the editor toolbar. - /// Only applicable while reviewing a file edited by the Agent. - /// - /// Default: true - pub agent_review: Option, - /// Whether to display code action buttons in the editor toolbar. - /// - /// Default: false - pub code_actions: Option, -} - -/// Scrollbar related settings -#[derive( - Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default, SettingsUi, -)] -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 text occurrences in the scrollbar. - /// - /// Default: true - pub selected_text: Option, - /// Whether to show selected symbol occurrences in the scrollbar. - /// - /// Default: true - pub selected_symbol: Option, - /// Which diagnostic indicators to show in the scrollbar: - /// - /// Default: all - pub diagnostics: Option, - /// Whether to show cursor positions in the scrollbar. - /// - /// Default: true - pub cursors: Option, - /// Forcefully enable or disable the scrollbar for each axis - pub axes: Option, -} - -/// Minimap related settings -#[derive( - Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, SettingsUi, -)] -pub struct MinimapContent { - /// When to show the minimap in the editor. - /// - /// Default: never - pub show: Option, - - /// Where to show the minimap in the editor. - /// - /// Default: [`DisplayIn::ActiveEditor`] - pub display_in: Option, - - /// When to show the minimap thumb. - /// - /// Default: always - pub thumb: Option, - - /// Defines the border style for the minimap's scrollbar thumb. - /// - /// Default: left_open - pub thumb_border: Option, - - /// How to highlight the current line in the minimap. - /// - /// Default: inherits editor line highlights setting - pub current_line_highlight: Option>, - - /// Maximum number of columns to display in the minimap. - /// - /// Default: 80 - pub max_width_columns: Option, -} - -/// Forcefully enable or disable the scrollbar for each axis -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default)] -pub struct ScrollbarAxesContent { - /// When false, forcefully disables the horizontal scrollbar. Otherwise, obey other settings. - /// - /// Default: true - horizontal: Option, - - /// When false, forcefully disables the vertical scrollbar. Otherwise, obey other settings. - /// - /// Default: true - vertical: Option, -} - -/// Gutter related settings -#[derive( - Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq, SettingsUi, -)] -#[settings_ui(group = "Gutter")] -pub struct GutterContent { - /// Whether to show line numbers in the gutter. - /// - /// Default: true - pub line_numbers: Option, - /// Minimum number of characters to reserve space for in the gutter. - /// - /// Default: 4 - pub min_line_number_digits: Option, - /// Whether to show runnable buttons in the gutter. - /// - /// Default: true - pub runnables: Option, - /// Whether to show breakpoints in the gutter. - /// - /// Default: true - pub breakpoints: Option, - /// Whether to show fold buttons in the gutter. - /// - /// Default: true - pub folds: Option, -} - impl EditorSettings { pub fn jupyter_enabled(cx: &App) -> bool { EditorSettings::get_global(cx).jupyter.enabled @@ -769,16 +189,266 @@ impl ScrollbarVisibility for EditorSettings { } impl Settings for EditorSettings { - type FileContent = EditorSettingsContent; + fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self { + let editor = content.editor.clone(); + let scrollbar = editor.scrollbar.unwrap(); + let minimap = editor.minimap.unwrap(); + let gutter = editor.gutter.unwrap(); + let axes = scrollbar.axes.unwrap(); + let status_bar = editor.status_bar.unwrap(); + let toolbar = editor.toolbar.unwrap(); + let search = editor.search.unwrap(); + let drag_and_drop_selection = editor.drag_and_drop_selection.unwrap(); + Self { + cursor_blink: editor.cursor_blink.unwrap(), + cursor_shape: editor.cursor_shape.map(Into::into), + current_line_highlight: editor.current_line_highlight.unwrap(), + selection_highlight: editor.selection_highlight.unwrap(), + rounded_selection: editor.rounded_selection.unwrap(), + lsp_highlight_debounce: editor.lsp_highlight_debounce.unwrap(), + hover_popover_enabled: editor.hover_popover_enabled.unwrap(), + hover_popover_delay: editor.hover_popover_delay.unwrap(), + status_bar: StatusBar { + active_language_button: status_bar.active_language_button.unwrap(), + cursor_position_button: status_bar.cursor_position_button.unwrap(), + }, + toolbar: Toolbar { + breadcrumbs: toolbar.breadcrumbs.unwrap(), + quick_actions: toolbar.quick_actions.unwrap(), + selections_menu: toolbar.selections_menu.unwrap(), + agent_review: toolbar.agent_review.unwrap(), + code_actions: toolbar.code_actions.unwrap(), + }, + scrollbar: Scrollbar { + show: scrollbar.show.map(Into::into).unwrap(), + git_diff: scrollbar.git_diff.unwrap(), + selected_text: scrollbar.selected_text.unwrap(), + selected_symbol: scrollbar.selected_symbol.unwrap(), + search_results: scrollbar.search_results.unwrap(), + diagnostics: scrollbar.diagnostics.unwrap(), + cursors: scrollbar.cursors.unwrap(), + axes: ScrollbarAxes { + horizontal: axes.horizontal.unwrap(), + vertical: axes.vertical.unwrap(), + }, + }, + minimap: Minimap { + show: minimap.show.unwrap(), + display_in: minimap.display_in.unwrap(), + thumb: minimap.thumb.unwrap(), + thumb_border: minimap.thumb_border.unwrap(), + current_line_highlight: minimap.current_line_highlight.flatten(), + max_width_columns: minimap.max_width_columns.unwrap(), + }, + gutter: Gutter { + min_line_number_digits: gutter.min_line_number_digits.unwrap(), + line_numbers: gutter.line_numbers.unwrap(), + runnables: gutter.runnables.unwrap(), + breakpoints: gutter.breakpoints.unwrap(), + folds: gutter.folds.unwrap(), + }, + scroll_beyond_last_line: editor.scroll_beyond_last_line.unwrap(), + vertical_scroll_margin: editor.vertical_scroll_margin.unwrap(), + autoscroll_on_clicks: editor.autoscroll_on_clicks.unwrap(), + horizontal_scroll_margin: editor.horizontal_scroll_margin.unwrap(), + scroll_sensitivity: editor.scroll_sensitivity.unwrap(), + fast_scroll_sensitivity: editor.fast_scroll_sensitivity.unwrap(), + relative_line_numbers: editor.relative_line_numbers.unwrap(), + seed_search_query_from_cursor: editor.seed_search_query_from_cursor.unwrap(), + use_smartcase_search: editor.use_smartcase_search.unwrap(), + multi_cursor_modifier: editor.multi_cursor_modifier.unwrap(), + redact_private_values: editor.redact_private_values.unwrap(), + expand_excerpt_lines: editor.expand_excerpt_lines.unwrap(), + excerpt_context_lines: editor.excerpt_context_lines.unwrap(), + middle_click_paste: editor.middle_click_paste.unwrap(), + double_click_in_multibuffer: editor.double_click_in_multibuffer.unwrap(), + search_wrap: editor.search_wrap.unwrap(), + search: SearchSettings { + button: search.button.unwrap(), + whole_word: search.whole_word.unwrap(), + case_sensitive: search.case_sensitive.unwrap(), + include_ignored: search.include_ignored.unwrap(), + regex: search.regex.unwrap(), + }, + auto_signature_help: editor.auto_signature_help.unwrap(), + show_signature_help_after_edits: editor.show_signature_help_after_edits.unwrap(), + go_to_definition_fallback: editor.go_to_definition_fallback.unwrap(), + jupyter: Jupyter { + enabled: editor.jupyter.unwrap().enabled.unwrap(), + }, + hide_mouse: editor.hide_mouse, + snippet_sort_order: editor.snippet_sort_order.unwrap(), + diagnostics_max_severity: editor.diagnostics_max_severity.map(Into::into), + inline_code_actions: editor.inline_code_actions.unwrap(), + drag_and_drop_selection: DragAndDropSelection { + enabled: drag_and_drop_selection.enabled.unwrap(), + delay: drag_and_drop_selection.delay.unwrap(), + }, + lsp_document_colors: editor.lsp_document_colors.unwrap(), + minimum_contrast_for_highlights: editor.minimum_contrast_for_highlights.unwrap(), + } + } - fn load(sources: SettingsSources, _: &mut App) -> anyhow::Result { - sources.json_merge() + fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) { + let editor = &content.editor; + self.cursor_blink.merge_from(&editor.cursor_blink); + if let Some(cursor_shape) = editor.cursor_shape { + self.cursor_shape = Some(cursor_shape.into()) + } + self.current_line_highlight + .merge_from(&editor.current_line_highlight); + self.selection_highlight + .merge_from(&editor.selection_highlight); + self.rounded_selection.merge_from(&editor.rounded_selection); + self.lsp_highlight_debounce + .merge_from(&editor.lsp_highlight_debounce); + self.hover_popover_enabled + .merge_from(&editor.hover_popover_enabled); + self.hover_popover_delay + .merge_from(&editor.hover_popover_delay); + self.scroll_beyond_last_line + .merge_from(&editor.scroll_beyond_last_line); + self.vertical_scroll_margin + .merge_from(&editor.vertical_scroll_margin); + self.autoscroll_on_clicks + .merge_from(&editor.autoscroll_on_clicks); + self.horizontal_scroll_margin + .merge_from(&editor.horizontal_scroll_margin); + self.scroll_sensitivity + .merge_from(&editor.scroll_sensitivity); + self.fast_scroll_sensitivity + .merge_from(&editor.fast_scroll_sensitivity); + self.relative_line_numbers + .merge_from(&editor.relative_line_numbers); + self.seed_search_query_from_cursor + .merge_from(&editor.seed_search_query_from_cursor); + self.use_smartcase_search + .merge_from(&editor.use_smartcase_search); + self.multi_cursor_modifier + .merge_from(&editor.multi_cursor_modifier); + self.redact_private_values + .merge_from(&editor.redact_private_values); + self.expand_excerpt_lines + .merge_from(&editor.expand_excerpt_lines); + self.excerpt_context_lines + .merge_from(&editor.excerpt_context_lines); + self.middle_click_paste + .merge_from(&editor.middle_click_paste); + self.double_click_in_multibuffer + .merge_from(&editor.double_click_in_multibuffer); + self.search_wrap.merge_from(&editor.search_wrap); + self.auto_signature_help + .merge_from(&editor.auto_signature_help); + self.show_signature_help_after_edits + .merge_from(&editor.show_signature_help_after_edits); + self.go_to_definition_fallback + .merge_from(&editor.go_to_definition_fallback); + if let Some(hide_mouse) = editor.hide_mouse { + self.hide_mouse = Some(hide_mouse) + } + self.snippet_sort_order + .merge_from(&editor.snippet_sort_order); + if let Some(diagnostics_max_severity) = editor.diagnostics_max_severity { + self.diagnostics_max_severity = Some(diagnostics_max_severity.into()); + } + self.inline_code_actions + .merge_from(&editor.inline_code_actions); + self.lsp_document_colors + .merge_from(&editor.lsp_document_colors); + self.minimum_contrast_for_highlights + .merge_from(&editor.minimum_contrast_for_highlights); + + if let Some(status_bar) = &editor.status_bar { + self.status_bar + .active_language_button + .merge_from(&status_bar.active_language_button); + self.status_bar + .cursor_position_button + .merge_from(&status_bar.cursor_position_button); + } + if let Some(toolbar) = &editor.toolbar { + self.toolbar.breadcrumbs.merge_from(&toolbar.breadcrumbs); + self.toolbar + .quick_actions + .merge_from(&toolbar.quick_actions); + self.toolbar + .selections_menu + .merge_from(&toolbar.selections_menu); + self.toolbar.agent_review.merge_from(&toolbar.agent_review); + self.toolbar.code_actions.merge_from(&toolbar.code_actions); + } + if let Some(scrollbar) = &editor.scrollbar { + self.scrollbar + .show + .merge_from(&scrollbar.show.map(Into::into)); + self.scrollbar.git_diff.merge_from(&scrollbar.git_diff); + self.scrollbar + .selected_text + .merge_from(&scrollbar.selected_text); + self.scrollbar + .selected_symbol + .merge_from(&scrollbar.selected_symbol); + self.scrollbar + .search_results + .merge_from(&scrollbar.search_results); + self.scrollbar + .diagnostics + .merge_from(&scrollbar.diagnostics); + self.scrollbar.cursors.merge_from(&scrollbar.cursors); + if let Some(axes) = &scrollbar.axes { + self.scrollbar.axes.horizontal.merge_from(&axes.horizontal); + self.scrollbar.axes.vertical.merge_from(&axes.vertical); + } + } + if let Some(minimap) = &editor.minimap { + self.minimap.show.merge_from(&minimap.show); + self.minimap.display_in.merge_from(&minimap.display_in); + self.minimap.thumb.merge_from(&minimap.thumb); + self.minimap.thumb_border.merge_from(&minimap.thumb_border); + self.minimap + .current_line_highlight + .merge_from(&minimap.current_line_highlight); + self.minimap + .max_width_columns + .merge_from(&minimap.max_width_columns); + } + if let Some(gutter) = &editor.gutter { + self.gutter + .min_line_number_digits + .merge_from(&gutter.min_line_number_digits); + self.gutter.line_numbers.merge_from(&gutter.line_numbers); + self.gutter.runnables.merge_from(&gutter.runnables); + self.gutter.breakpoints.merge_from(&gutter.breakpoints); + self.gutter.folds.merge_from(&gutter.folds); + } + if let Some(search) = &editor.search { + self.search.button.merge_from(&search.button); + self.search.whole_word.merge_from(&search.whole_word); + self.search + .case_sensitive + .merge_from(&search.case_sensitive); + self.search + .include_ignored + .merge_from(&search.include_ignored); + self.search.regex.merge_from(&search.regex); + } + if let Some(enabled) = editor.jupyter.as_ref().and_then(|jupyter| jupyter.enabled) { + self.jupyter.enabled = enabled; + } + if let Some(drag_and_drop_selection) = &editor.drag_and_drop_selection { + self.drag_and_drop_selection + .enabled + .merge_from(&drag_and_drop_selection.enabled); + self.drag_and_drop_selection + .delay + .merge_from(&drag_and_drop_selection.delay); + } } - fn import_from_vscode(vscode: &VsCodeSettings, current: &mut Self::FileContent) { + fn import_from_vscode(vscode: &VsCodeSettings, current: &mut SettingsContent) { vscode.enum_setting( "editor.cursorBlinking", - &mut current.cursor_blink, + &mut current.editor.cursor_blink, |s| match s { "blink" | "phase" | "expand" | "smooth" => Some(true), "solid" => Some(false), @@ -787,19 +457,19 @@ impl Settings for EditorSettings { ); vscode.enum_setting( "editor.cursorStyle", - &mut current.cursor_shape, + &mut current.editor.cursor_shape, |s| match s { - "block" => Some(CursorShape::Block), - "block-outline" => Some(CursorShape::Hollow), - "line" | "line-thin" => Some(CursorShape::Bar), - "underline" | "underline-thin" => Some(CursorShape::Underline), + "block" => Some(settings::CursorShape::Block), + "block-outline" => Some(settings::CursorShape::Hollow), + "line" | "line-thin" => Some(settings::CursorShape::Bar), + "underline" | "underline-thin" => Some(settings::CursorShape::Underline), _ => None, }, ); vscode.enum_setting( "editor.renderLineHighlight", - &mut current.current_line_highlight, + &mut current.editor.current_line_highlight, |s| match s { "gutter" => Some(CurrentLineHighlight::Gutter), "line" => Some(CurrentLineHighlight::Line), @@ -810,13 +480,22 @@ impl Settings for EditorSettings { vscode.bool_setting( "editor.selectionHighlight", - &mut current.selection_highlight, + &mut current.editor.selection_highlight, + ); + vscode.bool_setting( + "editor.roundedSelection", + &mut current.editor.rounded_selection, + ); + vscode.bool_setting( + "editor.hover.enabled", + &mut current.editor.hover_popover_enabled, + ); + vscode.u64_setting( + "editor.hover.delay", + &mut current.editor.hover_popover_delay, ); - vscode.bool_setting("editor.roundedSelection", &mut current.rounded_selection); - vscode.bool_setting("editor.hover.enabled", &mut current.hover_popover_enabled); - vscode.u64_setting("editor.hover.delay", &mut current.hover_popover_delay); - let mut gutter = GutterContent::default(); + let mut gutter = settings::GutterContent::default(); vscode.enum_setting( "editor.showFoldingControls", &mut gutter.folds, @@ -835,25 +514,25 @@ impl Settings for EditorSettings { _ => None, }, ); - if let Some(old_gutter) = current.gutter.as_mut() { + if let Some(old_gutter) = current.editor.gutter.as_mut() { if gutter.folds.is_some() { old_gutter.folds = gutter.folds } if gutter.line_numbers.is_some() { old_gutter.line_numbers = gutter.line_numbers } - } else if gutter != GutterContent::default() { - current.gutter = Some(gutter) + } else if gutter != settings::GutterContent::default() { + current.editor.gutter = Some(gutter) } if let Some(b) = vscode.read_bool("editor.scrollBeyondLastLine") { - current.scroll_beyond_last_line = Some(if b { + current.editor.scroll_beyond_last_line = Some(if b { ScrollBeyondLastLine::OnePage } else { ScrollBeyondLastLine::Off }) } - let mut scrollbar_axes = ScrollbarAxesContent::default(); + let mut scrollbar_axes = settings::ScrollbarAxesContent::default(); vscode.enum_setting( "editor.scrollbar.horizontal", &mut scrollbar_axes.horizontal, @@ -873,8 +552,8 @@ impl Settings for EditorSettings { }, ); - if scrollbar_axes != ScrollbarAxesContent::default() { - let scrollbar_settings = current.scrollbar.get_or_insert_default(); + if scrollbar_axes != settings::ScrollbarAxesContent::default() { + let scrollbar_settings = current.editor.scrollbar.get_or_insert_default(); let axes_settings = scrollbar_settings.axes.get_or_insert_default(); if let Some(vertical) = scrollbar_axes.vertical { @@ -888,23 +567,23 @@ impl Settings for EditorSettings { // TODO: check if this does the int->float conversion? vscode.f32_setting( "editor.cursorSurroundingLines", - &mut current.vertical_scroll_margin, + &mut current.editor.vertical_scroll_margin, ); vscode.f32_setting( "editor.mouseWheelScrollSensitivity", - &mut current.scroll_sensitivity, + &mut current.editor.scroll_sensitivity, ); vscode.f32_setting( "editor.fastScrollSensitivity", - &mut current.fast_scroll_sensitivity, + &mut current.editor.fast_scroll_sensitivity, ); if Some("relative") == vscode.read_string("editor.lineNumbers") { - current.relative_line_numbers = Some(true); + current.editor.relative_line_numbers = Some(true); } vscode.enum_setting( "editor.find.seedSearchStringFromSelection", - &mut current.seed_search_query_from_cursor, + &mut current.editor.seed_search_query_from_cursor, |s| match s { "always" => Some(SeedQuerySetting::Always), "selection" => Some(SeedQuerySetting::Selection), @@ -912,10 +591,10 @@ impl Settings for EditorSettings { _ => None, }, ); - vscode.bool_setting("search.smartCase", &mut current.use_smartcase_search); + vscode.bool_setting("search.smartCase", &mut current.editor.use_smartcase_search); vscode.enum_setting( "editor.multiCursorModifier", - &mut current.multi_cursor_modifier, + &mut current.editor.multi_cursor_modifier, |s| match s { "ctrlCmd" => Some(MultiCursorModifier::CmdOrCtrl), "alt" => Some(MultiCursorModifier::Alt), @@ -925,19 +604,19 @@ impl Settings for EditorSettings { vscode.bool_setting( "editor.parameterHints.enabled", - &mut current.auto_signature_help, + &mut current.editor.auto_signature_help, ); vscode.bool_setting( "editor.parameterHints.enabled", - &mut current.show_signature_help_after_edits, + &mut current.editor.show_signature_help_after_edits, ); if let Some(use_ignored) = vscode.read_bool("search.useIgnoreFiles") { - let search = current.search.get_or_insert_default(); - search.include_ignored = use_ignored; + let search = current.editor.search.get_or_insert_default(); + search.include_ignored = Some(use_ignored); } - let mut minimap = MinimapContent::default(); + let mut minimap = settings::MinimapContent::default(); let minimap_enabled = vscode.read_bool("editor.minimap.enabled").unwrap_or(true); let autohide = vscode.read_bool("editor.minimap.autohide"); let mut max_width_columns: Option = None; @@ -965,8 +644,8 @@ impl Settings for EditorSettings { }, ); - if minimap != MinimapContent::default() { - current.minimap = Some(minimap) + if minimap != settings::MinimapContent::default() { + current.editor.minimap = Some(minimap) } } } diff --git a/crates/editor/src/editor_settings_controls.rs b/crates/editor/src/editor_settings_controls.rs index 91022d94a8843a2e9b7e9c77137d4d2ba57bfa7f..a26af2739924152a972e00a660b03f72e3eff8d6 100644 --- a/crates/editor/src/editor_settings_controls.rs +++ b/crates/editor/src/editor_settings_controls.rs @@ -1,8 +1,8 @@ use std::sync::Arc; use gpui::{App, FontFeatures, FontWeight}; -use project::project_settings::{InlineBlameSettings, ProjectSettings}; -use settings::{EditableSettingControl, Settings}; +use project::project_settings::ProjectSettings; +use settings::{EditableSettingControl, Settings, SettingsContent}; use theme::{FontFamilyCache, FontFamilyName, ThemeSettings}; use ui::{ CheckboxWithLabel, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer, SettingsGroup, @@ -59,7 +59,6 @@ struct BufferFontFamilyControl; impl EditableSettingControl for BufferFontFamilyControl { type Value = SharedString; - type Settings = ThemeSettings; fn name(&self) -> SharedString { "Buffer Font Family".into() @@ -70,12 +69,8 @@ impl EditableSettingControl for BufferFontFamilyControl { settings.buffer_font.family.clone() } - fn apply( - settings: &mut ::FileContent, - value: Self::Value, - _cx: &App, - ) { - settings.buffer_font_family = Some(FontFamilyName(value.into())); + fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) { + settings.theme.buffer_font_family = Some(FontFamilyName(value.into())); } } @@ -118,7 +113,6 @@ struct BufferFontSizeControl; impl EditableSettingControl for BufferFontSizeControl { type Value = Pixels; - type Settings = ThemeSettings; fn name(&self) -> SharedString { "Buffer Font Size".into() @@ -128,12 +122,8 @@ impl EditableSettingControl for BufferFontSizeControl { ThemeSettings::get_global(cx).buffer_font_size(cx) } - fn apply( - settings: &mut ::FileContent, - value: Self::Value, - _cx: &App, - ) { - settings.buffer_font_size = Some(value.into()); + fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) { + settings.theme.buffer_font_size = Some(value.into()); } } @@ -162,7 +152,6 @@ struct BufferFontWeightControl; impl EditableSettingControl for BufferFontWeightControl { type Value = FontWeight; - type Settings = ThemeSettings; fn name(&self) -> SharedString { "Buffer Font Weight".into() @@ -173,12 +162,8 @@ impl EditableSettingControl for BufferFontWeightControl { settings.buffer_font.weight } - fn apply( - settings: &mut ::FileContent, - value: Self::Value, - _cx: &App, - ) { - settings.buffer_font_weight = Some(value.0); + fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) { + settings.theme.buffer_font_weight = Some(value.0); } } @@ -215,7 +200,6 @@ struct BufferFontLigaturesControl; impl EditableSettingControl for BufferFontLigaturesControl { type Value = bool; - type Settings = ThemeSettings; fn name(&self) -> SharedString { "Buffer Font Ligatures".into() @@ -230,14 +214,11 @@ impl EditableSettingControl for BufferFontLigaturesControl { .unwrap_or(true) } - fn apply( - settings: &mut ::FileContent, - value: Self::Value, - _cx: &App, - ) { + fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) { let value = if value { 1 } else { 0 }; let mut features = settings + .theme .buffer_font_features .as_ref() .map(|features| features.tag_value_list().to_vec()) @@ -249,7 +230,7 @@ impl EditableSettingControl for BufferFontLigaturesControl { features.push(("calt".into(), value)); } - settings.buffer_font_features = Some(FontFeatures(Arc::new(features))); + settings.theme.buffer_font_features = Some(FontFeatures(Arc::new(features))); } } @@ -279,7 +260,6 @@ struct InlineGitBlameControl; impl EditableSettingControl for InlineGitBlameControl { type Value = bool; - type Settings = ProjectSettings; fn name(&self) -> SharedString { "Inline Git Blame".into() @@ -290,19 +270,13 @@ impl EditableSettingControl for InlineGitBlameControl { settings.git.inline_blame_enabled() } - fn apply( - settings: &mut ::FileContent, - value: Self::Value, - _cx: &App, - ) { - 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() - }); - } + fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) { + settings + .git + .get_or_insert_default() + .inline_blame + .get_or_insert_default() + .enabled = Some(value) } } @@ -332,7 +306,6 @@ struct LineNumbersControl; impl EditableSettingControl for LineNumbersControl { type Value = bool; - type Settings = EditorSettings; fn name(&self) -> SharedString { "Line Numbers".into() @@ -343,19 +316,8 @@ impl EditableSettingControl for LineNumbersControl { settings.gutter.line_numbers } - fn apply( - settings: &mut ::FileContent, - value: Self::Value, - _cx: &App, - ) { - 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() - }); - } + fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) { + settings.editor.gutter.get_or_insert_default().line_numbers = Some(value); } } @@ -385,7 +347,6 @@ struct RelativeLineNumbersControl; impl EditableSettingControl for RelativeLineNumbersControl { type Value = bool; - type Settings = EditorSettings; fn name(&self) -> SharedString { "Relative Line Numbers".into() @@ -396,12 +357,8 @@ impl EditableSettingControl for RelativeLineNumbersControl { settings.relative_line_numbers } - fn apply( - settings: &mut ::FileContent, - value: Self::Value, - _cx: &App, - ) { - settings.relative_line_numbers = Some(value); + fn apply(settings: &mut SettingsContent, value: Self::Value, _cx: &App) { + settings.editor.relative_line_numbers = Some(value); } } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 131b03c61b9a36ca73bf91fbc3bdf35b0000e3ee..4973e0d9b98ac1e362518d68f081912ac13144a4 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -25,8 +25,8 @@ use language::{ DiagnosticSourceKind, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName, Override, Point, language_settings::{ - AllLanguageSettings, AllLanguageSettingsContent, CompletionSettings, FormatterList, - LanguageSettingsContent, LspInsertMode, PrettierSettings, SelectedFormatter, + CompletionSettings, FormatterList, LanguageSettingsContent, LspInsertMode, + SelectedFormatter, }, tree_sitter_python, }; @@ -38,9 +38,10 @@ use pretty_assertions::{assert_eq, assert_ne}; use project::{ FakeFs, debugger::breakpoint_store::{BreakpointState, SourceBreakpoint}, - project_settings::{LspSettings, ProjectSettings}, + project_settings::LspSettings, }; use serde_json::{self, json}; +use settings::{AllLanguageSettingsContent, ProjectSettingsContent}; use std::{cell::RefCell, future::Future, rc::Rc, sync::atomic::AtomicBool, time::Instant}; use std::{ iter, @@ -11699,10 +11700,7 @@ async fn test_document_format_manual_trigger(cx: &mut TestAppContext) { update_test_language_settings(cx, |settings| { // Enable Prettier formatting for the same buffer, and ensure // LSP is called instead of Prettier. - settings.defaults.prettier = Some(PrettierSettings { - allowed: true, - ..PrettierSettings::default() - }); + settings.defaults.prettier.get_or_insert_default().allowed = true; }); let mut fake_servers = language_registry.register_fake_lsp( "Rust", @@ -12088,10 +12086,7 @@ async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) { Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()), ))); update_test_language_settings(cx, |settings| { - settings.defaults.prettier = Some(PrettierSettings { - allowed: true, - ..PrettierSettings::default() - }); + settings.defaults.prettier.get_or_insert_default().allowed = true; }); let mut fake_servers = language_registry.register_fake_lsp( "TypeScript", @@ -12402,8 +12397,8 @@ 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 = Some(true); + settings.update_user_settings(cx, |settings| { + settings.editor.auto_signature_help = Some(true); }); }); }); @@ -12542,9 +12537,9 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestA cx.update(|cx| { cx.update_global::(|settings, cx| { - settings.update_user_settings::(cx, |settings| { - settings.auto_signature_help = Some(false); - settings.show_signature_help_after_edits = Some(false); + settings.update_user_settings(cx, |settings| { + settings.editor.auto_signature_help = Some(false); + settings.editor.show_signature_help_after_edits = Some(false); }); }); }); @@ -12669,9 +12664,9 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestA // Ensure that signature_help is called when enabled afte edits cx.update(|_, cx| { cx.update_global::(|settings, cx| { - settings.update_user_settings::(cx, |settings| { - settings.auto_signature_help = Some(false); - settings.show_signature_help_after_edits = Some(true); + settings.update_user_settings(cx, |settings| { + settings.editor.auto_signature_help = Some(false); + settings.editor.show_signature_help_after_edits = Some(true); }); }); }); @@ -12711,9 +12706,9 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut TestA // Ensure that signature_help is called when auto signature help override is enabled cx.update(|_, cx| { cx.update_global::(|settings, cx| { - settings.update_user_settings::(cx, |settings| { - settings.auto_signature_help = Some(true); - settings.show_signature_help_after_edits = Some(false); + settings.update_user_settings(cx, |settings| { + settings.editor.auto_signature_help = Some(true); + settings.editor.show_signature_help_after_edits = Some(false); }); }); }); @@ -12755,8 +12750,8 @@ async fn test_signature_help(cx: &mut TestAppContext) { init_test(cx, |_| {}); cx.update(|cx| { cx.update_global::(|settings, cx| { - settings.update_user_settings::(cx, |settings| { - settings.auto_signature_help = Some(true); + settings.update_user_settings(cx, |settings| { + settings.editor.auto_signature_help = Some(true); }); }); }); @@ -17067,7 +17062,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppCon let _fake_server = fake_servers.next().await.unwrap(); update_test_language_settings(cx, |language_settings| { language_settings.languages.0.insert( - language_name.clone(), + language_name.clone().0, LanguageSettingsContent { tab_size: NonZeroU32::new(8), ..Default::default() @@ -17991,10 +17986,7 @@ async fn test_document_format_with_prettier(cx: &mut TestAppContext) { Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()), ))); update_test_language_settings(cx, |settings| { - settings.defaults.prettier = Some(PrettierSettings { - allowed: true, - ..PrettierSettings::default() - }); + settings.defaults.prettier.get_or_insert_default().allowed = true; }); let test_plugin = "test_plugin"; @@ -23672,8 +23664,8 @@ println!("5"); }); cx.update_global(|store: &mut SettingsStore, cx| { - store.update_user_settings::(cx, |s| { - s.restore_on_file_reopen = Some(false); + store.update_user_settings(cx, |s| { + s.workspace.restore_on_file_reopen = Some(false); }); }); editor.update_in(cx, |editor, window, cx| { @@ -23697,8 +23689,8 @@ println!("5"); assert!(pane.active_item().is_none()); }); cx.update_global(|store: &mut SettingsStore, cx| { - store.update_user_settings::(cx, |s| { - s.restore_on_file_reopen = Some(true); + store.update_user_settings(cx, |s| { + s.workspace.restore_on_file_reopen = Some(true); }); }); @@ -25120,18 +25112,18 @@ pub(crate) fn update_test_language_settings( ) { cx.update(|cx| { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, f); + store.update_user_settings(cx, |settings| f(&mut settings.project.all_languages)); }); }); } pub(crate) fn update_test_project_settings( cx: &mut TestAppContext, - f: impl Fn(&mut ProjectSettings), + f: impl Fn(&mut ProjectSettingsContent), ) { cx.update(|cx| { SettingsStore::update_global(cx, |store, cx| { - store.update_user_settings::(cx, f); + store.update_user_settings(cx, |settings| f(&mut settings.project)); }); }); } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 603dcdece22053d131441929fe4270371e803463..ed35d853865adba0aa4a18218153e93afaff89a2 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -51,9 +51,7 @@ use gpui::{ transparent_black, }; use itertools::Itertools; -use language::language_settings::{ - IndentGuideBackgroundColoring, IndentGuideColoring, IndentGuideSettings, ShowWhitespaceSetting, -}; +use language::language_settings::{IndentGuideSettings, ShowWhitespaceSetting}; use markdown::Markdown; use multi_buffer::{ Anchor, ExcerptId, ExcerptInfo, ExpandExcerptDirection, ExpandInfo, MultiBufferPoint, @@ -63,9 +61,12 @@ use multi_buffer::{ use project::{ Entry, ProjectPath, debugger::breakpoint_store::{Breakpoint, BreakpointSessionState}, - project_settings::{GitGutterSetting, GitHunkStyleSetting, ProjectSettings}, + project_settings::ProjectSettings, +}; +use settings::{ + GitGutterSetting, GitHunkStyleSetting, IndentGuideBackgroundColoring, IndentGuideColoring, + Settings, }; -use settings::Settings; use smallvec::{SmallVec, smallvec}; use std::{ any::TypeId, @@ -2095,10 +2096,7 @@ impl EditorElement { .display_diff_hunks_for_rows(display_rows, folded_buffers) .map(|hunk| (hunk, None)) .collect::>(); - let git_gutter_setting = ProjectSettings::get_global(cx) - .git - .git_gutter - .unwrap_or_default(); + let git_gutter_setting = ProjectSettings::get_global(cx).git.git_gutter; if let GitGutterSetting::TrackedFiles = git_gutter_setting { for (hunk, hitbox) in &mut display_hunks { if matches!(hunk, DisplayDiffHunk::Unfolded { .. }) { @@ -2450,11 +2448,7 @@ impl EditorElement { let padding = { const INLINE_ACCEPT_SUGGESTION_EM_WIDTHS: f32 = 14.; - let mut padding = ProjectSettings::get_global(cx) - .git - .inline_blame - .unwrap_or_default() - .padding as f32; + let mut padding = ProjectSettings::get_global(cx).git.inline_blame.padding as f32; if let Some(edit_prediction) = editor.active_edit_prediction.as_ref() && let EditPrediction::Edit { @@ -2488,12 +2482,10 @@ impl EditorElement { let padded_line_end = line_end + padding; - let min_column_in_pixels = ProjectSettings::get_global(cx) - .git - .inline_blame - .map(|settings| settings.min_column) - .map(|col| self.column_pixels(col as usize, window)) - .unwrap_or(px(0.)); + let min_column_in_pixels = self.column_pixels( + ProjectSettings::get_global(cx).git.inline_blame.min_column as usize, + window, + ); let min_start = content_origin.x - scroll_pixel_position.x + min_column_in_pixels; cmp::max(padded_line_end, min_start) @@ -5666,7 +5658,7 @@ impl EditorElement { for indent_guide in indent_guides { let indent_accent_colors = cx.theme().accents().color_for_index(indent_guide.depth); - let settings = indent_guide.settings; + let settings = &indent_guide.settings; // TODO fixed for now, expose them through themes later const INDENT_AWARE_ALPHA: f32 = 0.2; @@ -6001,7 +5993,7 @@ impl EditorElement { .unwrap_or_else(|| { matches!( ProjectSettings::get_global(cx).git.git_gutter, - Some(GitGutterSetting::TrackedFiles) + GitGutterSetting::TrackedFiles ) }); if show_git_gutter { @@ -7296,10 +7288,10 @@ impl EditorElement { fn diff_hunk_hollow(status: DiffHunkStatus, cx: &mut App) -> bool { let unstaged = status.has_secondary_hunk(); - let unstaged_hollow = ProjectSettings::get_global(cx) - .git - .hunk_style - .is_some_and(|style| matches!(style, GitHunkStyleSetting::UnstagedHollow)); + let unstaged_hollow = matches!( + ProjectSettings::get_global(cx).git.hunk_style, + GitHunkStyleSetting::UnstagedHollow + ); unstaged == unstaged_hollow } @@ -8836,13 +8828,9 @@ impl Element for EditorElement { }) .flatten()?; let mut element = render_inline_blame_entry(blame_entry, style, cx)?; - let inline_blame_padding = ProjectSettings::get_global(cx) - .git - .inline_blame - .unwrap_or_default() - .padding - as f32 - * em_advance; + let inline_blame_padding = + ProjectSettings::get_global(cx).git.inline_blame.padding as f32 + * em_advance; Some( element .layout_as_root(AvailableSpace::min_size(), window, cx) diff --git a/crates/editor/src/hover_links.rs b/crates/editor/src/hover_links.rs index ba0b6f88683969aca3818a2795aa6b8454de3bb8..bf0367c159f5e576f9f2cf2a5c32f790f9c85ea5 100644 --- a/crates/editor/src/hover_links.rs +++ b/crates/editor/src/hover_links.rs @@ -931,8 +931,8 @@ mod tests { use futures::StreamExt; use gpui::Modifiers; use indoc::indoc; - use language::language_settings::InlayHintSettings; use lsp::request::{GotoDefinition, GotoTypeDefinition}; + use settings::InlayHintSettings; use util::{assert_set_eq, path}; use workspace::item::Item; diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 51be0d234eca9c2e6b908c0aba6f3746b3eff460..21b9c777e409ef2b2d8a3ecb990b5bd77d408ea5 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -1004,8 +1004,8 @@ mod tests { use collections::BTreeSet; use gpui::App; use indoc::indoc; - use language::language_settings::InlayHintSettings; use markdown::parser::MarkdownEvent; + use settings::InlayHintSettings; use smol::stream::StreamExt; use std::sync::atomic; use std::sync::atomic::AtomicUsize; diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index c1b0a7640c155fff02f0b778e8996a9b68ea452e..970c533710a09c84875ce0047060ede5e3a6aa56 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -25,7 +25,7 @@ use parking_lot::RwLock; use project::{InlayHint, ResolveState}; use collections::{HashMap, HashSet, hash_map}; -use language::language_settings::InlayHintSettings; +use settings::InlayHintSettings; use smol::lock::Semaphore; use sum_tree::Bias; use text::{BufferId, ToOffset, ToPoint}; @@ -1301,13 +1301,13 @@ pub mod tests { use futures::StreamExt; use gpui::{AppContext as _, Context, SemanticVersion, TestAppContext, WindowHandle}; use itertools::Itertools as _; - use language::{Capability, FakeLspAdapter, language_settings::AllLanguageSettingsContent}; + use language::{Capability, FakeLspAdapter}; use language::{Language, LanguageConfig, LanguageMatcher}; use lsp::FakeLanguageServer; use parking_lot::Mutex; use project::{FakeFs, Project}; use serde_json::json; - use settings::SettingsStore; + use settings::{AllLanguageSettingsContent, InlayHintSettings, SettingsStore}; use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; use text::Point; use util::path; diff --git a/crates/editor/src/jsx_tag_auto_close.rs b/crates/editor/src/jsx_tag_auto_close.rs index e6c518beae3ecf3741b5f74be6087628f5231c8c..f2bdb717efe4b4d7521d4d6906b3c0f77dcb14f6 100644 --- a/crates/editor/src/jsx_tag_auto_close.rs +++ b/crates/editor/src/jsx_tag_auto_close.rs @@ -620,14 +620,17 @@ mod jsx_tag_autoclose_tests { use super::*; use gpui::{AppContext as _, TestAppContext}; - use language::language_settings::JsxTagAutoCloseSettings; use languages::language; use multi_buffer::ExcerptRange; use text::Selection; async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext { init_test(cx, |settings| { - settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true }); + settings + .defaults + .jsx_tag_auto_close + .get_or_insert_default() + .enabled = true; }); let mut cx = EditorTestContext::new(cx).await; @@ -789,7 +792,11 @@ mod jsx_tag_autoclose_tests { #[gpui::test] async fn test_multibuffer(cx: &mut TestAppContext) { init_test(cx, |settings| { - settings.defaults.jsx_tag_auto_close = Some(JsxTagAutoCloseSettings { enabled: true }); + settings + .defaults + .jsx_tag_auto_close + .get_or_insert_default() + .enabled = true; }); let buffer_a = cx.new(|cx| { diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index f03df08a55b2759885d133b9a7dc3556b549a184..e55d1f2d2385d41fa30643987cfd026958b9b803 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -30,10 +30,9 @@ use gpui::{ use lsp::{LanguageServerId, NumberOrString}; use parking_lot::Mutex; -use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_json::Value; -use settings::{SettingsUi, WorktreeId}; +use settings::WorktreeId; use smallvec::SmallVec; use smol::future::yield_now; use std::{ @@ -174,10 +173,7 @@ pub enum IndentKind { } /// The shape of a selection cursor. -#[derive( - Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, SettingsUi, -)] -#[serde(rename_all = "snake_case")] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub enum CursorShape { /// A vertical bar #[default] @@ -190,6 +186,17 @@ pub enum CursorShape { Hollow, } +impl From for CursorShape { + fn from(shape: settings::CursorShape) -> Self { + match shape { + settings::CursorShape::Bar => CursorShape::Bar, + settings::CursorShape::Block => CursorShape::Block, + settings::CursorShape::Underline => CursorShape::Underline, + settings::CursorShape::Hollow => CursorShape::Hollow, + } + } +} + #[derive(Clone, Debug)] struct SelectionSet { line_mode: bool, diff --git a/crates/settings/src/editable_setting_control.rs b/crates/settings/src/editable_setting_control.rs index 40244c10c4e5789e56a8458e2cc0e7737f14a2d9..fd9d9869620f57b9cee9c522312bb2e59222e4c0 100644 --- a/crates/settings/src/editable_setting_control.rs +++ b/crates/settings/src/editable_setting_control.rs @@ -1,16 +1,13 @@ use fs::Fs; use gpui::{App, RenderOnce, SharedString}; -use crate::{Settings, settings_content::SettingsContent, update_settings_file}; +use crate::{settings_content::SettingsContent, update_settings_file}; /// A UI control that can be used to edit a setting. pub trait EditableSettingControl: RenderOnce { /// The type of the setting value. type Value: Send; - /// The settings type to which this setting belongs. - type Settings: Settings; - /// Returns the name of this setting. fn name(&self) -> SharedString; diff --git a/crates/settings/src/settings_content.rs b/crates/settings/src/settings_content.rs index 85aba4d3e04c0378c7ddb6dc0a09dc49216bbeef..c01253d8b98884b430a44b194416c2d9c07279b3 100644 --- a/crates/settings/src/settings_content.rs +++ b/crates/settings/src/settings_content.rs @@ -1,10 +1,12 @@ mod agent; +mod editor; mod language; mod project; mod terminal; mod theme; mod workspace; pub use agent::*; +pub use editor::*; pub use language::*; pub use project::*; pub use terminal::*; @@ -36,6 +38,9 @@ pub struct SettingsContent { #[serde(flatten)] pub workspace: WorkspaceSettingsContent, + #[serde(flatten)] + pub editor: EditorSettingsContent, + pub tabs: Option, pub tab_bar: Option, diff --git a/crates/settings/src/settings_content/editor.rs b/crates/settings/src/settings_content/editor.rs new file mode 100644 index 0000000000000000000000000000000000000000..5220c029697e9714dc80f31fec23a4406e8d1855 --- /dev/null +++ b/crates/settings/src/settings_content/editor.rs @@ -0,0 +1,586 @@ +use std::num; + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::{DiagnosticSeverityContent, ShowScrollbar}; + +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct EditorSettingsContent { + /// Whether the cursor blinks in the editor. + /// + /// Default: true + pub cursor_blink: Option, + /// Cursor shape for the default editor. + /// Can be "bar", "block", "underline", or "hollow". + /// + /// Default: bar + pub cursor_shape: Option, + /// Determines when the mouse cursor should be hidden in an editor or input box. + /// + /// Default: on_typing_and_movement + pub hide_mouse: Option, + /// Determines how snippets are sorted relative to other completion items. + /// + /// Default: inline + pub snippet_sort_order: Option, + /// How to highlight the current line in the editor. + /// + /// Default: all + pub current_line_highlight: Option, + /// Whether to highlight all occurrences of the selected text in an editor. + /// + /// Default: true + pub selection_highlight: Option, + /// Whether the text selection should have rounded corners. + /// + /// Default: true + pub rounded_selection: Option, + /// The debounce delay before querying highlights from the language + /// server based on the current cursor location. + /// + /// Default: 75 + pub lsp_highlight_debounce: Option, + /// Whether to show the informational hover box when moving the mouse + /// over symbols in the editor. + /// + /// Default: true + pub hover_popover_enabled: Option, + /// Time to wait in milliseconds before showing the informational hover box. + /// + /// Default: 300 + pub hover_popover_delay: Option, + /// Status bar related settings + pub status_bar: Option, + /// Toolbar related settings + pub toolbar: Option, + /// Scrollbar related settings + pub scrollbar: Option, + /// Minimap related settings + pub minimap: 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, + /// Whether to scroll when clicking near the edge of the visible text area. + /// + /// Default: false + pub autoscroll_on_clicks: Option, + /// The number of characters to keep on either side when scrolling with the mouse. + /// + /// Default: 5. + pub horizontal_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, + /// Scroll sensitivity multiplier for fast scrolling. This multiplier is applied + /// to both the horizontal and vertical delta values while scrolling. Fast scrolling + /// happens when a user holds the alt or option key while scrolling. + /// + /// Default: 4.0 + pub fast_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, + /// Determines the modifier to be used to add multiple cursors with the mouse. The open hover link mouse gestures will adapt such that it do not conflict with the multicursor modifier. + /// + /// 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, + + /// How many lines of context to provide in multibuffer excerpts by default + /// + /// Default: 2 + pub excerpt_context_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: false + pub show_signature_help_after_edits: Option, + /// The minimum APCA perceptual contrast to maintain when + /// rendering text over highlight backgrounds in the editor. + /// + /// Values range from 0 to 106. Set to 0 to disable adjustments. + /// Default: 45 + pub minimum_contrast_for_highlights: Option, + + /// Whether to follow-up empty go to definition responses from the language server or not. + /// `FindAllReferences` allows to look up references of the same symbol instead. + /// `None` disables the fallback. + /// + /// Default: FindAllReferences + pub go_to_definition_fallback: Option, + + /// Jupyter REPL settings. + pub jupyter: Option, + + /// Which level to use to filter out diagnostics displayed in the editor. + /// + /// Affects the editor rendering only, and does not interrupt + /// the functionality of diagnostics fetching and project diagnostics editor. + /// Which files containing diagnostic errors/warnings to mark in the tabs. + /// Diagnostics are only shown when file icons are also active. + /// + /// Shows all diagnostics if not specified. + /// + /// Default: warning + pub diagnostics_max_severity: Option, + + /// Whether to show code action button at start of buffer line. + /// + /// Default: true + pub inline_code_actions: Option, + + /// Drag and drop related settings + pub drag_and_drop_selection: Option, + + /// How to render LSP `textDocument/documentColor` colors in the editor. + /// + /// Default: [`DocumentColorsRenderMode::Inlay`] + pub lsp_document_colors: Option, +} + +// Status bar related settings +#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct StatusBarContent { + /// Whether to display the active language button in the status bar. + /// + /// Default: true + pub active_language_button: Option, + /// Whether to show the cursor position button in the status bar. + /// + /// Default: true + pub cursor_position_button: Option, +} + +// Toolbar related settings +#[derive(Debug, 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, + /// Whether to display Agent review buttons in the editor toolbar. + /// Only applicable while reviewing a file edited by the Agent. + /// + /// Default: true + pub agent_review: Option, + /// Whether to display code action buttons in the editor toolbar. + /// + /// Default: false + pub code_actions: Option, +} + +/// Scrollbar related settings +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default)] +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 text occurrences in the scrollbar. + /// + /// Default: true + pub selected_text: Option, + /// Whether to show selected symbol occurrences in the scrollbar. + /// + /// Default: true + pub selected_symbol: Option, + /// Which diagnostic indicators to show in the scrollbar: + /// + /// Default: all + pub diagnostics: Option, + /// Whether to show cursor positions in the scrollbar. + /// + /// Default: true + pub cursors: Option, + /// Forcefully enable or disable the scrollbar for each axis + pub axes: Option, +} + +/// Minimap related settings +#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] +pub struct MinimapContent { + /// When to show the minimap in the editor. + /// + /// Default: never + pub show: Option, + + /// Where to show the minimap in the editor. + /// + /// Default: [`DisplayIn::ActiveEditor`] + pub display_in: Option, + + /// When to show the minimap thumb. + /// + /// Default: always + pub thumb: Option, + + /// Defines the border style for the minimap's scrollbar thumb. + /// + /// Default: left_open + pub thumb_border: Option, + + /// How to highlight the current line in the minimap. + /// + /// Default: inherits editor line highlights setting + pub current_line_highlight: Option>, + + /// Maximum number of columns to display in the minimap. + /// + /// Default: 80 + pub max_width_columns: Option, +} + +/// Forcefully enable or disable the scrollbar for each axis +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default)] +pub struct ScrollbarAxesContent { + /// When false, forcefully disables the horizontal scrollbar. Otherwise, obey other settings. + /// + /// Default: true + pub horizontal: Option, + + /// When false, forcefully disables the vertical scrollbar. Otherwise, obey other settings. + /// + /// Default: true + pub vertical: Option, +} + +/// Gutter related settings +#[derive(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, + /// Minimum number of characters to reserve space for in the gutter. + /// + /// Default: 4 + pub min_line_number_digits: Option, + /// Whether to show runnable buttons in the gutter. + /// + /// Default: true + pub runnables: Option, + /// Whether to show breakpoints in the gutter. + /// + /// Default: true + pub breakpoints: Option, + /// Whether to show fold buttons in the gutter. + /// + /// Default: true + pub folds: Option, +} + +/// How to render LSP `textDocument/documentColor` colors in the editor. +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum DocumentColorsRenderMode { + /// Do not query and render document colors. + None, + /// Render document colors as inlay hints near the color text. + #[default] + Inlay, + /// Draw a border around the color text. + Border, + /// Draw a background behind the color text. + Background, +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum CurrentLineHighlight { + // Don't highlight the current line. + None, + // Highlight the gutter area. + Gutter, + // Highlight the editor area. + Line, + // Highlight the full line. + All, +} + +/// When to populate a new search's query based on the text under the cursor. +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum SeedQuerySetting { + /// Always populate the search query with the word under the cursor. + Always, + /// Only populate the search query when there is text selected. + Selection, + /// Never populate the search query + Never, +} + +/// What to do when multibuffer is double clicked in some of its excerpts (parts of singleton buffers). +#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum DoubleClickInMultibuffer { + /// Behave as a regular buffer and select the whole word. + #[default] + Select, + /// Open the excerpt clicked as a new buffer in the new tab, if no `alt` modifier was pressed during double click. + /// Otherwise, behave as a regular buffer and select the whole word. + Open, +} + +/// When to show the minimap thumb. +/// +/// Default: always +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum MinimapThumb { + /// Show the minimap thumb only when the mouse is hovering over the minimap. + Hover, + /// Always show the minimap thumb. + #[default] + Always, +} + +/// Defines the border style for the minimap's scrollbar thumb. +/// +/// Default: left_open +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum MinimapThumbBorder { + /// Displays a border on all sides of the thumb. + Full, + /// Displays a border on all sides except the left side of the thumb. + #[default] + LeftOpen, + /// Displays a border on all sides except the right side of the thumb. + RightOpen, + /// Displays a border only on the left side of the thumb. + LeftOnly, + /// Displays the thumb without any border. + None, +} + +/// Which diagnostic indicators to show in the scrollbar. +/// +/// Default: all +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] +pub enum ScrollbarDiagnostics { + /// Show all diagnostic levels: hint, information, warnings, error. + All, + /// Show only the following diagnostic levels: information, warning, error. + Information, + /// Show only the following diagnostic levels: warning, error. + Warning, + /// Show only the following diagnostic level: error. + Error, + /// Do not show diagnostics. + None, +} + +/// The key to use for adding multiple cursors +/// +/// Default: alt +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum MultiCursorModifier { + Alt, + #[serde(alias = "cmd", alias = "ctrl")] + CmdOrCtrl, +} + +/// Whether the editor will scroll beyond the last line. +/// +/// Default: one_page +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum ScrollBeyondLastLine { + /// The editor will not scroll beyond the last line. + Off, + + /// The editor will scroll beyond the last line by one page. + OnePage, + + /// The editor will scroll beyond the last line by the same number of lines as vertical_scroll_margin. + VerticalScrollMargin, +} + +/// The shape of a selection cursor. +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum CursorShape { + /// A vertical bar + #[default] + Bar, + /// A block that surrounds the following character + Block, + /// An underline that runs along the following character + Underline, + /// A box drawn around the following character + Hollow, +} + +/// What to do when go to definition yields no results. +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum GoToDefinitionFallback { + /// Disables the fallback. + None, + /// Looks up references of the same symbol instead. + #[default] + FindAllReferences, +} + +/// Determines when the mouse cursor should be hidden in an editor or input box. +/// +/// Default: on_typing_and_movement +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum HideMouseMode { + /// Never hide the mouse cursor + Never, + /// Hide only when typing + OnTyping, + /// Hide on both typing and cursor movement + #[default] + OnTypingAndMovement, +} + +/// Determines how snippets are sorted relative to other completion items. +/// +/// Default: inline +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum SnippetSortOrder { + /// Place snippets at the top of the completion list + Top, + /// Sort snippets normally using the default comparison logic + #[default] + Inline, + /// Place snippets at the bottom of the completion list + Bottom, + /// Do not show snippets in the completion list + None, +} + +/// Default options for buffer and project search items. +#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct SearchSettingsContent { + /// Whether to show the project search button in the status bar. + pub button: Option, + pub whole_word: Option, + pub case_sensitive: Option, + pub include_ignored: Option, + pub regex: Option, +} + +#[derive(Default, 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, +} + +/// Whether to allow drag and drop text selection in buffer. +#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct DragAndDropSelectionContent { + /// When true, enables drag and drop text selection in buffer. + /// + /// Default: true + pub enabled: Option, + + /// The delay in milliseconds that must elapse before drag and drop is allowed. Otherwise, a new text selection is created. + /// + /// Default: 300 + pub delay: Option, +} + +/// When to show the minimap in the editor. +/// +/// Default: never +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum ShowMinimap { + /// Follow the visibility of the scrollbar. + Auto, + /// Always show the minimap. + Always, + /// Never show the minimap. + #[default] + Never, +} + +/// Where to show the minimap in the editor. +/// +/// Default: all_editors +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum DisplayIn { + /// Show on all open editors. + AllEditors, + /// Show the minimap on the active editor only. + #[default] + ActiveEditor, +} diff --git a/crates/settings/src/settings_content/language.rs b/crates/settings/src/settings_content/language.rs index c86c36954a022c20aac184c993be2b59117c9958..e9588ac8de21181d2414fcf15f179b4b9f2966f7 100644 --- a/crates/settings/src/settings_content/language.rs +++ b/crates/settings/src/settings_content/language.rs @@ -379,7 +379,7 @@ pub struct JsxTagAutoCloseSettings { } /// The settings for inlay hints. -/// todo(settings_refactor) the fields of this struct should likely be optional, +/// todo!() the fields of this struct should likely be optional, /// and a similar struct exposed from the language crate. #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] pub struct InlayHintSettings { diff --git a/crates/settings/src/settings_content/terminal.rs b/crates/settings/src/settings_content/terminal.rs index 351ee41402ee9aea77282d23ea6fd0a35f8b9e8e..8a98265c564f17be39888a4b8ec8adbdd5e7eddb 100644 --- a/crates/settings/src/settings_content/terminal.rs +++ b/crates/settings/src/settings_content/terminal.rs @@ -192,7 +192,7 @@ impl TerminalLineHeight { } } -/// When to show the scrollbar in the terminal. +/// When to show the scrollbar. /// /// Default: auto #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] diff --git a/crates/ui/src/components/scrollbar.rs b/crates/ui/src/components/scrollbar.rs index f3cc37d2f55356f05af3f1644dc4292a6add2660..b00cbc5441c92626ff117fc96fbf5bca3891ed3b 100644 --- a/crates/ui/src/components/scrollbar.rs +++ b/crates/ui/src/components/scrollbar.rs @@ -44,6 +44,17 @@ pub mod scrollbars { Never, } + impl From for ShowScrollbar { + fn from(value: settings::ShowScrollbar) -> Self { + match value { + settings::ShowScrollbar::Auto => ShowScrollbar::Auto, + settings::ShowScrollbar::System => ShowScrollbar::System, + settings::ShowScrollbar::Always => ShowScrollbar::Always, + settings::ShowScrollbar::Never => ShowScrollbar::Never, + } + } + } + impl ShowScrollbar { pub(super) fn show(&self) -> bool { *self != Self::Never