diff options
Diffstat (limited to 'crates/ra_batch/src/lib.rs')
-rw-r--r-- | crates/ra_batch/src/lib.rs | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs new file mode 100644 index 000000000..837fff4dc --- /dev/null +++ b/crates/ra_batch/src/lib.rs | |||
@@ -0,0 +1,151 @@ | |||
1 | use std::sync::Arc; | ||
2 | use std::path::Path; | ||
3 | use std::collections::HashSet; | ||
4 | |||
5 | use rustc_hash::FxHashMap; | ||
6 | |||
7 | use ra_db::{ | ||
8 | CrateGraph, FileId, SourceRoot, SourceRootId, SourceDatabase, salsa, | ||
9 | }; | ||
10 | use ra_hir::{db, HirInterner}; | ||
11 | use ra_project_model::ProjectWorkspace; | ||
12 | use ra_vfs::{Vfs, VfsChange}; | ||
13 | |||
14 | type Result<T> = std::result::Result<T, failure::Error>; | ||
15 | |||
16 | #[salsa::database( | ||
17 | ra_db::SourceDatabaseStorage, | ||
18 | db::HirDatabaseStorage, | ||
19 | db::PersistentHirDatabaseStorage | ||
20 | )] | ||
21 | #[derive(Debug)] | ||
22 | pub struct BatchDatabase { | ||
23 | runtime: salsa::Runtime<BatchDatabase>, | ||
24 | interner: Arc<HirInterner>, | ||
25 | } | ||
26 | |||
27 | impl salsa::Database for BatchDatabase { | ||
28 | fn salsa_runtime(&self) -> &salsa::Runtime<BatchDatabase> { | ||
29 | &self.runtime | ||
30 | } | ||
31 | } | ||
32 | |||
33 | impl AsRef<HirInterner> for BatchDatabase { | ||
34 | fn as_ref(&self) -> &HirInterner { | ||
35 | &self.interner | ||
36 | } | ||
37 | } | ||
38 | |||
39 | fn vfs_file_to_id(f: ra_vfs::VfsFile) -> FileId { | ||
40 | FileId(f.0.into()) | ||
41 | } | ||
42 | fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId { | ||
43 | SourceRootId(r.0.into()) | ||
44 | } | ||
45 | |||
46 | impl BatchDatabase { | ||
47 | pub fn load(crate_graph: CrateGraph, vfs: &mut Vfs) -> BatchDatabase { | ||
48 | let mut db = | ||
49 | BatchDatabase { runtime: salsa::Runtime::default(), interner: Default::default() }; | ||
50 | db.set_crate_graph(Arc::new(crate_graph)); | ||
51 | |||
52 | // wait until Vfs has loaded all roots | ||
53 | let receiver = vfs.task_receiver().clone(); | ||
54 | let mut roots_loaded = HashSet::new(); | ||
55 | for task in receiver { | ||
56 | vfs.handle_task(task); | ||
57 | let mut done = false; | ||
58 | for change in vfs.commit_changes() { | ||
59 | match change { | ||
60 | VfsChange::AddRoot { root, files } => { | ||
61 | let source_root_id = vfs_root_to_id(root); | ||
62 | log::debug!( | ||
63 | "loaded source root {:?} with path {:?}", | ||
64 | source_root_id, | ||
65 | vfs.root2path(root) | ||
66 | ); | ||
67 | let mut file_map = FxHashMap::default(); | ||
68 | for (vfs_file, path, text) in files { | ||
69 | let file_id = vfs_file_to_id(vfs_file); | ||
70 | db.set_file_text(file_id, text); | ||
71 | db.set_file_relative_path(file_id, path.clone()); | ||
72 | db.set_file_source_root(file_id, source_root_id); | ||
73 | file_map.insert(path, file_id); | ||
74 | } | ||
75 | let source_root = SourceRoot { files: file_map }; | ||
76 | db.set_source_root(source_root_id, Arc::new(source_root)); | ||
77 | roots_loaded.insert(source_root_id); | ||
78 | if roots_loaded.len() == vfs.num_roots() { | ||
79 | done = true; | ||
80 | } | ||
81 | } | ||
82 | VfsChange::AddFile { .. } | ||
83 | | VfsChange::RemoveFile { .. } | ||
84 | | VfsChange::ChangeFile { .. } => { | ||
85 | // We just need the first scan, so just ignore these | ||
86 | } | ||
87 | } | ||
88 | } | ||
89 | if done { | ||
90 | break; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | db | ||
95 | } | ||
96 | |||
97 | pub fn load_cargo(root: impl AsRef<Path>) -> Result<(BatchDatabase, Vec<SourceRootId>)> { | ||
98 | let root = root.as_ref().canonicalize()?; | ||
99 | let ws = ProjectWorkspace::discover(root.as_ref())?; | ||
100 | let mut roots = Vec::new(); | ||
101 | roots.push(root.clone()); | ||
102 | for pkg in ws.cargo.packages() { | ||
103 | roots.push(pkg.root(&ws.cargo).to_path_buf()); | ||
104 | } | ||
105 | for krate in ws.sysroot.crates() { | ||
106 | roots.push(krate.root_dir(&ws.sysroot).to_path_buf()) | ||
107 | } | ||
108 | let (mut vfs, roots) = Vfs::new(roots); | ||
109 | let mut load = |path: &Path| { | ||
110 | let vfs_file = vfs.load(path); | ||
111 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); | ||
112 | vfs_file.map(vfs_file_to_id) | ||
113 | }; | ||
114 | let crate_graph = ws.to_crate_graph(&mut load); | ||
115 | log::debug!("crate graph: {:?}", crate_graph); | ||
116 | |||
117 | let local_roots = roots | ||
118 | .into_iter() | ||
119 | .filter(|r| vfs.root2path(*r).starts_with(&root)) | ||
120 | .map(vfs_root_to_id) | ||
121 | .collect(); | ||
122 | |||
123 | let db = BatchDatabase::load(crate_graph, &mut vfs); | ||
124 | let _ = vfs.shutdown(); | ||
125 | Ok((db, local_roots)) | ||
126 | } | ||
127 | } | ||
128 | |||
129 | #[cfg(test)] | ||
130 | mod tests { | ||
131 | use ra_hir::Crate; | ||
132 | use super::*; | ||
133 | |||
134 | #[test] | ||
135 | fn test_loading_rust_analyzer() { | ||
136 | let mut path = std::env::current_exe().unwrap(); | ||
137 | while !path.join("Cargo.toml").is_file() { | ||
138 | path = path.parent().unwrap().to_owned(); | ||
139 | } | ||
140 | let (db, roots) = BatchDatabase::load_cargo(path).unwrap(); | ||
141 | let mut num_crates = 0; | ||
142 | for root in roots { | ||
143 | for _krate in Crate::source_root_crates(&db, root) { | ||
144 | num_crates += 1; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | // RA has quite a few crates, but the exact count doesn't matter | ||
149 | assert!(num_crates > 20); | ||
150 | } | ||
151 | } | ||