diff --git a/assets/settings/default.json b/assets/settings/default.json new file mode 100644 index 0000000000..2417d0f5d4 --- /dev/null +++ b/assets/settings/default.json @@ -0,0 +1,109 @@ +{ + // 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", + + // 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" + }, + "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/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/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..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; @@ -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); }); }); @@ -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/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..f976eb3054 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -1,17 +1,18 @@ 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::{ - InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec, SubschemaValidation, - }, + schema::{InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec}, JsonSchema, }; 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 _; @@ -26,14 +27,15 @@ pub struct Settings { pub hover_popover_enabled: bool, pub vim_mode: bool, pub autosave: Autosave, - pub language_settings: 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, @@ -83,44 +85,61 @@ 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, + pub editor: EditorSettings, #[serde(default)] - pub language_overrides: HashMap, LanguageSettings>, + #[serde(alias = "language_overrides")] + pub languages: HashMap, EditorSettings>, #[serde(default)] pub theme: Option, } 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 { + fn required(value: Option) -> Option { + assert!(value.is_some(), "missing default setting value"); + value + } + + let defaults: SettingsFileContent = parse_json_with_comments( + str::from_utf8(assets.load("settings/default.json").unwrap().as_ref()).unwrap(), + ) + .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(), + editor_defaults: EditorSettings { + 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.languages, + editor_overrides: Default::default(), language_overrides: Default::default(), - projects_online_by_default: true, - theme, - }) + theme: themes.get(&defaults.theme.unwrap()).unwrap(), + } } pub fn with_language_defaults( mut self, language_name: impl Into>, - overrides: LanguageSettings, + overrides: EditorSettings, ) -> Self { self.language_defaults .insert(language_name.into(), overrides); @@ -129,48 +148,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, + F: Fn(&EditorSettings) -> 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"))] @@ -182,7 +190,15 @@ impl Settings { hover_popover_enabled: true, vim_mode: false, autosave: Autosave::Off, - language_settings: Default::default(), + editor_defaults: EditorSettings { + 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, @@ -224,22 +240,23 @@ 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, ); - 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()) @@ -270,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() } 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/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()); 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/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() }, diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 56ccfaf9fe..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}; @@ -52,6 +53,7 @@ actions!( DebugElements, OpenSettings, OpenKeymap, + OpenDefaultSettings, OpenDefaultKeymap, IncreaseBufferFontSize, DecreaseBufferFontSize, @@ -99,39 +101,48 @@ 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({ 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(), + "settings/default.json", + "Default Settings", + cx, + ); } }); cx.add_action( @@ -366,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 @@ -386,6 +400,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::*; @@ -400,7 +438,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 +1568,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);