From 162dea51bfa6b7f6d64d138a84c16c96495a0fcd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 17 Feb 2019 20:22:46 +0300 Subject: hide root config --- crates/ra_vfs/src/io.rs | 31 +++++------ crates/ra_vfs/src/lib.rs | 14 ++--- crates/ra_vfs/src/roots.rs | 125 ++++++++++++++++++++++++--------------------- 3 files changed, 89 insertions(+), 81 deletions(-) (limited to 'crates/ra_vfs/src') diff --git a/crates/ra_vfs/src/io.rs b/crates/ra_vfs/src/io.rs index f64b4c532..0cffc03f3 100644 --- a/crates/ra_vfs/src/io.rs +++ b/crates/ra_vfs/src/io.rs @@ -9,10 +9,10 @@ use relative_path::RelativePathBuf; use walkdir::WalkDir; use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as _Watcher}; -use crate::{RootConfig, Roots, VfsRoot}; +use crate::{Roots, VfsRoot}; pub(crate) enum Task { - AddRoot { root: VfsRoot, config: Arc }, + AddRoot { root: VfsRoot }, } /// `TaskResult` transfers files read on the IO thread to the VFS on the main @@ -98,8 +98,8 @@ pub(crate) fn start(roots: Arc) -> Worker { drop(input_receiver); break }, - Ok(Task::AddRoot { root, config }) => { - watch_root(watcher.as_mut(), &output_sender, root, Arc::clone(&config)); + Ok(Task::AddRoot { root }) => { + watch_root(watcher.as_mut(), &output_sender, &*roots, root); } }, // Watcher send us changes. If **this** channel is @@ -123,20 +123,21 @@ pub(crate) fn start(roots: Arc) -> Worker { fn watch_root( watcher: Option<&mut RecommendedWatcher>, sender: &Sender, + roots: &Roots, root: VfsRoot, - config: Arc, ) { - log::debug!("loading {} ...", config.root.as_path().display()); - let files = watch_recursive(watcher, config.root.as_path(), &*config) + let root_path = roots.path(root); + log::debug!("loading {} ...", root_path.display()); + let files = watch_recursive(watcher, root_path, roots, root) .into_iter() .filter_map(|path| { - let abs_path = path.to_path(&config.root); + let abs_path = path.to_path(&root_path); let text = read_to_string(&abs_path)?; Some((path, text)) }) .collect(); sender.send(TaskResult::BulkLoadRoot { root, files }).unwrap(); - log::debug!("... loaded {}", config.root.as_path().display()); + log::debug!("... loaded {}", root_path.display()); } fn convert_notify_event(event: DebouncedEvent, sender: &Sender<(PathBuf, ChangeKind)>) { @@ -181,19 +182,18 @@ fn handle_change( None => return, Some(it) => it, }; - let config = &roots[root]; match kind { ChangeKind::Create => { let mut paths = Vec::new(); if path.is_dir() { - paths.extend(watch_recursive(watcher, &path, &config)); + paths.extend(watch_recursive(watcher, &path, roots, root)); } else { paths.push(rel_path); } paths .into_iter() .try_for_each(|rel_path| { - let abs_path = rel_path.to_path(&config.root); + let abs_path = rel_path.to_path(&roots.path(root)); let text = read_to_string(&abs_path); sender.send(TaskResult::SingleFile { root, path: rel_path, text }) }) @@ -209,12 +209,13 @@ fn handle_change( fn watch_recursive( mut watcher: Option<&mut RecommendedWatcher>, dir: &Path, - config: &RootConfig, + roots: &Roots, + root: VfsRoot, ) -> Vec { let mut files = Vec::new(); for entry in WalkDir::new(dir) .into_iter() - .filter_entry(|it| config.contains(it.path()).is_some()) + .filter_entry(|it| roots.contains(root, it.path()).is_some()) .filter_map(|it| it.map_err(|e| log::warn!("watcher error: {}", e)).ok()) { if entry.file_type().is_dir() { @@ -222,7 +223,7 @@ fn watch_recursive( watch_one(watcher, entry.path()); } } else { - let path = config.contains(entry.path()).unwrap(); + let path = roots.contains(root, entry.path()).unwrap(); files.push(path.to_owned()); } } diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs index a740e82ef..c78096ee1 100644 --- a/crates/ra_vfs/src/lib.rs +++ b/crates/ra_vfs/src/lib.rs @@ -31,7 +31,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ io::{TaskResult, Worker}, - roots::{RootConfig, Roots}, + roots::Roots, }; pub use crate::{ @@ -74,18 +74,18 @@ impl Vfs { let worker = io::start(Arc::clone(&roots)); let mut root2files = ArenaMap::default(); - for (root, config) in roots.iter() { + for root in roots.iter() { root2files.insert(root, Default::default()); - worker.sender().send(io::Task::AddRoot { root, config: Arc::clone(config) }).unwrap(); + worker.sender().send(io::Task::AddRoot { root }).unwrap(); } let res = Vfs { roots, files: Arena::default(), root2files, worker, pending_changes: Vec::new() }; - let vfs_roots = res.roots.iter().map(|(id, _)| id).collect(); + let vfs_roots = res.roots.iter().collect(); (res, vfs_roots) } pub fn root2path(&self, root: VfsRoot) -> PathBuf { - self.roots[root].root.clone() + self.roots.path(root).to_path_buf() } pub fn path2file(&self, path: &Path) -> Option { @@ -97,7 +97,7 @@ impl Vfs { pub fn file2path(&self, file: VfsFile) -> PathBuf { let rel_path = &self.files[file].path; - let root_path = &self.roots[self.files[file].root].root; + let root_path = &self.roots.path(self.files[file].root); rel_path.to_path(root_path) } @@ -232,7 +232,7 @@ impl Vfs { pub fn remove_file_overlay(&mut self, path: &Path) -> Option { 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 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 { diff --git a/crates/ra_vfs/src/roots.rs b/crates/ra_vfs/src/roots.rs index 564e12239..5e2776a35 100644 --- a/crates/ra_vfs/src/roots.rs +++ b/crates/ra_vfs/src/roots.rs @@ -1,11 +1,13 @@ use std::{ + iter, sync::Arc, path::{Path, PathBuf}, }; -use relative_path::RelativePathBuf; +use relative_path::{ RelativePath, RelativePathBuf}; use ra_arena::{impl_arena_id, Arena, RawId}; +/// VfsRoot identifies a watched directory on the file system. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct VfsRoot(pub RawId); impl_arena_id!(VfsRoot); @@ -15,88 +17,93 @@ impl_arena_id!(VfsRoot); /// `RootConfig` can be thought of as a glob pattern like `src/**.rs` which /// specifies the source root or as a function which takes a `PathBuf` and /// returns `true` iff path belongs to the source root -pub(crate) struct RootConfig { - pub(crate) root: PathBuf, +struct RootData { + path: PathBuf, // result of `root.canonicalize()` if that differs from `root`; `None` otherwise. - canonical_root: Option, - excluded_dirs: Vec, + canonical_path: Option, + excluded_dirs: Vec, } pub(crate) struct Roots { - roots: Arena>, + roots: Arena>, } -impl std::ops::Deref for Roots { - type Target = Arena>; - fn deref(&self) -> &Self::Target { - &self.roots - } -} +impl Roots { + pub(crate) fn new(mut paths: Vec) -> Roots { + let mut roots = Arena::default(); + // A hack to make nesting work. + paths.sort_by_key(|it| std::cmp::Reverse(it.as_os_str().len())); + paths.dedup(); + for (i, path) in paths.iter().enumerate() { + let nested_roots = + paths[..i].iter().filter_map(|it| rel_path(path, it)).collect::>(); + + let config = Arc::new(RootData::new(path.clone(), nested_roots)); -impl RootConfig { - fn new(root: PathBuf, excluded_dirs: Vec) -> RootConfig { - let mut canonical_root = root.canonicalize().ok(); - if Some(&root) == canonical_root.as_ref() { - canonical_root = None; + roots.alloc(config.clone()); } - RootConfig { root, canonical_root, excluded_dirs } + Roots { roots } + } + pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> { + self.iter().find_map(|root| { + let rel_path = self.contains(root, path)?; + Some((root, rel_path)) + }) + } + pub(crate) fn len(&self) -> usize { + self.roots.len() + } + pub(crate) fn iter<'a>(&'a self) -> impl Iterator + 'a { + self.roots.iter().map(|(id, _)| id) + } + pub(crate) fn path(&self, root: VfsRoot) -> &Path { + self.roots[root].path.as_path() } /// Checks if root contains a path and returns a root-relative path. - pub(crate) fn contains(&self, path: &Path) -> Option { - // First, check excluded dirs - if self.excluded_dirs.iter().any(|it| path.starts_with(it)) { - return None; + pub(crate) fn contains(&self, root: VfsRoot, path: &Path) -> Option { + let data = &self.roots[root]; + iter::once(&data.path) + .chain(data.canonical_path.as_ref().into_iter()) + .find_map(|base| rel_path(base, path)) + .filter(|path| !data.excluded_dirs.contains(path)) + .filter(|path| !data.is_excluded(path)) + } +} + +impl RootData { + fn new(path: PathBuf, excluded_dirs: Vec) -> RootData { + let mut canonical_path = path.canonicalize().ok(); + if Some(&path) == canonical_path.as_ref() { + canonical_path = None; } - let rel_path = path - .strip_prefix(&self.root) - .or_else(|err_payload| { - self.canonical_root - .as_ref() - .map_or(Err(err_payload), |canonical_root| path.strip_prefix(canonical_root)) - }) - .ok()?; - let rel_path = RelativePathBuf::from_path(rel_path).ok()?; + RootData { path, canonical_path, excluded_dirs } + } + fn is_excluded(&self, path: &RelativePath) -> bool { + if self.excluded_dirs.iter().any(|it| it == path) { + return true; + } // Ignore some common directories. // // FIXME: don't hard-code, specify at source-root creation time using // gitignore - for (i, c) in rel_path.components().enumerate() { + for (i, c) in path.components().enumerate() { if let relative_path::Component::Normal(c) = c { if (i == 0 && c == "target") || c == ".git" || c == "node_modules" { - return None; + return true; } } } - if path.is_file() && rel_path.extension() != Some("rs") { - return None; + match path.extension() { + None | Some("rs") => false, + _ => true, } - - Some(rel_path) } } -impl Roots { - pub(crate) fn new(mut paths: Vec) -> Roots { - let mut roots = Arena::default(); - // A hack to make nesting work. - paths.sort_by_key(|it| std::cmp::Reverse(it.as_os_str().len())); - paths.dedup(); - for (i, path) in paths.iter().enumerate() { - let nested_roots = paths[..i] - .iter() - .filter(|it| it.starts_with(path)) - .map(|it| it.clone()) - .collect::>(); - - let config = Arc::new(RootConfig::new(path.clone(), nested_roots)); - - roots.alloc(config.clone()); - } - Roots { roots } - } - pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> { - self.roots.iter().find_map(|(root, data)| data.contains(path).map(|it| (root, it))) - } +fn rel_path(base: &Path, path: &Path) -> Option { + let path = path.strip_prefix(base).ok()?; + let path = RelativePathBuf::from_path(path).unwrap(); + Some(path) } -- cgit v1.2.3