language_settings.rs

  1use anyhow::Result;
  2use collections::HashMap;
  3use gpui::AppContext;
  4use schemars::JsonSchema;
  5use serde::{Deserialize, Serialize};
  6use std::{num::NonZeroU32, path::Path, sync::Arc};
  7
  8pub fn init(cx: &mut AppContext) {
  9    settings::register_setting::<AllLanguageSettings>(cx);
 10}
 11
 12pub fn language_settings<'a>(
 13    path: Option<&Path>,
 14    language: Option<&str>,
 15    cx: &'a AppContext,
 16) -> &'a LanguageSettings {
 17    settings::get_setting::<AllLanguageSettings>(path, cx).language(language)
 18}
 19
 20pub fn all_language_settings<'a>(
 21    path: Option<&Path>,
 22    cx: &'a AppContext,
 23) -> &'a AllLanguageSettings {
 24    settings::get_setting::<AllLanguageSettings>(path, cx)
 25}
 26
 27#[derive(Debug, Clone)]
 28pub struct AllLanguageSettings {
 29    pub copilot: CopilotSettings,
 30    defaults: LanguageSettings,
 31    languages: HashMap<Arc<str>, LanguageSettings>,
 32}
 33
 34#[derive(Debug, Clone, Deserialize)]
 35pub struct LanguageSettings {
 36    pub tab_size: NonZeroU32,
 37    pub hard_tabs: bool,
 38    pub soft_wrap: SoftWrap,
 39    pub preferred_line_length: u32,
 40    pub format_on_save: FormatOnSave,
 41    pub remove_trailing_whitespace_on_save: bool,
 42    pub ensure_final_newline_on_save: bool,
 43    pub formatter: Formatter,
 44    pub enable_language_server: bool,
 45    pub show_copilot_suggestions: bool,
 46    pub show_whitespaces: ShowWhitespaceSetting,
 47}
 48
 49#[derive(Clone, Debug, Default)]
 50pub struct CopilotSettings {
 51    pub feature_enabled: bool,
 52    pub disabled_globs: Vec<glob::Pattern>,
 53}
 54
 55#[derive(Clone, Serialize, Deserialize, JsonSchema)]
 56pub struct AllLanguageSettingsContent {
 57    #[serde(default)]
 58    pub features: Option<FeaturesContent>,
 59    #[serde(default)]
 60    pub copilot: Option<CopilotSettingsContent>,
 61    #[serde(flatten)]
 62    pub defaults: LanguageSettingsContent,
 63    #[serde(default, alias = "language_overrides")]
 64    pub languages: HashMap<Arc<str>, LanguageSettingsContent>,
 65}
 66
 67#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
 68pub struct LanguageSettingsContent {
 69    #[serde(default)]
 70    pub tab_size: Option<NonZeroU32>,
 71    #[serde(default)]
 72    pub hard_tabs: Option<bool>,
 73    #[serde(default)]
 74    pub soft_wrap: Option<SoftWrap>,
 75    #[serde(default)]
 76    pub preferred_line_length: Option<u32>,
 77    #[serde(default)]
 78    pub format_on_save: Option<FormatOnSave>,
 79    #[serde(default)]
 80    pub remove_trailing_whitespace_on_save: Option<bool>,
 81    #[serde(default)]
 82    pub ensure_final_newline_on_save: Option<bool>,
 83    #[serde(default)]
 84    pub formatter: Option<Formatter>,
 85    #[serde(default)]
 86    pub enable_language_server: Option<bool>,
 87    #[serde(default)]
 88    pub show_copilot_suggestions: Option<bool>,
 89    #[serde(default)]
 90    pub show_whitespaces: Option<ShowWhitespaceSetting>,
 91}
 92
 93#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
 94pub struct CopilotSettingsContent {
 95    #[serde(default)]
 96    pub disabled_globs: Option<Vec<String>>,
 97}
 98
 99#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
