]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/src/cli/load_cargo.rs
Merge #5015 #5027 #5028
[rust.git] / crates / rust-analyzer / src / cli / load_cargo.rs
1 //! Loads a Cargo project into a static instance of analysis, without support
2 //! for incorporating changes.
3 use std::{convert::TryFrom, path::Path, sync::Arc};
4
5 use anyhow::Result;
6 use crossbeam_channel::{unbounded, Receiver};
7 use ra_db::{AbsPathBuf, CrateGraph};
8 use ra_ide::{AnalysisChange, AnalysisHost};
9 use ra_project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace};
10 use vfs::loader::Handle;
11
12 use crate::global_state::{ProjectFolders, SourceRootConfig};
13
14 pub fn load_cargo(
15     root: &Path,
16     load_out_dirs_from_check: bool,
17     with_proc_macro: bool,
18 ) -> Result<(AnalysisHost, vfs::Vfs)> {
19     let root = AbsPathBuf::assert(std::env::current_dir()?.join(root));
20     let root = ProjectManifest::discover_single(&root)?;
21     let ws = ProjectWorkspace::load(
22         root,
23         &CargoConfig { load_out_dirs_from_check, ..Default::default() },
24         true,
25     )?;
26
27     let (sender, receiver) = unbounded();
28     let mut vfs = vfs::Vfs::default();
29     let mut loader = {
30         let loader =
31             vfs_notify::LoaderHandle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
32         Box::new(loader)
33     };
34
35     let proc_macro_client = if with_proc_macro {
36         let path = std::env::current_exe()?;
37         ProcMacroClient::extern_process(path, &["proc-macro"]).unwrap()
38     } else {
39         ProcMacroClient::dummy()
40     };
41
42     let crate_graph = ws.to_crate_graph(None, &proc_macro_client, &mut |path: &Path| {
43         let path = AbsPathBuf::try_from(path.to_path_buf()).unwrap();
44         let contents = loader.load_sync(&path);
45         let path = vfs::VfsPath::from(path);
46         vfs.set_file_contents(path.clone(), contents);
47         vfs.file_id(&path)
48     });
49
50     let project_folders = ProjectFolders::new(&[ws]);
51     loader.set_config(vfs::loader::Config { load: project_folders.load, watch: vec![] });
52
53     log::debug!("crate graph: {:?}", crate_graph);
54     let host = load(crate_graph, project_folders.source_root_config, &mut vfs, &receiver);
55     Ok((host, vfs))
56 }
57
58 pub(crate) fn load(
59     crate_graph: CrateGraph,
60     source_root_config: SourceRootConfig,
61     vfs: &mut vfs::Vfs,
62     receiver: &Receiver<vfs::loader::Message>,
63 ) -> AnalysisHost {
64     let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
65     let mut host = AnalysisHost::new(lru_cap);
66     let mut analysis_change = AnalysisChange::new();
67
68     // wait until Vfs has loaded all roots
69     for task in receiver {
70         match task {
71             vfs::loader::Message::Progress { n_entries_done, n_entries_total } => {
72                 if n_entries_done == n_entries_total {
73                     break;
74                 }
75             }
76             vfs::loader::Message::Loaded { files } => {
77                 for (path, contents) in files {
78                     vfs.set_file_contents(path.into(), contents)
79                 }
80             }
81         }
82     }
83     let changes = vfs.take_changes();
84     for file in changes {
85         if file.exists() {
86             let contents = vfs.file_contents(file.file_id).to_vec();
87             if let Ok(text) = String::from_utf8(contents) {
88                 analysis_change.change_file(file.file_id, Some(Arc::new(text)))
89             }
90         }
91     }
92     let source_roots = source_root_config.partition(&vfs);
93     analysis_change.set_roots(source_roots);
94
95     analysis_change.set_crate_graph(crate_graph);
96
97     host.apply_change(analysis_change);
98     host
99 }
100
101 #[cfg(test)]
102 mod tests {
103     use super::*;
104
105     use hir::Crate;
106
107     #[test]
108     fn test_loading_rust_analyzer() {
109         let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
110         let (host, _vfs) = load_cargo(path, false, false).unwrap();
111         let n_crates = Crate::all(host.raw_database()).len();
112         // RA has quite a few crates, but the exact count doesn't matter
113         assert!(n_crates > 20);
114     }
115 }