diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-26 15:13:44 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-26 15:13:44 +0000 |
commit | a8d32c4d1ae4b3e4276f7a97b6c6e5f95f91e67a (patch) | |
tree | 9e56b095a073ff3c82322302356f4d096a70afcd /crates/ra_vfs/src/lib.rs | |
parent | 5af7b2f4af51291fa4a0549c549796ba0520927b (diff) | |
parent | 9f16892b94817d144f37dfe0081b39aacec65635 (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.rs | 146 |
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; | |||
18 | use std::{ | 18 | use 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 | ||
27 | use crossbeam_channel::Receiver; | 26 | use crossbeam_channel::Receiver; |
28 | use ra_arena::{impl_arena_id, Arena, RawId}; | 27 | use ra_arena::{impl_arena_id, Arena, RawId, map::ArenaMap}; |
29 | use relative_path::{Component, RelativePath, RelativePathBuf}; | 28 | use relative_path::{Component, RelativePath, RelativePathBuf}; |
30 | use rustc_hash::{FxHashMap, FxHashSet}; | 29 | use rustc_hash::{FxHashMap, FxHashSet}; |
31 | use walkdir::DirEntry; | ||
32 | 30 | ||
33 | pub use crate::io::TaskResult as VfsTask; | 31 | pub use crate::io::TaskResult as VfsTask; |
34 | use io::{TaskResult, Worker}; | 32 | use 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. | 35 | pub struct VfsRoot(pub RawId); |
38 | pub(crate) struct RootFilter { | 36 | impl_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 | ||
43 | pub(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 | ||
44 | impl RootFilter { | 48 | pub(crate) struct Roots { |
45 | fn new(root: PathBuf, excluded_dirs: Vec<PathBuf>) -> RootFilter { | 49 | roots: Arena<VfsRoot, Arc<RootConfig>>, |
46 | RootFilter { | 50 | } |
51 | |||
52 | impl std::ops::Deref for Roots { | ||
53 | type Target = Arena<VfsRoot, Arc<RootConfig>>; | ||
54 | fn deref(&self) -> &Self::Target { | ||
55 | &self.roots | ||
56 | } | ||
57 | } | ||
58 | |||
59 | impl 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 | ||
76 | pub(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)] | ||
93 | pub struct VfsRoot(pub RawId); | ||
94 | impl_arena_id!(VfsRoot); | ||
95 | |||
96 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
97 | pub struct VfsFile(pub RawId); | ||
98 | impl_arena_id!(VfsFile); | ||
99 | 86 | ||
100 | struct 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 | ||
107 | pub(crate) struct Roots { | 91 | Some(rel_path) |
108 | roots: Arena<VfsRoot, Arc<RootFilter>>, | 92 | } |
109 | } | 93 | } |
110 | 94 | ||
111 | impl Roots { | 95 | impl 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 | ||
136 | impl Deref for Roots { | 120 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
137 | type Target = Arena<VfsRoot, Arc<RootFilter>>; | 121 | pub struct VfsFile(pub RawId); |
138 | fn deref(&self) -> &Self::Target { | 122 | impl_arena_id!(VfsFile); |
139 | &self.roots | ||
140 | } | ||
141 | } | ||
142 | 123 | ||
143 | impl DerefMut for Roots { | 124 | struct 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 | ||
149 | pub struct Vfs { | 131 | pub 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 | ||
157 | impl fmt::Debug for Vfs { | 139 | impl 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 | ||
163 | impl Vfs { | 149 | impl 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) |