diff options
author | Bernardo <berublan@gmail.com> | 2019-01-12 17:17:52 +0000 |
---|---|---|
committer | Aleksey Kladov <aleksey.kladov@gmail.com> | 2019-01-26 08:46:16 +0000 |
commit | 76bf7498aa88c4de4517f4eb1218807fdfc7071b (patch) | |
tree | e95b92ce0f54df298d59eb96450bd6bd06ba2939 /crates/ra_vfs/src | |
parent | 6b86f038d61752bbf306ed5dd9def74be3b5dcc1 (diff) |
handle watched events filtering in `Vfs`add `is_overlayed`load changed files contents in `io`
Diffstat (limited to 'crates/ra_vfs/src')
-rw-r--r-- | crates/ra_vfs/src/io.rs | 74 | ||||
-rw-r--r-- | crates/ra_vfs/src/lib.rs | 165 | ||||
-rw-r--r-- | crates/ra_vfs/src/watcher.rs | 85 |
3 files changed, 203 insertions, 121 deletions
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 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | fmt, | 2 | fmt, fs, |
3 | fs, | ||
4 | path::{Path, PathBuf}, | 3 | path::{Path, PathBuf}, |
5 | }; | 4 | }; |
6 | 5 | ||
7 | use walkdir::{DirEntry, WalkDir}; | ||
8 | use thread_worker::{WorkerHandle}; | ||
9 | use relative_path::RelativePathBuf; | 6 | use relative_path::RelativePathBuf; |
7 | use thread_worker::WorkerHandle; | ||
8 | use walkdir::{DirEntry, WalkDir}; | ||
10 | 9 | ||
11 | use crate::{VfsRoot, has_rs_extension}; | 10 | use crate::{has_rs_extension, watcher::WatcherChange, VfsRoot}; |
12 | 11 | ||
13 | pub(crate) enum Task { | 12 | pub(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)] |
29 | pub enum WatcherChangeResult { | 28 | pub 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 | ||
49 | pub enum TaskResult { | 34 | pub enum TaskResult { |
50 | AddRoot(AddRootResult), | 35 | AddRoot(AddRootResult), |
51 | WatcherChange(WatcherChangeResult), | 36 | HandleChange(WatcherChange), |
37 | LoadChange(Option<WatcherChangeData>), | ||
52 | } | 38 | } |
53 | 39 | ||
54 | impl fmt::Debug for TaskResult { | 40 | impl 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 | |||
104 | fn 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); | |||
75 | struct VfsFileData { | 75 | struct 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 | ||
8 | use crate::io; | ||
8 | use crossbeam_channel::Sender; | 9 | use crossbeam_channel::Sender; |
9 | use drop_bomb::DropBomb; | 10 | use drop_bomb::DropBomb; |
10 | use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcher}; | 11 | use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcher}; |
11 | use crate::{has_rs_extension, io}; | ||
12 | 12 | ||
13 | pub struct Watcher { | 13 | pub 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 | ||
28 | impl WatcherChange { | 27 | fn 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 | ||
79 | impl Watcher { | 61 | impl 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 { |