Detailed changes
@@ -1,8 +1,8 @@
#[cfg(target_os = "macos")]
mod mac_watcher;
-#[cfg(any(target_os = "linux", target_os = "freebsd"))]
-pub mod linux_watcher;
+#[cfg(not(target_os = "macos"))]
+pub mod fs_watcher;
use anyhow::{anyhow, Result};
use git::GitHostingProviderRegistry;
@@ -700,7 +700,7 @@ impl Fs for RealFs {
)
}
- #[cfg(any(target_os = "linux", target_os = "freebsd"))]
+ #[cfg(not(target_os = "macos"))]
async fn watch(
&self,
path: &Path,
@@ -710,10 +710,11 @@ impl Fs for RealFs {
Arc<dyn Watcher>,
) {
use parking_lot::Mutex;
+ use util::paths::SanitizedPath;
let (tx, rx) = smol::channel::unbounded();
let pending_paths: Arc<Mutex<Vec<PathEvent>>> = Default::default();
- let watcher = Arc::new(linux_watcher::LinuxWatcher::new(tx, pending_paths.clone()));
+ let watcher = Arc::new(fs_watcher::FsWatcher::new(tx, pending_paths.clone()));
if watcher.add(path).is_err() {
// If the path doesn't exist yet (e.g. settings.json), watch the parent dir to learn when it's created.
@@ -731,7 +732,7 @@ impl Fs for RealFs {
if let Some(parent) = path.parent() {
target = parent.join(target);
if let Ok(canonical) = self.canonicalize(&target).await {
- target = canonical;
+ target = SanitizedPath::from(canonical).as_path().to_path_buf();
}
}
}
@@ -758,56 +759,6 @@ impl Fs for RealFs {
)
}
- #[cfg(target_os = "windows")]
- async fn watch(
- &self,
- path: &Path,
- _latency: Duration,
- ) -> (
- Pin<Box<dyn Send + Stream<Item = Vec<PathEvent>>>>,
- Arc<dyn Watcher>,
- ) {
- use notify::{EventKind, Watcher};
-
- let (tx, rx) = smol::channel::unbounded();
-
- let mut file_watcher = notify::recommended_watcher({
- let tx = tx.clone();
- move |event: Result<notify::Event, _>| {
- if let Some(event) = event.log_err() {
- let kind = match event.kind {
- EventKind::Create(_) => Some(PathEventKind::Created),
- EventKind::Modify(_) => Some(PathEventKind::Changed),
- EventKind::Remove(_) => Some(PathEventKind::Removed),
- _ => None,
- };
-
- tx.try_send(
- event
- .paths
- .into_iter()
- .map(|path| PathEvent { path, kind })
- .collect::<Vec<_>>(),
- )
- .ok();
- }
- }
- })
- .expect("Could not start file watcher");
-
- file_watcher
- .watch(path, notify::RecursiveMode::Recursive)
- .log_err();
-
- (
- Box::pin(rx.chain(futures::stream::once(async move {
- drop(file_watcher);
- vec![]
- }))),
- Arc::new(RealWatcher {}),
- )
- }
-
fn open_repo(&self, dotgit_path: &Path) -> Option<Arc<dyn GitRepository>> {
// with libgit2, we can open git repo from an existing work dir
// https://libgit2.org/docs/reference/main/repository/git_repository_open.html
@@ -5,12 +5,12 @@ use util::ResultExt;
use crate::{PathEvent, PathEventKind, Watcher};
-pub struct LinuxWatcher {
+pub struct FsWatcher {
tx: smol::channel::Sender<()>,
pending_path_events: Arc<Mutex<Vec<PathEvent>>>,
}
-impl LinuxWatcher {
+impl FsWatcher {
pub fn new(
tx: smol::channel::Sender<()>,
pending_path_events: Arc<Mutex<Vec<PathEvent>>>,
@@ -22,7 +22,7 @@ impl LinuxWatcher {
}
}
-impl Watcher for LinuxWatcher {
+impl Watcher for FsWatcher {
fn add(&self, path: &std::path::Path) -> gpui::Result<()> {
let root_path = path.to_path_buf();
@@ -69,7 +69,7 @@ impl Watcher for LinuxWatcher {
})?;
global(|g| {
- g.inotify
+ g.watcher
.lock()
.watch(path, notify::RecursiveMode::NonRecursive)
})??;
@@ -79,16 +79,18 @@ impl Watcher for LinuxWatcher {
fn remove(&self, path: &std::path::Path) -> gpui::Result<()> {
use notify::Watcher;
- Ok(global(|w| w.inotify.lock().unwatch(path))??)
+ Ok(global(|w| w.watcher.lock().unwatch(path))??)
}
}
pub struct GlobalWatcher {
- // two mutexes because calling inotify.add triggers an inotify.event, which needs watchers.
+ // two mutexes because calling watcher.add triggers an watcher.event, which needs watchers.
#[cfg(target_os = "linux")]
- pub(super) inotify: Mutex<notify::INotifyWatcher>,
+ pub(super) watcher: Mutex<notify::INotifyWatcher>,
#[cfg(target_os = "freebsd")]
- pub(super) inotify: Mutex<notify::KqueueWatcher>,
+ pub(super) watcher: Mutex<notify::KqueueWatcher>,
+ #[cfg(target_os = "windows")]
+ pub(super) watcher: Mutex<notify::ReadDirectoryChangesWatcher>,
pub(super) watchers: Mutex<Vec<Box<dyn Fn(¬ify::Event) + Send + Sync>>>,
}
@@ -98,7 +100,8 @@ impl GlobalWatcher {
}
}
-static INOTIFY_INSTANCE: OnceLock<anyhow::Result<GlobalWatcher, notify::Error>> = OnceLock::new();
+static FS_WATCHER_INSTANCE: OnceLock<anyhow::Result<GlobalWatcher, notify::Error>> =
+ OnceLock::new();
fn handle_event(event: Result<notify::Event, notify::Error>) {
let Some(event) = event.log_err() else { return };
@@ -111,9 +114,9 @@ fn handle_event(event: Result<notify::Event, notify::Error>) {
}
pub fn global<T>(f: impl FnOnce(&GlobalWatcher) -> T) -> anyhow::Result<T> {
- let result = INOTIFY_INSTANCE.get_or_init(|| {
+ let result = FS_WATCHER_INSTANCE.get_or_init(|| {
notify::recommended_watcher(handle_event).map(|file_watcher| GlobalWatcher {
- inotify: Mutex::new(file_watcher),
+ watcher: Mutex::new(file_watcher),
watchers: Default::default(),
})
});
@@ -854,8 +854,8 @@ async fn test_write_file(cx: &mut TestAppContext) {
.await
.unwrap();
- #[cfg(any(target_os = "linux", target_os = "freebsd"))]
- fs::linux_watcher::global(|_| {}).unwrap();
+ #[cfg(not(target_os = "macos"))]
+ fs::fs_watcher::global(|_| {}).unwrap();
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
.await;
@@ -153,8 +153,8 @@ pub fn initialize_workspace(
})
.detach();
- #[cfg(any(target_os = "linux", target_os = "freebsd"))]
- initialize_linux_file_watcher(cx);
+ #[cfg(not(target_os = "macos"))]
+ initialize_file_watcher(cx);
if let Some(specs) = cx.gpu_specs() {
log::info!("Using GPU: {:?}", specs);
@@ -235,8 +235,8 @@ fn feature_gate_zed_pro_actions(cx: &mut AppContext) {
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
-fn initialize_linux_file_watcher(cx: &mut ViewContext<Workspace>) {
- if let Err(e) = fs::linux_watcher::global(|_| {}) {
+fn initialize_file_watcher(cx: &mut ViewContext<Workspace>) {
+ if let Err(e) = fs::fs_watcher::global(|_| {}) {
let message = format!(
db::indoc! {r#"
inotify_init returned {}
@@ -264,6 +264,36 @@ fn initialize_linux_file_watcher(cx: &mut ViewContext<Workspace>) {
}
}
+#[cfg(target_os = "windows")]
+fn initialize_file_watcher(cx: &mut ViewContext<Workspace>) {
+ if let Err(e) = fs::fs_watcher::global(|_| {}) {
+ let message = format!(
+ db::indoc! {r#"
+ ReadDirectoryChangesW initialization failed: {}
+
+ This may occur on network filesystems and WSL paths. For troubleshooting see: https://zed.dev/docs/windows
+ "#},
+ e
+ );
+ let prompt = cx.prompt(
+ PromptLevel::Critical,
+ "Could not start ReadDirectoryChangesW",
+ Some(&message),
+ &["Troubleshoot and Quit"],
+ );
+ cx.spawn(|_, mut cx| async move {
+ if prompt.await == Ok(0) {
+ cx.update(|cx| {
+ cx.open_url("https://zed.dev/docs/windows");
+ cx.quit()
+ })
+ .ok();
+ }
+ })
+ .detach()
+ }
+}
+
fn show_software_emulation_warning_if_needed(
specs: gpui::GpuSpecs,
cx: &mut ViewContext<Workspace>,