aboutsummaryrefslogtreecommitdiff
path: root/crates/vfs/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/vfs/src')
-rw-r--r--crates/vfs/src/file_set.rs39
-rw-r--r--crates/vfs/src/lib.rs4
-rw-r--r--crates/vfs/src/loader.rs26
-rw-r--r--crates/vfs/src/vfs_path.rs76
-rw-r--r--crates/vfs/src/walkdir_loader.rs108
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`.
5use std::{cmp, fmt, iter}; 5use std::{fmt, iter};
6 6
7use paths::AbsPathBuf;
8use rustc_hash::FxHashMap; 7use rustc_hash::FxHashMap;
9 8
10use crate::{FileId, Vfs, VfsPath}; 9use 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)]
42pub struct FileSetConfig { 41pub struct FileSetConfig {
43 n_file_sets: usize, 42 n_file_sets: usize,
44 roots: Vec<(AbsPathBuf, usize)>, 43 roots: Vec<(VfsPath, usize)>,
44}
45
46impl Default for FileSetConfig {
47 fn default() -> Self {
48 FileSetConfig::builder().build()
49 }
45} 50}
46 51
47impl FileSetConfig { 52impl 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
74pub struct FileSetConfigBuilder { 80pub struct FileSetConfigBuilder {
75 roots: Vec<Vec<AbsPathBuf>>, 81 roots: Vec<Vec<VfsPath>>,
76} 82}
77 83
78impl Default for FileSetConfigBuilder { 84impl Default for FileSetConfigBuilder {
@@ -82,18 +88,21 @@ impl Default for FileSetConfigBuilder {
82} 88}
83 89
84impl FileSetConfigBuilder { 90impl 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;
38mod path_interner; 38mod path_interner;
39pub mod file_set; 39pub mod file_set;
40pub mod loader; 40pub mod loader;
41pub mod walkdir_loader;
42 41
43use std::{fmt, mem}; 42use std::{fmt, mem};
44 43
@@ -79,6 +78,9 @@ pub enum ChangeKind {
79} 78}
80 79
81impl Vfs { 80impl 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.
2use std::fmt; 2use std::fmt;
3 3
4use paths::AbsPathBuf; 4use paths::{AbsPath, AbsPathBuf};
5 5
6#[derive(Debug)]
6pub enum Entry { 7pub 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)]
11pub struct Config { 13pub 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
16pub enum Message { 18pub 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
33impl Entry { 34impl 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)]
9pub struct VfsPath(VfsPathRepr); 9pub struct VfsPath(VfsPathRepr);
10 10
11impl VfsPath { 11impl 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)]
33enum VfsPathRepr { 54enum VfsPathRepr {
34 PathBuf(AbsPathBuf), 55 PathBuf(AbsPathBuf),
56 VirtualPath(VirtualPath),
35} 57}
36 58
37impl From<AbsPathBuf> for VfsPath { 59impl 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
74impl fmt::Debug for VfsPath {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 fmt::Debug::fmt(&self.0, f)
77 }
78}
79
80impl 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)]
90struct VirtualPath(String);
91
92impl 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.
3use std::convert::TryFrom;
4
5use globset::{Glob, GlobSetBuilder};
6use paths::{AbsPath, AbsPathBuf};
7use walkdir::WalkDir;
8
9use crate::loader;
10
11#[derive(Debug)]
12pub struct WalkdirLoaderHandle {
13 // Relative order of fields below is significant.
14 sender: crossbeam_channel::Sender<Message>,
15 _thread: jod_thread::JoinHandle,
16}
17
18enum Message {
19 Config(loader::Config),
20 Invalidate(AbsPathBuf),
21}
22
23impl 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
41struct WalkdirLoaderActor {
42 sender: loader::Sender,
43}
44
45impl 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
106fn read(path: &AbsPath) -> Option<Vec<u8>> {
107 std::fs::read(path).ok()
108}