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