100#[serde(rename_all = "snake_case")]
101pub struct FeaturesContent {
102    pub copilot: Option<bool>,
103}
104
105#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
106#[serde(rename_all = "snake_case")]
107pub enum SoftWrap {
108    None,
109    EditorWidth,
110    PreferredLineLength,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
114#[serde(rename_all = "snake_case")]
115pub enum FormatOnSave {
116    On,
117    Off,
118    LanguageServer,
119    External {
120        command: Arc<str>,
121        arguments: Arc<[String]>,
122    },
123}
124
125#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
126#[serde(rename_all = "snake_case")]
127pub enum ShowWhitespaceSetting {
128    Selection,
129    None,
130    All,
131}
132
133#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
134#[serde(rename_all = "snake_case")]
135pub enum Formatter {
136    LanguageServer,
137    External {
138        command: Arc<str>,
139        arguments: Arc<[String]>,
140    },
141}
142
143impl AllLanguageSettings {
144    pub fn language<'a>(&'a self, language_name: Option<&str>) -> &'a LanguageSettings {
145        if let Some(name) = language_name {
146            if let Some(overrides) = self.languages.get(name) {
147                return overrides;
148            }
149        }
150        &self.defaults
151    }
152
153    pub fn copilot_enabled_for_path(&self, path: &Path) -> bool {
154        !self
155            .copilot
156            .disabled_globs
157            .iter()
158            .any(|glob| glob.matches_path(path))
159    }
160
161    pub fn copilot_enabled(&self, language_name: Option<&str>, path: Option<&Path>) -> bool {
162        if !self.copilot.feature_enabled {
163            return false;
164        }
165
166        if let Some(path) = path {
167            if !self.copilot_enabled_for_path(path) {
168                return false;
169            }
170        }
171
172        self.language(language_name).show_copilot_suggestions
173    }
174}
175
176impl settings::Setting for AllLanguageSettings {
177    const KEY: Option<&'static str> = None;
178
179    type FileContent = AllLanguageSettingsContent;
180
181    fn load(
182        default_value: &Self::FileContent,
183        user_settings: &[&Self::FileContent],
184        _: &AppContext,
185    ) -> Result<Self> {
186        // A default is provided for all settings.
187        let mut defaults: LanguageSettings =
188            serde_json::from_value(serde_json::to_value(&default_value.defaults)?)?;
189
190        let mut languages = HashMap::default();
191        for (language_name, settings) in &default_value.languages {
192            let mut language_settings = defaults.clone();
193            merge_settings(&mut language_settings, &settings);
194            languages.insert(language_name.clone(), language_settings);
195        }
196
197        let mut copilot_enabled = default_value
198            .features
199            .as_ref()
200            .and_then(|f| f.copilot)
201            .ok_or_else(Self::missing_default)?;
202        let mut copilot_globs = default_value
203            .copilot
204            .as_ref()
205            .and_then(|c| c.disabled_globs.as_ref())
206            .ok_or_else(Self::missing_default)?;
207
208        for user_settings in user_settings {
209            if let Some(copilot) = user_settings.features.as_ref().and_then(|f| f.copilot) {
210                copilot_enabled = copilot;
211            }
212            if let Some(globs) = user_settings
213                .copilot
214                .as_ref()
215                .and_then(|f| f.disabled_globs.as_ref())
216            {
217                copilot_globs = globs;
218            }
219
220            // A user's global settings override the default global settings and
221            // all default language-specific settings.
222            merge_settings(&mut defaults, &user_settings.defaults);
223            for language_settings in languages.values_mut() {
224                merge_settings(language_settings, &user_settings.defaults);
225            }
226
227            // A user's language-specific settings override default language-specific settings.
228            for (language_name, user_language_settings) in &user_settings.languages {
229                merge_settings(
230                    languages
231                        .entry(language_name.clone())
232                        .or_insert_with(|| defaults.clone()),
233                    &user_language_settings,
234                );
235            }
236        }
237
238        Ok(Self {
239            copilot: CopilotSettings {
240                feature_enabled: copilot_enabled,
241                disabled_globs: copilot_globs
242                    .iter()
243                    .filter_map(|pattern| glob::Pattern::new(pattern).ok())
244                    .collect(),
245            },
246            defaults,
247            languages,
248        })
249    }
250}
251
252fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent) {
253    merge(&mut settings.tab_size, src.tab_size);
254    merge(&mut settings.hard_tabs, src.hard_tabs);
255    merge(&mut settings.soft_wrap, src.soft_wrap);
256    merge(
257        &mut settings.preferred_line_length,
258        src.preferred_line_length,
259    );
260    merge(&mut settings.formatter, src.formatter.clone());
261    merge(&mut settings.format_on_save, src.format_on_save.clone());
262    merge(
263        &mut settings.remove_trailing_whitespace_on_save,
264        src.remove_trailing_whitespace_on_save,
265    );
266    merge(
267        &mut settings.ensure_final_newline_on_save,
268        src.ensure_final_newline_on_save,
269    );
270    merge(
271        &mut settings.enable_language_server,
272        src.enable_language_server,
273    );
274    merge(
275        &mut settings.show_copilot_suggestions,
276        src.show_copilot_suggestions,
277    );
278    merge(&mut settings.show_whitespaces, src.show_whitespaces);
279
280    fn merge<T>(target: &mut T, value: Option<T>) {
281        if let Some(value) = value {
282            *target = value;
283        }
284    }
285}