mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-26 18:42:31 +03:00
Introduce cross-platform file-watching (#6855)
This adds cross-platform file-watching via the [Notify](https://github.com/notify-rs/notify) crate. The previous fs-events implementation is now only used on MacOS, and on other platforms Notify is used. The watching function interface is the same. Related to #5391 #5395 #5394. Release Notes: - N/A
This commit is contained in:
parent
b29f45ea68
commit
1313402a6b
72
Cargo.lock
generated
72
Cargo.lock
generated
@ -2737,6 +2737,7 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
"notify",
|
||||||
"parking_lot 0.11.2",
|
"parking_lot 0.11.2",
|
||||||
"regex",
|
"regex",
|
||||||
"rope",
|
"rope",
|
||||||
@ -2756,7 +2757,7 @@ name = "fsevent"
|
|||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"fsevent-sys",
|
"fsevent-sys 3.1.0",
|
||||||
"parking_lot 0.11.2",
|
"parking_lot 0.11.2",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
@ -2770,6 +2771,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fsevent-sys"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fuchsia-zircon"
|
name = "fuchsia-zircon"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@ -3509,6 +3519,26 @@ dependencies = [
|
|||||||
"syn 2.0.48",
|
"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]]
|
[[package]]
|
||||||
name = "install_cli"
|
name = "install_cli"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -3726,6 +3756,26 @@ dependencies = [
|
|||||||
"winapi-build",
|
"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]]
|
[[package]]
|
||||||
name = "kurbo"
|
name = "kurbo"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
@ -4281,6 +4331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
|
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
|
"log",
|
||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
@ -4534,6 +4585,25 @@ dependencies = [
|
|||||||
"util",
|
"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]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
|
@ -20,7 +20,6 @@ anyhow.workspace = true
|
|||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
fsevent = { path = "../fsevent" }
|
|
||||||
lazy_static.workspace = true
|
lazy_static.workspace = true
|
||||||
parking_lot.workspace = true
|
parking_lot.workspace = true
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
@ -35,6 +34,12 @@ time.workspace = true
|
|||||||
|
|
||||||
gpui = { path = "../gpui", optional = 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]
|
[dev-dependencies]
|
||||||
gpui = { path = "../gpui", features = ["test-support"] }
|
gpui = { path = "../gpui", features = ["test-support"] }
|
||||||
|
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
pub mod repository;
|
pub mod repository;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub use fsevent::Event;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
use fsevent::EventStream;
|
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 futures::{future::BoxFuture, Stream, StreamExt};
|
||||||
use git2::Repository as LibGitRepository;
|
use git2::Repository as LibGitRepository;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
@ -48,11 +57,13 @@ pub trait Fs: Send + Sync {
|
|||||||
&self,
|
&self,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Result<Pin<Box<dyn Send + Stream<Item = Result<PathBuf>>>>>;
|
) -> Result<Pin<Box<dyn Send + Stream<Item = Result<PathBuf>>>>>;
|
||||||
|
|
||||||
async fn watch(
|
async fn watch(
|
||||||
&self,
|
&self,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
latency: Duration,
|
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 open_repo(&self, abs_dot_git: &Path) -> Option<Arc<Mutex<dyn GitRepository>>>;
|
||||||
fn is_fake(&self) -> bool;
|
fn is_fake(&self) -> bool;
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
@ -251,11 +262,12 @@ impl Fs for RealFs {
|
|||||||
Ok(Box::pin(result))
|
Ok(Box::pin(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
async fn watch(
|
async fn watch(
|
||||||
&self,
|
&self,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
latency: Duration,
|
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 (tx, rx) = smol::channel::unbounded();
|
||||||
let (stream, handle) = EventStream::new(&[path], latency);
|
let (stream, handle) = EventStream::new(&[path], latency);
|
||||||
std::thread::spawn(move || {
|
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>>> {
|
fn open_repo(&self, dotgit_path: &Path) -> Option<Arc<Mutex<dyn GitRepository>>> {
|
||||||
LibGitRepository::open(&dotgit_path)
|
LibGitRepository::open(&dotgit_path)
|
||||||
.log_err()
|
.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"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub struct FakeFs {
|
pub struct FakeFs {
|
||||||
// Use an unfair lock to ensure tests are deterministic.
|
// Use an unfair lock to ensure tests are deterministic.
|
||||||
|
@ -3221,10 +3221,7 @@ impl BackgroundScanner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&mut self, mut fs_events_rx: Pin<Box<dyn Send + Stream<Item = Vec<fs::Event>>>>) {
|
||||||
&mut self,
|
|
||||||
mut fs_events_rx: Pin<Box<dyn Send + Stream<Item = Vec<fsevent::Event>>>>,
|
|
||||||
) {
|
|
||||||
use futures::FutureExt as _;
|
use futures::FutureExt as _;
|
||||||
|
|
||||||
// Populate ignores above the root.
|
// Populate ignores above the root.
|
||||||
@ -3271,9 +3268,10 @@ impl BackgroundScanner {
|
|||||||
// have the previous state loaded yet.
|
// have the previous state loaded yet.
|
||||||
self.phase = BackgroundScannerPhase::EventsReceivedDuringInitialScan;
|
self.phase = BackgroundScannerPhase::EventsReceivedDuringInitialScan;
|
||||||
if let Poll::Ready(Some(events)) = futures::poll!(fs_events_rx.next()) {
|
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()) {
|
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;
|
self.process_events(paths).await;
|
||||||
}
|
}
|
||||||
@ -3312,9 +3310,10 @@ impl BackgroundScanner {
|
|||||||
|
|
||||||
events = fs_events_rx.next().fuse() => {
|
events = fs_events_rx.next().fuse() => {
|
||||||
let Some(events) = events else { break };
|
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()) {
|
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;
|
self.process_events(paths.clone()).await;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user