};
use crossbeam_channel::Receiver;
-use ra_arena::{impl_arena_id, Arena, RawId, map::ArenaMap};
use relative_path::{RelativePath, RelativePathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct VfsFile(pub RawId);
-impl_arena_id!(VfsFile);
+pub struct VfsFile(pub u32);
struct VfsFileData {
root: VfsRoot,
pub struct Vfs {
roots: Arc<Roots>,
- files: Arena<VfsFile, VfsFileData>,
- root2files: ArenaMap<VfsRoot, FxHashSet<VfsFile>>,
+ files: Vec<VfsFileData>,
+ root2files: FxHashMap<VfsRoot, FxHashSet<VfsFile>>,
pending_changes: Vec<VfsChange>,
worker: Worker,
}
}
}
+#[derive(Debug, Clone)]
+pub enum VfsChange {
+ AddRoot { root: VfsRoot, files: Vec<(VfsFile, RelativePathBuf, Arc<String>)> },
+ AddFile { root: VfsRoot, file: VfsFile, path: RelativePathBuf, text: Arc<String> },
+ RemoveFile { root: VfsRoot, file: VfsFile, path: RelativePathBuf },
+ ChangeFile { file: VfsFile, text: Arc<String> },
+}
+
impl Vfs {
pub fn new(roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) {
let roots = Arc::new(Roots::new(roots));
let worker = io::start(Arc::clone(&roots));
- let mut root2files = ArenaMap::default();
+ let mut root2files = FxHashMap::default();
for root in roots.iter() {
root2files.insert(root, Default::default());
worker.sender().send(io::Task::AddRoot { root }).unwrap();
}
- let res =
- Vfs { roots, files: Arena::default(), root2files, worker, pending_changes: Vec::new() };
+ let res = Vfs { roots, files: Vec::new(), root2files, worker, pending_changes: Vec::new() };
let vfs_roots = res.roots.iter().collect();
(res, vfs_roots)
}
}
pub fn file2path(&self, file: VfsFile) -> PathBuf {
- let rel_path = &self.files[file].path;
- let root_path = &self.roots.path(self.files[file].root);
+ let rel_path = &self.file(file).path;
+ let root_path = &self.roots.path(self.file(file).root);
rel_path.to_path(root_path)
}
- pub fn file_for_path(&self, path: &Path) -> Option<VfsFile> {
- if let Some((_root, _path, Some(file))) = self.find_root(path) {
- return Some(file);
- }
- None
- }
-
- pub fn num_roots(&self) -> usize {
+ pub fn n_roots(&self) -> usize {
self.roots.len()
}
} else {
let text = fs::read_to_string(path).unwrap_or_default();
let text = Arc::new(text);
- let file = self.add_file(root, rel_path.clone(), Arc::clone(&text), false);
+ let file = self.raw_add_file(root, rel_path.clone(), Arc::clone(&text), false);
let change = VfsChange::AddFile { file, text, root, path: rel_path };
self.pending_changes.push(change);
Some(file)
None
}
+ pub fn add_file_overlay(&mut self, path: &Path, text: String) -> Option<VfsFile> {
+ let (root, rel_path, file) = self.find_root(path)?;
+ if let Some(file) = file {
+ self.change_file_event(file, text, true);
+ Some(file)
+ } else {
+ self.add_file_event(root, rel_path, text, true)
+ }
+ }
+
+ pub fn change_file_overlay(&mut self, path: &Path, new_text: String) {
+ if let Some((_root, _path, file)) = self.find_root(path) {
+ let file = file.expect("can't change a file which wasn't added");
+ self.change_file_event(file, new_text, true);
+ }
+ }
+
+ pub fn remove_file_overlay(&mut self, path: &Path) -> Option<VfsFile> {
+ let (root, rel_path, file) = self.find_root(path)?;
+ let file = file.expect("can't remove a file which wasn't added");
+ let full_path = rel_path.to_path(&self.roots.path(root));
+ if let Ok(text) = fs::read_to_string(&full_path) {
+ self.change_file_event(file, text, false);
+ } else {
+ self.remove_file_event(root, rel_path, file);
+ }
+ Some(file)
+ }
+
+ pub fn commit_changes(&mut self) -> Vec<VfsChange> {
+ mem::replace(&mut self.pending_changes, Vec::new())
+ }
+
pub fn task_receiver(&self) -> &Receiver<io::TaskResult> {
self.worker.receiver()
}
let mut cur_files = Vec::new();
// While we were scanning the root in the background, a file might have
// been open in the editor, so we need to account for that.
- let existing = self.root2files[root]
+ let existing = self.root2files[&root]
.iter()
- .map(|&file| (self.files[file].path.clone(), file))
+ .map(|&file| (self.file(file).path.clone(), file))
.collect::<FxHashMap<_, _>>();
for (path, text) in files {
if let Some(&file) = existing.get(&path) {
- let text = Arc::clone(&self.files[file].text);
+ let text = Arc::clone(&self.file(file).text);
cur_files.push((file, path, text));
continue;
}
let text = Arc::new(text);
- let file = self.add_file(root, path.clone(), Arc::clone(&text), false);
+ let file = self.raw_add_file(root, path.clone(), Arc::clone(&text), false);
cur_files.push((file, path, text));
}
self.pending_changes.push(change);
}
TaskResult::SingleFile { root, path, text } => {
- match (self.find_file(root, &path), text) {
+ let existing_file = self.find_file(root, &path);
+ if existing_file.map(|file| self.file(file).is_overlayed) == Some(true) {
+ return;
+ }
+ match (existing_file, text) {
(Some(file), None) => {
- self.do_remove_file(root, path, file, false);
+ self.remove_file_event(root, path, file);
}
(None, Some(text)) => {
- self.do_add_file(root, path, text, false);
+ self.add_file_event(root, path, text, false);
}
(Some(file), Some(text)) => {
- self.do_change_file(file, text, false);
+ self.change_file_event(file, text, false);
}
(None, None) => (),
}
}
}
- fn do_add_file(
+ // *_event calls change the state of VFS and push a change onto pending
+ // changes array.
+
+ fn add_file_event(
&mut self,
root: VfsRoot,
path: RelativePathBuf,
is_overlay: bool,
) -> Option<VfsFile> {
let text = Arc::new(text);
- let file = self.add_file(root, path.clone(), text.clone(), is_overlay);
+ let file = self.raw_add_file(root, path.clone(), text.clone(), is_overlay);
self.pending_changes.push(VfsChange::AddFile { file, root, path, text });
Some(file)
}
- fn do_change_file(&mut self, file: VfsFile, text: String, is_overlay: bool) {
- if !is_overlay && self.files[file].is_overlayed {
- return;
- }
+ fn change_file_event(&mut self, file: VfsFile, text: String, is_overlay: bool) {
let text = Arc::new(text);
- self.change_file(file, text.clone(), is_overlay);
+ self.raw_change_file(file, text.clone(), is_overlay);
self.pending_changes.push(VfsChange::ChangeFile { file, text });
}
- fn do_remove_file(
- &mut self,
- root: VfsRoot,
- path: RelativePathBuf,
- file: VfsFile,
- is_overlay: bool,
- ) {
- if !is_overlay && self.files[file].is_overlayed {
- return;
- }
- self.remove_file(file);
+ fn remove_file_event(&mut self, root: VfsRoot, path: RelativePathBuf, file: VfsFile) {
+ self.raw_remove_file(file);
self.pending_changes.push(VfsChange::RemoveFile { root, path, file });
}
- pub fn add_file_overlay(&mut self, path: &Path, text: String) -> Option<VfsFile> {
- if let Some((root, rel_path, file)) = self.find_root(path) {
- if let Some(file) = file {
- self.do_change_file(file, text, true);
- Some(file)
- } else {
- self.do_add_file(root, rel_path, text, true)
- }
- } else {
- None
- }
- }
-
- pub fn change_file_overlay(&mut self, path: &Path, new_text: String) {
- if let Some((_root, _path, file)) = self.find_root(path) {
- let file = file.expect("can't change a file which wasn't added");
- self.do_change_file(file, new_text, true);
- }
- }
-
- pub fn remove_file_overlay(&mut self, path: &Path) -> Option<VfsFile> {
- if let Some((root, path, file)) = self.find_root(path) {
- let file = file.expect("can't remove a file which wasn't added");
- let full_path = path.to_path(&self.roots.path(root));
- if let Ok(text) = fs::read_to_string(&full_path) {
- self.do_change_file(file, text, true);
- } else {
- self.do_remove_file(root, path, file, true);
- }
- Some(file)
- } else {
- None
- }
- }
-
- pub fn commit_changes(&mut self) -> Vec<VfsChange> {
- mem::replace(&mut self.pending_changes, Vec::new())
- }
+ // raw_* calls change the state of VFS, but **do not** emit events.
- fn add_file(
+ fn raw_add_file(
&mut self,
root: VfsRoot,
path: RelativePathBuf,
is_overlayed: bool,
) -> VfsFile {
let data = VfsFileData { root, path, text, is_overlayed };
- let file = self.files.alloc(data);
- self.root2files.get_mut(root).unwrap().insert(file);
+ let file = VfsFile(self.files.len() as u32);
+ self.files.push(data);
+ self.root2files.get_mut(&root).unwrap().insert(file);
file
}
- fn change_file(&mut self, file: VfsFile, new_text: Arc<String>, is_overlayed: bool) {
- let mut file_data = &mut self.files[file];
+ fn raw_change_file(&mut self, file: VfsFile, new_text: Arc<String>, is_overlayed: bool) {
+ let mut file_data = &mut self.file_mut(file);
file_data.text = new_text;
file_data.is_overlayed = is_overlayed;
}
- fn remove_file(&mut self, file: VfsFile) {
+ fn raw_remove_file(&mut self, file: VfsFile) {
// FIXME: use arena with removal
- self.files[file].text = Default::default();
- self.files[file].path = Default::default();
- let root = self.files[file].root;
- let removed = self.root2files.get_mut(root).unwrap().remove(&file);
+ self.file_mut(file).text = Default::default();
+ self.file_mut(file).path = Default::default();
+ let root = self.file(file).root;
+ let removed = self.root2files.get_mut(&root).unwrap().remove(&file);
assert!(removed);
}
}
fn find_file(&self, root: VfsRoot, path: &RelativePath) -> Option<VfsFile> {
- self.root2files[root].iter().map(|&it| it).find(|&file| self.files[file].path == path)
+ self.root2files[&root].iter().map(|&it| it).find(|&file| self.file(file).path == path)
}
-}
-#[derive(Debug, Clone)]
-pub enum VfsChange {
- AddRoot { root: VfsRoot, files: Vec<(VfsFile, RelativePathBuf, Arc<String>)> },
- AddFile { root: VfsRoot, file: VfsFile, path: RelativePathBuf, text: Arc<String> },
- RemoveFile { root: VfsRoot, file: VfsFile, path: RelativePathBuf },
- ChangeFile { file: VfsFile, text: Arc<String> },
+ fn file(&self, file: VfsFile) -> &VfsFileData {
+ &self.files[file.0 as usize]
+ }
+
+ fn file_mut(&mut self, file: VfsFile) -> &mut VfsFileData {
+ &mut self.files[file.0 as usize]
+ }
}