settings.rs

  1pub mod keymap_file;
  2
  3use anyhow::Result;
  4use gpui::font_cache::{FamilyId, FontCache};
  5use schemars::{schema_for, JsonSchema};
  6use serde::Deserialize;
  7use std::{collections::HashMap, sync::Arc};
  8use theme::{Theme, ThemeRegistry};
  9use util::ResultExt as _;
 10
 11#[derive(Clone)]
 12pub struct Settings {
 13    pub buffer_font_family: FamilyId,
 14    pub buffer_font_size: f32,
 15    pub vim_mode: bool,
 16    pub tab_size: u32,
 17    pub soft_wrap: SoftWrap,
 18    pub preferred_line_length: u32,
 19    pub language_overrides: HashMap<Arc<str>, LanguageOverride>,
 20    pub theme: Arc<Theme>,
 21}
 22
 23#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
 24pub struct LanguageOverride {
 25    pub tab_size: Option<u32>,
 26    pub soft_wrap: Option<SoftWrap>,
 27    pub preferred_line_length: Option<u32>,
 28}
 29
 30#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
 31#[serde(rename_all = "snake_case")]
 32pub enum SoftWrap {
 33    None,
 34    EditorWidth,
 35    PreferredLineLength,
 36}
 37
 38#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
 39pub struct SettingsFileContent {
 40    #[serde(default)]
 41    pub buffer_font_family: Option<String>,
 42    #[serde(default)]
 43    pub buffer_font_size: Option<f32>,
 44    #[serde(default)]
 45    pub vim_mode: Option<bool>,
 46    #[serde(flatten)]
 47    pub editor: LanguageOverride,
 48    #[serde(default)]
 49    pub language_overrides: HashMap<Arc<str>, LanguageOverride>,
 50    #[serde(default)]
 51    pub theme: Option<String>,
 52}
 53
 54impl Settings {
 55    pub fn new(
 56        buffer_font_family: &str,
 57        font_cache: &FontCache,
 58        theme: Arc<Theme>,
 59    ) -> Result<Self> {
 60        Ok(Self {
 61            buffer_font_family: font_cache.load_family(&[buffer_font_family])?,
 62            buffer_font_size: 15.,
 63            vim_mode: false,
 64            tab_size: 4,
 65            soft_wrap: SoftWrap::None,
 66            preferred_line_length: 80,
 67            language_overrides: Default::default(),
 68            theme,
 69        })
 70    }
 71
 72    pub fn file_json_schema() -> serde_json::Value {
 73        serde_json::to_value(schema_for!(SettingsFileContent)).unwrap()
 74    }
 75
 76    pub fn with_overrides(
 77        mut self,
 78        language_name: impl Into<Arc<str>>,
 79        overrides: LanguageOverride,
 80    ) -> Self {
 81        self.language_overrides
 82            .insert(language_name.into(), overrides);
 83        self
 84    }
 85
 86    pub fn tab_size(&self, language: Option<&str>) -> u32 {
 87        language
 88            .and_then(|language| self.language_overrides.get(language))
 89            .and_then(|settings| settings.tab_size)
 90            .unwrap_or(self.tab_size)
 91    }
 92
 93    pub fn soft_wrap(&self, language: Option<&str>) -> SoftWrap {
 94        language
 95            .and_then(|language| self.language_overrides.get(language))
 96            .and_then(|settings| settings.soft_wrap)
 97            .unwrap_or(self.soft_wrap)
 98    }
 99
100    pub fn preferred_line_length(&self, language: Option<&str>) -> u32 {
101        language
102            .and_then(|language| self.language_overrides.get(language))
103            .and_then(|settings| settings.preferred_line_length)
104            .unwrap_or(self.preferred_line_length)
105    }
106
107    #[cfg(any(test, feature = "test-support"))]
108    pub fn test(cx: &gpui::AppContext) -> Settings {
109        Settings {
110            buffer_font_family: cx.font_cache().load_family(&["Monaco"]).unwrap(),
111            buffer_font_size: 14.,
112            vim_mode: false,
113            tab_size: 4,
114            soft_wrap: SoftWrap::None,
115            preferred_line_length: 80,
116            language_overrides: Default::default(),
117            theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), || Default::default()),
118        }
119    }
120
121    pub fn merge(
122        &mut self,
123        data: &SettingsFileContent,
124        theme_registry: &ThemeRegistry,
125        font_cache: &FontCache,
126    ) {
127        if let Some(value) = &data.buffer_font_family {
128            if let Some(id) = font_cache.load_family(&[value]).log_err() {
129                self.buffer_font_family = id;
130            }
131        }
132        if let Some(value) = &data.theme {
133            if let Some(theme) = theme_registry.get(value).log_err() {
134                self.theme = theme;
135            }
136        }
137
138        merge(&mut self.buffer_font_size, data.buffer_font_size);
139        merge(&mut self.vim_mode, data.vim_mode);
140        merge(&mut self.soft_wrap, data.editor.soft_wrap);
141        merge(&mut self.tab_size, data.editor.tab_size);
142        merge(
143            &mut self.preferred_line_length,
144            data.editor.preferred_line_length,
145        );
146
147        for (language_name, settings) in &data.language_overrides {
148            let target = self
149                .language_overrides
150                .entry(language_name.clone())
151                .or_default();
152
153            merge_option(&mut target.tab_size, settings.tab_size);
154            merge_option(&mut target.soft_wrap, settings.soft_wrap);
155            merge_option(
156                &mut target.preferred_line_length,
157                settings.preferred_line_length,
158            );
159        }
160    }
161}
162
163fn merge<T: Copy>(target: &mut T, value: Option<T>) {
164    if let Some(value) = value {
165        *target = value;
166    }
167}
168
169fn merge_option<T: Copy>(target: &mut Option<T>, value: Option<T>) {
170    if value.is_some() {
171        *target = value;
172    }
173}