diff options
author | Bernardo <[email protected]> | 2019-01-25 17:39:35 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-01-26 08:46:37 +0000 |
commit | d63e1cebff771621b90bdce25ac013eecb415e1e (patch) | |
tree | b4b83981fb7734d036be1dc4e9385a9c6e71e6ea /crates/ra_vfs/src/lib.rs | |
parent | 86fadbd4e59f12535edcc280ec227d7ee8a0848d (diff) |
use `Roots` in watcher
Diffstat (limited to 'crates/ra_vfs/src/lib.rs')
-rw-r--r-- | crates/ra_vfs/src/lib.rs | 168 |
1 files changed, 70 insertions, 98 deletions
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs index cba3a463a..661892f8a 100644 --- a/crates/ra_vfs/src/lib.rs +++ b/crates/ra_vfs/src/lib.rs | |||
@@ -28,22 +28,25 @@ use crossbeam_channel::Receiver; | |||
28 | use ra_arena::{impl_arena_id, Arena, RawId}; | 28 | use ra_arena::{impl_arena_id, Arena, RawId}; |
29 | use relative_path::{Component, RelativePath, RelativePathBuf}; | 29 | use relative_path::{Component, RelativePath, RelativePathBuf}; |
30 | use rustc_hash::{FxHashMap, FxHashSet}; | 30 | use rustc_hash::{FxHashMap, FxHashSet}; |
31 | use walkdir::DirEntry; | ||
31 | 32 | ||
32 | pub use crate::io::TaskResult as VfsTask; | 33 | pub use crate::io::TaskResult as VfsTask; |
33 | use io::{Task, TaskResult, WatcherChange, WatcherChangeData, Worker}; | 34 | use io::{TaskResult, Worker}; |
34 | 35 | ||
35 | /// `RootFilter` is a predicate that checks if a file can belong to a root. If | 36 | /// `RootFilter` is a predicate that checks if a file can belong to a root. If |
36 | /// several filters match a file (nested dirs), the most nested one wins. | 37 | /// several filters match a file (nested dirs), the most nested one wins. |
37 | pub(crate) struct RootFilter { | 38 | pub(crate) struct RootFilter { |
38 | root: PathBuf, | 39 | root: PathBuf, |
39 | filter: fn(&Path, &RelativePath) -> bool, | 40 | filter: fn(&Path, &RelativePath) -> bool, |
41 | excluded_dirs: Vec<PathBuf>, | ||
40 | } | 42 | } |
41 | 43 | ||
42 | impl RootFilter { | 44 | impl RootFilter { |
43 | fn new(root: PathBuf) -> RootFilter { | 45 | fn new(root: PathBuf, excluded_dirs: Vec<PathBuf>) -> RootFilter { |
44 | RootFilter { | 46 | RootFilter { |
45 | root, | 47 | root, |
46 | filter: default_filter, | 48 | filter: default_filter, |
49 | excluded_dirs, | ||
47 | } | 50 | } |
48 | } | 51 | } |
49 | /// Check if this root can contain `path`. NB: even if this returns | 52 | /// Check if this root can contain `path`. NB: even if this returns |
@@ -56,6 +59,17 @@ impl RootFilter { | |||
56 | } | 59 | } |
57 | Some(rel_path) | 60 | Some(rel_path) |
58 | } | 61 | } |
62 | |||
63 | pub(crate) fn entry_filter<'a>(&'a self) -> impl FnMut(&DirEntry) -> bool + 'a { | ||
64 | move |entry: &DirEntry| { | ||
65 | if entry.path().is_dir() && self.excluded_dirs.iter().any(|it| it == entry.path()) { | ||
66 | // do not walk nested roots | ||
67 | false | ||
68 | } else { | ||
69 | self.can_contain(entry.path()).is_some() | ||
70 | } | ||
71 | } | ||
72 | } | ||
59 | } | 73 | } |
60 | 74 | ||
61 | pub(crate) fn default_filter(path: &Path, rel_path: &RelativePath) -> bool { | 75 | pub(crate) fn default_filter(path: &Path, rel_path: &RelativePath) -> bool { |
@@ -94,10 +108,22 @@ pub(crate) struct Roots { | |||
94 | } | 108 | } |
95 | 109 | ||
96 | impl Roots { | 110 | impl Roots { |
97 | pub(crate) fn new() -> Roots { | 111 | pub(crate) fn new(mut paths: Vec<PathBuf>) -> Roots { |
98 | Roots { | 112 | let mut roots = Arena::default(); |
99 | roots: Arena::default(), | 113 | // A hack to make nesting work. |
114 | paths.sort_by_key(|it| Reverse(it.as_os_str().len())); | ||
115 | for (i, path) in paths.iter().enumerate() { | ||
116 | let nested_roots = paths[..i] | ||
117 | .iter() | ||
118 | .filter(|it| it.starts_with(path)) | ||
119 | .map(|it| it.clone()) | ||
120 | .collect::<Vec<_>>(); | ||
121 | |||
122 | let root_filter = Arc::new(RootFilter::new(path.clone(), nested_roots)); | ||
123 | |||
124 | roots.alloc(root_filter.clone()); | ||
100 | } | 125 | } |
126 | Roots { roots } | ||
101 | } | 127 | } |
102 | pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> { | 128 | pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> { |
103 | self.roots | 129 | self.roots |
@@ -135,36 +161,22 @@ impl fmt::Debug for Vfs { | |||
135 | 161 | ||
136 | impl Vfs { | 162 | impl Vfs { |
137 | pub fn new(roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) { | 163 | pub fn new(roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) { |
138 | let mut root_paths = roots; | 164 | let roots = Arc::new(Roots::new(roots)); |
139 | let worker = io::Worker::start(); | 165 | let worker = io::Worker::start(roots.clone()); |
140 | |||
141 | let mut roots = Roots::new(); | ||
142 | let mut root2files = FxHashMap::default(); | 166 | let mut root2files = FxHashMap::default(); |
143 | 167 | ||
144 | // A hack to make nesting work. | 168 | for (root, filter) in roots.iter() { |
145 | root_paths.sort_by_key(|it| Reverse(it.as_os_str().len())); | ||
146 | for (i, path) in root_paths.iter().enumerate() { | ||
147 | let root_filter = Arc::new(RootFilter::new(path.clone())); | ||
148 | |||
149 | let root = roots.alloc(root_filter.clone()); | ||
150 | root2files.insert(root, Default::default()); | 169 | root2files.insert(root, Default::default()); |
151 | 170 | worker | |
152 | let nested_roots = root_paths[..i] | 171 | .sender() |
153 | .iter() | 172 | .send(io::Task::AddRoot { |
154 | .filter(|it| it.starts_with(path)) | 173 | root, |
155 | .map(|it| it.clone()) | 174 | filter: filter.clone(), |
156 | .collect::<Vec<_>>(); | 175 | }) |
157 | 176 | .unwrap(); | |
158 | let task = io::Task::AddRoot { | ||
159 | root, | ||
160 | path: path.clone(), | ||
161 | root_filter, | ||
162 | nested_roots, | ||
163 | }; | ||
164 | worker.sender().send(task).unwrap(); | ||
165 | } | 177 | } |
166 | let res = Vfs { | 178 | let res = Vfs { |
167 | roots: Arc::new(roots), | 179 | roots, |
168 | files: Arena::default(), | 180 | files: Arena::default(), |
169 | root2files, | 181 | root2files, |
170 | worker, | 182 | worker, |
@@ -225,90 +237,46 @@ impl Vfs { | |||
225 | 237 | ||
226 | pub fn handle_task(&mut self, task: io::TaskResult) { | 238 | pub fn handle_task(&mut self, task: io::TaskResult) { |
227 | match task { | 239 | match task { |
228 | TaskResult::AddRoot(task) => { | 240 | TaskResult::BulkLoadRoot { root, files } => { |
229 | let mut files = Vec::new(); | 241 | let mut cur_files = Vec::new(); |
230 | // While we were scanning the root in the backgound, a file might have | 242 | // While we were scanning the root in the backgound, a file might have |
231 | // been open in the editor, so we need to account for that. | 243 | // been open in the editor, so we need to account for that. |
232 | let exising = self.root2files[&task.root] | 244 | let exising = self.root2files[&root] |
233 | .iter() | 245 | .iter() |
234 | .map(|&file| (self.files[file].path.clone(), file)) | 246 | .map(|&file| (self.files[file].path.clone(), file)) |
235 | .collect::<FxHashMap<_, _>>(); | 247 | .collect::<FxHashMap<_, _>>(); |
236 | for (path, text) in task.files { | 248 | for (path, text) in files { |
237 | if let Some(&file) = exising.get(&path) { | 249 | if let Some(&file) = exising.get(&path) { |
238 | let text = Arc::clone(&self.files[file].text); | 250 | let text = Arc::clone(&self.files[file].text); |
239 | files.push((file, path, text)); | 251 | cur_files.push((file, path, text)); |
240 | continue; | 252 | continue; |
241 | } | 253 | } |
242 | let text = Arc::new(text); | 254 | let text = Arc::new(text); |
243 | let file = self.add_file(task.root, path.clone(), Arc::clone(&text), false); | 255 | let file = self.add_file(root, path.clone(), Arc::clone(&text), false); |
244 | files.push((file, path, text)); | 256 | cur_files.push((file, path, text)); |
245 | } | 257 | } |
246 | 258 | ||
247 | let change = VfsChange::AddRoot { | 259 | let change = VfsChange::AddRoot { |
248 | root: task.root, | 260 | root, |
249 | files, | 261 | files: cur_files, |
250 | }; | 262 | }; |
251 | self.pending_changes.push(change); | 263 | self.pending_changes.push(change); |
252 | } | 264 | } |
253 | TaskResult::HandleChange(change) => match &change { | 265 | TaskResult::AddSingleFile { root, path, text } => { |
254 | WatcherChange::Create(path) if path.is_dir() => { | 266 | self.do_add_file(root, path, text, false); |
255 | if let Some((root, _path, _file)) = self.find_root(&path) { | 267 | } |
256 | let root_filter = self.roots[root].clone(); | 268 | TaskResult::ChangeSingleFile { root, path, text } => { |
257 | self.worker | 269 | if let Some(file) = self.find_file(root, &path) { |
258 | .sender() | 270 | self.do_change_file(file, text, false); |
259 | .send(Task::Watch { | 271 | } else { |
260 | dir: path.to_path_buf(), | 272 | self.do_add_file(root, path, text, false); |
261 | root_filter, | ||
262 | }) | ||
263 | .unwrap() | ||
264 | } | ||
265 | } | ||
266 | WatcherChange::Create(path) | ||
267 | | WatcherChange::Remove(path) | ||
268 | | WatcherChange::Write(path) => { | ||
269 | if self.should_handle_change(&path) { | ||
270 | self.worker.sender().send(Task::LoadChange(change)).unwrap() | ||
271 | } | ||
272 | } | ||
273 | WatcherChange::Rescan => { | ||
274 | // TODO we should reload all files | ||
275 | } | ||
276 | }, | ||
277 | TaskResult::LoadChange(change) => match change { | ||
278 | WatcherChangeData::Create { path, text } | ||
279 | | WatcherChangeData::Write { path, text } => { | ||
280 | if let Some((root, path, file)) = self.find_root(&path) { | ||
281 | if let Some(file) = file { | ||
282 | self.do_change_file(file, text, false); | ||
283 | } else { | ||
284 | self.do_add_file(root, path, text, false); | ||
285 | } | ||
286 | } | ||
287 | } | ||
288 | WatcherChangeData::Remove { path } => { | ||
289 | if let Some((root, path, file)) = self.find_root(&path) { | ||
290 | if let Some(file) = file { | ||
291 | self.do_remove_file(root, path, file, false); | ||
292 | } | ||
293 | } | ||
294 | } | 273 | } |
295 | }, | 274 | } |
296 | } | 275 | TaskResult::RemoveSingleFile { root, path } => { |
297 | } | 276 | if let Some(file) = self.find_file(root, &path) { |
298 | 277 | self.do_remove_file(root, path, file, false); | |
299 | fn should_handle_change(&self, path: &Path) -> bool { | ||
300 | if let Some((_root, _rel_path, file)) = self.find_root(&path) { | ||
301 | if let Some(file) = file { | ||
302 | if self.files[file].is_overlayed { | ||
303 | // file is overlayed | ||
304 | log::debug!("skipping overlayed \"{}\"", path.display()); | ||
305 | return false; | ||
306 | } | 278 | } |
307 | } | 279 | } |
308 | true | ||
309 | } else { | ||
310 | // file doesn't belong to any root | ||
311 | false | ||
312 | } | 280 | } |
313 | } | 281 | } |
314 | 282 | ||
@@ -434,11 +402,15 @@ impl Vfs { | |||
434 | 402 | ||
435 | fn find_root(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf, Option<VfsFile>)> { | 403 | fn find_root(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf, Option<VfsFile>)> { |
436 | let (root, path) = self.roots.find(&path)?; | 404 | let (root, path) = self.roots.find(&path)?; |
437 | let file = self.root2files[&root] | 405 | let file = self.find_file(root, &path); |
406 | Some((root, path, file)) | ||
407 | } | ||
408 | |||
409 | fn find_file(&self, root: VfsRoot, path: &RelativePath) -> Option<VfsFile> { | ||
410 | self.root2files[&root] | ||
438 | .iter() | 411 | .iter() |
439 | .map(|&it| it) | 412 | .map(|&it| it) |
440 | .find(|&file| self.files[file].path == path); | 413 | .find(|&file| self.files[file].path == path) |
441 | Some((root, path, file)) | ||
442 | } | 414 | } |
443 | } | 415 | } |
444 | 416 | ||