diff options
Diffstat (limited to 'crates/ra_vfs/src/io.rs')
-rw-r--r-- | crates/ra_vfs/src/io.rs | 200 |
1 files changed, 51 insertions, 149 deletions
diff --git a/crates/ra_vfs/src/io.rs b/crates/ra_vfs/src/io.rs index 83a021c2f..7ca1e9835 100644 --- a/crates/ra_vfs/src/io.rs +++ b/crates/ra_vfs/src/io.rs | |||
@@ -1,95 +1,72 @@ | |||
1 | use std::{ | 1 | use std::{fs, sync::Arc, thread}; |
2 | fmt, fs, | ||
3 | path::{Path, PathBuf}, | ||
4 | sync::Arc, | ||
5 | thread, | ||
6 | }; | ||
7 | 2 | ||
8 | use crossbeam_channel::{Receiver, Sender}; | 3 | use crossbeam_channel::{Receiver, Sender}; |
9 | use parking_lot::Mutex; | ||
10 | use relative_path::RelativePathBuf; | 4 | use relative_path::RelativePathBuf; |
11 | use thread_worker::WorkerHandle; | 5 | use thread_worker::WorkerHandle; |
12 | use walkdir::WalkDir; | 6 | use walkdir::WalkDir; |
13 | 7 | ||
14 | mod watcher; | 8 | mod watcher; |
15 | use watcher::Watcher; | 9 | use watcher::Watcher; |
16 | pub use watcher::WatcherChange; | ||
17 | 10 | ||
18 | use crate::{RootFilter, VfsRoot}; | 11 | use crate::{RootFilter, Roots, VfsRoot}; |
19 | 12 | ||
20 | pub(crate) enum Task { | 13 | pub(crate) enum Task { |
21 | AddRoot { | 14 | AddRoot { |
22 | root: VfsRoot, | 15 | root: VfsRoot, |
23 | path: PathBuf, | 16 | filter: Arc<RootFilter>, |
24 | root_filter: Arc<RootFilter>, | ||
25 | nested_roots: Vec<PathBuf>, | ||
26 | }, | ||
27 | /// this variant should only be created by the watcher | ||
28 | HandleChange(WatcherChange), | ||
29 | LoadChange(WatcherChange), | ||
30 | Watch { | ||
31 | dir: PathBuf, | ||
32 | root_filter: Arc<RootFilter>, | ||
33 | }, | 17 | }, |
34 | } | 18 | } |
35 | 19 | ||
36 | #[derive(Debug)] | 20 | #[derive(Debug)] |
37 | pub struct AddRootResult { | ||
38 | pub(crate) root: VfsRoot, | ||
39 | pub(crate) files: Vec<(RelativePathBuf, String)>, | ||
40 | } | ||
41 | |||
42 | #[derive(Debug)] | ||
43 | pub enum WatcherChangeData { | ||
44 | Create { path: PathBuf, text: String }, | ||
45 | Write { path: PathBuf, text: String }, | ||
46 | Remove { path: PathBuf }, | ||
47 | } | ||
48 | |||
49 | pub enum TaskResult { | 21 | pub enum TaskResult { |
50 | AddRoot(AddRootResult), | 22 | BulkLoadRoot { |
51 | HandleChange(WatcherChange), | 23 | root: VfsRoot, |
52 | LoadChange(WatcherChangeData), | 24 | files: Vec<(RelativePathBuf, String)>, |
53 | } | 25 | }, |
54 | 26 | AddSingleFile { | |
55 | impl fmt::Debug for TaskResult { | 27 | root: VfsRoot, |
56 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 28 | path: RelativePathBuf, |
57 | match self { | 29 | text: String, |
58 | TaskResult::AddRoot(..) => f.write_str("TaskResult::AddRoot(..)"), | 30 | }, |
59 | TaskResult::HandleChange(c) => write!(f, "TaskResult::HandleChange({:?})", c), | 31 | ChangeSingleFile { |
60 | TaskResult::LoadChange(c) => write!(f, "TaskResult::LoadChange({:?})", c), | 32 | root: VfsRoot, |
61 | } | 33 | path: RelativePathBuf, |
62 | } | 34 | text: String, |
35 | }, | ||
36 | RemoveSingleFile { | ||
37 | root: VfsRoot, | ||
38 | path: RelativePathBuf, | ||
39 | }, | ||
63 | } | 40 | } |
64 | 41 | ||
65 | pub(crate) struct Worker { | 42 | pub(crate) struct Worker { |
66 | worker: thread_worker::Worker<Task, TaskResult>, | 43 | worker: thread_worker::Worker<Task, TaskResult>, |
67 | worker_handle: WorkerHandle, | 44 | worker_handle: WorkerHandle, |
68 | watcher: Arc<Mutex<Option<Watcher>>>, | ||
69 | } | 45 | } |
70 | 46 | ||
71 | impl Worker { | 47 | impl Worker { |
72 | pub(crate) fn start() -> Worker { | 48 | pub(crate) fn start(roots: Arc<Roots>) -> Worker { |
73 | let watcher = Arc::new(Mutex::new(None)); | ||
74 | let watcher_clone = watcher.clone(); | ||
75 | let (worker, worker_handle) = | 49 | let (worker, worker_handle) = |
76 | thread_worker::spawn("vfs", 128, move |input_receiver, output_sender| { | 50 | thread_worker::spawn("vfs", 128, move |input_receiver, output_sender| { |
77 | input_receiver | 51 | let mut watcher = match Watcher::start(roots, output_sender.clone()) { |
52 | Ok(w) => Some(w), | ||
53 | Err(e) => { | ||
54 | log::error!("could not start watcher: {}", e); | ||
55 | None | ||
56 | } | ||
57 | }; | ||
58 | let res = input_receiver | ||
78 | .into_iter() | 59 | .into_iter() |
79 | .filter_map(|t| handle_task(t, &watcher_clone)) | 60 | .filter_map(|t| handle_task(t, &mut watcher)) |
80 | .try_for_each(|it| output_sender.send(it)) | 61 | .try_for_each(|it| output_sender.send(it)); |
81 | .unwrap() | 62 | if let Some(watcher) = watcher { |
63 | let _ = watcher.shutdown(); | ||
64 | } | ||
65 | res.unwrap() | ||
82 | }); | 66 | }); |
83 | match Watcher::start(worker.inp.clone()) { | ||
84 | Ok(w) => { | ||
85 | watcher.lock().replace(w); | ||
86 | } | ||
87 | Err(e) => log::error!("could not start watcher: {}", e), | ||
88 | }; | ||
89 | Worker { | 67 | Worker { |
90 | worker, | 68 | worker, |
91 | worker_handle, | 69 | worker_handle, |
92 | watcher, | ||
93 | } | 70 | } |
94 | } | 71 | } |
95 | 72 | ||
@@ -102,72 +79,31 @@ impl Worker { | |||
102 | } | 79 | } |
103 | 80 | ||
104 | pub(crate) fn shutdown(self) -> thread::Result<()> { | 81 | pub(crate) fn shutdown(self) -> thread::Result<()> { |
105 | if let Some(watcher) = self.watcher.lock().take() { | ||
106 | let _ = watcher.shutdown(); | ||
107 | } | ||
108 | let _ = self.worker.shutdown(); | 82 | let _ = self.worker.shutdown(); |
109 | self.worker_handle.shutdown() | 83 | self.worker_handle.shutdown() |
110 | } | 84 | } |
111 | } | 85 | } |
112 | 86 | ||
113 | fn watch( | 87 | fn handle_task(task: Task, watcher: &mut Option<Watcher>) -> Option<TaskResult> { |
114 | watcher: &Arc<Mutex<Option<Watcher>>>, | ||
115 | dir: &Path, | ||
116 | filter_entry: &RootFilter, | ||
117 | emit_for_existing: bool, | ||
118 | ) { | ||
119 | if let Some(watcher) = watcher.lock().as_mut() { | ||
120 | watcher.watch_recursive(dir, filter_entry, emit_for_existing) | ||
121 | } | ||
122 | } | ||
123 | |||
124 | fn handle_task(task: Task, watcher: &Arc<Mutex<Option<Watcher>>>) -> Option<TaskResult> { | ||
125 | match task { | 88 | match task { |
126 | Task::AddRoot { | 89 | Task::AddRoot { root, filter } => { |
127 | root, | 90 | if let Some(watcher) = watcher { |
128 | path, | 91 | watcher.watch_root(&filter) |
129 | root_filter, | 92 | } |
130 | nested_roots, | 93 | log::debug!("loading {} ...", filter.root.as_path().display()); |
131 | } => { | 94 | let files = load_root(filter.as_ref()); |
132 | watch(watcher, &path, root_filter.as_ref(), false); | 95 | log::debug!("... loaded {}", filter.root.as_path().display()); |
133 | log::debug!("loading {} ...", path.as_path().display()); | 96 | Some(TaskResult::BulkLoadRoot { root, files }) |
134 | let files = load_root( | ||
135 | path.as_path(), | ||
136 | root_filter.as_ref(), | ||
137 | nested_roots.as_slice(), | ||
138 | ); | ||
139 | log::debug!("... loaded {}", path.as_path().display()); | ||
140 | Some(TaskResult::AddRoot(AddRootResult { root, files })) | ||
141 | } | ||
142 | Task::HandleChange(change) => { | ||
143 | // forward as is because Vfs has to decide if we should load it | ||
144 | Some(TaskResult::HandleChange(change)) | ||
145 | } | ||
146 | Task::LoadChange(change) => { | ||
147 | log::debug!("loading {:?} ...", change); | ||
148 | load_change(change).map(TaskResult::LoadChange) | ||
149 | } | ||
150 | Task::Watch { dir, root_filter } => { | ||
151 | watch(watcher, &dir, root_filter.as_ref(), true); | ||
152 | None | ||
153 | } | 97 | } |
154 | } | 98 | } |
155 | } | 99 | } |
156 | 100 | ||
157 | fn load_root( | 101 | fn load_root(filter: &RootFilter) -> Vec<(RelativePathBuf, String)> { |
158 | root: &Path, | ||
159 | root_filter: &RootFilter, | ||
160 | nested_roots: &[PathBuf], | ||
161 | ) -> Vec<(RelativePathBuf, String)> { | ||
162 | let mut res = Vec::new(); | 102 | let mut res = Vec::new(); |
163 | for entry in WalkDir::new(root).into_iter().filter_entry(|entry| { | 103 | for entry in WalkDir::new(&filter.root) |
164 | if entry.file_type().is_dir() && nested_roots.iter().any(|it| it == entry.path()) { | 104 | .into_iter() |
165 | // do not load files of a nested root | 105 | .filter_entry(filter.entry_filter()) |
166 | false | 106 | { |
167 | } else { | ||
168 | root_filter.can_contain(entry.path()).is_some() | ||
169 | } | ||
170 | }) { | ||
171 | let entry = match entry { | 107 | let entry = match entry { |
172 | Ok(entry) => entry, | 108 | Ok(entry) => entry, |
173 | Err(e) => { | 109 | Err(e) => { |
@@ -186,42 +122,8 @@ fn load_root( | |||
186 | continue; | 122 | continue; |
187 | } | 123 | } |
188 | }; | 124 | }; |
189 | let path = RelativePathBuf::from_path(path.strip_prefix(root).unwrap()).unwrap(); | 125 | let path = RelativePathBuf::from_path(path.strip_prefix(&filter.root).unwrap()).unwrap(); |
190 | res.push((path.to_owned(), text)) | 126 | res.push((path.to_owned(), text)) |
191 | } | 127 | } |
192 | res | 128 | res |
193 | } | 129 | } |
194 | |||
195 | fn load_change(change: WatcherChange) -> Option<WatcherChangeData> { | ||
196 | let data = match change { | ||
197 | WatcherChange::Create(path) => { | ||
198 | if path.is_dir() { | ||
199 | return None; | ||
200 | } | ||
201 | let text = match fs::read_to_string(&path) { | ||
202 | Ok(text) => text, | ||
203 | Err(e) => { | ||
204 | log::warn!("watcher error \"{}\": {}", path.display(), e); | ||
205 | return None; | ||
206 | } | ||
207 | }; | ||
208 | WatcherChangeData::Create { path, text } | ||
209 | } | ||
210 | WatcherChange::Write(path) => { | ||
211 | let text = match fs::read_to_string(&path) { | ||
212 | Ok(text) => text, | ||
213 | Err(e) => { | ||
214 | log::warn!("watcher error \"{}\": {}", path.display(), e); | ||
215 | return None; | ||
216 | } | ||
217 | }; | ||
218 | WatcherChangeData::Write { path, text } | ||
219 | } | ||
220 | WatcherChange::Remove(path) => WatcherChangeData::Remove { path }, | ||
221 | WatcherChange::Rescan => { | ||
222 | // this should be handled by Vfs::handle_task | ||
223 | return None; | ||
224 | } | ||
225 | }; | ||
226 | Some(data) | ||
227 | } | ||