aboutsummaryrefslogtreecommitdiff
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
parente69b05781f7fb0f0dfdcd4acb433dbcde9cbb7b7 (diff)
implement vfs events handling
-rw-r--r--Cargo.lock81
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/support.rs4
-rw-r--r--crates/ra_vfs/Cargo.toml3
-rw-r--r--crates/ra_vfs/src/arena.rs7
-rw-r--r--crates/ra_vfs/src/io.rs43
-rw-r--r--crates/ra_vfs/src/lib.rs153
-rw-r--r--crates/ra_vfs/tests/vfs.rs101
7 files changed, 349 insertions, 43 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c15955f4c..aac4d91b3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -752,6 +752,7 @@ dependencies = [
752 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 752 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
753 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 753 "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
754 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 754 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
755 "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
755 "thread_worker 0.1.0", 756 "thread_worker 0.1.0",
756 "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 757 "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
757] 758]
@@ -779,6 +780,33 @@ dependencies = [
779] 780]
780 781
781[[package]] 782[[package]]
783name = "rand"
784version = "0.6.1"
785source = "registry+https://github.com/rust-lang/crates.io-index"
786dependencies = [
787 "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
788 "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
789 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
790 "rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
791 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
792 "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
793 "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
794 "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
795 "rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
796 "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
797 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
798]
799
800[[package]]
801name = "rand_chacha"
802version = "0.1.0"
803source = "registry+https://github.com/rust-lang/crates.io-index"
804dependencies = [
805 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
806 "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
807]
808
809[[package]]
782name = "rand_core" 810name = "rand_core"
783version = "0.2.2" 811version = "0.2.2"
784source = "registry+https://github.com/rust-lang/crates.io-index" 812source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -792,6 +820,39 @@ version = "0.3.0"
792source = "registry+https://github.com/rust-lang/crates.io-index" 820source = "registry+https://github.com/rust-lang/crates.io-index"
793 821
794[[package]] 822[[package]]
823name = "rand_hc"
824version = "0.1.0"
825source = "registry+https://github.com/rust-lang/crates.io-index"
826dependencies = [
827 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
828]
829
830[[package]]
831name = "rand_isaac"
832version = "0.1.1"
833source = "registry+https://github.com/rust-lang/crates.io-index"
834dependencies = [
835 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
836]
837
838[[package]]
839name = "rand_pcg"
840version = "0.1.1"
841source = "registry+https://github.com/rust-lang/crates.io-index"
842dependencies = [
843 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
844 "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
845]
846
847[[package]]
848name = "rand_xorshift"
849version = "0.1.0"
850source = "registry+https://github.com/rust-lang/crates.io-index"
851dependencies = [
852 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
853]
854
855[[package]]
795name = "rayon" 856name = "rayon"
796version = "1.0.3" 857version = "1.0.3"
797source = "registry+https://github.com/rust-lang/crates.io-index" 858source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1069,6 +1130,19 @@ dependencies = [
1069] 1130]
1070 1131
1071[[package]] 1132[[package]]
1133name = "tempfile"
1134version = "3.0.5"
1135source = "registry+https://github.com/rust-lang/crates.io-index"
1136dependencies = [
1137 "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
1138 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
1139 "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
1140 "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)",
1141 "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
1142 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
1143]
1144
1145[[package]]
1072name = "tera" 1146name = "tera"
1073version = "0.11.20" 1147version = "0.11.20"
1074source = "registry+https://github.com/rust-lang/crates.io-index" 1148source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1431,8 +1505,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1431"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" 1505"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"
1432"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" 1506"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd"
1433"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" 1507"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
1508"checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a"
1509"checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a"
1434"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" 1510"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
1435"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" 1511"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
1512"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
1513"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
1514"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05"
1515"checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3"
1436"checksum rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473" 1516"checksum rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473"
1437"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" 1517"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356"
1438"checksum redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "a84bcd297b87a545980a2d25a0beb72a1f490c31f0a9fde52fca35bfbb1ceb70" 1518"checksum redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "a84bcd297b87a545980a2d25a0beb72a1f490c31f0a9fde52fca35bfbb1ceb70"
@@ -1467,6 +1547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1467"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc" 1547"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc"
1468"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" 1548"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
1469"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" 1549"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
1550"checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2"
1470"checksum tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)" = "4b505279e19d8f7d24b1a9dc58327c9c36174b1a2c7ebdeac70792d017cb64f3" 1551"checksum tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)" = "4b505279e19d8f7d24b1a9dc58327c9c36174b1a2c7ebdeac70792d017cb64f3"
1471"checksum teraron 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0d89ad4617d1dec55331067fadaa041e813479e1779616f3d3ce9308bf46184e" 1552"checksum teraron 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0d89ad4617d1dec55331067fadaa041e813479e1779616f3d3ce9308bf46184e"
1472"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" 1553"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs
index 07a878a26..c14d287ca 100644
--- a/crates/ra_lsp_server/tests/heavy_tests/support.rs
+++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs
@@ -174,11 +174,11 @@ impl Server {
174impl Drop for Server { 174impl Drop for Server {
175 fn drop(&mut self) { 175 fn drop(&mut self) {
176 self.send_request::<Shutdown>(666, ()); 176 self.send_request::<Shutdown>(666, ());
177 let receiver = self.worker.take().unwrap().stop(); 177 let receiver = self.worker.take().unwrap().shutdown();
178 while let Some(msg) = recv_timeout(&receiver) { 178 while let Some(msg) = recv_timeout(&receiver) {
179 drop(msg); 179 drop(msg);
180 } 180 }
181 self.watcher.take().unwrap().stop().unwrap(); 181 self.watcher.take().unwrap().shutdown().unwrap();
182 } 182 }
183} 183}
184 184
diff --git a/crates/ra_vfs/Cargo.toml b/crates/ra_vfs/Cargo.toml
index 9ce619a77..ccea8a866 100644
--- a/crates/ra_vfs/Cargo.toml
+++ b/crates/ra_vfs/Cargo.toml
@@ -12,3 +12,6 @@ crossbeam-channel = "0.2.4"
12log = "0.4.6" 12log = "0.4.6"
13 13
14thread_worker = { path = "../thread_worker" } 14thread_worker = { path = "../thread_worker" }
15
16[dev-dependencies]
17tempfile = "3"
diff --git a/crates/ra_vfs/src/arena.rs b/crates/ra_vfs/src/arena.rs
index d6fad753b..6b42ae26d 100644
--- a/crates/ra_vfs/src/arena.rs
+++ b/crates/ra_vfs/src/arena.rs
@@ -1,5 +1,4 @@
1use std::{ 1use std::{
2 hash::{Hash, Hasher},
3 marker::PhantomData, 2 marker::PhantomData,
4 ops::{Index, IndexMut}, 3 ops::{Index, IndexMut},
5}; 4};
@@ -21,6 +20,12 @@ impl<ID: ArenaId, T> Arena<ID, T> {
21 self.data.push(value); 20 self.data.push(value);
22 ID::from_u32(id) 21 ID::from_u32(id)
23 } 22 }
23 pub fn iter<'a>(&'a self) -> impl Iterator<Item = (ID, &'a T)> {
24 self.data
25 .iter()
26 .enumerate()
27 .map(|(idx, value)| (ID::from_u32(idx as u32), value))
28 }
24} 29}
25 30
26impl<ID: ArenaId, T> Default for Arena<ID, T> { 31impl<ID: ArenaId, T> Default for Arena<ID, T> {
diff --git a/crates/ra_vfs/src/io.rs b/crates/ra_vfs/src/io.rs
index c46760583..178c9beff 100644
--- a/crates/ra_vfs/src/io.rs
+++ b/crates/ra_vfs/src/io.rs
@@ -1,35 +1,26 @@
1use std::{ 1use std::{
2 fs, 2 fs,
3 path::{Path, PathBuf}, 3 path::{Path, PathBuf},
4 thread::JoinHandle,
5}; 4};
6 5
7use walkdir::{DirEntry, WalkDir}; 6use walkdir::{DirEntry, WalkDir};
8use crossbeam_channel::{Sender, Receiver};
9use thread_worker::{WorkerHandle}; 7use thread_worker::{WorkerHandle};
8use relative_path::RelativePathBuf;
10 9
11use crate::VfsRoot; 10use crate::VfsRoot;
12 11
13pub(crate) enum Task { 12pub(crate) struct Task {
14 ScanRoot { 13 pub(crate) root: VfsRoot,
15 root: VfsRoot,
16 path: PathBuf,
17 filter: Box<FnMut(&DirEntry) -> bool + Send>,
18 },
19}
20
21#[derive(Debug)]
22pub(crate) struct FileEvent {
23 pub(crate) path: PathBuf, 14 pub(crate) path: PathBuf,
24 pub(crate) kind: FileEventKind, 15 pub(crate) filter: Box<Fn(&DirEntry) -> bool + Send>,
25} 16}
26 17
27#[derive(Debug)] 18pub struct TaskResult {
28pub(crate) enum FileEventKind { 19 pub(crate) root: VfsRoot,
29 Add(String), 20 pub(crate) files: Vec<(RelativePathBuf, String)>,
30} 21}
31 22
32pub(crate) type Worker = thread_worker::Worker<Task, (PathBuf, Vec<FileEvent>)>; 23pub(crate) type Worker = thread_worker::Worker<Task, TaskResult>;
33 24
34pub(crate) fn start() -> (Worker, WorkerHandle) { 25pub(crate) fn start() -> (Worker, WorkerHandle) {
35 thread_worker::spawn("vfs", 128, |input_receiver, output_sender| { 26 thread_worker::spawn("vfs", 128, |input_receiver, output_sender| {
@@ -39,17 +30,17 @@ pub(crate) fn start() -> (Worker, WorkerHandle) {
39 }) 30 })
40} 31}
41 32
42fn handle_task(task: Task) -> (PathBuf, Vec<FileEvent>) { 33fn handle_task(task: Task) -> TaskResult {
43 let Task::ScanRoot { path, .. } = task; 34 let Task { root, path, filter } = task;
44 log::debug!("loading {} ...", path.as_path().display()); 35 log::debug!("loading {} ...", path.as_path().display());
45 let events = load_root(path.as_path()); 36 let files = load_root(path.as_path(), &*filter);
46 log::debug!("... loaded {}", path.as_path().display()); 37 log::debug!("... loaded {}", path.as_path().display());
47 (path, events) 38 TaskResult { root, files }
48} 39}
49 40
50fn load_root(path: &Path) -> Vec<FileEvent> { 41fn load_root(root: &Path, filter: &dyn Fn(&DirEntry) -> bool) -> Vec<(RelativePathBuf, String)> {
51 let mut res = Vec::new(); 42 let mut res = Vec::new();
52 for entry in WalkDir::new(path) { 43 for entry in WalkDir::new(root).into_iter().filter_entry(filter) {
53 let entry = match entry { 44 let entry = match entry {
54 Ok(entry) => entry, 45 Ok(entry) => entry,
55 Err(e) => { 46 Err(e) => {
@@ -71,10 +62,8 @@ fn load_root(path: &Path) -> Vec<FileEvent> {
71 continue; 62 continue;
72 } 63 }
73 }; 64 };
74 res.push(FileEvent { 65 let path = RelativePathBuf::from_path(path.strip_prefix(root).unwrap()).unwrap();
75 path: path.to_owned(), 66 res.push((path.to_owned(), text))
76 kind: FileEventKind::Add(text),
77 })
78 } 67 }
79 res 68 res
80} 69}
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)]
diff --git a/crates/ra_vfs/tests/vfs.rs b/crates/ra_vfs/tests/vfs.rs
new file mode 100644
index 000000000..4f44215c8
--- /dev/null
+++ b/crates/ra_vfs/tests/vfs.rs
@@ -0,0 +1,101 @@
1use std::{
2 fs,
3 collections::HashSet,
4};
5
6use tempfile::tempdir;
7
8use ra_vfs::{Vfs, VfsChange};
9
10#[test]
11fn test_vfs_works() -> std::io::Result<()> {
12 let files = [
13 ("a/foo.rs", "hello"),
14 ("a/bar.rs", "world"),
15 ("a/b/baz.rs", "nested hello"),
16 ];
17
18 let dir = tempdir()?;
19 for (path, text) in files.iter() {
20 let file_path = dir.path().join(path);
21 fs::create_dir_all(file_path.parent().unwrap())?;
22 fs::write(file_path, text)?
23 }
24
25 let a_root = dir.path().join("a");
26 let b_root = dir.path().join("a/b");
27
28 let mut vfs = Vfs::new(vec![a_root, b_root]);
29 for _ in 0..2 {
30 let task = vfs.task_receiver().recv().unwrap();
31 vfs.handle_task(task);
32 }
33 {
34 let files = vfs
35 .commit_changes()
36 .into_iter()
37 .flat_map(|change| {
38 let files = match change {
39 VfsChange::AddRoot { files, .. } => files,
40 _ => panic!("unexpected change"),
41 };
42 files.into_iter().map(|(_id, path, text)| {
43 let text: String = (&*text).clone();
44 (format!("{}", path.display()), text)
45 })
46 })
47 .collect::<HashSet<_>>();
48
49 let expected_files = [
50 ("foo.rs", "hello"),
51 ("bar.rs", "world"),
52 ("baz.rs", "nested hello"),
53 ]
54 .iter()
55 .map(|(path, text)| (path.to_string(), text.to_string()))
56 .collect::<HashSet<_>>();
57
58 assert_eq!(files, expected_files);
59 }
60
61 vfs.add_file_overlay(&dir.path().join("a/b/baz.rs"), "quux".to_string());
62 let change = vfs.commit_changes().pop().unwrap();
63 match change {
64 VfsChange::ChangeFile { text, .. } => assert_eq!(&*text, "quux"),
65 _ => panic!("unexpected change"),
66 }
67
68 vfs.change_file_overlay(&dir.path().join("a/b/baz.rs"), "m".to_string());
69 let change = vfs.commit_changes().pop().unwrap();
70 match change {
71 VfsChange::ChangeFile { text, .. } => assert_eq!(&*text, "m"),
72 _ => panic!("unexpected change"),
73 }
74
75 vfs.remove_file_overlay(&dir.path().join("a/b/baz.rs"));
76 let change = vfs.commit_changes().pop().unwrap();
77 match change {
78 VfsChange::ChangeFile { text, .. } => assert_eq!(&*text, "nested hello"),
79 _ => panic!("unexpected change"),
80 }
81
82 vfs.add_file_overlay(&dir.path().join("a/b/spam.rs"), "spam".to_string());
83 let change = vfs.commit_changes().pop().unwrap();
84 match change {
85 VfsChange::AddFile { text, path, .. } => {
86 assert_eq!(&*text, "spam");
87 assert_eq!(path, "spam.rs");
88 }
89 _ => panic!("unexpected change"),
90 }
91
92 vfs.remove_file_overlay(&dir.path().join("a/b/spam.rs"));
93 let change = vfs.commit_changes().pop().unwrap();
94 match change {
95 VfsChange::RemoveFile { .. } => (),
96 _ => panic!("unexpected change"),
97 }
98
99 vfs.shutdown().unwrap();
100 Ok(())
101}