2022-04-11 23:50:44 +00:00
|
|
|
mod keymap_file;
|
2022-04-11 22:22:18 +00:00
|
|
|
|
2022-04-06 00:10:17 +00:00
|
|
|
use anyhow::Result;
|
|
|
|
use gpui::font_cache::{FamilyId, FontCache};
|
2022-04-07 23:00:51 +00:00
|
|
|
use schemars::{
|
|
|
|
gen::{SchemaGenerator, SchemaSettings},
|
2022-04-08 01:15:02 +00:00
|
|
|
schema::{
|
|
|
|
InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec, SubschemaValidation,
|
|
|
|
},
|
2022-04-07 23:00:51 +00:00
|
|
|
JsonSchema,
|
|
|
|
};
|
2022-04-21 18:58:18 +00:00
|
|
|
use serde::{de::DeserializeOwned, Deserialize};
|
2022-04-08 01:15:02 +00:00
|
|
|
use serde_json::Value;
|
2022-06-23 00:13:52 +00:00
|
|
|
use std::{collections::HashMap, num::NonZeroU32, sync::Arc};
|
2022-04-06 00:10:17 +00:00
|
|
|
use theme::{Theme, ThemeRegistry};
|
|
|
|
use util::ResultExt as _;
|
|
|
|
|
2022-04-21 20:33:39 +00:00
|
|
|
pub use keymap_file::{keymap_file_json_schema, KeymapFileContent};
|
2022-04-11 23:50:44 +00:00
|
|
|
|
2022-04-06 00:10:17 +00:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct Settings {
|
2022-06-03 23:57:50 +00:00
|
|
|
pub projects_online_by_default: bool,
|
2022-04-06 00:10:17 +00:00
|
|
|
pub buffer_font_family: FamilyId,
|
|
|
|
pub buffer_font_size: f32,
|
2022-05-27 20:02:52 +00:00
|
|
|
pub default_buffer_font_size: f32,
|
2022-04-06 00:10:17 +00:00
|
|
|
pub vim_mode: bool,
|
2022-06-23 14:40:57 +00:00
|
|
|
pub language_settings: LanguageSettings,
|
|
|
|
pub language_defaults: HashMap<Arc<str>, LanguageSettings>,
|
|
|
|
pub language_overrides: HashMap<Arc<str>, LanguageSettings>,
|
2022-04-06 00:10:17 +00:00
|
|
|
pub theme: Arc<Theme>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
|
2022-06-23 14:40:57 +00:00
|
|
|
pub struct LanguageSettings {
|
2022-06-23 00:13:52 +00:00
|
|
|
pub tab_size: Option<NonZeroU32>,
|
2022-06-09 01:08:07 +00:00
|
|
|
pub hard_tabs: Option<bool>,
|
2022-04-06 00:10:17 +00:00
|
|
|
pub soft_wrap: Option<SoftWrap>,
|
|
|
|
pub preferred_line_length: Option<u32>,
|
2022-04-28 21:31:06 +00:00
|
|
|
pub format_on_save: Option<bool>,
|
2022-06-09 08:07:31 +00:00
|
|
|
pub enable_language_server: Option<bool>,
|
2022-04-06 00:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
|
|
|
|
#[serde(rename_all = "snake_case")]
|
|
|
|
pub enum SoftWrap {
|
|
|
|
None,
|
|
|
|
EditorWidth,
|
|
|
|
PreferredLineLength,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
|
|
|
|
pub struct SettingsFileContent {
|
2022-06-03 23:57:50 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub projects_online_by_default: Option<bool>,
|
2022-04-06 00:10:17 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub buffer_font_family: Option<String>,
|
|
|
|
#[serde(default)]
|
|
|
|
pub buffer_font_size: Option<f32>,
|
|
|
|
#[serde(default)]
|
|
|
|
pub vim_mode: Option<bool>,
|
2022-04-28 21:31:06 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub format_on_save: Option<bool>,
|
2022-06-09 08:07:31 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub enable_language_server: Option<bool>,
|
2022-04-06 00:10:17 +00:00
|
|
|
#[serde(flatten)]
|
2022-06-23 14:40:57 +00:00
|
|
|
pub editor: LanguageSettings,
|
2022-04-06 00:10:17 +00:00
|
|
|
#[serde(default)]
|
2022-06-23 14:40:57 +00:00
|
|
|
pub language_overrides: HashMap<Arc<str>, LanguageSettings>,
|
2022-04-06 00:10:17 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub theme: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Settings {
|
|
|
|
pub fn new(
|
|
|
|
buffer_font_family: &str,
|
|
|
|
font_cache: &FontCache,
|
|
|
|
theme: Arc<Theme>,
|
|
|
|
) -> Result<Self> {
|
|
|
|
Ok(Self {
|
|
|
|
buffer_font_family: font_cache.load_family(&[buffer_font_family])?,
|
|
|
|
buffer_font_size: 15.,
|
2022-05-27 20:02:52 +00:00
|
|
|
default_buffer_font_size: 15.,
|
2022-04-06 00:10:17 +00:00
|
|
|
vim_mode: false,
|
2022-06-23 14:40:57 +00:00
|
|
|
language_settings: Default::default(),
|
|
|
|
language_defaults: Default::default(),
|
2022-04-06 00:10:17 +00:00
|
|
|
language_overrides: Default::default(),
|
2022-06-03 23:57:50 +00:00
|
|
|
projects_online_by_default: true,
|
2022-04-06 00:10:17 +00:00
|
|
|
theme,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-06-23 14:40:57 +00:00
|
|
|
pub fn with_language_defaults(
|
2022-04-06 00:10:17 +00:00
|
|
|
mut self,
|
|
|
|
language_name: impl Into<Arc<str>>,
|
2022-06-23 14:40:57 +00:00
|
|
|
overrides: LanguageSettings,
|
2022-04-06 00:10:17 +00:00
|
|
|
) -> Self {
|
2022-06-23 14:40:57 +00:00
|
|
|
self.language_defaults
|
2022-04-06 00:10:17 +00:00
|
|
|
.insert(language_name.into(), overrides);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2022-06-23 00:13:52 +00:00
|
|
|
pub fn tab_size(&self, language: Option<&str>) -> NonZeroU32 {
|
2022-06-23 14:40:57 +00:00
|
|
|
self.language_setting(language, |settings| settings.tab_size)
|
2022-06-23 00:13:52 +00:00
|
|
|
.unwrap_or(4.try_into().unwrap())
|
2022-04-06 00:10:17 +00:00
|
|
|
}
|
|
|
|
|
2022-06-09 01:08:07 +00:00
|
|
|
pub fn hard_tabs(&self, language: Option<&str>) -> bool {
|
2022-06-23 14:40:57 +00:00
|
|
|
self.language_setting(language, |settings| settings.hard_tabs)
|
|
|
|
.unwrap_or(false)
|
2022-06-09 01:08:07 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 00:10:17 +00:00
|
|
|
pub fn soft_wrap(&self, language: Option<&str>) -> SoftWrap {
|
2022-06-23 14:40:57 +00:00
|
|
|
self.language_setting(language, |settings| settings.soft_wrap)
|
|
|
|
.unwrap_or(SoftWrap::None)
|
2022-04-06 00:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn preferred_line_length(&self, language: Option<&str>) -> u32 {
|
2022-06-23 14:40:57 +00:00
|
|
|
self.language_setting(language, |settings| settings.preferred_line_length)
|
|
|
|
.unwrap_or(80)
|
2022-04-06 00:10:17 +00:00
|
|
|
}
|
|
|
|
|
2022-04-28 21:31:06 +00:00
|
|
|
pub fn format_on_save(&self, language: Option<&str>) -> bool {
|
2022-06-23 14:40:57 +00:00
|
|
|
self.language_setting(language, |settings| settings.format_on_save)
|
|
|
|
.unwrap_or(true)
|
2022-04-28 21:31:06 +00:00
|
|
|
}
|
|
|
|
|
2022-06-09 08:07:31 +00:00
|
|
|
pub fn enable_language_server(&self, language: Option<&str>) -> bool {
|
2022-06-23 14:40:57 +00:00
|
|
|
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>
|
|
|
|
where
|
|
|
|
F: Fn(&LanguageSettings) -> Option<R>,
|
|
|
|
{
|
|
|
|
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)
|
2022-06-09 08:07:31 +00:00
|
|
|
}
|
|
|
|
|
2022-04-06 00:10:17 +00:00
|
|
|
#[cfg(any(test, feature = "test-support"))]
|
|
|
|
pub fn test(cx: &gpui::AppContext) -> Settings {
|
|
|
|
Settings {
|
|
|
|
buffer_font_family: cx.font_cache().load_family(&["Monaco"]).unwrap(),
|
|
|
|
buffer_font_size: 14.,
|
2022-05-27 20:02:52 +00:00
|
|
|
default_buffer_font_size: 14.,
|
2022-04-06 00:10:17 +00:00
|
|
|
vim_mode: false,
|
2022-06-23 14:40:57 +00:00
|
|
|
language_settings: Default::default(),
|
|
|
|
language_defaults: Default::default(),
|
2022-04-06 00:10:17 +00:00
|
|
|
language_overrides: Default::default(),
|
2022-06-03 23:57:50 +00:00
|
|
|
projects_online_by_default: true,
|
2022-04-06 00:10:17 +00:00
|
|
|
theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), || Default::default()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-23 23:03:00 +00:00
|
|
|
#[cfg(any(test, feature = "test-support"))]
|
|
|
|
pub fn test_async(cx: &mut gpui::TestAppContext) {
|
|
|
|
cx.update(|cx| {
|
|
|
|
let settings = Self::test(cx);
|
|
|
|
cx.set_global(settings.clone());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-04-06 00:10:17 +00:00
|
|
|
pub fn merge(
|
|
|
|
&mut self,
|
|
|
|
data: &SettingsFileContent,
|
|
|
|
theme_registry: &ThemeRegistry,
|
|
|
|
font_cache: &FontCache,
|
|
|
|
) {
|
|
|
|
if let Some(value) = &data.buffer_font_family {
|
|
|
|
if let Some(id) = font_cache.load_family(&[value]).log_err() {
|
|
|
|
self.buffer_font_family = id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(value) = &data.theme {
|
2022-04-07 23:00:51 +00:00
|
|
|
if let Some(theme) = theme_registry.get(&value.to_string()).log_err() {
|
2022-04-06 00:10:17 +00:00
|
|
|
self.theme = theme;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-03 23:57:50 +00:00
|
|
|
merge(
|
|
|
|
&mut self.projects_online_by_default,
|
|
|
|
data.projects_online_by_default,
|
|
|
|
);
|
2022-04-06 00:10:17 +00:00
|
|
|
merge(&mut self.buffer_font_size, data.buffer_font_size);
|
2022-05-27 20:02:52 +00:00
|
|
|
merge(&mut self.default_buffer_font_size, data.buffer_font_size);
|
2022-04-06 00:10:17 +00:00
|
|
|
merge(&mut self.vim_mode, data.vim_mode);
|
2022-06-23 14:40:57 +00:00
|
|
|
merge_option(
|
|
|
|
&mut self.language_settings.format_on_save,
|
|
|
|
data.format_on_save,
|
|
|
|
);
|
|
|
|
merge_option(
|
|
|
|
&mut self.language_settings.enable_language_server,
|
2022-06-09 08:07:31 +00:00
|
|
|
data.enable_language_server,
|
|
|
|
);
|
2022-06-23 14:40:57 +00:00
|
|
|
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.language_settings.preferred_line_length,
|
2022-04-06 00:10:17 +00:00
|
|
|
data.editor.preferred_line_length,
|
|
|
|
);
|
|
|
|
|
2022-04-07 23:00:51 +00:00
|
|
|
for (language_name, settings) in data.language_overrides.clone().into_iter() {
|
2022-04-06 00:10:17 +00:00
|
|
|
let target = self
|
|
|
|
.language_overrides
|
2022-04-07 23:00:51 +00:00
|
|
|
.entry(language_name.into())
|
2022-04-06 00:10:17 +00:00
|
|
|
.or_default();
|
|
|
|
|
|
|
|
merge_option(&mut target.tab_size, settings.tab_size);
|
|
|
|
merge_option(&mut target.soft_wrap, settings.soft_wrap);
|
2022-04-28 21:31:06 +00:00
|
|
|
merge_option(&mut target.format_on_save, settings.format_on_save);
|
2022-06-09 08:07:31 +00:00
|
|
|
merge_option(
|
|
|
|
&mut target.enable_language_server,
|
|
|
|
settings.enable_language_server,
|
|
|
|
);
|
2022-04-06 00:10:17 +00:00
|
|
|
merge_option(
|
|
|
|
&mut target.preferred_line_length,
|
|
|
|
settings.preferred_line_length,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-21 20:33:39 +00:00
|
|
|
pub fn settings_file_json_schema(
|
|
|
|
theme_names: Vec<String>,
|
|
|
|
language_names: Vec<String>,
|
|
|
|
) -> serde_json::Value {
|
|
|
|
let settings = SchemaSettings::draft07().with(|settings| {
|
|
|
|
settings.option_add_null_type = false;
|
|
|
|
});
|
|
|
|
let generator = SchemaGenerator::new(settings);
|
|
|
|
let mut root_schema = generator.into_root_schema_for::<SettingsFileContent>();
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
|
|
|
|
enum_values: Some(theme_names),
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
root_schema
|
|
|
|
.definitions
|
|
|
|
.insert("ThemeName".to_owned(), theme_names_schema);
|
|
|
|
|
2022-06-23 14:40:57 +00:00
|
|
|
// Construct language settings reference type
|
|
|
|
let language_settings_schema_reference = Schema::Object(SchemaObject {
|
|
|
|
reference: Some("#/definitions/LanguageSettings".to_owned()),
|
2022-04-21 20:33:39 +00:00
|
|
|
..Default::default()
|
|
|
|
});
|
2022-06-23 14:40:57 +00:00
|
|
|
let language_settings_properties = language_names
|
2022-04-21 20:33:39 +00:00
|
|
|
.into_iter()
|
|
|
|
.map(|name| {
|
|
|
|
(
|
|
|
|
name,
|
|
|
|
Schema::Object(SchemaObject {
|
|
|
|
subschemas: Some(Box::new(SubschemaValidation {
|
2022-06-23 14:40:57 +00:00
|
|
|
all_of: Some(vec![language_settings_schema_reference.clone()]),
|
2022-04-21 20:33:39 +00:00
|
|
|
..Default::default()
|
|
|
|
})),
|
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
let language_overrides_schema = Schema::Object(SchemaObject {
|
|
|
|
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))),
|
|
|
|
object: Some(Box::new(ObjectValidation {
|
2022-06-23 14:40:57 +00:00
|
|
|
properties: language_settings_properties,
|
2022-04-21 20:33:39 +00:00
|
|
|
..Default::default()
|
|
|
|
})),
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
root_schema
|
|
|
|
.definitions
|
|
|
|
.insert("LanguageOverrides".to_owned(), language_overrides_schema);
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
}
|
|
|
|
|
2022-04-06 00:10:17 +00:00
|
|
|
fn merge<T: Copy>(target: &mut T, value: Option<T>) {
|
|
|
|
if let Some(value) = value {
|
|
|
|
*target = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn merge_option<T: Copy>(target: &mut Option<T>, value: Option<T>) {
|
|
|
|
if value.is_some() {
|
|
|
|
*target = value;
|
|
|
|
}
|
|
|
|
}
|
2022-04-21 18:58:18 +00:00
|
|
|
|
|
|
|
pub fn parse_json_with_comments<T: DeserializeOwned>(content: &str) -> Result<T> {
|
|
|
|
Ok(serde_json::from_reader(
|
|
|
|
json_comments::CommentSettings::c_style().strip_comments(content.as_bytes()),
|
|
|
|
)?)
|
|
|
|
}
|