aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_vfs/src/lib.rs
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-01-26 15:13:44 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-01-26 15:13:44 +0000
commita8d32c4d1ae4b3e4276f7a97b6c6e5f95f91e67a (patch)
tree9e56b095a073ff3c82322302356f4d096a70afcd /crates/ra_vfs/src/lib.rs
parent5af7b2f4af51291fa4a0549c549796ba0520927b (diff)
parent9f16892b94817d144f37dfe0081b39aacec65635 (diff)
Merge #671
671: Makre VFS slightly less super obscure r=vemoo a=matklad I've decided to better understand what we do in VFS, and this turns out to be really hard. Jugling threads and channels is one of the most unfortunately arcane bits of rust... I had some success though by flattenning the structure so that all channel & thread creation routines are on one screen. r? @vemoo Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_vfs/src/lib.rs')
-rw-r--r--crates/ra_vfs/src/lib.rs146
1 files changed, 66 insertions, 80 deletions
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs
index 70a13f765..71a3f807d 100644
--- a/crates/ra_vfs/src/lib.rs
+++ b/crates/ra_vfs/src/lib.rs
@@ -18,94 +18,78 @@ mod io;
18use std::{ 18use std::{
19 cmp::Reverse, 19 cmp::Reverse,
20 fmt, fs, mem, 20 fmt, fs, mem,
21 ops::{Deref, DerefMut},
22 path::{Path, PathBuf}, 21 path::{Path, PathBuf},
23 sync::Arc, 22 sync::Arc,
24 thread, 23 thread,
25}; 24};
26 25
27use crossbeam_channel::Receiver; 26use crossbeam_channel::Receiver;
28use ra_arena::{impl_arena_id, Arena, RawId}; 27use ra_arena::{impl_arena_id, Arena, RawId, map::ArenaMap};
29use relative_path::{Component, RelativePath, RelativePathBuf}; 28use relative_path::{Component, RelativePath, RelativePathBuf};
30use rustc_hash::{FxHashMap, FxHashSet}; 29use rustc_hash::{FxHashMap, FxHashSet};
31use walkdir::DirEntry;
32 30
33pub use crate::io::TaskResult as VfsTask; 31pub use crate::io::TaskResult as VfsTask;
34use io::{TaskResult, Worker}; 32use io::{TaskResult, Worker};
35 33
36/// `RootFilter` is a predicate that checks if a file can belong to a root. If 34#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
37/// several filters match a file (nested dirs), the most nested one wins. 35pub struct VfsRoot(pub RawId);
38pub(crate) struct RootFilter { 36impl_arena_id!(VfsRoot);
37
38/// Describes the contents of a single source root.
39///
40/// `RootConfig` can be thought of as a glob pattern like `src/**.rs` whihc
41/// specifes the source root or as a function whihc takes a `PathBuf` and
42/// returns `true` iff path belongs to the source root
43pub(crate) struct RootConfig {
39 root: PathBuf, 44 root: PathBuf,
40 filter: fn(&Path, &RelativePath) -> bool,
41 excluded_dirs: Vec<PathBuf>, 45 excluded_dirs: Vec<PathBuf>,
42} 46}
43 47
44impl RootFilter { 48pub(crate) struct Roots {
45 fn new(root: PathBuf, excluded_dirs: Vec<PathBuf>) -> RootFilter { 49 roots: Arena<VfsRoot, Arc<RootConfig>>,
46 RootFilter { 50}
51
52impl std::ops::Deref for Roots {
53 type Target = Arena<VfsRoot, Arc<RootConfig>>;
54 fn deref(&self) -> &Self::Target {
55 &self.roots
56 }
57}
58
59impl RootConfig {
60 fn new(root: PathBuf, excluded_dirs: Vec<PathBuf>) -> RootConfig {
61 RootConfig {
47 root, 62 root,
48 filter: default_filter,
49 excluded_dirs, 63 excluded_dirs,
50 } 64 }
51 } 65 }
52 /// Check if this root can contain `path`. NB: even if this returns 66 /// Cheks if root contains a path and returns a root-relative path.
53 /// true, the `path` might actually be conained in some nested root. 67 pub(crate) fn contains(&self, path: &Path) -> Option<RelativePathBuf> {
54 pub(crate) fn can_contain(&self, path: &Path) -> Option<RelativePathBuf> { 68 // First, check excluded dirs
55 let rel_path = path.strip_prefix(&self.root).ok()?; 69 if self.excluded_dirs.iter().any(|it| path.starts_with(it)) {
56 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
57 if !(self.filter)(path, rel_path.as_relative_path()) {
58 return None; 70 return None;
59 } 71 }
60 Some(rel_path) 72 let rel_path = path.strip_prefix(&self.root).ok()?;
61 } 73 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
62
63 pub(crate) fn entry_filter<'a>(&'a self) -> impl FnMut(&DirEntry) -> bool + 'a {
64 move |entry: &DirEntry| {
65 if entry.file_type().is_dir() && self.excluded_dirs.iter().any(|it| it == entry.path())
66 {
67 // do not walk nested roots
68 false
69 } else {
70 self.can_contain(entry.path()).is_some()
71 }
72 }
73 }
74}
75 74
76pub(crate) fn default_filter(path: &Path, rel_path: &RelativePath) -> bool { 75 // Ignore some common directories.
77 if path.is_dir() { 76 //
77 // FIXME: don't hard-code, specify at source-root creation time using
78 // gitignore
78 for (i, c) in rel_path.components().enumerate() { 79 for (i, c) in rel_path.components().enumerate() {
79 if let Component::Normal(c) = c { 80 if let Component::Normal(c) = c {
80 // TODO hardcoded for now
81 if (i == 0 && c == "target") || c == ".git" || c == "node_modules" { 81 if (i == 0 && c == "target") || c == ".git" || c == "node_modules" {
82 return false; 82 return None;
83 } 83 }
84 } 84 }
85 } 85 }
86 true
87 } else {
88 rel_path.extension() == Some("rs")
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
93pub struct VfsRoot(pub RawId);
94impl_arena_id!(VfsRoot);
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
97pub struct VfsFile(pub RawId);
98impl_arena_id!(VfsFile);
99 86
100struct VfsFileData { 87 if path.is_file() && rel_path.extension() != Some("rs") {
101 root: VfsRoot, 88 return None;
102 path: RelativePathBuf, 89 }
103 is_overlayed: bool,
104 text: Arc<String>,
105}
106 90
107pub(crate) struct Roots { 91 Some(rel_path)
108 roots: Arena<VfsRoot, Arc<RootFilter>>, 92 }
109} 93}
110 94
111impl Roots { 95impl Roots {
@@ -120,59 +104,61 @@ impl Roots {
120 .map(|it| it.clone()) 104 .map(|it| it.clone())
121 .collect::<Vec<_>>(); 105 .collect::<Vec<_>>();
122 106
123 let root_filter = Arc::new(RootFilter::new(path.clone(), nested_roots)); 107 let config = Arc::new(RootConfig::new(path.clone(), nested_roots));
124 108
125 roots.alloc(root_filter.clone()); 109 roots.alloc(config.clone());
126 } 110 }
127 Roots { roots } 111 Roots { roots }
128 } 112 }
129 pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> { 113 pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> {
130 self.roots 114 self.roots
131 .iter() 115 .iter()
132 .find_map(|(root, data)| data.can_contain(path).map(|it| (root, it))) 116 .find_map(|(root, data)| data.contains(path).map(|it| (root, it)))
133 } 117 }
134} 118}
135 119
136impl Deref for Roots { 120#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
137 type Target = Arena<VfsRoot, Arc<RootFilter>>; 121pub struct VfsFile(pub RawId);
138 fn deref(&self) -> &Self::Target { 122impl_arena_id!(VfsFile);
139 &self.roots
140 }
141}
142 123
143impl DerefMut for Roots { 124struct VfsFileData {
144 fn deref_mut(&mut self) -> &mut Self::Target { 125 root: VfsRoot,
145 &mut self.roots 126 path: RelativePathBuf,
146 } 127 is_overlayed: bool,
128 text: Arc<String>,
147} 129}
148 130
149pub struct Vfs { 131pub struct Vfs {
150 roots: Arc<Roots>, 132 roots: Arc<Roots>,
151 files: Arena<VfsFile, VfsFileData>, 133 files: Arena<VfsFile, VfsFileData>,
152 root2files: FxHashMap<VfsRoot, FxHashSet<VfsFile>>, 134 root2files: ArenaMap<VfsRoot, FxHashSet<VfsFile>>,
153 pending_changes: Vec<VfsChange>, 135 pending_changes: Vec<VfsChange>,
154 worker: Worker, 136 worker: Worker,
155} 137}
156 138
157impl fmt::Debug for Vfs { 139impl fmt::Debug for Vfs {
158 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 140 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159 f.write_str("Vfs { ... }") 141 f.debug_struct("Vfs")
142 .field("n_roots", &self.roots.len())
143 .field("n_files", &self.files.len())
144 .field("n_pending_changes", &self.pending_changes.len())
145 .finish()
160 } 146 }
161} 147}
162 148
163impl Vfs { 149impl Vfs {
164 pub fn new(roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) { 150 pub fn new(roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) {
165 let roots = Arc::new(Roots::new(roots)); 151 let roots = Arc::new(Roots::new(roots));
166 let worker = io::Worker::start(roots.clone()); 152 let worker = io::Worker::start(Arc::clone(&roots));
167 let mut root2files = FxHashMap::default(); 153 let mut root2files = ArenaMap::default();
168 154
169 for (root, filter) in roots.iter() { 155 for (root, config) in roots.iter() {
170 root2files.insert(root, Default::default()); 156 root2files.insert(root, Default::default());
171 worker 157 worker
172 .sender() 158 .sender()
173 .send(io::Task::AddRoot { 159 .send(io::Task::AddRoot {
174 root, 160 root,
175 filter: filter.clone(), 161 config: Arc::clone(config),
176 }) 162 })
177 .unwrap(); 163 .unwrap();
178 } 164 }
@@ -242,7 +228,7 @@ impl Vfs {
242 let mut cur_files = Vec::new(); 228 let mut cur_files = Vec::new();
243 // While we were scanning the root in the backgound, a file might have 229 // While we were scanning the root in the backgound, a file might have
244 // been open in the editor, so we need to account for that. 230 // been open in the editor, so we need to account for that.
245 let exising = self.root2files[&root] 231 let exising = self.root2files[root]
246 .iter() 232 .iter()
247 .map(|&file| (self.files[file].path.clone(), file)) 233 .map(|&file| (self.files[file].path.clone(), file))
248 .collect::<FxHashMap<_, _>>(); 234 .collect::<FxHashMap<_, _>>();
@@ -384,7 +370,7 @@ impl Vfs {
384 is_overlayed, 370 is_overlayed,
385 }; 371 };
386 let file = self.files.alloc(data); 372 let file = self.files.alloc(data);
387 self.root2files.get_mut(&root).unwrap().insert(file); 373 self.root2files.get_mut(root).unwrap().insert(file);
388 file 374 file
389 } 375 }
390 376
@@ -399,7 +385,7 @@ impl Vfs {
399 self.files[file].text = Default::default(); 385 self.files[file].text = Default::default();
400 self.files[file].path = Default::default(); 386 self.files[file].path = Default::default();
401 let root = self.files[file].root; 387 let root = self.files[file].root;
402 let removed = self.root2files.get_mut(&root).unwrap().remove(&file); 388 let removed = self.root2files.get_mut(root).unwrap().remove(&file);
403 assert!(removed); 389 assert!(removed);
404 } 390 }
405 391
@@ -410,7 +396,7 @@ impl Vfs {
410 } 396 }
411 397
412 fn find_file(&self, root: VfsRoot, path: &RelativePath) -> Option<VfsFile> { 398 fn find_file(&self, root: VfsRoot, path: &RelativePath) -> Option<VfsFile> {
413 self.root2files[&root] 399 self.root2files[root]
414 .iter() 400 .iter()
415 .map(|&it| it) 401 .map(|&it| it)
416 .find(|&file| self.files[file].path == path) 402 .find(|&file| self.files[file].path == path)