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