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 | |
parent | e69b05781f7fb0f0dfdcd4acb433dbcde9cbb7b7 (diff) |
implement vfs events handling
-rw-r--r-- | Cargo.lock | 81 | ||||
-rw-r--r-- | crates/ra_lsp_server/tests/heavy_tests/support.rs | 4 | ||||
-rw-r--r-- | crates/ra_vfs/Cargo.toml | 3 | ||||
-rw-r--r-- | crates/ra_vfs/src/arena.rs | 7 | ||||
-rw-r--r-- | crates/ra_vfs/src/io.rs | 43 | ||||
-rw-r--r-- | crates/ra_vfs/src/lib.rs | 153 | ||||
-rw-r--r-- | crates/ra_vfs/tests/vfs.rs | 101 |
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]] |
783 | name = "rand" | ||
784 | version = "0.6.1" | ||
785 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
786 | dependencies = [ | ||
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]] | ||
801 | name = "rand_chacha" | ||
802 | version = "0.1.0" | ||
803 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
804 | dependencies = [ | ||
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]] | ||
782 | name = "rand_core" | 810 | name = "rand_core" |
783 | version = "0.2.2" | 811 | version = "0.2.2" |
784 | source = "registry+https://github.com/rust-lang/crates.io-index" | 812 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -792,6 +820,39 @@ version = "0.3.0" | |||
792 | source = "registry+https://github.com/rust-lang/crates.io-index" | 820 | source = "registry+https://github.com/rust-lang/crates.io-index" |
793 | 821 | ||
794 | [[package]] | 822 | [[package]] |
823 | name = "rand_hc" | ||
824 | version = "0.1.0" | ||
825 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
826 | dependencies = [ | ||
827 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
828 | ] | ||
829 | |||
830 | [[package]] | ||
831 | name = "rand_isaac" | ||
832 | version = "0.1.1" | ||
833 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
834 | dependencies = [ | ||
835 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
836 | ] | ||
837 | |||
838 | [[package]] | ||
839 | name = "rand_pcg" | ||
840 | version = "0.1.1" | ||
841 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
842 | dependencies = [ | ||
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]] | ||
848 | name = "rand_xorshift" | ||
849 | version = "0.1.0" | ||
850 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
851 | dependencies = [ | ||
852 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
853 | ] | ||
854 | |||
855 | [[package]] | ||
795 | name = "rayon" | 856 | name = "rayon" |
796 | version = "1.0.3" | 857 | version = "1.0.3" |
797 | source = "registry+https://github.com/rust-lang/crates.io-index" | 858 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1069,6 +1130,19 @@ dependencies = [ | |||
1069 | ] | 1130 | ] |
1070 | 1131 | ||
1071 | [[package]] | 1132 | [[package]] |
1133 | name = "tempfile" | ||
1134 | version = "3.0.5" | ||
1135 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1136 | dependencies = [ | ||
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]] | ||
1072 | name = "tera" | 1146 | name = "tera" |
1073 | version = "0.11.20" | 1147 | version = "0.11.20" |
1074 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1148 | source = "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 { | |||
174 | impl Drop for Server { | 174 | impl 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" | |||
12 | log = "0.4.6" | 12 | log = "0.4.6" |
13 | 13 | ||
14 | thread_worker = { path = "../thread_worker" } | 14 | thread_worker = { path = "../thread_worker" } |
15 | |||
16 | [dev-dependencies] | ||
17 | tempfile = "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 @@ | |||
1 | use std::{ | 1 | use 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 | ||
26 | impl<ID: ArenaId, T> Default for Arena<ID, T> { | 31 | impl<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 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | fs, | 2 | fs, |
3 | path::{Path, PathBuf}, | 3 | path::{Path, PathBuf}, |
4 | thread::JoinHandle, | ||
5 | }; | 4 | }; |
6 | 5 | ||
7 | use walkdir::{DirEntry, WalkDir}; | 6 | use walkdir::{DirEntry, WalkDir}; |
8 | use crossbeam_channel::{Sender, Receiver}; | ||
9 | use thread_worker::{WorkerHandle}; | 7 | use thread_worker::{WorkerHandle}; |
8 | use relative_path::RelativePathBuf; | ||
10 | 9 | ||
11 | use crate::VfsRoot; | 10 | use crate::VfsRoot; |
12 | 11 | ||
13 | pub(crate) enum Task { | 12 | pub(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)] | ||
22 | pub(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)] | 18 | pub struct TaskResult { |
28 | pub(crate) enum FileEventKind { | 19 | pub(crate) root: VfsRoot, |
29 | Add(String), | 20 | pub(crate) files: Vec<(RelativePathBuf, String)>, |
30 | } | 21 | } |
31 | 22 | ||
32 | pub(crate) type Worker = thread_worker::Worker<Task, (PathBuf, Vec<FileEvent>)>; | 23 | pub(crate) type Worker = thread_worker::Worker<Task, TaskResult>; |
33 | 24 | ||
34 | pub(crate) fn start() -> (Worker, WorkerHandle) { | 25 | pub(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 | ||
42 | fn handle_task(task: Task) -> (PathBuf, Vec<FileEvent>) { | 33 | fn 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 | ||
50 | fn load_root(path: &Path) -> Vec<FileEvent> { | 41 | fn 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; | |||
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)] |
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 @@ | |||
1 | use std::{ | ||
2 | fs, | ||
3 | collections::HashSet, | ||
4 | }; | ||
5 | |||
6 | use tempfile::tempdir; | ||
7 | |||
8 | use ra_vfs::{Vfs, VfsChange}; | ||
9 | |||
10 | #[test] | ||
11 | fn 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 | } | ||