aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_vfs/src/lib.rs108
-rw-r--r--crates/ra_vfs/src/roots.rs102
2 files changed, 112 insertions, 98 deletions
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs
index cfdc1275f..a740e82ef 100644
--- a/crates/ra_vfs/src/lib.rs
+++ b/crates/ra_vfs/src/lib.rs
@@ -15,10 +15,10 @@
15//! VFS is based on a concept of roots: a set of directories on the file system 15//! VFS is based on a concept of roots: a set of directories on the file system
16//! which are watched for changes. Typically, there will be a root for each 16//! which are watched for changes. Typically, there will be a root for each
17//! Cargo package. 17//! Cargo package.
18mod roots;
18mod io; 19mod io;
19 20
20use std::{ 21use std::{
21 cmp::Reverse,
22 fmt, fs, mem, 22 fmt, fs, mem,
23 path::{Path, PathBuf}, 23 path::{Path, PathBuf},
24 sync::Arc, 24 sync::Arc,
@@ -26,106 +26,18 @@ use std::{
26 26
27use crossbeam_channel::Receiver; 27use crossbeam_channel::Receiver;
28use ra_arena::{impl_arena_id, Arena, RawId, map::ArenaMap}; 28use ra_arena::{impl_arena_id, Arena, RawId, map::ArenaMap};
29use relative_path::{Component, RelativePath, RelativePathBuf}; 29use relative_path::{RelativePath, RelativePathBuf};
30use rustc_hash::{FxHashMap, FxHashSet}; 30use rustc_hash::{FxHashMap, FxHashSet};
31 31
32pub use crate::io::TaskResult as VfsTask; 32use crate::{
33use io::{TaskResult, Worker}; 33 io::{TaskResult, Worker},
34 34 roots::{RootConfig, Roots},
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 35};
36pub struct VfsRoot(pub RawId);
37impl_arena_id!(VfsRoot);
38
39/// Describes the contents of a single source root.
40///
41/// `RootConfig` can be thought of as a glob pattern like `src/**.rs` which
42/// specifies the source root or as a function which takes a `PathBuf` and
43/// returns `true` iff path belongs to the source root
44pub(crate) struct RootConfig {
45 root: PathBuf,
46 // result of `root.canonicalize()` if that differs from `root`; `None` otherwise.
47 canonical_root: Option<PathBuf>,
48 excluded_dirs: Vec<PathBuf>,
49}
50
51pub(crate) struct Roots {
52 roots: Arena<VfsRoot, Arc<RootConfig>>,
53}
54
55impl std::ops::Deref for Roots {
56 type Target = Arena<VfsRoot, Arc<RootConfig>>;
57 fn deref(&self) -> &Self::Target {
58 &self.roots
59 }
60}
61
62impl RootConfig {
63 fn new(root: PathBuf, excluded_dirs: Vec<PathBuf>) -> RootConfig {
64 let mut canonical_root = root.canonicalize().ok();
65 if Some(&root) == canonical_root.as_ref() {
66 canonical_root = None;
67 }
68 RootConfig { root, canonical_root, excluded_dirs }
69 }
70 /// Checks if root contains a path and returns a root-relative path.
71 pub(crate) fn contains(&self, path: &Path) -> Option<RelativePathBuf> {
72 // First, check excluded dirs
73 if self.excluded_dirs.iter().any(|it| path.starts_with(it)) {
74 return None;
75 }
76 let rel_path = path
77 .strip_prefix(&self.root)
78 .or_else(|err_payload| {
79 self.canonical_root
80 .as_ref()
81 .map_or(Err(err_payload), |canonical_root| path.strip_prefix(canonical_root))
82 })
83 .ok()?;
84 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
85
86 // Ignore some common directories.
87 //
88 // FIXME: don't hard-code, specify at source-root creation time using
89 // gitignore
90 for (i, c) in rel_path.components().enumerate() {
91 if let Component::Normal(c) = c {
92 if (i == 0 && c == "target") || c == ".git" || c == "node_modules" {
93 return None;
94 }
95 }
96 }
97
98 if path.is_file() && rel_path.extension() != Some("rs") {
99 return None;
100 }
101
102 Some(rel_path)
103 }
104}
105 36
106impl Roots { 37pub use crate::{
107 pub(crate) fn new(mut paths: Vec<PathBuf>) -> Roots { 38 io::TaskResult as VfsTask,
108 let mut roots = Arena::default(); 39 roots::VfsRoot,
109 // A hack to make nesting work. 40};
110 paths.sort_by_key(|it| Reverse(it.as_os_str().len()));
111 paths.dedup();
112 for (i, path) in paths.iter().enumerate() {
113 let nested_roots = paths[..i]
114 .iter()
115 .filter(|it| it.starts_with(path))
116 .map(|it| it.clone())
117 .collect::<Vec<_>>();
118
119 let config = Arc::new(RootConfig::new(path.clone(), nested_roots));
120
121 roots.alloc(config.clone());
122 }
123 Roots { roots }
124 }
125 pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> {
126 self.roots.iter().find_map(|(root, data)| data.contains(path).map(|it| (root, it)))
127 }
128}
129 41
130#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
131pub struct VfsFile(pub RawId); 43pub struct VfsFile(pub RawId);
diff --git a/crates/ra_vfs/src/roots.rs b/crates/ra_vfs/src/roots.rs
new file mode 100644
index 000000000..564e12239
--- /dev/null
+++ b/crates/ra_vfs/src/roots.rs
@@ -0,0 +1,102 @@
1use std::{
2 sync::Arc,
3 path::{Path, PathBuf},
4};
5
6use relative_path::RelativePathBuf;
7use ra_arena::{impl_arena_id, Arena, RawId};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct VfsRoot(pub RawId);
11impl_arena_id!(VfsRoot);
12
13/// Describes the contents of a single source root.
14///
15/// `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
17/// returns `true` iff path belongs to the source root
18pub(crate) struct RootConfig {
19 pub(crate) root: PathBuf,
20 // result of `root.canonicalize()` if that differs from `root`; `None` otherwise.
21 canonical_root: Option<PathBuf>,
22 excluded_dirs: Vec<PathBuf>,
23}
24
25pub(crate) struct Roots {
26 roots: Arena<VfsRoot, Arc<RootConfig>>,
27}
28
29impl std::ops::Deref for Roots {
30 type Target = Arena<VfsRoot, Arc<RootConfig>>;
31 fn deref(&self) -> &Self::Target {
32 &self.roots
33 }
34}
35
36impl RootConfig {
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 }
42 RootConfig { root, canonical_root, excluded_dirs }
43 }
44 /// Checks if root contains a path and returns a root-relative path.
45 pub(crate) fn contains(&self, path: &Path) -> Option<RelativePathBuf> {
46 // First, check excluded dirs
47 if self.excluded_dirs.iter().any(|it| path.starts_with(it)) {
48 return None;
49 }
50 let rel_path = path
51 .strip_prefix(&self.root)
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
60 // Ignore some common directories.
61 //
62 // FIXME: don't hard-code, specify at source-root creation time using
63 // gitignore
64 for (i, c) in rel_path.components().enumerate() {
65 if let relative_path::Component::Normal(c) = c {
66 if (i == 0 && c == "target") || c == ".git" || c == "node_modules" {
67 return None;
68 }
69 }
70 }
71
72 if path.is_file() && rel_path.extension() != Some("rs") {
73 return None;
74 }
75
76 Some(rel_path)
77 }
78}
79
80impl Roots {
81 pub(crate) fn new(mut paths: Vec<PathBuf>) -> Roots {
82 let mut roots = Arena::default();
83 // A hack to make nesting work.
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}