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, FxHashMap}
;
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
, &cargo_config
.extra_env
, 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 extra_env
: &FxHashMap
<String
, String
>,
52 load_config
: &LoadCargoConfig
,
53 ) -> Result
<(AnalysisHost
, vfs
::Vfs
, Option
<ProcMacroServer
>)> {
54 let (sender
, receiver
) = unbounded();
55 let mut vfs
= vfs
::Vfs
::default();
58 vfs_notify
::NotifyHandle
::spawn(Box
::new(move |msg
| sender
.send(msg
).unwrap()));
62 let proc_macro_client
= if load_config
.with_proc_macro
{
63 let mut path
= AbsPathBuf
::assert(std
::env
::current_exe()?
);
64 let mut args
= vec
!["proc-macro"];
66 if let ProjectWorkspace
::Cargo { sysroot, .. }
| ProjectWorkspace
::Json { sysroot, .. }
=
69 if let Some(sysroot
) = sysroot
.as_ref() {
70 let standalone_server_name
=
71 format
!("rust-analyzer-proc-macro-srv{}", std
::env
::consts
::EXE_SUFFIX
);
72 let server_path
= sysroot
.root().join("libexec").join(&standalone_server_name
);
73 if std
::fs
::metadata(&server_path
).is_ok() {
80 ProcMacroServer
::spawn(path
.clone(), args
.clone()).map_err(|e
| e
.to_string())
82 Err("proc macro server disabled".to_owned())
85 let crate_graph
= ws
.to_crate_graph(
86 &mut |_
, path
: &AbsPath
| {
87 load_proc_macro(proc_macro_client
.as_ref().map_err(|e
| &**e
), path
, &[])
89 &mut |path
: &AbsPath
| {
90 let contents
= loader
.load_sync(path
);
91 let path
= vfs
::VfsPath
::from(path
.to_path_buf());
92 vfs
.set_file_contents(path
.clone(), contents
);
98 let project_folders
= ProjectFolders
::new(&[ws
], &[]);
99 loader
.set_config(vfs
::loader
::Config
{
100 load
: project_folders
.load
,
105 tracing
::debug
!("crate graph: {:?}", crate_graph
);
107 load_crate_graph(crate_graph
, project_folders
.source_root_config
, &mut vfs
, &receiver
);
109 if load_config
.prefill_caches
{
110 host
.analysis().parallel_prime_caches(1, |_
| {}
)?
;
112 Ok((host
, vfs
, proc_macro_client
.ok()))
116 crate_graph
: CrateGraph
,
117 source_root_config
: SourceRootConfig
,
119 receiver
: &Receiver
<vfs
::loader
::Message
>,
121 let lru_cap
= std
::env
::var("RA_LRU_CAP").ok().and_then(|it
| it
.parse
::<usize>().ok());
122 let mut host
= AnalysisHost
::new(lru_cap
);
123 let mut analysis_change
= Change
::new();
125 host
.raw_database_mut().set_enable_proc_attr_macros(true);
127 // wait until Vfs has loaded all roots
128 for task
in receiver
{
130 vfs
::loader
::Message
::Progress { n_done, n_total, config_version: _ }
=> {
131 if n_done
== n_total
{
135 vfs
::loader
::Message
::Loaded { files }
=> {
136 for (path
, contents
) in files
{
137 vfs
.set_file_contents(path
.into(), contents
);
142 let changes
= vfs
.take_changes();
143 for file
in changes
{
145 let contents
= vfs
.file_contents(file
.file_id
).to_vec();
146 if let Ok(text
) = String
::from_utf8(contents
) {
147 analysis_change
.change_file(file
.file_id
, Some(Arc
::new(text
)))
151 let source_roots
= source_root_config
.partition(vfs
);
152 analysis_change
.set_roots(source_roots
);
154 analysis_change
.set_crate_graph(crate_graph
);
156 host
.apply_change(analysis_change
);
167 fn test_loading_rust_analyzer() {
168 let path
= Path
::new(env
!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
169 let cargo_config
= CargoConfig
::default();
170 let load_cargo_config
= LoadCargoConfig
{
171 load_out_dirs_from_check
: false,
172 with_proc_macro
: false,
173 prefill_caches
: false,
175 let (host
, _vfs
, _proc_macro
) =
176 load_workspace_at(path
, &cargo_config
, &load_cargo_config
, &|_
| {}
).unwrap();
178 let n_crates
= Crate
::all(host
.raw_database()).len();
179 // RA has quite a few crates, but the exact count doesn't matter
180 assert
!(n_crates
> 20);