aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_vfs/src/lib.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-12-18 13:38:05 +0000
committerAleksey Kladov <[email protected]>2018-12-20 09:15:38 +0000
commita422d480a188a28c6b5e7862fbf07817eb2c7447 (patch)
treed2a1945e49d1728f210c29ae8e88bffef19d22b7 /crates/ra_vfs/src/lib.rs
parente69b05781f7fb0f0dfdcd4acb433dbcde9cbb7b7 (diff)
implement vfs events handling
Diffstat (limited to 'crates/ra_vfs/src/lib.rs')
-rw-r--r--crates/ra_vfs/src/lib.rs153
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;
15mod io; 15mod io;
16 16
17use std::{ 17use 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
27use rustc_hash::{FxHashMap, FxHashSet};
25use relative_path::RelativePathBuf; 28use relative_path::RelativePathBuf;
29use crossbeam_channel::Receiver;
30use walkdir::DirEntry;
26use thread_worker::{WorkerHandle}; 31use thread_worker::{WorkerHandle};
27 32
28use crate::{ 33use 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
51fn rs_extension_filter(p: &Path) -> bool { 66fn 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
85struct Vfs { 100pub 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)]