diff options
Diffstat (limited to 'crates/vfs/src/file_set.rs')
-rw-r--r-- | crates/vfs/src/file_set.rs | 135 |
1 files changed, 114 insertions, 21 deletions
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index d0ddeafe7..e9196fcd2 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs | |||
@@ -2,8 +2,9 @@ | |||
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::{fmt, iter}; | 5 | use std::fmt; |
6 | 6 | ||
7 | use fst::{IntoStreamer, Streamer}; | ||
7 | use rustc_hash::FxHashMap; | 8 | use rustc_hash::FxHashMap; |
8 | 9 | ||
9 | use crate::{FileId, Vfs, VfsPath}; | 10 | use crate::{FileId, Vfs, VfsPath}; |
@@ -15,6 +16,9 @@ pub struct FileSet { | |||
15 | } | 16 | } |
16 | 17 | ||
17 | impl FileSet { | 18 | impl FileSet { |
19 | pub fn len(&self) -> usize { | ||
20 | self.files.len() | ||
21 | } | ||
18 | pub fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { | 22 | pub fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { |
19 | let mut base = self.paths[&anchor].clone(); | 23 | let mut base = self.paths[&anchor].clone(); |
20 | base.pop(); | 24 | base.pop(); |
@@ -40,7 +44,7 @@ impl fmt::Debug for FileSet { | |||
40 | #[derive(Debug)] | 44 | #[derive(Debug)] |
41 | pub struct FileSetConfig { | 45 | pub struct FileSetConfig { |
42 | n_file_sets: usize, | 46 | n_file_sets: usize, |
43 | roots: Vec<(VfsPath, usize)>, | 47 | map: fst::Map<Vec<u8>>, |
44 | } | 48 | } |
45 | 49 | ||
46 | impl Default for FileSetConfig { | 50 | impl Default for FileSetConfig { |
@@ -54,26 +58,27 @@ impl FileSetConfig { | |||
54 | FileSetConfigBuilder::default() | 58 | FileSetConfigBuilder::default() |
55 | } | 59 | } |
56 | pub fn partition(&self, vfs: &Vfs) -> Vec<FileSet> { | 60 | pub fn partition(&self, vfs: &Vfs) -> Vec<FileSet> { |
61 | let mut scratch_space = Vec::new(); | ||
57 | let mut res = vec![FileSet::default(); self.len()]; | 62 | let mut res = vec![FileSet::default(); self.len()]; |
58 | for (file_id, path) in vfs.iter() { | 63 | for (file_id, path) in vfs.iter() { |
59 | let root = self.classify(&path); | 64 | let root = self.classify(&path, &mut scratch_space); |
60 | res[root].insert(file_id, path) | 65 | res[root].insert(file_id, path.clone()) |
61 | } | 66 | } |
62 | res | 67 | res |
63 | } | 68 | } |
64 | fn len(&self) -> usize { | 69 | fn len(&self) -> usize { |
65 | self.n_file_sets | 70 | self.n_file_sets |
66 | } | 71 | } |
67 | fn classify(&self, path: &VfsPath) -> usize { | 72 | fn classify(&self, path: &VfsPath, scratch_space: &mut Vec<u8>) -> usize { |
68 | let idx = match self.roots.binary_search_by(|(p, _)| p.cmp(path)) { | 73 | scratch_space.clear(); |
69 | Ok(it) => it, | 74 | path.encode(scratch_space); |
70 | Err(it) => it.saturating_sub(1), | 75 | let automaton = PrefixOf::new(scratch_space.as_slice()); |
71 | }; | 76 | let mut longest_prefix = self.len() - 1; |
72 | if path.starts_with(&self.roots[idx].0) { | 77 | let mut stream = self.map.search(automaton).into_stream(); |
73 | self.roots[idx].1 | 78 | while let Some((_, v)) = stream.next() { |
74 | } else { | 79 | longest_prefix = v as usize; |
75 | self.len() - 1 | ||
76 | } | 80 | } |
81 | longest_prefix | ||
77 | } | 82 | } |
78 | } | 83 | } |
79 | 84 | ||
@@ -96,13 +101,101 @@ impl FileSetConfigBuilder { | |||
96 | } | 101 | } |
97 | pub fn build(self) -> FileSetConfig { | 102 | pub fn build(self) -> FileSetConfig { |
98 | let n_file_sets = self.roots.len() + 1; | 103 | let n_file_sets = self.roots.len() + 1; |
99 | let mut roots: Vec<(VfsPath, usize)> = self | 104 | let map = { |
100 | .roots | 105 | let mut entries = Vec::new(); |
101 | .into_iter() | 106 | for (i, paths) in self.roots.into_iter().enumerate() { |
102 | .enumerate() | 107 | for p in paths { |
103 | .flat_map(|(i, paths)| paths.into_iter().zip(iter::repeat(i))) | 108 | let mut buf = Vec::new(); |
104 | .collect(); | 109 | p.encode(&mut buf); |
105 | roots.sort(); | 110 | entries.push((buf, i as u64)); |
106 | FileSetConfig { n_file_sets, roots } | 111 | } |
112 | } | ||
113 | entries.sort(); | ||
114 | entries.dedup_by(|(a, _), (b, _)| a == b); | ||
115 | fst::Map::from_iter(entries).unwrap() | ||
116 | }; | ||
117 | FileSetConfig { n_file_sets, map } | ||
118 | } | ||
119 | } | ||
120 | |||
121 | struct PrefixOf<'a> { | ||
122 | prefix_of: &'a [u8], | ||
123 | } | ||
124 | |||
125 | impl<'a> PrefixOf<'a> { | ||
126 | fn new(prefix_of: &'a [u8]) -> Self { | ||
127 | Self { prefix_of } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | impl fst::Automaton for PrefixOf<'_> { | ||
132 | type State = usize; | ||
133 | fn start(&self) -> usize { | ||
134 | 0 | ||
135 | } | ||
136 | fn is_match(&self, &state: &usize) -> bool { | ||
137 | state != !0 | ||
138 | } | ||
139 | fn can_match(&self, &state: &usize) -> bool { | ||
140 | state != !0 | ||
141 | } | ||
142 | fn accept(&self, &state: &usize, byte: u8) -> usize { | ||
143 | if self.prefix_of.get(state) == Some(&byte) { | ||
144 | state + 1 | ||
145 | } else { | ||
146 | !0 | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | |||
151 | #[cfg(test)] | ||
152 | mod tests { | ||
153 | use super::*; | ||
154 | |||
155 | #[test] | ||
156 | fn path_prefix() { | ||
157 | let mut file_set = FileSetConfig::builder(); | ||
158 | file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]); | ||
159 | file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo/bar/baz".into())]); | ||
160 | let file_set = file_set.build(); | ||
161 | |||
162 | let mut vfs = Vfs::default(); | ||
163 | vfs.set_file_contents( | ||
164 | VfsPath::new_virtual_path("/foo/src/lib.rs".into()), | ||
165 | Some(Vec::new()), | ||
166 | ); | ||
167 | vfs.set_file_contents( | ||
168 | VfsPath::new_virtual_path("/foo/src/bar/baz/lib.rs".into()), | ||
169 | Some(Vec::new()), | ||
170 | ); | ||
171 | vfs.set_file_contents( | ||
172 | VfsPath::new_virtual_path("/foo/bar/baz/lib.rs".into()), | ||
173 | Some(Vec::new()), | ||
174 | ); | ||
175 | vfs.set_file_contents(VfsPath::new_virtual_path("/quux/lib.rs".into()), Some(Vec::new())); | ||
176 | |||
177 | let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>(); | ||
178 | assert_eq!(partition, vec![2, 1, 1]); | ||
179 | } | ||
180 | |||
181 | #[test] | ||
182 | fn name_prefix() { | ||
183 | let mut file_set = FileSetConfig::builder(); | ||
184 | file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]); | ||
185 | file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo-things".into())]); | ||
186 | let file_set = file_set.build(); | ||
187 | |||
188 | let mut vfs = Vfs::default(); | ||
189 | vfs.set_file_contents( | ||
190 | VfsPath::new_virtual_path("/foo/src/lib.rs".into()), | ||
191 | Some(Vec::new()), | ||
192 | ); | ||
193 | vfs.set_file_contents( | ||
194 | VfsPath::new_virtual_path("/foo-things/src/lib.rs".into()), | ||
195 | Some(Vec::new()), | ||
196 | ); | ||
197 | |||
198 | let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>(); | ||
199 | assert_eq!(partition, vec![1, 1, 0]); | ||
107 | } | 200 | } |
108 | } | 201 | } |