1 //! VFS stands for Virtual File System.
3 //! When doing analysis, we don't want to do any IO, we want to keep all source
4 //! code in memory. However, the actual source code is stored on disk, so you
5 //! need to get it into the memory in the first place somehow. VFS is the
6 //! component which does this.
8 //! It is also responsible for watching the disk for changes, and for merging
9 //! editor state (modified, unsaved files) with disk state.
11 //! TODO: Some LSP clients support watching the disk, so this crate should to
12 //! support custom watcher events (related to
13 //! <https://github.com/rust-analyzer/rust-analyzer/issues/131>)
15 //! VFS is based on a concept of roots: a set of directories on the file system
16 //! which are watched for changes. Typically, there will be a root for each
23 path::{Path, PathBuf},
27 use crossbeam_channel::Receiver;
28 use relative_path::{RelativePath, RelativePathBuf};
29 use rustc_hash::{FxHashMap, FxHashSet};
32 io::{TaskResult, Worker},
37 io::TaskResult as VfsTask,
41 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
42 pub struct VfsFile(pub u32);
46 path: RelativePathBuf,
53 files: Vec<VfsFileData>,
54 root2files: FxHashMap<VfsRoot, FxHashSet<VfsFile>>,
55 pending_changes: Vec<VfsChange>,
59 impl fmt::Debug for Vfs {
60 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62 .field("n_roots", &self.roots.len())
63 .field("n_files", &self.files.len())
64 .field("n_pending_changes", &self.pending_changes.len())
69 #[derive(Debug, Clone)]
71 AddRoot { root: VfsRoot, files: Vec<(VfsFile, RelativePathBuf, Arc<String>)> },
72 AddFile { root: VfsRoot, file: VfsFile, path: RelativePathBuf, text: Arc<String> },
73 RemoveFile { root: VfsRoot, file: VfsFile, path: RelativePathBuf },
74 ChangeFile { file: VfsFile, text: Arc<String> },
78 pub fn new(roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) {
79 let roots = Arc::new(Roots::new(roots));
80 let worker = io::start(Arc::clone(&roots));
81 let mut root2files = FxHashMap::default();
83 for root in roots.iter() {
84 root2files.insert(root, Default::default());
85 worker.sender().send(io::Task::AddRoot { root }).unwrap();
87 let res = Vfs { roots, files: Vec::new(), root2files, worker, pending_changes: Vec::new() };
88 let vfs_roots = res.roots.iter().collect();
92 pub fn root2path(&self, root: VfsRoot) -> PathBuf {
93 self.roots.path(root).to_path_buf()
96 pub fn path2file(&self, path: &Path) -> Option<VfsFile> {
97 if let Some((_root, _path, Some(file))) = self.find_root(path) {
103 pub fn file2path(&self, file: VfsFile) -> PathBuf {
104 let rel_path = &self.file(file).path;
105 let root_path = &self.roots.path(self.file(file).root);
106 rel_path.to_path(root_path)
109 pub fn n_roots(&self) -> usize {
113 pub fn load(&mut self, path: &Path) -> Option<VfsFile> {
114 if let Some((root, rel_path, file)) = self.find_root(path) {
115 return if let Some(file) = file {
118 let text = fs::read_to_string(path).unwrap_or_default();
119 let text = Arc::new(text);
120 let file = self.raw_add_file(root, rel_path.clone(), Arc::clone(&text), false);
121 let change = VfsChange::AddFile { file, text, root, path: rel_path };
122 self.pending_changes.push(change);
129 pub fn add_file_overlay(&mut self, path: &Path, text: String) -> Option<VfsFile> {
130 let (root, rel_path, file) = self.find_root(path)?;
131 if let Some(file) = file {
132 self.change_file_event(file, text, true);
135 self.add_file_event(root, rel_path, text, true)
139 pub fn change_file_overlay(&mut self, path: &Path, new_text: String) {
140 if let Some((_root, _path, file)) = self.find_root(path) {
141 let file = file.expect("can't change a file which wasn't added");
142 self.change_file_event(file, new_text, true);
146 pub fn remove_file_overlay(&mut self, path: &Path) -> Option<VfsFile> {
147 let (root, rel_path, file) = self.find_root(path)?;
148 let file = file.expect("can't remove a file which wasn't added");
149 let full_path = rel_path.to_path(&self.roots.path(root));
150 if let Ok(text) = fs::read_to_string(&full_path) {
151 self.change_file_event(file, text, false);
153 self.remove_file_event(root, rel_path, file);
158 pub fn commit_changes(&mut self) -> Vec<VfsChange> {
159 mem::replace(&mut self.pending_changes, Vec::new())
162 pub fn task_receiver(&self) -> &Receiver<io::TaskResult> {
163 self.worker.receiver()
166 pub fn handle_task(&mut self, task: io::TaskResult) {
168 TaskResult::BulkLoadRoot { root, files } => {
169 let mut cur_files = Vec::new();
170 // While we were scanning the root in the background, a file might have
171 // been open in the editor, so we need to account for that.
172 let existing = self.root2files[&root]
174 .map(|&file| (self.file(file).path.clone(), file))
175 .collect::<FxHashMap<_, _>>();
176 for (path, text) in files {
177 if let Some(&file) = existing.get(&path) {
178 let text = Arc::clone(&self.file(file).text);
179 cur_files.push((file, path, text));
182 let text = Arc::new(text);
183 let file = self.raw_add_file(root, path.clone(), Arc::clone(&text), false);
184 cur_files.push((file, path, text));
187 let change = VfsChange::AddRoot { root, files: cur_files };
188 self.pending_changes.push(change);
190 TaskResult::SingleFile { root, path, text } => {
191 let existing_file = self.find_file(root, &path);
192 if existing_file.map(|file| self.file(file).is_overlayed) == Some(true) {
195 match (existing_file, text) {
196 (Some(file), None) => {
197 self.remove_file_event(root, path, file);
199 (None, Some(text)) => {
200 self.add_file_event(root, path, text, false);
202 (Some(file), Some(text)) => {
203 self.change_file_event(file, text, false);
211 // *_event calls change the state of VFS and push a change onto pending
217 path: RelativePathBuf,
220 ) -> Option<VfsFile> {
221 let text = Arc::new(text);
222 let file = self.raw_add_file(root, path.clone(), text.clone(), is_overlay);
223 self.pending_changes.push(VfsChange::AddFile { file, root, path, text });
227 fn change_file_event(&mut self, file: VfsFile, text: String, is_overlay: bool) {
228 let text = Arc::new(text);
229 self.raw_change_file(file, text.clone(), is_overlay);
230 self.pending_changes.push(VfsChange::ChangeFile { file, text });
233 fn remove_file_event(&mut self, root: VfsRoot, path: RelativePathBuf, file: VfsFile) {
234 self.raw_remove_file(file);
235 self.pending_changes.push(VfsChange::RemoveFile { root, path, file });
238 // raw_* calls change the state of VFS, but **do not** emit events.
243 path: RelativePathBuf,
247 let data = VfsFileData { root, path, text, is_overlayed };
248 let file = VfsFile(self.files.len() as u32);
249 self.files.push(data);
250 self.root2files.get_mut(&root).unwrap().insert(file);
254 fn raw_change_file(&mut self, file: VfsFile, new_text: Arc<String>, is_overlayed: bool) {
255 let mut file_data = &mut self.file_mut(file);
256 file_data.text = new_text;
257 file_data.is_overlayed = is_overlayed;
260 fn raw_remove_file(&mut self, file: VfsFile) {
261 // FIXME: use arena with removal
262 self.file_mut(file).text = Default::default();
263 self.file_mut(file).path = Default::default();
264 let root = self.file(file).root;
265 let removed = self.root2files.get_mut(&root).unwrap().remove(&file);
269 fn find_root(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf, Option<VfsFile>)> {
270 let (root, path) = self.roots.find(&path)?;
271 let file = self.find_file(root, &path);
272 Some((root, path, file))
275 fn find_file(&self, root: VfsRoot, path: &RelativePath) -> Option<VfsFile> {
276 self.root2files[&root].iter().map(|&it| it).find(|&file| self.file(file).path == path)
279 fn file(&self, file: VfsFile) -> &VfsFileData {
280 &self.files[file.0 as usize]
283 fn file_mut(&mut self, file: VfsFile) -> &mut VfsFileData {
284 &mut self.files[file.0 as usize]