aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs10
-rw-r--r--crates/rust-analyzer/src/global_state.rs244
-rw-r--r--crates/rust-analyzer/src/lib.rs1
-rw-r--r--crates/rust-analyzer/src/reload.rs241
5 files changed, 260 insertions, 238 deletions
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index c5cf5ff27..b1250f2fe 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -9,7 +9,7 @@ use ra_ide::{AnalysisChange, AnalysisHost};
9use ra_project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace}; 9use ra_project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace};
10use vfs::{loader::Handle, AbsPath}; 10use vfs::{loader::Handle, AbsPath};
11 11
12use crate::global_state::{ProjectFolders, SourceRootConfig}; 12use crate::reload::{ProjectFolders, SourceRootConfig};
13 13
14pub fn load_cargo( 14pub fn load_cargo(
15 root: &Path, 15 root: &Path,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 435bbbb6b..6b17ce18b 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -30,7 +30,7 @@ pub struct Config {
30 30
31 pub cargo: CargoConfig, 31 pub cargo: CargoConfig,
32 pub rustfmt: RustfmtConfig, 32 pub rustfmt: RustfmtConfig,
33 pub check: Option<FlycheckConfig>, 33 pub flycheck: Option<FlycheckConfig>,
34 34
35 pub inlay_hints: InlayHintsConfig, 35 pub inlay_hints: InlayHintsConfig,
36 pub completion: CompletionConfig, 36 pub completion: CompletionConfig,
@@ -147,7 +147,7 @@ impl Config {
147 147
148 cargo: CargoConfig::default(), 148 cargo: CargoConfig::default(),
149 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() }, 149 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
150 check: Some(FlycheckConfig::CargoCommand { 150 flycheck: Some(FlycheckConfig::CargoCommand {
151 command: "check".to_string(), 151 command: "check".to_string(),
152 all_targets: true, 152 all_targets: true,
153 all_features: false, 153 all_features: false,
@@ -227,14 +227,14 @@ impl Config {
227 227
228 if let Some(false) = get(value, "/checkOnSave/enable") { 228 if let Some(false) = get(value, "/checkOnSave/enable") {
229 // check is disabled 229 // check is disabled
230 self.check = None; 230 self.flycheck = None;
231 } else { 231 } else {
232 // check is enabled 232 // check is enabled
233 match get::<Vec<String>>(value, "/checkOnSave/overrideCommand") { 233 match get::<Vec<String>>(value, "/checkOnSave/overrideCommand") {
234 // first see if the user has completely overridden the command 234 // first see if the user has completely overridden the command
235 Some(mut args) if !args.is_empty() => { 235 Some(mut args) if !args.is_empty() => {
236 let command = args.remove(0); 236 let command = args.remove(0);
237 self.check = Some(FlycheckConfig::CustomCommand { 237 self.flycheck = Some(FlycheckConfig::CustomCommand {
238 command, 238 command,
239 args, 239 args,
240 }); 240 });
@@ -242,7 +242,7 @@ impl Config {
242 // otherwise configure command customizations 242 // otherwise configure command customizations
243 _ => { 243 _ => {
244 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features, features }) 244 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features, features })
245 = &mut self.check 245 = &mut self.flycheck
246 { 246 {
247 set(value, "/checkOnSave/extraArgs", extra_args); 247 set(value, "/checkOnSave/extraArgs", extra_args);
248 set(value, "/checkOnSave/command", command); 248 set(value, "/checkOnSave/command", command);
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index ca4a248f1..8486da627 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -3,24 +3,25 @@
3//! 3//!
4//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. 4//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
5 5
6use std::{convert::TryFrom, sync::Arc}; 6use std::sync::Arc;
7 7
8use crossbeam_channel::{unbounded, Receiver, Sender}; 8use crossbeam_channel::{unbounded, Receiver, Sender};
9use flycheck::{FlycheckConfig, FlycheckHandle}; 9use flycheck::FlycheckHandle;
10use lsp_types::{request::Request, Url}; 10use lsp_types::Url;
11use parking_lot::RwLock; 11use parking_lot::RwLock;
12use ra_db::{CrateId, SourceRoot, VfsPath}; 12use ra_db::{CrateId, VfsPath};
13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId}; 13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId};
14use ra_project_model::{CargoWorkspace, PackageRoot, ProcMacroClient, ProjectWorkspace, Target}; 14use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target};
15use stdx::format_to; 15use stdx::format_to;
16use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf}; 16use vfs::loader::Handle;
17 17
18use crate::{ 18use crate::{
19 config::{Config, FilesWatcher, LinkedProject}, 19 config::Config,
20 diagnostics::{CheckFixes, DiagnosticCollection}, 20 diagnostics::{CheckFixes, DiagnosticCollection},
21 from_proto, 21 from_proto,
22 line_endings::LineEndings, 22 line_endings::LineEndings,
23 main_loop::{ReqQueue, Task}, 23 main_loop::{ReqQueue, Task},
24 reload::SourceRootConfig,
24 request_metrics::{LatestRequests, RequestMetrics}, 25 request_metrics::{LatestRequests, RequestMetrics},
25 show_message, 26 show_message,
26 thread_pool::TaskPool, 27 thread_pool::TaskPool,
@@ -29,26 +30,6 @@ use crate::{
29}; 30};
30use rustc_hash::{FxHashMap, FxHashSet}; 31use rustc_hash::{FxHashMap, FxHashSet};
31 32
32fn create_flycheck(
33 workspaces: &[ProjectWorkspace],
34 config: &FlycheckConfig,
35) -> Option<(FlycheckHandle, Receiver<flycheck::Message>)> {
36 // FIXME: Figure out the multi-workspace situation
37 workspaces.iter().find_map(move |w| match w {
38 ProjectWorkspace::Cargo { cargo, .. } => {
39 let (sender, receiver) = unbounded();
40 let sender = Box::new(move |msg| sender.send(msg).unwrap());
41 let cargo_project_root = cargo.workspace_root().to_path_buf();
42 let flycheck = FlycheckHandle::spawn(sender, config.clone(), cargo_project_root.into());
43 Some((flycheck, receiver))
44 }
45 ProjectWorkspace::Json { .. } => {
46 log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
47 None
48 }
49 })
50}
51
52#[derive(Eq, PartialEq)] 33#[derive(Eq, PartialEq)]
53pub(crate) enum Status { 34pub(crate) enum Status {
54 Loading, 35 Loading,
@@ -79,10 +60,10 @@ pub(crate) struct GlobalState {
79 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, 60 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
80 pub(crate) status: Status, 61 pub(crate) status: Status,
81 pub(crate) req_queue: ReqQueue, 62 pub(crate) req_queue: ReqQueue,
63 pub(crate) source_root_config: SourceRootConfig,
64 pub(crate) proc_macro_client: ProcMacroClient,
65 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
82 latest_requests: Arc<RwLock<LatestRequests>>, 66 latest_requests: Arc<RwLock<LatestRequests>>,
83 source_root_config: SourceRootConfig,
84 proc_macro_client: ProcMacroClient,
85 workspaces: Arc<Vec<ProjectWorkspace>>,
86} 67}
87 68
88/// An immutable snapshot of the world's state at a point in time. 69/// An immutable snapshot of the world's state at a point in time.
@@ -135,132 +116,6 @@ impl GlobalState {
135 } 116 }
136 } 117 }
137 118
138 pub(crate) fn reload(&mut self) {
139 let workspaces = {
140 if self.config.linked_projects.is_empty()
141 && self.config.notifications.cargo_toml_not_found
142 {
143 self.show_message(
144 lsp_types::MessageType::Error,
145 "rust-analyzer failed to discover workspace".to_string(),
146 );
147 };
148
149 self.config
150 .linked_projects
151 .iter()
152 .filter_map(|project| match project {
153 LinkedProject::ProjectManifest(manifest) => {
154 ra_project_model::ProjectWorkspace::load(
155 manifest.clone(),
156 &self.config.cargo,
157 self.config.with_sysroot,
158 )
159 .map_err(|err| {
160 log::error!("failed to load workspace: {:#}", err);
161 self.show_message(
162 lsp_types::MessageType::Error,
163 format!("rust-analyzer failed to load workspace: {:#}", err),
164 );
165 })
166 .ok()
167 }
168 LinkedProject::InlineJsonProject(it) => {
169 Some(ra_project_model::ProjectWorkspace::Json { project: it.clone() })
170 }
171 })
172 .collect::<Vec<_>>()
173 };
174
175 if let FilesWatcher::Client = self.config.files.watcher {
176 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
177 watchers: workspaces
178 .iter()
179 .flat_map(ProjectWorkspace::to_roots)
180 .filter(PackageRoot::is_member)
181 .map(|root| format!("{}/**/*.rs", root.path().display()))
182 .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
183 .collect(),
184 };
185 let registration = lsp_types::Registration {
186 id: "file-watcher".to_string(),
187 method: "workspace/didChangeWatchedFiles".to_string(),
188 register_options: Some(serde_json::to_value(registration_options).unwrap()),
189 };
190 let params = lsp_types::RegistrationParams { registrations: vec![registration] };
191 let request = self.req_queue.outgoing.register(
192 lsp_types::request::RegisterCapability::METHOD.to_string(),
193 params,
194 |_, _| (),
195 );
196 self.send(request.into());
197 }
198
199 let mut change = AnalysisChange::new();
200
201 let project_folders = ProjectFolders::new(&workspaces);
202
203 self.proc_macro_client = match &self.config.proc_macro_srv {
204 None => ProcMacroClient::dummy(),
205 Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
206 Ok(it) => it,
207 Err(err) => {
208 log::error!(
209 "Failed to run ra_proc_macro_srv from path {}, error: {:?}",
210 path.display(),
211 err
212 );
213 ProcMacroClient::dummy()
214 }
215 },
216 };
217 let watch = match self.config.files.watcher {
218 FilesWatcher::Client => vec![],
219 FilesWatcher::Notify => project_folders.watch,
220 };
221 self.loader.set_config(vfs::loader::Config { load: project_folders.load, watch });
222
223 // Create crate graph from all the workspaces
224 let crate_graph = {
225 let mut crate_graph = CrateGraph::default();
226 let vfs = &mut self.vfs.write().0;
227 let loader = &mut self.loader;
228 let mut load = |path: &AbsPath| {
229 let contents = loader.load_sync(path);
230 let path = vfs::VfsPath::from(path.to_path_buf());
231 vfs.set_file_contents(path.clone(), contents);
232 vfs.file_id(&path)
233 };
234 for ws in workspaces.iter() {
235 crate_graph.extend(ws.to_crate_graph(
236 self.config.cargo.target.as_deref(),
237 &self.proc_macro_client,
238 &mut load,
239 ));
240 }
241
242 crate_graph
243 };
244 change.set_crate_graph(crate_graph);
245
246 self.flycheck = self.config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c));
247 self.source_root_config = project_folders.source_root_config;
248 self.workspaces = Arc::new(workspaces);
249
250 self.analysis_host.apply_change(change);
251 self.process_changes();
252 }
253
254 pub(crate) fn update_configuration(&mut self, config: Config) {
255 self.analysis_host.update_lru_capacity(config.lru_capacity);
256 if config.check != self.config.check {
257 self.flycheck =
258 config.check.as_ref().and_then(|it| create_flycheck(&self.workspaces, it));
259 }
260
261 self.config = config;
262 }
263
264 pub(crate) fn process_changes(&mut self) -> bool { 119 pub(crate) fn process_changes(&mut self) -> bool {
265 let change = { 120 let change = {
266 let mut change = AnalysisChange::new(); 121 let mut change = AnalysisChange::new();
@@ -408,78 +263,3 @@ pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
408 let path = path.as_path().unwrap(); 263 let path = path.as_path().unwrap();
409 url_from_abs_path(&path) 264 url_from_abs_path(&path)
410} 265}
411
412#[derive(Default)]
413pub(crate) struct ProjectFolders {
414 pub(crate) load: Vec<vfs::loader::Entry>,
415 pub(crate) watch: Vec<usize>,
416 pub(crate) source_root_config: SourceRootConfig,
417}
418
419impl ProjectFolders {
420 pub(crate) fn new(workspaces: &[ProjectWorkspace]) -> ProjectFolders {
421 let mut res = ProjectFolders::default();
422 let mut fsc = FileSetConfig::builder();
423 let mut local_filesets = vec![];
424
425 for root in workspaces.iter().flat_map(|it| it.to_roots()) {
426 let path = root.path().to_owned();
427
428 let mut file_set_roots: Vec<VfsPath> = vec![];
429
430 let entry = if root.is_member() {
431 vfs::loader::Entry::local_cargo_package(path.to_path_buf())
432 } else {
433 vfs::loader::Entry::cargo_package_dependency(path.to_path_buf())
434 };
435 res.load.push(entry);
436 if root.is_member() {
437 res.watch.push(res.load.len() - 1);
438 }
439
440 if let Some(out_dir) = root.out_dir() {
441 let out_dir = AbsPathBuf::try_from(out_dir.to_path_buf()).unwrap();
442 res.load.push(vfs::loader::Entry::rs_files_recursively(out_dir.clone()));
443 if root.is_member() {
444 res.watch.push(res.load.len() - 1);
445 }
446 file_set_roots.push(out_dir.into());
447 }
448 file_set_roots.push(path.to_path_buf().into());
449
450 if root.is_member() {
451 local_filesets.push(fsc.len());
452 }
453 fsc.add_file_set(file_set_roots)
454 }
455
456 let fsc = fsc.build();
457 res.source_root_config = SourceRootConfig { fsc, local_filesets };
458
459 res
460 }
461}
462
463#[derive(Default, Debug)]
464pub(crate) struct SourceRootConfig {
465 pub(crate) fsc: FileSetConfig,
466 pub(crate) local_filesets: Vec<usize>,
467}
468
469impl SourceRootConfig {
470 pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> {
471 self.fsc
472 .partition(vfs)
473 .into_iter()
474 .enumerate()
475 .map(|(idx, file_set)| {
476 let is_local = self.local_filesets.contains(&idx);
477 if is_local {
478 SourceRoot::new_local(file_set)
479 } else {
480 SourceRoot::new_library(file_set)
481 }
482 })
483 .collect()
484 }
485}
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index d503fe96e..a24dfe58c 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -18,6 +18,7 @@ macro_rules! eprintln {
18} 18}
19 19
20mod global_state; 20mod global_state;
21mod reload;
21mod main_loop; 22mod main_loop;
22mod dispatch; 23mod dispatch;
23mod handlers; 24mod handlers;
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
new file mode 100644
index 000000000..1981a97da
--- /dev/null
+++ b/crates/rust-analyzer/src/reload.rs
@@ -0,0 +1,241 @@
1//! Project loading & configuration updates
2use std::sync::Arc;
3
4use crossbeam_channel::unbounded;
5use flycheck::FlycheckHandle;
6use lsp_types::request::Request;
7use ra_db::{CrateGraph, SourceRoot, VfsPath};
8use ra_ide::AnalysisChange;
9use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace};
10use vfs::{file_set::FileSetConfig, AbsPath};
11
12use crate::{
13 config::{Config, FilesWatcher, LinkedProject},
14 global_state::GlobalState,
15};
16
17impl GlobalState {
18 pub(crate) fn update_configuration(&mut self, new_config: Config) {
19 self.analysis_host.update_lru_capacity(new_config.lru_capacity);
20 if new_config.flycheck != self.config.flycheck {
21 self.reload_flycheck();
22 }
23 self.config = new_config;
24 }
25 pub(crate) fn reload(&mut self) {
26 let workspaces = {
27 if self.config.linked_projects.is_empty()
28 && self.config.notifications.cargo_toml_not_found
29 {
30 self.show_message(
31 lsp_types::MessageType::Error,
32 "rust-analyzer failed to discover workspace".to_string(),
33 );
34 };
35
36 self.config
37 .linked_projects
38 .iter()
39 .filter_map(|project| match project {
40 LinkedProject::ProjectManifest(manifest) => {
41 ra_project_model::ProjectWorkspace::load(
42 manifest.clone(),
43 &self.config.cargo,
44 self.config.with_sysroot,
45 )
46 .map_err(|err| {
47 log::error!("failed to load workspace: {:#}", err);
48 self.show_message(
49 lsp_types::MessageType::Error,
50 format!("rust-analyzer failed to load workspace: {:#}", err),
51 );
52 })
53 .ok()
54 }
55 LinkedProject::InlineJsonProject(it) => {
56 Some(ra_project_model::ProjectWorkspace::Json { project: it.clone() })
57 }
58 })
59 .collect::<Vec<_>>()
60 };
61
62 if let FilesWatcher::Client = self.config.files.watcher {
63 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
64 watchers: workspaces
65 .iter()
66 .flat_map(ProjectWorkspace::to_roots)
67 .filter(PackageRoot::is_member)
68 .map(|root| format!("{}/**/*.rs", root.path().display()))
69 .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
70 .collect(),
71 };
72 let registration = lsp_types::Registration {
73 id: "file-watcher".to_string(),
74 method: "workspace/didChangeWatchedFiles".to_string(),
75 register_options: Some(serde_json::to_value(registration_options).unwrap()),
76 };
77 let params = lsp_types::RegistrationParams { registrations: vec![registration] };
78 let request = self.req_queue.outgoing.register(
79 lsp_types::request::RegisterCapability::METHOD.to_string(),
80 params,
81 |_, _| (),
82 );
83 self.send(request.into());
84 }
85
86 let mut change = AnalysisChange::new();
87
88 let project_folders = ProjectFolders::new(&workspaces);
89
90 self.proc_macro_client = match &self.config.proc_macro_srv {
91 None => ProcMacroClient::dummy(),
92 Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
93 Ok(it) => it,
94 Err(err) => {
95 log::error!(
96 "Failed to run ra_proc_macro_srv from path {}, error: {:?}",
97 path.display(),
98 err
99 );
100 ProcMacroClient::dummy()
101 }
102 },
103 };
104 let watch = match self.config.files.watcher {
105 FilesWatcher::Client => vec![],
106 FilesWatcher::Notify => project_folders.watch,
107 };
108 self.loader.set_config(vfs::loader::Config { load: project_folders.load, watch });
109
110 // Create crate graph from all the workspaces
111 let crate_graph = {
112 let mut crate_graph = CrateGraph::default();
113 let vfs = &mut self.vfs.write().0;
114 let loader = &mut self.loader;
115 let mut load = |path: &AbsPath| {
116 let contents = loader.load_sync(path);
117 let path = vfs::VfsPath::from(path.to_path_buf());
118 vfs.set_file_contents(path.clone(), contents);
119 vfs.file_id(&path)
120 };
121 for ws in workspaces.iter() {
122 crate_graph.extend(ws.to_crate_graph(
123 self.config.cargo.target.as_deref(),
124 &self.proc_macro_client,
125 &mut load,
126 ));
127 }
128
129 crate_graph
130 };
131 change.set_crate_graph(crate_graph);
132
133 self.source_root_config = project_folders.source_root_config;
134 self.workspaces = Arc::new(workspaces);
135
136 self.analysis_host.apply_change(change);
137 self.process_changes();
138 self.reload_flycheck();
139 }
140
141 fn reload_flycheck(&mut self) {
142 let config = match self.config.flycheck.clone() {
143 Some(it) => it,
144 None => {
145 self.flycheck = None;
146 return;
147 }
148 };
149
150 // FIXME: Figure out the multi-workspace situation
151 self.flycheck = self.workspaces.iter().find_map(move |w| match w {
152 ProjectWorkspace::Cargo { cargo, .. } => {
153 let (sender, receiver) = unbounded();
154 let sender = Box::new(move |msg| sender.send(msg).unwrap());
155 let cargo_project_root = cargo.workspace_root().to_path_buf();
156 let flycheck =
157 FlycheckHandle::spawn(sender, config.clone(), cargo_project_root.into());
158 Some((flycheck, receiver))
159 }
160 ProjectWorkspace::Json { .. } => {
161 log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
162 None
163 }
164 })
165 }
166}
167
168#[derive(Default)]
169pub(crate) struct ProjectFolders {
170 pub(crate) load: Vec<vfs::loader::Entry>,
171 pub(crate) watch: Vec<usize>,
172 pub(crate) source_root_config: SourceRootConfig,
173}
174
175impl ProjectFolders {
176 pub(crate) fn new(workspaces: &[ProjectWorkspace]) -> ProjectFolders {
177 let mut res = ProjectFolders::default();
178 let mut fsc = FileSetConfig::builder();
179 let mut local_filesets = vec![];
180
181 for root in workspaces.iter().flat_map(|it| it.to_roots()) {
182 let path = root.path().to_owned();
183
184 let mut file_set_roots: Vec<VfsPath> = vec![];
185
186 let entry = if root.is_member() {
187 vfs::loader::Entry::local_cargo_package(path.to_path_buf())
188 } else {
189 vfs::loader::Entry::cargo_package_dependency(path.to_path_buf())
190 };
191 res.load.push(entry);
192 if root.is_member() {
193 res.watch.push(res.load.len() - 1);
194 }
195
196 if let Some(out_dir) = root.out_dir() {
197 let out_dir = out_dir.to_path_buf();
198 res.load.push(vfs::loader::Entry::rs_files_recursively(out_dir.clone()));
199 if root.is_member() {
200 res.watch.push(res.load.len() - 1);
201 }
202 file_set_roots.push(out_dir.into());
203 }
204 file_set_roots.push(path.to_path_buf().into());
205
206 if root.is_member() {
207 local_filesets.push(fsc.len());
208 }
209 fsc.add_file_set(file_set_roots)
210 }
211
212 let fsc = fsc.build();
213 res.source_root_config = SourceRootConfig { fsc, local_filesets };
214
215 res
216 }
217}
218
219#[derive(Default, Debug)]
220pub(crate) struct SourceRootConfig {
221 pub(crate) fsc: FileSetConfig,
222 pub(crate) local_filesets: Vec<usize>,
223}
224
225impl SourceRootConfig {
226 pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> {
227 self.fsc
228 .partition(vfs)
229 .into_iter()
230 .enumerate()
231 .map(|(idx, file_set)| {
232 let is_local = self.local_filesets.contains(&idx);
233 if is_local {
234 SourceRoot::new_local(file_set)
235 } else {
236 SourceRoot::new_library(file_set)
237 }
238 })
239 .collect()
240 }
241}