diff options
author | Aleksey Kladov <[email protected]> | 2019-02-17 17:22:46 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-02-17 17:22:46 +0000 |
commit | 162dea51bfa6b7f6d64d138a84c16c96495a0fcd (patch) | |
tree | f321f11cd0d9c88c8c684f65312041d8db5598d2 /crates/ra_vfs/src/roots.rs | |
parent | 6b5d90972ae2f288ae7cf57e209c0d5d8c7a1fd2 (diff) |
hide root config
Diffstat (limited to 'crates/ra_vfs/src/roots.rs')
-rw-r--r-- | crates/ra_vfs/src/roots.rs | 125 |
1 files changed, 66 insertions, 59 deletions
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 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | iter, | ||
2 | sync::Arc, | 3 | sync::Arc, |
3 | path::{Path, PathBuf}, | 4 | path::{Path, PathBuf}, |
4 | }; | 5 | }; |
5 | 6 | ||
6 | use relative_path::RelativePathBuf; | 7 | use relative_path::{ RelativePath, RelativePathBuf}; |
7 | use ra_arena::{impl_arena_id, Arena, RawId}; | 8 | use 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)] |
10 | pub struct VfsRoot(pub RawId); | 12 | pub struct VfsRoot(pub RawId); |
11 | impl_arena_id!(VfsRoot); | 13 | impl_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 |
18 | pub(crate) struct RootConfig { | 20 | struct 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 | ||
25 | pub(crate) struct Roots { | 27 | pub(crate) struct Roots { |
26 | roots: Arena<VfsRoot, Arc<RootConfig>>, | 28 | roots: Arena<VfsRoot, Arc<RootData>>, |
27 | } | 29 | } |
28 | 30 | ||
29 | impl std::ops::Deref for Roots { | 31 | impl 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 | ||
36 | impl 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 | |||
73 | impl 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 | ||
80 | impl Roots { | 105 | fn 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 | } |