aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_vfs/src/io.rs
diff options
context:
space:
mode:
authorBernardo <[email protected]>2019-01-25 17:39:35 +0000
committerAleksey Kladov <[email protected]>2019-01-26 08:46:37 +0000
commitd63e1cebff771621b90bdce25ac013eecb415e1e (patch)
treeb4b83981fb7734d036be1dc4e9385a9c6e71e6ea /crates/ra_vfs/src/io.rs
parent86fadbd4e59f12535edcc280ec227d7ee8a0848d (diff)
use `Roots` in watcher
Diffstat (limited to 'crates/ra_vfs/src/io.rs')
-rw-r--r--crates/ra_vfs/src/io.rs200
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 @@
1use std::{ 1use std::{fs, sync::Arc, thread};
2 fmt, fs,
3 path::{Path, PathBuf},
4 sync::Arc,
5 thread,
6};
7 2
8use crossbeam_channel::{Receiver, Sender}; 3use crossbeam_channel::{Receiver, Sender};
9use parking_lot::Mutex;
10use relative_path::RelativePathBuf; 4use relative_path::RelativePathBuf;
11use thread_worker::WorkerHandle; 5use thread_worker::WorkerHandle;
12use walkdir::WalkDir; 6use walkdir::WalkDir;
13 7
14mod watcher; 8mod watcher;
15use watcher::Watcher; 9use watcher::Watcher;
16pub use watcher::WatcherChange;
17 10
18use crate::{RootFilter, VfsRoot}; 11use crate::{RootFilter, Roots, VfsRoot};
19 12
20pub(crate) enum Task { 13pub(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)]
37pub struct AddRootResult {
38 pub(crate) root: VfsRoot,
39 pub(crate) files: Vec<(RelativePathBuf, String)>,
40}
41
42#[derive(Debug)]
43pub enum WatcherChangeData {
44 Create { path: PathBuf, text: String },
45 Write { path: PathBuf, text: String },
46 Remove { path: PathBuf },
47}
48
49pub enum TaskResult { 21pub 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 {
55impl 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
65pub(crate) struct Worker { 42pub(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
71impl Worker { 47impl 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
113fn watch( 87fn 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
124fn 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
157fn load_root( 101fn 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
195fn 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}