diff options
author | Aleksey Kladov <[email protected]> | 2018-12-18 13:38:05 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-12-20 09:15:38 +0000 |
commit | a422d480a188a28c6b5e7862fbf07817eb2c7447 (patch) | |
tree | d2a1945e49d1728f210c29ae8e88bffef19d22b7 /crates/ra_vfs/src/lib.rs | |
parent | e69b05781f7fb0f0dfdcd4acb433dbcde9cbb7b7 (diff) |
implement vfs events handling
Diffstat (limited to 'crates/ra_vfs/src/lib.rs')
-rw-r--r-- | crates/ra_vfs/src/lib.rs | 153 |
1 files changed, 140 insertions, 13 deletions
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs index 8ce6b6ee0..792f722a7 100644 --- a/crates/ra_vfs/src/lib.rs +++ b/crates/ra_vfs/src/lib.rs | |||
@@ -15,14 +15,19 @@ mod arena; | |||
15 | mod io; | 15 | mod io; |
16 | 16 | ||
17 | use std::{ | 17 | use std::{ |
18 | mem, | ||
18 | thread, | 19 | thread, |
19 | cmp::Reverse, | 20 | cmp::Reverse, |
20 | path::{Path, PathBuf}, | 21 | path::{Path, PathBuf}, |
21 | ffi::OsStr, | 22 | ffi::OsStr, |
22 | sync::Arc, | 23 | sync::Arc, |
24 | fs, | ||
23 | }; | 25 | }; |
24 | 26 | ||
27 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
25 | use relative_path::RelativePathBuf; | 28 | use relative_path::RelativePathBuf; |
29 | use crossbeam_channel::Receiver; | ||
30 | use walkdir::DirEntry; | ||
26 | use thread_worker::{WorkerHandle}; | 31 | use thread_worker::{WorkerHandle}; |
27 | 32 | ||
28 | use crate::{ | 33 | use crate::{ |
@@ -40,15 +45,25 @@ impl RootFilter { | |||
40 | fn new(root: PathBuf) -> RootFilter { | 45 | fn new(root: PathBuf) -> RootFilter { |
41 | RootFilter { | 46 | RootFilter { |
42 | root, | 47 | root, |
43 | file_filter: rs_extension_filter, | 48 | file_filter: has_rs_extension, |
44 | } | 49 | } |
45 | } | 50 | } |
46 | fn can_contain(&self, path: &Path) -> bool { | 51 | /// Check if this root can contain `path`. NB: even if this returns |
47 | (self.file_filter)(path) && path.starts_with(&self.root) | 52 | /// true, the `path` might actually be conained in some nested root. |
53 | fn can_contain(&self, path: &Path) -> Option<RelativePathBuf> { | ||
54 | if !(self.file_filter)(path) { | ||
55 | return None; | ||
56 | } | ||
57 | if !(path.starts_with(&self.root)) { | ||
58 | return None; | ||
59 | } | ||
60 | let path = path.strip_prefix(&self.root).unwrap(); | ||
61 | let path = RelativePathBuf::from_path(path).unwrap(); | ||
62 | Some(path) | ||
48 | } | 63 | } |
49 | } | 64 | } |
50 | 65 | ||
51 | fn rs_extension_filter(p: &Path) -> bool { | 66 | fn has_rs_extension(p: &Path) -> bool { |
52 | p.extension() == Some(OsStr::new("rs")) | 67 | p.extension() == Some(OsStr::new("rs")) |
53 | } | 68 | } |
54 | 69 | ||
@@ -82,10 +97,11 @@ struct VfsFileData { | |||
82 | text: Arc<String>, | 97 | text: Arc<String>, |
83 | } | 98 | } |
84 | 99 | ||
85 | struct Vfs { | 100 | pub struct Vfs { |
86 | roots: Arena<VfsRoot, RootFilter>, | 101 | roots: Arena<VfsRoot, RootFilter>, |
87 | files: Arena<VfsFile, VfsFileData>, | 102 | files: Arena<VfsFile, VfsFileData>, |
88 | // pending_changes: Vec<PendingChange>, | 103 | root2files: FxHashMap<VfsRoot, FxHashSet<VfsFile>>, |
104 | pending_changes: Vec<VfsChange>, | ||
89 | worker: io::Worker, | 105 | worker: io::Worker, |
90 | worker_handle: WorkerHandle, | 106 | worker_handle: WorkerHandle, |
91 | } | 107 | } |
@@ -97,33 +113,144 @@ impl Vfs { | |||
97 | let mut res = Vfs { | 113 | let mut res = Vfs { |
98 | roots: Arena::default(), | 114 | roots: Arena::default(), |
99 | files: Arena::default(), | 115 | files: Arena::default(), |
116 | root2files: FxHashMap::default(), | ||
100 | worker, | 117 | worker, |
101 | worker_handle, | 118 | worker_handle, |
119 | pending_changes: Vec::new(), | ||
102 | }; | 120 | }; |
103 | 121 | ||
104 | // A hack to make nesting work. | 122 | // A hack to make nesting work. |
105 | roots.sort_by_key(|it| Reverse(it.as_os_str().len())); | 123 | roots.sort_by_key(|it| Reverse(it.as_os_str().len())); |
106 | 124 | for (i, path) in roots.iter().enumerate() { | |
107 | for path in roots { | 125 | let root = res.roots.alloc(RootFilter::new(path.clone())); |
108 | res.roots.alloc(RootFilter::new(path)); | 126 | let nested = roots[..i] |
127 | .iter() | ||
128 | .filter(|it| it.starts_with(path)) | ||
129 | .map(|it| it.clone()) | ||
130 | .collect::<Vec<_>>(); | ||
131 | let filter = move |entry: &DirEntry| { | ||
132 | if entry.file_type().is_file() { | ||
133 | has_rs_extension(entry.path()) | ||
134 | } else { | ||
135 | nested.iter().all(|it| it != entry.path()) | ||
136 | } | ||
137 | }; | ||
138 | let task = io::Task { | ||
139 | root, | ||
140 | path: path.clone(), | ||
141 | filter: Box::new(filter), | ||
142 | }; | ||
143 | res.worker.inp.send(task); | ||
109 | } | 144 | } |
110 | res | 145 | res |
111 | } | 146 | } |
112 | 147 | ||
113 | pub fn add_file_overlay(&mut self, path: &Path, content: String) {} | 148 | pub fn task_receiver(&self) -> &Receiver<io::TaskResult> { |
149 | &self.worker.out | ||
150 | } | ||
151 | |||
152 | pub fn handle_task(&mut self, task: io::TaskResult) { | ||
153 | let mut files = Vec::new(); | ||
154 | for (path, text) in task.files { | ||
155 | let text = Arc::new(text); | ||
156 | let file = self.add_file(task.root, path.clone(), Arc::clone(&text)); | ||
157 | files.push((file, path, text)); | ||
158 | } | ||
159 | let change = VfsChange::AddRoot { | ||
160 | root: task.root, | ||
161 | files, | ||
162 | }; | ||
163 | self.pending_changes.push(change); | ||
164 | } | ||
114 | 165 | ||
115 | pub fn change_file_overlay(&mut self, path: &Path, new_content: String) {} | 166 | pub fn add_file_overlay(&mut self, path: &Path, text: String) { |
167 | if let Some((root, path, file)) = self.find_root(path) { | ||
168 | let text = Arc::new(text); | ||
169 | let change = if let Some(file) = file { | ||
170 | self.change_file(file, Arc::clone(&text)); | ||
171 | VfsChange::ChangeFile { file, text } | ||
172 | } else { | ||
173 | let file = self.add_file(root, path.clone(), Arc::clone(&text)); | ||
174 | VfsChange::AddFile { | ||
175 | file, | ||
176 | text, | ||
177 | root, | ||
178 | path, | ||
179 | } | ||
180 | }; | ||
181 | self.pending_changes.push(change); | ||
182 | } | ||
183 | } | ||
116 | 184 | ||
117 | pub fn remove_file_overlay(&mut self, path: &Path) {} | 185 | pub fn change_file_overlay(&mut self, path: &Path, new_text: String) { |
186 | if let Some((_root, _path, file)) = self.find_root(path) { | ||
187 | let file = file.expect("can't change a file which wasn't added"); | ||
188 | let text = Arc::new(new_text); | ||
189 | self.change_file(file, Arc::clone(&text)); | ||
190 | let change = VfsChange::ChangeFile { file, text }; | ||
191 | self.pending_changes.push(change); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | pub fn remove_file_overlay(&mut self, path: &Path) { | ||
196 | if let Some((root, path, file)) = self.find_root(path) { | ||
197 | let file = file.expect("can't remove a file which wasn't added"); | ||
198 | let full_path = path.to_path(&self.roots[root].root); | ||
199 | let change = if let Ok(text) = fs::read_to_string(&full_path) { | ||
200 | let text = Arc::new(text); | ||
201 | self.change_file(file, Arc::clone(&text)); | ||
202 | VfsChange::ChangeFile { file, text } | ||
203 | } else { | ||
204 | self.remove_file(file); | ||
205 | VfsChange::RemoveFile { file } | ||
206 | }; | ||
207 | self.pending_changes.push(change); | ||
208 | } | ||
209 | } | ||
118 | 210 | ||
119 | pub fn commit_changes(&mut self) -> Vec<VfsChange> { | 211 | pub fn commit_changes(&mut self) -> Vec<VfsChange> { |
120 | unimplemented!() | 212 | mem::replace(&mut self.pending_changes, Vec::new()) |
121 | } | 213 | } |
122 | 214 | ||
123 | pub fn shutdown(self) -> thread::Result<()> { | 215 | pub fn shutdown(self) -> thread::Result<()> { |
124 | let _ = self.worker.shutdown(); | 216 | let _ = self.worker.shutdown(); |
125 | self.worker_handle.shutdown() | 217 | self.worker_handle.shutdown() |
126 | } | 218 | } |
219 | |||
220 | fn add_file(&mut self, root: VfsRoot, path: RelativePathBuf, text: Arc<String>) -> VfsFile { | ||
221 | let data = VfsFileData { root, path, text }; | ||
222 | let file = self.files.alloc(data); | ||
223 | self.root2files | ||
224 | .entry(root) | ||
225 | .or_insert_with(FxHashSet::default) | ||
226 | .insert(file); | ||
227 | file | ||
228 | } | ||
229 | |||
230 | fn change_file(&mut self, file: VfsFile, new_text: Arc<String>) { | ||
231 | self.files[file].text = new_text; | ||
232 | } | ||
233 | |||
234 | fn remove_file(&mut self, file: VfsFile) { | ||
235 | //FIXME: use arena with removal | ||
236 | self.files[file].text = Default::default(); | ||
237 | self.files[file].path = Default::default(); | ||
238 | let root = self.files[file].root; | ||
239 | let removed = self.root2files.get_mut(&root).unwrap().remove(&file); | ||
240 | assert!(removed); | ||
241 | } | ||
242 | |||
243 | fn find_root(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf, Option<VfsFile>)> { | ||
244 | let (root, path) = self | ||
245 | .roots | ||
246 | .iter() | ||
247 | .find_map(|(root, data)| data.can_contain(path).map(|it| (root, it)))?; | ||
248 | let file = self.root2files[&root] | ||
249 | .iter() | ||
250 | .map(|&it| it) | ||
251 | .find(|&file| self.files[file].path == path); | ||
252 | Some((root, path, file)) | ||
253 | } | ||
127 | } | 254 | } |
128 | 255 | ||
129 | #[derive(Debug, Clone)] | 256 | #[derive(Debug, Clone)] |