From e51dc25e1d2fd1a0ff58e2098efd135851daff60 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 12 Jul 2022 11:00:02 -0700 Subject: [PATCH 1/6] Start moving default settings into a JSON file Co-authored-by: Antonio Scandurra --- assets/default-settings.json | 39 ++++++++++++++++ crates/search/src/buffer_search.rs | 7 ++- crates/search/src/project_search.rs | 7 ++- crates/settings/src/settings.rs | 48 +++++++++++++------- crates/zed/src/main.rs | 70 +---------------------------- 5 files changed, 82 insertions(+), 89 deletions(-) create mode 100644 assets/default-settings.json diff --git a/assets/default-settings.json b/assets/default-settings.json new file mode 100644 index 0000000000..e88df5c2c5 --- /dev/null +++ b/assets/default-settings.json @@ -0,0 +1,39 @@ +{ + "theme": "cave-light", + "buffer_font_family": "Zed Mono", + "buffer_font_size": 15, + "hover_popover_enabled": true, + "vim_mode": false, + "autosave": "off", + "projects_online_by_default": true, + "languages_overrides": { + "Plain Text": { + "soft_wrap": "preferred_line_length" + }, + "C": { + "tab_size": 2 + }, + "C++": { + "tab_size": 2 + }, + "Go": { + "tab_size": 4, + "hard_tabs": true + }, + "Markdown": { + "soft_wrap": "preferred_line_length" + }, + "Rust": { + "tab_size": 4 + }, + "JavaScript": { + "tab_size": 2 + }, + "TypeScript": { + "tab_size": 2 + }, + "TSX": { + "tab_size": 2 + } + } +} diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 59f78cdc33..5303c20e89 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -608,8 +608,11 @@ mod tests { let fonts = cx.font_cache(); let mut theme = gpui::fonts::with_font_cache(fonts.clone(), || theme::Theme::default()); theme.search.match_background = Color::red(); - let settings = Settings::new("Courier", &fonts, Arc::new(theme)).unwrap(); - cx.update(|cx| cx.set_global(settings)); + cx.update(|cx| { + let mut settings = Settings::test(cx); + settings.theme = Arc::new(theme); + cx.set_global(settings) + }); let buffer = cx.add_model(|cx| { Buffer::new( diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 5ee2dcbb27..e1acc6a771 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -911,8 +911,11 @@ mod tests { let fonts = cx.font_cache(); let mut theme = gpui::fonts::with_font_cache(fonts.clone(), || theme::Theme::default()); theme.search.match_background = Color::red(); - let settings = Settings::new("Courier", &fonts, Arc::new(theme)).unwrap(); - cx.update(|cx| cx.set_global(settings)); + cx.update(|cx| { + let mut settings = Settings::test(cx); + settings.theme = Arc::new(theme); + cx.set_global(settings) + }); let fs = FakeFs::new(cx.background()); fs.insert_tree( diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 98df5e2f1f..2e418e502d 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -1,7 +1,10 @@ mod keymap_file; use anyhow::Result; -use gpui::font_cache::{FamilyId, FontCache}; +use gpui::{ + font_cache::{FamilyId, FontCache}, + AssetSource, +}; use schemars::{ gen::{SchemaGenerator, SchemaSettings}, schema::{ @@ -97,24 +100,35 @@ pub struct SettingsFileContent { } impl Settings { - pub fn new( - buffer_font_family: &str, + pub fn defaults( + assets: impl AssetSource, font_cache: &FontCache, - theme: Arc, - ) -> Result { - Ok(Self { - buffer_font_family: font_cache.load_family(&[buffer_font_family])?, - buffer_font_size: 15., - default_buffer_font_size: 15., - hover_popover_enabled: true, - vim_mode: false, - autosave: Autosave::Off, - language_settings: Default::default(), - language_defaults: Default::default(), + themes: &ThemeRegistry, + ) -> Self { + let defaults = assets.load("default-settings.json").unwrap(); + let defaults: SettingsFileContent = serde_json::from_slice(defaults.as_ref()).unwrap(); + Self { + buffer_font_family: font_cache + .load_family(&[defaults.buffer_font_family.as_ref().unwrap()]) + .unwrap(), + buffer_font_size: defaults.buffer_font_size.unwrap(), + default_buffer_font_size: defaults.buffer_font_size.unwrap(), + hover_popover_enabled: defaults.hover_popover_enabled.unwrap(), + projects_online_by_default: defaults.projects_online_by_default.unwrap(), + vim_mode: defaults.vim_mode.unwrap(), + autosave: defaults.autosave.unwrap(), + language_settings: LanguageSettings { + tab_size: defaults.editor.tab_size, + hard_tabs: defaults.editor.hard_tabs, + soft_wrap: defaults.editor.soft_wrap, + preferred_line_length: defaults.editor.preferred_line_length, + format_on_save: defaults.editor.format_on_save, + enable_language_server: defaults.editor.enable_language_server, + }, + language_defaults: defaults.language_overrides, language_overrides: Default::default(), - projects_online_by_default: true, - theme, - }) + theme: themes.get(&defaults.theme.unwrap()).unwrap(), + } } pub fn with_language_defaults( diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 723738cf28..02f8008028 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -38,7 +38,7 @@ use std::{ time::Duration, }; use terminal; -use theme::{ThemeRegistry, DEFAULT_THEME_NAME}; +use theme::ThemeRegistry; use util::{ResultExt, TryFutureExt}; use workspace::{self, AppState, NewFile, OpenPaths}; use zed::{ @@ -72,73 +72,7 @@ fn main() { let fs = Arc::new(RealFs); let themes = ThemeRegistry::new(Assets, app.font_cache()); - let theme = themes.get(DEFAULT_THEME_NAME).unwrap(); - let default_settings = Settings::new("Zed Mono", &app.font_cache(), theme) - .unwrap() - .with_language_defaults( - languages::PLAIN_TEXT.name(), - settings::LanguageSettings { - soft_wrap: Some(settings::SoftWrap::PreferredLineLength), - ..Default::default() - }, - ) - .with_language_defaults( - "C", - settings::LanguageSettings { - tab_size: Some(2.try_into().unwrap()), - ..Default::default() - }, - ) - .with_language_defaults( - "C++", - settings::LanguageSettings { - tab_size: Some(2.try_into().unwrap()), - ..Default::default() - }, - ) - .with_language_defaults( - "Go", - settings::LanguageSettings { - tab_size: Some(4.try_into().unwrap()), - hard_tabs: Some(true), - ..Default::default() - }, - ) - .with_language_defaults( - "Markdown", - settings::LanguageSettings { - soft_wrap: Some(settings::SoftWrap::PreferredLineLength), - ..Default::default() - }, - ) - .with_language_defaults( - "Rust", - settings::LanguageSettings { - tab_size: Some(4.try_into().unwrap()), - ..Default::default() - }, - ) - .with_language_defaults( - "JavaScript", - settings::LanguageSettings { - tab_size: Some(2.try_into().unwrap()), - ..Default::default() - }, - ) - .with_language_defaults( - "TypeScript", - settings::LanguageSettings { - tab_size: Some(2.try_into().unwrap()), - ..Default::default() - }, - ) - .with_language_defaults( - "TSX", - settings::LanguageSettings { - tab_size: Some(2.try_into().unwrap()), - ..Default::default() - }, - ); + let default_settings = Settings::defaults(Assets, &app.font_cache(), &themes); let config_files = load_config_files(&app, fs.clone()); From ec8a49370075d947b2ca49fbc73e1874267bfea6 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 12 Jul 2022 11:35:19 -0700 Subject: [PATCH 2/6] Move all default settings from source code into the JSON file --- assets/default-settings.json | 8 ++- crates/collab/src/integration_tests.rs | 2 +- crates/editor/src/display_map.rs | 2 +- crates/editor/src/editor.rs | 2 +- crates/settings/src/settings.rs | 80 +++++++++++++------------- crates/theme/src/theme.rs | 2 - crates/zed/src/zed.rs | 32 ++++++----- 7 files changed, 70 insertions(+), 58 deletions(-) diff --git a/assets/default-settings.json b/assets/default-settings.json index e88df5c2c5..d9c9580242 100644 --- a/assets/default-settings.json +++ b/assets/default-settings.json @@ -1,11 +1,17 @@ { - "theme": "cave-light", + "theme": "cave-dark", "buffer_font_family": "Zed Mono", "buffer_font_size": 15, "hover_popover_enabled": true, "vim_mode": false, "autosave": "off", "projects_online_by_default": true, + "enable_language_server": true, + "format_on_save": "language_server", + "preferred_line_length": 80, + "soft_wrap": "none", + "hard_tabs": false, + "tab_size": 4, "languages_overrides": { "Plain Text": { "soft_wrap": "preferred_line_length" diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index db67df42c7..358f016366 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -2010,7 +2010,7 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon // host's configuration is honored as opposed to using the guest's settings. cx_a.update(|cx| { cx.update_global(|settings: &mut Settings, _| { - settings.language_settings.format_on_save = Some(FormatOnSave::External { + settings.editor_defaults.format_on_save = Some(FormatOnSave::External { command: "awk".to_string(), arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()], }); diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 917553c791..e4c888af4d 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -983,7 +983,7 @@ pub mod tests { language.set_theme(&theme); cx.update(|cx| { let mut settings = Settings::test(cx); - settings.language_settings.tab_size = Some(2.try_into().unwrap()); + settings.editor_defaults.tab_size = Some(2.try_into().unwrap()); cx.set_global(settings); }); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ae00f1b3fc..a4f166b956 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7613,7 +7613,7 @@ mod tests { let mut cx = EditorTestContext::new(cx).await; cx.update(|cx| { cx.update_global::(|settings, _| { - settings.language_settings.hard_tabs = Some(true); + settings.editor_overrides.hard_tabs = Some(true); }); }); diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 2e418e502d..028abbca45 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -29,7 +29,8 @@ pub struct Settings { pub hover_popover_enabled: bool, pub vim_mode: bool, pub autosave: Autosave, - pub language_settings: LanguageSettings, + pub editor_defaults: LanguageSettings, + pub editor_overrides: LanguageSettings, pub language_defaults: HashMap, LanguageSettings>, pub language_overrides: HashMap, LanguageSettings>, pub theme: Arc, @@ -86,11 +87,7 @@ pub struct SettingsFileContent { #[serde(default)] pub vim_mode: Option, #[serde(default)] - pub format_on_save: Option, - #[serde(default)] pub autosave: Option, - #[serde(default)] - pub enable_language_server: Option, #[serde(flatten)] pub editor: LanguageSettings, #[serde(default)] @@ -105,8 +102,14 @@ impl Settings { font_cache: &FontCache, themes: &ThemeRegistry, ) -> Self { - let defaults = assets.load("default-settings.json").unwrap(); - let defaults: SettingsFileContent = serde_json::from_slice(defaults.as_ref()).unwrap(); + fn required(value: Option) -> Option { + assert!(value.is_some(), "missing default setting value"); + value + } + + let defaults: SettingsFileContent = + serde_json::from_slice(assets.load("default-settings.json").unwrap().as_ref()).unwrap(); + Self { buffer_font_family: font_cache .load_family(&[defaults.buffer_font_family.as_ref().unwrap()]) @@ -117,15 +120,16 @@ impl Settings { projects_online_by_default: defaults.projects_online_by_default.unwrap(), vim_mode: defaults.vim_mode.unwrap(), autosave: defaults.autosave.unwrap(), - language_settings: LanguageSettings { - tab_size: defaults.editor.tab_size, - hard_tabs: defaults.editor.hard_tabs, - soft_wrap: defaults.editor.soft_wrap, - preferred_line_length: defaults.editor.preferred_line_length, - format_on_save: defaults.editor.format_on_save, - enable_language_server: defaults.editor.enable_language_server, + editor_defaults: LanguageSettings { + tab_size: required(defaults.editor.tab_size), + hard_tabs: required(defaults.editor.hard_tabs), + soft_wrap: required(defaults.editor.soft_wrap), + preferred_line_length: required(defaults.editor.preferred_line_length), + format_on_save: required(defaults.editor.format_on_save), + enable_language_server: required(defaults.editor.enable_language_server), }, language_defaults: defaults.language_overrides, + editor_overrides: Default::default(), language_overrides: Default::default(), theme: themes.get(&defaults.theme.unwrap()).unwrap(), } @@ -143,48 +147,37 @@ impl Settings { pub fn tab_size(&self, language: Option<&str>) -> NonZeroU32 { self.language_setting(language, |settings| settings.tab_size) - .unwrap_or(4.try_into().unwrap()) } pub fn hard_tabs(&self, language: Option<&str>) -> bool { self.language_setting(language, |settings| settings.hard_tabs) - .unwrap_or(false) } pub fn soft_wrap(&self, language: Option<&str>) -> SoftWrap { self.language_setting(language, |settings| settings.soft_wrap) - .unwrap_or(SoftWrap::None) } pub fn preferred_line_length(&self, language: Option<&str>) -> u32 { self.language_setting(language, |settings| settings.preferred_line_length) - .unwrap_or(80) } pub fn format_on_save(&self, language: Option<&str>) -> FormatOnSave { self.language_setting(language, |settings| settings.format_on_save.clone()) - .unwrap_or(FormatOnSave::LanguageServer) } pub fn enable_language_server(&self, language: Option<&str>) -> bool { self.language_setting(language, |settings| settings.enable_language_server) - .unwrap_or(true) } - fn language_setting(&self, language: Option<&str>, f: F) -> Option + fn language_setting(&self, language: Option<&str>, f: F) -> R where F: Fn(&LanguageSettings) -> Option, { - let mut language_override = None; - let mut language_default = None; - if let Some(language) = language { - language_override = self.language_overrides.get(language).and_then(&f); - language_default = self.language_defaults.get(language).and_then(&f); - } - - language_override - .or_else(|| f(&self.language_settings)) - .or(language_default) + None.or_else(|| language.and_then(|l| self.language_overrides.get(l).and_then(&f))) + .or_else(|| f(&self.editor_overrides)) + .or_else(|| language.and_then(|l| self.language_defaults.get(l).and_then(&f))) + .or_else(|| f(&self.editor_defaults)) + .expect("missing default") } #[cfg(any(test, feature = "test-support"))] @@ -196,7 +189,15 @@ impl Settings { hover_popover_enabled: true, vim_mode: false, autosave: Autosave::Off, - language_settings: Default::default(), + editor_defaults: LanguageSettings { + tab_size: Some(4.try_into().unwrap()), + hard_tabs: Some(false), + soft_wrap: Some(SoftWrap::None), + preferred_line_length: Some(80), + format_on_save: Some(FormatOnSave::LanguageServer), + enable_language_server: Some(true), + }, + editor_overrides: Default::default(), language_defaults: Default::default(), language_overrides: Default::default(), projects_online_by_default: true, @@ -238,18 +239,19 @@ impl Settings { merge(&mut self.hover_popover_enabled, data.hover_popover_enabled); merge(&mut self.vim_mode, data.vim_mode); merge(&mut self.autosave, data.autosave); + merge_option( - &mut self.language_settings.format_on_save, - data.format_on_save.clone(), + &mut self.editor_overrides.format_on_save, + data.editor.format_on_save.clone(), ); merge_option( - &mut self.language_settings.enable_language_server, - data.enable_language_server, + &mut self.editor_overrides.enable_language_server, + data.editor.enable_language_server, ); - merge_option(&mut self.language_settings.soft_wrap, data.editor.soft_wrap); - merge_option(&mut self.language_settings.tab_size, data.editor.tab_size); + merge_option(&mut self.editor_overrides.soft_wrap, data.editor.soft_wrap); + merge_option(&mut self.editor_overrides.tab_size, data.editor.tab_size); merge_option( - &mut self.language_settings.preferred_line_length, + &mut self.editor_overrides.preferred_line_length, data.editor.preferred_line_length, ); diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 51f5fb7fcc..32ddb9c825 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -12,8 +12,6 @@ use std::{collections::HashMap, sync::Arc}; pub use theme_registry::*; -pub const DEFAULT_THEME_NAME: &'static str = "cave-dark"; - #[derive(Deserialize, Default)] pub struct Theme { #[serde(default)] diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 56ccfaf9fe..d78cb9c9a2 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -400,7 +400,7 @@ mod tests { collections::HashSet, path::{Path, PathBuf}, }; - use theme::{Theme, ThemeRegistry, DEFAULT_THEME_NAME}; + use theme::ThemeRegistry; use workspace::{ open_paths, pane, Item, ItemHandle, NewFile, Pane, SplitDirection, WorkspaceHandle, }; @@ -1530,23 +1530,29 @@ mod tests { } #[gpui::test] - fn test_bundled_themes(cx: &mut MutableAppContext) { + fn test_bundled_settings_and_themes(cx: &mut MutableAppContext) { + cx.platform() + .fonts() + .add_fonts(&[ + Assets + .load("fonts/zed-sans/zed-sans-extended.ttf") + .unwrap() + .to_vec() + .into(), + Assets + .load("fonts/zed-mono/zed-mono-extended.ttf") + .unwrap() + .to_vec() + .into(), + ]) + .unwrap(); let themes = ThemeRegistry::new(Assets, cx.font_cache().clone()); - - lazy_static::lazy_static! { - static ref DEFAULT_THEME: parking_lot::Mutex>> = Default::default(); - static ref FONTS: Vec>> = vec![ - Assets.load("fonts/zed-sans/zed-sans-extended.ttf").unwrap().to_vec().into(), - Assets.load("fonts/zed-mono/zed-mono-extended.ttf").unwrap().to_vec().into(), - ]; - } - - cx.platform().fonts().add_fonts(&FONTS).unwrap(); + let settings = Settings::defaults(Assets, cx.font_cache(), &themes); let mut has_default_theme = false; for theme_name in themes.list() { let theme = themes.get(&theme_name).unwrap(); - if theme.name == DEFAULT_THEME_NAME { + if theme.name == settings.theme.name { has_default_theme = true; } assert_eq!(theme.name, theme_name); From c02f4ea8dc56f76c832ccdd8e89a49e397a83646 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 12 Jul 2022 11:42:43 -0700 Subject: [PATCH 3/6] Rename LanguageSettings -> EditorSettings --- assets/default-settings.json | 6 ++--- crates/editor/src/editor.rs | 10 ++++----- crates/project/src/project_tests.rs | 6 ++--- crates/settings/src/settings.rs | 35 ++++++++++++++++------------- crates/zed/src/settings_file.rs | 4 ++-- 5 files changed, 32 insertions(+), 29 deletions(-) diff --git a/assets/default-settings.json b/assets/default-settings.json index d9c9580242..45032b4288 100644 --- a/assets/default-settings.json +++ b/assets/default-settings.json @@ -2,17 +2,17 @@ "theme": "cave-dark", "buffer_font_family": "Zed Mono", "buffer_font_size": 15, - "hover_popover_enabled": true, "vim_mode": false, - "autosave": "off", + "hover_popover_enabled": true, "projects_online_by_default": true, "enable_language_server": true, + "autosave": "off", "format_on_save": "language_server", "preferred_line_length": 80, "soft_wrap": "none", "hard_tabs": false, "tab_size": 4, - "languages_overrides": { + "languages": { "Plain Text": { "soft_wrap": "preferred_line_length" }, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index a4f166b956..4d77dc113d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -6236,7 +6236,7 @@ mod tests { use language::{FakeLspAdapter, LanguageConfig}; use lsp::FakeLanguageServer; use project::FakeFs; - use settings::LanguageSettings; + use settings::EditorSettings; use std::{cell::RefCell, rc::Rc, time::Instant}; use text::Point; use unindent::Unindent; @@ -7696,14 +7696,14 @@ mod tests { Settings::test(cx) .with_language_defaults( "TOML", - LanguageSettings { + EditorSettings { tab_size: Some(2.try_into().unwrap()), ..Default::default() }, ) .with_language_defaults( "Rust", - LanguageSettings { + EditorSettings { tab_size: Some(4.try_into().unwrap()), ..Default::default() }, @@ -9380,7 +9380,7 @@ mod tests { cx.update_global::(|settings, _| { settings.language_overrides.insert( "Rust".into(), - LanguageSettings { + EditorSettings { tab_size: Some(8.try_into().unwrap()), ..Default::default() }, @@ -9496,7 +9496,7 @@ mod tests { cx.update_global::(|settings, _| { settings.language_overrides.insert( "Rust".into(), - LanguageSettings { + EditorSettings { tab_size: Some(8.try_into().unwrap()), ..Default::default() }, diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 6f863ee8c9..1080daff1d 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -883,7 +883,7 @@ async fn test_toggling_enable_language_server( cx.update_global(|settings: &mut Settings, _| { settings.language_overrides.insert( Arc::from("Rust"), - settings::LanguageSettings { + settings::EditorSettings { enable_language_server: Some(false), ..Default::default() }, @@ -900,14 +900,14 @@ async fn test_toggling_enable_language_server( cx.update_global(|settings: &mut Settings, _| { settings.language_overrides.insert( Arc::from("Rust"), - settings::LanguageSettings { + settings::EditorSettings { enable_language_server: Some(true), ..Default::default() }, ); settings.language_overrides.insert( Arc::from("JavaScript"), - settings::LanguageSettings { + settings::EditorSettings { enable_language_server: Some(false), ..Default::default() }, diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 028abbca45..f787b9a516 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -14,7 +14,7 @@ use schemars::{ }; use serde::{de::DeserializeOwned, Deserialize}; use serde_json::Value; -use std::{collections::HashMap, num::NonZeroU32, sync::Arc}; +use std::{collections::HashMap, num::NonZeroU32, str, sync::Arc}; use theme::{Theme, ThemeRegistry}; use util::ResultExt as _; @@ -29,15 +29,15 @@ pub struct Settings { pub hover_popover_enabled: bool, pub vim_mode: bool, pub autosave: Autosave, - pub editor_defaults: LanguageSettings, - pub editor_overrides: LanguageSettings, - pub language_defaults: HashMap, LanguageSettings>, - pub language_overrides: HashMap, LanguageSettings>, + pub editor_defaults: EditorSettings, + pub editor_overrides: EditorSettings, + pub language_defaults: HashMap, EditorSettings>, + pub language_overrides: HashMap, EditorSettings>, pub theme: Arc, } #[derive(Clone, Debug, Default, Deserialize, JsonSchema)] -pub struct LanguageSettings { +pub struct EditorSettings { pub tab_size: Option, pub hard_tabs: Option, pub soft_wrap: Option, @@ -89,9 +89,10 @@ pub struct SettingsFileContent { #[serde(default)] pub autosave: Option, #[serde(flatten)] - pub editor: LanguageSettings, + pub editor: EditorSettings, #[serde(default)] - pub language_overrides: HashMap, LanguageSettings>, + #[serde(alias = "language_overrides")] + pub languages: HashMap, EditorSettings>, #[serde(default)] pub theme: Option, } @@ -107,8 +108,10 @@ impl Settings { value } - let defaults: SettingsFileContent = - serde_json::from_slice(assets.load("default-settings.json").unwrap().as_ref()).unwrap(); + let defaults: SettingsFileContent = parse_json_with_comments( + str::from_utf8(assets.load("default-settings.json").unwrap().as_ref()).unwrap(), + ) + .unwrap(); Self { buffer_font_family: font_cache @@ -120,7 +123,7 @@ impl Settings { projects_online_by_default: defaults.projects_online_by_default.unwrap(), vim_mode: defaults.vim_mode.unwrap(), autosave: defaults.autosave.unwrap(), - editor_defaults: LanguageSettings { + editor_defaults: EditorSettings { tab_size: required(defaults.editor.tab_size), hard_tabs: required(defaults.editor.hard_tabs), soft_wrap: required(defaults.editor.soft_wrap), @@ -128,7 +131,7 @@ impl Settings { format_on_save: required(defaults.editor.format_on_save), enable_language_server: required(defaults.editor.enable_language_server), }, - language_defaults: defaults.language_overrides, + language_defaults: defaults.languages, editor_overrides: Default::default(), language_overrides: Default::default(), theme: themes.get(&defaults.theme.unwrap()).unwrap(), @@ -138,7 +141,7 @@ impl Settings { pub fn with_language_defaults( mut self, language_name: impl Into>, - overrides: LanguageSettings, + overrides: EditorSettings, ) -> Self { self.language_defaults .insert(language_name.into(), overrides); @@ -171,7 +174,7 @@ impl Settings { fn language_setting(&self, language: Option<&str>, f: F) -> R where - F: Fn(&LanguageSettings) -> Option, + F: Fn(&EditorSettings) -> Option, { None.or_else(|| language.and_then(|l| self.language_overrides.get(l).and_then(&f))) .or_else(|| f(&self.editor_overrides)) @@ -189,7 +192,7 @@ impl Settings { hover_popover_enabled: true, vim_mode: false, autosave: Autosave::Off, - editor_defaults: LanguageSettings { + editor_defaults: EditorSettings { tab_size: Some(4.try_into().unwrap()), hard_tabs: Some(false), soft_wrap: Some(SoftWrap::None), @@ -255,7 +258,7 @@ impl Settings { data.editor.preferred_line_length, ); - for (language_name, settings) in data.language_overrides.clone().into_iter() { + for (language_name, settings) in data.languages.clone().into_iter() { let target = self .language_overrides .entry(language_name.into()) diff --git a/crates/zed/src/settings_file.rs b/crates/zed/src/settings_file.rs index 65cac2a496..da728f9dd6 100644 --- a/crates/zed/src/settings_file.rs +++ b/crates/zed/src/settings_file.rs @@ -93,7 +93,7 @@ pub async fn watch_keymap_file( mod tests { use super::*; use project::FakeFs; - use settings::{LanguageSettings, SoftWrap}; + use settings::{EditorSettings, SoftWrap}; #[gpui::test] async fn test_settings_from_files(cx: &mut gpui::TestAppContext) { @@ -128,7 +128,7 @@ mod tests { let settings = cx.read(Settings::test).with_language_defaults( "JavaScript", - LanguageSettings { + EditorSettings { tab_size: Some(2.try_into().unwrap()), ..Default::default() }, From 8464c03e65ceb82d0d6da923e17dc58dece4b8f1 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 12 Jul 2022 12:19:01 -0700 Subject: [PATCH 4/6] Adjust settings schema generation to reflect rename Also, simplify that code. --- crates/settings/src/settings.rs | 110 +++++++++++++------------------- 1 file changed, 46 insertions(+), 64 deletions(-) diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index f787b9a516..219351896a 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -7,9 +7,7 @@ use gpui::{ }; use schemars::{ gen::{SchemaGenerator, SchemaSettings}, - schema::{ - InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec, SubschemaValidation, - }, + schema::{InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec}, JsonSchema, }; use serde::{de::DeserializeOwned, Deserialize}; @@ -289,77 +287,61 @@ pub fn settings_file_json_schema( let generator = SchemaGenerator::new(settings); let mut root_schema = generator.into_root_schema_for::(); - // Construct theme names reference type - let theme_names = theme_names - .into_iter() - .map(|name| Value::String(name)) - .collect(); - let theme_names_schema = Schema::Object(SchemaObject { + // Create a schema for a theme name. + let theme_name_schema = SchemaObject { instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), - enum_values: Some(theme_names), + enum_values: Some( + theme_names + .into_iter() + .map(|name| Value::String(name)) + .collect(), + ), ..Default::default() - }); - root_schema - .definitions - .insert("ThemeName".to_owned(), theme_names_schema); + }; - // Construct language settings reference type - let language_settings_schema_reference = Schema::Object(SchemaObject { - reference: Some("#/definitions/LanguageSettings".to_owned()), - ..Default::default() - }); - let language_settings_properties = language_names - .into_iter() - .map(|name| { - ( - name, - Schema::Object(SchemaObject { - subschemas: Some(Box::new(SubschemaValidation { - all_of: Some(vec![language_settings_schema_reference.clone()]), - ..Default::default() - })), - ..Default::default() - }), - ) - }) - .collect(); - let language_overrides_schema = Schema::Object(SchemaObject { + // Create a schema for a 'languages overrides' object, associating editor + // settings with specific langauges. + assert!(root_schema.definitions.contains_key("EditorSettings")); + let languages_object_schema = SchemaObject { instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), object: Some(Box::new(ObjectValidation { - properties: language_settings_properties, + properties: language_names + .into_iter() + .map(|name| (name, Schema::new_ref("#/definitions/EditorSettings".into()))) + .collect(), ..Default::default() })), ..Default::default() - }); + }; + + // Add these new schemas as definitions, and modify properties of the root + // schema to reference them. + root_schema.definitions.extend([ + ("ThemeName".into(), theme_name_schema.into()), + ("Languages".into(), languages_object_schema.into()), + ]); root_schema - .definitions - .insert("LanguageOverrides".to_owned(), language_overrides_schema); + .schema + .object + .as_mut() + .unwrap() + .properties + .extend([ + ( + "theme".to_owned(), + Schema::new_ref("#/definitions/ThemeName".into()), + ), + ( + "languages".to_owned(), + Schema::new_ref("#/definitions/Languages".into()), + ), + // For backward compatibility + ( + "language_overrides".to_owned(), + Schema::new_ref("#/definitions/Languages".into()), + ), + ]); - // Modify theme property to use new theme reference type - let settings_file_schema = root_schema.schema.object.as_mut().unwrap(); - let language_overrides_schema_reference = Schema::Object(SchemaObject { - reference: Some("#/definitions/ThemeName".to_owned()), - ..Default::default() - }); - settings_file_schema.properties.insert( - "theme".to_owned(), - Schema::Object(SchemaObject { - subschemas: Some(Box::new(SubschemaValidation { - all_of: Some(vec![language_overrides_schema_reference]), - ..Default::default() - })), - ..Default::default() - }), - ); - - // Modify language_overrides property to use LanguageOverrides reference - settings_file_schema.properties.insert( - "language_overrides".to_owned(), - Schema::Object(SchemaObject { - reference: Some("#/definitions/LanguageOverrides".to_owned()), - ..Default::default() - }), - ); serde_json::to_value(root_schema).unwrap() } From 7750054a45763203c6d43e92d0be4c1c5c0c1e82 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 12 Jul 2022 13:38:24 -0700 Subject: [PATCH 5/6] Add application menu item to open the default settings --- assets/default-settings.json | 66 ++++++++++++++++++++++++++++++++++- crates/zed/src/menus.rs | 4 +++ crates/zed/src/zed.rs | 67 +++++++++++++++++++++++++----------- 3 files changed, 115 insertions(+), 22 deletions(-) diff --git a/assets/default-settings.json b/assets/default-settings.json index 45032b4288..2417d0f5d4 100644 --- a/assets/default-settings.json +++ b/assets/default-settings.json @@ -1,17 +1,81 @@ { + // The name of the Zed theme to use for the UI "theme": "cave-dark", + + // The name of a font to use for rendering text in the editor "buffer_font_family": "Zed Mono", + + // The default font size for text in the editor "buffer_font_size": 15, + + // Whether to enable vim modes and key bindings "vim_mode": false, + + // Whether to show the informational hover box when moving the mouse + // over symbols in the editor. "hover_popover_enabled": true, + + // Whether new projects should start out 'online'. Online projects + // appear in the contacts panel under your name, so that your contacts + // can see which projects you are working on. Regardless of this + // setting, projects keep their last online status when you reopen them. "projects_online_by_default": true, + + // Whether to use language servers to provide code intelligence. "enable_language_server": true, + + // When to automatically save edited buffers. This setting can + // take four values. + // + // 1. Never automatically save: + // "autosave": "off", + // 2. Save when changing focus away from the Zed window: + // "autosave": "on_window_change", + // 3. Save when changing focus away from a specific buffer: + // "autosave": "on_focus_change", + // 4. Save when idle for a certain amount of time: + // "autosave": { "after_delay": {"milliseconds": 500} }, "autosave": "off", + + // How to auto-format modified buffers when saving them. This + // setting can take three values: + // + // 1. Don't format code + // "format_on_save": "off" + // 2. Format code using the current language server: + // "format_on_save": "language_server" + // 3. Format code using an external command: + // "format_on_save": { + // "external": { + // "command": "sed", + // "arguments": ["-e", "s/ *$//"] + // } + // }, "format_on_save": "language_server", - "preferred_line_length": 80, + + // How to soft-wrap long lines of text. This setting can take + // three values: + // + // 1. Do not soft wrap. + // "soft_wrap": "none", + // 2. Soft wrap lines that overflow the editor: + // "soft_wrap": "editor_width", + // 2. Soft wrap lines at the preferred line length + // "soft_wrap": "preferred_line_length", "soft_wrap": "none", + + // The column at which to soft-wrap lines, for buffers where soft-wrap + // is enabled. + "preferred_line_length": 80, + + // Whether to indent lines using tab characters, as opposed to multiple + // spaces. "hard_tabs": false, + + // How many columns a tab should occupy. "tab_size": 4, + + // Different settings for specific languages. "languages": { "Plain Text": { "soft_wrap": "preferred_line_length" diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index a53a44fa8d..d4d9b401e6 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -26,6 +26,10 @@ pub fn menus() -> Vec> { name: "Open Key Bindings", action: Box::new(super::OpenKeymap), }, + MenuItem::Action { + name: "Open Default Settings", + action: Box::new(super::OpenDefaultSettings), + }, MenuItem::Action { name: "Open Default Key Bindings", action: Box::new(super::OpenDefaultKeymap), diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index d78cb9c9a2..1bfa75b920 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -52,6 +52,7 @@ actions!( DebugElements, OpenSettings, OpenKeymap, + OpenDefaultSettings, OpenDefaultKeymap, IncreaseBufferFontSize, DecreaseBufferFontSize, @@ -111,27 +112,27 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { cx.add_action({ let app_state = app_state.clone(); move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext| { - workspace.with_local_workspace(cx, app_state.clone(), |workspace, cx| { - let project = workspace.project().clone(); - let buffer = project.update(cx, |project, cx| { - let text = Assets::get("keymaps/default.json").unwrap().data; - let text = str::from_utf8(text.as_ref()).unwrap(); - project - .create_buffer(text, project.languages().get_language("JSON"), cx) - .expect("creating buffers on a local workspace always succeeds") - }); - let buffer = cx.add_model(|cx| { - MultiBuffer::singleton(buffer, cx).with_title("Default Key Bindings".into()) - }); - workspace.add_item( - Box::new( - cx.add_view(|cx| { - Editor::for_multibuffer(buffer, Some(project.clone()), cx) - }), - ), - cx, - ); - }); + open_bundled_config_file( + workspace, + app_state.clone(), + "keymaps/default.json", + "Default Key Bindings", + cx, + ); + } + }); + cx.add_action({ + let app_state = app_state.clone(); + move |workspace: &mut Workspace, + _: &OpenDefaultSettings, + cx: &mut ViewContext| { + open_bundled_config_file( + workspace, + app_state.clone(), + "default-settings.json", + "Default Settings", + cx, + ); } }); cx.add_action( @@ -386,6 +387,30 @@ fn open_config_file( .detach_and_log_err(cx) } +fn open_bundled_config_file( + workspace: &mut Workspace, + app_state: Arc, + asset_path: &'static str, + title: &str, + cx: &mut ViewContext, +) { + workspace.with_local_workspace(cx, app_state.clone(), |workspace, cx| { + let project = workspace.project().clone(); + let buffer = project.update(cx, |project, cx| { + let text = Assets::get(asset_path).unwrap().data; + let text = str::from_utf8(text.as_ref()).unwrap(); + project + .create_buffer(text, project.languages().get_language("JSON"), cx) + .expect("creating buffers on a local workspace always succeeds") + }); + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx).with_title(title.into())); + workspace.add_item( + Box::new(cx.add_view(|cx| Editor::for_multibuffer(buffer, Some(project.clone()), cx))), + cx, + ); + }); +} + #[cfg(test)] mod tests { use super::*; From 0ebf417c2ef6b1aeb6ca0b369c837cd44366502d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 12 Jul 2022 14:19:32 -0700 Subject: [PATCH 6/6] Pre-populate settings.json when initializing it from Zed --- .../default.json} | 0 assets/settings/header-comments.json | 8 +++++++ crates/settings/src/settings.rs | 2 +- crates/zed/src/zed.rs | 21 +++++++++++++++---- 4 files changed, 26 insertions(+), 5 deletions(-) rename assets/{default-settings.json => settings/default.json} (100%) create mode 100644 assets/settings/header-comments.json diff --git a/assets/default-settings.json b/assets/settings/default.json similarity index 100% rename from assets/default-settings.json rename to assets/settings/default.json diff --git a/assets/settings/header-comments.json b/assets/settings/header-comments.json new file mode 100644 index 0000000000..6180d310df --- /dev/null +++ b/assets/settings/header-comments.json @@ -0,0 +1,8 @@ +// Zed settings +// +// For information on how to configure Zed, see the Zed +// documentation: https://zed.dev/docs/configuring-zed +// +// To see all of Zed's default settings without changing your +// custom settings, run the `open default settings` command +// from the command palette or from `Zed` application menu. diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 219351896a..f976eb3054 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -107,7 +107,7 @@ impl Settings { } let defaults: SettingsFileContent = parse_json_with_comments( - str::from_utf8(assets.load("default-settings.json").unwrap().as_ref()).unwrap(), + str::from_utf8(assets.load("settings/default.json").unwrap().as_ref()).unwrap(), ) .unwrap(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 1bfa75b920..d27a5b7c5b 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -18,8 +18,9 @@ use gpui::{ geometry::vector::vec2f, impl_actions, platform::{WindowBounds, WindowOptions}, - AsyncAppContext, ViewContext, + AssetSource, AsyncAppContext, ViewContext, }; +use language::Rope; use lazy_static::lazy_static; pub use lsp; pub use project::{self, fs}; @@ -100,13 +101,22 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { cx.add_action({ let app_state = app_state.clone(); move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext| { - open_config_file(&SETTINGS_PATH, app_state.clone(), cx); + open_config_file(&SETTINGS_PATH, app_state.clone(), cx, || { + let header = Assets.load("settings/header-comments.json").unwrap(); + let json = Assets.load("settings/default.json").unwrap(); + let header = str::from_utf8(header.as_ref()).unwrap(); + let json = str::from_utf8(json.as_ref()).unwrap(); + let mut content = Rope::new(); + content.push(header); + content.push(json); + content + }); } }); cx.add_action({ let app_state = app_state.clone(); move |_: &mut Workspace, _: &OpenKeymap, cx: &mut ViewContext| { - open_config_file(&KEYMAP_PATH, app_state.clone(), cx); + open_config_file(&KEYMAP_PATH, app_state.clone(), cx, || Default::default()); } }); cx.add_action({ @@ -129,7 +139,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { open_bundled_config_file( workspace, app_state.clone(), - "default-settings.json", + "settings/default.json", "Default Settings", cx, ); @@ -367,12 +377,15 @@ fn open_config_file( path: &'static Path, app_state: Arc, cx: &mut ViewContext, + default_content: impl 'static + Send + FnOnce() -> Rope, ) { cx.spawn(|workspace, mut cx| async move { let fs = &app_state.fs; if !fs.is_file(path).await { fs.create_dir(&ROOT_PATH).await?; fs.create_file(path, Default::default()).await?; + fs.save(path, &default_content(), Default::default()) + .await?; } workspace