use fs::Fs; use futures::StreamExt; use gpui::{executor, MutableAppContext}; use postage::sink::Sink as _; use postage::{prelude::Stream, watch}; use serde::Deserialize; use std::{path::Path, sync::Arc, time::Duration}; use theme::ThemeRegistry; use util::ResultExt; use crate::{parse_json_with_comments, KeymapFileContent, Settings, SettingsFileContent}; #[derive(Clone)] pub struct WatchedJsonFile(pub watch::Receiver); impl WatchedJsonFile where T: 'static + for<'de> Deserialize<'de> + Clone + Default + Send + Sync, { pub async fn new( fs: Arc, executor: &executor::Background, path: impl Into>, ) -> Self { let path = path.into(); let settings = Self::load(fs.clone(), &path).await.unwrap_or_default(); let mut events = fs.watch(&path, Duration::from_millis(500)).await; let (mut tx, rx) = watch::channel_with(settings); executor .spawn(async move { while events.next().await.is_some() { if let Some(settings) = Self::load(fs.clone(), &path).await { if tx.send(settings).await.is_err() { break; } } } }) .detach(); Self(rx) } ///Loads the given watched JSON file. In the special case that the file is ///empty (ignoring whitespace) or is not a file, this will return T::default() async fn load(fs: Arc, path: &Path) -> Option { if !fs.is_file(path).await { return Some(T::default()); } fs.load(path).await.log_err().and_then(|data| { if data.trim().is_empty() { Some(T::default()) } else { parse_json_with_comments(&data).log_err() } }) } pub fn current(&self) -> T { self.0.borrow().clone() } } pub fn watch_settings_file( defaults: Settings, mut file: WatchedJsonFile, theme_registry: Arc, cx: &mut MutableAppContext, ) { settings_updated(&defaults, file.0.borrow().clone(), &theme_registry, cx); cx.spawn(|mut cx| async move { while let Some(content) = file.0.recv().await { cx.update(|cx| settings_updated(&defaults, content, &theme_registry, cx)); } }) .detach(); } pub fn keymap_updated(content: KeymapFileContent, cx: &mut MutableAppContext) { cx.clear_bindings(); KeymapFileContent::load_defaults(cx); content.add_to_cx(cx).log_err(); } pub fn settings_updated( defaults: &Settings, content: SettingsFileContent, theme_registry: &Arc, cx: &mut MutableAppContext, ) { let mut settings = defaults.clone(); settings.set_user_settings(content, theme_registry, cx.font_cache()); cx.set_global(settings); cx.refresh_windows(); } pub fn watch_keymap_file(mut file: WatchedJsonFile, cx: &mut MutableAppContext) { cx.spawn(|mut cx| async move { while let Some(content) = file.0.recv().await { cx.update(|cx| keymap_updated(content, cx)); } }) .detach(); }