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