diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | crates/ra_vfs/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_vfs/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_vfs/src/watcher.rs | 37 | ||||
-rw-r--r-- | crates/ra_vfs/tests/vfs.rs | 106 |
5 files changed, 95 insertions, 52 deletions
diff --git a/Cargo.lock b/Cargo.lock index fccf8a65e..a694c2d91 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -1009,6 +1009,7 @@ version = "0.1.0" | |||
1009 | dependencies = [ | 1009 | dependencies = [ |
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 | "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)", |
1013 | "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)", |
1014 | "ra_arena 0.1.0", | 1015 | "ra_arena 0.1.0", |
diff --git a/crates/ra_vfs/Cargo.toml b/crates/ra_vfs/Cargo.toml index f7a972e91..b703cbd9f 100644 --- a/crates/ra_vfs/Cargo.toml +++ b/crates/ra_vfs/Cargo.toml | |||
@@ -18,3 +18,4 @@ ra_arena = { path = "../ra_arena" } | |||
18 | 18 | ||
19 | [dev-dependencies] | 19 | [dev-dependencies] |
20 | tempfile = "3" | 20 | tempfile = "3" |
21 | flexi_logger = "0.10.0" | ||
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs index 5336822b3..1ca94dcd6 100644 --- a/crates/ra_vfs/src/lib.rs +++ b/crates/ra_vfs/src/lib.rs | |||
@@ -98,7 +98,7 @@ impl Vfs { | |||
98 | pub fn new(mut roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) { | 98 | pub fn new(mut roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) { |
99 | let (worker, worker_handle) = io::start(); | 99 | let (worker, worker_handle) = io::start(); |
100 | 100 | ||
101 | let watcher = Watcher::new().unwrap(); // TODO return Result? | 101 | let watcher = Watcher::start().unwrap(); // TODO return Result? |
102 | 102 | ||
103 | let mut res = Vfs { | 103 | let mut res = Vfs { |
104 | roots: Arena::default(), | 104 | roots: Arena::default(), |
diff --git a/crates/ra_vfs/src/watcher.rs b/crates/ra_vfs/src/watcher.rs index cc05f949e..1aac23616 100644 --- a/crates/ra_vfs/src/watcher.rs +++ b/crates/ra_vfs/src/watcher.rs | |||
@@ -39,7 +39,6 @@ impl WatcherChange { | |||
39 | DebouncedEvent::Remove(path) => Some(WatcherChange::Remove(path)), | 39 | DebouncedEvent::Remove(path) => Some(WatcherChange::Remove(path)), |
40 | DebouncedEvent::Rename(src, dst) => Some(WatcherChange::Rename(src, dst)), | 40 | DebouncedEvent::Rename(src, dst) => Some(WatcherChange::Rename(src, dst)), |
41 | DebouncedEvent::Error(err, path) => { | 41 | DebouncedEvent::Error(err, path) => { |
42 | // TODO | ||
43 | log::warn!("watch error {}, {:?}", err, path); | 42 | log::warn!("watch error {}, {:?}", err, path); |
44 | None | 43 | None |
45 | } | 44 | } |
@@ -48,23 +47,17 @@ impl WatcherChange { | |||
48 | } | 47 | } |
49 | 48 | ||
50 | impl Watcher { | 49 | impl Watcher { |
51 | pub fn new() -> Result<Watcher, Box<std::error::Error>> { | 50 | pub fn start() -> Result<Watcher, Box<std::error::Error>> { |
52 | let (input_sender, input_receiver) = mpsc::channel(); | 51 | let (input_sender, input_receiver) = mpsc::channel(); |
53 | let watcher = notify::watcher(input_sender, Duration::from_millis(250))?; | 52 | let watcher = notify::watcher(input_sender, Duration::from_millis(250))?; |
54 | let (output_sender, output_receiver) = crossbeam_channel::unbounded(); | 53 | let (output_sender, output_receiver) = crossbeam_channel::unbounded(); |
55 | let thread = thread::spawn(move || loop { | 54 | let thread = thread::spawn(move || { |
56 | match input_receiver.recv() { | 55 | input_receiver |
57 | Ok(ev) => { | 56 | .into_iter() |
58 | // forward relevant events only | 57 | // forward relevant events only |
59 | if let Some(change) = WatcherChange::from_debounced_event(ev) { | 58 | .filter_map(WatcherChange::from_debounced_event) |
60 | output_sender.send(change).unwrap(); | 59 | .try_for_each(|change| output_sender.send(change)) |
61 | } | 60 | .unwrap() |
62 | } | ||
63 | Err(err) => { | ||
64 | log::debug!("Watcher stopped ({})", err); | ||
65 | break; | ||
66 | } | ||
67 | } | ||
68 | }); | 61 | }); |
69 | Ok(Watcher { | 62 | Ok(Watcher { |
70 | receiver: output_receiver, | 63 | receiver: output_receiver, |
@@ -86,11 +79,13 @@ impl Watcher { | |||
86 | pub fn shutdown(mut self) -> thread::Result<()> { | 79 | pub fn shutdown(mut self) -> thread::Result<()> { |
87 | self.bomb.defuse(); | 80 | self.bomb.defuse(); |
88 | drop(self.watcher); | 81 | drop(self.watcher); |
89 | let res = self.thread.join(); | 82 | // TODO this doesn't terminate for some reason |
90 | match &res { | 83 | // let res = self.thread.join(); |
91 | Ok(()) => log::info!("... Watcher terminated with ok"), | 84 | // match &res { |
92 | Err(_) => log::error!("... Watcher terminated with err"), | 85 | // Ok(()) => log::info!("... Watcher terminated with ok"), |
93 | } | 86 | // Err(_) => log::error!("... Watcher terminated with err"), |
94 | res | 87 | // } |
88 | // res | ||
89 | Ok(()) | ||
95 | } | 90 | } |
96 | } | 91 | } |
diff --git a/crates/ra_vfs/tests/vfs.rs b/crates/ra_vfs/tests/vfs.rs index f56fc4603..8634be9c4 100644 --- a/crates/ra_vfs/tests/vfs.rs +++ b/crates/ra_vfs/tests/vfs.rs | |||
@@ -1,14 +1,13 @@ | |||
1 | use std::{ | 1 | use std::{collections::HashSet, fs}; |
2 | fs, | ||
3 | collections::HashSet, | ||
4 | }; | ||
5 | |||
6 | use tempfile::tempdir; | ||
7 | 2 | ||
3 | use flexi_logger::Logger; | ||
8 | use ra_vfs::{Vfs, VfsChange}; | 4 | use ra_vfs::{Vfs, VfsChange}; |
5 | use tempfile::tempdir; | ||
9 | 6 | ||
10 | #[test] | 7 | #[test] |
11 | fn test_vfs_works() -> std::io::Result<()> { | 8 | fn test_vfs_works() -> std::io::Result<()> { |
9 | Logger::with_str("debug").start().unwrap(); | ||
10 | |||
12 | let files = [ | 11 | let files = [ |
13 | ("a/foo.rs", "hello"), | 12 | ("a/foo.rs", "hello"), |
14 | ("a/bar.rs", "world"), | 13 | ("a/bar.rs", "world"), |
@@ -58,42 +57,89 @@ fn test_vfs_works() -> std::io::Result<()> { | |||
58 | assert_eq!(files, expected_files); | 57 | assert_eq!(files, expected_files); |
59 | } | 58 | } |
60 | 59 | ||
61 | vfs.add_file_overlay(&dir.path().join("a/b/baz.rs"), "quux".to_string()); | 60 | // on disk change |
62 | let change = vfs.commit_changes().pop().unwrap(); | 61 | fs::write(&dir.path().join("a/b/baz.rs"), "quux").unwrap(); |
63 | match change { | 62 | let change = vfs.change_receiver().recv().unwrap(); |
64 | VfsChange::ChangeFile { text, .. } => assert_eq!(&*text, "quux"), | 63 | vfs.handle_change(change); |
65 | _ => panic!("unexpected change"), | 64 | match vfs.commit_changes().as_slice() { |
65 | [VfsChange::ChangeFile { text, .. }] => assert_eq!(text.as_str(), "quux"), | ||
66 | _ => panic!("unexpected changes"), | ||
66 | } | 67 | } |
67 | 68 | ||
68 | vfs.change_file_overlay(&dir.path().join("a/b/baz.rs"), "m".to_string()); | 69 | // in memory change |
69 | let change = vfs.commit_changes().pop().unwrap(); | 70 | vfs.change_file_overlay(&dir.path().join("a/b/baz.rs"), Some("m".to_string())); |
70 | match change { | 71 | match vfs.commit_changes().as_slice() { |
71 | VfsChange::ChangeFile { text, .. } => assert_eq!(&*text, "m"), | 72 | [VfsChange::ChangeFile { text, .. }] => assert_eq!(text.as_str(), "m"), |
72 | _ => panic!("unexpected change"), | 73 | _ => panic!("unexpected changes"), |
73 | } | 74 | } |
74 | 75 | ||
76 | // in memory remove, restores data on disk | ||
75 | vfs.remove_file_overlay(&dir.path().join("a/b/baz.rs")); | 77 | vfs.remove_file_overlay(&dir.path().join("a/b/baz.rs")); |
76 | let change = vfs.commit_changes().pop().unwrap(); | 78 | match vfs.commit_changes().as_slice() { |
77 | match change { | 79 | [VfsChange::ChangeFile { text, .. }] => assert_eq!(text.as_str(), "quux"), |
78 | VfsChange::ChangeFile { text, .. } => assert_eq!(&*text, "nested hello"), | 80 | _ => panic!("unexpected changes"), |
79 | _ => panic!("unexpected change"), | ||
80 | } | 81 | } |
81 | 82 | ||
82 | vfs.add_file_overlay(&dir.path().join("a/b/spam.rs"), "spam".to_string()); | 83 | // in memory add |
83 | let change = vfs.commit_changes().pop().unwrap(); | 84 | vfs.add_file_overlay(&dir.path().join("a/b/spam.rs"), Some("spam".to_string())); |
84 | match change { | 85 | match vfs.commit_changes().as_slice() { |
85 | VfsChange::AddFile { text, path, .. } => { | 86 | [VfsChange::AddFile { text, path, .. }] => { |
86 | assert_eq!(&*text, "spam"); | 87 | assert_eq!(text.as_str(), "spam"); |
87 | assert_eq!(path, "spam.rs"); | 88 | assert_eq!(path, "spam.rs"); |
88 | } | 89 | } |
89 | _ => panic!("unexpected change"), | 90 | _ => panic!("unexpected changes"), |
90 | } | 91 | } |
91 | 92 | ||
93 | // in memory remove | ||
92 | vfs.remove_file_overlay(&dir.path().join("a/b/spam.rs")); | 94 | vfs.remove_file_overlay(&dir.path().join("a/b/spam.rs")); |
93 | let change = vfs.commit_changes().pop().unwrap(); | 95 | match vfs.commit_changes().as_slice() { |
94 | match change { | 96 | [VfsChange::RemoveFile { path, .. }] => assert_eq!(path, "spam.rs"), |
95 | VfsChange::RemoveFile { .. } => (), | 97 | _ => panic!("unexpected changes"), |
96 | _ => panic!("unexpected change"), | 98 | } |
99 | |||
100 | // on disk add | ||
101 | fs::write(&dir.path().join("a/new.rs"), "new hello").unwrap(); | ||
102 | let change = vfs.change_receiver().recv().unwrap(); | ||
103 | vfs.handle_change(change); | ||
104 | match vfs.commit_changes().as_slice() { | ||
105 | [VfsChange::AddFile { text, path, .. }] => { | ||
106 | assert_eq!(text.as_str(), "new hello"); | ||
107 | assert_eq!(path, "new.rs"); | ||
108 | } | ||
109 | _ => panic!("unexpected changes"), | ||
110 | } | ||
111 | |||
112 | // on disk rename | ||
113 | fs::rename(&dir.path().join("a/new.rs"), &dir.path().join("a/new1.rs")).unwrap(); | ||
114 | let change = vfs.change_receiver().recv().unwrap(); | ||
115 | vfs.handle_change(change); | ||
116 | match vfs.commit_changes().as_slice() { | ||
117 | [VfsChange::RemoveFile { | ||
118 | path: removed_path, .. | ||
119 | }, VfsChange::AddFile { | ||
120 | text, | ||
121 | path: added_path, | ||
122 | .. | ||
123 | }] => { | ||
124 | assert_eq!(removed_path, "new.rs"); | ||
125 | assert_eq!(added_path, "new1.rs"); | ||
126 | assert_eq!(text.as_str(), "new hello"); | ||
127 | } | ||
128 | _ => panic!("unexpected changes"), | ||
129 | } | ||
130 | |||
131 | // on disk remove | ||
132 | fs::remove_file(&dir.path().join("a/new1.rs")).unwrap(); | ||
133 | let change = vfs.change_receiver().recv().unwrap(); | ||
134 | vfs.handle_change(change); | ||
135 | match vfs.commit_changes().as_slice() { | ||
136 | [VfsChange::RemoveFile { path, .. }] => assert_eq!(path, "new1.rs"), | ||
137 | _ => panic!("unexpected changes"), | ||
138 | } | ||
139 | |||
140 | match vfs.change_receiver().try_recv() { | ||
141 | Err(crossbeam_channel::TryRecvError::Empty) => (), | ||
142 | res => panic!("unexpected {:?}", res), | ||
97 | } | 143 | } |
98 | 144 | ||
99 | vfs.shutdown().unwrap(); | 145 | vfs.shutdown().unwrap(); |