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