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