aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_vfs/src/io.rs45
-rw-r--r--crates/ra_vfs/src/lib.rs35
2 files changed, 42 insertions, 38 deletions
diff --git a/crates/ra_vfs/src/io.rs b/crates/ra_vfs/src/io.rs
index ff5ae3a19..dc0b84d5a 100644
--- a/crates/ra_vfs/src/io.rs
+++ b/crates/ra_vfs/src/io.rs
@@ -17,14 +17,28 @@ pub(crate) enum Task {
17 AddRoot { root: VfsRoot, config: Arc<RootConfig> }, 17 AddRoot { root: VfsRoot, config: Arc<RootConfig> },
18} 18}
19 19
20/// `TaskResult` transfers files read on the IO thread to the VFS on the main
21/// thread.
20#[derive(Debug)] 22#[derive(Debug)]
21pub enum TaskResult { 23pub enum TaskResult {
24 /// Emitted when we've recursively scanned a source root during the initial
25 /// load.
22 BulkLoadRoot { root: VfsRoot, files: Vec<(RelativePathBuf, String)> }, 26 BulkLoadRoot { root: VfsRoot, files: Vec<(RelativePathBuf, String)> },
23 AddSingleFile { root: VfsRoot, path: RelativePathBuf, text: String }, 27 /// Emitted when we've noticed that a single file has changed.
24 ChangeSingleFile { root: VfsRoot, path: RelativePathBuf, text: String }, 28 ///
25 RemoveSingleFile { root: VfsRoot, path: RelativePathBuf }, 29 /// Note that this by design does not distinguish between
30 /// create/delete/write events, and instead specifies the *current* state of
31 /// the file. The idea is to guarantee that in the quiescent state the sum
32 /// of all results equals to the current state of the file system, while
33 /// allowing to skip intermediate events in non-quiescent states.
34 SingleFile { root: VfsRoot, path: RelativePathBuf, text: Option<String> },
26} 35}
27 36
37/// The kind of raw notification we've received from the notify library.
38///
39/// Note that these are not necessary 100% precise (for example we might receive
40/// `Create` instead of `Write`, see #734), but we try do distinguish `Create`s
41/// to implement recursive watching of directories.
28#[derive(Debug)] 42#[derive(Debug)]
29enum ChangeKind { 43enum ChangeKind {
30 Create, 44 Create,
@@ -45,7 +59,7 @@ impl Worker {
45 // explained by the following concerns: 59 // explained by the following concerns:
46 // * we need to burn a thread translating from notify's mpsc to 60 // * we need to burn a thread translating from notify's mpsc to
47 // crossbeam_channel. 61 // crossbeam_channel.
48 // * we want to read all files from a single thread, to gurantee that 62 // * we want to read all files from a single thread, to guarantee that
49 // we always get fresher versions and never go back in time. 63 // we always get fresher versions and never go back in time.
50 // * we want to tear down everything neatly during shutdown. 64 // * we want to tear down everything neatly during shutdown.
51 let (worker, worker_handle) = thread_worker::spawn( 65 let (worker, worker_handle) = thread_worker::spawn(
@@ -63,7 +77,7 @@ impl Worker {
63 let mut watcher = notify::watcher(notify_sender, WATCHER_DELAY) 77 let mut watcher = notify::watcher(notify_sender, WATCHER_DELAY)
64 .map_err(|e| log::error!("failed to spawn notify {}", e)) 78 .map_err(|e| log::error!("failed to spawn notify {}", e))
65 .ok(); 79 .ok();
66 // Start a silly thread to tranform between two channels 80 // Start a silly thread to transform between two channels
67 let thread = thread::spawn(move || { 81 let thread = thread::spawn(move || {
68 notify_receiver 82 notify_receiver
69 .into_iter() 83 .into_iter()
@@ -98,7 +112,7 @@ impl Worker {
98 } 112 }
99 // Stopped the watcher 113 // Stopped the watcher
100 drop(watcher.take()); 114 drop(watcher.take());
101 // Drain pending events: we are not inrerested in them anyways! 115 // Drain pending events: we are not interested in them anyways!
102 watcher_receiver.into_iter().for_each(|_| ()); 116 watcher_receiver.into_iter().for_each(|_| ());
103 117
104 let res = thread.join(); 118 let res = thread.join();
@@ -199,23 +213,16 @@ fn handle_change(
199 } 213 }
200 paths 214 paths
201 .into_iter() 215 .into_iter()
202 .filter_map(|rel_path| { 216 .try_for_each(|rel_path| {
203 let abs_path = rel_path.to_path(&config.root); 217 let abs_path = rel_path.to_path(&config.root);
204 let text = read_to_string(&abs_path)?; 218 let text = read_to_string(&abs_path);
205 Some((rel_path, text)) 219 sender.send(TaskResult::SingleFile { root, path: rel_path, text })
206 })
207 .try_for_each(|(path, text)| {
208 sender.send(TaskResult::AddSingleFile { root, path, text })
209 }) 220 })
210 .unwrap() 221 .unwrap()
211 } 222 }
212 ChangeKind::Write => { 223 ChangeKind::Write | ChangeKind::Remove => {
213 if let Some(text) = read_to_string(&path) { 224 let text = read_to_string(&path);
214 sender.send(TaskResult::ChangeSingleFile { root, path: rel_path, text }).unwrap(); 225 sender.send(TaskResult::SingleFile { root, path: rel_path, text }).unwrap();
215 }
216 }
217 ChangeKind::Remove => {
218 sender.send(TaskResult::RemoveSingleFile { root, path: rel_path }).unwrap()
219 } 226 }
220 } 227 }
221} 228}
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs
index 3805be570..5d98d905c 100644
--- a/crates/ra_vfs/src/lib.rs
+++ b/crates/ra_vfs/src/lib.rs
@@ -37,8 +37,8 @@ impl_arena_id!(VfsRoot);
37 37
38/// Describes the contents of a single source root. 38/// Describes the contents of a single source root.
39/// 39///
40/// `RootConfig` can be thought of as a glob pattern like `src/**.rs` whihc 40/// `RootConfig` can be thought of as a glob pattern like `src/**.rs` which
41/// specifes the source root or as a function whihc takes a `PathBuf` and 41/// specifies the source root or as a function which takes a `PathBuf` and
42/// returns `true` iff path belongs to the source root 42/// returns `true` iff path belongs to the source root
43pub(crate) struct RootConfig { 43pub(crate) struct RootConfig {
44 root: PathBuf, 44 root: PathBuf,
@@ -60,7 +60,7 @@ impl RootConfig {
60 fn new(root: PathBuf, excluded_dirs: Vec<PathBuf>) -> RootConfig { 60 fn new(root: PathBuf, excluded_dirs: Vec<PathBuf>) -> RootConfig {
61 RootConfig { root, excluded_dirs } 61 RootConfig { root, excluded_dirs }
62 } 62 }
63 /// Cheks if root contains a path and returns a root-relative path. 63 /// Checks if root contains a path and returns a root-relative path.
64 pub(crate) fn contains(&self, path: &Path) -> Option<RelativePathBuf> { 64 pub(crate) fn contains(&self, path: &Path) -> Option<RelativePathBuf> {
65 // First, check excluded dirs 65 // First, check excluded dirs
66 if self.excluded_dirs.iter().any(|it| path.starts_with(it)) { 66 if self.excluded_dirs.iter().any(|it| path.starts_with(it)) {
@@ -210,7 +210,7 @@ impl Vfs {
210 match task { 210 match task {
211 TaskResult::BulkLoadRoot { root, files } => { 211 TaskResult::BulkLoadRoot { root, files } => {
212 let mut cur_files = Vec::new(); 212 let mut cur_files = Vec::new();
213 // While we were scanning the root in the backgound, a file might have 213 // While we were scanning the root in the background, a file might have
214 // been open in the editor, so we need to account for that. 214 // been open in the editor, so we need to account for that.
215 let exising = self.root2files[root] 215 let exising = self.root2files[root]
216 .iter() 216 .iter()
@@ -230,21 +230,18 @@ impl Vfs {
230 let change = VfsChange::AddRoot { root, files: cur_files }; 230 let change = VfsChange::AddRoot { root, files: cur_files };
231 self.pending_changes.push(change); 231 self.pending_changes.push(change);
232 } 232 }
233 TaskResult::AddSingleFile { root, path, text } => { 233 TaskResult::SingleFile { root, path, text } => {
234 if self.find_file(root, &path).is_none() { 234 match (self.find_file(root, &path), text) {
235 self.do_add_file(root, path, text, false); 235 (Some(file), None) => {
236 } 236 self.do_remove_file(root, path, file, false);
237 } 237 }
238 TaskResult::ChangeSingleFile { root, path, text } => { 238 (None, Some(text)) => {
239 if let Some(file) = self.find_file(root, &path) { 239 self.do_add_file(root, path, text, false);
240 self.do_change_file(file, text, false); 240 }
241 } else { 241 (Some(file), Some(text)) => {
242 self.do_add_file(root, path, text, false); 242 self.do_change_file(file, text, false);
243 } 243 }
244 } 244 (None, None) => (),
245 TaskResult::RemoveSingleFile { root, path } => {
246 if let Some(file) = self.find_file(root, &path) {
247 self.do_remove_file(root, path, file, false);
248 } 245 }
249 } 246 }
250 } 247 }