From 23258d8436ab8cfda74a3ac5dc1a30ec63bfb6e6 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 6 Jul 2020 09:28:17 +0200 Subject: Simplify file watcher --- crates/vfs-notify/src/lib.rs | 43 ++++++++++++++----------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) (limited to 'crates/vfs-notify') diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index b1ea298ae..ee5035554 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -10,10 +10,9 @@ mod include; use std::convert::{TryFrom, TryInto}; -use crossbeam_channel::{select, unbounded, Receiver, Sender}; +use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; use notify::{RecommendedWatcher, RecursiveMode, Watcher}; use paths::{AbsPath, AbsPathBuf}; -use rustc_hash::FxHashSet; use vfs::loader; use walkdir::WalkDir; @@ -55,10 +54,8 @@ type NotifyEvent = notify::Result; struct NotifyActor { sender: loader::Sender, config: Vec<(AbsPathBuf, Include, bool)>, - watched_paths: FxHashSet, - // Drop order of fields bellow is significant, - watcher: Option, - watcher_receiver: Receiver, + // Drop order is significant. + watcher: Option<(RecommendedWatcher, Receiver)>, } #[derive(Debug)] @@ -69,23 +66,13 @@ enum Event { impl NotifyActor { fn new(sender: loader::Sender) -> NotifyActor { - let (watcher_sender, watcher_receiver) = unbounded(); - let watcher = log_notify_error(Watcher::new_immediate(move |event| { - watcher_sender.send(event).unwrap() - })); - - NotifyActor { - sender, - config: Vec::new(), - watched_paths: FxHashSet::default(), - watcher, - watcher_receiver, - } + NotifyActor { sender, config: Vec::new(), watcher: None } } fn next_event(&self, receiver: &Receiver) -> Option { + let watcher_receiver = self.watcher.as_ref().map(|(_, receiver)| receiver); select! { recv(receiver) -> it => it.ok().map(Event::Message), - recv(&self.watcher_receiver) -> it => Some(Event::NotifyEvent(it.unwrap())), + recv(watcher_receiver.unwrap_or(&never())) -> it => Some(Event::NotifyEvent(it.unwrap())), } } fn run(mut self, inbox: Receiver) { @@ -94,10 +81,16 @@ impl NotifyActor { match event { Event::Message(msg) => match msg { Message::Config(config) => { + self.watcher = None; + let (watcher_sender, watcher_receiver) = unbounded(); + let watcher = log_notify_error(Watcher::new_immediate(move |event| { + watcher_sender.send(event).unwrap() + })); + self.watcher = watcher.map(|it| (it, watcher_receiver)); + let n_total = config.load.len(); self.send(loader::Message::Progress { n_total, n_done: 0 }); - self.unwatch_all(); self.config.clear(); for (i, entry) in config.load.into_iter().enumerate() { @@ -217,16 +210,8 @@ impl NotifyActor { } fn watch(&mut self, path: AbsPathBuf) { - if let Some(watcher) = &mut self.watcher { + if let Some((watcher, _)) = &mut self.watcher { log_notify_error(watcher.watch(&path, RecursiveMode::NonRecursive)); - self.watched_paths.insert(path); - } - } - fn unwatch_all(&mut self) { - if let Some(watcher) = &mut self.watcher { - for path in self.watched_paths.drain() { - log_notify_error(watcher.unwatch(path)); - } } } fn send(&mut self, msg: loader::Message) { -- cgit v1.2.3 From e4b4600752d3c7102f01d074c25b4798b75c2bed Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 8 Jul 2020 22:47:50 +0200 Subject: better language --- crates/vfs-notify/src/include.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crates/vfs-notify') diff --git a/crates/vfs-notify/src/include.rs b/crates/vfs-notify/src/include.rs index 7378766f5..72f928536 100644 --- a/crates/vfs-notify/src/include.rs +++ b/crates/vfs-notify/src/include.rs @@ -9,8 +9,8 @@ use paths::{RelPath, RelPathBuf}; /// /// It describes the set of files inside some directory. /// -/// The current implementation is very limited, it allows white-listing file -/// globs and black-listing directories. +/// The current implementation is very limited, it allows including file globs +/// and recursively excluding directories. #[derive(Debug, Clone)] pub(crate) struct Include { include_files: GlobSet, -- cgit v1.2.3 From dac9a4cebd39e7865235530fa69e5ae4ab42962b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 10 Jul 2020 23:39:25 +0200 Subject: Don't spawn notify unnecessary --- crates/vfs-notify/src/include.rs | 1 - crates/vfs-notify/src/lib.rs | 12 +++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'crates/vfs-notify') diff --git a/crates/vfs-notify/src/include.rs b/crates/vfs-notify/src/include.rs index 72f928536..59da3d77a 100644 --- a/crates/vfs-notify/src/include.rs +++ b/crates/vfs-notify/src/include.rs @@ -1,5 +1,4 @@ //! See `Include`. - use std::convert::TryFrom; use globset::{Glob, GlobSet, GlobSetBuilder}; diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index ee5035554..6aaa53f63 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -82,11 +82,13 @@ impl NotifyActor { Event::Message(msg) => match msg { Message::Config(config) => { self.watcher = None; - let (watcher_sender, watcher_receiver) = unbounded(); - let watcher = log_notify_error(Watcher::new_immediate(move |event| { - watcher_sender.send(event).unwrap() - })); - self.watcher = watcher.map(|it| (it, watcher_receiver)); + if !config.watch.is_empty() { + let (watcher_sender, watcher_receiver) = unbounded(); + let watcher = log_notify_error(Watcher::new_immediate(move |event| { + watcher_sender.send(event).unwrap() + })); + self.watcher = watcher.map(|it| (it, watcher_receiver)); + } let n_total = config.load.len(); self.send(loader::Message::Progress { n_total, n_done: 0 }); -- cgit v1.2.3 From 847135495fb194f9eef7b65b515982161d77face Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 11 Jul 2020 13:31:02 +0200 Subject: no doctests --- crates/vfs-notify/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) (limited to 'crates/vfs-notify') diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml index 4737a52a7..eb1764c36 100644 --- a/crates/vfs-notify/Cargo.toml +++ b/crates/vfs-notify/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" authors = ["rust-analyzer developers"] edition = "2018" +[lib] +doctest = false + [dependencies] log = "0.4.8" rustc-hash = "1.0" -- cgit v1.2.3 From 6f423466d181130848c229e2684c6dd18f8a5e9d Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Tue, 14 Jul 2020 10:57:26 +0900 Subject: Add a license field to all the crates --- crates/vfs-notify/Cargo.toml | 1 + 1 file changed, 1 insertion(+) (limited to 'crates/vfs-notify') diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml index eb1764c36..95c56ffa6 100644 --- a/crates/vfs-notify/Cargo.toml +++ b/crates/vfs-notify/Cargo.toml @@ -3,6 +3,7 @@ name = "vfs-notify" version = "0.1.0" authors = ["rust-analyzer developers"] edition = "2018" +license = "MIT OR Apache-2.0" [lib] doctest = false -- cgit v1.2.3 From 46ac9ff5e3cf070584d8167150655d091d47e3c2 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 18 Jul 2020 16:40:10 +0200 Subject: Simplify exclusion logic --- crates/vfs-notify/Cargo.toml | 1 - crates/vfs-notify/src/include.rs | 42 ---------------- crates/vfs-notify/src/lib.rs | 104 ++++++++++++++++++--------------------- 3 files changed, 47 insertions(+), 100 deletions(-) delete mode 100644 crates/vfs-notify/src/include.rs (limited to 'crates/vfs-notify') diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml index 95c56ffa6..fce7bae3a 100644 --- a/crates/vfs-notify/Cargo.toml +++ b/crates/vfs-notify/Cargo.toml @@ -13,7 +13,6 @@ log = "0.4.8" rustc-hash = "1.0" jod-thread = "0.1.0" walkdir = "2.3.1" -globset = "0.4.5" crossbeam-channel = "0.4.0" notify = "5.0.0-pre.3" diff --git a/crates/vfs-notify/src/include.rs b/crates/vfs-notify/src/include.rs deleted file mode 100644 index 59da3d77a..000000000 --- a/crates/vfs-notify/src/include.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! See `Include`. -use std::convert::TryFrom; - -use globset::{Glob, GlobSet, GlobSetBuilder}; -use paths::{RelPath, RelPathBuf}; - -/// `Include` is the opposite of .gitignore. -/// -/// It describes the set of files inside some directory. -/// -/// The current implementation is very limited, it allows including file globs -/// and recursively excluding directories. -#[derive(Debug, Clone)] -pub(crate) struct Include { - include_files: GlobSet, - exclude_dirs: Vec, -} - -impl Include { - pub(crate) fn new(include: Vec) -> Include { - let mut include_files = GlobSetBuilder::new(); - let mut exclude_dirs = Vec::new(); - - for glob in include { - if glob.starts_with("!/") { - if let Ok(path) = RelPathBuf::try_from(&glob["!/".len()..]) { - exclude_dirs.push(path) - } - } else { - include_files.add(Glob::new(&glob).unwrap()); - } - } - let include_files = include_files.build().unwrap(); - Include { include_files, exclude_dirs } - } - pub(crate) fn include_file(&self, path: &RelPath) -> bool { - self.include_files.is_match(path) - } - pub(crate) fn exclude_dir(&self, path: &RelPath) -> bool { - self.exclude_dirs.iter().any(|excluded| path.starts_with(excluded)) - } -} diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index 6aaa53f63..e1e36612a 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -6,9 +6,7 @@ //! //! Hopefully, one day a reliable file watching/walking crate appears on //! crates.io, and we can reduce this to trivial glue code. -mod include; - -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; use notify::{RecommendedWatcher, RecursiveMode, Watcher}; @@ -16,8 +14,6 @@ use paths::{AbsPath, AbsPathBuf}; use vfs::loader; use walkdir::WalkDir; -use crate::include::Include; - #[derive(Debug)] pub struct NotifyHandle { // Relative order of fields below is significant. @@ -53,7 +49,7 @@ type NotifyEvent = notify::Result; struct NotifyActor { sender: loader::Sender, - config: Vec<(AbsPathBuf, Include, bool)>, + watched_entries: Vec, // Drop order is significant. watcher: Option<(RecommendedWatcher, Receiver)>, } @@ -66,7 +62,7 @@ enum Event { impl NotifyActor { fn new(sender: loader::Sender) -> NotifyActor { - NotifyActor { sender, config: Vec::new(), watcher: None } + NotifyActor { sender, watched_entries: Vec::new(), watcher: None } } fn next_event(&self, receiver: &Receiver) -> Option { let watcher_receiver = self.watcher.as_ref().map(|(_, receiver)| receiver); @@ -93,15 +89,17 @@ impl NotifyActor { let n_total = config.load.len(); self.send(loader::Message::Progress { n_total, n_done: 0 }); - self.config.clear(); + self.watched_entries.clear(); for (i, entry) in config.load.into_iter().enumerate() { let watch = config.watch.contains(&i); + if watch { + self.watched_entries.push(entry.clone()) + } let files = self.load_entry(entry, watch); self.send(loader::Message::Loaded { files }); self.send(loader::Message::Progress { n_total, n_done: i + 1 }); } - self.config.sort_by(|x, y| x.0.cmp(&y.0)); } Message::Invalidate(path) => { let contents = read(path.as_path()); @@ -116,34 +114,27 @@ impl NotifyActor { .into_iter() .map(|path| AbsPathBuf::try_from(path).unwrap()) .filter_map(|path| { - let is_dir = path.is_dir(); - let is_file = path.is_file(); - - let config_idx = - match self.config.binary_search_by(|it| it.0.cmp(&path)) { - Ok(it) => it, - Err(it) => it.saturating_sub(1), - }; - let include = self.config.get(config_idx).and_then(|it| { - let rel_path = path.strip_prefix(&it.0)?; - Some((rel_path, &it.1)) - }); - - if let Some((rel_path, include)) = include { - if is_dir && include.exclude_dir(&rel_path) - || is_file && !include.include_file(&rel_path) - { - return None; - } + if path.is_dir() + && self + .watched_entries + .iter() + .any(|entry| entry.contains_dir(&path)) + { + self.watch(path); + return None; } - if is_dir { - self.watch(path); + if !path.is_file() { return None; } - if !is_file { + if !self + .watched_entries + .iter() + .any(|entry| entry.contains_file(&path)) + { return None; } + let contents = read(&path); Some((path, contents)) }) @@ -170,43 +161,42 @@ impl NotifyActor { (file, contents) }) .collect::>(), - loader::Entry::Directory { path, include } => { - let include = Include::new(include); - self.config.push((path.clone(), include.clone(), watch)); - - let files = WalkDir::new(&path) - .into_iter() - .filter_entry(|entry| { - let abs_path: &AbsPath = entry.path().try_into().unwrap(); - match abs_path.strip_prefix(&path) { - Some(rel_path) => { - !(entry.file_type().is_dir() && include.exclude_dir(rel_path)) - } - None => false, + loader::Entry::Directories(dirs) => { + let mut res = Vec::new(); + + for root in dirs.include.iter() { + let walkdir = WalkDir::new(root).into_iter().filter_entry(|entry| { + if !entry.file_type().is_dir() { + return true; } - }) - .filter_map(|entry| entry.ok()) - .filter_map(|entry| { + let path = AbsPath::assert(entry.path()); + root == path + || dirs.exclude.iter().chain(&dirs.include).all(|it| it != path) + }); + + let files = walkdir.filter_map(|it| it.ok()).filter_map(|entry| { let is_dir = entry.file_type().is_dir(); let is_file = entry.file_type().is_file(); - let abs_path = AbsPathBuf::try_from(entry.into_path()).unwrap(); + let abs_path = AbsPathBuf::assert(entry.into_path()); if is_dir && watch { self.watch(abs_path.clone()); } - let rel_path = abs_path.strip_prefix(&path)?; - if is_file && include.include_file(&rel_path) { - Some(abs_path) - } else { - None + if !is_file { + return None; + } + let ext = abs_path.extension().unwrap_or_default(); + if dirs.extensions.iter().all(|it| it.as_str() != ext) { + return None; } + Some(abs_path) }); - files - .map(|file| { + res.extend(files.map(|file| { let contents = read(file.as_path()); (file, contents) - }) - .collect() + })); + } + res } } } -- cgit v1.2.3