aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_vfs/src/io.rs271
-rw-r--r--crates/ra_vfs/src/io/watcher.rs200
-rw-r--r--crates/ra_vfs/src/lib.rs146
3 files changed, 281 insertions, 336 deletions
diff --git a/crates/ra_vfs/src/io.rs b/crates/ra_vfs/src/io.rs
index 7ca1e9835..d764c534a 100644
--- a/crates/ra_vfs/src/io.rs
+++ b/crates/ra_vfs/src/io.rs
@@ -1,19 +1,22 @@
1use std::{fs, sync::Arc, thread}; 1use std::{
2 2 fs,
3use crossbeam_channel::{Receiver, Sender}; 3 thread,
4 path::{Path, PathBuf},
5 sync::{mpsc, Arc},
6 time::Duration,
7};
8use crossbeam_channel::{Receiver, Sender, unbounded, RecvError, select};
4use relative_path::RelativePathBuf; 9use relative_path::RelativePathBuf;
5use thread_worker::WorkerHandle; 10use thread_worker::WorkerHandle;
6use walkdir::WalkDir; 11use walkdir::WalkDir;
12use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as _Watcher};
7 13
8mod watcher; 14use crate::{RootConfig, Roots, VfsRoot};
9use watcher::Watcher;
10
11use crate::{RootFilter, Roots, VfsRoot};
12 15
13pub(crate) enum Task { 16pub(crate) enum Task {
14 AddRoot { 17 AddRoot {
15 root: VfsRoot, 18 root: VfsRoot,
16 filter: Arc<RootFilter>, 19 config: Arc<RootConfig>,
17 }, 20 },
18} 21}
19 22
@@ -39,6 +42,15 @@ pub enum TaskResult {
39 }, 42 },
40} 43}
41 44
45#[derive(Debug)]
46enum ChangeKind {
47 Create,
48 Write,
49 Remove,
50}
51
52const WATCHER_DELAY: Duration = Duration::from_millis(250);
53
42pub(crate) struct Worker { 54pub(crate) struct Worker {
43 worker: thread_worker::Worker<Task, TaskResult>, 55 worker: thread_worker::Worker<Task, TaskResult>,
44 worker_handle: WorkerHandle, 56 worker_handle: WorkerHandle,
@@ -46,24 +58,75 @@ pub(crate) struct Worker {
46 58
47impl Worker { 59impl Worker {
48 pub(crate) fn start(roots: Arc<Roots>) -> Worker { 60 pub(crate) fn start(roots: Arc<Roots>) -> Worker {
49 let (worker, worker_handle) = 61 // This is a pretty elaborate setup of threads & channels! It is
50 thread_worker::spawn("vfs", 128, move |input_receiver, output_sender| { 62 // explained by the following concerns:
51 let mut watcher = match Watcher::start(roots, output_sender.clone()) { 63 // * we need to burn a thread translating from notify's mpsc to
52 Ok(w) => Some(w), 64 // crossbeam_channel.
53 Err(e) => { 65 // * we want to read all files from a single thread, to gurantee that
54 log::error!("could not start watcher: {}", e); 66 // we always get fresher versions and never go back in time.
55 None 67 // * we want to tear down everything neatly during shutdown.
68 let (worker, worker_handle) = thread_worker::spawn(
69 "vfs",
70 128,
71 // This are the channels we use to communicate with outside world.
72 // If `input_receiver` is closed we need to tear ourselves down.
73 // `output_sender` should not be closed unless the parent died.
74 move |input_receiver, output_sender| {
75 // These are `std` channels notify will send events to
76 let (notify_sender, notify_receiver) = mpsc::channel();
77 // These are the corresponding crossbeam channels
78 let (watcher_sender, watcher_receiver) = unbounded();
79
80 let mut watcher = notify::watcher(notify_sender, WATCHER_DELAY)
81 .map_err(|e| log::error!("failed to spawn notify {}", e))
82 .ok();
83 // Start a silly thread to tranform between two channels
84 let thread = thread::spawn(move || {
85 notify_receiver
86 .into_iter()
87 .for_each(|event| convert_notify_event(event, &watcher_sender))
88 });
89
90 // Process requests from the called or notifications from
91 // watcher until the caller says stop.
92 loop {
93 select! {
94 // Received request from the caller. If this channel is
95 // closed, we should shutdown everything.
96 recv(input_receiver) -> t => match t {
97 Err(RecvError) => {
98 drop(input_receiver);
99 break
100 },
101 Ok(Task::AddRoot { root, config }) => {
102 watch_root(watcher.as_mut(), &output_sender, root, Arc::clone(&config));
103 }
104 },
105 // Watcher send us changes. If **this** channel is
106 // closed, the watcher has died, which indicates a bug
107 // -- escalate!
108 recv(watcher_receiver) -> event => match event {
109 Err(RecvError) => panic!("watcher is dead"),
110 Ok((path, change)) => {
111 handle_change(watcher.as_mut(), &output_sender, &*roots, path, change);
112 }
113 },
56 } 114 }
57 };
58 let res = input_receiver
59 .into_iter()
60 .filter_map(|t| handle_task(t, &mut watcher))
61 .try_for_each(|it| output_sender.send(it));
62 if let Some(watcher) = watcher {
63 let _ = watcher.shutdown();
64 } 115 }
65 res.unwrap() 116 // Stopped the watcher
66 }); 117 drop(watcher.take());
118 // Drain pending events: we are not inrerested in them anyways!
119 watcher_receiver.into_iter().for_each(|_| ());
120
121 let res = thread.join();
122 match &res {
123 Ok(()) => log::info!("... Watcher terminated with ok"),
124 Err(_) => log::error!("... Watcher terminated with err"),
125 }
126 res.unwrap();
127 },
128 );
129
67 Worker { 130 Worker {
68 worker, 131 worker,
69 worker_handle, 132 worker_handle,
@@ -84,46 +147,142 @@ impl Worker {
84 } 147 }
85} 148}
86 149
87fn handle_task(task: Task, watcher: &mut Option<Watcher>) -> Option<TaskResult> { 150fn watch_root(
88 match task { 151 watcher: Option<&mut RecommendedWatcher>,
89 Task::AddRoot { root, filter } => { 152 sender: &Sender<TaskResult>,
90 if let Some(watcher) = watcher { 153 root: VfsRoot,
91 watcher.watch_root(&filter) 154 config: Arc<RootConfig>,
155) {
156 log::debug!("loading {} ...", config.root.as_path().display());
157 let files = watch_recursive(watcher, config.root.as_path(), &*config)
158 .into_iter()
159 .filter_map(|path| {
160 let abs_path = path.to_path(&config.root);
161 let text = read_to_string(&abs_path)?;
162 Some((path, text))
163 })
164 .collect();
165 sender
166 .send(TaskResult::BulkLoadRoot { root, files })
167 .unwrap();
168 log::debug!("... loaded {}", config.root.as_path().display());
169}
170
171fn convert_notify_event(event: DebouncedEvent, sender: &Sender<(PathBuf, ChangeKind)>) {
172 // forward relevant events only
173 match event {
174 DebouncedEvent::NoticeWrite(_)
175 | DebouncedEvent::NoticeRemove(_)
176 | DebouncedEvent::Chmod(_) => {
177 // ignore
178 }
179 DebouncedEvent::Rescan => {
180 // TODO rescan all roots
181 }
182 DebouncedEvent::Create(path) => {
183 sender.send((path, ChangeKind::Create)).unwrap();
184 }
185 DebouncedEvent::Write(path) => {
186 sender.send((path, ChangeKind::Write)).unwrap();
187 }
188 DebouncedEvent::Remove(path) => {
189 sender.send((path, ChangeKind::Remove)).unwrap();
190 }
191 DebouncedEvent::Rename(src, dst) => {
192 sender.send((src, ChangeKind::Remove)).unwrap();
193 sender.send((dst, ChangeKind::Create)).unwrap();
194 }
195 DebouncedEvent::Error(err, path) => {
196 // TODO should we reload the file contents?
197 log::warn!("watcher error \"{}\", {:?}", err, path);
198 }
199 }
200}
201
202fn handle_change(
203 watcher: Option<&mut RecommendedWatcher>,
204 sender: &Sender<TaskResult>,
205 roots: &Roots,
206 path: PathBuf,
207 kind: ChangeKind,
208) {
209 let (root, rel_path) = match roots.find(&path) {
210 None => return,
211 Some(it) => it,
212 };
213 let config = &roots[root];
214 match kind {
215 ChangeKind::Create => {
216 let mut paths = Vec::new();
217 if path.is_dir() {
218 paths.extend(watch_recursive(watcher, &path, &config));
219 } else {
220 paths.push(rel_path);
92 } 221 }
93 log::debug!("loading {} ...", filter.root.as_path().display()); 222 paths
94 let files = load_root(filter.as_ref()); 223 .into_iter()
95 log::debug!("... loaded {}", filter.root.as_path().display()); 224 .filter_map(|rel_path| {
96 Some(TaskResult::BulkLoadRoot { root, files }) 225 let abs_path = rel_path.to_path(&config.root);
226 let text = read_to_string(&abs_path)?;
227 Some((rel_path, text))
228 })
229 .try_for_each(|(path, text)| {
230 sender.send(TaskResult::AddSingleFile { root, path, text })
231 })
232 .unwrap()
97 } 233 }
234 ChangeKind::Write => {
235 if let Some(text) = read_to_string(&path) {
236 sender
237 .send(TaskResult::ChangeSingleFile {
238 root,
239 path: rel_path,
240 text,
241 })
242 .unwrap();
243 }
244 }
245 ChangeKind::Remove => sender
246 .send(TaskResult::RemoveSingleFile {
247 root,
248 path: rel_path,
249 })
250 .unwrap(),
98 } 251 }
99} 252}
100 253
101fn load_root(filter: &RootFilter) -> Vec<(RelativePathBuf, String)> { 254fn watch_recursive(
102 let mut res = Vec::new(); 255 mut watcher: Option<&mut RecommendedWatcher>,
103 for entry in WalkDir::new(&filter.root) 256 dir: &Path,
257 config: &RootConfig,
258) -> Vec<RelativePathBuf> {
259 let mut files = Vec::new();
260 for entry in WalkDir::new(dir)
104 .into_iter() 261 .into_iter()
105 .filter_entry(filter.entry_filter()) 262 .filter_entry(|it| config.contains(it.path()).is_some())
263 .filter_map(|it| it.map_err(|e| log::warn!("watcher error: {}", e)).ok())
106 { 264 {
107 let entry = match entry { 265 if entry.file_type().is_dir() {
108 Ok(entry) => entry, 266 if let Some(watcher) = &mut watcher {
109 Err(e) => { 267 watch_one(watcher, entry.path());
110 log::warn!("watcher error: {}", e);
111 continue;
112 } 268 }
113 }; 269 } else {
114 if !entry.file_type().is_file() { 270 let path = config.contains(entry.path()).unwrap();
115 continue; 271 files.push(path.to_owned());
116 } 272 }
117 let path = entry.path();
118 let text = match fs::read_to_string(path) {
119 Ok(text) => text,
120 Err(e) => {
121 log::warn!("watcher error: {}", e);
122 continue;
123 }
124 };
125 let path = RelativePathBuf::from_path(path.strip_prefix(&filter.root).unwrap()).unwrap();
126 res.push((path.to_owned(), text))
127 } 273 }
128 res 274 files
275}
276
277fn watch_one(watcher: &mut RecommendedWatcher, dir: &Path) {
278 match watcher.watch(dir, RecursiveMode::NonRecursive) {
279 Ok(()) => log::debug!("watching \"{}\"", dir.display()),
280 Err(e) => log::warn!("could not watch \"{}\": {}", dir.display(), e),
281 }
282}
283
284fn read_to_string(path: &Path) -> Option<String> {
285 fs::read_to_string(&path)
286 .map_err(|e| log::warn!("failed to read file {}", e))
287 .ok()
129} 288}
diff --git a/crates/ra_vfs/src/io/watcher.rs b/crates/ra_vfs/src/io/watcher.rs
deleted file mode 100644
index ff6775f59..000000000
--- a/crates/ra_vfs/src/io/watcher.rs
+++ /dev/null
@@ -1,200 +0,0 @@
1use crate::{io, RootFilter, Roots, VfsRoot};
2use crossbeam_channel::Sender;
3use drop_bomb::DropBomb;
4use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcher};
5use parking_lot::Mutex;
6use std::{
7 fs,
8 path::{Path, PathBuf},
9 sync::{mpsc, Arc},
10 thread,
11 time::Duration,
12};
13use walkdir::WalkDir;
14
15#[derive(Debug)]
16enum ChangeKind {
17 Create,
18 Write,
19 Remove,
20}
21
22const WATCHER_DELAY: Duration = Duration::from_millis(250);
23
24pub(crate) struct Watcher {
25 thread: thread::JoinHandle<()>,
26 bomb: DropBomb,
27 watcher: Arc<Mutex<Option<RecommendedWatcher>>>,
28}
29
30impl Watcher {
31 pub(crate) fn start(
32 roots: Arc<Roots>,
33 output_sender: Sender<io::TaskResult>,
34 ) -> Result<Watcher, Box<std::error::Error>> {
35 let (input_sender, input_receiver) = mpsc::channel();
36 let watcher = Arc::new(Mutex::new(Some(notify::watcher(
37 input_sender,
38 WATCHER_DELAY,
39 )?)));
40 let sender = output_sender.clone();
41 let watcher_clone = watcher.clone();
42 let thread = thread::spawn(move || {
43 let worker = WatcherWorker {
44 roots,
45 watcher: watcher_clone,
46 sender,
47 };
48 input_receiver
49 .into_iter()
50 // forward relevant events only
51 .try_for_each(|change| worker.handle_debounced_event(change))
52 .unwrap()
53 });
54 Ok(Watcher {
55 thread,
56 watcher,
57 bomb: DropBomb::new(format!("Watcher was not shutdown")),
58 })
59 }
60
61 pub fn watch_root(&mut self, filter: &RootFilter) {
62 for res in WalkDir::new(&filter.root)
63 .into_iter()
64 .filter_entry(filter.entry_filter())
65 {
66 match res {
67 Ok(entry) => {
68 if entry.file_type().is_dir() {
69 watch_one(self.watcher.as_ref(), entry.path());
70 }
71 }
72 Err(e) => log::warn!("watcher error: {}", e),
73 }
74 }
75 }
76
77 pub fn shutdown(mut self) -> thread::Result<()> {
78 self.bomb.defuse();
79 drop(self.watcher.lock().take());
80 let res = self.thread.join();
81 match &res {
82 Ok(()) => log::info!("... Watcher terminated with ok"),
83 Err(_) => log::error!("... Watcher terminated with err"),
84 }
85 res
86 }
87}
88
89struct WatcherWorker {
90 watcher: Arc<Mutex<Option<RecommendedWatcher>>>,
91 roots: Arc<Roots>,
92 sender: Sender<io::TaskResult>,
93}
94
95impl WatcherWorker {
96 fn handle_debounced_event(&self, ev: DebouncedEvent) -> Result<(), Box<std::error::Error>> {
97 match ev {
98 DebouncedEvent::NoticeWrite(_)
99 | DebouncedEvent::NoticeRemove(_)
100 | DebouncedEvent::Chmod(_) => {
101 // ignore
102 }
103 DebouncedEvent::Rescan => {
104 // TODO rescan all roots
105 }
106 DebouncedEvent::Create(path) => {
107 self.handle_change(path, ChangeKind::Create);
108 }
109 DebouncedEvent::Write(path) => {
110 self.handle_change(path, ChangeKind::Write);
111 }
112 DebouncedEvent::Remove(path) => {
113 self.handle_change(path, ChangeKind::Remove);
114 }
115 DebouncedEvent::Rename(src, dst) => {
116 self.handle_change(src, ChangeKind::Remove);
117 self.handle_change(dst, ChangeKind::Create);
118 }
119 DebouncedEvent::Error(err, path) => {
120 // TODO should we reload the file contents?
121 log::warn!("watcher error \"{}\", {:?}", err, path);
122 }
123 }
124 Ok(())
125 }
126
127 fn handle_change(&self, path: PathBuf, kind: ChangeKind) {
128 if let Err(e) = self.try_handle_change(path, kind) {
129 log::warn!("watcher error: {}", e)
130 }
131 }
132
133 fn try_handle_change(
134 &self,
135 path: PathBuf,
136 kind: ChangeKind,
137 ) -> Result<(), Box<std::error::Error>> {
138 let (root, rel_path) = match self.roots.find(&path) {
139 Some(x) => x,
140 None => return Ok(()),
141 };
142 match kind {
143 ChangeKind::Create => {
144 if path.is_dir() {
145 self.watch_recursive(&path, root);
146 } else {
147 let text = fs::read_to_string(&path)?;
148 self.sender.send(io::TaskResult::AddSingleFile {
149 root,
150 path: rel_path,
151 text,
152 })?
153 }
154 }
155 ChangeKind::Write => {
156 let text = fs::read_to_string(&path)?;
157 self.sender.send(io::TaskResult::ChangeSingleFile {
158 root,
159 path: rel_path,
160 text,
161 })?
162 }
163 ChangeKind::Remove => self.sender.send(io::TaskResult::RemoveSingleFile {
164 root,
165 path: rel_path,
166 })?,
167 }
168 Ok(())
169 }
170
171 fn watch_recursive(&self, dir: &Path, root: VfsRoot) {
172 let filter = &self.roots[root];
173 for res in WalkDir::new(dir)
174 .into_iter()
175 .filter_entry(filter.entry_filter())
176 {
177 match res {
178 Ok(entry) => {
179 if entry.file_type().is_dir() {
180 watch_one(self.watcher.as_ref(), entry.path());
181 } else {
182 // emit only for files otherwise we will cause watch_recursive to be called again with a dir that we are already watching
183 // emit as create because we haven't seen it yet
184 self.handle_change(entry.path().to_path_buf(), ChangeKind::Create);
185 }
186 }
187 Err(e) => log::warn!("watcher error: {}", e),
188 }
189 }
190 }
191}
192
193fn watch_one(watcher: &Mutex<Option<RecommendedWatcher>>, dir: &Path) {
194 if let Some(watcher) = watcher.lock().as_mut() {
195 match watcher.watch(dir, RecursiveMode::NonRecursive) {
196 Ok(()) => log::debug!("watching \"{}\"", dir.display()),
197 Err(e) => log::warn!("could not watch \"{}\": {}", dir.display(), e),
198 }
199 }
200}
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs
index 70a13f765..71a3f807d 100644
--- a/crates/ra_vfs/src/lib.rs
+++ b/crates/ra_vfs/src/lib.rs
@@ -18,94 +18,78 @@ mod io;
18use std::{ 18use std::{
19 cmp::Reverse, 19 cmp::Reverse,
20 fmt, fs, mem, 20 fmt, fs, mem,
21 ops::{Deref, DerefMut},
22 path::{Path, PathBuf}, 21 path::{Path, PathBuf},
23 sync::Arc, 22 sync::Arc,
24 thread, 23 thread,
25}; 24};
26 25
27use crossbeam_channel::Receiver; 26use crossbeam_channel::Receiver;
28use ra_arena::{impl_arena_id, Arena, RawId}; 27use ra_arena::{impl_arena_id, Arena, RawId, map::ArenaMap};
29use relative_path::{Component, RelativePath, RelativePathBuf}; 28use relative_path::{Component, RelativePath, RelativePathBuf};
30use rustc_hash::{FxHashMap, FxHashSet}; 29use rustc_hash::{FxHashMap, FxHashSet};
31use walkdir::DirEntry;
32 30
33pub use crate::io::TaskResult as VfsTask; 31pub use crate::io::TaskResult as VfsTask;
34use io::{TaskResult, Worker}; 32use io::{TaskResult, Worker};
35 33
36/// `RootFilter` is a predicate that checks if a file can belong to a root. If 34#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
37/// several filters match a file (nested dirs), the most nested one wins. 35pub struct VfsRoot(pub RawId);
38pub(crate) struct RootFilter { 36impl_arena_id!(VfsRoot);
37
38/// Describes the contents of a single source root.
39///
40/// `RootConfig` can be thought of as a glob pattern like `src/**.rs` whihc
41/// specifes the source root or as a function whihc takes a `PathBuf` and
42/// returns `true` iff path belongs to the source root
43pub(crate) struct RootConfig {
39 root: PathBuf, 44 root: PathBuf,
40 filter: fn(&Path, &RelativePath) -> bool,
41 excluded_dirs: Vec<PathBuf>, 45 excluded_dirs: Vec<PathBuf>,
42} 46}
43 47
44impl RootFilter { 48pub(crate) struct Roots {
45 fn new(root: PathBuf, excluded_dirs: Vec<PathBuf>) -> RootFilter { 49 roots: Arena<VfsRoot, Arc<RootConfig>>,
46 RootFilter { 50}
51
52impl std::ops::Deref for Roots {
53 type Target = Arena<VfsRoot, Arc<RootConfig>>;
54 fn deref(&self) -> &Self::Target {
55 &self.roots
56 }
57}
58
59impl RootConfig {
60 fn new(root: PathBuf, excluded_dirs: Vec<PathBuf>) -> RootConfig {
61 RootConfig {
47 root, 62 root,
48 filter: default_filter,
49 excluded_dirs, 63 excluded_dirs,
50 } 64 }
51 } 65 }
52 /// Check if this root can contain `path`. NB: even if this returns 66 /// Cheks if root contains a path and returns a root-relative path.
53 /// true, the `path` might actually be conained in some nested root. 67 pub(crate) fn contains(&self, path: &Path) -> Option<RelativePathBuf> {
54 pub(crate) fn can_contain(&self, path: &Path) -> Option<RelativePathBuf> { 68 // First, check excluded dirs
55 let rel_path = path.strip_prefix(&self.root).ok()?; 69 if self.excluded_dirs.iter().any(|it| path.starts_with(it)) {
56 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
57 if !(self.filter)(path, rel_path.as_relative_path()) {
58 return None; 70 return None;
59 } 71 }
60 Some(rel_path) 72 let rel_path = path.strip_prefix(&self.root).ok()?;
61 } 73 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
62
63 pub(crate) fn entry_filter<'a>(&'a self) -> impl FnMut(&DirEntry) -> bool + 'a {
64 move |entry: &DirEntry| {
65 if entry.file_type().is_dir() && self.excluded_dirs.iter().any(|it| it == entry.path())
66 {
67 // do not walk nested roots
68 false
69 } else {
70 self.can_contain(entry.path()).is_some()
71 }
72 }
73 }
74}
75 74
76pub(crate) fn default_filter(path: &Path, rel_path: &RelativePath) -> bool { 75 // Ignore some common directories.
77 if path.is_dir() { 76 //
77 // FIXME: don't hard-code, specify at source-root creation time using
78 // gitignore
78 for (i, c) in rel_path.components().enumerate() { 79 for (i, c) in rel_path.components().enumerate() {
79 if let Component::Normal(c) = c { 80 if let Component::Normal(c) = c {
80 // TODO hardcoded for now
81 if (i == 0 && c == "target") || c == ".git" || c == "node_modules" { 81 if (i == 0 && c == "target") || c == ".git" || c == "node_modules" {
82 return false; 82 return None;
83 } 83 }
84 } 84 }
85 } 85 }
86 true
87 } else {
88 rel_path.extension() == Some("rs")
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
93pub struct VfsRoot(pub RawId);
94impl_arena_id!(VfsRoot);
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
97pub struct VfsFile(pub RawId);
98impl_arena_id!(VfsFile);
99 86
100struct VfsFileData { 87 if path.is_file() && rel_path.extension() != Some("rs") {
101 root: VfsRoot, 88 return None;
102 path: RelativePathBuf, 89 }
103 is_overlayed: bool,
104 text: Arc<String>,
105}
106 90
107pub(crate) struct Roots { 91 Some(rel_path)
108 roots: Arena<VfsRoot, Arc<RootFilter>>, 92 }
109} 93}
110 94
111impl Roots { 95impl Roots {
@@ -120,59 +104,61 @@ impl Roots {
120 .map(|it| it.clone()) 104 .map(|it| it.clone())
121 .collect::<Vec<_>>(); 105 .collect::<Vec<_>>();
122 106
123 let root_filter = Arc::new(RootFilter::new(path.clone(), nested_roots)); 107 let config = Arc::new(RootConfig::new(path.clone(), nested_roots));
124 108
125 roots.alloc(root_filter.clone()); 109 roots.alloc(config.clone());
126 } 110 }
127 Roots { roots } 111 Roots { roots }
128 } 112 }
129 pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> { 113 pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> {
130 self.roots 114 self.roots
131 .iter() 115 .iter()
132 .find_map(|(root, data)| data.can_contain(path).map(|it| (root, it))) 116 .find_map(|(root, data)| data.contains(path).map(|it| (root, it)))
133 } 117 }
134} 118}
135 119
136impl Deref for Roots { 120#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
137 type Target = Arena<VfsRoot, Arc<RootFilter>>; 121pub struct VfsFile(pub RawId);
138 fn deref(&self) -> &Self::Target { 122impl_arena_id!(VfsFile);
139 &self.roots
140 }
141}
142 123
143impl DerefMut for Roots { 124struct VfsFileData {
144 fn deref_mut(&mut self) -> &mut Self::Target { 125 root: VfsRoot,
145 &mut self.roots 126 path: RelativePathBuf,
146 } 127 is_overlayed: bool,
128 text: Arc<String>,
147} 129}
148 130
149pub struct Vfs { 131pub struct Vfs {
150 roots: Arc<Roots>, 132 roots: Arc<Roots>,
151 files: Arena<VfsFile, VfsFileData>, 133 files: Arena<VfsFile, VfsFileData>,
152 root2files: FxHashMap<VfsRoot, FxHashSet<VfsFile>>, 134 root2files: ArenaMap<VfsRoot, FxHashSet<VfsFile>>,
153 pending_changes: Vec<VfsChange>, 135 pending_changes: Vec<VfsChange>,
154 worker: Worker, 136 worker: Worker,
155} 137}
156 138
157impl fmt::Debug for Vfs { 139impl fmt::Debug for Vfs {
158 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 140 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159 f.write_str("Vfs { ... }") 141 f.debug_struct("Vfs")
142 .field("n_roots", &self.roots.len())
143 .field("n_files", &self.files.len())
144 .field("n_pending_changes", &self.pending_changes.len())
145 .finish()
160 } 146 }
161} 147}
162 148
163impl Vfs { 149impl Vfs {
164 pub fn new(roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) { 150 pub fn new(roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) {
165 let roots = Arc::new(Roots::new(roots)); 151 let roots = Arc::new(Roots::new(roots));
166 let worker = io::Worker::start(roots.clone()); 152 let worker = io::Worker::start(Arc::clone(&roots));
167 let mut root2files = FxHashMap::default(); 153 let mut root2files = ArenaMap::default();
168 154
169 for (root, filter) in roots.iter() { 155 for (root, config) in roots.iter() {
170 root2files.insert(root, Default::default()); 156 root2files.insert(root, Default::default());
171 worker 157 worker
172 .sender() 158 .sender()
173 .send(io::Task::AddRoot { 159 .send(io::Task::AddRoot {
174 root, 160 root,
175 filter: filter.clone(), 161 config: Arc::clone(config),
176 }) 162 })
177 .unwrap(); 163 .unwrap();
178 } 164 }
@@ -242,7 +228,7 @@ impl Vfs {
242 let mut cur_files = Vec::new(); 228 let mut cur_files = Vec::new();
243 // While we were scanning the root in the backgound, a file might have 229 // While we were scanning the root in the backgound, a file might have
244 // been open in the editor, so we need to account for that. 230 // been open in the editor, so we need to account for that.
245 let exising = self.root2files[&root] 231 let exising = self.root2files[root]
246 .iter() 232 .iter()
247 .map(|&file| (self.files[file].path.clone(), file)) 233 .map(|&file| (self.files[file].path.clone(), file))
248 .collect::<FxHashMap<_, _>>(); 234 .collect::<FxHashMap<_, _>>();
@@ -384,7 +370,7 @@ impl Vfs {
384 is_overlayed, 370 is_overlayed,
385 }; 371 };
386 let file = self.files.alloc(data); 372 let file = self.files.alloc(data);
387 self.root2files.get_mut(&root).unwrap().insert(file); 373 self.root2files.get_mut(root).unwrap().insert(file);
388 file 374 file
389 } 375 }
390 376
@@ -399,7 +385,7 @@ impl Vfs {
399 self.files[file].text = Default::default(); 385 self.files[file].text = Default::default();
400 self.files[file].path = Default::default(); 386 self.files[file].path = Default::default();
401 let root = self.files[file].root; 387 let root = self.files[file].root;
402 let removed = self.root2files.get_mut(&root).unwrap().remove(&file); 388 let removed = self.root2files.get_mut(root).unwrap().remove(&file);
403 assert!(removed); 389 assert!(removed);
404 } 390 }
405 391
@@ -410,7 +396,7 @@ impl Vfs {
410 } 396 }
411 397
412 fn find_file(&self, root: VfsRoot, path: &RelativePath) -> Option<VfsFile> { 398 fn find_file(&self, root: VfsRoot, path: &RelativePath) -> Option<VfsFile> {
413 self.root2files[&root] 399 self.root2files[root]
414 .iter() 400 .iter()
415 .map(|&it| it) 401 .map(|&it| it)
416 .find(|&file| self.files[file].path == path) 402 .find(|&file| self.files[file].path == path)