diff options
-rw-r--r-- | crates/ra_vfs/src/io.rs | 45 | ||||
-rw-r--r-- | crates/ra_vfs/src/lib.rs | 35 |
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)] |
21 | pub enum TaskResult { | 23 | pub 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)] |
29 | enum ChangeKind { | 43 | enum 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 |
43 | pub(crate) struct RootConfig { | 43 | pub(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 | } |