aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_vfs/src/lib.rs
diff options
context:
space:
mode:
authorBernardo <[email protected]>2019-01-20 21:13:21 +0000
committerAleksey Kladov <[email protected]>2019-01-26 08:46:27 +0000
commitf88355ccb5e8ea2381e13eabcdb64880e757aff1 (patch)
tree3eb37e82786ed739a69d3981129e12d884be1cf3 /crates/ra_vfs/src/lib.rs
parenteacf7aeb42d7ba54c305664773e77eb592b51b99 (diff)
refactor, put watcher with `io::Worker`
use `RootFilter` to filter recursive watches untested
Diffstat (limited to 'crates/ra_vfs/src/lib.rs')
-rw-r--r--crates/ra_vfs/src/lib.rs100
1 files changed, 59 insertions, 41 deletions
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs
index ad40db340..196180890 100644
--- a/crates/ra_vfs/src/lib.rs
+++ b/crates/ra_vfs/src/lib.rs
@@ -20,7 +20,7 @@ use std::{
20 cmp::Reverse, 20 cmp::Reverse,
21 ffi::OsStr, 21 ffi::OsStr,
22 fmt, fs, mem, 22 fmt, fs, mem,
23 path::{Path, PathBuf}, 23 path::{Component, Path, PathBuf},
24 sync::Arc, 24 sync::Arc,
25 thread, 25 thread,
26}; 26};
@@ -29,30 +29,37 @@ use crossbeam_channel::Receiver;
29use ra_arena::{impl_arena_id, Arena, RawId}; 29use ra_arena::{impl_arena_id, Arena, RawId};
30use relative_path::RelativePathBuf; 30use relative_path::RelativePathBuf;
31use rustc_hash::{FxHashMap, FxHashSet}; 31use rustc_hash::{FxHashMap, FxHashSet};
32use thread_worker::WorkerHandle;
33use walkdir::DirEntry; 32use walkdir::DirEntry;
34 33
35pub use crate::io::TaskResult as VfsTask; 34pub use crate::io::TaskResult as VfsTask;
36pub use crate::watcher::{Watcher, WatcherChange}; 35pub use crate::watcher::WatcherChange;
37 36
38/// `RootFilter` is a predicate that checks if a file can belong to a root. If 37/// `RootFilter` is a predicate that checks if a file can belong to a root. If
39/// several filters match a file (nested dirs), the most nested one wins. 38/// several filters match a file (nested dirs), the most nested one wins.
40struct RootFilter { 39pub(crate) struct RootFilter {
41 root: PathBuf, 40 root: PathBuf,
42 file_filter: fn(&Path) -> bool, 41 filter: fn(RootEntry) -> bool,
42}
43
44pub(crate) struct RootEntry<'a, 'b> {
45 root: &'a Path,
46 path: &'b Path,
43} 47}
44 48
45impl RootFilter { 49impl RootFilter {
46 fn new(root: PathBuf) -> RootFilter { 50 fn new(root: PathBuf) -> RootFilter {
47 RootFilter { 51 RootFilter {
48 root, 52 root,
49 file_filter: has_rs_extension, 53 filter: default_filter,
50 } 54 }
51 } 55 }
52 /// Check if this root can contain `path`. NB: even if this returns 56 /// Check if this root can contain `path`. NB: even if this returns
53 /// true, the `path` might actually be conained in some nested root. 57 /// true, the `path` might actually be conained in some nested root.
54 fn can_contain(&self, path: &Path) -> Option<RelativePathBuf> { 58 pub(crate) fn can_contain(&self, path: &Path) -> Option<RelativePathBuf> {
55 if !(self.file_filter)(path) { 59 if !(self.filter)(RootEntry {
60 root: &self.root,
61 path,
62 }) {
56 return None; 63 return None;
57 } 64 }
58 let path = path.strip_prefix(&self.root).ok()?; 65 let path = path.strip_prefix(&self.root).ok()?;
@@ -60,8 +67,17 @@ impl RootFilter {
60 } 67 }
61} 68}
62 69
63pub(crate) fn has_rs_extension(p: &Path) -> bool { 70pub(crate) fn default_filter(entry: RootEntry) -> bool {
64 p.extension() == Some(OsStr::new("rs")) 71 if entry.path.is_dir() {
72 // first component relative to root is "target"
73 entry
74 .path
75 .strip_prefix(entry.root)
76 .map(|p| p.components().next() != Some(Component::Normal(OsStr::new("target"))))
77 .unwrap_or(false)
78 } else {
79 entry.path.extension() == Some(OsStr::new("rs"))
80 }
65} 81}
66 82
67#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 83#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -80,13 +96,11 @@ struct VfsFileData {
80} 96}
81 97
82pub struct Vfs { 98pub struct Vfs {
83 roots: Arena<VfsRoot, RootFilter>, 99 roots: Arena<VfsRoot, Arc<RootFilter>>,
84 files: Arena<VfsFile, VfsFileData>, 100 files: Arena<VfsFile, VfsFileData>,
85 root2files: FxHashMap<VfsRoot, FxHashSet<VfsFile>>, 101 root2files: FxHashMap<VfsRoot, FxHashSet<VfsFile>>,
86 pending_changes: Vec<VfsChange>, 102 pending_changes: Vec<VfsChange>,
87 worker: io::Worker, 103 worker: io::Worker,
88 worker_handle: WorkerHandle,
89 watcher: Option<Watcher>,
90} 104}
91 105
92impl fmt::Debug for Vfs { 106impl fmt::Debug for Vfs {
@@ -97,41 +111,35 @@ impl fmt::Debug for Vfs {
97 111
98impl Vfs { 112impl Vfs {
99 pub fn new(mut roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) { 113 pub fn new(mut roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) {
100 let (worker, worker_handle) = io::start(); 114 let worker = io::Worker::start();
101
102 let watcher = match Watcher::start(worker.inp.clone()) {
103 Ok(watcher) => Some(watcher),
104 Err(e) => {
105 log::error!("could not start watcher: {}", e);
106 None
107 }
108 };
109 115
110 let mut res = Vfs { 116 let mut res = Vfs {
111 roots: Arena::default(), 117 roots: Arena::default(),
112 files: Arena::default(), 118 files: Arena::default(),
113 root2files: FxHashMap::default(), 119 root2files: FxHashMap::default(),
114 worker, 120 worker,
115 worker_handle,
116 watcher,
117 pending_changes: Vec::new(), 121 pending_changes: Vec::new(),
118 }; 122 };
119 123
120 // A hack to make nesting work. 124 // A hack to make nesting work.
121 roots.sort_by_key(|it| Reverse(it.as_os_str().len())); 125 roots.sort_by_key(|it| Reverse(it.as_os_str().len()));
122 for (i, path) in roots.iter().enumerate() { 126 for (i, path) in roots.iter().enumerate() {
123 let root = res.roots.alloc(RootFilter::new(path.clone())); 127 let root_filter = Arc::new(RootFilter::new(path.clone()));
128
129 let root = res.roots.alloc(root_filter.clone());
124 res.root2files.insert(root, Default::default()); 130 res.root2files.insert(root, Default::default());
131
125 let nested = roots[..i] 132 let nested = roots[..i]
126 .iter() 133 .iter()
127 .filter(|it| it.starts_with(path)) 134 .filter(|it| it.starts_with(path))
128 .map(|it| it.clone()) 135 .map(|it| it.clone())
129 .collect::<Vec<_>>(); 136 .collect::<Vec<_>>();
137
130 let filter = move |entry: &DirEntry| { 138 let filter = move |entry: &DirEntry| {
131 if entry.file_type().is_file() { 139 if entry.file_type().is_dir() && nested.iter().any(|it| it == entry.path()) {
132 has_rs_extension(entry.path()) 140 false
133 } else { 141 } else {
134 nested.iter().all(|it| it != entry.path()) 142 root_filter.can_contain(entry.path()).is_some()
135 } 143 }
136 }; 144 };
137 let task = io::Task::AddRoot { 145 let task = io::Task::AddRoot {
@@ -139,10 +147,7 @@ impl Vfs {
139 path: path.clone(), 147 path: path.clone(),
140 filter: Box::new(filter), 148 filter: Box::new(filter),
141 }; 149 };
142 res.worker.inp.send(task).unwrap(); 150 res.worker.sender().send(task).unwrap();
143 if let Some(ref mut watcher) = res.watcher {
144 watcher.watch(path);
145 }
146 } 151 }
147 let roots = res.roots.iter().map(|(id, _)| id).collect(); 152 let roots = res.roots.iter().map(|(id, _)| id).collect();
148 (res, roots) 153 (res, roots)
@@ -194,7 +199,7 @@ impl Vfs {
194 } 199 }
195 200
196 pub fn task_receiver(&self) -> &Receiver<io::TaskResult> { 201 pub fn task_receiver(&self) -> &Receiver<io::TaskResult> {
197 &self.worker.out 202 self.worker.receiver()
198 } 203 }
199 204
200 pub fn handle_task(&mut self, task: io::TaskResult) { 205 pub fn handle_task(&mut self, task: io::TaskResult) {
@@ -225,19 +230,35 @@ impl Vfs {
225 self.pending_changes.push(change); 230 self.pending_changes.push(change);
226 } 231 }
227 io::TaskResult::HandleChange(change) => match &change { 232 io::TaskResult::HandleChange(change) => match &change {
233 watcher::WatcherChange::Create(path) if path.is_dir() => {
234 if let Some((root, _path, _file)) = self.find_root(&path) {
235 let root_filter = self.roots[root].clone();
236 let filter =
237 move |entry: &DirEntry| root_filter.can_contain(entry.path()).is_some();
238 self.worker
239 .sender()
240 .send(io::Task::Watch {
241 dir: path.to_path_buf(),
242 filter: Box::new(filter),
243 })
244 .unwrap()
245 }
246 }
228 watcher::WatcherChange::Create(path) 247 watcher::WatcherChange::Create(path)
229 | watcher::WatcherChange::Remove(path) 248 | watcher::WatcherChange::Remove(path)
230 | watcher::WatcherChange::Write(path) => { 249 | watcher::WatcherChange::Write(path) => {
231 if self.should_handle_change(&path) { 250 if self.should_handle_change(&path) {
232 self.worker.inp.send(io::Task::LoadChange(change)).unwrap() 251 self.worker
252 .sender()
253 .send(io::Task::LoadChange(change))
254 .unwrap()
233 } 255 }
234 } 256 }
235 watcher::WatcherChange::Rescan => { 257 watcher::WatcherChange::Rescan => {
236 // TODO we should reload all files 258 // TODO we should reload all files
237 } 259 }
238 }, 260 },
239 io::TaskResult::LoadChange(None) => {} 261 io::TaskResult::LoadChange(change) => match change {
240 io::TaskResult::LoadChange(Some(change)) => match change {
241 io::WatcherChangeData::Create { path, text } 262 io::WatcherChangeData::Create { path, text }
242 | io::WatcherChangeData::Write { path, text } => { 263 | io::WatcherChangeData::Write { path, text } => {
243 if let Some((root, path, file)) = self.find_root(&path) { 264 if let Some((root, path, file)) = self.find_root(&path) {
@@ -256,6 +277,7 @@ impl Vfs {
256 } 277 }
257 } 278 }
258 }, 279 },
280 io::TaskResult::NoOp => {}
259 } 281 }
260 } 282 }
261 283
@@ -359,11 +381,7 @@ impl Vfs {
359 381
360 /// Sutdown the VFS and terminate the background watching thread. 382 /// Sutdown the VFS and terminate the background watching thread.
361 pub fn shutdown(self) -> thread::Result<()> { 383 pub fn shutdown(self) -> thread::Result<()> {
362 if let Some(watcher) = self.watcher { 384 self.worker.shutdown()
363 let _ = watcher.shutdown();
364 }
365 let _ = self.worker.shutdown();
366 self.worker_handle.shutdown()
367 } 385 }
368 386
369 fn add_file( 387 fn add_file(