settings_file.rs

  1use crate::{Settings, settings_store::SettingsStore};
  2use collections::HashSet;
  3use fs::{Fs, PathEventKind};
  4use futures::{StreamExt, channel::mpsc};
  5use gpui::{App, BackgroundExecutor, ReadGlobal};
  6use std::{path::PathBuf, sync::Arc, time::Duration};
  7
  8pub const EMPTY_THEME_NAME: &str = "empty-theme";
  9
 10#[cfg(any(test, feature = "test-support"))]
 11pub fn test_settings() -> String {
 12    let mut value = crate::settings_store::parse_json_with_comments::<serde_json::Value>(
 13        crate::default_settings().as_ref(),
 14    )
 15    .unwrap();
 16    #[cfg(not(target_os = "windows"))]
 17    util::merge_non_null_json_value_into(
 18        serde_json::json!({
 19            "ui_font_family": "Courier",
 20            "ui_font_features": {},
 21            "ui_font_size": 14,
 22            "ui_font_fallback": [],
 23            "buffer_font_family": "Courier",
 24            "buffer_font_features": {},
 25            "buffer_font_size": 14,
 26            "buffer_font_fallback": [],
 27            "theme": EMPTY_THEME_NAME,
 28        }),
 29        &mut value,
 30    );
 31    #[cfg(target_os = "windows")]
 32    util::merge_non_null_json_value_into(
 33        serde_json::json!({
 34            "ui_font_family": "Courier New",
 35            "ui_font_features": {},
 36            "ui_font_size": 14,
 37            "ui_font_fallback": [],
 38            "buffer_font_family": "Courier New",
 39            "buffer_font_features": {},
 40            "buffer_font_size": 14,
 41            "buffer_font_fallback": [],
 42            "theme": EMPTY_THEME_NAME,
 43        }),
 44        &mut value,
 45    );
 46    value.as_object_mut().unwrap().remove("languages");
 47    serde_json::to_string(&value).unwrap()
 48}
 49
 50pub fn watch_config_file(
 51    executor: &BackgroundExecutor,
 52    fs: Arc<dyn Fs>,
 53    path: PathBuf,
 54) -> mpsc::UnboundedReceiver<String> {
 55    let (tx, rx) = mpsc::unbounded();
 56    executor
 57        .spawn(async move {
 58            let (events, _) = fs.watch(&path, Duration::from_millis(100)).await;
 59            futures::pin_mut!(events);
 60
 61            let contents = fs.load(&path).await.unwrap_or_default();
 62            if tx.unbounded_send(contents).is_err() {
 63                return;
 64            }
 65
 66            loop {
 67                if events.next().await.is_none() {
 68                    break;
 69                }
 70
 71                if let Ok(contents) = fs.load(&path).await {
 72                    if tx.unbounded_send(contents).is_err() {
 73                        break;
 74                    }
 75                }
 76            }
 77        })
 78        .detach();
 79    rx
 80}
 81
 82pub fn watch_config_dir(
 83    executor: &BackgroundExecutor,
 84    fs: Arc<dyn Fs>,
 85    dir_path: PathBuf,
 86    config_paths: HashSet<PathBuf>,
 87) -> mpsc::UnboundedReceiver<String> {
 88    let (tx, rx) = mpsc::unbounded();
 89    executor
 90        .spawn(async move {
 91            for file_path in &config_paths {
 92                if fs.metadata(file_path).await.is_ok_and(|v| v.is_some()) {
 93                    if let Ok(contents) = fs.load(file_path).await {
 94                        if tx.unbounded_send(contents).is_err() {
 95                            return;
 96                        }
 97                    }
 98                }
 99            }
100
101            let (events, _) = fs.watch(&dir_path, Duration::from_millis(100)).await;
102            futures::pin_mut!(events);
103
104            while let Some(event_batch) = events.next().await {
105                for event in event_batch {
106                    if config_paths.contains(&event.path) {
107                        match event.kind {
108                            Some(PathEventKind::Removed) => {
109                                if tx.unbounded_send(String::new()).is_err() {
110                                    return;
111                                }
112                            }
113                            Some(PathEventKind::Created) | Some(PathEventKind::Changed) => {
114                                if let Ok(contents) = fs.load(&event.path).await {
115                                    if tx.unbounded_send(contents).is_err() {
116                                        return;
117                                    }
118                                }
119                            }
120                            _ => {}
121                        }
122                    }
123                }
124            }
125        })
126        .detach();
127
128    rx
129}
130
131pub fn update_settings_file<T: Settings>(
132    fs: Arc<dyn Fs>,
133    cx: &App,
134    update: impl 'static + Send + FnOnce(&mut T::FileContent, &App),
135) {
136    SettingsStore::global(cx).update_settings_file::<T>(fs, update);
137}