1 //! Loads a Cargo project into a static instance of analysis, without support
2 //! for incorporating changes.
3 use std
::{path::Path, sync::Arc}
;
6 use crossbeam_channel
::{unbounded, Receiver}
;
7 use hir
::db
::DefDatabase
;
8 use ide
::{AnalysisHost, Change}
;
9 use ide_db
::base_db
::CrateGraph
;
10 use proc_macro_api
::ProcMacroServer
;
11 use project_model
::{CargoConfig, ProjectManifest, ProjectWorkspace}
;
12 use vfs
::{loader::Handle, AbsPath, AbsPathBuf}
;
14 use crate::reload
::{load_proc_macro, ProjectFolders, SourceRootConfig}
;
16 // Note: Since this type is used by external tools that use rust-analyzer as a library
17 // what otherwise would be `pub(crate)` has to be `pub` here instead.
18 pub struct LoadCargoConfig
{
19 pub load_out_dirs_from_check
: bool
,
20 pub with_proc_macro
: bool
,
21 pub prefill_caches
: bool
,
24 // Note: Since this function is used by external tools that use rust-analyzer as a library
25 // what otherwise would be `pub(crate)` has to be `pub` here instead.
26 pub fn load_workspace_at(
28 cargo_config
: &CargoConfig
,
29 load_config
: &LoadCargoConfig
,
30 progress
: &dyn Fn(String
),
31 ) -> Result
<(AnalysisHost
, vfs
::Vfs
, Option
<ProcMacroServer
>)> {
32 let root
= AbsPathBuf
::assert(std
::env
::current_dir()?
.join(root
));
33 let root
= ProjectManifest
::discover_single(&root
)?
;
34 let mut workspace
= ProjectWorkspace
::load(root
, cargo_config
, progress
)?
;
36 if load_config
.load_out_dirs_from_check
{
37 let build_scripts
= workspace
.run_build_scripts(cargo_config
, progress
)?
;
38 workspace
.set_build_scripts(build_scripts
)
41 load_workspace(workspace
, load_config
)
44 // Note: Since this function is used by external tools that use rust-analyzer as a library
45 // what otherwise would be `pub(crate)` has to be `pub` here instead.
47 // The reason both, `load_workspace_at` and `load_workspace` are `pub` is that some of
48 // these tools need access to `ProjectWorkspace`, too, which `load_workspace_at` hides.
49 pub fn load_workspace(
51 load_config
: &LoadCargoConfig
,
52 ) -> Result
<(AnalysisHost
, vfs
::Vfs
, Option
<ProcMacroServer
>)> {
53 let (sender
, receiver
) = unbounded();
54 let mut vfs
= vfs
::Vfs
::default();
57 vfs_notify
::NotifyHandle
::spawn(Box
::new(move |msg
| sender
.send(msg
).unwrap()));
61 let proc_macro_client
= if load_config
.with_proc_macro
{
62 let path
= AbsPathBuf
::assert(std
::env
::current_exe()?
);
63 Ok(ProcMacroServer
::spawn(path
, &["proc-macro"]).unwrap())
65 Err("proc macro server not started".to_owned())
68 let crate_graph
= ws
.to_crate_graph(
69 &mut |_
, path
: &AbsPath
| {
70 load_proc_macro(proc_macro_client
.as_ref().map_err(|e
| &**e
), path
, &[])
72 &mut |path
: &AbsPath
| {
73 let contents
= loader
.load_sync(path
);
74 let path
= vfs
::VfsPath
::from(path
.to_path_buf());
75 vfs
.set_file_contents(path
.clone(), contents
);
80 let project_folders
= ProjectFolders
::new(&[ws
], &[]);
81 loader
.set_config(vfs
::loader
::Config
{
82 load
: project_folders
.load
,
87 tracing
::debug
!("crate graph: {:?}", crate_graph
);
89 load_crate_graph(crate_graph
, project_folders
.source_root_config
, &mut vfs
, &receiver
);
91 if load_config
.prefill_caches
{
92 host
.analysis().parallel_prime_caches(1, |_
| {}
)?
;
94 Ok((host
, vfs
, proc_macro_client
.ok()))
98 crate_graph
: CrateGraph
,
99 source_root_config
: SourceRootConfig
,
101 receiver
: &Receiver
<vfs
::loader
::Message
>,
103 let lru_cap
= std
::env
::var("RA_LRU_CAP").ok().and_then(|it
| it
.parse
::<usize>().ok());
104 let mut host
= AnalysisHost
::new(lru_cap
);
105 let mut analysis_change
= Change
::new();
107 host
.raw_database_mut().set_enable_proc_attr_macros(true);
109 // wait until Vfs has loaded all roots
110 for task
in receiver
{
112 vfs
::loader
::Message
::Progress { n_done, n_total, config_version: _ }
=> {
113 if n_done
== n_total
{
117 vfs
::loader
::Message
::Loaded { files }
=> {
118 for (path
, contents
) in files
{
119 vfs
.set_file_contents(path
.into(), contents
);
124 let changes
= vfs
.take_changes();
125 for file
in changes
{
127 let contents
= vfs
.file_contents(file
.file_id
).to_vec();
128 if let Ok(text
) = String
::from_utf8(contents
) {
129 analysis_change
.change_file(file
.file_id
, Some(Arc
::new(text
)))
133 let source_roots
= source_root_config
.partition(vfs
);
134 analysis_change
.set_roots(source_roots
);
136 analysis_change
.set_crate_graph(crate_graph
);
138 host
.apply_change(analysis_change
);
149 fn test_loading_rust_analyzer() {
150 let path
= Path
::new(env
!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
151 let cargo_config
= CargoConfig
::default();
152 let load_cargo_config
= LoadCargoConfig
{
153 load_out_dirs_from_check
: false,
154 with_proc_macro
: false,
155 prefill_caches
: false,
157 let (host
, _vfs
, _proc_macro
) =
158 load_workspace_at(path
, &cargo_config
, &load_cargo_config
, &|_
| {}
).unwrap();
160 let n_crates
= Crate
::all(host
.raw_database()).len();
161 // RA has quite a few crates, but the exact count doesn't matter
162 assert
!(n_crates
> 20);