diff options
Diffstat (limited to 'crates/ra_vfs/src/watcher.rs')
-rw-r--r-- | crates/ra_vfs/src/watcher.rs | 61 |
1 files changed, 47 insertions, 14 deletions
diff --git a/crates/ra_vfs/src/watcher.rs b/crates/ra_vfs/src/watcher.rs index 9d552f886..f0ef9bc0e 100644 --- a/crates/ra_vfs/src/watcher.rs +++ b/crates/ra_vfs/src/watcher.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use crate::io; | 1 | use crate::io; |
2 | use crossbeam_channel::Sender; | 2 | use crossbeam_channel::Sender; |
3 | use drop_bomb::DropBomb; | 3 | use drop_bomb::DropBomb; |
4 | use ignore; | 4 | use ignore::{gitignore::Gitignore, Walk}; |
5 | use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcher}; | 5 | use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcher}; |
6 | use parking_lot::Mutex; | 6 | use parking_lot::Mutex; |
7 | use std::{ | 7 | use std::{ |
@@ -40,8 +40,11 @@ fn handle_change_event( | |||
40 | sender.send(io::Task::HandleChange(WatcherChange::Rescan))?; | 40 | sender.send(io::Task::HandleChange(WatcherChange::Rescan))?; |
41 | } | 41 | } |
42 | DebouncedEvent::Create(path) => { | 42 | DebouncedEvent::Create(path) => { |
43 | if path.is_dir() { | 43 | // we have to check if `path` is ignored because Walk iterator doesn't check it |
44 | watch_recursive(watcher, &path); | 44 | // also childs are only ignored if they match a pattern |
45 | // (see `matched` vs `matched_path_or_any_parents` in `Gitignore`) | ||
46 | if path.is_dir() && !should_ignore_dir(&path) { | ||
47 | watch_recursive(watcher, &path, Some(sender)); | ||
45 | } | 48 | } |
46 | sender.send(io::Task::HandleChange(WatcherChange::Create(path)))?; | 49 | sender.send(io::Task::HandleChange(WatcherChange::Create(path)))?; |
47 | } | 50 | } |
@@ -63,15 +66,18 @@ fn handle_change_event( | |||
63 | Ok(()) | 66 | Ok(()) |
64 | } | 67 | } |
65 | 68 | ||
66 | fn watch_one(watcher: &mut RecommendedWatcher, path: &Path) { | 69 | fn watch_one(watcher: &mut RecommendedWatcher, dir: &Path) { |
67 | match watcher.watch(path, RecursiveMode::NonRecursive) { | 70 | match watcher.watch(dir, RecursiveMode::NonRecursive) { |
68 | Ok(()) => log::debug!("watching \"{}\"", path.display()), | 71 | Ok(()) => log::debug!("watching \"{}\"", dir.display()), |
69 | Err(e) => log::warn!("could not watch \"{}\": {}", path.display(), e), | 72 | Err(e) => log::warn!("could not watch \"{}\": {}", dir.display(), e), |
70 | } | 73 | } |
71 | } | 74 | } |
72 | 75 | ||
73 | fn watch_recursive(watcher: &Arc<Mutex<Option<RecommendedWatcher>>>, path: &Path) { | 76 | fn watch_recursive( |
74 | log::debug!("watch_recursive \"{}\"", path.display()); | 77 | watcher: &Arc<Mutex<Option<RecommendedWatcher>>>, |
78 | dir: &Path, | ||
79 | sender: Option<&Sender<io::Task>>, | ||
80 | ) { | ||
75 | let mut watcher = watcher.lock(); | 81 | let mut watcher = watcher.lock(); |
76 | let mut watcher = match *watcher { | 82 | let mut watcher = match *watcher { |
77 | Some(ref mut watcher) => watcher, | 83 | Some(ref mut watcher) => watcher, |
@@ -80,20 +86,47 @@ fn watch_recursive(watcher: &Arc<Mutex<Option<RecommendedWatcher>>>, path: &Path | |||
80 | return; | 86 | return; |
81 | } | 87 | } |
82 | }; | 88 | }; |
83 | // TODO it seems path itself isn't checked against ignores | 89 | for res in Walk::new(dir) { |
84 | // check if path should be ignored before walking it | ||
85 | for res in ignore::Walk::new(path) { | ||
86 | match res { | 90 | match res { |
87 | Ok(entry) => { | 91 | Ok(entry) => { |
88 | if entry.path().is_dir() { | 92 | if entry.path().is_dir() { |
89 | watch_one(&mut watcher, entry.path()); | 93 | watch_one(&mut watcher, entry.path()); |
90 | } | 94 | } |
95 | if let Some(sender) = sender { | ||
96 | // emit as create because we haven't seen it yet | ||
97 | if let Err(e) = sender.send(io::Task::HandleChange(WatcherChange::Create( | ||
98 | entry.path().to_path_buf(), | ||
99 | ))) { | ||
100 | log::warn!("watcher error: {}", e) | ||
101 | } | ||
102 | } | ||
91 | } | 103 | } |
92 | Err(e) => log::warn!("watcher error: {}", e), | 104 | Err(e) => log::warn!("watcher error: {}", e), |
93 | } | 105 | } |
94 | } | 106 | } |
95 | } | 107 | } |
96 | 108 | ||
109 | fn should_ignore_dir(dir: &Path) -> bool { | ||
110 | let mut parent = dir; | ||
111 | loop { | ||
112 | parent = match parent.parent() { | ||
113 | Some(p) => p, | ||
114 | None => break, | ||
115 | }; | ||
116 | let gitignore = parent.join(".gitignore"); | ||
117 | if gitignore.exists() { | ||
118 | let gitignore = Gitignore::new(gitignore).0; | ||
119 | if gitignore.matched_path_or_any_parents(dir, true).is_ignore() { | ||
120 | log::debug!("ignored {}", dir.display()); | ||
121 | return true; | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | false | ||
126 | } | ||
127 | |||
128 | const WATCHER_DELAY: Duration = Duration::from_millis(250); | ||
129 | |||
97 | impl Watcher { | 130 | impl Watcher { |
98 | pub(crate) fn start( | 131 | pub(crate) fn start( |
99 | output_sender: Sender<io::Task>, | 132 | output_sender: Sender<io::Task>, |
@@ -101,7 +134,7 @@ impl Watcher { | |||
101 | let (input_sender, input_receiver) = mpsc::channel(); | 134 | let (input_sender, input_receiver) = mpsc::channel(); |
102 | let watcher = Arc::new(Mutex::new(Some(notify::watcher( | 135 | let watcher = Arc::new(Mutex::new(Some(notify::watcher( |
103 | input_sender, | 136 | input_sender, |
104 | Duration::from_millis(250), | 137 | WATCHER_DELAY, |
105 | )?))); | 138 | )?))); |
106 | let w = watcher.clone(); | 139 | let w = watcher.clone(); |
107 | let thread = thread::spawn(move || { | 140 | let thread = thread::spawn(move || { |
@@ -119,7 +152,7 @@ impl Watcher { | |||
119 | } | 152 | } |
120 | 153 | ||
121 | pub fn watch(&mut self, root: impl AsRef<Path>) { | 154 | pub fn watch(&mut self, root: impl AsRef<Path>) { |
122 | watch_recursive(&self.watcher, root.as_ref()); | 155 | watch_recursive(&self.watcher, root.as_ref(), None); |
123 | } | 156 | } |
124 | 157 | ||
125 | pub fn shutdown(mut self) -> thread::Result<()> { | 158 | pub fn shutdown(mut self) -> thread::Result<()> { |