diff options
-rw-r--r-- | crates/vfs/src/file_set.rs | 15 | ||||
-rw-r--r-- | crates/vfs/src/lib.rs | 20 | ||||
-rw-r--r-- | crates/vfs/src/loader.rs | 15 | ||||
-rw-r--r-- | crates/vfs/src/path_interner.rs | 14 | ||||
-rw-r--r-- | crates/vfs/src/vfs_path.rs | 55 |
5 files changed, 117 insertions, 2 deletions
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index 348b6dbfd..0a4590c8d 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs | |||
@@ -83,7 +83,12 @@ impl fmt::Debug for FileSet { | |||
83 | /// ``` | 83 | /// ``` |
84 | #[derive(Debug)] | 84 | #[derive(Debug)] |
85 | pub struct FileSetConfig { | 85 | pub struct FileSetConfig { |
86 | /// Number of sets that `self` can partition a [`Vfs`] into. | ||
87 | /// | ||
88 | /// This should be the number of sets in `self.map` + 1 for files that don't fit in any | ||
89 | /// defined set. | ||
86 | n_file_sets: usize, | 90 | n_file_sets: usize, |
91 | /// Map from encoded paths to the set they belong to. | ||
87 | map: fst::Map<Vec<u8>>, | 92 | map: fst::Map<Vec<u8>>, |
88 | } | 93 | } |
89 | 94 | ||
@@ -111,9 +116,15 @@ impl FileSetConfig { | |||
111 | } | 116 | } |
112 | res | 117 | res |
113 | } | 118 | } |
119 | |||
120 | /// Number of sets that `self` can partition a [`Vfs`] into. | ||
114 | fn len(&self) -> usize { | 121 | fn len(&self) -> usize { |
115 | self.n_file_sets | 122 | self.n_file_sets |
116 | } | 123 | } |
124 | |||
125 | /// Returns the set index for the given `path`. | ||
126 | /// | ||
127 | /// `scratch_space` is used as a buffer and will be entirely replaced. | ||
117 | fn classify(&self, path: &VfsPath, scratch_space: &mut Vec<u8>) -> usize { | 128 | fn classify(&self, path: &VfsPath, scratch_space: &mut Vec<u8>) -> usize { |
118 | scratch_space.clear(); | 129 | scratch_space.clear(); |
119 | path.encode(scratch_space); | 130 | path.encode(scratch_space); |
@@ -169,11 +180,15 @@ impl FileSetConfigBuilder { | |||
169 | } | 180 | } |
170 | } | 181 | } |
171 | 182 | ||
183 | /// Implements [`fst::Automaton`] | ||
184 | /// | ||
185 | /// It will match if `prefix_of` is a prefix of the given data. | ||
172 | struct PrefixOf<'a> { | 186 | struct PrefixOf<'a> { |
173 | prefix_of: &'a [u8], | 187 | prefix_of: &'a [u8], |
174 | } | 188 | } |
175 | 189 | ||
176 | impl<'a> PrefixOf<'a> { | 190 | impl<'a> PrefixOf<'a> { |
191 | /// Creates a new `PrefixOf` from the given slice. | ||
177 | fn new(prefix_of: &'a [u8]) -> Self { | 192 | fn new(prefix_of: &'a [u8]) -> Self { |
178 | Self { prefix_of } | 193 | Self { prefix_of } |
179 | } | 194 | } |
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index bae2c6118..e075d752b 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs | |||
@@ -176,6 +176,14 @@ impl Vfs { | |||
176 | pub fn take_changes(&mut self) -> Vec<ChangedFile> { | 176 | pub fn take_changes(&mut self) -> Vec<ChangedFile> { |
177 | mem::take(&mut self.changes) | 177 | mem::take(&mut self.changes) |
178 | } | 178 | } |
179 | |||
180 | /// Returns the id associated with `path` | ||
181 | /// | ||
182 | /// - If `path` does not exists in the `Vfs`, allocate a new id for it, associated with a | ||
183 | /// deleted file; | ||
184 | /// - Else, returns `path`'s id. | ||
185 | /// | ||
186 | /// Does not record a change. | ||
179 | fn alloc_file_id(&mut self, path: VfsPath) -> FileId { | 187 | fn alloc_file_id(&mut self, path: VfsPath) -> FileId { |
180 | let file_id = self.interner.intern(path); | 188 | let file_id = self.interner.intern(path); |
181 | let idx = file_id.0 as usize; | 189 | let idx = file_id.0 as usize; |
@@ -183,9 +191,21 @@ impl Vfs { | |||
183 | self.data.resize_with(len, || None); | 191 | self.data.resize_with(len, || None); |
184 | file_id | 192 | file_id |
185 | } | 193 | } |
194 | |||
195 | /// Returns the content associated with the given `file_id`. | ||
196 | /// | ||
197 | /// # Panics | ||
198 | /// | ||
199 | /// Panics if no file is associated to that id. | ||
186 | fn get(&self, file_id: FileId) -> &Option<Vec<u8>> { | 200 | fn get(&self, file_id: FileId) -> &Option<Vec<u8>> { |
187 | &self.data[file_id.0 as usize] | 201 | &self.data[file_id.0 as usize] |
188 | } | 202 | } |
203 | |||
204 | /// Mutably returns the content associated with the given `file_id`. | ||
205 | /// | ||
206 | /// # Panics | ||
207 | /// | ||
208 | /// Panics if no file is associated to that id. | ||
189 | fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> { | 209 | fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> { |
190 | &mut self.data[file_id.0 as usize] | 210 | &mut self.data[file_id.0 as usize] |
191 | } | 211 | } |
diff --git a/crates/vfs/src/loader.rs b/crates/vfs/src/loader.rs index 399015043..d3bdae562 100644 --- a/crates/vfs/src/loader.rs +++ b/crates/vfs/src/loader.rs | |||
@@ -147,6 +147,13 @@ impl Directories { | |||
147 | pub fn contains_dir(&self, path: &AbsPath) -> bool { | 147 | pub fn contains_dir(&self, path: &AbsPath) -> bool { |
148 | self.includes_path(path) | 148 | self.includes_path(path) |
149 | } | 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. | ||
150 | fn includes_path(&self, path: &AbsPath) -> bool { | 157 | fn includes_path(&self, path: &AbsPath) -> bool { |
151 | let mut include: Option<&AbsPathBuf> = None; | 158 | let mut include: Option<&AbsPathBuf> = None; |
152 | for incl in &self.include { | 159 | for incl in &self.include { |
@@ -170,6 +177,14 @@ impl Directories { | |||
170 | } | 177 | } |
171 | } | 178 | } |
172 | 179 | ||
180 | /// Returns : | ||
181 | /// ```text | ||
182 | /// Directories { | ||
183 | /// extensions: ["rs"], | ||
184 | /// include: [base], | ||
185 | /// exclude: [base/<exclude>], | ||
186 | /// } | ||
187 | /// ``` | ||
173 | fn dirs(base: AbsPathBuf, exclude: &[&str]) -> Directories { | 188 | fn dirs(base: AbsPathBuf, exclude: &[&str]) -> Directories { |
174 | let exclude = exclude.iter().map(|it| base.join(it)).collect::<Vec<_>>(); | 189 | let exclude = exclude.iter().map(|it| base.join(it)).collect::<Vec<_>>(); |
175 | Directories { extensions: vec!["rs".to_string()], include: vec![base], exclude } | 190 | Directories { extensions: vec!["rs".to_string()], include: vec![base], exclude } |
diff --git a/crates/vfs/src/path_interner.rs b/crates/vfs/src/path_interner.rs index 4f70d61e8..2189e5e25 100644 --- a/crates/vfs/src/path_interner.rs +++ b/crates/vfs/src/path_interner.rs | |||
@@ -5,6 +5,7 @@ use rustc_hash::FxHashMap; | |||
5 | 5 | ||
6 | use crate::{FileId, VfsPath}; | 6 | use crate::{FileId, VfsPath}; |
7 | 7 | ||
8 | /// Structure to map between [`VfsPath`] and [`FileId`]. | ||
8 | #[derive(Default)] | 9 | #[derive(Default)] |
9 | pub(crate) struct PathInterner { | 10 | pub(crate) struct PathInterner { |
10 | map: FxHashMap<VfsPath, FileId>, | 11 | map: FxHashMap<VfsPath, FileId>, |
@@ -12,9 +13,17 @@ pub(crate) struct PathInterner { | |||
12 | } | 13 | } |
13 | 14 | ||
14 | impl PathInterner { | 15 | impl PathInterner { |
16 | /// Get the id corresponding to `path`. | ||
17 | /// | ||
18 | /// If `path` does not exists in `self`, returns [`None`]. | ||
15 | pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> { | 19 | pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> { |
16 | self.map.get(path).copied() | 20 | self.map.get(path).copied() |
17 | } | 21 | } |
22 | |||
23 | /// Insert `path` in `self`. | ||
24 | /// | ||
25 | /// - If `path` already exists in `self`, returns its associated id; | ||
26 | /// - Else, returns a newly allocated id. | ||
18 | pub(crate) fn intern(&mut self, path: VfsPath) -> FileId { | 27 | pub(crate) fn intern(&mut self, path: VfsPath) -> FileId { |
19 | if let Some(id) = self.get(&path) { | 28 | if let Some(id) = self.get(&path) { |
20 | return id; | 29 | return id; |
@@ -25,6 +34,11 @@ impl PathInterner { | |||
25 | id | 34 | id |
26 | } | 35 | } |
27 | 36 | ||
37 | /// Returns the path corresponding to `id`. | ||
38 | /// | ||
39 | /// # Panics | ||
40 | /// | ||
41 | /// Panics if `id` does not exists in `self`. | ||
28 | pub(crate) fn lookup(&self, id: FileId) -> &VfsPath { | 42 | pub(crate) fn lookup(&self, id: FileId) -> &VfsPath { |
29 | &self.vec[id.0 as usize] | 43 | &self.vec[id.0 as usize] |
30 | } | 44 | } |
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs index 74b6333e2..2b3d7fd84 100644 --- a/crates/vfs/src/vfs_path.rs +++ b/crates/vfs/src/vfs_path.rs | |||
@@ -102,7 +102,14 @@ impl VfsPath { | |||
102 | } | 102 | } |
103 | } | 103 | } |
104 | 104 | ||
105 | // Don't make this `pub` | 105 | /// **Don't make this `pub`** |
106 | /// | ||
107 | /// Encode the path in the given buffer. | ||
108 | /// | ||
109 | /// The encoding will be `0` if [`AbsPathBuf`], `1` if [`VirtualPath`], followed | ||
110 | /// by `self`'s representation. | ||
111 | /// | ||
112 | /// Note that this encoding is dependent on the operating system. | ||
106 | pub(crate) fn encode(&self, buf: &mut Vec<u8>) { | 113 | pub(crate) fn encode(&self, buf: &mut Vec<u8>) { |
107 | let tag = match &self.0 { | 114 | let tag = match &self.0 { |
108 | VfsPathRepr::PathBuf(_) => 0, | 115 | VfsPathRepr::PathBuf(_) => 0, |
@@ -259,6 +266,7 @@ mod windows_paths { | |||
259 | } | 266 | } |
260 | } | 267 | } |
261 | 268 | ||
269 | /// Internal, private representation of [`VfsPath`]. | ||
262 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 270 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
263 | enum VfsPathRepr { | 271 | enum VfsPathRepr { |
264 | PathBuf(AbsPathBuf), | 272 | PathBuf(AbsPathBuf), |
@@ -295,13 +303,34 @@ impl fmt::Debug for VfsPathRepr { | |||
295 | } | 303 | } |
296 | } | 304 | } |
297 | 305 | ||
306 | /// `/`-separated virtual path. | ||
307 | /// | ||
308 | /// This is used to describe files that do not reside on the file system. | ||
298 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 309 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
299 | struct VirtualPath(String); | 310 | struct VirtualPath(String); |
300 | 311 | ||
301 | impl VirtualPath { | 312 | impl VirtualPath { |
313 | /// Returns `true` if `other` is a prefix of `self` (as strings). | ||
302 | fn starts_with(&self, other: &VirtualPath) -> bool { | 314 | fn starts_with(&self, other: &VirtualPath) -> bool { |
303 | self.0.starts_with(&other.0) | 315 | self.0.starts_with(&other.0) |
304 | } | 316 | } |
317 | |||
318 | /// Remove the last component of `self`. | ||
319 | /// | ||
320 | /// This will find the last `'/'` in `self`, and remove everything after it, | ||
321 | /// including the `'/'`. | ||
322 | /// | ||
323 | /// If `self` contains no `'/'`, returns `false`; else returns `true`. | ||
324 | /// | ||
325 | /// # Example | ||
326 | /// | ||
327 | /// ```rust,ignore | ||
328 | /// let mut path = VirtualPath("/foo/bar".to_string()); | ||
329 | /// path.pop(); | ||
330 | /// assert_eq!(path.0, "/foo"); | ||
331 | /// path.pop(); | ||
332 | /// assert_eq!(path.0, ""); | ||
333 | /// ``` | ||
305 | fn pop(&mut self) -> bool { | 334 | fn pop(&mut self) -> bool { |
306 | let pos = match self.0.rfind('/') { | 335 | let pos = match self.0.rfind('/') { |
307 | Some(pos) => pos, | 336 | Some(pos) => pos, |
@@ -310,6 +339,17 @@ impl VirtualPath { | |||
310 | self.0 = self.0[..pos].to_string(); | 339 | self.0 = self.0[..pos].to_string(); |
311 | true | 340 | true |
312 | } | 341 | } |
342 | |||
343 | /// Append the given *relative* path `path` to `self`. | ||
344 | /// | ||
345 | /// This will resolve any leading `"../"` in `path` before appending it. | ||
346 | /// | ||
347 | /// Returns [`None`] if `path` has more leading `"../"` than the number of | ||
348 | /// components in `self`. | ||
349 | /// | ||
350 | /// # Notes | ||
351 | /// | ||
352 | /// In practice, appending here means `self/path` as strings. | ||
313 | fn join(&self, mut path: &str) -> Option<VirtualPath> { | 353 | fn join(&self, mut path: &str) -> Option<VirtualPath> { |
314 | let mut res = self.clone(); | 354 | let mut res = self.clone(); |
315 | while path.starts_with("../") { | 355 | while path.starts_with("../") { |
@@ -322,7 +362,18 @@ impl VirtualPath { | |||
322 | Some(res) | 362 | Some(res) |
323 | } | 363 | } |
324 | 364 | ||
325 | pub(crate) fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { | 365 | /// Returns `self`'s base name and file extension. |
366 | /// | ||
367 | /// # Returns | ||
368 | /// - `None` if `self` ends with `"//"`. | ||
369 | /// - `Some((name, None))` if `self`'s base contains no `.`, or only one `.` at | ||
370 | /// the start. | ||
371 | /// - `Some((name, Some(extension))` else. | ||
372 | /// | ||
373 | /// # Note | ||
374 | /// The extension will not contains `.`. This means `"/foo/bar.baz.rs"` will | ||
375 | /// return `Some(("bar.baz", Some("rs"))`. | ||
376 | fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { | ||
326 | let file_path = if self.0.ends_with('/') { &self.0[..&self.0.len() - 1] } else { &self.0 }; | 377 | let file_path = if self.0.ends_with('/') { &self.0[..&self.0.len() - 1] } else { &self.0 }; |
327 | let file_name = match file_path.rfind('/') { | 378 | let file_name = match file_path.rfind('/') { |
328 | Some(position) => &file_path[position + 1..], | 379 | Some(position) => &file_path[position + 1..], |