1use crate::{update_settings_file, watched_json::WatchedJsonFile, SettingsFileContent};
2use anyhow::Result;
3use fs::Fs;
4use gpui::MutableAppContext;
5use std::{path::Path, sync::Arc};
6
7// TODO: Switch SettingsFile to open a worktree and buffer for synchronization
8// And instant updates in the Zed editor
9#[derive(Clone)]
10pub struct SettingsFile {
11 path: &'static Path,
12 settings_file_content: WatchedJsonFile<SettingsFileContent>,
13 fs: Arc<dyn Fs>,
14}
15
16impl SettingsFile {
17 pub fn new(
18 path: &'static Path,
19 settings_file_content: WatchedJsonFile<SettingsFileContent>,
20 fs: Arc<dyn Fs>,
21 ) -> Self {
22 SettingsFile {
23 path,
24 settings_file_content,
25 fs,
26 }
27 }
28
29 pub fn update(
30 cx: &mut MutableAppContext,
31 update: impl 'static + Send + FnOnce(&mut SettingsFileContent),
32 ) {
33 let this = cx.global::<SettingsFile>();
34
35 let current_file_content = this.settings_file_content.current();
36
37 let fs = this.fs.clone();
38 let path = this.path.clone();
39
40 cx.background()
41 .spawn(async move {
42 let old_text = fs.load(path).await?;
43
44 let new_text = update_settings_file(old_text, current_file_content, update);
45
46 fs.atomic_write(path.to_path_buf(), new_text).await?;
47
48 Ok(()) as Result<()>
49 })
50 .detach_and_log_err(cx);
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57 use crate::{watched_json::watch_settings_file, EditorSettings, Settings, SoftWrap};
58 use fs::FakeFs;
59 use theme::ThemeRegistry;
60
61 #[gpui::test]
62 async fn test_watch_settings_files(cx: &mut gpui::TestAppContext) {
63 let executor = cx.background();
64 let fs = FakeFs::new(executor.clone());
65 let font_cache = cx.font_cache();
66
67 fs.save(
68 "/settings.json".as_ref(),
69 &r#"
70 {
71 "buffer_font_size": 24,
72 "soft_wrap": "editor_width",
73 "tab_size": 8,
74 "language_overrides": {
75 "Markdown": {
76 "tab_size": 2,
77 "preferred_line_length": 100,
78 "soft_wrap": "preferred_line_length"
79 }
80 }
81 }
82 "#
83 .into(),
84 Default::default(),
85 )
86 .await
87 .unwrap();
88
89 let source = WatchedJsonFile::new(fs.clone(), &executor, "/settings.json".as_ref()).await;
90
91 let default_settings = cx.read(Settings::test).with_language_defaults(
92 "JavaScript",
93 EditorSettings {
94 tab_size: Some(2.try_into().unwrap()),
95 ..Default::default()
96 },
97 );
98 cx.update(|cx| {
99 watch_settings_file(
100 default_settings.clone(),
101 source,
102 ThemeRegistry::new((), font_cache),
103 cx,
104 )
105 });
106
107 cx.foreground().run_until_parked();
108 let settings = cx.read(|cx| cx.global::<Settings>().clone());
109 assert_eq!(settings.buffer_font_size, 24.0);
110
111 assert_eq!(settings.soft_wrap(None), SoftWrap::EditorWidth);
112 assert_eq!(
113 settings.soft_wrap(Some("Markdown")),
114 SoftWrap::PreferredLineLength
115 );
116 assert_eq!(
117 settings.soft_wrap(Some("JavaScript")),
118 SoftWrap::EditorWidth
119 );
120
121 assert_eq!(settings.preferred_line_length(None), 80);
122 assert_eq!(settings.preferred_line_length(Some("Markdown")), 100);
123 assert_eq!(settings.preferred_line_length(Some("JavaScript")), 80);
124
125 assert_eq!(settings.tab_size(None).get(), 8);
126 assert_eq!(settings.tab_size(Some("Markdown")).get(), 2);
127 assert_eq!(settings.tab_size(Some("JavaScript")).get(), 8);
128
129 fs.save(
130 "/settings.json".as_ref(),
131 &"(garbage)".into(),
132 Default::default(),
133 )
134 .await
135 .unwrap();
136 // fs.remove_file("/settings.json".as_ref(), Default::default())
137 // .await
138 // .unwrap();
139
140 cx.foreground().run_until_parked();
141 let settings = cx.read(|cx| cx.global::<Settings>().clone());
142 assert_eq!(settings.buffer_font_size, 24.0);
143
144 assert_eq!(settings.soft_wrap(None), SoftWrap::EditorWidth);
145 assert_eq!(
146 settings.soft_wrap(Some("Markdown")),
147 SoftWrap::PreferredLineLength
148 );
149 assert_eq!(
150 settings.soft_wrap(Some("JavaScript")),
151 SoftWrap::EditorWidth
152 );
153
154 assert_eq!(settings.preferred_line_length(None), 80);
155 assert_eq!(settings.preferred_line_length(Some("Markdown")), 100);
156 assert_eq!(settings.preferred_line_length(Some("JavaScript")), 80);
157
158 assert_eq!(settings.tab_size(None).get(), 8);
159 assert_eq!(settings.tab_size(Some("Markdown")).get(), 2);
160 assert_eq!(settings.tab_size(Some("JavaScript")).get(), 8);
161
162 fs.remove_file("/settings.json".as_ref(), Default::default())
163 .await
164 .unwrap();
165 cx.foreground().run_until_parked();
166 let settings = cx.read(|cx| cx.global::<Settings>().clone());
167 assert_eq!(settings.buffer_font_size, default_settings.buffer_font_size);
168 }
169}