mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 12:54:58 +00:00
implement vfs events handling
This commit is contained in:
parent
e69b05781f
commit
a422d480a1
7 changed files with 349 additions and 43 deletions
|
@ -15,14 +15,19 @@ mod arena;
|
|||
mod io;
|
||||
|
||||
use std::{
|
||||
mem,
|
||||
thread,
|
||||
cmp::Reverse,
|
||||
path::{Path, PathBuf},
|
||||
ffi::OsStr,
|
||||
sync::Arc,
|
||||
fs,
|
||||
};
|
||||
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use relative_path::RelativePathBuf;
|
||||
use crossbeam_channel::Receiver;
|
||||
use walkdir::DirEntry;
|
||||
use thread_worker::{WorkerHandle};
|
||||
|
||||
use crate::{
|
||||
|
@ -40,15 +45,25 @@ impl RootFilter {
|
|||
fn new(root: PathBuf) -> RootFilter {
|
||||
RootFilter {
|
||||
root,
|
||||
file_filter: rs_extension_filter,
|
||||
file_filter: has_rs_extension,
|
||||
}
|
||||
}
|
||||
fn can_contain(&self, path: &Path) -> bool {
|
||||
(self.file_filter)(path) && path.starts_with(&self.root)
|
||||
/// Check if this root can contain `path`. NB: even if this returns
|
||||
/// true, the `path` might actually be conained in some nested root.
|
||||
fn can_contain(&self, path: &Path) -> Option<RelativePathBuf> {
|
||||
if !(self.file_filter)(path) {
|
||||
return None;
|
||||
}
|
||||
if !(path.starts_with(&self.root)) {
|
||||
return None;
|
||||
}
|
||||
let path = path.strip_prefix(&self.root).unwrap();
|
||||
let path = RelativePathBuf::from_path(path).unwrap();
|
||||
Some(path)
|
||||
}
|
||||
}
|
||||
|
||||
fn rs_extension_filter(p: &Path) -> bool {
|
||||
fn has_rs_extension(p: &Path) -> bool {
|
||||
p.extension() == Some(OsStr::new("rs"))
|
||||
}
|
||||
|
||||
|
@ -82,10 +97,11 @@ struct VfsFileData {
|
|||
text: Arc<String>,
|
||||
}
|
||||
|
||||
struct Vfs {
|
||||
pub struct Vfs {
|
||||
roots: Arena<VfsRoot, RootFilter>,
|
||||
files: Arena<VfsFile, VfsFileData>,
|
||||
// pending_changes: Vec<PendingChange>,
|
||||
root2files: FxHashMap<VfsRoot, FxHashSet<VfsFile>>,
|
||||
pending_changes: Vec<VfsChange>,
|
||||
worker: io::Worker,
|
||||
worker_handle: WorkerHandle,
|
||||
}
|
||||
|
@ -97,33 +113,144 @@ impl Vfs {
|
|||
let mut res = Vfs {
|
||||
roots: Arena::default(),
|
||||
files: Arena::default(),
|
||||
root2files: FxHashMap::default(),
|
||||
worker,
|
||||
worker_handle,
|
||||
pending_changes: Vec::new(),
|
||||
};
|
||||
|
||||
// A hack to make nesting work.
|
||||
roots.sort_by_key(|it| Reverse(it.as_os_str().len()));
|
||||
|
||||
for path in roots {
|
||||
res.roots.alloc(RootFilter::new(path));
|
||||
for (i, path) in roots.iter().enumerate() {
|
||||
let root = res.roots.alloc(RootFilter::new(path.clone()));
|
||||
let nested = roots[..i]
|
||||
.iter()
|
||||
.filter(|it| it.starts_with(path))
|
||||
.map(|it| it.clone())
|
||||
.collect::<Vec<_>>();
|
||||
let filter = move |entry: &DirEntry| {
|
||||
if entry.file_type().is_file() {
|
||||
has_rs_extension(entry.path())
|
||||
} else {
|
||||
nested.iter().all(|it| it != entry.path())
|
||||
}
|
||||
};
|
||||
let task = io::Task {
|
||||
root,
|
||||
path: path.clone(),
|
||||
filter: Box::new(filter),
|
||||
};
|
||||
res.worker.inp.send(task);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub fn add_file_overlay(&mut self, path: &Path, content: String) {}
|
||||
pub fn task_receiver(&self) -> &Receiver<io::TaskResult> {
|
||||
&self.worker.out
|
||||
}
|
||||
|
||||
pub fn change_file_overlay(&mut self, path: &Path, new_content: String) {}
|
||||
pub fn handle_task(&mut self, task: io::TaskResult) {
|
||||
let mut files = Vec::new();
|
||||
for (path, text) in task.files {
|
||||
let text = Arc::new(text);
|
||||
let file = self.add_file(task.root, path.clone(), Arc::clone(&text));
|
||||
files.push((file, path, text));
|
||||
}
|
||||
let change = VfsChange::AddRoot {
|
||||
root: task.root,
|
||||
files,
|
||||
};
|
||||
self.pending_changes.push(change);
|
||||
}
|
||||
|
||||
pub fn remove_file_overlay(&mut self, path: &Path) {}
|
||||
pub fn add_file_overlay(&mut self, path: &Path, text: String) {
|
||||
if let Some((root, path, file)) = self.find_root(path) {
|
||||
let text = Arc::new(text);
|
||||
let change = if let Some(file) = file {
|
||||
self.change_file(file, Arc::clone(&text));
|
||||
VfsChange::ChangeFile { file, text }
|
||||
} else {
|
||||
let file = self.add_file(root, path.clone(), Arc::clone(&text));
|
||||
VfsChange::AddFile {
|
||||
file,
|
||||
text,
|
||||
root,
|
||||
path,
|
||||
}
|
||||
};
|
||||
self.pending_changes.push(change);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
let text = Arc::new(new_text);
|
||||
self.change_file(file, Arc::clone(&text));
|
||||
let change = VfsChange::ChangeFile { file, text };
|
||||
self.pending_changes.push(change);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_file_overlay(&mut self, path: &Path) {
|
||||
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[root].root);
|
||||
let change = if let Ok(text) = fs::read_to_string(&full_path) {
|
||||
let text = Arc::new(text);
|
||||
self.change_file(file, Arc::clone(&text));
|
||||
VfsChange::ChangeFile { file, text }
|
||||
} else {
|
||||
self.remove_file(file);
|
||||
VfsChange::RemoveFile { file }
|
||||
};
|
||||
self.pending_changes.push(change);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn commit_changes(&mut self) -> Vec<VfsChange> {
|
||||
unimplemented!()
|
||||
mem::replace(&mut self.pending_changes, Vec::new())
|
||||
}
|
||||
|
||||
pub fn shutdown(self) -> thread::Result<()> {
|
||||
let _ = self.worker.shutdown();
|
||||
self.worker_handle.shutdown()
|
||||
}
|
||||
|
||||
fn add_file(&mut self, root: VfsRoot, path: RelativePathBuf, text: Arc<String>) -> VfsFile {
|
||||
let data = VfsFileData { root, path, text };
|
||||
let file = self.files.alloc(data);
|
||||
self.root2files
|
||||
.entry(root)
|
||||
.or_insert_with(FxHashSet::default)
|
||||
.insert(file);
|
||||
file
|
||||
}
|
||||
|
||||
fn change_file(&mut self, file: VfsFile, new_text: Arc<String>) {
|
||||
self.files[file].text = new_text;
|
||||
}
|
||||
|
||||
fn 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);
|
||||
assert!(removed);
|
||||
}
|
||||
|
||||
fn find_root(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf, Option<VfsFile>)> {
|
||||
let (root, path) = self
|
||||
.roots
|
||||
.iter()
|
||||
.find_map(|(root, data)| data.can_contain(path).map(|it| (root, it)))?;
|
||||
let file = self.root2files[&root]
|
||||
.iter()
|
||||
.map(|&it| it)
|
||||
.find(|&file| self.files[file].path == path);
|
||||
Some((root, path, file))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue