Merge pull request #1331 from zed-industries/discoverable-settings

Make settings more discoverable
This commit is contained in:
Max Brunsfeld 2022-07-12 14:38:16 -07:00 committed by GitHub
commit dd554c19df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 348 additions and 244 deletions

View file

@ -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
}
}
}

View file

@ -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.

View file

@ -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. // host's configuration is honored as opposed to using the guest's settings.
cx_a.update(|cx| { cx_a.update(|cx| {
cx.update_global(|settings: &mut Settings, _| { 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(), command: "awk".to_string(),
arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()], arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()],
}); });

View file

@ -983,7 +983,7 @@ pub mod tests {
language.set_theme(&theme); language.set_theme(&theme);
cx.update(|cx| { cx.update(|cx| {
let mut settings = Settings::test(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); cx.set_global(settings);
}); });

View file

@ -6236,7 +6236,7 @@ mod tests {
use language::{FakeLspAdapter, LanguageConfig}; use language::{FakeLspAdapter, LanguageConfig};
use lsp::FakeLanguageServer; use lsp::FakeLanguageServer;
use project::FakeFs; use project::FakeFs;
use settings::LanguageSettings; use settings::EditorSettings;
use std::{cell::RefCell, rc::Rc, time::Instant}; use std::{cell::RefCell, rc::Rc, time::Instant};
use text::Point; use text::Point;
use unindent::Unindent; use unindent::Unindent;
@ -7613,7 +7613,7 @@ mod tests {
let mut cx = EditorTestContext::new(cx).await; let mut cx = EditorTestContext::new(cx).await;
cx.update(|cx| { cx.update(|cx| {
cx.update_global::<Settings, _, _>(|settings, _| { cx.update_global::<Settings, _, _>(|settings, _| {
settings.language_settings.hard_tabs = Some(true); settings.editor_overrides.hard_tabs = Some(true);
}); });
}); });
@ -7696,14 +7696,14 @@ mod tests {
Settings::test(cx) Settings::test(cx)
.with_language_defaults( .with_language_defaults(
"TOML", "TOML",
LanguageSettings { EditorSettings {
tab_size: Some(2.try_into().unwrap()), tab_size: Some(2.try_into().unwrap()),
..Default::default() ..Default::default()
}, },
) )
.with_language_defaults( .with_language_defaults(
"Rust", "Rust",
LanguageSettings { EditorSettings {
tab_size: Some(4.try_into().unwrap()), tab_size: Some(4.try_into().unwrap()),
..Default::default() ..Default::default()
}, },
@ -9380,7 +9380,7 @@ mod tests {
cx.update_global::<Settings, _, _>(|settings, _| { cx.update_global::<Settings, _, _>(|settings, _| {
settings.language_overrides.insert( settings.language_overrides.insert(
"Rust".into(), "Rust".into(),
LanguageSettings { EditorSettings {
tab_size: Some(8.try_into().unwrap()), tab_size: Some(8.try_into().unwrap()),
..Default::default() ..Default::default()
}, },
@ -9496,7 +9496,7 @@ mod tests {
cx.update_global::<Settings, _, _>(|settings, _| { cx.update_global::<Settings, _, _>(|settings, _| {
settings.language_overrides.insert( settings.language_overrides.insert(
"Rust".into(), "Rust".into(),
LanguageSettings { EditorSettings {
tab_size: Some(8.try_into().unwrap()), tab_size: Some(8.try_into().unwrap()),
..Default::default() ..Default::default()
}, },

View file

@ -883,7 +883,7 @@ async fn test_toggling_enable_language_server(
cx.update_global(|settings: &mut Settings, _| { cx.update_global(|settings: &mut Settings, _| {
settings.language_overrides.insert( settings.language_overrides.insert(
Arc::from("Rust"), Arc::from("Rust"),
settings::LanguageSettings { settings::EditorSettings {
enable_language_server: Some(false), enable_language_server: Some(false),
..Default::default() ..Default::default()
}, },
@ -900,14 +900,14 @@ async fn test_toggling_enable_language_server(
cx.update_global(|settings: &mut Settings, _| { cx.update_global(|settings: &mut Settings, _| {
settings.language_overrides.insert( settings.language_overrides.insert(
Arc::from("Rust"), Arc::from("Rust"),
settings::LanguageSettings { settings::EditorSettings {
enable_language_server: Some(true), enable_language_server: Some(true),
..Default::default() ..Default::default()
}, },
); );
settings.language_overrides.insert( settings.language_overrides.insert(
Arc::from("JavaScript"), Arc::from("JavaScript"),
settings::LanguageSettings { settings::EditorSettings {
enable_language_server: Some(false), enable_language_server: Some(false),
..Default::default() ..Default::default()
}, },

View file

@ -608,8 +608,11 @@ mod tests {
let fonts = cx.font_cache(); let fonts = cx.font_cache();
let mut theme = gpui::fonts::with_font_cache(fonts.clone(), || theme::Theme::default()); let mut theme = gpui::fonts::with_font_cache(fonts.clone(), || theme::Theme::default());
theme.search.match_background = Color::red(); theme.search.match_background = Color::red();
let settings = Settings::new("Courier", &fonts, Arc::new(theme)).unwrap(); cx.update(|cx| {
cx.update(|cx| cx.set_global(settings)); let mut settings = Settings::test(cx);
settings.theme = Arc::new(theme);
cx.set_global(settings)
});
let buffer = cx.add_model(|cx| { let buffer = cx.add_model(|cx| {
Buffer::new( Buffer::new(

View file

@ -911,8 +911,11 @@ mod tests {
let fonts = cx.font_cache(); let fonts = cx.font_cache();
let mut theme = gpui::fonts::with_font_cache(fonts.clone(), || theme::Theme::default()); let mut theme = gpui::fonts::with_font_cache(fonts.clone(), || theme::Theme::default());
theme.search.match_background = Color::red(); theme.search.match_background = Color::red();
let settings = Settings::new("Courier", &fonts, Arc::new(theme)).unwrap(); cx.update(|cx| {
cx.update(|cx| cx.set_global(settings)); let mut settings = Settings::test(cx);
settings.theme = Arc::new(theme);
cx.set_global(settings)
});
let fs = FakeFs::new(cx.background()); let fs = FakeFs::new(cx.background());
fs.insert_tree( fs.insert_tree(

View file

@ -1,17 +1,18 @@
mod keymap_file; mod keymap_file;
use anyhow::Result; use anyhow::Result;
use gpui::font_cache::{FamilyId, FontCache}; use gpui::{
font_cache::{FamilyId, FontCache},
AssetSource,
};
use schemars::{ use schemars::{
gen::{SchemaGenerator, SchemaSettings}, gen::{SchemaGenerator, SchemaSettings},
schema::{ schema::{InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec},
InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec, SubschemaValidation,
},
JsonSchema, JsonSchema,
}; };
use serde::{de::DeserializeOwned, Deserialize}; use serde::{de::DeserializeOwned, Deserialize};
use serde_json::Value; 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 theme::{Theme, ThemeRegistry};
use util::ResultExt as _; use util::ResultExt as _;
@ -26,14 +27,15 @@ pub struct Settings {
pub hover_popover_enabled: bool, pub hover_popover_enabled: bool,
pub vim_mode: bool, pub vim_mode: bool,
pub autosave: Autosave, pub autosave: Autosave,
pub language_settings: LanguageSettings, pub editor_defaults: EditorSettings,
pub language_defaults: HashMap<Arc<str>, LanguageSettings>, pub editor_overrides: EditorSettings,
pub language_overrides: HashMap<Arc<str>, LanguageSettings>, pub language_defaults: HashMap<Arc<str>, EditorSettings>,
pub language_overrides: HashMap<Arc<str>, EditorSettings>,
pub theme: Arc<Theme>, pub theme: Arc<Theme>,
} }
#[derive(Clone, Debug, Default, Deserialize, JsonSchema)] #[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
pub struct LanguageSettings { pub struct EditorSettings {
pub tab_size: Option<NonZeroU32>, pub tab_size: Option<NonZeroU32>,
pub hard_tabs: Option<bool>, pub hard_tabs: Option<bool>,
pub soft_wrap: Option<SoftWrap>, pub soft_wrap: Option<SoftWrap>,
@ -83,44 +85,61 @@ pub struct SettingsFileContent {
#[serde(default)] #[serde(default)]
pub vim_mode: Option<bool>, pub vim_mode: Option<bool>,
#[serde(default)] #[serde(default)]
pub format_on_save: Option<FormatOnSave>,
#[serde(default)]
pub autosave: Option<Autosave>, pub autosave: Option<Autosave>,
#[serde(default)]
pub enable_language_server: Option<bool>,
#[serde(flatten)] #[serde(flatten)]
pub editor: LanguageSettings, pub editor: EditorSettings,
#[serde(default)] #[serde(default)]
pub language_overrides: HashMap<Arc<str>, LanguageSettings>, #[serde(alias = "language_overrides")]
pub languages: HashMap<Arc<str>, EditorSettings>,
#[serde(default)] #[serde(default)]
pub theme: Option<String>, pub theme: Option<String>,
} }
impl Settings { impl Settings {
pub fn new( pub fn defaults(
buffer_font_family: &str, assets: impl AssetSource,
font_cache: &FontCache, font_cache: &FontCache,
theme: Arc<Theme>, themes: &ThemeRegistry,
) -> Result<Self> { ) -> Self {
Ok(Self { fn required<T>(value: Option<T>) -> Option<T> {
buffer_font_family: font_cache.load_family(&[buffer_font_family])?, assert!(value.is_some(), "missing default setting value");
buffer_font_size: 15., value
default_buffer_font_size: 15., }
hover_popover_enabled: true,
vim_mode: false, let defaults: SettingsFileContent = parse_json_with_comments(
autosave: Autosave::Off, str::from_utf8(assets.load("settings/default.json").unwrap().as_ref()).unwrap(),
language_settings: Default::default(), )
language_defaults: Default::default(), .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(), language_overrides: Default::default(),
projects_online_by_default: true, theme: themes.get(&defaults.theme.unwrap()).unwrap(),
theme, }
})
} }
pub fn with_language_defaults( pub fn with_language_defaults(
mut self, mut self,
language_name: impl Into<Arc<str>>, language_name: impl Into<Arc<str>>,
overrides: LanguageSettings, overrides: EditorSettings,
) -> Self { ) -> Self {
self.language_defaults self.language_defaults
.insert(language_name.into(), overrides); .insert(language_name.into(), overrides);
@ -129,48 +148,37 @@ impl Settings {
pub fn tab_size(&self, language: Option<&str>) -> NonZeroU32 { pub fn tab_size(&self, language: Option<&str>) -> NonZeroU32 {
self.language_setting(language, |settings| settings.tab_size) self.language_setting(language, |settings| settings.tab_size)
.unwrap_or(4.try_into().unwrap())
} }
pub fn hard_tabs(&self, language: Option<&str>) -> bool { pub fn hard_tabs(&self, language: Option<&str>) -> bool {
self.language_setting(language, |settings| settings.hard_tabs) self.language_setting(language, |settings| settings.hard_tabs)
.unwrap_or(false)
} }
pub fn soft_wrap(&self, language: Option<&str>) -> SoftWrap { pub fn soft_wrap(&self, language: Option<&str>) -> SoftWrap {
self.language_setting(language, |settings| settings.soft_wrap) self.language_setting(language, |settings| settings.soft_wrap)
.unwrap_or(SoftWrap::None)
} }
pub fn preferred_line_length(&self, language: Option<&str>) -> u32 { pub fn preferred_line_length(&self, language: Option<&str>) -> u32 {
self.language_setting(language, |settings| settings.preferred_line_length) self.language_setting(language, |settings| settings.preferred_line_length)
.unwrap_or(80)
} }
pub fn format_on_save(&self, language: Option<&str>) -> FormatOnSave { pub fn format_on_save(&self, language: Option<&str>) -> FormatOnSave {
self.language_setting(language, |settings| settings.format_on_save.clone()) self.language_setting(language, |settings| settings.format_on_save.clone())
.unwrap_or(FormatOnSave::LanguageServer)
} }
pub fn enable_language_server(&self, language: Option<&str>) -> bool { pub fn enable_language_server(&self, language: Option<&str>) -> bool {
self.language_setting(language, |settings| settings.enable_language_server) self.language_setting(language, |settings| settings.enable_language_server)
.unwrap_or(true)
} }
fn language_setting<F, R>(&self, language: Option<&str>, f: F) -> Option<R> fn language_setting<F, R>(&self, language: Option<&str>, f: F) -> R
where where
F: Fn(&LanguageSettings) -> Option<R>, F: Fn(&EditorSettings) -> Option<R>,
{ {
let mut language_override = None; None.or_else(|| language.and_then(|l| self.language_overrides.get(l).and_then(&f)))
let mut language_default = None; .or_else(|| f(&self.editor_overrides))
if let Some(language) = language { .or_else(|| language.and_then(|l| self.language_defaults.get(l).and_then(&f)))
language_override = self.language_overrides.get(language).and_then(&f); .or_else(|| f(&self.editor_defaults))
language_default = self.language_defaults.get(language).and_then(&f); .expect("missing default")
}
language_override
.or_else(|| f(&self.language_settings))
.or(language_default)
} }
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
@ -182,7 +190,15 @@ impl Settings {
hover_popover_enabled: true, hover_popover_enabled: true,
vim_mode: false, vim_mode: false,
autosave: Autosave::Off, 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_defaults: Default::default(),
language_overrides: Default::default(), language_overrides: Default::default(),
projects_online_by_default: true, projects_online_by_default: true,
@ -224,22 +240,23 @@ impl Settings {
merge(&mut self.hover_popover_enabled, data.hover_popover_enabled); merge(&mut self.hover_popover_enabled, data.hover_popover_enabled);
merge(&mut self.vim_mode, data.vim_mode); merge(&mut self.vim_mode, data.vim_mode);
merge(&mut self.autosave, data.autosave); merge(&mut self.autosave, data.autosave);
merge_option( merge_option(
&mut self.language_settings.format_on_save, &mut self.editor_overrides.format_on_save,
data.format_on_save.clone(), data.editor.format_on_save.clone(),
); );
merge_option( merge_option(
&mut self.language_settings.enable_language_server, &mut self.editor_overrides.enable_language_server,
data.enable_language_server, data.editor.enable_language_server,
); );
merge_option(&mut self.language_settings.soft_wrap, data.editor.soft_wrap); merge_option(&mut self.editor_overrides.soft_wrap, data.editor.soft_wrap);
merge_option(&mut self.language_settings.tab_size, data.editor.tab_size); merge_option(&mut self.editor_overrides.tab_size, data.editor.tab_size);
merge_option( merge_option(
&mut self.language_settings.preferred_line_length, &mut self.editor_overrides.preferred_line_length,
data.editor.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 let target = self
.language_overrides .language_overrides
.entry(language_name.into()) .entry(language_name.into())
@ -270,77 +287,61 @@ pub fn settings_file_json_schema(
let generator = SchemaGenerator::new(settings); let generator = SchemaGenerator::new(settings);
let mut root_schema = generator.into_root_schema_for::<SettingsFileContent>(); let mut root_schema = generator.into_root_schema_for::<SettingsFileContent>();
// Construct theme names reference type // Create a schema for a theme name.
let theme_names = theme_names let theme_name_schema = SchemaObject {
.into_iter()
.map(|name| Value::String(name))
.collect();
let theme_names_schema = Schema::Object(SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), 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() ..Default::default()
}); };
root_schema
.definitions
.insert("ThemeName".to_owned(), theme_names_schema);
// Construct language settings reference type // Create a schema for a 'languages overrides' object, associating editor
let language_settings_schema_reference = Schema::Object(SchemaObject { // settings with specific langauges.
reference: Some("#/definitions/LanguageSettings".to_owned()), assert!(root_schema.definitions.contains_key("EditorSettings"));
..Default::default() let languages_object_schema = SchemaObject {
});
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 {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))),
object: Some(Box::new(ObjectValidation { 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()
})), })),
..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 root_schema
.definitions .schema
.insert("LanguageOverrides".to_owned(), language_overrides_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() serde_json::to_value(root_schema).unwrap()
} }

View file

@ -12,8 +12,6 @@ use std::{collections::HashMap, sync::Arc};
pub use theme_registry::*; pub use theme_registry::*;
pub const DEFAULT_THEME_NAME: &'static str = "cave-dark";
#[derive(Deserialize, Default)] #[derive(Deserialize, Default)]
pub struct Theme { pub struct Theme {
#[serde(default)] #[serde(default)]

View file

@ -38,7 +38,7 @@ use std::{
time::Duration, time::Duration,
}; };
use terminal; use terminal;
use theme::{ThemeRegistry, DEFAULT_THEME_NAME}; use theme::ThemeRegistry;
use util::{ResultExt, TryFutureExt}; use util::{ResultExt, TryFutureExt};
use workspace::{self, AppState, NewFile, OpenPaths}; use workspace::{self, AppState, NewFile, OpenPaths};
use zed::{ use zed::{
@ -72,73 +72,7 @@ fn main() {
let fs = Arc::new(RealFs); let fs = Arc::new(RealFs);
let themes = ThemeRegistry::new(Assets, app.font_cache()); let themes = ThemeRegistry::new(Assets, app.font_cache());
let theme = themes.get(DEFAULT_THEME_NAME).unwrap(); let default_settings = Settings::defaults(Assets, &app.font_cache(), &themes);
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 config_files = load_config_files(&app, fs.clone()); let config_files = load_config_files(&app, fs.clone());

View file

@ -26,6 +26,10 @@ pub fn menus() -> Vec<Menu<'static>> {
name: "Open Key Bindings", name: "Open Key Bindings",
action: Box::new(super::OpenKeymap), action: Box::new(super::OpenKeymap),
}, },
MenuItem::Action {
name: "Open Default Settings",
action: Box::new(super::OpenDefaultSettings),
},
MenuItem::Action { MenuItem::Action {
name: "Open Default Key Bindings", name: "Open Default Key Bindings",
action: Box::new(super::OpenDefaultKeymap), action: Box::new(super::OpenDefaultKeymap),

View file

@ -93,7 +93,7 @@ pub async fn watch_keymap_file(
mod tests { mod tests {
use super::*; use super::*;
use project::FakeFs; use project::FakeFs;
use settings::{LanguageSettings, SoftWrap}; use settings::{EditorSettings, SoftWrap};
#[gpui::test] #[gpui::test]
async fn test_settings_from_files(cx: &mut gpui::TestAppContext) { 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( let settings = cx.read(Settings::test).with_language_defaults(
"JavaScript", "JavaScript",
LanguageSettings { EditorSettings {
tab_size: Some(2.try_into().unwrap()), tab_size: Some(2.try_into().unwrap()),
..Default::default() ..Default::default()
}, },

View file

@ -18,8 +18,9 @@ use gpui::{
geometry::vector::vec2f, geometry::vector::vec2f,
impl_actions, impl_actions,
platform::{WindowBounds, WindowOptions}, platform::{WindowBounds, WindowOptions},
AsyncAppContext, ViewContext, AssetSource, AsyncAppContext, ViewContext,
}; };
use language::Rope;
use lazy_static::lazy_static; use lazy_static::lazy_static;
pub use lsp; pub use lsp;
pub use project::{self, fs}; pub use project::{self, fs};
@ -52,6 +53,7 @@ actions!(
DebugElements, DebugElements,
OpenSettings, OpenSettings,
OpenKeymap, OpenKeymap,
OpenDefaultSettings,
OpenDefaultKeymap, OpenDefaultKeymap,
IncreaseBufferFontSize, IncreaseBufferFontSize,
DecreaseBufferFontSize, DecreaseBufferFontSize,
@ -99,39 +101,48 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
cx.add_action({ cx.add_action({
let app_state = app_state.clone(); let app_state = app_state.clone();
move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext<Workspace>| { move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext<Workspace>| {
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({ cx.add_action({
let app_state = app_state.clone(); let app_state = app_state.clone();
move |_: &mut Workspace, _: &OpenKeymap, cx: &mut ViewContext<Workspace>| { move |_: &mut Workspace, _: &OpenKeymap, cx: &mut ViewContext<Workspace>| {
open_config_file(&KEYMAP_PATH, app_state.clone(), cx); open_config_file(&KEYMAP_PATH, app_state.clone(), cx, || Default::default());
} }
}); });
cx.add_action({ cx.add_action({
let app_state = app_state.clone(); let app_state = app_state.clone();
move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext<Workspace>| { move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext<Workspace>| {
workspace.with_local_workspace(cx, app_state.clone(), |workspace, cx| { open_bundled_config_file(
let project = workspace.project().clone(); workspace,
let buffer = project.update(cx, |project, cx| { app_state.clone(),
let text = Assets::get("keymaps/default.json").unwrap().data; "keymaps/default.json",
let text = str::from_utf8(text.as_ref()).unwrap(); "Default Key Bindings",
project cx,
.create_buffer(text, project.languages().get_language("JSON"), cx) );
.expect("creating buffers on a local workspace always succeeds") }
}); });
let buffer = cx.add_model(|cx| { cx.add_action({
MultiBuffer::singleton(buffer, cx).with_title("Default Key Bindings".into()) let app_state = app_state.clone();
}); move |workspace: &mut Workspace,
workspace.add_item( _: &OpenDefaultSettings,
Box::new( cx: &mut ViewContext<Workspace>| {
cx.add_view(|cx| { open_bundled_config_file(
Editor::for_multibuffer(buffer, Some(project.clone()), cx) workspace,
}), app_state.clone(),
), "settings/default.json",
cx, "Default Settings",
); cx,
}); );
} }
}); });
cx.add_action( cx.add_action(
@ -366,12 +377,15 @@ fn open_config_file(
path: &'static Path, path: &'static Path,
app_state: Arc<AppState>, app_state: Arc<AppState>,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
default_content: impl 'static + Send + FnOnce() -> Rope,
) { ) {
cx.spawn(|workspace, mut cx| async move { cx.spawn(|workspace, mut cx| async move {
let fs = &app_state.fs; let fs = &app_state.fs;
if !fs.is_file(path).await { if !fs.is_file(path).await {
fs.create_dir(&ROOT_PATH).await?; fs.create_dir(&ROOT_PATH).await?;
fs.create_file(path, Default::default()).await?; fs.create_file(path, Default::default()).await?;
fs.save(path, &default_content(), Default::default())
.await?;
} }
workspace workspace
@ -386,6 +400,30 @@ fn open_config_file(
.detach_and_log_err(cx) .detach_and_log_err(cx)
} }
fn open_bundled_config_file(
workspace: &mut Workspace,
app_state: Arc<AppState>,
asset_path: &'static str,
title: &str,
cx: &mut ViewContext<Workspace>,
) {
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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -400,7 +438,7 @@ mod tests {
collections::HashSet, collections::HashSet,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use theme::{Theme, ThemeRegistry, DEFAULT_THEME_NAME}; use theme::ThemeRegistry;
use workspace::{ use workspace::{
open_paths, pane, Item, ItemHandle, NewFile, Pane, SplitDirection, WorkspaceHandle, open_paths, pane, Item, ItemHandle, NewFile, Pane, SplitDirection, WorkspaceHandle,
}; };
@ -1530,23 +1568,29 @@ mod tests {
} }
#[gpui::test] #[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()); let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());
let settings = Settings::defaults(Assets, cx.font_cache(), &themes);
lazy_static::lazy_static! {
static ref DEFAULT_THEME: parking_lot::Mutex<Option<Arc<Theme>>> = Default::default();
static ref FONTS: Vec<Arc<Vec<u8>>> = 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 mut has_default_theme = false; let mut has_default_theme = false;
for theme_name in themes.list() { for theme_name in themes.list() {
let theme = themes.get(&theme_name).unwrap(); let theme = themes.get(&theme_name).unwrap();
if theme.name == DEFAULT_THEME_NAME { if theme.name == settings.theme.name {
has_default_theme = true; has_default_theme = true;
} }
assert_eq!(theme.name, theme_name); assert_eq!(theme.name, theme_name);