diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-06-23 16:53:56 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-06-23 16:53:56 +0100 |
commit | 0c12c4f9609ee72487af9b55a558b01af73ffe3e (patch) | |
tree | 29be52a980b4cae72f46a48c48135a15e31641e0 /crates/vfs | |
parent | 7aa66371ee3e8b31217513204c8b4f683584419d (diff) | |
parent | dad1333b48c38bc7a5628fc0ff5304d003776a85 (diff) |
Merge #4976
4976: New VFS r=matklad a=matklad
Still a draft, but mostly working already.
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/vfs')
-rw-r--r-- | crates/vfs/Cargo.toml | 4 | ||||
-rw-r--r-- | crates/vfs/src/file_set.rs | 15 | ||||
-rw-r--r-- | crates/vfs/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/vfs/src/loader.rs | 22 | ||||
-rw-r--r-- | crates/vfs/src/vfs_path.rs | 51 | ||||
-rw-r--r-- | crates/vfs/src/walkdir_loader.rs | 108 |
6 files changed, 67 insertions, 134 deletions
diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml index c03e6363b..263069002 100644 --- a/crates/vfs/Cargo.toml +++ b/crates/vfs/Cargo.toml | |||
@@ -6,9 +6,5 @@ edition = "2018" | |||
6 | 6 | ||
7 | [dependencies] | 7 | [dependencies] |
8 | rustc-hash = "1.0" | 8 | rustc-hash = "1.0" |
9 | jod-thread = "0.1.0" | ||
10 | walkdir = "2.3.1" | ||
11 | globset = "0.4.5" | ||
12 | crossbeam-channel = "0.4.0" | ||
13 | 9 | ||
14 | paths = { path = "../paths" } | 10 | paths = { path = "../paths" } |
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index 724606a3d..0173f7464 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs | |||
@@ -4,7 +4,6 @@ | |||
4 | //! the default `FileSet`. | 4 | //! the default `FileSet`. |
5 | use std::{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}; |
@@ -41,7 +40,7 @@ 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)>, |
45 | } | 44 | } |
46 | 45 | ||
47 | impl Default for FileSetConfig { | 46 | impl Default for FileSetConfig { |
@@ -66,11 +65,7 @@ impl FileSetConfig { | |||
66 | self.n_file_sets | 65 | self.n_file_sets |
67 | } | 66 | } |
68 | fn classify(&self, path: &VfsPath) -> usize { | 67 | fn classify(&self, path: &VfsPath) -> usize { |
69 | let path = match path.as_path() { | 68 | let idx = match self.roots.binary_search_by(|(p, _)| p.cmp(path)) { |
70 | Some(it) => it, | ||
71 | None => return self.len() - 1, | ||
72 | }; | ||
73 | let idx = match self.roots.binary_search_by(|(p, _)| p.as_path().cmp(path)) { | ||
74 | Ok(it) => it, | 69 | Ok(it) => it, |
75 | Err(it) => it.saturating_sub(1), | 70 | Err(it) => it.saturating_sub(1), |
76 | }; | 71 | }; |
@@ -83,7 +78,7 @@ impl FileSetConfig { | |||
83 | } | 78 | } |
84 | 79 | ||
85 | pub struct FileSetConfigBuilder { | 80 | pub struct FileSetConfigBuilder { |
86 | roots: Vec<Vec<AbsPathBuf>>, | 81 | roots: Vec<Vec<VfsPath>>, |
87 | } | 82 | } |
88 | 83 | ||
89 | impl Default for FileSetConfigBuilder { | 84 | impl Default for FileSetConfigBuilder { |
@@ -96,12 +91,12 @@ impl FileSetConfigBuilder { | |||
96 | pub fn len(&self) -> usize { | 91 | pub fn len(&self) -> usize { |
97 | self.roots.len() | 92 | self.roots.len() |
98 | } | 93 | } |
99 | pub fn add_file_set(&mut self, roots: Vec<AbsPathBuf>) { | 94 | pub fn add_file_set(&mut self, roots: Vec<VfsPath>) { |
100 | self.roots.push(roots) | 95 | self.roots.push(roots) |
101 | } | 96 | } |
102 | pub fn build(self) -> FileSetConfig { | 97 | pub fn build(self) -> FileSetConfig { |
103 | let n_file_sets = self.roots.len() + 1; | 98 | let n_file_sets = self.roots.len() + 1; |
104 | let mut roots: Vec<(AbsPathBuf, usize)> = self | 99 | let mut roots: Vec<(VfsPath, usize)> = self |
105 | .roots | 100 | .roots |
106 | .into_iter() | 101 | .into_iter() |
107 | .enumerate() | 102 | .enumerate() |
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index 055219b0c..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 | ||
diff --git a/crates/vfs/src/loader.rs b/crates/vfs/src/loader.rs index 5a0ca68f3..a216b5f13 100644 --- a/crates/vfs/src/loader.rs +++ b/crates/vfs/src/loader.rs | |||
@@ -3,19 +3,20 @@ use std::fmt; | |||
3 | 3 | ||
4 | use paths::AbsPathBuf; | 4 | use paths::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_entries_total: usize, n_entries_done: usize }, |
18 | DidLoadAllEntries, | ||
19 | Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> }, | 20 | Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> }, |
20 | } | 21 | } |
21 | 22 | ||
@@ -32,15 +33,15 @@ pub trait Handle: fmt::Debug { | |||
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_entries_total, n_entries_done } => f |
59 | f.debug_struct("DidSwitchConfig").field("n_entries", n_entries).finish() | 60 | .debug_struct("Progress") |
60 | } | 61 | .field("n_entries_total", n_entries_total) |
61 | Message::DidLoadAllEntries => f.debug_struct("DidLoadAllEntries").finish(), | 62 | .field("n_entries_done", n_entries_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..0a8a86c62 100644 --- a/crates/vfs/src/vfs_path.rs +++ b/crates/vfs/src/vfs_path.rs | |||
@@ -9,9 +9,17 @@ use paths::{AbsPath, AbsPathBuf}; | |||
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) -> VfsPath { |
@@ -20,11 +28,24 @@ impl VfsPath { | |||
20 | let res = it.join(path).normalize(); | 28 | let res = it.join(path).normalize(); |
21 | VfsPath(VfsPathRepr::PathBuf(res)) | 29 | VfsPath(VfsPathRepr::PathBuf(res)) |
22 | } | 30 | } |
31 | VfsPathRepr::VirtualPath(it) => { | ||
32 | let res = it.join(path); | ||
33 | VfsPath(VfsPathRepr::VirtualPath(res)) | ||
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 | } |
@@ -32,11 +53,12 @@ impl VfsPath { | |||
32 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 53 | #[derive(Debug, 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,33 @@ 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 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | ||
75 | struct VirtualPath(String); | ||
76 | |||
77 | impl VirtualPath { | ||
78 | fn starts_with(&self, other: &VirtualPath) -> bool { | ||
79 | self.0.starts_with(&other.0) | ||
80 | } | ||
81 | fn pop(&mut self) -> bool { | ||
82 | let pos = match self.0.rfind('/') { | ||
83 | Some(pos) => pos, | ||
84 | None => return false, | ||
85 | }; | ||
86 | self.0 = self.0[..pos].to_string(); | ||
87 | true | ||
88 | } | ||
89 | fn join(&self, mut path: &str) -> VirtualPath { | ||
90 | let mut res = self.clone(); | ||
91 | while path.starts_with("../") { | ||
92 | assert!(res.pop()); | ||
93 | path = &path["../".len()..] | ||
47 | } | 94 | } |
95 | res.0 = format!("{}/{}", res.0, path); | ||
96 | res | ||
48 | } | 97 | } |
49 | } | 98 | } |
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 | } | ||