aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_vfs/src/io.rs31
-rw-r--r--crates/ra_vfs/src/lib.rs14
-rw-r--r--crates/ra_vfs/src/roots.rs125
3 files changed, 89 insertions, 81 deletions
diff --git a/crates/ra_vfs/src/io.rs b/crates/ra_vfs/src/io.rs
index f64b4c532..0cffc03f3 100644
--- a/crates/ra_vfs/src/io.rs
+++ b/crates/ra_vfs/src/io.rs
@@ -9,10 +9,10 @@ use relative_path::RelativePathBuf;
9use walkdir::WalkDir; 9use walkdir::WalkDir;
10use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as _Watcher}; 10use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as _Watcher};
11 11
12use crate::{RootConfig, Roots, VfsRoot}; 12use crate::{Roots, VfsRoot};
13 13
14pub(crate) enum Task { 14pub(crate) enum Task {
15 AddRoot { root: VfsRoot, config: Arc<RootConfig> }, 15 AddRoot { root: VfsRoot },
16} 16}
17 17
18/// `TaskResult` transfers files read on the IO thread to the VFS on the main 18/// `TaskResult` transfers files read on the IO thread to the VFS on the main
@@ -98,8 +98,8 @@ pub(crate) fn start(roots: Arc<Roots>) -> Worker {
98 drop(input_receiver); 98 drop(input_receiver);
99 break 99 break
100 }, 100 },
101 Ok(Task::AddRoot { root, config }) => { 101 Ok(Task::AddRoot { root }) => {
102 watch_root(watcher.as_mut(), &output_sender, root, Arc::clone(&config)); 102 watch_root(watcher.as_mut(), &output_sender, &*roots, root);
103 } 103 }
104 }, 104 },
105 // Watcher send us changes. If **this** channel is 105 // Watcher send us changes. If **this** channel is
@@ -123,20 +123,21 @@ pub(crate) fn start(roots: Arc<Roots>) -> Worker {
123fn watch_root( 123fn watch_root(
124 watcher: Option<&mut RecommendedWatcher>, 124 watcher: Option<&mut RecommendedWatcher>,
125 sender: &Sender<TaskResult>, 125 sender: &Sender<TaskResult>,
126 roots: &Roots,
126 root: VfsRoot, 127 root: VfsRoot,
127 config: Arc<RootConfig>,
128) { 128) {
129 log::debug!("loading {} ...", config.root.as_path().display()); 129 let root_path = roots.path(root);
130 let files = watch_recursive(watcher, config.root.as_path(), &*config) 130 log::debug!("loading {} ...", root_path.display());
131 let files = watch_recursive(watcher, root_path, roots, root)
131 .into_iter() 132 .into_iter()
132 .filter_map(|path| { 133 .filter_map(|path| {
133 let abs_path = path.to_path(&config.root); 134 let abs_path = path.to_path(&root_path);
134 let text = read_to_string(&abs_path)?; 135 let text = read_to_string(&abs_path)?;
135 Some((path, text)) 136 Some((path, text))
136 }) 137 })
137 .collect(); 138 .collect();
138 sender.send(TaskResult::BulkLoadRoot { root, files }).unwrap(); 139 sender.send(TaskResult::BulkLoadRoot { root, files }).unwrap();
139 log::debug!("... loaded {}", config.root.as_path().display()); 140 log::debug!("... loaded {}", root_path.display());
140} 141}
141 142
142fn convert_notify_event(event: DebouncedEvent, sender: &Sender<(PathBuf, ChangeKind)>) { 143fn convert_notify_event(event: DebouncedEvent, sender: &Sender<(PathBuf, ChangeKind)>) {
@@ -181,19 +182,18 @@ fn handle_change(
181 None => return, 182 None => return,
182 Some(it) => it, 183 Some(it) => it,
183 }; 184 };
184 let config = &roots[root];
185 match kind { 185 match kind {
186 ChangeKind::Create => { 186 ChangeKind::Create => {
187 let mut paths = Vec::new(); 187 let mut paths = Vec::new();
188 if path.is_dir() { 188 if path.is_dir() {
189 paths.extend(watch_recursive(watcher, &path, &config)); 189 paths.extend(watch_recursive(watcher, &path, roots, root));
190 } else { 190 } else {
191 paths.push(rel_path); 191 paths.push(rel_path);
192 } 192 }
193 paths 193 paths
194 .into_iter() 194 .into_iter()
195 .try_for_each(|rel_path| { 195 .try_for_each(|rel_path| {
196 let abs_path = rel_path.to_path(&config.root); 196 let abs_path = rel_path.to_path(&roots.path(root));
197 let text = read_to_string(&abs_path); 197 let text = read_to_string(&abs_path);
198 sender.send(TaskResult::SingleFile { root, path: rel_path, text }) 198 sender.send(TaskResult::SingleFile { root, path: rel_path, text })
199 }) 199 })
@@ -209,12 +209,13 @@ fn handle_change(
209fn watch_recursive( 209fn watch_recursive(
210 mut watcher: Option<&mut RecommendedWatcher>, 210 mut watcher: Option<&mut RecommendedWatcher>,
211 dir: &Path, 211 dir: &Path,
212 config: &RootConfig, 212 roots: &Roots,
213 root: VfsRoot,
213) -> Vec<RelativePathBuf> { 214) -> Vec<RelativePathBuf> {
214 let mut files = Vec::new(); 215 let mut files = Vec::new();
215 for entry in WalkDir::new(dir) 216 for entry in WalkDir::new(dir)
216 .into_iter() 217 .into_iter()
217 .filter_entry(|it| config.contains(it.path()).is_some()) 218 .filter_entry(|it| roots.contains(root, it.path()).is_some())
218 .filter_map(|it| it.map_err(|e| log::warn!("watcher error: {}", e)).ok()) 219 .filter_map(|it| it.map_err(|e| log::warn!("watcher error: {}", e)).ok())
219 { 220 {
220 if entry.file_type().is_dir() { 221 if entry.file_type().is_dir() {
@@ -222,7 +223,7 @@ fn watch_recursive(
222 watch_one(watcher, entry.path()); 223 watch_one(watcher, entry.path());
223 } 224 }
224 } else { 225 } else {
225 let path = config.contains(entry.path()).unwrap(); 226 let path = roots.contains(root, entry.path()).unwrap();
226 files.push(path.to_owned()); 227 files.push(path.to_owned());
227 } 228 }
228 } 229 }
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs
index a740e82ef..c78096ee1 100644
--- a/crates/ra_vfs/src/lib.rs
+++ b/crates/ra_vfs/src/lib.rs
@@ -31,7 +31,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
31 31
32use crate::{ 32use crate::{
33 io::{TaskResult, Worker}, 33 io::{TaskResult, Worker},
34 roots::{RootConfig, Roots}, 34 roots::Roots,
35}; 35};
36 36
37pub use crate::{ 37pub use crate::{
@@ -74,18 +74,18 @@ impl Vfs {
74 let worker = io::start(Arc::clone(&roots)); 74 let worker = io::start(Arc::clone(&roots));
75 let mut root2files = ArenaMap::default(); 75 let mut root2files = ArenaMap::default();
76 76
77 for (root, config) in roots.iter() { 77 for root in roots.iter() {
78 root2files.insert(root, Default::default()); 78 root2files.insert(root, Default::default());
79 worker.sender().send(io::Task::AddRoot { root, config: Arc::clone(config) }).unwrap(); 79 worker.sender().send(io::Task::AddRoot { root }).unwrap();
80 } 80 }
81 let res = 81 let res =
82 Vfs { roots, files: Arena::default(), root2files, worker, pending_changes: Vec::new() }; 82 Vfs { roots, files: Arena::default(), root2files, worker, pending_changes: Vec::new() };
83 let vfs_roots = res.roots.iter().map(|(id, _)| id).collect(); 83 let vfs_roots = res.roots.iter().collect();
84 (res, vfs_roots) 84 (res, vfs_roots)
85 } 85 }
86 86
87 pub fn root2path(&self, root: VfsRoot) -> PathBuf { 87 pub fn root2path(&self, root: VfsRoot) -> PathBuf {
88 self.roots[root].root.clone() 88 self.roots.path(root).to_path_buf()
89 } 89 }
90 90
91 pub fn path2file(&self, path: &Path) -> Option<VfsFile> { 91 pub fn path2file(&self, path: &Path) -> Option<VfsFile> {
@@ -97,7 +97,7 @@ impl Vfs {
97 97
98 pub fn file2path(&self, file: VfsFile) -> PathBuf { 98 pub fn file2path(&self, file: VfsFile) -> PathBuf {
99 let rel_path = &self.files[file].path; 99 let rel_path = &self.files[file].path;
100 let root_path = &self.roots[self.files[file].root].root; 100 let root_path = &self.roots.path(self.files[file].root);
101 rel_path.to_path(root_path) 101 rel_path.to_path(root_path)
102 } 102 }
103 103
@@ -232,7 +232,7 @@ impl Vfs {
232 pub fn remove_file_overlay(&mut self, path: &Path) -> Option<VfsFile> { 232 pub fn remove_file_overlay(&mut self, path: &Path) -> Option<VfsFile> {
233 if let Some((root, path, file)) = self.find_root(path) { 233 if let Some((root, path, file)) = self.find_root(path) {
234 let file = file.expect("can't remove a file which wasn't added"); 234 let file = file.expect("can't remove a file which wasn't added");
235 let full_path = path.to_path(&self.roots[root].root); 235 let full_path = path.to_path(&self.roots.path(root));
236 if let Ok(text) = fs::read_to_string(&full_path) { 236 if let Ok(text) = fs::read_to_string(&full_path) {
237 self.do_change_file(file, text, true); 237 self.do_change_file(file, text, true);
238 } else { 238 } else {
diff --git a/crates/ra_vfs/src/roots.rs b/crates/ra_vfs/src/roots.rs
index 564e12239..5e2776a35 100644
--- a/crates/ra_vfs/src/roots.rs
+++ b/crates/ra_vfs/src/roots.rs
@@ -1,11 +1,13 @@
1use std::{ 1use std::{
2 iter,
2 sync::Arc, 3 sync::Arc,
3 path::{Path, PathBuf}, 4 path::{Path, PathBuf},
4}; 5};
5 6
6use relative_path::RelativePathBuf; 7use relative_path::{ RelativePath, RelativePathBuf};
7use ra_arena::{impl_arena_id, Arena, RawId}; 8use ra_arena::{impl_arena_id, Arena, RawId};
8 9
10/// VfsRoot identifies a watched directory on the file system.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct VfsRoot(pub RawId); 12pub struct VfsRoot(pub RawId);
11impl_arena_id!(VfsRoot); 13impl_arena_id!(VfsRoot);
@@ -15,88 +17,93 @@ impl_arena_id!(VfsRoot);
15/// `RootConfig` can be thought of as a glob pattern like `src/**.rs` which 17/// `RootConfig` can be thought of as a glob pattern like `src/**.rs` which
16/// specifies the source root or as a function which takes a `PathBuf` and 18/// specifies the source root or as a function which takes a `PathBuf` and
17/// returns `true` iff path belongs to the source root 19/// returns `true` iff path belongs to the source root
18pub(crate) struct RootConfig { 20struct RootData {
19 pub(crate) root: PathBuf, 21 path: PathBuf,
20 // result of `root.canonicalize()` if that differs from `root`; `None` otherwise. 22 // result of `root.canonicalize()` if that differs from `root`; `None` otherwise.
21 canonical_root: Option<PathBuf>, 23 canonical_path: Option<PathBuf>,
22 excluded_dirs: Vec<PathBuf>, 24 excluded_dirs: Vec<RelativePathBuf>,
23} 25}
24 26
25pub(crate) struct Roots { 27pub(crate) struct Roots {
26 roots: Arena<VfsRoot, Arc<RootConfig>>, 28 roots: Arena<VfsRoot, Arc<RootData>>,
27} 29}
28 30
29impl std::ops::Deref for Roots { 31impl Roots {
30 type Target = Arena<VfsRoot, Arc<RootConfig>>; 32 pub(crate) fn new(mut paths: Vec<PathBuf>) -> Roots {
31 fn deref(&self) -> &Self::Target { 33 let mut roots = Arena::default();
32 &self.roots 34 // A hack to make nesting work.
33 } 35 paths.sort_by_key(|it| std::cmp::Reverse(it.as_os_str().len()));
34} 36 paths.dedup();
37 for (i, path) in paths.iter().enumerate() {
38 let nested_roots =
39 paths[..i].iter().filter_map(|it| rel_path(path, it)).collect::<Vec<_>>();
40
41 let config = Arc::new(RootData::new(path.clone(), nested_roots));
35 42
36impl RootConfig { 43 roots.alloc(config.clone());
37 fn new(root: PathBuf, excluded_dirs: Vec<PathBuf>) -> RootConfig {
38 let mut canonical_root = root.canonicalize().ok();
39 if Some(&root) == canonical_root.as_ref() {
40 canonical_root = None;
41 } 44 }
42 RootConfig { root, canonical_root, excluded_dirs } 45 Roots { roots }
46 }
47 pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> {
48 self.iter().find_map(|root| {
49 let rel_path = self.contains(root, path)?;
50 Some((root, rel_path))
51 })
52 }
53 pub(crate) fn len(&self) -> usize {
54 self.roots.len()
55 }
56 pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = VfsRoot> + 'a {
57 self.roots.iter().map(|(id, _)| id)
58 }
59 pub(crate) fn path(&self, root: VfsRoot) -> &Path {
60 self.roots[root].path.as_path()
43 } 61 }
44 /// Checks if root contains a path and returns a root-relative path. 62 /// Checks if root contains a path and returns a root-relative path.
45 pub(crate) fn contains(&self, path: &Path) -> Option<RelativePathBuf> { 63 pub(crate) fn contains(&self, root: VfsRoot, path: &Path) -> Option<RelativePathBuf> {
46 // First, check excluded dirs 64 let data = &self.roots[root];
47 if self.excluded_dirs.iter().any(|it| path.starts_with(it)) { 65 iter::once(&data.path)
48 return None; 66 .chain(data.canonical_path.as_ref().into_iter())
67 .find_map(|base| rel_path(base, path))
68 .filter(|path| !data.excluded_dirs.contains(path))
69 .filter(|path| !data.is_excluded(path))
70 }
71}
72
73impl RootData {
74 fn new(path: PathBuf, excluded_dirs: Vec<RelativePathBuf>) -> RootData {
75 let mut canonical_path = path.canonicalize().ok();
76 if Some(&path) == canonical_path.as_ref() {
77 canonical_path = None;
49 } 78 }
50 let rel_path = path 79 RootData { path, canonical_path, excluded_dirs }
51 .strip_prefix(&self.root) 80 }
52 .or_else(|err_payload| {
53 self.canonical_root
54 .as_ref()
55 .map_or(Err(err_payload), |canonical_root| path.strip_prefix(canonical_root))
56 })
57 .ok()?;
58 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
59 81
82 fn is_excluded(&self, path: &RelativePath) -> bool {
83 if self.excluded_dirs.iter().any(|it| it == path) {
84 return true;
85 }
60 // Ignore some common directories. 86 // Ignore some common directories.
61 // 87 //
62 // FIXME: don't hard-code, specify at source-root creation time using 88 // FIXME: don't hard-code, specify at source-root creation time using
63 // gitignore 89 // gitignore
64 for (i, c) in rel_path.components().enumerate() { 90 for (i, c) in path.components().enumerate() {
65 if let relative_path::Component::Normal(c) = c { 91 if let relative_path::Component::Normal(c) = c {
66 if (i == 0 && c == "target") || c == ".git" || c == "node_modules" { 92 if (i == 0 && c == "target") || c == ".git" || c == "node_modules" {
67 return None; 93 return true;
68 } 94 }
69 } 95 }
70 } 96 }
71 97
72 if path.is_file() && rel_path.extension() != Some("rs") { 98 match path.extension() {
73 return None; 99 None | Some("rs") => false,
100 _ => true,
74 } 101 }
75
76 Some(rel_path)
77 } 102 }
78} 103}
79 104
80impl Roots { 105fn rel_path(base: &Path, path: &Path) -> Option<RelativePathBuf> {
81 pub(crate) fn new(mut paths: Vec<PathBuf>) -> Roots { 106 let path = path.strip_prefix(base).ok()?;
82 let mut roots = Arena::default(); 107 let path = RelativePathBuf::from_path(path).unwrap();
83 // A hack to make nesting work. 108 Some(path)
84 paths.sort_by_key(|it| std::cmp::Reverse(it.as_os_str().len()));
85 paths.dedup();
86 for (i, path) in paths.iter().enumerate() {
87 let nested_roots = paths[..i]
88 .iter()
89 .filter(|it| it.starts_with(path))
90 .map(|it| it.clone())
91 .collect::<Vec<_>>();
92
93 let config = Arc::new(RootConfig::new(path.clone(), nested_roots));
94
95 roots.alloc(config.clone());
96 }
97 Roots { roots }
98 }
99 pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> {
100 self.roots.iter().find_map(|(root, data)| data.contains(path).map(|it| (root, it)))
101 }
102} 109}