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