@@ -2737,6 +2737,7 @@ dependencies = [
"lazy_static",
"libc",
"log",
+ "notify",
"parking_lot 0.11.2",
"regex",
"rope",
@@ -2756,7 +2757,7 @@ name = "fsevent"
version = "2.0.2"
dependencies = [
"bitflags 1.3.2",
- "fsevent-sys",
+ "fsevent-sys 3.1.0",
"parking_lot 0.11.2",
"tempfile",
]
@@ -2770,6 +2771,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "fsevent-sys"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
@@ -3509,6 +3519,26 @@ dependencies = [
"syn 2.0.48",
]
+[[package]]
+name = "inotify"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
+dependencies = [
+ "bitflags 1.3.2",
+ "inotify-sys",
+ "libc",
+]
+
+[[package]]
+name = "inotify-sys"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "install_cli"
version = "0.1.0"
@@ -3726,6 +3756,26 @@ dependencies = [
"winapi-build",
]
+[[package]]
+name = "kqueue"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c"
+dependencies = [
+ "kqueue-sys",
+ "libc",
+]
+
+[[package]]
+name = "kqueue-sys"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
+dependencies = [
+ "bitflags 1.3.2",
+ "libc",
+]
+
[[package]]
name = "kurbo"
version = "0.8.3"
@@ -4281,6 +4331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
dependencies = [
"libc",
+ "log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.48.0",
]
@@ -4534,6 +4585,25 @@ dependencies = [
"util",
]
+[[package]]
+name = "notify"
+version = "6.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
+dependencies = [
+ "bitflags 2.4.1",
+ "crossbeam-channel",
+ "filetime",
+ "fsevent-sys 4.1.0",
+ "inotify",
+ "kqueue",
+ "libc",
+ "log",
+ "mio 0.8.8",
+ "walkdir",
+ "windows-sys 0.48.0",
+]
+
[[package]]
name = "ntapi"
version = "0.3.7"
@@ -20,7 +20,6 @@ anyhow.workspace = true
async-trait.workspace = true
futures.workspace = true
tempfile = "3"
-fsevent = { path = "../fsevent" }
lazy_static.workspace = true
parking_lot.workspace = true
smol.workspace = true
@@ -35,6 +34,12 @@ time.workspace = true
gpui = { path = "../gpui", optional = true}
+[target.'cfg(target_os = "macos")'.dependencies]
+fsevent = { path = "../fsevent" }
+
+[target.'cfg(not(target_os = "macos"))'.dependencies]
+notify = "6.1.1"
+
[dev-dependencies]
gpui = { path = "../gpui", features = ["test-support"] }
@@ -1,7 +1,16 @@
pub mod repository;
use anyhow::{anyhow, Result};
+#[cfg(target_os = "macos")]
+pub use fsevent::Event;
+#[cfg(target_os = "macos")]
use fsevent::EventStream;
+
+#[cfg(not(target_os = "macos"))]
+pub use notify::Event;
+#[cfg(not(target_os = "macos"))]
+use notify::{Config, Watcher};
+
use futures::{future::BoxFuture, Stream, StreamExt};
use git2::Repository as LibGitRepository;
use parking_lot::Mutex;
@@ -48,11 +57,13 @@ pub trait Fs: Send + Sync {
&self,
path: &Path,
) -> Result<Pin<Box<dyn Send + Stream<Item = Result<PathBuf>>>>>;
+
async fn watch(
&self,
path: &Path,
latency: Duration,
- ) -> Pin<Box<dyn Send + Stream<Item = Vec<fsevent::Event>>>>;
+ ) -> Pin<Box<dyn Send + Stream<Item = Vec<Event>>>>;
+
fn open_repo(&self, abs_dot_git: &Path) -> Option<Arc<Mutex<dyn GitRepository>>>;
fn is_fake(&self) -> bool;
#[cfg(any(test, feature = "test-support"))]
@@ -251,11 +262,12 @@ impl Fs for RealFs {
Ok(Box::pin(result))
}
+ #[cfg(target_os = "macos")]
async fn watch(
&self,
path: &Path,
latency: Duration,
- ) -> Pin<Box<dyn Send + Stream<Item = Vec<fsevent::Event>>>> {
+ ) -> Pin<Box<dyn Send + Stream<Item = Vec<Event>>>> {
let (tx, rx) = smol::channel::unbounded();
let (stream, handle) = EventStream::new(&[path], latency);
std::thread::spawn(move || {
@@ -267,6 +279,35 @@ impl Fs for RealFs {
})))
}
+ #[cfg(not(target_os = "macos"))]
+ async fn watch(
+ &self,
+ path: &Path,
+ latency: Duration,
+ ) -> Pin<Box<dyn Send + Stream<Item = Vec<Event>>>> {
+ let (tx, rx) = smol::channel::unbounded();
+
+ let mut watcher = notify::recommended_watcher(move |res| match res {
+ Ok(event) => {
+ let _ = tx.try_send(vec![event]);
+ }
+ Err(err) => {
+ eprintln!("watch error: {:?}", err);
+ }
+ })
+ .unwrap();
+
+ watcher
+ .configure(Config::default().with_poll_interval(latency))
+ .unwrap();
+
+ watcher
+ .watch(path, notify::RecursiveMode::Recursive)
+ .unwrap();
+
+ Box::pin(rx)
+ }
+
fn open_repo(&self, dotgit_path: &Path) -> Option<Arc<Mutex<dyn GitRepository>>> {
LibGitRepository::open(&dotgit_path)
.log_err()
@@ -284,6 +325,20 @@ impl Fs for RealFs {
}
}
+#[cfg(target_os = "macos")]
+pub fn fs_events_paths(events: Vec<Event>) -> Vec<PathBuf> {
+ events.into_iter().map(|event| event.path).collect()
+}
+
+#[cfg(not(target_os = "macos"))]
+pub fn fs_events_paths(events: Vec<Event>) -> Vec<PathBuf> {
+ events
+ .into_iter()
+ .map(|event| event.paths.into_iter())
+ .flatten()
+ .collect()
+}
+
#[cfg(any(test, feature = "test-support"))]
pub struct FakeFs {
// Use an unfair lock to ensure tests are deterministic.
@@ -3221,10 +3221,7 @@ impl BackgroundScanner {
}
}
- async fn run(
- &mut self,
- mut fs_events_rx: Pin<Box<dyn Send + Stream<Item = Vec<fsevent::Event>>>>,
- ) {
+ async fn run(&mut self, mut fs_events_rx: Pin<Box<dyn Send + Stream<Item = Vec<fs::Event>>>>) {
use futures::FutureExt as _;
// Populate ignores above the root.
@@ -3271,9 +3268,10 @@ impl BackgroundScanner {
// have the previous state loaded yet.
self.phase = BackgroundScannerPhase::EventsReceivedDuringInitialScan;
if let Poll::Ready(Some(events)) = futures::poll!(fs_events_rx.next()) {
- let mut paths = events.into_iter().map(|e| e.path).collect::<Vec<_>>();
+ let mut paths = fs::fs_events_paths(events);
+
while let Poll::Ready(Some(more_events)) = futures::poll!(fs_events_rx.next()) {
- paths.extend(more_events.into_iter().map(|e| e.path));
+ paths.extend(fs::fs_events_paths(more_events));
}
self.process_events(paths).await;
}
@@ -3312,9 +3310,10 @@ impl BackgroundScanner {
events = fs_events_rx.next().fuse() => {
let Some(events) = events else { break };
- let mut paths = events.into_iter().map(|e| e.path).collect::<Vec<_>>();
+ let mut paths = fs::fs_events_paths(events);
+
while let Poll::Ready(Some(more_events)) = futures::poll!(fs_events_rx.next()) {
- paths.extend(more_events.into_iter().map(|e| e.path));
+ paths.extend(fs::fs_events_paths(more_events));
}
self.process_events(paths.clone()).await;
}