aboutsummaryrefslogtreecommitdiff
path: root/crates/vfs/src/loader.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/vfs/src/loader.rs')
-rw-r--r--crates/vfs/src/loader.rs80
1 files changed, 80 insertions, 0 deletions
diff --git a/crates/vfs/src/loader.rs b/crates/vfs/src/loader.rs
index 40cf96020..d3bdae562 100644
--- a/crates/vfs/src/loader.rs
+++ b/crates/vfs/src/loader.rs
@@ -3,9 +3,12 @@ use std::fmt;
3 3
4use paths::{AbsPath, AbsPathBuf}; 4use paths::{AbsPath, AbsPathBuf};
5 5
6/// A set of files on the file system.
6#[derive(Debug, Clone)] 7#[derive(Debug, Clone)]
7pub enum Entry { 8pub enum Entry {
9 /// The `Entry` is represented by a raw set of files.
8 Files(Vec<AbsPathBuf>), 10 Files(Vec<AbsPathBuf>),
11 /// The `Entry` is represented by `Directories`.
9 Directories(Directories), 12 Directories(Directories),
10} 13}
11 14
@@ -17,6 +20,8 @@ pub enum Entry {
17/// * it is not under `exclude` path 20/// * it is not under `exclude` path
18/// 21///
19/// If many include/exclude paths match, the longest one wins. 22/// If many include/exclude paths match, the longest one wins.
23///
24/// If a path is in both `include` and `exclude`, the `exclude` one wins.
20#[derive(Debug, Clone, Default)] 25#[derive(Debug, Clone, Default)]
21pub struct Directories { 26pub struct Directories {
22 pub extensions: Vec<String>, 27 pub extensions: Vec<String>,
@@ -24,45 +29,99 @@ pub struct Directories {
24 pub exclude: Vec<AbsPathBuf>, 29 pub exclude: Vec<AbsPathBuf>,
25} 30}
26 31
32/// [`Handle`]'s configuration.
27#[derive(Debug)] 33#[derive(Debug)]
28pub struct Config { 34pub struct Config {
35 /// Set of initially loaded files.
29 pub load: Vec<Entry>, 36 pub load: Vec<Entry>,
37 /// Index of watched entries in `load`.
38 ///
39 /// If a path in a watched entry is modified,the [`Handle`] should notify it.
30 pub watch: Vec<usize>, 40 pub watch: Vec<usize>,
31} 41}
32 42
43/// Message about an action taken by a [`Handle`].
33pub enum Message { 44pub enum Message {
45 /// Indicate a gradual progress.
46 ///
47 /// This is supposed to be the number of loaded files.
34 Progress { n_total: usize, n_done: usize }, 48 Progress { n_total: usize, n_done: usize },
49 /// The handle loaded the following files' content.
35 Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> }, 50 Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> },
36} 51}
37 52
53/// Type that will receive [`Messages`](Message) from a [`Handle`].
38pub type Sender = Box<dyn Fn(Message) + Send>; 54pub type Sender = Box<dyn Fn(Message) + Send>;
39 55
56/// Interface for reading and watching files.
40pub trait Handle: fmt::Debug { 57pub trait Handle: fmt::Debug {
58 /// Spawn a new handle with the given `sender`.
41 fn spawn(sender: Sender) -> Self 59 fn spawn(sender: Sender) -> Self
42 where 60 where
43 Self: Sized; 61 Self: Sized;
62
63 /// Set this handle's configuration.
44 fn set_config(&mut self, config: Config); 64 fn set_config(&mut self, config: Config);
65
66 /// The file's content at `path` has been modified, and should be reloaded.
45 fn invalidate(&mut self, path: AbsPathBuf); 67 fn invalidate(&mut self, path: AbsPathBuf);
68
69 /// Load the content of the given file, returning [`None`] if it does not
70 /// exists.
46 fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>; 71 fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>;
47} 72}
48 73
49impl Entry { 74impl Entry {
75 /// Returns:
76 /// ```text
77 /// Entry::Directories(Directories {
78 /// extensions: ["rs"],
79 /// include: [base],
80 /// exclude: [base/.git],
81 /// })
82 /// ```
50 pub fn rs_files_recursively(base: AbsPathBuf) -> Entry { 83 pub fn rs_files_recursively(base: AbsPathBuf) -> Entry {
51 Entry::Directories(dirs(base, &[".git"])) 84 Entry::Directories(dirs(base, &[".git"]))
52 } 85 }
86
87 /// Returns:
88 /// ```text
89 /// Entry::Directories(Directories {
90 /// extensions: ["rs"],
91 /// include: [base],
92 /// exclude: [base/.git, base/target],
93 /// })
94 /// ```
53 pub fn local_cargo_package(base: AbsPathBuf) -> Entry { 95 pub fn local_cargo_package(base: AbsPathBuf) -> Entry {
54 Entry::Directories(dirs(base, &[".git", "target"])) 96 Entry::Directories(dirs(base, &[".git", "target"]))
55 } 97 }
98
99 /// Returns:
100 /// ```text
101 /// Entry::Directories(Directories {
102 /// extensions: ["rs"],
103 /// include: [base],
104 /// exclude: [base/.git, /tests, /examples, /benches],
105 /// })
106 /// ```
56 pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry { 107 pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry {
57 Entry::Directories(dirs(base, &[".git", "/tests", "/examples", "/benches"])) 108 Entry::Directories(dirs(base, &[".git", "/tests", "/examples", "/benches"]))
58 } 109 }
59 110
111 /// Returns `true` if `path` is included in `self`.
112 ///
113 /// See [`Directories::contains_file`].
60 pub fn contains_file(&self, path: &AbsPath) -> bool { 114 pub fn contains_file(&self, path: &AbsPath) -> bool {
61 match self { 115 match self {
62 Entry::Files(files) => files.iter().any(|it| it == path), 116 Entry::Files(files) => files.iter().any(|it| it == path),
63 Entry::Directories(dirs) => dirs.contains_file(path), 117 Entry::Directories(dirs) => dirs.contains_file(path),
64 } 118 }
65 } 119 }
120
121 /// Returns `true` if `path` is included in `self`.
122 ///
123 /// - If `self` is `Entry::Files`, returns `false`
124 /// - Else, see [`Directories::contains_dir`].
66 pub fn contains_dir(&self, path: &AbsPath) -> bool { 125 pub fn contains_dir(&self, path: &AbsPath) -> bool {
67 match self { 126 match self {
68 Entry::Files(_) => false, 127 Entry::Files(_) => false,
@@ -72,6 +131,7 @@ impl Entry {
72} 131}
73 132
74impl Directories { 133impl Directories {
134 /// Returns `true` if `path` is included in `self`.
75 pub fn contains_file(&self, path: &AbsPath) -> bool { 135 pub fn contains_file(&self, path: &AbsPath) -> bool {
76 let ext = path.extension().unwrap_or_default(); 136 let ext = path.extension().unwrap_or_default();
77 if self.extensions.iter().all(|it| it.as_str() != ext) { 137 if self.extensions.iter().all(|it| it.as_str() != ext) {
@@ -79,9 +139,21 @@ impl Directories {
79 } 139 }
80 self.includes_path(path) 140 self.includes_path(path)
81 } 141 }
142
143 /// Returns `true` if `path` is included in `self`.
144 ///
145 /// Since `path` is supposed to be a directory, this will not take extension
146 /// into account.
82 pub fn contains_dir(&self, path: &AbsPath) -> bool { 147 pub fn contains_dir(&self, path: &AbsPath) -> bool {
83 self.includes_path(path) 148 self.includes_path(path)
84 } 149 }
150
151 /// Returns `true` if `path` is included in `self`.
152 ///
153 /// It is included if
154 /// - An element in `self.include` is a prefix of `path`.
155 /// - This path is longer than any element in `self.exclude` that is a prefix
156 /// of `path`. In case of equality, exclusion wins.
85 fn includes_path(&self, path: &AbsPath) -> bool { 157 fn includes_path(&self, path: &AbsPath) -> bool {
86 let mut include: Option<&AbsPathBuf> = None; 158 let mut include: Option<&AbsPathBuf> = None;
87 for incl in &self.include { 159 for incl in &self.include {
@@ -105,6 +177,14 @@ impl Directories {
105 } 177 }
106} 178}
107 179
180/// Returns :
181/// ```text
182/// Directories {
183/// extensions: ["rs"],
184/// include: [base],
185/// exclude: [base/<exclude>],
186/// }
187/// ```
108fn dirs(base: AbsPathBuf, exclude: &[&str]) -> Directories { 188fn dirs(base: AbsPathBuf, exclude: &[&str]) -> Directories {
109 let exclude = exclude.iter().map(|it| base.join(it)).collect::<Vec<_>>(); 189 let exclude = exclude.iter().map(|it| base.join(it)).collect::<Vec<_>>();
110 Directories { extensions: vec!["rs".to_string()], include: vec![base], exclude } 190 Directories { extensions: vec!["rs".to_string()], include: vec![base], exclude }