diff options
Diffstat (limited to 'crates/vfs-notify/src')
-rw-r--r-- | crates/vfs-notify/src/include.rs | 42 | ||||
-rw-r--r-- | crates/vfs-notify/src/lib.rs | 104 |
2 files changed, 47 insertions, 99 deletions
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 @@ | |||
1 | //! See `Include`. | ||
2 | use std::convert::TryFrom; | ||
3 | |||
4 | use globset::{Glob, GlobSet, GlobSetBuilder}; | ||
5 | use paths::{RelPath, RelPathBuf}; | ||
6 | |||
7 | /// `Include` is the opposite of .gitignore. | ||
8 | /// | ||
9 | /// It describes the set of files inside some directory. | ||
10 | /// | ||
11 | /// The current implementation is very limited, it allows including file globs | ||
12 | /// and recursively excluding directories. | ||
13 | #[derive(Debug, Clone)] | ||
14 | pub(crate) struct Include { | ||
15 | include_files: GlobSet, | ||
16 | exclude_dirs: Vec<RelPathBuf>, | ||
17 | } | ||
18 | |||
19 | impl Include { | ||
20 | pub(crate) fn new(include: Vec<String>) -> Include { | ||
21 | let mut include_files = GlobSetBuilder::new(); | ||
22 | let mut exclude_dirs = Vec::new(); | ||
23 | |||
24 | for glob in include { | ||
25 | if glob.starts_with("!/") { | ||
26 | if let Ok(path) = RelPathBuf::try_from(&glob["!/".len()..]) { | ||
27 | exclude_dirs.push(path) | ||
28 | } | ||
29 | } else { | ||
30 | include_files.add(Glob::new(&glob).unwrap()); | ||
31 | } | ||
32 | } | ||
33 | let include_files = include_files.build().unwrap(); | ||
34 | Include { include_files, exclude_dirs } | ||
35 | } | ||
36 | pub(crate) fn include_file(&self, path: &RelPath) -> bool { | ||
37 | self.include_files.is_match(path) | ||
38 | } | ||
39 | pub(crate) fn exclude_dir(&self, path: &RelPath) -> bool { | ||
40 | self.exclude_dirs.iter().any(|excluded| path.starts_with(excluded)) | ||
41 | } | ||
42 | } | ||
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 @@ | |||
6 | //! | 6 | //! |
7 | //! Hopefully, one day a reliable file watching/walking crate appears on | 7 | //! Hopefully, one day a reliable file watching/walking crate appears on |
8 | //! crates.io, and we can reduce this to trivial glue code. | 8 | //! crates.io, and we can reduce this to trivial glue code. |
9 | mod include; | 9 | use std::convert::TryFrom; |
10 | |||
11 | use std::convert::{TryFrom, TryInto}; | ||
12 | 10 | ||
13 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; | 11 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; |
14 | use notify::{RecommendedWatcher, RecursiveMode, Watcher}; | 12 | use notify::{RecommendedWatcher, RecursiveMode, Watcher}; |
@@ -16,8 +14,6 @@ use paths::{AbsPath, AbsPathBuf}; | |||
16 | use vfs::loader; | 14 | use vfs::loader; |
17 | use walkdir::WalkDir; | 15 | use walkdir::WalkDir; |
18 | 16 | ||
19 | use crate::include::Include; | ||
20 | |||
21 | #[derive(Debug)] | 17 | #[derive(Debug)] |
22 | pub struct NotifyHandle { | 18 | pub struct NotifyHandle { |
23 | // Relative order of fields below is significant. | 19 | // Relative order of fields below is significant. |
@@ -53,7 +49,7 @@ type NotifyEvent = notify::Result<notify::Event>; | |||
53 | 49 | ||
54 | struct NotifyActor { | 50 | struct NotifyActor { |
55 | sender: loader::Sender, | 51 | sender: loader::Sender, |
56 | config: Vec<(AbsPathBuf, Include, bool)>, | 52 | watched_entries: Vec<loader::Entry>, |
57 | // Drop order is significant. | 53 | // Drop order is significant. |
58 | watcher: Option<(RecommendedWatcher, Receiver<NotifyEvent>)>, | 54 | watcher: Option<(RecommendedWatcher, Receiver<NotifyEvent>)>, |
59 | } | 55 | } |
@@ -66,7 +62,7 @@ enum Event { | |||
66 | 62 | ||
67 | impl NotifyActor { | 63 | impl NotifyActor { |
68 | fn new(sender: loader::Sender) -> NotifyActor { | 64 | fn new(sender: loader::Sender) -> NotifyActor { |
69 | NotifyActor { sender, config: Vec::new(), watcher: None } | 65 | NotifyActor { sender, watched_entries: Vec::new(), watcher: None } |
70 | } | 66 | } |
71 | fn next_event(&self, receiver: &Receiver<Message>) -> Option<Event> { | 67 | fn next_event(&self, receiver: &Receiver<Message>) -> Option<Event> { |
72 | let watcher_receiver = self.watcher.as_ref().map(|(_, receiver)| receiver); | 68 | let watcher_receiver = self.watcher.as_ref().map(|(_, receiver)| receiver); |
@@ -93,15 +89,17 @@ impl NotifyActor { | |||
93 | let n_total = config.load.len(); | 89 | let n_total = config.load.len(); |
94 | self.send(loader::Message::Progress { n_total, n_done: 0 }); | 90 | self.send(loader::Message::Progress { n_total, n_done: 0 }); |
95 | 91 | ||
96 | self.config.clear(); | 92 | self.watched_entries.clear(); |
97 | 93 | ||
98 | for (i, entry) in config.load.into_iter().enumerate() { | 94 | for (i, entry) in config.load.into_iter().enumerate() { |
99 | let watch = config.watch.contains(&i); | 95 | let watch = config.watch.contains(&i); |
96 | if watch { | ||
97 | self.watched_entries.push(entry.clone()) | ||
98 | } | ||
100 | let files = self.load_entry(entry, watch); | 99 | let files = self.load_entry(entry, watch); |
101 | self.send(loader::Message::Loaded { files }); | 100 | self.send(loader::Message::Loaded { files }); |
102 | self.send(loader::Message::Progress { n_total, n_done: i + 1 }); | 101 | self.send(loader::Message::Progress { n_total, n_done: i + 1 }); |
103 | } | 102 | } |
104 | self.config.sort_by(|x, y| x.0.cmp(&y.0)); | ||
105 | } | 103 | } |
106 | Message::Invalidate(path) => { | 104 | Message::Invalidate(path) => { |
107 | let contents = read(path.as_path()); | 105 | let contents = read(path.as_path()); |
@@ -116,34 +114,27 @@ impl NotifyActor { | |||
116 | .into_iter() | 114 | .into_iter() |
117 | .map(|path| AbsPathBuf::try_from(path).unwrap()) | 115 | .map(|path| AbsPathBuf::try_from(path).unwrap()) |
118 | .filter_map(|path| { | 116 | .filter_map(|path| { |
119 | let is_dir = path.is_dir(); | 117 | if path.is_dir() |
120 | let is_file = path.is_file(); | 118 | && self |
121 | 119 | .watched_entries | |
122 | let config_idx = | 120 | .iter() |
123 | match self.config.binary_search_by(|it| it.0.cmp(&path)) { | 121 | .any(|entry| entry.contains_dir(&path)) |
124 | Ok(it) => it, | 122 | { |
125 | Err(it) => it.saturating_sub(1), | 123 | self.watch(path); |
126 | }; | 124 | return None; |
127 | let include = self.config.get(config_idx).and_then(|it| { | ||
128 | let rel_path = path.strip_prefix(&it.0)?; | ||
129 | Some((rel_path, &it.1)) | ||
130 | }); | ||
131 | |||
132 | if let Some((rel_path, include)) = include { | ||
133 | if is_dir && include.exclude_dir(&rel_path) | ||
134 | || is_file && !include.include_file(&rel_path) | ||
135 | { | ||
136 | return None; | ||
137 | } | ||
138 | } | 125 | } |
139 | 126 | ||
140 | if is_dir { | 127 | if !path.is_file() { |
141 | self.watch(path); | ||
142 | return None; | 128 | return None; |
143 | } | 129 | } |
144 | if !is_file { | 130 | if !self |
131 | .watched_entries | ||
132 | .iter() | ||
133 | .any(|entry| entry.contains_file(&path)) | ||
134 | { | ||
145 | return None; | 135 | return None; |
146 | } | 136 | } |
137 | |||
147 | let contents = read(&path); | 138 | let contents = read(&path); |
148 | Some((path, contents)) | 139 | Some((path, contents)) |
149 | }) | 140 | }) |
@@ -170,43 +161,42 @@ impl NotifyActor { | |||
170 | (file, contents) | 161 | (file, contents) |
171 | }) | 162 | }) |
172 | .collect::<Vec<_>>(), | 163 | .collect::<Vec<_>>(), |
173 | loader::Entry::Directory { path, include } => { | 164 | loader::Entry::Directories(dirs) => { |
174 | let include = Include::new(include); | 165 | let mut res = Vec::new(); |
175 | self.config.push((path.clone(), include.clone(), watch)); | 166 | |
176 | 167 | for root in dirs.include.iter() { | |
177 | let files = WalkDir::new(&path) | 168 | let walkdir = WalkDir::new(root).into_iter().filter_entry(|entry| { |
178 | .into_iter() | 169 | if !entry.file_type().is_dir() { |
179 | .filter_entry(|entry| { | 170 | return true; |
180 | let abs_path: &AbsPath = entry.path().try_into().unwrap(); | ||
181 | match abs_path.strip_prefix(&path) { | ||
182 | Some(rel_path) => { | ||
183 | !(entry.file_type().is_dir() && include.exclude_dir(rel_path)) | ||
184 | } | ||
185 | None => false, | ||
186 | } | 171 | } |
187 | }) | 172 | let path = AbsPath::assert(entry.path()); |
188 | .filter_map(|entry| entry.ok()) | 173 | root == path |
189 | .filter_map(|entry| { | 174 | || dirs.exclude.iter().chain(&dirs.include).all(|it| it != path) |
175 | }); | ||
176 | |||
177 | let files = walkdir.filter_map(|it| it.ok()).filter_map(|entry| { | ||
190 | let is_dir = entry.file_type().is_dir(); | 178 | let is_dir = entry.file_type().is_dir(); |
191 | let is_file = entry.file_type().is_file(); | 179 | let is_file = entry.file_type().is_file(); |
192 | let abs_path = AbsPathBuf::try_from(entry.into_path()).unwrap(); | 180 | let abs_path = AbsPathBuf::assert(entry.into_path()); |
193 | if is_dir && watch { | 181 | if is_dir && watch { |
194 | self.watch(abs_path.clone()); | 182 | self.watch(abs_path.clone()); |
195 | } | 183 | } |
196 | let rel_path = abs_path.strip_prefix(&path)?; | 184 | if !is_file { |
197 | if is_file && include.include_file(&rel_path) { | 185 | return None; |
198 | Some(abs_path) | 186 | } |
199 | } else { | 187 | let ext = abs_path.extension().unwrap_or_default(); |
200 | None | 188 | if dirs.extensions.iter().all(|it| it.as_str() != ext) { |
189 | return None; | ||
201 | } | 190 | } |
191 | Some(abs_path) | ||
202 | }); | 192 | }); |
203 | 193 | ||
204 | files | 194 | res.extend(files.map(|file| { |
205 | .map(|file| { | ||
206 | let contents = read(file.as_path()); | 195 | let contents = read(file.as_path()); |
207 | (file, contents) | 196 | (file, contents) |
208 | }) | 197 | })); |
209 | .collect() | 198 | } |
199 | res | ||
210 | } | 200 | } |
211 | } | 201 | } |
212 | } | 202 | } |