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/src/include.rs | 42 ---------------- crates/vfs-notify/src/lib.rs | 104 ++++++++++++++++++--------------------- 2 files changed, 47 insertions(+), 99 deletions(-) delete mode 100644 crates/vfs-notify/src/include.rs (limited to 'crates/vfs-notify/src') 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