aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock2
-rw-r--r--crates/ra_vfs/src/io.rs74
-rw-r--r--crates/ra_vfs/src/lib.rs165
-rw-r--r--crates/ra_vfs/src/watcher.rs85
-rw-r--r--crates/ra_vfs/tests/vfs.rs35
5 files changed, 218 insertions, 143 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a694c2d91..b8c840c68 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1009,7 +1009,7 @@ version = "0.1.0"
1009dependencies = [ 1009dependencies = [
1010 "crossbeam-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 1010 "crossbeam-channel 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
1011 "drop_bomb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 1011 "drop_bomb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
1012 "flexi_logger 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", 1012 "flexi_logger 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
1013 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 1013 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
1014 "notify 4.0.7 (registry+https://github.com/rust-lang/crates.io-index)", 1014 "notify 4.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
1015 "ra_arena 0.1.0", 1015 "ra_arena 0.1.0",
diff --git a/crates/ra_vfs/src/io.rs b/crates/ra_vfs/src/io.rs
index 79dea5dc7..a0c87fb25 100644
--- a/crates/ra_vfs/src/io.rs
+++ b/crates/ra_vfs/src/io.rs
@@ -1,14 +1,13 @@
1use std::{ 1use std::{
2 fmt, 2 fmt, fs,
3 fs,
4 path::{Path, PathBuf}, 3 path::{Path, PathBuf},
5}; 4};
6 5
7use walkdir::{DirEntry, WalkDir};
8use thread_worker::{WorkerHandle};
9use relative_path::RelativePathBuf; 6use relative_path::RelativePathBuf;
7use thread_worker::WorkerHandle;
8use walkdir::{DirEntry, WalkDir};
10 9
11use crate::{VfsRoot, has_rs_extension}; 10use crate::{has_rs_extension, watcher::WatcherChange, VfsRoot};
12 11
13pub(crate) enum Task { 12pub(crate) enum Task {
14 AddRoot { 13 AddRoot {
@@ -16,7 +15,7 @@ pub(crate) enum Task {
16 path: PathBuf, 15 path: PathBuf,
17 filter: Box<Fn(&DirEntry) -> bool + Send>, 16 filter: Box<Fn(&DirEntry) -> bool + Send>,
18 }, 17 },
19 WatcherChange(crate::watcher::WatcherChange), 18 LoadChange(crate::watcher::WatcherChange),
20} 19}
21 20
22#[derive(Debug)] 21#[derive(Debug)]
@@ -26,29 +25,16 @@ pub struct AddRootResult {
26} 25}
27 26
28#[derive(Debug)] 27#[derive(Debug)]
29pub enum WatcherChangeResult { 28pub enum WatcherChangeData {
30 Create { 29 Create { path: PathBuf, text: String },
31 path: PathBuf, 30 Write { path: PathBuf, text: String },
32 text: String, 31 Remove { path: PathBuf },
33 },
34 Write {
35 path: PathBuf,
36 text: String,
37 },
38 Remove {
39 path: PathBuf,
40 },
41 // can this be replaced and use Remove and Create instead?
42 Rename {
43 src: PathBuf,
44 dst: PathBuf,
45 text: String,
46 },
47} 32}
48 33
49pub enum TaskResult { 34pub enum TaskResult {
50 AddRoot(AddRootResult), 35 AddRoot(AddRootResult),
51 WatcherChange(WatcherChangeResult), 36 HandleChange(WatcherChange),
37 LoadChange(Option<WatcherChangeData>),
52} 38}
53 39
54impl fmt::Debug for TaskResult { 40impl fmt::Debug for TaskResult {
@@ -77,9 +63,10 @@ fn handle_task(task: Task) -> TaskResult {
77 log::debug!("... loaded {}", path.as_path().display()); 63 log::debug!("... loaded {}", path.as_path().display());
78 TaskResult::AddRoot(AddRootResult { root, files }) 64 TaskResult::AddRoot(AddRootResult { root, files })
79 } 65 }
80 Task::WatcherChange(change) => { 66 Task::LoadChange(change) => {
81 // TODO 67 log::debug!("loading {:?} ...", change);
82 unimplemented!() 68 let data = load_change(change);
69 TaskResult::LoadChange(data)
83 } 70 }
84 } 71 }
85} 72}
@@ -113,3 +100,34 @@ fn load_root(root: &Path, filter: &dyn Fn(&DirEntry) -> bool) -> Vec<(RelativePa
113 } 100 }
114 res 101 res
115} 102}
103
104fn load_change(change: WatcherChange) -> Option<WatcherChangeData> {
105 let data = match change {
106 WatcherChange::Create(path) => {
107 let text = match fs::read_to_string(&path) {
108 Ok(text) => text,
109 Err(e) => {
110 log::warn!("watcher error: {}", e);
111 return None;
112 }
113 };
114 WatcherChangeData::Create { path, text }
115 }
116 WatcherChange::Write(path) => {
117 let text = match fs::read_to_string(&path) {
118 Ok(text) => text,
119 Err(e) => {
120 log::warn!("watcher error: {}", e);
121 return None;
122 }
123 };
124 WatcherChangeData::Write { path, text }
125 }
126 WatcherChange::Remove(path) => WatcherChangeData::Remove { path },
127 WatcherChange::Rescan => {
128 // this should be handled by Vfs::handle_task
129 return None;
130 }
131 };
132 Some(data)
133}
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs
index 889ed788d..2930f6b80 100644
--- a/crates/ra_vfs/src/lib.rs
+++ b/crates/ra_vfs/src/lib.rs
@@ -75,6 +75,7 @@ impl_arena_id!(VfsFile);
75struct VfsFileData { 75struct VfsFileData {
76 root: VfsRoot, 76 root: VfsRoot,
77 path: RelativePathBuf, 77 path: RelativePathBuf,
78 is_overlayed: bool,
78 text: Arc<String>, 79 text: Arc<String>,
79} 80}
80 81
@@ -170,7 +171,7 @@ impl Vfs {
170 } else { 171 } else {
171 let text = fs::read_to_string(path).unwrap_or_default(); 172 let text = fs::read_to_string(path).unwrap_or_default();
172 let text = Arc::new(text); 173 let text = Arc::new(text);
173 let file = self.add_file(root, rel_path.clone(), Arc::clone(&text)); 174 let file = self.add_file(root, rel_path.clone(), Arc::clone(&text), false);
174 let change = VfsChange::AddFile { 175 let change = VfsChange::AddFile {
175 file, 176 file,
176 text, 177 text,
@@ -205,7 +206,7 @@ impl Vfs {
205 continue; 206 continue;
206 } 207 }
207 let text = Arc::new(text); 208 let text = Arc::new(text);
208 let file = self.add_file(task.root, path.clone(), Arc::clone(&text)); 209 let file = self.add_file(task.root, path.clone(), Arc::clone(&text), false);
209 files.push((file, path, text)); 210 files.push((file, path, text));
210 } 211 }
211 212
@@ -215,63 +216,132 @@ impl Vfs {
215 }; 216 };
216 self.pending_changes.push(change); 217 self.pending_changes.push(change);
217 } 218 }
218 io::TaskResult::WatcherChange(change) => { 219 io::TaskResult::HandleChange(change) => match &change {
219 // TODO 220 watcher::WatcherChange::Create(path)
220 unimplemented!() 221 | watcher::WatcherChange::Remove(path)
222 | watcher::WatcherChange::Write(path) => {
223 if self.should_handle_change(&path) {
224 self.worker.inp.send(io::Task::LoadChange(change)).unwrap()
225 }
226 }
227 watcher::WatcherChange::Rescan => {
228 // TODO send Task::AddRoot?
229 }
230 },
231 io::TaskResult::LoadChange(None) => {}
232 io::TaskResult::LoadChange(Some(change)) => match change {
233 io::WatcherChangeData::Create { path, text }
234 | io::WatcherChangeData::Write { path, text } => {
235 if let Some((root, path, file)) = self.find_root(&path) {
236 if let Some(file) = file {
237 self.do_change_file(file, text, false);
238 } else {
239 self.do_add_file(root, path, text, false);
240 }
241 }
242 }
243 io::WatcherChangeData::Remove { path } => {
244 if let Some((root, path, file)) = self.find_root(&path) {
245 if let Some(file) = file {
246 self.do_remove_file(root, path, file, false);
247 }
248 }
249 }
250 },
251 }
252 }
253
254 fn should_handle_change(&self, path: &Path) -> bool {
255 if let Some((_root, _rel_path, file)) = self.find_root(&path) {
256 if let Some(file) = file {
257 if self.files[file].is_overlayed {
258 // file is overlayed
259 return false;
260 }
221 } 261 }
262 true
263 } else {
264 // file doesn't belong to any root
265 false
222 } 266 }
223 } 267 }
224 268
269 fn do_add_file(
270 &mut self,
271 root: VfsRoot,
272 path: RelativePathBuf,
273 text: String,
274 is_overlay: bool,
275 ) -> Option<VfsFile> {
276 let text = Arc::new(text);
277 let file = self.add_file(root, path.clone(), text.clone(), is_overlay);
278 self.pending_changes.push(VfsChange::AddFile {
279 file,
280 root,
281 path,
282 text,
283 });
284 Some(file)
285 }
286
287 fn do_change_file(&mut self, file: VfsFile, text: String, is_overlay: bool) {
288 if !is_overlay && self.files[file].is_overlayed {
289 return;
290 }
291 let text = Arc::new(text);
292 self.change_file(file, text.clone(), is_overlay);
293 self.pending_changes
294 .push(VfsChange::ChangeFile { file, text });
295 }
296
297 fn do_remove_file(
298 &mut self,
299 root: VfsRoot,
300 path: RelativePathBuf,
301 file: VfsFile,
302 is_overlay: bool,
303 ) {
304 if !is_overlay && self.files[file].is_overlayed {
305 return;
306 }
307 self.remove_file(file);
308 self.pending_changes
309 .push(VfsChange::RemoveFile { root, path, file });
310 }
311
225 pub fn add_file_overlay(&mut self, path: &Path, text: String) -> Option<VfsFile> { 312 pub fn add_file_overlay(&mut self, path: &Path, text: String) -> Option<VfsFile> {
226 let mut res = None;
227 if let Some((root, rel_path, file)) = self.find_root(path) { 313 if let Some((root, rel_path, file)) = self.find_root(path) {
228 let text = Arc::new(text); 314 if let Some(file) = file {
229 let change = if let Some(file) = file { 315 self.do_change_file(file, text, true);
230 res = Some(file); 316 Some(file)
231 self.change_file(file, Arc::clone(&text));
232 VfsChange::ChangeFile { file, text }
233 } else { 317 } else {
234 let file = self.add_file(root, rel_path.clone(), Arc::clone(&text)); 318 self.do_add_file(root, rel_path, text, true)
235 res = Some(file); 319 }
236 VfsChange::AddFile { 320 } else {
237 file, 321 None
238 text,
239 root,
240 path: rel_path,
241 }
242 };
243 self.pending_changes.push(change);
244 } 322 }
245 res
246 } 323 }
247 324
248 pub fn change_file_overlay(&mut self, path: &Path, new_text: String) { 325 pub fn change_file_overlay(&mut self, path: &Path, new_text: String) {
249 if let Some((_root, _path, file)) = self.find_root(path) { 326 if let Some((_root, _path, file)) = self.find_root(path) {
250 let file = file.expect("can't change a file which wasn't added"); 327 let file = file.expect("can't change a file which wasn't added");
251 let text = Arc::new(new_text); 328 self.do_change_file(file, new_text, true);
252 self.change_file(file, Arc::clone(&text));
253 let change = VfsChange::ChangeFile { file, text };
254 self.pending_changes.push(change);
255 } 329 }
256 } 330 }
257 331
258 pub fn remove_file_overlay(&mut self, path: &Path) -> Option<VfsFile> { 332 pub fn remove_file_overlay(&mut self, path: &Path) -> Option<VfsFile> {
259 let mut res = None;
260 if let Some((root, path, file)) = self.find_root(path) { 333 if let Some((root, path, file)) = self.find_root(path) {
261 let file = file.expect("can't remove a file which wasn't added"); 334 let file = file.expect("can't remove a file which wasn't added");
262 res = Some(file);
263 let full_path = path.to_path(&self.roots[root].root); 335 let full_path = path.to_path(&self.roots[root].root);
264 let change = if let Ok(text) = fs::read_to_string(&full_path) { 336 if let Ok(text) = fs::read_to_string(&full_path) {
265 let text = Arc::new(text); 337 self.do_change_file(file, text, true);
266 self.change_file(file, Arc::clone(&text));
267 VfsChange::ChangeFile { file, text }
268 } else { 338 } else {
269 self.remove_file(file); 339 self.do_remove_file(root, path, file, true);
270 VfsChange::RemoveFile { root, file, path } 340 }
271 }; 341 Some(file)
272 self.pending_changes.push(change); 342 } else {
343 None
273 } 344 }
274 res
275 } 345 }
276 346
277 pub fn commit_changes(&mut self) -> Vec<VfsChange> { 347 pub fn commit_changes(&mut self) -> Vec<VfsChange> {
@@ -285,15 +355,28 @@ impl Vfs {
285 self.worker_handle.shutdown() 355 self.worker_handle.shutdown()
286 } 356 }
287 357
288 fn add_file(&mut self, root: VfsRoot, path: RelativePathBuf, text: Arc<String>) -> VfsFile { 358 fn add_file(
289 let data = VfsFileData { root, path, text }; 359 &mut self,
360 root: VfsRoot,
361 path: RelativePathBuf,
362 text: Arc<String>,
363 is_overlayed: bool,
364 ) -> VfsFile {
365 let data = VfsFileData {
366 root,
367 path,
368 text,
369 is_overlayed,
370 };
290 let file = self.files.alloc(data); 371 let file = self.files.alloc(data);
291 self.root2files.get_mut(&root).unwrap().insert(file); 372 self.root2files.get_mut(&root).unwrap().insert(file);
292 file 373 file
293 } 374 }
294 375
295 fn change_file(&mut self, file: VfsFile, new_text: Arc<String>) { 376 fn change_file(&mut self, file: VfsFile, new_text: Arc<String>, is_overlayed: bool) {
296 self.files[file].text = new_text; 377 let mut file_data = &mut self.files[file];
378 file_data.text = new_text;
379 file_data.is_overlayed = is_overlayed;
297 } 380 }
298 381
299 fn remove_file(&mut self, file: VfsFile) { 382 fn remove_file(&mut self, file: VfsFile) {
diff --git a/crates/ra_vfs/src/watcher.rs b/crates/ra_vfs/src/watcher.rs
index a6d0496c0..a5401869c 100644
--- a/crates/ra_vfs/src/watcher.rs
+++ b/crates/ra_vfs/src/watcher.rs
@@ -5,10 +5,10 @@ use std::{
5 time::Duration, 5 time::Duration,
6}; 6};
7 7
8use crate::io;
8use crossbeam_channel::Sender; 9use crossbeam_channel::Sender;
9use drop_bomb::DropBomb; 10use drop_bomb::DropBomb;
10use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcher}; 11use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcher};
11use crate::{has_rs_extension, io};
12 12
13pub struct Watcher { 13pub struct Watcher {
14 watcher: RecommendedWatcher, 14 watcher: RecommendedWatcher,
@@ -21,59 +21,41 @@ pub enum WatcherChange {
21 Create(PathBuf), 21 Create(PathBuf),
22 Write(PathBuf), 22 Write(PathBuf),
23 Remove(PathBuf), 23 Remove(PathBuf),
24 // can this be replaced and use Remove and Create instead? 24 Rescan,
25 Rename(PathBuf, PathBuf),
26} 25}
27 26
28impl WatcherChange { 27fn send_change_events(
29 fn try_from_debounced_event(ev: DebouncedEvent) -> Option<WatcherChange> { 28 ev: DebouncedEvent,
30 match ev { 29 sender: &Sender<io::Task>,
31 DebouncedEvent::NoticeWrite(_) 30) -> Result<(), Box<std::error::Error>> {
32 | DebouncedEvent::NoticeRemove(_) 31 match ev {
33 | DebouncedEvent::Chmod(_) => { 32 DebouncedEvent::NoticeWrite(_)
34 // ignore 33 | DebouncedEvent::NoticeRemove(_)
35 None 34 | DebouncedEvent::Chmod(_) => {
36 } 35 // ignore
37 DebouncedEvent::Rescan => { 36 }
38 // TODO should we rescan the root? 37 DebouncedEvent::Rescan => {
39 None 38 sender.send(io::Task::LoadChange(WatcherChange::Rescan))?;
40 } 39 }
41 DebouncedEvent::Create(path) => { 40 DebouncedEvent::Create(path) => {
42 if has_rs_extension(&path) { 41 sender.send(io::Task::LoadChange(WatcherChange::Create(path)))?;
43 Some(WatcherChange::Create(path)) 42 }
44 } else { 43 DebouncedEvent::Write(path) => {
45 None 44 sender.send(io::Task::LoadChange(WatcherChange::Write(path)))?;
46 } 45 }
47 } 46 DebouncedEvent::Remove(path) => {
48 DebouncedEvent::Write(path) => { 47 sender.send(io::Task::LoadChange(WatcherChange::Remove(path)))?;
49 if has_rs_extension(&path) { 48 }
50 Some(WatcherChange::Write(path)) 49 DebouncedEvent::Rename(src, dst) => {
51 } else { 50 sender.send(io::Task::LoadChange(WatcherChange::Remove(src)))?;
52 None 51 sender.send(io::Task::LoadChange(WatcherChange::Create(dst)))?;
53 } 52 }
54 } 53 DebouncedEvent::Error(err, path) => {
55 DebouncedEvent::Remove(path) => { 54 // TODO should we reload the file contents?
56 if has_rs_extension(&path) { 55 log::warn!("watcher error {}, {:?}", err, path);
57 Some(WatcherChange::Remove(path))
58 } else {
59 None
60 }
61 }
62 DebouncedEvent::Rename(src, dst) => {
63 match (has_rs_extension(&src), has_rs_extension(&dst)) {
64 (true, true) => Some(WatcherChange::Rename(src, dst)),
65 (true, false) => Some(WatcherChange::Remove(src)),
66 (false, true) => Some(WatcherChange::Create(dst)),
67 (false, false) => None,
68 }
69 }
70 DebouncedEvent::Error(err, path) => {
71 // TODO should we reload the file contents?
72 log::warn!("watch error {}, {:?}", err, path);
73 None
74 }
75 } 56 }
76 } 57 }
58 Ok(())
77} 59}
78 60
79impl Watcher { 61impl Watcher {
@@ -86,8 +68,7 @@ impl Watcher {
86 input_receiver 68 input_receiver
87 .into_iter() 69 .into_iter()
88 // forward relevant events only 70 // forward relevant events only
89 .filter_map(WatcherChange::try_from_debounced_event) 71 .try_for_each(|change| send_change_events(change, &output_sender))
90 .try_for_each(|change| output_sender.send(io::Task::WatcherChange(change)))
91 .unwrap() 72 .unwrap()
92 }); 73 });
93 Ok(Watcher { 74 Ok(Watcher {
diff --git a/crates/ra_vfs/tests/vfs.rs b/crates/ra_vfs/tests/vfs.rs
index 21d5633b1..9cde2bed7 100644
--- a/crates/ra_vfs/tests/vfs.rs
+++ b/crates/ra_vfs/tests/vfs.rs
@@ -4,6 +4,13 @@ use flexi_logger::Logger;
4use ra_vfs::{Vfs, VfsChange}; 4use ra_vfs::{Vfs, VfsChange};
5use tempfile::tempdir; 5use tempfile::tempdir;
6 6
7fn process_tasks(vfs: &mut Vfs, num_tasks: u32) {
8 for _ in 0..num_tasks {
9 let task = vfs.task_receiver().recv().unwrap();
10 vfs.handle_task(task);
11 }
12}
13
7#[test] 14#[test]
8fn test_vfs_works() -> std::io::Result<()> { 15fn test_vfs_works() -> std::io::Result<()> {
9 Logger::with_str("debug").start().unwrap(); 16 Logger::with_str("debug").start().unwrap();
@@ -25,10 +32,7 @@ fn test_vfs_works() -> std::io::Result<()> {
25 let b_root = dir.path().join("a/b"); 32 let b_root = dir.path().join("a/b");
26 33
27 let (mut vfs, _) = Vfs::new(vec![a_root, b_root]); 34 let (mut vfs, _) = Vfs::new(vec![a_root, b_root]);
28 for _ in 0..2 { 35 process_tasks(&mut vfs, 2);
29 let task = vfs.task_receiver().recv().unwrap();
30 vfs.handle_task(task);
31 }
32 { 36 {
33 let files = vfs 37 let files = vfs
34 .commit_changes() 38 .commit_changes()
@@ -57,30 +61,26 @@ fn test_vfs_works() -> std::io::Result<()> {
57 assert_eq!(files, expected_files); 61 assert_eq!(files, expected_files);
58 } 62 }
59 63
60 // on disk change
61 fs::write(&dir.path().join("a/b/baz.rs"), "quux").unwrap(); 64 fs::write(&dir.path().join("a/b/baz.rs"), "quux").unwrap();
62 let task = vfs.task_receiver().recv().unwrap(); 65 process_tasks(&mut vfs, 1);
63 vfs.handle_task(task);
64 match vfs.commit_changes().as_slice() { 66 match vfs.commit_changes().as_slice() {
65 [VfsChange::ChangeFile { text, .. }] => assert_eq!(text.as_str(), "quux"), 67 [VfsChange::ChangeFile { text, .. }] => assert_eq!(text.as_str(), "quux"),
66 _ => panic!("unexpected changes"), 68 _ => panic!("unexpected changes"),
67 } 69 }
68 70
69 // in memory change
70 vfs.change_file_overlay(&dir.path().join("a/b/baz.rs"), "m".to_string()); 71 vfs.change_file_overlay(&dir.path().join("a/b/baz.rs"), "m".to_string());
71 match vfs.commit_changes().as_slice() { 72 match vfs.commit_changes().as_slice() {
72 [VfsChange::ChangeFile { text, .. }] => assert_eq!(text.as_str(), "m"), 73 [VfsChange::ChangeFile { text, .. }] => assert_eq!(text.as_str(), "m"),
73 _ => panic!("unexpected changes"), 74 _ => panic!("unexpected changes"),
74 } 75 }
75 76
76 // in memory remove, restores data on disk 77 // removing overlay restores data on disk
77 vfs.remove_file_overlay(&dir.path().join("a/b/baz.rs")); 78 vfs.remove_file_overlay(&dir.path().join("a/b/baz.rs"));
78 match vfs.commit_changes().as_slice() { 79 match vfs.commit_changes().as_slice() {
79 [VfsChange::ChangeFile { text, .. }] => assert_eq!(text.as_str(), "quux"), 80 [VfsChange::ChangeFile { text, .. }] => assert_eq!(text.as_str(), "quux"),
80 _ => panic!("unexpected changes"), 81 _ => panic!("unexpected changes"),
81 } 82 }
82 83
83 // in memory add
84 vfs.add_file_overlay(&dir.path().join("a/b/spam.rs"), "spam".to_string()); 84 vfs.add_file_overlay(&dir.path().join("a/b/spam.rs"), "spam".to_string());
85 match vfs.commit_changes().as_slice() { 85 match vfs.commit_changes().as_slice() {
86 [VfsChange::AddFile { text, path, .. }] => { 86 [VfsChange::AddFile { text, path, .. }] => {
@@ -90,17 +90,14 @@ fn test_vfs_works() -> std::io::Result<()> {
90 _ => panic!("unexpected changes"), 90 _ => panic!("unexpected changes"),
91 } 91 }
92 92
93 // in memory remove
94 vfs.remove_file_overlay(&dir.path().join("a/b/spam.rs")); 93 vfs.remove_file_overlay(&dir.path().join("a/b/spam.rs"));
95 match vfs.commit_changes().as_slice() { 94 match vfs.commit_changes().as_slice() {
96 [VfsChange::RemoveFile { path, .. }] => assert_eq!(path, "spam.rs"), 95 [VfsChange::RemoveFile { path, .. }] => assert_eq!(path, "spam.rs"),
97 _ => panic!("unexpected changes"), 96 _ => panic!("unexpected changes"),
98 } 97 }
99 98
100 // on disk add
101 fs::write(&dir.path().join("a/new.rs"), "new hello").unwrap(); 99 fs::write(&dir.path().join("a/new.rs"), "new hello").unwrap();
102 let task = vfs.task_receiver().recv().unwrap(); 100 process_tasks(&mut vfs, 1);
103 vfs.handle_task(task);
104 match vfs.commit_changes().as_slice() { 101 match vfs.commit_changes().as_slice() {
105 [VfsChange::AddFile { text, path, .. }] => { 102 [VfsChange::AddFile { text, path, .. }] => {
106 assert_eq!(text.as_str(), "new hello"); 103 assert_eq!(text.as_str(), "new hello");
@@ -109,10 +106,8 @@ fn test_vfs_works() -> std::io::Result<()> {
109 _ => panic!("unexpected changes"), 106 _ => panic!("unexpected changes"),
110 } 107 }
111 108
112 // on disk rename
113 fs::rename(&dir.path().join("a/new.rs"), &dir.path().join("a/new1.rs")).unwrap(); 109 fs::rename(&dir.path().join("a/new.rs"), &dir.path().join("a/new1.rs")).unwrap();
114 let task = vfs.task_receiver().recv().unwrap(); 110 process_tasks(&mut vfs, 2);
115 vfs.handle_task(task);
116 match vfs.commit_changes().as_slice() { 111 match vfs.commit_changes().as_slice() {
117 [VfsChange::RemoveFile { 112 [VfsChange::RemoveFile {
118 path: removed_path, .. 113 path: removed_path, ..
@@ -125,13 +120,11 @@ fn test_vfs_works() -> std::io::Result<()> {
125 assert_eq!(added_path, "new1.rs"); 120 assert_eq!(added_path, "new1.rs");
126 assert_eq!(text.as_str(), "new hello"); 121 assert_eq!(text.as_str(), "new hello");
127 } 122 }
128 _ => panic!("unexpected changes"), 123 xs => panic!("unexpected changes {:?}", xs),
129 } 124 }
130 125
131 // on disk remove
132 fs::remove_file(&dir.path().join("a/new1.rs")).unwrap(); 126 fs::remove_file(&dir.path().join("a/new1.rs")).unwrap();
133 let task = vfs.task_receiver().recv().unwrap(); 127 process_tasks(&mut vfs, 1);
134 vfs.handle_task(task);
135 match vfs.commit_changes().as_slice() { 128 match vfs.commit_changes().as_slice() {
136 [VfsChange::RemoveFile { path, .. }] => assert_eq!(path, "new1.rs"), 129 [VfsChange::RemoveFile { path, .. }] => assert_eq!(path, "new1.rs"),
137 _ => panic!("unexpected changes"), 130 _ => panic!("unexpected changes"),