aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock3
-rw-r--r--crates/ra_vfs/Cargo.toml4
-rw-r--r--crates/ra_vfs/src/io.rs182
-rw-r--r--crates/ra_vfs/src/lib.rs92
-rw-r--r--crates/ra_vfs/src/roots.rs23
5 files changed, 178 insertions, 126 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 39bfcde41..8f087749f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1123,16 +1123,13 @@ name = "ra_vfs"
1123version = "0.1.0" 1123version = "0.1.0"
1124dependencies = [ 1124dependencies = [
1125 "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 1125 "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
1126 "drop_bomb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
1127 "flexi_logger 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", 1126 "flexi_logger 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
1128 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 1127 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
1129 "notify 4.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 1128 "notify 4.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
1130 "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 1129 "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
1131 "ra_arena 0.1.0",
1132 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 1130 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
1133 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 1131 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
1134 "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", 1132 "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
1135 "thread_worker 0.1.0",
1136 "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 1133 "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
1137] 1134]
1138 1135
diff --git a/crates/ra_vfs/Cargo.toml b/crates/ra_vfs/Cargo.toml
index 2da5c499b..fdaf31b9c 100644
--- a/crates/ra_vfs/Cargo.toml
+++ b/crates/ra_vfs/Cargo.toml
@@ -11,12 +11,8 @@ rustc-hash = "1.0"
11crossbeam-channel = "0.3.5" 11crossbeam-channel = "0.3.5"
12log = "0.4.6" 12log = "0.4.6"
13notify = "4.0.9" 13notify = "4.0.9"
14drop_bomb = "0.1.0"
15parking_lot = "0.7.0" 14parking_lot = "0.7.0"
16 15
17thread_worker = { path = "../thread_worker" }
18ra_arena = { path = "../ra_arena" }
19
20[dev-dependencies] 16[dev-dependencies]
21tempfile = "3" 17tempfile = "3"
22flexi_logger = "0.10.0" 18flexi_logger = "0.10.0"
diff --git a/crates/ra_vfs/src/io.rs b/crates/ra_vfs/src/io.rs
index 0cffc03f3..5969ee0d0 100644
--- a/crates/ra_vfs/src/io.rs
+++ b/crates/ra_vfs/src/io.rs
@@ -3,13 +3,14 @@ use std::{
3 path::{Path, PathBuf}, 3 path::{Path, PathBuf},
4 sync::{mpsc, Arc}, 4 sync::{mpsc, Arc},
5 time::Duration, 5 time::Duration,
6 thread,
6}; 7};
7use crossbeam_channel::{Sender, unbounded, RecvError, select}; 8use crossbeam_channel::{Sender, Receiver, unbounded, RecvError, select};
8use relative_path::RelativePathBuf; 9use relative_path::RelativePathBuf;
9use walkdir::WalkDir; 10use walkdir::WalkDir;
10use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as _Watcher}; 11use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as _Watcher};
11 12
12use crate::{Roots, VfsRoot}; 13use crate::{Roots, VfsRoot, VfsTask};
13 14
14pub(crate) enum Task { 15pub(crate) enum Task {
15 AddRoot { root: VfsRoot }, 16 AddRoot { root: VfsRoot },
@@ -18,7 +19,7 @@ pub(crate) enum Task {
18/// `TaskResult` transfers files read on the IO thread to the VFS on the main 19/// `TaskResult` transfers files read on the IO thread to the VFS on the main
19/// thread. 20/// thread.
20#[derive(Debug)] 21#[derive(Debug)]
21pub enum TaskResult { 22pub(crate) enum TaskResult {
22 /// Emitted when we've recursively scanned a source root during the initial 23 /// Emitted when we've recursively scanned a source root during the initial
23 /// load. 24 /// load.
24 BulkLoadRoot { root: VfsRoot, files: Vec<(RelativePathBuf, String)> }, 25 BulkLoadRoot { root: VfsRoot, files: Vec<(RelativePathBuf, String)> },
@@ -46,7 +47,46 @@ enum ChangeKind {
46 47
47const WATCHER_DELAY: Duration = Duration::from_millis(250); 48const WATCHER_DELAY: Duration = Duration::from_millis(250);
48 49
49pub(crate) type Worker = thread_worker::Worker<Task, TaskResult>; 50// Like thread::JoinHandle, but joins the thread on drop.
51//
52// This is useful because it guarantees the absence of run-away threads, even if
53// code panics. This is important, because we might see panics in the test and
54// we might be used in an IDE context, where a failed component is just
55// restarted.
56//
57// Because all threads are joined, care must be taken to avoid deadlocks. That
58// typically means ensuring that channels are dropped before the threads.
59struct ScopedThread(Option<thread::JoinHandle<()>>);
60
61impl ScopedThread {
62 fn spawn(name: String, f: impl FnOnce() + Send + 'static) -> ScopedThread {
63 let handle = thread::Builder::new().name(name).spawn(f).unwrap();
64 ScopedThread(Some(handle))
65 }
66}
67
68impl Drop for ScopedThread {
69 fn drop(&mut self) {
70 let res = self.0.take().unwrap().join();
71 if !thread::panicking() {
72 res.unwrap();
73 }
74 }
75}
76
77pub(crate) struct Worker {
78 // XXX: field order is significant here.
79 //
80 // In Rust, fields are dropped in the declaration order, and we rely on this
81 // here. We must close sender first, so that the `thread` (who holds the
82 // opposite side of the channel) noticed shutdown. Then, we must join the
83 // thread, but we must keep receiver alive so that the thread does not
84 // panic.
85 pub(crate) sender: Sender<Task>,
86 _thread: ScopedThread,
87 pub(crate) receiver: Receiver<VfsTask>,
88}
89
50pub(crate) fn start(roots: Arc<Roots>) -> Worker { 90pub(crate) fn start(roots: Arc<Roots>) -> Worker {
51 // This is a pretty elaborate setup of threads & channels! It is 91 // This is a pretty elaborate setup of threads & channels! It is
52 // explained by the following concerns: 92 // explained by the following concerns:
@@ -55,74 +95,75 @@ pub(crate) fn start(roots: Arc<Roots>) -> Worker {
55 // * we want to read all files from a single thread, to guarantee that 95 // * we want to read all files from a single thread, to guarantee that
56 // we always get fresher versions and never go back in time. 96 // we always get fresher versions and never go back in time.
57 // * we want to tear down everything neatly during shutdown. 97 // * we want to tear down everything neatly during shutdown.
58 Worker::spawn( 98 let _thread;
59 "vfs", 99 // This are the channels we use to communicate with outside world.
60 128, 100 // If `input_receiver` is closed we need to tear ourselves down.
61 // This are the channels we use to communicate with outside world. 101 // `output_sender` should not be closed unless the parent died.
62 // If `input_receiver` is closed we need to tear ourselves down. 102 let (input_sender, input_receiver) = unbounded();
63 // `output_sender` should not be closed unless the parent died. 103 let (output_sender, output_receiver) = unbounded();
64 move |input_receiver, output_sender| { 104
65 // Make sure that the destruction order is 105 _thread = ScopedThread::spawn("vfs".to_string(), move || {
66 // 106 // Make sure that the destruction order is
67 // * notify_sender 107 //
68 // * _thread 108 // * notify_sender
69 // * watcher_sender 109 // * _thread
70 // 110 // * watcher_sender
71 // this is required to avoid deadlocks. 111 //
72 112 // this is required to avoid deadlocks.
73 // These are the corresponding crossbeam channels 113
74 let (watcher_sender, watcher_receiver) = unbounded(); 114 // These are the corresponding crossbeam channels
75 let _thread; 115 let (watcher_sender, watcher_receiver) = unbounded();
76 { 116 let _notify_thread;
77 // These are `std` channels notify will send events to 117 {
78 let (notify_sender, notify_receiver) = mpsc::channel(); 118 // These are `std` channels notify will send events to
79 119 let (notify_sender, notify_receiver) = mpsc::channel();
80 let mut watcher = notify::watcher(notify_sender, WATCHER_DELAY) 120
81 .map_err(|e| log::error!("failed to spawn notify {}", e)) 121 let mut watcher = notify::watcher(notify_sender, WATCHER_DELAY)
82 .ok(); 122 .map_err(|e| log::error!("failed to spawn notify {}", e))
83 // Start a silly thread to transform between two channels 123 .ok();
84 _thread = thread_worker::ScopedThread::spawn("notify-convertor", move || { 124 // Start a silly thread to transform between two channels
85 notify_receiver 125 _notify_thread = ScopedThread::spawn("notify-convertor".to_string(), move || {
86 .into_iter() 126 notify_receiver
87 .for_each(|event| convert_notify_event(event, &watcher_sender)) 127 .into_iter()
88 }); 128 .for_each(|event| convert_notify_event(event, &watcher_sender))
89 129 });
90 // Process requests from the called or notifications from 130
91 // watcher until the caller says stop. 131 // Process requests from the called or notifications from
92 loop { 132 // watcher until the caller says stop.
93 select! { 133 loop {
94 // Received request from the caller. If this channel is 134 select! {
95 // closed, we should shutdown everything. 135 // Received request from the caller. If this channel is
96 recv(input_receiver) -> t => match t { 136 // closed, we should shutdown everything.
97 Err(RecvError) => { 137 recv(input_receiver) -> t => match t {
98 drop(input_receiver); 138 Err(RecvError) => {
99 break 139 drop(input_receiver);
100 }, 140 break
101 Ok(Task::AddRoot { root }) => {
102 watch_root(watcher.as_mut(), &output_sender, &*roots, root);
103 }
104 },
105 // Watcher send us changes. If **this** channel is
106 // closed, the watcher has died, which indicates a bug
107 // -- escalate!
108 recv(watcher_receiver) -> event => match event {
109 Err(RecvError) => panic!("watcher is dead"),
110 Ok((path, change)) => {
111 handle_change(watcher.as_mut(), &output_sender, &*roots, path, change);
112 }
113 }, 141 },
114 } 142 Ok(Task::AddRoot { root }) => {
143 watch_root(watcher.as_mut(), &output_sender, &*roots, root);
144 }
145 },
146 // Watcher send us changes. If **this** channel is
147 // closed, the watcher has died, which indicates a bug
148 // -- escalate!
149 recv(watcher_receiver) -> event => match event {
150 Err(RecvError) => panic!("watcher is dead"),
151 Ok((path, change)) => {
152 handle_change(watcher.as_mut(), &output_sender, &*roots, path, change);
153 }
154 },
115 } 155 }
116 } 156 }
117 // Drain pending events: we are not interested in them anyways! 157 }
118 watcher_receiver.into_iter().for_each(|_| ()); 158 // Drain pending events: we are not interested in them anyways!
119 }, 159 watcher_receiver.into_iter().for_each(|_| ());
120 ) 160 });
161 Worker { sender: input_sender, _thread, receiver: output_receiver }
121} 162}
122 163
123fn watch_root( 164fn watch_root(
124 watcher: Option<&mut RecommendedWatcher>, 165 watcher: Option<&mut RecommendedWatcher>,
125 sender: &Sender<TaskResult>, 166 sender: &Sender<VfsTask>,
126 roots: &Roots, 167 roots: &Roots,
127 root: VfsRoot, 168 root: VfsRoot,
128) { 169) {
@@ -136,7 +177,8 @@ fn watch_root(
136 Some((path, text)) 177 Some((path, text))
137 }) 178 })
138 .collect(); 179 .collect();
139 sender.send(TaskResult::BulkLoadRoot { root, files }).unwrap(); 180 let res = TaskResult::BulkLoadRoot { root, files };
181 sender.send(VfsTask(res)).unwrap();
140 log::debug!("... loaded {}", root_path.display()); 182 log::debug!("... loaded {}", root_path.display());
141} 183}
142 184
@@ -173,7 +215,7 @@ fn convert_notify_event(event: DebouncedEvent, sender: &Sender<(PathBuf, ChangeK
173 215
174fn handle_change( 216fn handle_change(
175 watcher: Option<&mut RecommendedWatcher>, 217 watcher: Option<&mut RecommendedWatcher>,
176 sender: &Sender<TaskResult>, 218 sender: &Sender<VfsTask>,
177 roots: &Roots, 219 roots: &Roots,
178 path: PathBuf, 220 path: PathBuf,
179 kind: ChangeKind, 221 kind: ChangeKind,
@@ -195,13 +237,15 @@ fn handle_change(
195 .try_for_each(|rel_path| { 237 .try_for_each(|rel_path| {
196 let abs_path = rel_path.to_path(&roots.path(root)); 238 let abs_path = rel_path.to_path(&roots.path(root));
197 let text = read_to_string(&abs_path); 239 let text = read_to_string(&abs_path);
198 sender.send(TaskResult::SingleFile { root, path: rel_path, text }) 240 let res = TaskResult::SingleFile { root, path: rel_path, text };
241 sender.send(VfsTask(res))
199 }) 242 })
200 .unwrap() 243 .unwrap()
201 } 244 }
202 ChangeKind::Write | ChangeKind::Remove => { 245 ChangeKind::Write | ChangeKind::Remove => {
203 let text = read_to_string(&path); 246 let text = read_to_string(&path);
204 sender.send(TaskResult::SingleFile { root, path: rel_path, text }).unwrap(); 247 let res = TaskResult::SingleFile { root, path: rel_path, text };
248 sender.send(VfsTask(res)).unwrap();
205 } 249 }
206 } 250 }
207} 251}
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs
index 7f555a3c0..808c138df 100644
--- a/crates/ra_vfs/src/lib.rs
+++ b/crates/ra_vfs/src/lib.rs
@@ -25,7 +25,6 @@ use std::{
25}; 25};
26 26
27use crossbeam_channel::Receiver; 27use crossbeam_channel::Receiver;
28use ra_arena::{impl_arena_id, Arena, RawId, map::ArenaMap};
29use relative_path::{RelativePath, RelativePathBuf}; 28use relative_path::{RelativePath, RelativePathBuf};
30use rustc_hash::{FxHashMap, FxHashSet}; 29use rustc_hash::{FxHashMap, FxHashSet};
31 30
@@ -34,14 +33,23 @@ use crate::{
34 roots::Roots, 33 roots::Roots,
35}; 34};
36 35
37pub use crate::{ 36pub use crate::roots::VfsRoot;
38 io::TaskResult as VfsTask, 37
39 roots::VfsRoot, 38/// Opaque wrapper around file-system event.
40}; 39///
40/// Calling code is expected to just pass `VfsTask` to `handle_task` method. It
41/// is exposed as a public API so that the caller can plug vfs events into the
42/// main event loop and be notified when changes happen.
43pub struct VfsTask(TaskResult);
44
45impl fmt::Debug for VfsTask {
46 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
47 f.write_str("VfsTask { ... }")
48 }
49}
41 50
42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 51#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub struct VfsFile(pub RawId); 52pub struct VfsFile(pub u32);
44impl_arena_id!(VfsFile);
45 53
46struct VfsFileData { 54struct VfsFileData {
47 root: VfsRoot, 55 root: VfsRoot,
@@ -52,8 +60,8 @@ struct VfsFileData {
52 60
53pub struct Vfs { 61pub struct Vfs {
54 roots: Arc<Roots>, 62 roots: Arc<Roots>,
55 files: Arena<VfsFile, VfsFileData>, 63 files: Vec<VfsFileData>,
56 root2files: ArenaMap<VfsRoot, FxHashSet<VfsFile>>, 64 root2files: FxHashMap<VfsRoot, FxHashSet<VfsFile>>,
57 pending_changes: Vec<VfsChange>, 65 pending_changes: Vec<VfsChange>,
58 worker: Worker, 66 worker: Worker,
59} 67}
@@ -68,18 +76,25 @@ impl fmt::Debug for Vfs {
68 } 76 }
69} 77}
70 78
79#[derive(Debug, Clone)]
80pub enum VfsChange {
81 AddRoot { root: VfsRoot, files: Vec<(VfsFile, RelativePathBuf, Arc<String>)> },
82 AddFile { root: VfsRoot, file: VfsFile, path: RelativePathBuf, text: Arc<String> },
83 RemoveFile { root: VfsRoot, file: VfsFile, path: RelativePathBuf },
84 ChangeFile { file: VfsFile, text: Arc<String> },
85}
86
71impl Vfs { 87impl Vfs {
72 pub fn new(roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) { 88 pub fn new(roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) {
73 let roots = Arc::new(Roots::new(roots)); 89 let roots = Arc::new(Roots::new(roots));
74 let worker = io::start(Arc::clone(&roots)); 90 let worker = io::start(Arc::clone(&roots));
75 let mut root2files = ArenaMap::default(); 91 let mut root2files = FxHashMap::default();
76 92
77 for root in roots.iter() { 93 for root in roots.iter() {
78 root2files.insert(root, Default::default()); 94 root2files.insert(root, Default::default());
79 worker.sender().send(io::Task::AddRoot { root }).unwrap(); 95 worker.sender.send(io::Task::AddRoot { root }).unwrap();
80 } 96 }
81 let res = 97 let res = Vfs { roots, files: Vec::new(), root2files, worker, pending_changes: Vec::new() };
82 Vfs { roots, files: Arena::default(), root2files, worker, pending_changes: Vec::new() };
83 let vfs_roots = res.roots.iter().collect(); 98 let vfs_roots = res.roots.iter().collect();
84 (res, vfs_roots) 99 (res, vfs_roots)
85 } 100 }
@@ -96,8 +111,8 @@ impl Vfs {
96 } 111 }
97 112
98 pub fn file2path(&self, file: VfsFile) -> PathBuf { 113 pub fn file2path(&self, file: VfsFile) -> PathBuf {
99 let rel_path = &self.files[file].path; 114 let rel_path = &self.file(file).path;
100 let root_path = &self.roots.path(self.files[file].root); 115 let root_path = &self.roots.path(self.file(file).root);
101 rel_path.to_path(root_path) 116 rel_path.to_path(root_path)
102 } 117 }
103 118
@@ -154,23 +169,23 @@ impl Vfs {
154 mem::replace(&mut self.pending_changes, Vec::new()) 169 mem::replace(&mut self.pending_changes, Vec::new())
155 } 170 }
156 171
157 pub fn task_receiver(&self) -> &Receiver<io::TaskResult> { 172 pub fn task_receiver(&self) -> &Receiver<VfsTask> {
158 self.worker.receiver() 173 &self.worker.receiver
159 } 174 }
160 175
161 pub fn handle_task(&mut self, task: io::TaskResult) { 176 pub fn handle_task(&mut self, task: VfsTask) {
162 match task { 177 match task.0 {
163 TaskResult::BulkLoadRoot { root, files } => { 178 TaskResult::BulkLoadRoot { root, files } => {
164 let mut cur_files = Vec::new(); 179 let mut cur_files = Vec::new();
165 // While we were scanning the root in the background, a file might have 180 // While we were scanning the root in the background, a file might have
166 // been open in the editor, so we need to account for that. 181 // been open in the editor, so we need to account for that.
167 let existing = self.root2files[root] 182 let existing = self.root2files[&root]
168 .iter() 183 .iter()
169 .map(|&file| (self.files[file].path.clone(), file)) 184 .map(|&file| (self.file(file).path.clone(), file))
170 .collect::<FxHashMap<_, _>>(); 185 .collect::<FxHashMap<_, _>>();
171 for (path, text) in files { 186 for (path, text) in files {
172 if let Some(&file) = existing.get(&path) { 187 if let Some(&file) = existing.get(&path) {
173 let text = Arc::clone(&self.files[file].text); 188 let text = Arc::clone(&self.file(file).text);
174 cur_files.push((file, path, text)); 189 cur_files.push((file, path, text));
175 continue; 190 continue;
176 } 191 }
@@ -184,7 +199,7 @@ impl Vfs {
184 } 199 }
185 TaskResult::SingleFile { root, path, text } => { 200 TaskResult::SingleFile { root, path, text } => {
186 let existing_file = self.find_file(root, &path); 201 let existing_file = self.find_file(root, &path);
187 if existing_file.map(|file| self.files[file].is_overlayed) == Some(true) { 202 if existing_file.map(|file| self.file(file).is_overlayed) == Some(true) {
188 return; 203 return;
189 } 204 }
190 match (existing_file, text) { 205 match (existing_file, text) {
@@ -240,23 +255,24 @@ impl Vfs {
240 is_overlayed: bool, 255 is_overlayed: bool,
241 ) -> VfsFile { 256 ) -> VfsFile {
242 let data = VfsFileData { root, path, text, is_overlayed }; 257 let data = VfsFileData { root, path, text, is_overlayed };
243 let file = self.files.alloc(data); 258 let file = VfsFile(self.files.len() as u32);
244 self.root2files.get_mut(root).unwrap().insert(file); 259 self.files.push(data);
260 self.root2files.get_mut(&root).unwrap().insert(file);
245 file 261 file
246 } 262 }
247 263
248 fn raw_change_file(&mut self, file: VfsFile, new_text: Arc<String>, is_overlayed: bool) { 264 fn raw_change_file(&mut self, file: VfsFile, new_text: Arc<String>, is_overlayed: bool) {
249 let mut file_data = &mut self.files[file]; 265 let mut file_data = &mut self.file_mut(file);
250 file_data.text = new_text; 266 file_data.text = new_text;
251 file_data.is_overlayed = is_overlayed; 267 file_data.is_overlayed = is_overlayed;
252 } 268 }
253 269
254 fn raw_remove_file(&mut self, file: VfsFile) { 270 fn raw_remove_file(&mut self, file: VfsFile) {
255 // FIXME: use arena with removal 271 // FIXME: use arena with removal
256 self.files[file].text = Default::default(); 272 self.file_mut(file).text = Default::default();
257 self.files[file].path = Default::default(); 273 self.file_mut(file).path = Default::default();
258 let root = self.files[file].root; 274 let root = self.file(file).root;
259 let removed = self.root2files.get_mut(root).unwrap().remove(&file); 275 let removed = self.root2files.get_mut(&root).unwrap().remove(&file);
260 assert!(removed); 276 assert!(removed);
261 } 277 }
262 278
@@ -267,14 +283,14 @@ impl Vfs {
267 } 283 }
268 284
269 fn find_file(&self, root: VfsRoot, path: &RelativePath) -> Option<VfsFile> { 285 fn find_file(&self, root: VfsRoot, path: &RelativePath) -> Option<VfsFile> {
270 self.root2files[root].iter().map(|&it| it).find(|&file| self.files[file].path == path) 286 self.root2files[&root].iter().map(|&it| it).find(|&file| self.file(file).path == path)
271 } 287 }
272}
273 288
274#[derive(Debug, Clone)] 289 fn file(&self, file: VfsFile) -> &VfsFileData {
275pub enum VfsChange { 290 &self.files[file.0 as usize]
276 AddRoot { root: VfsRoot, files: Vec<(VfsFile, RelativePathBuf, Arc<String>)> }, 291 }
277 AddFile { root: VfsRoot, file: VfsFile, path: RelativePathBuf, text: Arc<String> }, 292
278 RemoveFile { root: VfsRoot, file: VfsFile, path: RelativePathBuf }, 293 fn file_mut(&mut self, file: VfsFile) -> &mut VfsFileData {
279 ChangeFile { file: VfsFile, text: Arc<String> }, 294 &mut self.files[file.0 as usize]
295 }
280} 296}
diff --git a/crates/ra_vfs/src/roots.rs b/crates/ra_vfs/src/roots.rs
index 5e2776a35..4503458ee 100644
--- a/crates/ra_vfs/src/roots.rs
+++ b/crates/ra_vfs/src/roots.rs
@@ -1,16 +1,13 @@
1use std::{ 1use std::{
2 iter, 2 iter,
3 sync::Arc,
4 path::{Path, PathBuf}, 3 path::{Path, PathBuf},
5}; 4};
6 5
7use relative_path::{ RelativePath, RelativePathBuf}; 6use relative_path::{ RelativePath, RelativePathBuf};
8use ra_arena::{impl_arena_id, Arena, RawId};
9 7
10/// VfsRoot identifies a watched directory on the file system. 8/// VfsRoot identifies a watched directory on the file system.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct VfsRoot(pub RawId); 10pub struct VfsRoot(pub u32);
13impl_arena_id!(VfsRoot);
14 11
15/// Describes the contents of a single source root. 12/// Describes the contents of a single source root.
16/// 13///
@@ -25,12 +22,12 @@ struct RootData {
25} 22}
26 23
27pub(crate) struct Roots { 24pub(crate) struct Roots {
28 roots: Arena<VfsRoot, Arc<RootData>>, 25 roots: Vec<RootData>,
29} 26}
30 27
31impl Roots { 28impl Roots {
32 pub(crate) fn new(mut paths: Vec<PathBuf>) -> Roots { 29 pub(crate) fn new(mut paths: Vec<PathBuf>) -> Roots {
33 let mut roots = Arena::default(); 30 let mut roots = Vec::new();
34 // A hack to make nesting work. 31 // A hack to make nesting work.
35 paths.sort_by_key(|it| std::cmp::Reverse(it.as_os_str().len())); 32 paths.sort_by_key(|it| std::cmp::Reverse(it.as_os_str().len()));
36 paths.dedup(); 33 paths.dedup();
@@ -38,9 +35,7 @@ impl Roots {
38 let nested_roots = 35 let nested_roots =
39 paths[..i].iter().filter_map(|it| rel_path(path, it)).collect::<Vec<_>>(); 36 paths[..i].iter().filter_map(|it| rel_path(path, it)).collect::<Vec<_>>();
40 37
41 let config = Arc::new(RootData::new(path.clone(), nested_roots)); 38 roots.push(RootData::new(path.clone(), nested_roots));
42
43 roots.alloc(config.clone());
44 } 39 }
45 Roots { roots } 40 Roots { roots }
46 } 41 }
@@ -54,20 +49,24 @@ impl Roots {
54 self.roots.len() 49 self.roots.len()
55 } 50 }
56 pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = VfsRoot> + 'a { 51 pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = VfsRoot> + 'a {
57 self.roots.iter().map(|(id, _)| id) 52 (0..self.roots.len()).into_iter().map(|idx| VfsRoot(idx as u32))
58 } 53 }
59 pub(crate) fn path(&self, root: VfsRoot) -> &Path { 54 pub(crate) fn path(&self, root: VfsRoot) -> &Path {
60 self.roots[root].path.as_path() 55 self.root(root).path.as_path()
61 } 56 }
62 /// Checks if root contains a path and returns a root-relative path. 57 /// Checks if root contains a path and returns a root-relative path.
63 pub(crate) fn contains(&self, root: VfsRoot, path: &Path) -> Option<RelativePathBuf> { 58 pub(crate) fn contains(&self, root: VfsRoot, path: &Path) -> Option<RelativePathBuf> {
64 let data = &self.roots[root]; 59 let data = self.root(root);
65 iter::once(&data.path) 60 iter::once(&data.path)
66 .chain(data.canonical_path.as_ref().into_iter()) 61 .chain(data.canonical_path.as_ref().into_iter())
67 .find_map(|base| rel_path(base, path)) 62 .find_map(|base| rel_path(base, path))
68 .filter(|path| !data.excluded_dirs.contains(path)) 63 .filter(|path| !data.excluded_dirs.contains(path))
69 .filter(|path| !data.is_excluded(path)) 64 .filter(|path| !data.is_excluded(path))
70 } 65 }
66
67 fn root(&self, root: VfsRoot) -> &RootData {
68 &self.roots[root.0 as usize]
69 }
71} 70}
72 71
73impl RootData { 72impl RootData {