diff options
Diffstat (limited to 'crates/vfs/src')
-rw-r--r-- | crates/vfs/src/file_set.rs | 39 | ||||
-rw-r--r-- | crates/vfs/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/vfs/src/loader.rs | 26 | ||||
-rw-r--r-- | crates/vfs/src/vfs_path.rs | 76 | ||||
-rw-r--r-- | crates/vfs/src/walkdir_loader.rs | 108 |
5 files changed, 112 insertions, 141 deletions
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index 7dc721f7e..d0ddeafe7 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs | |||
@@ -2,9 +2,8 @@ | |||
2 | //! | 2 | //! |
3 | //! Files which do not belong to any explicitly configured `FileSet` belong to | 3 | //! Files which do not belong to any explicitly configured `FileSet` belong to |
4 | //! the default `FileSet`. | 4 | //! the default `FileSet`. |
5 | use std::{cmp, fmt, iter}; | 5 | use std::{fmt, iter}; |
6 | 6 | ||
7 | use paths::AbsPathBuf; | ||
8 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::FxHashMap; |
9 | 8 | ||
10 | use crate::{FileId, Vfs, VfsPath}; | 9 | use crate::{FileId, Vfs, VfsPath}; |
@@ -19,7 +18,7 @@ impl FileSet { | |||
19 | pub fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { | 18 | pub fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { |
20 | let mut base = self.paths[&anchor].clone(); | 19 | let mut base = self.paths[&anchor].clone(); |
21 | base.pop(); | 20 | base.pop(); |
22 | let path = base.join(path); | 21 | let path = base.join(path)?; |
23 | let res = self.files.get(&path).copied(); | 22 | let res = self.files.get(&path).copied(); |
24 | res | 23 | res |
25 | } | 24 | } |
@@ -41,7 +40,13 @@ impl fmt::Debug for FileSet { | |||
41 | #[derive(Debug)] | 40 | #[derive(Debug)] |
42 | pub struct FileSetConfig { | 41 | pub struct FileSetConfig { |
43 | n_file_sets: usize, | 42 | n_file_sets: usize, |
44 | roots: Vec<(AbsPathBuf, usize)>, | 43 | roots: Vec<(VfsPath, usize)>, |
44 | } | ||
45 | |||
46 | impl Default for FileSetConfig { | ||
47 | fn default() -> Self { | ||
48 | FileSetConfig::builder().build() | ||
49 | } | ||
45 | } | 50 | } |
46 | 51 | ||
47 | impl FileSetConfig { | 52 | impl FileSetConfig { |
@@ -60,19 +65,20 @@ impl FileSetConfig { | |||
60 | self.n_file_sets | 65 | self.n_file_sets |
61 | } | 66 | } |
62 | fn classify(&self, path: &VfsPath) -> usize { | 67 | fn classify(&self, path: &VfsPath) -> usize { |
63 | for (root, idx) in self.roots.iter() { | 68 | let idx = match self.roots.binary_search_by(|(p, _)| p.cmp(path)) { |
64 | if let Some(path) = path.as_path() { | 69 | Ok(it) => it, |
65 | if path.starts_with(root) { | 70 | Err(it) => it.saturating_sub(1), |
66 | return *idx; | 71 | }; |
67 | } | 72 | if path.starts_with(&self.roots[idx].0) { |
68 | } | 73 | self.roots[idx].1 |
74 | } else { | ||
75 | self.len() - 1 | ||
69 | } | 76 | } |
70 | self.len() - 1 | ||
71 | } | 77 | } |
72 | } | 78 | } |
73 | 79 | ||
74 | pub struct FileSetConfigBuilder { | 80 | pub struct FileSetConfigBuilder { |
75 | roots: Vec<Vec<AbsPathBuf>>, | 81 | roots: Vec<Vec<VfsPath>>, |
76 | } | 82 | } |
77 | 83 | ||
78 | impl Default for FileSetConfigBuilder { | 84 | impl Default for FileSetConfigBuilder { |
@@ -82,18 +88,21 @@ impl Default for FileSetConfigBuilder { | |||
82 | } | 88 | } |
83 | 89 | ||
84 | impl FileSetConfigBuilder { | 90 | impl FileSetConfigBuilder { |
85 | pub fn add_file_set(&mut self, roots: Vec<AbsPathBuf>) { | 91 | pub fn len(&self) -> usize { |
92 | self.roots.len() | ||
93 | } | ||
94 | pub fn add_file_set(&mut self, roots: Vec<VfsPath>) { | ||
86 | self.roots.push(roots) | 95 | self.roots.push(roots) |
87 | } | 96 | } |
88 | pub fn build(self) -> FileSetConfig { | 97 | pub fn build(self) -> FileSetConfig { |
89 | let n_file_sets = self.roots.len() + 1; | 98 | let n_file_sets = self.roots.len() + 1; |
90 | let mut roots: Vec<(AbsPathBuf, usize)> = self | 99 | let mut roots: Vec<(VfsPath, usize)> = self |
91 | .roots | 100 | .roots |
92 | .into_iter() | 101 | .into_iter() |
93 | .enumerate() | 102 | .enumerate() |
94 | .flat_map(|(i, paths)| paths.into_iter().zip(iter::repeat(i))) | 103 | .flat_map(|(i, paths)| paths.into_iter().zip(iter::repeat(i))) |
95 | .collect(); | 104 | .collect(); |
96 | roots.sort_by_key(|(path, _)| cmp::Reverse(path.to_string_lossy().len())); | 105 | roots.sort(); |
97 | FileSetConfig { n_file_sets, roots } | 106 | FileSetConfig { n_file_sets, roots } |
98 | } | 107 | } |
99 | } | 108 | } |
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index 75ce61cf9..024e58018 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs | |||
@@ -38,7 +38,6 @@ mod vfs_path; | |||
38 | mod path_interner; | 38 | mod path_interner; |
39 | pub mod file_set; | 39 | pub mod file_set; |
40 | pub mod loader; | 40 | pub mod loader; |
41 | pub mod walkdir_loader; | ||
42 | 41 | ||
43 | use std::{fmt, mem}; | 42 | use std::{fmt, mem}; |
44 | 43 | ||
@@ -79,6 +78,9 @@ pub enum ChangeKind { | |||
79 | } | 78 | } |
80 | 79 | ||
81 | impl Vfs { | 80 | impl Vfs { |
81 | pub fn len(&self) -> usize { | ||
82 | self.data.len() | ||
83 | } | ||
82 | pub fn file_id(&self, path: &VfsPath) -> Option<FileId> { | 84 | pub fn file_id(&self, path: &VfsPath) -> Option<FileId> { |
83 | self.interner.get(path).filter(|&it| self.get(it).is_some()) | 85 | self.interner.get(path).filter(|&it| self.get(it).is_some()) |
84 | } | 86 | } |
diff --git a/crates/vfs/src/loader.rs b/crates/vfs/src/loader.rs index 5a0ca68f3..6de2e5b3f 100644 --- a/crates/vfs/src/loader.rs +++ b/crates/vfs/src/loader.rs | |||
@@ -1,21 +1,22 @@ | |||
1 | //! Object safe interface for file watching and reading. | 1 | //! Object safe interface for file watching and reading. |
2 | use std::fmt; | 2 | use std::fmt; |
3 | 3 | ||
4 | use paths::AbsPathBuf; | 4 | use paths::{AbsPath, AbsPathBuf}; |
5 | 5 | ||
6 | #[derive(Debug)] | ||
6 | pub enum Entry { | 7 | pub enum Entry { |
7 | Files(Vec<AbsPathBuf>), | 8 | Files(Vec<AbsPathBuf>), |
8 | Directory { path: AbsPathBuf, globs: Vec<String> }, | 9 | Directory { path: AbsPathBuf, include: Vec<String> }, |
9 | } | 10 | } |
10 | 11 | ||
12 | #[derive(Debug)] | ||
11 | pub struct Config { | 13 | pub struct Config { |
12 | pub load: Vec<Entry>, | 14 | pub load: Vec<Entry>, |
13 | pub watch: Vec<usize>, | 15 | pub watch: Vec<usize>, |
14 | } | 16 | } |
15 | 17 | ||
16 | pub enum Message { | 18 | pub enum Message { |
17 | DidSwitchConfig { n_entries: usize }, | 19 | Progress { n_total: usize, n_done: usize }, |
18 | DidLoadAllEntries, | ||
19 | Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> }, | 20 | Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> }, |
20 | } | 21 | } |
21 | 22 | ||
@@ -27,20 +28,20 @@ pub trait Handle: fmt::Debug { | |||
27 | Self: Sized; | 28 | Self: Sized; |
28 | fn set_config(&mut self, config: Config); | 29 | fn set_config(&mut self, config: Config); |
29 | fn invalidate(&mut self, path: AbsPathBuf); | 30 | fn invalidate(&mut self, path: AbsPathBuf); |
30 | fn load_sync(&mut self, path: &AbsPathBuf) -> Option<Vec<u8>>; | 31 | fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>; |
31 | } | 32 | } |
32 | 33 | ||
33 | impl Entry { | 34 | impl Entry { |
34 | pub fn rs_files_recursively(base: AbsPathBuf) -> Entry { | 35 | pub fn rs_files_recursively(base: AbsPathBuf) -> Entry { |
35 | Entry::Directory { path: base, globs: globs(&["*.rs"]) } | 36 | Entry::Directory { path: base, include: globs(&["*.rs", "!/.git/"]) } |
36 | } | 37 | } |
37 | pub fn local_cargo_package(base: AbsPathBuf) -> Entry { | 38 | pub fn local_cargo_package(base: AbsPathBuf) -> Entry { |
38 | Entry::Directory { path: base, globs: globs(&["*.rs", "!/target/"]) } | 39 | Entry::Directory { path: base, include: globs(&["*.rs", "!/target/", "!/.git/"]) } |
39 | } | 40 | } |
40 | pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry { | 41 | pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry { |
41 | Entry::Directory { | 42 | Entry::Directory { |
42 | path: base, | 43 | path: base, |
43 | globs: globs(&["*.rs", "!/tests/", "!/examples/", "!/benches/"]), | 44 | include: globs(&["*.rs", "!/tests/", "!/examples/", "!/benches/", "!/.git/"]), |
44 | } | 45 | } |
45 | } | 46 | } |
46 | } | 47 | } |
@@ -55,10 +56,11 @@ impl fmt::Debug for Message { | |||
55 | Message::Loaded { files } => { | 56 | Message::Loaded { files } => { |
56 | f.debug_struct("Loaded").field("n_files", &files.len()).finish() | 57 | f.debug_struct("Loaded").field("n_files", &files.len()).finish() |
57 | } | 58 | } |
58 | Message::DidSwitchConfig { n_entries } => { | 59 | Message::Progress { n_total, n_done } => f |
59 | f.debug_struct("DidSwitchConfig").field("n_entries", n_entries).finish() | 60 | .debug_struct("Progress") |
60 | } | 61 | .field("n_total", n_total) |
61 | Message::DidLoadAllEntries => f.debug_struct("DidLoadAllEntries").finish(), | 62 | .field("n_done", n_done) |
63 | .finish(), | ||
62 | } | 64 | } |
63 | } | 65 | } |
64 | } | 66 | } |
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs index de5dc0bf3..dc3031ada 100644 --- a/crates/vfs/src/vfs_path.rs +++ b/crates/vfs/src/vfs_path.rs | |||
@@ -5,38 +5,60 @@ use paths::{AbsPath, AbsPathBuf}; | |||
5 | 5 | ||
6 | /// Long-term, we want to support files which do not reside in the file-system, | 6 | /// Long-term, we want to support files which do not reside in the file-system, |
7 | /// so we treat VfsPaths as opaque identifiers. | 7 | /// so we treat VfsPaths as opaque identifiers. |
8 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 8 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
9 | pub struct VfsPath(VfsPathRepr); | 9 | pub struct VfsPath(VfsPathRepr); |
10 | 10 | ||
11 | impl VfsPath { | 11 | impl VfsPath { |
12 | /// Creates an "in-memory" path from `/`-separates string. | ||
13 | /// This is most useful for testing, to avoid windows/linux differences | ||
14 | pub fn new_virtual_path(path: String) -> VfsPath { | ||
15 | assert!(path.starts_with('/')); | ||
16 | VfsPath(VfsPathRepr::VirtualPath(VirtualPath(path))) | ||
17 | } | ||
18 | |||
12 | pub fn as_path(&self) -> Option<&AbsPath> { | 19 | pub fn as_path(&self) -> Option<&AbsPath> { |
13 | match &self.0 { | 20 | match &self.0 { |
14 | VfsPathRepr::PathBuf(it) => Some(it.as_path()), | 21 | VfsPathRepr::PathBuf(it) => Some(it.as_path()), |
22 | VfsPathRepr::VirtualPath(_) => None, | ||
15 | } | 23 | } |
16 | } | 24 | } |
17 | pub fn join(&self, path: &str) -> VfsPath { | 25 | pub fn join(&self, path: &str) -> Option<VfsPath> { |
18 | match &self.0 { | 26 | match &self.0 { |
19 | VfsPathRepr::PathBuf(it) => { | 27 | VfsPathRepr::PathBuf(it) => { |
20 | let res = it.join(path).normalize(); | 28 | let res = it.join(path).normalize(); |
21 | VfsPath(VfsPathRepr::PathBuf(res)) | 29 | Some(VfsPath(VfsPathRepr::PathBuf(res))) |
30 | } | ||
31 | VfsPathRepr::VirtualPath(it) => { | ||
32 | let res = it.join(path)?; | ||
33 | Some(VfsPath(VfsPathRepr::VirtualPath(res))) | ||
22 | } | 34 | } |
23 | } | 35 | } |
24 | } | 36 | } |
25 | pub fn pop(&mut self) -> bool { | 37 | pub fn pop(&mut self) -> bool { |
26 | match &mut self.0 { | 38 | match &mut self.0 { |
27 | VfsPathRepr::PathBuf(it) => it.pop(), | 39 | VfsPathRepr::PathBuf(it) => it.pop(), |
40 | VfsPathRepr::VirtualPath(it) => it.pop(), | ||
41 | } | ||
42 | } | ||
43 | pub fn starts_with(&self, other: &VfsPath) -> bool { | ||
44 | match (&self.0, &other.0) { | ||
45 | (VfsPathRepr::PathBuf(lhs), VfsPathRepr::PathBuf(rhs)) => lhs.starts_with(rhs), | ||
46 | (VfsPathRepr::PathBuf(_), _) => false, | ||
47 | (VfsPathRepr::VirtualPath(lhs), VfsPathRepr::VirtualPath(rhs)) => lhs.starts_with(rhs), | ||
48 | (VfsPathRepr::VirtualPath(_), _) => false, | ||
28 | } | 49 | } |
29 | } | 50 | } |
30 | } | 51 | } |
31 | 52 | ||
32 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 53 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
33 | enum VfsPathRepr { | 54 | enum VfsPathRepr { |
34 | PathBuf(AbsPathBuf), | 55 | PathBuf(AbsPathBuf), |
56 | VirtualPath(VirtualPath), | ||
35 | } | 57 | } |
36 | 58 | ||
37 | impl From<AbsPathBuf> for VfsPath { | 59 | impl From<AbsPathBuf> for VfsPath { |
38 | fn from(v: AbsPathBuf) -> Self { | 60 | fn from(v: AbsPathBuf) -> Self { |
39 | VfsPath(VfsPathRepr::PathBuf(v)) | 61 | VfsPath(VfsPathRepr::PathBuf(v.normalize())) |
40 | } | 62 | } |
41 | } | 63 | } |
42 | 64 | ||
@@ -44,6 +66,50 @@ impl fmt::Display for VfsPath { | |||
44 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 66 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
45 | match &self.0 { | 67 | match &self.0 { |
46 | VfsPathRepr::PathBuf(it) => fmt::Display::fmt(&it.display(), f), | 68 | VfsPathRepr::PathBuf(it) => fmt::Display::fmt(&it.display(), f), |
69 | VfsPathRepr::VirtualPath(VirtualPath(it)) => fmt::Display::fmt(it, f), | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | impl fmt::Debug for VfsPath { | ||
75 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
76 | fmt::Debug::fmt(&self.0, f) | ||
77 | } | ||
78 | } | ||
79 | |||
80 | impl fmt::Debug for VfsPathRepr { | ||
81 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
82 | match &self { | ||
83 | VfsPathRepr::PathBuf(it) => fmt::Debug::fmt(&it.display(), f), | ||
84 | VfsPathRepr::VirtualPath(VirtualPath(it)) => fmt::Debug::fmt(&it, f), | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | ||
90 | struct VirtualPath(String); | ||
91 | |||
92 | impl VirtualPath { | ||
93 | fn starts_with(&self, other: &VirtualPath) -> bool { | ||
94 | self.0.starts_with(&other.0) | ||
95 | } | ||
96 | fn pop(&mut self) -> bool { | ||
97 | let pos = match self.0.rfind('/') { | ||
98 | Some(pos) => pos, | ||
99 | None => return false, | ||
100 | }; | ||
101 | self.0 = self.0[..pos].to_string(); | ||
102 | true | ||
103 | } | ||
104 | fn join(&self, mut path: &str) -> Option<VirtualPath> { | ||
105 | let mut res = self.clone(); | ||
106 | while path.starts_with("../") { | ||
107 | if !res.pop() { | ||
108 | return None; | ||
109 | } | ||
110 | path = &path["../".len()..] | ||
47 | } | 111 | } |
112 | res.0 = format!("{}/{}", res.0, path); | ||
113 | Some(res) | ||
48 | } | 114 | } |
49 | } | 115 | } |
diff --git a/crates/vfs/src/walkdir_loader.rs b/crates/vfs/src/walkdir_loader.rs deleted file mode 100644 index 13e59e3f3..000000000 --- a/crates/vfs/src/walkdir_loader.rs +++ /dev/null | |||
@@ -1,108 +0,0 @@ | |||
1 | //! A walkdir-based implementation of `loader::Handle`, which doesn't try to | ||
2 | //! watch files. | ||
3 | use std::convert::TryFrom; | ||
4 | |||
5 | use globset::{Glob, GlobSetBuilder}; | ||
6 | use paths::{AbsPath, AbsPathBuf}; | ||
7 | use walkdir::WalkDir; | ||
8 | |||
9 | use crate::loader; | ||
10 | |||
11 | #[derive(Debug)] | ||
12 | pub struct WalkdirLoaderHandle { | ||
13 | // Relative order of fields below is significant. | ||
14 | sender: crossbeam_channel::Sender<Message>, | ||
15 | _thread: jod_thread::JoinHandle, | ||
16 | } | ||
17 | |||
18 | enum Message { | ||
19 | Config(loader::Config), | ||
20 | Invalidate(AbsPathBuf), | ||
21 | } | ||
22 | |||
23 | impl loader::Handle for WalkdirLoaderHandle { | ||
24 | fn spawn(sender: loader::Sender) -> WalkdirLoaderHandle { | ||
25 | let actor = WalkdirLoaderActor { sender }; | ||
26 | let (sender, receiver) = crossbeam_channel::unbounded::<Message>(); | ||
27 | let thread = jod_thread::spawn(move || actor.run(receiver)); | ||
28 | WalkdirLoaderHandle { sender, _thread: thread } | ||
29 | } | ||
30 | fn set_config(&mut self, config: loader::Config) { | ||
31 | self.sender.send(Message::Config(config)).unwrap() | ||
32 | } | ||
33 | fn invalidate(&mut self, path: AbsPathBuf) { | ||
34 | self.sender.send(Message::Invalidate(path)).unwrap(); | ||
35 | } | ||
36 | fn load_sync(&mut self, path: &AbsPathBuf) -> Option<Vec<u8>> { | ||
37 | read(path) | ||
38 | } | ||
39 | } | ||
40 | |||
41 | struct WalkdirLoaderActor { | ||
42 | sender: loader::Sender, | ||
43 | } | ||
44 | |||
45 | impl WalkdirLoaderActor { | ||
46 | fn run(mut self, receiver: crossbeam_channel::Receiver<Message>) { | ||
47 | for msg in receiver { | ||
48 | match msg { | ||
49 | Message::Config(config) => { | ||
50 | self.send(loader::Message::DidSwitchConfig { n_entries: config.load.len() }); | ||
51 | for entry in config.load.into_iter() { | ||
52 | let files = self.load_entry(entry); | ||
53 | self.send(loader::Message::Loaded { files }); | ||
54 | } | ||
55 | drop(config.watch); | ||
56 | self.send(loader::Message::DidLoadAllEntries); | ||
57 | } | ||
58 | Message::Invalidate(path) => { | ||
59 | let contents = read(path.as_path()); | ||
60 | let files = vec![(path, contents)]; | ||
61 | self.send(loader::Message::Loaded { files }); | ||
62 | } | ||
63 | } | ||
64 | } | ||
65 | } | ||
66 | fn load_entry(&mut self, entry: loader::Entry) -> Vec<(AbsPathBuf, Option<Vec<u8>>)> { | ||
67 | match entry { | ||
68 | loader::Entry::Files(files) => files | ||
69 | .into_iter() | ||
70 | .map(|file| { | ||
71 | let contents = read(file.as_path()); | ||
72 | (file, contents) | ||
73 | }) | ||
74 | .collect::<Vec<_>>(), | ||
75 | loader::Entry::Directory { path, globs } => { | ||
76 | let globset = { | ||
77 | let mut builder = GlobSetBuilder::new(); | ||
78 | for glob in &globs { | ||
79 | builder.add(Glob::new(glob).unwrap()); | ||
80 | } | ||
81 | builder.build().unwrap() | ||
82 | }; | ||
83 | |||
84 | let files = WalkDir::new(path) | ||
85 | .into_iter() | ||
86 | .filter_map(|it| it.ok()) | ||
87 | .filter(|it| it.file_type().is_file()) | ||
88 | .map(|it| it.into_path()) | ||
89 | .map(|it| AbsPathBuf::try_from(it).unwrap()) | ||
90 | .filter(|it| globset.is_match(&it)); | ||
91 | |||
92 | files | ||
93 | .map(|file| { | ||
94 | let contents = read(file.as_path()); | ||
95 | (file, contents) | ||
96 | }) | ||
97 | .collect() | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | fn send(&mut self, msg: loader::Message) { | ||
102 | (self.sender)(msg) | ||
103 | } | ||
104 | } | ||
105 | |||
106 | fn read(path: &AbsPath) -> Option<Vec<u8>> { | ||
107 | std::fs::read(path).ok() | ||
108 | } | ||