aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_vfs/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_vfs/src/lib.rs')
-rw-r--r--crates/ra_vfs/src/lib.rs168
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;
28use ra_arena::{impl_arena_id, Arena, RawId}; 28use ra_arena::{impl_arena_id, Arena, RawId};
29use relative_path::{Component, RelativePath, RelativePathBuf}; 29use relative_path::{Component, RelativePath, RelativePathBuf};
30use rustc_hash::{FxHashMap, FxHashSet}; 30use rustc_hash::{FxHashMap, FxHashSet};
31use walkdir::DirEntry;
31 32
32pub use crate::io::TaskResult as VfsTask; 33pub use crate::io::TaskResult as VfsTask;
33use io::{Task, TaskResult, WatcherChange, WatcherChangeData, Worker}; 34use 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.
37pub(crate) struct RootFilter { 38pub(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
42impl RootFilter { 44impl 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
61pub(crate) fn default_filter(path: &Path, rel_path: &RelativePath) -> bool { 75pub(crate) fn default_filter(path: &Path, rel_path: &RelativePath) -> bool {
@@ -94,10 +108,22 @@ pub(crate) struct Roots {
94} 108}
95 109
96impl Roots { 110impl 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
136impl Vfs { 162impl 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