aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_vfs/src/watcher.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_vfs/src/watcher.rs')
-rw-r--r--crates/ra_vfs/src/watcher.rs61
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 @@
1use crate::io; 1use crate::io;
2use crossbeam_channel::Sender; 2use crossbeam_channel::Sender;
3use drop_bomb::DropBomb; 3use drop_bomb::DropBomb;
4use ignore; 4use ignore::{gitignore::Gitignore, Walk};
5use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcher}; 5use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcher};
6use parking_lot::Mutex; 6use parking_lot::Mutex;
7use std::{ 7use 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
66fn watch_one(watcher: &mut RecommendedWatcher, path: &Path) { 69fn 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
73fn watch_recursive(watcher: &Arc<Mutex<Option<RecommendedWatcher>>>, path: &Path) { 76fn 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
109fn 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
128const WATCHER_DELAY: Duration = Duration::from_millis(250);
129
97impl Watcher { 130impl 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<()> {