1use anyhow::Result;
2use collections::{btree_map, hash_map, BTreeMap, HashMap};
3use gpui::AppContext;
4use lazy_static::lazy_static;
5use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema};
6use serde::{de::DeserializeOwned, Deserialize as _, Serialize};
7use smallvec::SmallVec;
8use std::{
9 any::{type_name, Any, TypeId},
10 fmt::Debug,
11 ops::Range,
12 path::Path,
13 str,
14 sync::Arc,
15};
16use util::{merge_non_null_json_value_into, RangeExt, ResultExt as _};
17
18/// A value that can be defined as a user setting.
19///
20/// Settings can be loaded from a combination of multiple JSON files.
21pub trait Setting: 'static {
22 /// The name of a key within the JSON file from which this setting should
23 /// be deserialized. If this is `None`, then the setting will be deserialized
24 /// from the root object.
25 const KEY: Option<&'static str>;
26
27 /// The type that is stored in an individual JSON file.
28 type FileContent: Clone + Serialize + DeserializeOwned + JsonSchema;
29
30 /// The logic for combining together values from one or more JSON files into the
31 /// final value for this setting.
32 ///
33 /// The user values are ordered from least specific (the global settings file)
34 /// to most specific (the innermost local settings file).
35 fn load(
36 default_value: &Self::FileContent,
37 user_values: &[&Self::FileContent],
38 cx: &AppContext,
39 ) -> Result<Self>
40 where
41 Self: Sized;
42
43 fn json_schema(generator: &mut SchemaGenerator, _: &SettingsJsonSchemaParams) -> RootSchema {
44 generator.root_schema_for::<Self::FileContent>()
45 }
46
47 fn json_merge(
48 default_value: &Self::FileContent,
49 user_values: &[&Self::FileContent],
50 ) -> Result<Self::FileContent> {
51 let mut merged = serde_json::Value::Null;
52 for value in [default_value].iter().chain(user_values) {
53 merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged);
54 }
55 Ok(serde_json::from_value(merged)?)
56 }
57
58 fn load_via_json_merge(
59 default_value: &Self::FileContent,
60 user_values: &[&Self::FileContent],
61 ) -> Result<Self>
62 where
63 Self: DeserializeOwned,
64 {
65 let mut merged = serde_json::Value::Null;
66 for value in [default_value].iter().chain(user_values) {
67 merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged);
68 }
69 Ok(serde_json::from_value(merged)?)
70 }
71
72 fn missing_default() -> anyhow::Error {
73 anyhow::anyhow!("missing default")
74 }
75}
76
77pub struct SettingsJsonSchemaParams<'a> {
78 pub theme_names: &'a [String],
79 pub language_names: &'a [String],
80}
81
82/// A set of strongly-typed setting values defined via multiple JSON files.
83#[derive(Default)]
84pub struct SettingsStore {
85 setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>,
86 default_deserialized_settings: Option<serde_json::Value>,
87 user_deserialized_settings: Option<serde_json::Value>,
88 local_deserialized_settings: BTreeMap<Arc<Path>, serde_json::Value>,
89 tab_size_callback: Option<(TypeId, Box<dyn Fn(&dyn Any) -> Option<usize>>)>,
90}
91
92#[derive(Debug)]
93struct SettingValue<T> {
94 global_value: Option<T>,
95 local_values: Vec<(Arc<Path>, T)>,
96}
97
98trait AnySettingValue {
99 fn key(&self) -> Option<&'static str>;
100 fn setting_type_name(&self) -> &'static str;
101 fn deserialize_setting(&self, json: &serde_json::Value) -> Result<DeserializedSetting>;
102 fn load_setting(
103 &self,
104 default_value: &DeserializedSetting,
105 custom: &[DeserializedSetting],
106 cx: &AppContext,
107 ) -> Result<Box<dyn Any>>;
108 fn value_for_path(&self, path: Option<&Path>) -> &dyn Any;
109 fn set_global_value(&mut self, value: Box<dyn Any>);
110 fn set_local_value(&mut self, path: Arc<Path>, value: Box<dyn Any>);
111 fn json_schema(
112 &self,
113 generator: &mut SchemaGenerator,
114 _: &SettingsJsonSchemaParams,
115 ) -> RootSchema;
116}
117
118struct DeserializedSetting(Box<dyn Any>);
119
120impl SettingsStore {
121 /// Add a new type of setting to the store.
122 pub fn register_setting<T: Setting>(&mut self, cx: &AppContext) {
123 let setting_type_id = TypeId::of::<T>();
124 let entry = self.setting_values.entry(setting_type_id);
125 if matches!(entry, hash_map::Entry::Occupied(_)) {
126 return;
127 }
128
129 let setting_value = entry.or_insert(Box::new(SettingValue::<T> {
130 global_value: None,
131 local_values: Vec::new(),
132 }));
133
134 if let Some(default_settings) = &self.default_deserialized_settings {
135 if let Some(default_settings) = setting_value
136 .deserialize_setting(default_settings)
137 .log_err()
138 {
139 let mut user_values_stack = Vec::new();
140
141 if let Some(user_settings) = &self.user_deserialized_settings {
142 if let Some(user_settings) =
143 setting_value.deserialize_setting(user_settings).log_err()
144 {
145 user_values_stack = vec![user_settings];
146 }
147 }
148
149 if let Some(setting) = setting_value
150 .load_setting(&default_settings, &user_values_stack, cx)
151 .log_err()
152 {
153 setting_value.set_global_value(setting);
154 }
155 }
156 }
157 }
158
159 /// Get the value of a setting.
160 ///
161 /// Panics if the given setting type has not been registered, or if there is no
162 /// value for this setting.
163 pub fn get<T: Setting>(&self, path: Option<&Path>) -> &T {
164 self.setting_values
165 .get(&TypeId::of::<T>())
166 .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))
167 .value_for_path(path)
168 .downcast_ref::<T>()
169 .expect("no default value for setting type")
170 }
171
172 /// Get the user's settings as a raw JSON value.
173 ///
174 /// This is only for debugging and reporting. For user-facing functionality,
175 /// use the typed setting interface.
176 pub fn untyped_user_settings(&self) -> &serde_json::Value {
177 self.user_deserialized_settings
178 .as_ref()
179 .unwrap_or(&serde_json::Value::Null)
180 }
181
182 #[cfg(any(test, feature = "test-support"))]
183 pub fn test(cx: &AppContext) -> Self {
184 let mut this = Self::default();
185 this.set_default_settings(&crate::test_settings(), cx)
186 .unwrap();
187 this.set_user_settings("{}", cx).unwrap();
188 this
189 }
190
191 /// Update the value of a setting in the user's global configuration.
192 ///
193 /// This is only for tests. Normally, settings are only loaded from
194 /// JSON files.
195 #[cfg(any(test, feature = "test-support"))]
196 pub fn update_user_settings<T: Setting>(
197 &mut self,
198 cx: &AppContext,
199 update: impl FnOnce(&mut T::FileContent),
200 ) {
201 if self.user_deserialized_settings.is_none() {
202 self.set_user_settings("{}", cx).unwrap();
203 }
204 let old_text =
205 serde_json::to_string(self.user_deserialized_settings.as_ref().unwrap()).unwrap();
206 let new_text = self.new_text_for_update::<T>(old_text, update);
207 self.set_user_settings(&new_text, cx).unwrap();
208 }
209
210 /// Update the value of a setting in a JSON file, returning the new text
211 /// for that JSON file.
212 pub fn new_text_for_update<T: Setting>(
213 &self,
214 old_text: String,
215 update: impl FnOnce(&mut T::FileContent),
216 ) -> String {
217 let edits = self.edits_for_update::<T>(&old_text, update);
218 let mut new_text = old_text;
219 for (range, replacement) in edits.into_iter() {
220 new_text.replace_range(range, &replacement);
221 }
222 new_text
223 }
224
225 /// Update the value of a setting in a JSON file, returning a list
226 /// of edits to apply to the JSON file.
227 pub fn edits_for_update<T: Setting>(
228 &self,
229 text: &str,
230 update: impl FnOnce(&mut T::FileContent),
231 ) -> Vec<(Range<usize>, String)> {
232 let setting_type_id = TypeId::of::<T>();
233
234 let old_content = self
235 .setting_values
236 .get(&setting_type_id)
237 .unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))
238 .deserialize_setting(
239 self.user_deserialized_settings
240 .as_ref()
241 .expect("no user settings loaded"),
242 )
243 .unwrap_or_else(|e| {
244 panic!(
245 "could not deserialize setting type {} from user settings: {}",
246 type_name::<T>(),
247 e
248 )
249 })
250 .0
251 .downcast::<T::FileContent>()
252 .unwrap();
253 let mut new_content = old_content.clone();
254 update(&mut new_content);
255
256 let old_value = &serde_json::to_value(&old_content).unwrap();
257 let new_value = serde_json::to_value(new_content).unwrap();
258
259 let mut key_path = Vec::new();
260 if let Some(key) = T::KEY {
261 key_path.push(key);
262 }
263
264 let mut edits = Vec::new();
265 let tab_size = self.json_tab_size();
266 let mut text = text.to_string();
267 update_value_in_json_text(
268 &mut text,
269 &mut key_path,
270 tab_size,
271 &old_value,
272 &new_value,
273 &mut edits,
274 );
275 return edits;
276 }
277
278 /// Configure the tab sized when updating JSON files.
279 pub fn set_json_tab_size_callback<T: Setting>(
280 &mut self,
281 get_tab_size: fn(&T) -> Option<usize>,
282 ) {
283 self.tab_size_callback = Some((
284 TypeId::of::<T>(),
285 Box::new(move |value| get_tab_size(value.downcast_ref::<T>().unwrap())),
286 ));
287 }
288
289 fn json_tab_size(&self) -> usize {
290 const DEFAULT_JSON_TAB_SIZE: usize = 2;
291
292 if let Some((setting_type_id, callback)) = &self.tab_size_callback {
293 let setting_value = self.setting_values.get(setting_type_id).unwrap();
294 let value = setting_value.value_for_path(None);
295 if let Some(value) = callback(value) {
296 return value;
297 }
298 }
299
300 DEFAULT_JSON_TAB_SIZE
301 }
302
303 /// Set the default settings via a JSON string.
304 ///
305 /// The string should contain a JSON object with a default value for every setting.
306 pub fn set_default_settings(
307 &mut self,
308 default_settings_content: &str,
309 cx: &AppContext,
310 ) -> Result<()> {
311 self.default_deserialized_settings = Some(serde_json::from_str(default_settings_content)?);
312 self.recompute_values(None, cx)?;
313 Ok(())
314 }
315
316 /// Set the user settings via a JSON string.
317 pub fn set_user_settings(
318 &mut self,
319 user_settings_content: &str,
320 cx: &AppContext,
321 ) -> Result<()> {
322 self.user_deserialized_settings = Some(serde_json::from_str(user_settings_content)?);
323 self.recompute_values(None, cx)?;
324 Ok(())
325 }
326
327 /// Add or remove a set of local settings via a JSON string.
328 pub fn set_local_settings(
329 &mut self,
330 path: Arc<Path>,
331 settings_content: Option<&str>,
332 cx: &AppContext,
333 ) -> Result<()> {
334 if let Some(content) = settings_content {
335 self.local_deserialized_settings
336 .insert(path.clone(), serde_json::from_str(content)?);
337 } else {
338 self.local_deserialized_settings.remove(&path);
339 }
340 self.recompute_values(Some(&path), cx)?;
341 Ok(())
342 }
343
344 pub fn json_schema(&self, schema_params: &SettingsJsonSchemaParams) -> serde_json::Value {
345 use schemars::{
346 gen::SchemaSettings,
347 schema::{Schema, SchemaObject},
348 };
349
350 let settings = SchemaSettings::draft07().with(|settings| {
351 settings.option_add_null_type = false;
352 });
353 let mut generator = SchemaGenerator::new(settings);
354 let mut combined_schema = RootSchema::default();
355
356 for setting_value in self.setting_values.values() {
357 let setting_schema = setting_value.json_schema(&mut generator, schema_params);
358 combined_schema
359 .definitions
360 .extend(setting_schema.definitions);
361
362 let target_schema = if let Some(key) = setting_value.key() {
363 let key_schema = combined_schema
364 .schema
365 .object()
366 .properties
367 .entry(key.to_string())
368 .or_insert_with(|| Schema::Object(SchemaObject::default()));
369 if let Schema::Object(key_schema) = key_schema {
370 key_schema
371 } else {
372 continue;
373 }
374 } else {
375 &mut combined_schema.schema
376 };
377
378 merge_schema(target_schema, setting_schema.schema);
379 }
380
381 fn merge_schema(target: &mut SchemaObject, source: SchemaObject) {
382 if let Some(source) = source.object {
383 let target_properties = &mut target.object().properties;
384 for (key, value) in source.properties {
385 match target_properties.entry(key) {
386 btree_map::Entry::Vacant(e) => {
387 e.insert(value);
388 }
389 btree_map::Entry::Occupied(e) => {
390 if let (Schema::Object(target), Schema::Object(src)) =
391 (e.into_mut(), value)
392 {
393 merge_schema(target, src);
394 }
395 }
396 }
397 }
398 }
399
400 overwrite(&mut target.instance_type, source.instance_type);
401 overwrite(&mut target.string, source.string);
402 overwrite(&mut target.number, source.number);
403 overwrite(&mut target.reference, source.reference);
404 overwrite(&mut target.array, source.array);
405 overwrite(&mut target.enum_values, source.enum_values);
406
407 fn overwrite<T>(target: &mut Option<T>, source: Option<T>) {
408 if let Some(source) = source {
409 *target = Some(source);
410 }
411 }
412 }
413
414 serde_json::to_value(&combined_schema).unwrap()
415 }
416
417 fn recompute_values(
418 &mut self,
419 changed_local_path: Option<&Path>,
420 cx: &AppContext,
421 ) -> Result<()> {
422 // Reload the global and local values for every setting.
423 let mut user_settings_stack = Vec::<DeserializedSetting>::new();
424 let mut paths_stack = Vec::<Option<&Path>>::new();
425 for setting_value in self.setting_values.values_mut() {
426 if let Some(default_settings) = &self.default_deserialized_settings {
427 let default_settings = setting_value.deserialize_setting(default_settings)?;
428
429 user_settings_stack.clear();
430 paths_stack.clear();
431
432 if let Some(user_settings) = &self.user_deserialized_settings {
433 if let Some(user_settings) =
434 setting_value.deserialize_setting(user_settings).log_err()
435 {
436 user_settings_stack.push(user_settings);
437 paths_stack.push(None);
438 }
439 }
440
441 // If the global settings file changed, reload the global value for the field.
442 if changed_local_path.is_none() {
443 setting_value.set_global_value(setting_value.load_setting(
444 &default_settings,
445 &user_settings_stack,
446 cx,
447 )?);
448 }
449
450 // Reload the local values for the setting.
451 for (path, local_settings) in &self.local_deserialized_settings {
452 // Build a stack of all of the local values for that setting.
453 while let Some(prev_path) = paths_stack.last() {
454 if let Some(prev_path) = prev_path {
455 if !path.starts_with(prev_path) {
456 paths_stack.pop();
457 user_settings_stack.pop();
458 continue;
459 }
460 }
461 break;
462 }
463
464 if let Some(local_settings) =
465 setting_value.deserialize_setting(&local_settings).log_err()
466 {
467 paths_stack.push(Some(path.as_ref()));
468 user_settings_stack.push(local_settings);
469
470 // If a local settings file changed, then avoid recomputing local
471 // settings for any path outside of that directory.
472 if changed_local_path.map_or(false, |changed_local_path| {
473 !path.starts_with(changed_local_path)
474 }) {
475 continue;
476 }
477
478 setting_value.set_local_value(
479 path.clone(),
480 setting_value.load_setting(
481 &default_settings,
482 &user_settings_stack,
483 cx,
484 )?,
485 );
486 }
487 }
488 }
489 }
490 Ok(())
491 }
492}
493
494impl<T: Setting> AnySettingValue for SettingValue<T> {
495 fn key(&self) -> Option<&'static str> {
496 T::KEY
497 }
498
499 fn setting_type_name(&self) -> &'static str {
500 type_name::<T>()
501 }
502
503 fn load_setting(
504 &self,
505 default_value: &DeserializedSetting,
506 user_values: &[DeserializedSetting],
507 cx: &AppContext,
508 ) -> Result<Box<dyn Any>> {
509 let default_value = default_value.0.downcast_ref::<T::FileContent>().unwrap();
510 let values: SmallVec<[&T::FileContent; 6]> = user_values
511 .iter()
512 .map(|value| value.0.downcast_ref().unwrap())
513 .collect();
514 Ok(Box::new(T::load(default_value, &values, cx)?))
515 }
516
517 fn deserialize_setting(&self, mut json: &serde_json::Value) -> Result<DeserializedSetting> {
518 if let Some(key) = T::KEY {
519 json = json.get(key).unwrap_or(&serde_json::Value::Null);
520 }
521 let value = T::FileContent::deserialize(json)?;
522 Ok(DeserializedSetting(Box::new(value)))
523 }
524
525 fn value_for_path(&self, path: Option<&Path>) -> &dyn Any {
526 if let Some(path) = path {
527 for (settings_path, value) in self.local_values.iter().rev() {
528 if path.starts_with(&settings_path) {
529 return value;
530 }
531 }
532 }
533 self.global_value
534 .as_ref()
535 .unwrap_or_else(|| panic!("no default value for setting {}", self.setting_type_name()))
536 }
537
538 fn set_global_value(&mut self, value: Box<dyn Any>) {
539 self.global_value = Some(*value.downcast().unwrap());
540 }
541
542 fn set_local_value(&mut self, path: Arc<Path>, value: Box<dyn Any>) {
543 let value = *value.downcast().unwrap();
544 match self.local_values.binary_search_by_key(&&path, |e| &e.0) {
545 Ok(ix) => self.local_values[ix].1 = value,
546 Err(ix) => self.local_values.insert(ix, (path, value)),
547 }
548 }
549
550 fn json_schema(
551 &self,
552 generator: &mut SchemaGenerator,
553 params: &SettingsJsonSchemaParams,
554 ) -> RootSchema {
555 T::json_schema(generator, params)
556 }
557}
558
559// impl Debug for SettingsStore {
560// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
561// return f
562// .debug_struct("SettingsStore")
563// .field(
564// "setting_value_sets_by_type",
565// &self
566// .setting_values
567// .values()
568// .map(|set| (set.setting_type_name(), set))
569// .collect::<HashMap<_, _>>(),
570// )
571// .finish_non_exhaustive();
572// }
573// }
574
575fn update_value_in_json_text<'a>(
576 text: &mut String,
577 key_path: &mut Vec<&'a str>,
578 tab_size: usize,
579 old_value: &'a serde_json::Value,
580 new_value: &'a serde_json::Value,
581 edits: &mut Vec<(Range<usize>, String)>,
582) {
583 // If the old and new values are both objects, then compare them key by key,
584 // preserving the comments and formatting of the unchanged parts. Otherwise,
585 // replace the old value with the new value.
586 if let (serde_json::Value::Object(old_object), serde_json::Value::Object(new_object)) =
587 (old_value, new_value)
588 {
589 for (key, old_sub_value) in old_object.iter() {
590 key_path.push(key);
591 let new_sub_value = new_object.get(key).unwrap_or(&serde_json::Value::Null);
592 update_value_in_json_text(
593 text,
594 key_path,
595 tab_size,
596 old_sub_value,
597 new_sub_value,
598 edits,
599 );
600 key_path.pop();
601 }
602 for (key, new_sub_value) in new_object.iter() {
603 key_path.push(key);
604 if !old_object.contains_key(key) {
605 update_value_in_json_text(
606 text,
607 key_path,
608 tab_size,
609 &serde_json::Value::Null,
610 new_sub_value,
611 edits,
612 );
613 }
614 key_path.pop();
615 }
616 } else if old_value != new_value {
617 let (range, replacement) =
618 replace_value_in_json_text(text, &key_path, tab_size, &new_value);
619 text.replace_range(range.clone(), &replacement);
620 edits.push((range, replacement));
621 }
622}
623
624fn replace_value_in_json_text(
625 text: &str,
626 key_path: &[&str],
627 tab_size: usize,
628 new_value: impl Serialize,
629) -> (Range<usize>, String) {
630 const LANGUAGE_OVERRIDES: &'static str = "language_overrides";
631 const LANGUAGES: &'static str = "languages";
632
633 lazy_static! {
634 static ref PAIR_QUERY: tree_sitter::Query = tree_sitter::Query::new(
635 tree_sitter_json::language(),
636 "(pair key: (string) @key value: (_) @value)",
637 )
638 .unwrap();
639 }
640
641 let mut parser = tree_sitter::Parser::new();
642 parser.set_language(tree_sitter_json::language()).unwrap();
643 let syntax_tree = parser.parse(text, None).unwrap();
644
645 let mut cursor = tree_sitter::QueryCursor::new();
646
647 let has_language_overrides = text.contains(LANGUAGE_OVERRIDES);
648
649 let mut depth = 0;
650 let mut last_value_range = 0..0;
651 let mut first_key_start = None;
652 let mut existing_value_range = 0..text.len();
653 let matches = cursor.matches(&PAIR_QUERY, syntax_tree.root_node(), text.as_bytes());
654 for mat in matches {
655 if mat.captures.len() != 2 {
656 continue;
657 }
658
659 let key_range = mat.captures[0].node.byte_range();
660 let value_range = mat.captures[1].node.byte_range();
661
662 // Don't enter sub objects until we find an exact
663 // match for the current keypath
664 if last_value_range.contains_inclusive(&value_range) {
665 continue;
666 }
667
668 last_value_range = value_range.clone();
669
670 if key_range.start > existing_value_range.end {
671 break;
672 }
673
674 first_key_start.get_or_insert_with(|| key_range.start);
675
676 let found_key = text
677 .get(key_range.clone())
678 .map(|key_text| {
679 if key_path[depth] == LANGUAGES && has_language_overrides {
680 return key_text == format!("\"{}\"", LANGUAGE_OVERRIDES);
681 } else {
682 return key_text == format!("\"{}\"", key_path[depth]);
683 }
684 })
685 .unwrap_or(false);
686
687 if found_key {
688 existing_value_range = value_range;
689 // Reset last value range when increasing in depth
690 last_value_range = existing_value_range.start..existing_value_range.start;
691 depth += 1;
692
693 if depth == key_path.len() {
694 break;
695 } else {
696 first_key_start = None;
697 }
698 }
699 }
700
701 // We found the exact key we want, insert the new value
702 if depth == key_path.len() {
703 let new_val = to_pretty_json(&new_value, tab_size, tab_size * depth);
704 (existing_value_range, new_val)
705 } else {
706 // We have key paths, construct the sub objects
707 let new_key = if has_language_overrides && key_path[depth] == LANGUAGES {
708 LANGUAGE_OVERRIDES
709 } else {
710 key_path[depth]
711 };
712
713 // We don't have the key, construct the nested objects
714 let mut new_value = serde_json::to_value(new_value).unwrap();
715 for key in key_path[(depth + 1)..].iter().rev() {
716 if has_language_overrides && key == &LANGUAGES {
717 new_value = serde_json::json!({ LANGUAGE_OVERRIDES.to_string(): new_value });
718 } else {
719 new_value = serde_json::json!({ key.to_string(): new_value });
720 }
721 }
722
723 if let Some(first_key_start) = first_key_start {
724 let mut row = 0;
725 let mut column = 0;
726 for (ix, char) in text.char_indices() {
727 if ix == first_key_start {
728 break;
729 }
730 if char == '\n' {
731 row += 1;
732 column = 0;
733 } else {
734 column += char.len_utf8();
735 }
736 }
737
738 if row > 0 {
739 // depth is 0 based, but division needs to be 1 based.
740 let new_val = to_pretty_json(&new_value, column / (depth + 1), column);
741 let space = ' ';
742 let content = format!("\"{new_key}\": {new_val},\n{space:width$}", width = column);
743 (first_key_start..first_key_start, content)
744 } else {
745 let new_val = serde_json::to_string(&new_value).unwrap();
746 let mut content = format!(r#""{new_key}": {new_val},"#);
747 content.push(' ');
748 (first_key_start..first_key_start, content)
749 }
750 } else {
751 new_value = serde_json::json!({ new_key.to_string(): new_value });
752 let indent_prefix_len = 4 * depth;
753 let mut new_val = to_pretty_json(&new_value, 4, indent_prefix_len);
754 if depth == 0 {
755 new_val.push('\n');
756 }
757
758 (existing_value_range, new_val)
759 }
760 }
761}
762
763fn to_pretty_json(value: &impl Serialize, indent_size: usize, indent_prefix_len: usize) -> String {
764 const SPACES: [u8; 32] = [b' '; 32];
765
766 debug_assert!(indent_size <= SPACES.len());
767 debug_assert!(indent_prefix_len <= SPACES.len());
768
769 let mut output = Vec::new();
770 let mut ser = serde_json::Serializer::with_formatter(
771 &mut output,
772 serde_json::ser::PrettyFormatter::with_indent(&SPACES[0..indent_size.min(SPACES.len())]),
773 );
774
775 value.serialize(&mut ser).unwrap();
776 let text = String::from_utf8(output).unwrap();
777
778 let mut adjusted_text = String::new();
779 for (i, line) in text.split('\n').enumerate() {
780 if i > 0 {
781 adjusted_text.push_str(str::from_utf8(&SPACES[0..indent_prefix_len]).unwrap());
782 }
783 adjusted_text.push_str(line);
784 adjusted_text.push('\n');
785 }
786 adjusted_text.pop();
787 adjusted_text
788}
789
790pub fn parse_json_with_comments<T: DeserializeOwned>(content: &str) -> Result<T> {
791 Ok(serde_json::from_reader(
792 json_comments::CommentSettings::c_style().strip_comments(content.as_bytes()),
793 )?)
794}
795
796#[cfg(test)]
797mod tests {
798 use super::*;
799 use serde_derive::Deserialize;
800 use unindent::Unindent;
801
802 #[gpui::test]
803 fn test_settings_store_basic(cx: &mut AppContext) {
804 let mut store = SettingsStore::default();
805 store.register_setting::<UserSettings>(cx);
806 store.register_setting::<TurboSetting>(cx);
807 store.register_setting::<MultiKeySettings>(cx);
808
809 // error - missing required field in default settings
810 store
811 .set_default_settings(
812 r#"{
813 "user": {
814 "name": "John Doe",
815 "age": 30,
816 "staff": false
817 }
818 }"#,
819 cx,
820 )
821 .unwrap_err();
822
823 // error - type error in default settings
824 store
825 .set_default_settings(
826 r#"{
827 "turbo": "the-wrong-type",
828 "user": {
829 "name": "John Doe",
830 "age": 30,
831 "staff": false
832 }
833 }"#,
834 cx,
835 )
836 .unwrap_err();
837
838 // valid default settings.
839 store
840 .set_default_settings(
841 r#"{
842 "turbo": false,
843 "user": {
844 "name": "John Doe",
845 "age": 30,
846 "staff": false
847 }
848 }"#,
849 cx,
850 )
851 .unwrap();
852
853 assert_eq!(store.get::<TurboSetting>(None), &TurboSetting(false));
854 assert_eq!(
855 store.get::<UserSettings>(None),
856 &UserSettings {
857 name: "John Doe".to_string(),
858 age: 30,
859 staff: false,
860 }
861 );
862 assert_eq!(
863 store.get::<MultiKeySettings>(None),
864 &MultiKeySettings {
865 key1: String::new(),
866 key2: String::new(),
867 }
868 );
869
870 store
871 .set_user_settings(
872 r#"{
873 "turbo": true,
874 "user": { "age": 31 },
875 "key1": "a"
876 }"#,
877 cx,
878 )
879 .unwrap();
880
881 assert_eq!(store.get::<TurboSetting>(None), &TurboSetting(true));
882 assert_eq!(
883 store.get::<UserSettings>(None),
884 &UserSettings {
885 name: "John Doe".to_string(),
886 age: 31,
887 staff: false
888 }
889 );
890
891 store
892 .set_local_settings(
893 Path::new("/root1").into(),
894 Some(r#"{ "user": { "staff": true } }"#),
895 cx,
896 )
897 .unwrap();
898 store
899 .set_local_settings(
900 Path::new("/root1/subdir").into(),
901 Some(r#"{ "user": { "name": "Jane Doe" } }"#),
902 cx,
903 )
904 .unwrap();
905
906 store
907 .set_local_settings(
908 Path::new("/root2").into(),
909 Some(r#"{ "user": { "age": 42 }, "key2": "b" }"#),
910 cx,
911 )
912 .unwrap();
913
914 assert_eq!(
915 store.get::<UserSettings>(Some(Path::new("/root1/something"))),
916 &UserSettings {
917 name: "John Doe".to_string(),
918 age: 31,
919 staff: true
920 }
921 );
922 assert_eq!(
923 store.get::<UserSettings>(Some(Path::new("/root1/subdir/something"))),
924 &UserSettings {
925 name: "Jane Doe".to_string(),
926 age: 31,
927 staff: true
928 }
929 );
930 assert_eq!(
931 store.get::<UserSettings>(Some(Path::new("/root2/something"))),
932 &UserSettings {
933 name: "John Doe".to_string(),
934 age: 42,
935 staff: false
936 }
937 );
938 assert_eq!(
939 store.get::<MultiKeySettings>(Some(Path::new("/root2/something"))),
940 &MultiKeySettings {
941 key1: "a".to_string(),
942 key2: "b".to_string(),
943 }
944 );
945 }
946
947 #[gpui::test]
948 fn test_setting_store_assign_json_before_register(cx: &mut AppContext) {
949 let mut store = SettingsStore::default();
950 store
951 .set_default_settings(
952 r#"{
953 "turbo": true,
954 "user": {
955 "name": "John Doe",
956 "age": 30,
957 "staff": false
958 },
959 "key1": "x"
960 }"#,
961 cx,
962 )
963 .unwrap();
964 store
965 .set_user_settings(r#"{ "turbo": false }"#, cx)
966 .unwrap();
967 store.register_setting::<UserSettings>(cx);
968 store.register_setting::<TurboSetting>(cx);
969
970 assert_eq!(store.get::<TurboSetting>(None), &TurboSetting(false));
971 assert_eq!(
972 store.get::<UserSettings>(None),
973 &UserSettings {
974 name: "John Doe".to_string(),
975 age: 30,
976 staff: false,
977 }
978 );
979
980 store.register_setting::<MultiKeySettings>(cx);
981 assert_eq!(
982 store.get::<MultiKeySettings>(None),
983 &MultiKeySettings {
984 key1: "x".into(),
985 key2: String::new(),
986 }
987 );
988 }
989
990 #[gpui::test]
991 fn test_setting_store_update(cx: &mut AppContext) {
992 let mut store = SettingsStore::default();
993 store.register_setting::<MultiKeySettings>(cx);
994 store.register_setting::<UserSettings>(cx);
995 store.register_setting::<LanguageSettings>(cx);
996
997 // entries added and updated
998 check_settings_update::<LanguageSettings>(
999 &mut store,
1000 r#"{
1001 "languages": {
1002 "JSON": {
1003 "is_enabled": true
1004 }
1005 }
1006 }"#
1007 .unindent(),
1008 |settings| {
1009 settings.languages.get_mut("JSON").unwrap().is_enabled = false;
1010 settings
1011 .languages
1012 .insert("Rust".into(), LanguageSettingEntry { is_enabled: true });
1013 },
1014 r#"{
1015 "languages": {
1016 "Rust": {
1017 "is_enabled": true
1018 },
1019 "JSON": {
1020 "is_enabled": false
1021 }
1022 }
1023 }"#
1024 .unindent(),
1025 cx,
1026 );
1027
1028 // weird formatting
1029 check_settings_update::<UserSettings>(
1030 &mut store,
1031 r#"{
1032 "user": { "age": 36, "name": "Max", "staff": true }
1033 }"#
1034 .unindent(),
1035 |settings| settings.age = Some(37),
1036 r#"{
1037 "user": { "age": 37, "name": "Max", "staff": true }
1038 }"#
1039 .unindent(),
1040 cx,
1041 );
1042
1043 // single-line formatting, other keys
1044 check_settings_update::<MultiKeySettings>(
1045 &mut store,
1046 r#"{ "one": 1, "two": 2 }"#.unindent(),
1047 |settings| settings.key1 = Some("x".into()),
1048 r#"{ "key1": "x", "one": 1, "two": 2 }"#.unindent(),
1049 cx,
1050 );
1051
1052 // empty object
1053 check_settings_update::<UserSettings>(
1054 &mut store,
1055 r#"{
1056 "user": {}
1057 }"#
1058 .unindent(),
1059 |settings| settings.age = Some(37),
1060 r#"{
1061 "user": {
1062 "age": 37
1063 }
1064 }"#
1065 .unindent(),
1066 cx,
1067 );
1068
1069 // no content
1070 check_settings_update::<UserSettings>(
1071 &mut store,
1072 r#""#.unindent(),
1073 |settings| settings.age = Some(37),
1074 r#"{
1075 "user": {
1076 "age": 37
1077 }
1078 }
1079 "#
1080 .unindent(),
1081 cx,
1082 );
1083 }
1084
1085 fn check_settings_update<T: Setting>(
1086 store: &mut SettingsStore,
1087 old_json: String,
1088 update: fn(&mut T::FileContent),
1089 expected_new_json: String,
1090 cx: &mut AppContext,
1091 ) {
1092 store.set_user_settings(&old_json, cx).ok();
1093 let edits = store.edits_for_update::<T>(&old_json, update);
1094 let mut new_json = old_json;
1095 for (range, replacement) in edits.into_iter() {
1096 new_json.replace_range(range, &replacement);
1097 }
1098 pretty_assertions::assert_eq!(new_json, expected_new_json);
1099 }
1100
1101 #[derive(Debug, PartialEq, Deserialize)]
1102 struct UserSettings {
1103 name: String,
1104 age: u32,
1105 staff: bool,
1106 }
1107
1108 #[derive(Clone, Serialize, Deserialize, JsonSchema)]
1109 struct UserSettingsJson {
1110 name: Option<String>,
1111 age: Option<u32>,
1112 staff: Option<bool>,
1113 }
1114
1115 impl Setting for UserSettings {
1116 const KEY: Option<&'static str> = Some("user");
1117 type FileContent = UserSettingsJson;
1118
1119 fn load(
1120 default_value: &UserSettingsJson,
1121 user_values: &[&UserSettingsJson],
1122 _: &AppContext,
1123 ) -> Result<Self> {
1124 Self::load_via_json_merge(default_value, user_values)
1125 }
1126 }
1127
1128 #[derive(Debug, Deserialize, PartialEq)]
1129 struct TurboSetting(bool);
1130
1131 impl Setting for TurboSetting {
1132 const KEY: Option<&'static str> = Some("turbo");
1133 type FileContent = Option<bool>;
1134
1135 fn load(
1136 default_value: &Option<bool>,
1137 user_values: &[&Option<bool>],
1138 _: &AppContext,
1139 ) -> Result<Self> {
1140 Self::load_via_json_merge(default_value, user_values)
1141 }
1142 }
1143
1144 #[derive(Clone, Debug, PartialEq, Deserialize)]
1145 struct MultiKeySettings {
1146 #[serde(default)]
1147 key1: String,
1148 #[serde(default)]
1149 key2: String,
1150 }
1151
1152 #[derive(Clone, Serialize, Deserialize, JsonSchema)]
1153 struct MultiKeySettingsJson {
1154 key1: Option<String>,
1155 key2: Option<String>,
1156 }
1157
1158 impl Setting for MultiKeySettings {
1159 const KEY: Option<&'static str> = None;
1160
1161 type FileContent = MultiKeySettingsJson;
1162
1163 fn load(
1164 default_value: &MultiKeySettingsJson,
1165 user_values: &[&MultiKeySettingsJson],
1166 _: &AppContext,
1167 ) -> Result<Self> {
1168 Self::load_via_json_merge(default_value, user_values)
1169 }
1170 }
1171
1172 #[derive(Debug, Deserialize)]
1173 struct JournalSettings {
1174 pub path: String,
1175 pub hour_format: HourFormat,
1176 }
1177
1178 #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1179 #[serde(rename_all = "snake_case")]
1180 enum HourFormat {
1181 Hour12,
1182 Hour24,
1183 }
1184
1185 #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1186 struct JournalSettingsJson {
1187 pub path: Option<String>,
1188 pub hour_format: Option<HourFormat>,
1189 }
1190
1191 impl Setting for JournalSettings {
1192 const KEY: Option<&'static str> = Some("journal");
1193
1194 type FileContent = JournalSettingsJson;
1195
1196 fn load(
1197 default_value: &JournalSettingsJson,
1198 user_values: &[&JournalSettingsJson],
1199 _: &AppContext,
1200 ) -> Result<Self> {
1201 Self::load_via_json_merge(default_value, user_values)
1202 }
1203 }
1204
1205 #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1206 struct LanguageSettings {
1207 #[serde(default)]
1208 languages: HashMap<String, LanguageSettingEntry>,
1209 }
1210
1211 #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1212 struct LanguageSettingEntry {
1213 is_enabled: bool,
1214 }
1215
1216 impl Setting for LanguageSettings {
1217 const KEY: Option<&'static str> = None;
1218
1219 type FileContent = Self;
1220
1221 fn load(default_value: &Self, user_values: &[&Self], _: &AppContext) -> Result<Self> {
1222 Self::load_via_json_merge(default_value, user_values)
1223 }
1224 }
1225}