Merge pull request #1598 from gitbutlerapp/add-notify-debouncer-full-dependency

Add notify debouncer
This commit is contained in:
Nikita Galaiko 2023-11-14 11:43:44 +01:00 committed by GitHub
commit 6b418a6199
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 89 additions and 70 deletions

24
Cargo.lock generated
View File

@ -1517,6 +1517,15 @@ dependencies = [
"rustc_version",
]
[[package]]
name = "file-id"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6584280525fb2059cba3db2c04abf947a1a29a45ddae89f3870f8281704fafc9"
dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "filetime"
version = "0.2.22"
@ -1933,6 +1942,7 @@ dependencies = [
"itertools",
"md5",
"notify",
"notify-debouncer-full",
"num_cpus",
"once_cell",
"pretty_assertions",
@ -3077,6 +3087,20 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "notify-debouncer-full"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f5dab59c348b9b50cf7f261960a20e389feb2713636399cd9082cd4b536154"
dependencies = [
"crossbeam-channel",
"file-id",
"log",
"notify",
"parking_lot",
"walkdir",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"

View File

@ -35,6 +35,7 @@ git2 = { version = "0.18.1", features = ["vendored-openssl", "vendored-libgit2"]
itertools = "0.11"
md5 = "0.7.0"
notify = { version = "6.0.1" }
notify-debouncer-full = "0.3.1"
num_cpus = "1.16.0"
r2d2 = "0.8.10"
r2d2_sqlite = "0.22.0"

View File

@ -1,11 +1,13 @@
use std::{
path,
sync::{Arc, Mutex},
time::Duration,
};
use anyhow::{Context, Result};
use futures::executor::block_on;
use notify::{Config, RecommendedWatcher, Watcher};
use notify::{RecommendedWatcher, Watcher};
use notify_debouncer_full::{new_debouncer, Debouncer, FileIdMap};
use tokio::{
sync::mpsc::{channel, Receiver},
task,
@ -15,7 +17,7 @@ use crate::{git, projects::ProjectId, watcher::events};
#[derive(Debug, Clone)]
pub struct Dispatcher {
watcher: Arc<Mutex<Option<RecommendedWatcher>>>,
watcher: Arc<Mutex<Option<Debouncer<RecommendedWatcher, FileIdMap>>>>,
}
impl Dispatcher {
@ -33,88 +35,80 @@ impl Dispatcher {
let repo = git::Repository::open(path)
.with_context(|| format!("failed to open project repository: {}", path.display()))?;
let (notify_tx, mut notify_rx) = channel(1);
let mut watcher = RecommendedWatcher::new(
{
move |res: notify::Result<notify::Event>| match res {
Ok(event) => {
if !is_interesting_kind(event.kind) {
return;
}
for path in event
.paths
.into_iter()
.filter(|file| is_interesting_file(&repo, file))
{
block_on(async {
if let Err(error) = notify_tx.send(path).await {
tracing::error!(?error, "failed to send file change event",);
}
});
}
}
Err(error) => tracing::error!(?error, "file watcher error"),
}
},
Config::default(),
)?;
let (notify_tx, notify_rx) = std::sync::mpsc::channel();
let mut debouncer = new_debouncer(Duration::from_millis(100), None, notify_tx)?;
watcher
.watch(std::path::Path::new(path), notify::RecursiveMode::Recursive)
.with_context(|| format!("failed to watch project path: {}", path.display()))?;
self.watcher.lock().unwrap().replace(watcher);
debouncer
.watcher()
.watch(path, notify::RecursiveMode::Recursive)?;
debouncer
.cache()
.add_root(path, notify::RecursiveMode::Recursive);
self.watcher.lock().unwrap().replace(debouncer);
tracing::debug!(%project_id, "file watcher started");
let (tx, rx) = channel(1);
task::Builder::new()
.name(&format!("{} file watcher", project_id))
.spawn({
.spawn_blocking({
let path = path.to_path_buf();
let project_id = *project_id;
async move {
while let Some(file_path) = notify_rx.recv().await {
match file_path.strip_prefix(&path) {
Ok(relative_file_path) => {
let event = if relative_file_path.starts_with(".git") {
tracing::info!(
%project_id,
file_path = %relative_file_path.display(),
"git file change",
);
events::Event::GitFileChange(
project_id,
relative_file_path
.strip_prefix(".git")
.unwrap()
.to_path_buf(),
)
} else {
tracing::info!(
%project_id,
file_path = %relative_file_path.display(),
"project file change",
);
events::Event::ProjectFileChange(
project_id,
relative_file_path.to_path_buf(),
)
};
if let Err(error) = tx.send(event).await {
tracing::error!(
%project_id,
?error,
"failed to send file change event",
);
move || {
for result in notify_rx {
match result {
Err(errors) => {
tracing::error!(?errors, "file watcher error");
}
Ok(events) => {
let file_paths = events.into_iter().filter(|event| is_interesting_kind(event.kind)).flat_map(|event| event.paths.clone()).filter(|file| is_interesting_file(&repo, file));
for file_path in file_paths {
match file_path.strip_prefix(&path) {
Ok(relative_file_path) => {
let event = if relative_file_path.starts_with(".git") {
tracing::info!(
%project_id,
file_path = %relative_file_path.display(),
"git file change",
);
events::Event::GitFileChange(
project_id,
relative_file_path
.strip_prefix(".git")
.unwrap()
.to_path_buf(),
)
} else {
tracing::info!(
%project_id,
file_path = %relative_file_path.display(),
"project file change",
);
events::Event::ProjectFileChange(
project_id,
relative_file_path.to_path_buf(),
)
};
if let Err(error) = block_on(tx.send(event)) {
tracing::error!(
%project_id,
?error,
"failed to send file change event",
);
}
}
Err(error) => {
tracing::error!(%project_id, ?error, "failed to strip prefix");
}
}
}
Err(error) => {
tracing::error!(%project_id, ?error, "failed to strip prefix");
}
}
}
}
}
tracing::debug!(%project_id, "file watcher stopped");
}
})?;
Ok(rx)